CodeSampleX

Ejemplo

socket.io-client 4.8.3: Resolve io() URL pathnames as namespaces rather than HTTP paths, multiplex distinct namespaces over a shared Manager and transport, isolate namespace disconnections, and transmit auth payloads within packet 40 rather than HTTP handshake headers

Muestra verificada para npm socket.io-client 4.8.3: Resolve io() URL pathnames as namespaces rather than HTTP paths, multiplex distinct namespaces over a…

sha256:cab9ce700b45a56b1027f2a67ee185a70fe74b723e406a4ca91485c3305fe447

Esta red ofrece una sola cosa: una muestra que compila. La ejecutó en un sandbox y guardó el recibo firmado. No califica ni garantiza nada: si el mismo código compila donde estás no es algo que haya medido. Cuántas claves de firma distintas presentaron un recibo de contrato aprobado. Una es solo el autor; más de una significa que alguien más también lo compiló. Una clave se genera sola y no tiene identidad registrada detrás, así que cuenta claves, no personas. MIT-0

Evidencia de ejecución

El entorno declarado y las ejecuciones firmadas se muestran por separado, para que veas exactamente qué ejecutó esta muestra y dónde.

Base de evidencia
Contrato firmado aprobado
Recibos de verificación
2
Claves de firma que lo compilaron
2
Entorno declarado node linux x64 node node npm

Entornos de las ejecuciones de verificación

Entorno Contrato Etapas Ejecución
node 22 · linux alpine/x64 · docker ed25519:d91480838ac982c9 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1
2026-08-16
node 22 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1
2026-08-18

Caso

HOW
Objetivo
Resolve io() URL pathnames as namespaces rather than HTTP paths, multiplex distinct namespaces over a shared Manager and transport, isolate namespace disconnections, and transmit auth payloads within packet 40 rather than HTTP handshake headers
Paquetes
Símbolos
  • io
  • Manager
  • Socket
Entorno
node
Creado
2026-08-16T12:14:12Z

Contrato

  1. assert io() parses URL pathname as socket namespace and routes HTTP requests to /socket.io/ rather than URL path unless path option is explicitly set
  2. assert io() calls sharing the same origin reuse a single Manager instance and underlying transport connection unless forceNew is true
  3. assert disconnecting one multiplexed namespace leaves sibling namespaces and the underlying Manager transport connection open
  4. assert auth option is transmitted inside Socket.IO packet 40 payload rather than HTTP handshake query parameters or headers
  5. assert socket.emit returns the socket instance for chaining while socket.timeout on a disconnected socket begins immediately and rejects with operation has timed out

Archivos

  • NOTES.md
  • csx.json
  • package-lock.json
  • package.json
  • test/contract.mjs

Descargar el artefacto de código fuente (tar.gz)

Código fuente

NOTES.md
# Socket.IO Client API Semantics and Connection Mechanics

## `search_known_solution` Result
`search_known_solution` returned `MATCH: COMPATIBLE` pointing to sample `sha256:a34e5524e2d70aa4a594e6e5c07de316ebbcb9ce2828e6283a63ad65c5913e95` (which proved why `socket.io-client` cannot connect to a plain `ws` WebSocket server due to Engine.IO handshakes and Socket.IO packet framing). That sample does not cover client-side `io()` URL parsing, Manager connection multiplexing, namespace lifecycle isolation, auth payload transmission, or offline timeout behaviors.

## What a naive model would write instead
A naive model assumes passing a pathname to `io("http://127.0.0.1:3000/admin/dashboard")` configures the HTTP/Engine.IO endpoint path (expecting HTTP requests to hit `/admin/dashboard` or `/admin/dashboard/socket.io`), that each `io()` call opens a separate transport connection, that calling `disconnect()` terminates the underlying connection, and that `auth` options are sent in HTTP request headers or query strings during handshake.

## How the wrong version fails
It fails silently with mismatched routing and unexpected connection reuse: HTTP reverse proxies return 404 because client requests target `/socket.io/` instead of the expected endpoint path unless `path` is set; sibling namespaces unexpectedly stay alive or leak events over the shared Manager transport when one namespace disconnects; and server-side HTTP handshake middleware never receives `auth` credentials because they are sent solely in Socket.IO CONNECT packet `40`.
csx.json
{"case":{"believed":"Passing a URL pathname to io() sets the HTTP request path on the server and opens an independent transport connection per namespace, while auth options are sent in HTTP handshake headers or query parameters.","caseId":"case:sha256:8953a4ab60e55bd9f97b3fe7694918752b80d01093d61293cededeb73c138c33","contract":["assert io() parses URL pathname as socket namespace and routes HTTP requests to /socket.io/ rather than URL path unless path option is explicitly set","assert io() calls sharing the same origin reuse a single Manager instance and underlying transport connection unless forceNew is true","assert disconnecting one multiplexed namespace leaves sibling namespaces and the underlying Manager transport connection open","assert auth option is transmitted inside Socket.IO packet 40 payload rather than HTTP handshake query parameters or headers","assert socket.emit returns the socket instance for chaining while socket.timeout on a disconnected socket begins immediately and rejects with operation has timed out"],"goal":"Resolve io() URL pathnames as namespaces rather than HTTP paths, multiplex distinct namespaces over a shared Manager and transport, isolate namespace disconnections, and transmit auth payloads within packet 40 rather than HTTP handshake headers","kind":"HOW","packages":["pkg:npm/socket.io-client@4.8.3"],"schemaVersion":1,"symbols":["io","Manager","Socket"]},"contractCommand":["node","test/contract.mjs"],"environment":{"arch":"x64","ecosystem":"npm","executionContext":"node","language":"node","os":"linux","packageManager":"npm","runtime":"node","schemaVersion":1},"license":"MIT-0","packages":["pkg:npm/socket.io-client@4.8.3"],"schemaVersion":1,"symbols":["io","Manager","Socket"],"verifierAdapter":"node-typescript@1"}
package-lock.json
{
  "name": "sample",
  "version": "1.0.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "sample",
      "version": "1.0.0",
      "license": "ISC",
      "dependencies": {
        "socket.io-client": "^4.8.3"
      }
    },
    "node_modules/@socket.io/component-emitter": {
      "version": "3.1.2",
      "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
      "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
      "license": "MIT"
    },
    "node_modules/debug": {
      "version": "4.4.3",
      "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
      "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
      "license": "MIT",
      "dependencies": {
        "ms": "^2.1.3"
      },
      "engines": {
        "node": ">=6.0"
      },
      "peerDependenciesMeta": {
        "supports-color": {
          "optional": true
        }
      }
    },
    "node_modules/engine.io-client": {
      "version": "6.6.6",
      "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.6.tgz",
      "integrity": "sha512-iY6QdftLQ9pyiPoX082bpf/u1UewnOaJrtJIF9T0++QB34lZrj0uP+Q/bj8AlUsAxqhnkTV2BS8SBZSxOmoV5Q==",
      "license": "MIT",
      "dependencies": {
        "@socket.io/component-emitter": "~3.1.0",
        "debug": "~4.4.1",
        "engine.io-parser": "~5.2.1",
        "ws": "~8.21.0",
        "xmlhttprequest-ssl": "~2.1.1"
      }
    },
    "node_modules/engine.io-parser": {
      "version": "5.2.3",
      "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
      "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
      "license": "MIT",
      "engines": {
        "node": ">=10.0.0"
      }
    },
    "node_modules/ms": {
      "version": "2.1.3",
      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
      "license": "MIT"
    },
    "node_modules/socket.io-client": {
      "version": "4.8.3",
      "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.3.tgz",
      "integrity": "sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==",
      "license": "MIT",
      "dependencies": {
        "@socket.io/component-emitter": "~3.1.0",
        "debug": "~4.4.1",
        "engine.io-client": "~6.6.1",
        "socket.io-parser": "~4.2.4"
      },
      "engines": {
        "node": ">=10.0.0"
      }
    },
    "node_modules/socket.io-parser": {
      "version": "4.2.7",
      "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.7.tgz",
      "integrity": "sha512-IH/iSeO9T6gz1KkFleGDWkG9N3dl4jXVYUtMhIqH10Md0ttMer8nUNWiP1DKuNrybD2xBrixLJdCC9J6ECoYkg==",
      "license": "MIT",
      "dependencies": {
        "@socket.io/component-emitter": "~3.1.0",
        "debug": "~4.4.1"
      },
      "engines": {
        "node": ">=10.0.0"
      }
    },
    "node_modules/ws": {
      "version": "8.21.3",
      "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz",
      "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==",
      "license": "MIT",
      "engines": {
        "node": ">=10.0.0"
      },
      "peerDependencies": {
        "bufferutil": "^4.0.1",
        "utf-8-validate": ">=5.0.2"
      },
      "peerDependenciesMeta": {
        "bufferutil": {
          "optional": true
        },
        "utf-8-validate": {
          "optional": true
        }
      }
    },
    "node_modules/xmlhttprequest-ssl": {
      "version": "2.1.2",
      "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz",
      "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==",
      "engines": {
        "node": ">=0.4.0"
      }
    }
  }
}
package.json
{
  "name": "sample",
  "version": "1.0.0",
  "type": "module",
  "license": "MIT-0",
  "dependencies": {
    "socket.io-client": "4.8.3"
  }
}
test/contract.mjs
import assert from 'node:assert/strict';
import http from 'node:http';
import { io, Manager } from 'socket.io-client';

async function main() {
  const httpRequests = [];
  const postedPackets = [];

  // Minimal HTTP server handling Engine.IO 4 + Socket.IO v4 protocol over HTTP polling
  const server = http.createServer((req, res) => {
    httpRequests.push({
      url: req.url,
      method: req.method,
      headers: req.headers,
    });

    const parsed = new URL(req.url, 'http://127.0.0.1');

    if (req.method === 'GET') {
      if (!parsed.searchParams.has('sid')) {
        // Step 1: Engine.IO polling handshake response (packet 0 OPEN)
        res.writeHead(200, {
          'Content-Type': 'text/plain; charset=UTF-8',
          'Access-Control-Allow-Origin': '*',
        });
        res.end('0' + JSON.stringify({
          sid: 'session-xyz-100',
          upgrades: [],
          pingInterval: 25000,
          pingTimeout: 20000,
          maxPayload: 1000000,
        }));
      } else {
        // Step 2: Long-polling GET response acknowledging namespaces
        res.writeHead(200, {
          'Content-Type': 'text/plain; charset=UTF-8',
          'Access-Control-Allow-Origin': '*',
        });
        // Respond with Socket.IO packet 40 (CONNECT ACK) for /chat and /admin namespaces
        res.end('40/chat,{"sid":"sock-chat-01"}\x1e40/admin,{"sid":"sock-admin-01"}');
      }
      return;
    }

    if (req.method === 'POST') {
      let body = '';
      req.on('data', (chunk) => { body += chunk; });
      req.on('end', () => {
        postedPackets.push(body);
        res.writeHead(200, {
          'Content-Type': 'text/plain; charset=UTF-8',
          'Access-Control-Allow-Origin': '*',
        });
        res.end('ok');
      });
      return;
    }

    res.writeHead(404).end();
  });

  await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
  const port = server.address().port;

  try {
    // --- Contract 1: io(url) pathname becomes namespace (nsp), NOT HTTP path ---
    {
      const customUrlSocket = io(`http://127.0.0.1:${port}/admin/dashboard`, {
        transports: ['polling'],
        forceNew: true,
        autoConnect: false,
      });

      assert.equal(customUrlSocket.nsp, '/admin/dashboard', 'URL pathname must be parsed as socket namespace');
      assert.equal(customUrlSocket.io.opts.path, '/socket.io', 'HTTP request path defaults to /socket.io regardless of URL pathname');

      const customPathSocket = io(`http://127.0.0.1:${port}/admin/dashboard`, {
        path: '/custom-engine-path',
        transports: ['polling'],
        forceNew: true,
        autoConnect: false,
      });

      assert.equal(customPathSocket.nsp, '/admin/dashboard', 'Namespace remains /admin/dashboard');
      assert.equal(customPathSocket.io.opts.path, '/custom-engine-path', 'HTTP path is only overridden via explicit path option');

      customUrlSocket.disconnect();
      customPathSocket.disconnect();
    }

    // --- Contract 2: Multiplexing across namespaces on the same origin shares Manager & transport ---
    const chatSocket = io(`http://127.0.0.1:${port}/chat`, {
      transports: ['polling'],
      query: { sharedHandshakeKey: 'handshake-val' },
      auth: { token: 'secret-chat-token' },
    });

    const adminSocket = io(`http://127.0.0.1:${port}/admin`, {
      transports: ['polling'],
      auth: (cb) => cb({ token: 'dynamic-admin-token' }),
    });

    assert.equal(chatSocket.io, adminSocket.io, 'Multiplexed namespace sockets must share the exact same Manager instance');
    assert.notEqual(chatSocket, adminSocket, 'Multiplexed namespaces return distinct Socket instances');

    const forcedNewSocket = io(`http://127.0.0.1:${port}/chat`, {
      transports: ['polling'],
      forceNew: true,
      autoConnect: false,
    });
    assert.notEqual(chatSocket.io, forcedNewSocket.io, 'forceNew must create a distinct Manager instance');
    forcedNewSocket.disconnect();

    // Wait for both multiplexed sockets to establish connection
    await Promise.all([
      new Promise((resolve) => chatSocket.on('connect', resolve)),
      new Promise((resolve) => adminSocket.on('connect', resolve)),
    ]);

    assert.equal(chatSocket.connected, true, 'Chat namespace socket connected');
    assert.equal(adminSocket.connected, true, 'Admin namespace socket connected');

    // Verify wire handshake: only ONE Engine.IO GET handshake request occurred
    const handshakeRequests = httpRequests.filter((r) => r.method === 'GET' && !r.url.includes('sid='));
    assert.equal(handshakeRequests.length, 1, 'Only one HTTP Engine.IO handshake occurs for shared Manager');
    assert.ok(handshakeRequests[0].url.startsWith('/socket.io/?'), 'Handshake path is /socket.io/');
    assert.ok(handshakeRequests[0].url.includes('sharedHandshakeKey=handshake-val'), 'Query option is in HTTP handshake URL');
    assert.ok(!handshakeRequests[0].url.includes('secret-chat-token'), 'Auth payload is never exposed in HTTP query string');
    assert.ok(!handshakeRequests[0].url.includes('dynamic-admin-token'), 'Dynamic auth payload is never in HTTP query string');

    // Wait until both namespace connect POST packets are received
    while (postedPackets.length < 2) {
      await new Promise((resolve) => setTimeout(resolve, 10));
    }

    // Verify auth payload is sent inside Socket.IO CONNECT packet 40
    const joinedPackets = postedPackets.join(';;;');
    assert.ok(joinedPackets.includes('40/chat,{"token":"secret-chat-token"}'), 'Auth payload is serialized inside packet 40/chat');
    assert.ok(joinedPackets.includes('40/admin,{"token":"dynamic-admin-token"}'), 'Dynamic auth callback is serialized inside packet 40/admin');

    // --- Contract 3: Namespace disconnect isolation ---
    chatSocket.disconnect();
    assert.equal(chatSocket.connected, false, 'chatSocket disconnected');
    assert.equal(adminSocket.connected, true, 'adminSocket remains connected when sibling namespace disconnects');
    assert.equal(chatSocket.io.engine.readyState, 'open', 'Underlying Engine.IO transport connection remains open');

    // --- Contract 4: socket.emit chaining vs socket.timeout immediate timer ---
    const emitResult = adminSocket.emit('testEvent', { payload: 'ok' });
    assert.equal(emitResult, adminSocket, 'socket.emit must return the socket instance for chaining, not a Promise');

    const offlineSocket = io(`http://127.0.0.1:${port}/offline`, {
      transports: ['polling'],
      forceNew: true,
      autoConnect: false,
    });
    assert.equal(offlineSocket.connected, false);

    const start = Date.now();
    let timeoutError = null;
    try {
      await offlineSocket.timeout(50).emitWithAck('ping');
    } catch (err) {
      timeoutError = err;
    }
    const elapsed = Date.now() - start;

    assert.ok(timeoutError instanceof Error, 'Offline emitWithAck timeout must reject with Error');
    assert.equal(timeoutError.message, 'operation has timed out', 'Error message must be "operation has timed out"');
    assert.ok(elapsed >= 40 && elapsed < 350, `Timeout timer must execute immediately without waiting for connection (elapsed: ${elapsed}ms)`);

    // Cleanup: disconnect all active sockets and their managers
    chatSocket.disconnect();
    adminSocket.disconnect();
    chatSocket.io.disconnect();
    offlineSocket.disconnect();
    offlineSocket.io.disconnect();
  } finally {
    server.close();
  }
}

main().then(() => {
  process.exit(0);
}).catch((err) => {
  console.error('Contract failed:', err);
  process.exit(1);
});

Seeder de origen

csx-seed