CodeSampleX

サンプル

socket.io-client 4.8.1: Handle request-response event acknowledgements via callbacks and emitWithAck, intercept incoming and outgoing events with onAny and onAnyOutgoing, and discard unsent events using volatile emit

検証済みサンプル — npm socket.io-client 4.8.1: Handle request-response event acknowledgements via callbacks and emitWithAck, intercept incoming and outgoing events…

sha256:094aa3ff46c77195e3261b3a7cbcce557af72e9f6032a688e8fcddae7ca9fa64

このネットワークが提供するのは一つだけです。ビルドされるサンプル。サンドボックスで実行し、署名済みの受領証を保管します。等級はつけず、何も保証しません — 同じコードがあなたの環境でビルドされるかは測定していません。 合格した契約受領証を提出した異なる署名鍵の数です。1 なら作者だけ、2 以上なら他の誰かもビルドしています。鍵は自己生成で背後に登録された身元がないため、数えているのは人ではなく鍵です。 MIT-0

実行証拠

宣言された環境と署名済みの実行を分けてあります。このサンプルが何をどこで実行したかをそのまま確認できます。

証拠の基準
署名済みコントラクト合格
検証レシート
1
ビルドした署名鍵
1
宣言された環境 node 22.23 linux 24 · ubuntu · glibc 2.39 x64 node 22.23 javascript npm 10

検証実行環境

環境 コントラクト ステージ 実行日
node 22 · linux alpine/x64 · docker ed25519:c1973797be207ac4 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1node:22-alpine@sha256:c610fcdfb1d5…
2026-08-23

ケース

HOW
ゴール
Handle request-response event acknowledgements via callbacks and emitWithAck, intercept incoming and outgoing events with onAny and onAnyOutgoing, and discard unsent events using volatile emit
パッケージ
シンボル
  • Socket.emitWithAck
  • Socket.onAny
  • Socket.onAnyOutgoing
  • Socket.volatile
環境
node 22.23.2
作成日
2026-08-23T16:09:48Z

コントラクト

  1. assert volatile emit while disconnected discards event immediately without queuing in sendBuffer whereas regular emit is queued
  2. assert onAnyOutgoing and prependAnyOutgoing intercept outgoing event names and payloads when draining buffered events and on live emits
  3. assert prependAny and onAny execute before specific event handlers in deterministic order upon receiving incoming events
  4. assert emitWithAck resolves Promise with server acknowledgement payload and rejects with operation has timed out when timeout occurs
  5. assert callback-based emit with timeout receives Error with operation has timed out and undefined result when unacknowledged

ファイル

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

ソースアーティファクトをダウンロード (tar.gz)

ソース

NOTES.md
# Socket.IO Client Event Interception, Acknowledgements, and Volatile Events

## `search_known_solution` Result
`search_known_solution` returned `NO_SAFE_MATCH` for `pkg:npm/socket.io-client@4.8.1` targeting `Socket.emitWithAck`, `Socket.onAny`, `Socket.onAnyOutgoing`, and `Socket.volatile`. Existing cached samples only addressed plain WebSocket handshake incompatibility and namespace URL path parsing / Manager multiplexing.

## What a naive model would write instead
A naive model assumes that `socket.onAnyOutgoing(...)` intercepts `socket.emit(...)` calls immediately even when disconnected, that `socket.volatile.emit(...)` buffers unsent events into `sendBuffer` when offline just like standard `socket.emit(...)`, and that request-response acknowledgement timeouts or callback errors fail silently or leave unresolved Promises rather than rejecting with a standard timeout error.

## How the wrong version fails
It fails with unexpected duplicate or out-of-order event delivery: volatile events intended to be dropped when offline end up lingering in `sendBuffer` and flooding the server upon connection; offline logging wrappers relying on `onAnyOutgoing` to trace emit call timings fail to record until reconnection drains the buffer; and unhandled acknowledgement timeouts cause unresolved Promises or unhandled exceptions rather than cleanly rejecting with an Error message `"operation has timed out"`.
csx.json
{"case":{"believed":"Emitted events are always intercepted immediately by onAnyOutgoing regardless of connection state, volatile.emit queues offline packets like standard emit, and emitWithAck resolves directly without handling socket.io error-first ack protocol or timeout errors.","caseId":"case:sha256:a6534f368cdaaf4168342c375355559b7bd500ca5c2121f5f6d0cd43eeaa44c7","constraints":{"executionContext":"node"},"contract":["assert volatile emit while disconnected discards event immediately without queuing in sendBuffer whereas regular emit is queued","assert onAnyOutgoing and prependAnyOutgoing intercept outgoing event names and payloads when draining buffered events and on live emits","assert prependAny and onAny execute before specific event handlers in deterministic order upon receiving incoming events","assert emitWithAck resolves Promise with server acknowledgement payload and rejects with operation has timed out when timeout occurs","assert callback-based emit with timeout receives Error with operation has timed out and undefined result when unacknowledged"],"goal":"Handle request-response event acknowledgements via callbacks and emitWithAck, intercept incoming and outgoing events with onAny and onAnyOutgoing, and discard unsent events using volatile emit","kind":"HOW","packages":["pkg:npm/socket.io-client@4.8.1"],"schemaVersion":1,"symbols":["Socket.emitWithAck","Socket.onAny","Socket.onAnyOutgoing","Socket.volatile"]},"contractCommand":["node","test/contract.mjs"],"environment":{"arch":"x64","distro":"ubuntu","ecosystem":"npm","executionContext":"node","language":"javascript","libc":"glibc","libcVersion":"2.39","moduleSystem":"cjs","os":"linux","osVersionBucket":"24","packageManager":"npm","packageManagerVersion":"10.9.8","runtime":"node","runtimeVersion":"22.23.2","schemaVersion":1},"license":"MIT-0","packages":["pkg:npm/socket.io-client@4.8.1"],"schemaVersion":1,"subject":"pkg:npm/socket.io-client@4.8.1","symbols":["Socket.emitWithAck","Socket.onAny","Socket.onAnyOutgoing","Socket.volatile"],"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": "MIT-0",
      "dependencies": {
        "socket.io-client": "4.8.1"
      }
    },
    "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.3.7",
      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
      "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
      "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-client/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-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.1",
      "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz",
      "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==",
      "license": "MIT",
      "dependencies": {
        "@socket.io/component-emitter": "~3.1.0",
        "debug": "~4.3.2",
        "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/socket.io-parser/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/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.1"
  }
}
test/contract.mjs
import assert from 'node:assert/strict';
import http from 'node:http';
import { io } from 'socket.io-client';

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

  // Minimal Engine.IO v4 + Socket.IO v4 polling server
  const server = http.createServer((req, res) => {
    const parsed = new URL(req.url, 'http://127.0.0.1');

    if (req.method === 'GET') {
      if (!parsed.searchParams.has('sid')) {
        // Handshake packet 0 OPEN
        res.writeHead(200, {
          'Content-Type': 'text/plain; charset=UTF-8',
          'Access-Control-Allow-Origin': '*',
        });
        res.end('0' + JSON.stringify({
          sid: 'session-client-001',
          upgrades: [],
          pingInterval: 25000,
          pingTimeout: 20000,
          maxPayload: 1000000,
        }));
      } else {
        // Polling response
        res.writeHead(200, {
          'Content-Type': 'text/plain; charset=UTF-8',
          'Access-Control-Allow-Origin': '*',
        });
        pollWaiters.push(res);
      }
      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');

        // Check if there is an event with ack requested, e.g. 420["getUser",{"id":100}]
        const match = body.match(/^42(\d+)\["getUser",(.+)\]$/);
        if (match) {
          const ackId = match[1];
          const param = JSON.parse(match[2]);
          sendToClient(`43${ackId}[{"userId":${param.id},"status":"active"}]`);
        }
      });
      return;
    }

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

  function sendToClient(packetStr) {
    if (pollWaiters.length > 0) {
      const res = pollWaiters.shift();
      res.end(packetStr);
    }
  }

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

  try {
    // --- Contract 1: Volatile vs regular emit while disconnected ---
    const disconnectedSocket = io(`http://127.0.0.1:${port}`, {
      transports: ['polling'],
      autoConnect: false,
    });

    assert.equal(disconnectedSocket.connected, false, 'Socket starts disconnected');
    assert.equal(disconnectedSocket.sendBuffer.length, 0, 'sendBuffer is initially empty');

    disconnectedSocket.emit('queuedEvent', { msg: 'first' });
    assert.equal(disconnectedSocket.sendBuffer.length, 1, 'Regular emit while disconnected is queued in sendBuffer');

    disconnectedSocket.volatile.emit('droppedEvent', { msg: 'second' });
    assert.equal(disconnectedSocket.sendBuffer.length, 1, 'Volatile emit while disconnected is discarded immediately without queuing');

    disconnectedSocket.disconnect();

    // --- Contract 2: Outgoing event interception and buffer flush order ---
    const outgoingLog = [];
    const client = io(`http://127.0.0.1:${port}`, {
      transports: ['polling'],
      autoConnect: false,
    });

    const out1 = (event, ...args) => outgoingLog.push(`out1:${event}:${JSON.stringify(args)}`);
    const outPrepend = (event, ...args) => outgoingLog.push(`prepend:${event}:${JSON.stringify(args)}`);

    client.onAnyOutgoing(out1);
    client.prependAnyOutgoing(outPrepend);

    // When disconnected, emit queues into sendBuffer without immediately notifying outgoing listeners
    client.emit('offlineBuffered', { buffered: true });
    assert.equal(outgoingLog.length, 0, 'onAnyOutgoing is not triggered on offline emit until sendBuffer is drained');

    // Connect to server
    const incomingLog = [];
    const executionOrder = [];

    const in1 = (event, ...args) => {
      incomingLog.push({ event, args });
      executionOrder.push(`in1:${event}`);
    };
    const inPrepend = (event, ...args) => executionOrder.push(`prepend:${event}`);

    client.onAny(in1);
    client.prependAny(inPrepend);

    client.on('chatMessage', (msg) => {
      executionOrder.push(`handler:chatMessage:${msg.text}`);
    });

    client.connect();

    while (pollWaiters.length === 0) {
      await new Promise((r) => setTimeout(r, 10));
    }
    // Server acknowledges namespace connect
    sendToClient('40{"sid":"session-client-001"}');

    await new Promise((resolve) => client.on('connect', resolve));
    assert.equal(client.connected, true, 'Client connected to server');

    // Upon connection, buffered packet is drained and outgoing listeners are invoked in prepend-first order
    assert.deepEqual(
      outgoingLog,
      ['prepend:offlineBuffered:[{"buffered":true}]', 'out1:offlineBuffered:[{"buffered":true}]'],
      'Draining sendBuffer on connect invokes prependAnyOutgoing before onAnyOutgoing',
    );

    // While connected, emit immediately notifies outgoing listeners
    outgoingLog.length = 0;
    client.emit('liveEvent', { active: true });
    assert.deepEqual(
      outgoingLog,
      ['prepend:liveEvent:[{"active":true}]', 'out1:liveEvent:[{"active":true}]'],
      'Live emit while connected notifies outgoing listeners immediately',
    );

    client.offAnyOutgoing(out1);
    outgoingLog.length = 0;
    client.emit('liveEvent2', 99);
    assert.deepEqual(outgoingLog, ['prepend:liveEvent2:[99]'], 'offAnyOutgoing removes the specific outgoing listener');
    client.offAnyOutgoing(outPrepend);

    // --- Contract 3: Incoming onAny and prependAny interception order ---
    while (pollWaiters.length === 0) {
      await new Promise((r) => setTimeout(r, 10));
    }
    sendToClient('42["chatMessage",{"text":"greetings"}]');

    while (executionOrder.length < 3) {
      await new Promise((r) => setTimeout(r, 10));
    }

    assert.deepEqual(
      executionOrder,
      ['prepend:chatMessage', 'in1:chatMessage', 'handler:chatMessage:greetings'],
      'prependAny and onAny execute before specific event handlers in exact order',
    );
    assert.deepEqual(incomingLog, [{ event: 'chatMessage', args: [{ text: 'greetings' }] }], 'onAny captures event name and payload');

    client.offAny(in1);
    client.offAny(inPrepend);

    // --- Contract 4: Request-response event acknowledgement via emitWithAck ---
    const ackResult = await client.emitWithAck('getUser', { id: 100 });
    assert.deepEqual(
      ackResult,
      { userId: 100, status: 'active' },
      'emitWithAck resolves with server ack response payload',
    );

    // --- Contract 5: Timeout handling on emitWithAck and callback emit ---
    let timeoutError = null;
    try {
      await client.timeout(40).emitWithAck('noResponseEvent', { data: 'test' });
    } catch (err) {
      timeoutError = err;
    }
    assert.ok(timeoutError instanceof Error, 'emitWithAck timeout rejects with Error');
    assert.equal(timeoutError.message, 'operation has timed out', 'Error message is "operation has timed out"');

    let cbTimeoutError = null;
    let cbResult = null;
    await new Promise((resolve) => {
      client.timeout(40).emit('noResponseCallback', { data: 'test' }, (err, res) => {
        cbTimeoutError = err;
        cbResult = res;
        resolve();
      });
    });
    assert.ok(cbTimeoutError instanceof Error, 'Callback timeout passes Error as first argument');
    assert.equal(cbTimeoutError.message, 'operation has timed out');
    assert.equal(cbResult, undefined, 'Callback timeout passes undefined result');

    // Cleanup
    client.disconnect();
    client.io.disconnect();
  } finally {
    server.close();
  }
}

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

オリジンシーダー

匿名