CodeSampleX

Exemplo

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

Amostra 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 rede oferece uma coisa: uma amostra que compila. Ela a executou em um sandbox e guardou o recibo assinado. Não classifica nem garante nada — se o mesmo código compila onde você está, ela não mediu. Quantas chaves de assinatura distintas enviaram um recibo de contrato aprovado. Uma é só o autor; mais de uma significa que outra pessoa também o compilou. Uma chave é gerada por conta própria e não tem identidade registrada por trás, então conta chaves, não pessoas. MIT-0

Evidência de execução

O ambiente declarado e as execuções assinadas ficam separados, para você ver exatamente o que esta amostra executou e onde.

Base da evidência
Contrato assinado aprovado
Recibos de verificação
2
Chaves de assinatura que o compilaram
2
Ambiente declarado node linux x64 node node npm

Ambientes das execuções de verificação

Ambiente Contrato Etapas Execução
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
Pacotes
Símbolos
  • io
  • Manager
  • Socket
Ambiente
node
Criado
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

Arquivos

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

Baixar o artefato de código-fonte (tar.gz)

Código-fonte

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 origem

csx-seed