iii-sdk 0.24.0 → 0.25.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/channel.cjs +1 -1
- package/dist/channel.mjs +1 -1
- package/dist/{channels-C4f7nfQr.mjs → channels-Di68FfqY.mjs} +3 -1
- package/dist/{channels-C4f7nfQr.mjs.map → channels-Di68FfqY.mjs.map} +1 -1
- package/dist/{channels-C2ezAJ_g.cjs → channels-Dn3OKXH5.cjs} +4 -2
- package/dist/{channels-C2ezAJ_g.cjs.map → channels-Dn3OKXH5.cjs.map} +1 -1
- package/dist/helpers.cjs +2 -2
- package/dist/helpers.mjs +1 -1
- package/dist/index.cjs +55 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +53 -38
- package/dist/index.mjs.map +1 -1
- package/dist/utils-8G4hHqvZ.mjs.map +1 -1
- package/dist/{utils-63wxBJg-.cjs → utils-SpwzEB77.cjs} +3 -3
- package/dist/{utils-63wxBJg-.cjs.map → utils-SpwzEB77.cjs.map} +1 -1
- package/package.json +9 -9
package/dist/channel.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
-
const require_channels = require('./channels-
|
|
2
|
+
const require_channels = require('./channels-Dn3OKXH5.cjs');
|
|
3
3
|
|
|
4
4
|
exports.ChannelReader = require_channels.ChannelReader;
|
|
5
5
|
exports.ChannelWriter = require_channels.ChannelWriter;
|
package/dist/channel.mjs
CHANGED
|
@@ -13,12 +13,14 @@ const ChannelDirection = {
|
|
|
13
13
|
Write: "write"
|
|
14
14
|
};
|
|
15
15
|
const ChannelItem = {
|
|
16
|
+
/** Construct a text channel item. */
|
|
16
17
|
Text(value) {
|
|
17
18
|
return {
|
|
18
19
|
type: "text",
|
|
19
20
|
value
|
|
20
21
|
};
|
|
21
22
|
},
|
|
23
|
+
/** Construct a binary channel item. */
|
|
22
24
|
Binary(value) {
|
|
23
25
|
return {
|
|
24
26
|
type: "binary",
|
|
@@ -222,4 +224,4 @@ function buildChannelUrl(engineWsBase, channelId, accessKey, direction) {
|
|
|
222
224
|
|
|
223
225
|
//#endregion
|
|
224
226
|
export { ChannelWriter as i, ChannelItem as n, ChannelReader as r, ChannelDirection as t };
|
|
225
|
-
//# sourceMappingURL=channels-
|
|
227
|
+
//# sourceMappingURL=channels-Di68FfqY.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"channels-C4f7nfQr.mjs","names":[],"sources":["../src/channels.ts"],"sourcesContent":["import { Readable, Writable } from 'node:stream'\nimport { WebSocket } from 'ws'\nimport { WS_HANDSHAKE_TIMEOUT_MS } from './iii-constants'\nimport type { StreamChannelRef } from './iii-types'\n\n/**\n * Direction of a streaming channel endpoint. Mirrors the Rust SDK's\n * `ChannelDirection` enum and matches the literal values used by\n * {@link StreamChannelRef.direction}.\n */\nexport const ChannelDirection = {\n Read: 'read',\n Write: 'write',\n} as const\nexport type ChannelDirection = (typeof ChannelDirection)[keyof typeof ChannelDirection]\n\n/**\n * Discriminated runtime tag for an item observed on a streaming channel.\n * Mirrors the Rust SDK's `ChannelItem` enum (`Text` / `Binary`). Carrier for\n * factory + type-guard helpers so callers can construct and discriminate\n * channel items without depending on Rust-specific shape.\n */\nexport type ChannelItem =\n | { type: 'text'; value: string }\n | { type: 'binary'; value: Uint8Array }\n\nexport const ChannelItem = {\n /** Construct a text channel item. */\n Text(value: string): ChannelItem {\n return { type: 'text', value }\n },\n /** Construct a binary channel item. */\n Binary(value: Uint8Array): ChannelItem {\n return { type: 'binary', value }\n },\n} as const\n\n/**\n * Write end of a streaming channel. Provides both a Node.js `Writable` stream\n * and a `sendMessage` method for sending structured text messages.\n *\n * @example\n * ```typescript\n * import { createChannel } from 'iii-sdk/helpers'\n * const channel = await createChannel(iii)\n *\n * // Stream binary data\n * channel.writer.stream.write(Buffer.from('hello'))\n * channel.writer.stream.end()\n *\n * // Or send text messages\n * channel.writer.sendMessage(JSON.stringify({ type: 'event', data: 'test' }))\n * channel.writer.close()\n * ```\n */\nexport class ChannelWriter {\n private static readonly FRAME_SIZE = 64 * 1024\n private ws: WebSocket | null = null\n private wsReady = false\n private readonly pendingMessages: {\n data: Buffer | string\n callback: (err?: Error | null) => void\n }[] = []\n /** Node.js Writable stream for binary data. */\n public readonly stream: Writable\n private readonly url: string\n\n constructor(engineWsBase: string, ref: StreamChannelRef) {\n this.url = buildChannelUrl(engineWsBase, ref.channel_id, ref.access_key, 'write')\n\n this.stream = new Writable({\n write: (chunk: Buffer, _encoding, callback) => {\n const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)\n this.sendChunked(buf, callback)\n },\n final: (callback) => {\n if (!this.ws) {\n callback()\n return\n }\n // Delay the close frame slightly to allow the TCP stack to flush\n // all buffered send() data. Without this, the close frame can arrive\n // at the engine before all data frames, causing data truncation.\n const doClose = () => {\n if (this.ws) {\n this.ws.close(1000, 'stream_complete')\n }\n callback()\n }\n if (this.wsReady) {\n setTimeout(doClose, 10)\n } else {\n this.ws.on('open', () => setTimeout(doClose, 10))\n }\n },\n destroy: (err, callback) => {\n if (this.ws) this.ws.terminate()\n callback(err)\n },\n })\n }\n\n private ensureConnected(): void {\n // A non-null socket is only reusable while it is still connecting or open.\n // A CLOSING/CLOSED socket is dead and must be replaced — guarding solely on\n // `this.ws` being non-null would pin a terminated socket forever, so every\n // subsequent send would target a connection that can never deliver it.\n if (\n this.ws &&\n this.ws.readyState !== WebSocket.CLOSING &&\n this.ws.readyState !== WebSocket.CLOSED\n ) {\n return\n }\n this.wsReady = false\n this.ws = new WebSocket(this.url, { handshakeTimeout: WS_HANDSHAKE_TIMEOUT_MS })\n\n this.ws.on('open', () => {\n this.wsReady = true\n for (const { data, callback } of this.pendingMessages) {\n this.ws?.send(data, callback)\n }\n this.pendingMessages.length = 0\n })\n\n this.ws.on('error', (err) => {\n this.wsReady = false\n this.stream.destroy(err)\n })\n\n this.ws.on('close', () => {\n this.wsReady = false\n if (!this.stream.destroyed) {\n this.stream.destroy()\n }\n })\n }\n\n /** Send a text message through the channel. */\n sendMessage(msg: string): void {\n this.ensureConnected()\n this.sendRaw(msg, (err) => {\n if (err) this.stream.destroy(err)\n })\n }\n\n /** Close the channel writer. */\n close(): void {\n if (!this.ws) return\n const doClose = () => {\n if (this.ws) {\n this.ws.close(1000, 'channel_close')\n }\n }\n if (this.wsReady) {\n doClose()\n } else {\n this.ws.on('open', () => doClose())\n }\n }\n\n private sendChunked(data: Buffer, callback: (err?: Error | null) => void): void {\n let offset = 0\n const sendNext = (err?: Error | null): void => {\n if (err) {\n callback(err)\n return\n }\n\n if (offset >= data.length) {\n callback(null)\n return\n }\n\n const end = Math.min(offset + ChannelWriter.FRAME_SIZE, data.length)\n const part = data.subarray(offset, end)\n offset = end\n this.sendRaw(part, sendNext)\n }\n sendNext(null)\n }\n\n private sendRaw(data: Buffer | string, callback: (err?: Error | null) => void): void {\n this.ensureConnected()\n // Trust the socket's live readyState over the tracked `wsReady` flag, which\n // can stay true after a drop. Queue anything we cannot send right now — and\n // requeue a send() that throws because the socket died underneath us — so it\n // flushes on the next open instead of being lost or throwing to the caller.\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n try {\n this.ws.send(data, (err) => callback(err ?? null))\n } catch {\n this.pendingMessages.push({ data, callback })\n }\n } else {\n this.pendingMessages.push({ data, callback })\n }\n }\n}\n\n/**\n * Read end of a streaming channel. Provides both a Node.js `Readable` stream\n * for binary data and an `onMessage` callback for structured text messages.\n *\n * @example\n * ```typescript\n * import { createChannel } from 'iii-sdk/helpers'\n * const channel = await createChannel(iii)\n *\n * // Stream binary data\n * channel.reader.stream.on('data', (chunk) => console.log(chunk))\n *\n * // Or receive text messages\n * channel.reader.onMessage((msg) => console.log('Got:', msg))\n * ```\n */\nexport class ChannelReader {\n private ws: WebSocket | null = null\n private connected = false\n private readonly messageCallbacks: Array<(msg: string) => void> = []\n /** Node.js Readable stream for binary data. */\n public readonly stream: Readable\n private readonly url: string\n\n constructor(engineWsBase: string, ref: StreamChannelRef) {\n this.url = buildChannelUrl(engineWsBase, ref.channel_id, ref.access_key, 'read')\n\n const self = this\n this.stream = new Readable({\n read() {\n self.ensureConnected()\n if (self.ws) self.ws.resume()\n },\n destroy(err, callback) {\n if (self.ws && self.ws.readyState !== WebSocket.CLOSED) {\n self.ws.terminate()\n }\n self.ws = null\n callback(err)\n },\n })\n }\n\n private ensureConnected(): void {\n if (this.connected) return\n this.connected = true\n this.ws = new WebSocket(this.url, { handshakeTimeout: WS_HANDSHAKE_TIMEOUT_MS })\n\n this.ws.on('open', () => {\n ;(this.ws as unknown as { binaryType: string }).binaryType = 'nodebuffer'\n })\n\n this.ws.on('message', (data: Buffer, isBinary: boolean) => {\n if (isBinary) {\n if (!this.stream.push(data)) {\n this.ws?.pause()\n }\n } else {\n const msg = data.toString('utf-8')\n for (const cb of this.messageCallbacks) {\n cb(msg)\n }\n }\n })\n\n this.ws.on('close', () => {\n this.ws = null\n if (!this.stream.destroyed) this.stream.push(null)\n })\n\n this.ws.on('error', (err) => {\n this.stream.destroy(err)\n })\n }\n\n /** Register a callback to receive text messages from the channel. */\n onMessage(callback: (msg: string) => void): void {\n this.messageCallbacks.push(callback)\n }\n\n async readAll(): Promise<Buffer> {\n this.ensureConnected()\n const chunks: Buffer[] = []\n\n for await (const chunk of this.stream) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk))\n }\n\n return Buffer.concat(chunks)\n }\n\n close(): void {\n if (this.ws && this.ws.readyState !== WebSocket.CLOSED) {\n this.ws.close(1000, 'channel_close')\n }\n }\n}\n\nfunction buildChannelUrl(\n engineWsBase: string,\n channelId: string,\n accessKey: string,\n direction: 'read' | 'write',\n): string {\n const base = engineWsBase.replace(/\\/$/, '')\n return `${base}/ws/channels/${channelId}?key=${encodeURIComponent(accessKey)}&dir=${direction}`\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"channels-Di68FfqY.mjs","names":[],"sources":["../src/channels.ts"],"sourcesContent":["import { Readable, Writable } from 'node:stream'\nimport { WebSocket } from 'ws'\nimport { WS_HANDSHAKE_TIMEOUT_MS } from './iii-constants'\nimport type { StreamChannelRef } from './iii-types'\n\n/**\n * Direction of a streaming channel endpoint. Mirrors the Rust SDK's\n * `ChannelDirection` enum and matches the literal values used by\n * {@link StreamChannelRef.direction}.\n */\nexport const ChannelDirection = {\n Read: 'read',\n Write: 'write',\n} as const\nexport type ChannelDirection = (typeof ChannelDirection)[keyof typeof ChannelDirection]\n\n/**\n * Discriminated runtime tag for an item observed on a streaming channel.\n * Mirrors the Rust SDK's `ChannelItem` enum (`Text` / `Binary`). Carrier for\n * factory + type-guard helpers so callers can construct and discriminate\n * channel items without depending on Rust-specific shape.\n */\nexport type ChannelItem =\n | { type: 'text'; value: string }\n | { type: 'binary'; value: Uint8Array }\n\nexport const ChannelItem = {\n /** Construct a text channel item. */\n Text(value: string): ChannelItem {\n return { type: 'text', value }\n },\n /** Construct a binary channel item. */\n Binary(value: Uint8Array): ChannelItem {\n return { type: 'binary', value }\n },\n} as const\n\n/**\n * Write end of a streaming channel. Provides both a Node.js `Writable` stream\n * and a `sendMessage` method for sending structured text messages.\n *\n * @example\n * ```typescript\n * import { createChannel } from 'iii-sdk/helpers'\n * const channel = await createChannel(iii)\n *\n * // Stream binary data\n * channel.writer.stream.write(Buffer.from('hello'))\n * channel.writer.stream.end()\n *\n * // Or send text messages\n * channel.writer.sendMessage(JSON.stringify({ type: 'event', data: 'test' }))\n * channel.writer.close()\n * ```\n */\nexport class ChannelWriter {\n private static readonly FRAME_SIZE = 64 * 1024\n private ws: WebSocket | null = null\n private wsReady = false\n private readonly pendingMessages: {\n data: Buffer | string\n callback: (err?: Error | null) => void\n }[] = []\n /** Node.js Writable stream for binary data. */\n public readonly stream: Writable\n private readonly url: string\n\n constructor(engineWsBase: string, ref: StreamChannelRef) {\n this.url = buildChannelUrl(engineWsBase, ref.channel_id, ref.access_key, 'write')\n\n this.stream = new Writable({\n write: (chunk: Buffer, _encoding, callback) => {\n const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)\n this.sendChunked(buf, callback)\n },\n final: (callback) => {\n if (!this.ws) {\n callback()\n return\n }\n // Delay the close frame slightly to allow the TCP stack to flush\n // all buffered send() data. Without this, the close frame can arrive\n // at the engine before all data frames, causing data truncation.\n const doClose = () => {\n if (this.ws) {\n this.ws.close(1000, 'stream_complete')\n }\n callback()\n }\n if (this.wsReady) {\n setTimeout(doClose, 10)\n } else {\n this.ws.on('open', () => setTimeout(doClose, 10))\n }\n },\n destroy: (err, callback) => {\n if (this.ws) this.ws.terminate()\n callback(err)\n },\n })\n }\n\n private ensureConnected(): void {\n // A non-null socket is only reusable while it is still connecting or open.\n // A CLOSING/CLOSED socket is dead and must be replaced — guarding solely on\n // `this.ws` being non-null would pin a terminated socket forever, so every\n // subsequent send would target a connection that can never deliver it.\n if (\n this.ws &&\n this.ws.readyState !== WebSocket.CLOSING &&\n this.ws.readyState !== WebSocket.CLOSED\n ) {\n return\n }\n this.wsReady = false\n this.ws = new WebSocket(this.url, { handshakeTimeout: WS_HANDSHAKE_TIMEOUT_MS })\n\n this.ws.on('open', () => {\n this.wsReady = true\n for (const { data, callback } of this.pendingMessages) {\n this.ws?.send(data, callback)\n }\n this.pendingMessages.length = 0\n })\n\n this.ws.on('error', (err) => {\n this.wsReady = false\n this.stream.destroy(err)\n })\n\n this.ws.on('close', () => {\n this.wsReady = false\n if (!this.stream.destroyed) {\n this.stream.destroy()\n }\n })\n }\n\n /** Send a text message through the channel. */\n sendMessage(msg: string): void {\n this.ensureConnected()\n this.sendRaw(msg, (err) => {\n if (err) this.stream.destroy(err)\n })\n }\n\n /** Close the channel writer. */\n close(): void {\n if (!this.ws) return\n const doClose = () => {\n if (this.ws) {\n this.ws.close(1000, 'channel_close')\n }\n }\n if (this.wsReady) {\n doClose()\n } else {\n this.ws.on('open', () => doClose())\n }\n }\n\n private sendChunked(data: Buffer, callback: (err?: Error | null) => void): void {\n let offset = 0\n const sendNext = (err?: Error | null): void => {\n if (err) {\n callback(err)\n return\n }\n\n if (offset >= data.length) {\n callback(null)\n return\n }\n\n const end = Math.min(offset + ChannelWriter.FRAME_SIZE, data.length)\n const part = data.subarray(offset, end)\n offset = end\n this.sendRaw(part, sendNext)\n }\n sendNext(null)\n }\n\n private sendRaw(data: Buffer | string, callback: (err?: Error | null) => void): void {\n this.ensureConnected()\n // Trust the socket's live readyState over the tracked `wsReady` flag, which\n // can stay true after a drop. Queue anything we cannot send right now — and\n // requeue a send() that throws because the socket died underneath us — so it\n // flushes on the next open instead of being lost or throwing to the caller.\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n try {\n this.ws.send(data, (err) => callback(err ?? null))\n } catch {\n this.pendingMessages.push({ data, callback })\n }\n } else {\n this.pendingMessages.push({ data, callback })\n }\n }\n}\n\n/**\n * Read end of a streaming channel. Provides both a Node.js `Readable` stream\n * for binary data and an `onMessage` callback for structured text messages.\n *\n * @example\n * ```typescript\n * import { createChannel } from 'iii-sdk/helpers'\n * const channel = await createChannel(iii)\n *\n * // Stream binary data\n * channel.reader.stream.on('data', (chunk) => console.log(chunk))\n *\n * // Or receive text messages\n * channel.reader.onMessage((msg) => console.log('Got:', msg))\n * ```\n */\nexport class ChannelReader {\n private ws: WebSocket | null = null\n private connected = false\n private readonly messageCallbacks: Array<(msg: string) => void> = []\n /** Node.js Readable stream for binary data. */\n public readonly stream: Readable\n private readonly url: string\n\n constructor(engineWsBase: string, ref: StreamChannelRef) {\n this.url = buildChannelUrl(engineWsBase, ref.channel_id, ref.access_key, 'read')\n\n const self = this\n this.stream = new Readable({\n read() {\n self.ensureConnected()\n if (self.ws) self.ws.resume()\n },\n destroy(err, callback) {\n if (self.ws && self.ws.readyState !== WebSocket.CLOSED) {\n self.ws.terminate()\n }\n self.ws = null\n callback(err)\n },\n })\n }\n\n private ensureConnected(): void {\n if (this.connected) return\n this.connected = true\n this.ws = new WebSocket(this.url, { handshakeTimeout: WS_HANDSHAKE_TIMEOUT_MS })\n\n this.ws.on('open', () => {\n ;(this.ws as unknown as { binaryType: string }).binaryType = 'nodebuffer'\n })\n\n this.ws.on('message', (data: Buffer, isBinary: boolean) => {\n if (isBinary) {\n if (!this.stream.push(data)) {\n this.ws?.pause()\n }\n } else {\n const msg = data.toString('utf-8')\n for (const cb of this.messageCallbacks) {\n cb(msg)\n }\n }\n })\n\n this.ws.on('close', () => {\n this.ws = null\n if (!this.stream.destroyed) this.stream.push(null)\n })\n\n this.ws.on('error', (err) => {\n this.stream.destroy(err)\n })\n }\n\n /** Register a callback to receive text messages from the channel. */\n onMessage(callback: (msg: string) => void): void {\n this.messageCallbacks.push(callback)\n }\n\n async readAll(): Promise<Buffer> {\n this.ensureConnected()\n const chunks: Buffer[] = []\n\n for await (const chunk of this.stream) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk))\n }\n\n return Buffer.concat(chunks)\n }\n\n close(): void {\n if (this.ws && this.ws.readyState !== WebSocket.CLOSED) {\n this.ws.close(1000, 'channel_close')\n }\n }\n}\n\nfunction buildChannelUrl(\n engineWsBase: string,\n channelId: string,\n accessKey: string,\n direction: 'read' | 'write',\n): string {\n const base = engineWsBase.replace(/\\/$/, '')\n return `${base}/ws/channels/${channelId}?key=${encodeURIComponent(accessKey)}&dir=${direction}`\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_utils = require('./utils-
|
|
1
|
+
const require_utils = require('./utils-SpwzEB77.cjs');
|
|
2
2
|
const require_iii_constants = require('./iii-constants-M_BGfxGD.cjs');
|
|
3
3
|
let ws = require("ws");
|
|
4
4
|
let node_stream = require("node:stream");
|
|
@@ -14,12 +14,14 @@ const ChannelDirection = {
|
|
|
14
14
|
Write: "write"
|
|
15
15
|
};
|
|
16
16
|
const ChannelItem = {
|
|
17
|
+
/** Construct a text channel item. */
|
|
17
18
|
Text(value) {
|
|
18
19
|
return {
|
|
19
20
|
type: "text",
|
|
20
21
|
value
|
|
21
22
|
};
|
|
22
23
|
},
|
|
24
|
+
/** Construct a binary channel item. */
|
|
23
25
|
Binary(value) {
|
|
24
26
|
return {
|
|
25
27
|
type: "binary",
|
|
@@ -246,4 +248,4 @@ Object.defineProperty(exports, 'ChannelWriter', {
|
|
|
246
248
|
return ChannelWriter;
|
|
247
249
|
}
|
|
248
250
|
});
|
|
249
|
-
//# sourceMappingURL=channels-
|
|
251
|
+
//# sourceMappingURL=channels-Dn3OKXH5.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"channels-C2ezAJ_g.cjs","names":["Writable","WebSocket","WS_HANDSHAKE_TIMEOUT_MS","Readable"],"sources":["../src/channels.ts"],"sourcesContent":["import { Readable, Writable } from 'node:stream'\nimport { WebSocket } from 'ws'\nimport { WS_HANDSHAKE_TIMEOUT_MS } from './iii-constants'\nimport type { StreamChannelRef } from './iii-types'\n\n/**\n * Direction of a streaming channel endpoint. Mirrors the Rust SDK's\n * `ChannelDirection` enum and matches the literal values used by\n * {@link StreamChannelRef.direction}.\n */\nexport const ChannelDirection = {\n Read: 'read',\n Write: 'write',\n} as const\nexport type ChannelDirection = (typeof ChannelDirection)[keyof typeof ChannelDirection]\n\n/**\n * Discriminated runtime tag for an item observed on a streaming channel.\n * Mirrors the Rust SDK's `ChannelItem` enum (`Text` / `Binary`). Carrier for\n * factory + type-guard helpers so callers can construct and discriminate\n * channel items without depending on Rust-specific shape.\n */\nexport type ChannelItem =\n | { type: 'text'; value: string }\n | { type: 'binary'; value: Uint8Array }\n\nexport const ChannelItem = {\n /** Construct a text channel item. */\n Text(value: string): ChannelItem {\n return { type: 'text', value }\n },\n /** Construct a binary channel item. */\n Binary(value: Uint8Array): ChannelItem {\n return { type: 'binary', value }\n },\n} as const\n\n/**\n * Write end of a streaming channel. Provides both a Node.js `Writable` stream\n * and a `sendMessage` method for sending structured text messages.\n *\n * @example\n * ```typescript\n * import { createChannel } from 'iii-sdk/helpers'\n * const channel = await createChannel(iii)\n *\n * // Stream binary data\n * channel.writer.stream.write(Buffer.from('hello'))\n * channel.writer.stream.end()\n *\n * // Or send text messages\n * channel.writer.sendMessage(JSON.stringify({ type: 'event', data: 'test' }))\n * channel.writer.close()\n * ```\n */\nexport class ChannelWriter {\n private static readonly FRAME_SIZE = 64 * 1024\n private ws: WebSocket | null = null\n private wsReady = false\n private readonly pendingMessages: {\n data: Buffer | string\n callback: (err?: Error | null) => void\n }[] = []\n /** Node.js Writable stream for binary data. */\n public readonly stream: Writable\n private readonly url: string\n\n constructor(engineWsBase: string, ref: StreamChannelRef) {\n this.url = buildChannelUrl(engineWsBase, ref.channel_id, ref.access_key, 'write')\n\n this.stream = new Writable({\n write: (chunk: Buffer, _encoding, callback) => {\n const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)\n this.sendChunked(buf, callback)\n },\n final: (callback) => {\n if (!this.ws) {\n callback()\n return\n }\n // Delay the close frame slightly to allow the TCP stack to flush\n // all buffered send() data. Without this, the close frame can arrive\n // at the engine before all data frames, causing data truncation.\n const doClose = () => {\n if (this.ws) {\n this.ws.close(1000, 'stream_complete')\n }\n callback()\n }\n if (this.wsReady) {\n setTimeout(doClose, 10)\n } else {\n this.ws.on('open', () => setTimeout(doClose, 10))\n }\n },\n destroy: (err, callback) => {\n if (this.ws) this.ws.terminate()\n callback(err)\n },\n })\n }\n\n private ensureConnected(): void {\n // A non-null socket is only reusable while it is still connecting or open.\n // A CLOSING/CLOSED socket is dead and must be replaced — guarding solely on\n // `this.ws` being non-null would pin a terminated socket forever, so every\n // subsequent send would target a connection that can never deliver it.\n if (\n this.ws &&\n this.ws.readyState !== WebSocket.CLOSING &&\n this.ws.readyState !== WebSocket.CLOSED\n ) {\n return\n }\n this.wsReady = false\n this.ws = new WebSocket(this.url, { handshakeTimeout: WS_HANDSHAKE_TIMEOUT_MS })\n\n this.ws.on('open', () => {\n this.wsReady = true\n for (const { data, callback } of this.pendingMessages) {\n this.ws?.send(data, callback)\n }\n this.pendingMessages.length = 0\n })\n\n this.ws.on('error', (err) => {\n this.wsReady = false\n this.stream.destroy(err)\n })\n\n this.ws.on('close', () => {\n this.wsReady = false\n if (!this.stream.destroyed) {\n this.stream.destroy()\n }\n })\n }\n\n /** Send a text message through the channel. */\n sendMessage(msg: string): void {\n this.ensureConnected()\n this.sendRaw(msg, (err) => {\n if (err) this.stream.destroy(err)\n })\n }\n\n /** Close the channel writer. */\n close(): void {\n if (!this.ws) return\n const doClose = () => {\n if (this.ws) {\n this.ws.close(1000, 'channel_close')\n }\n }\n if (this.wsReady) {\n doClose()\n } else {\n this.ws.on('open', () => doClose())\n }\n }\n\n private sendChunked(data: Buffer, callback: (err?: Error | null) => void): void {\n let offset = 0\n const sendNext = (err?: Error | null): void => {\n if (err) {\n callback(err)\n return\n }\n\n if (offset >= data.length) {\n callback(null)\n return\n }\n\n const end = Math.min(offset + ChannelWriter.FRAME_SIZE, data.length)\n const part = data.subarray(offset, end)\n offset = end\n this.sendRaw(part, sendNext)\n }\n sendNext(null)\n }\n\n private sendRaw(data: Buffer | string, callback: (err?: Error | null) => void): void {\n this.ensureConnected()\n // Trust the socket's live readyState over the tracked `wsReady` flag, which\n // can stay true after a drop. Queue anything we cannot send right now — and\n // requeue a send() that throws because the socket died underneath us — so it\n // flushes on the next open instead of being lost or throwing to the caller.\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n try {\n this.ws.send(data, (err) => callback(err ?? null))\n } catch {\n this.pendingMessages.push({ data, callback })\n }\n } else {\n this.pendingMessages.push({ data, callback })\n }\n }\n}\n\n/**\n * Read end of a streaming channel. Provides both a Node.js `Readable` stream\n * for binary data and an `onMessage` callback for structured text messages.\n *\n * @example\n * ```typescript\n * import { createChannel } from 'iii-sdk/helpers'\n * const channel = await createChannel(iii)\n *\n * // Stream binary data\n * channel.reader.stream.on('data', (chunk) => console.log(chunk))\n *\n * // Or receive text messages\n * channel.reader.onMessage((msg) => console.log('Got:', msg))\n * ```\n */\nexport class ChannelReader {\n private ws: WebSocket | null = null\n private connected = false\n private readonly messageCallbacks: Array<(msg: string) => void> = []\n /** Node.js Readable stream for binary data. */\n public readonly stream: Readable\n private readonly url: string\n\n constructor(engineWsBase: string, ref: StreamChannelRef) {\n this.url = buildChannelUrl(engineWsBase, ref.channel_id, ref.access_key, 'read')\n\n const self = this\n this.stream = new Readable({\n read() {\n self.ensureConnected()\n if (self.ws) self.ws.resume()\n },\n destroy(err, callback) {\n if (self.ws && self.ws.readyState !== WebSocket.CLOSED) {\n self.ws.terminate()\n }\n self.ws = null\n callback(err)\n },\n })\n }\n\n private ensureConnected(): void {\n if (this.connected) return\n this.connected = true\n this.ws = new WebSocket(this.url, { handshakeTimeout: WS_HANDSHAKE_TIMEOUT_MS })\n\n this.ws.on('open', () => {\n ;(this.ws as unknown as { binaryType: string }).binaryType = 'nodebuffer'\n })\n\n this.ws.on('message', (data: Buffer, isBinary: boolean) => {\n if (isBinary) {\n if (!this.stream.push(data)) {\n this.ws?.pause()\n }\n } else {\n const msg = data.toString('utf-8')\n for (const cb of this.messageCallbacks) {\n cb(msg)\n }\n }\n })\n\n this.ws.on('close', () => {\n this.ws = null\n if (!this.stream.destroyed) this.stream.push(null)\n })\n\n this.ws.on('error', (err) => {\n this.stream.destroy(err)\n })\n }\n\n /** Register a callback to receive text messages from the channel. */\n onMessage(callback: (msg: string) => void): void {\n this.messageCallbacks.push(callback)\n }\n\n async readAll(): Promise<Buffer> {\n this.ensureConnected()\n const chunks: Buffer[] = []\n\n for await (const chunk of this.stream) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk))\n }\n\n return Buffer.concat(chunks)\n }\n\n close(): void {\n if (this.ws && this.ws.readyState !== WebSocket.CLOSED) {\n this.ws.close(1000, 'channel_close')\n }\n }\n}\n\nfunction buildChannelUrl(\n engineWsBase: string,\n channelId: string,\n accessKey: string,\n direction: 'read' | 'write',\n): string {\n const base = engineWsBase.replace(/\\/$/, '')\n return `${base}/ws/channels/${channelId}?key=${encodeURIComponent(accessKey)}&dir=${direction}`\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"channels-Dn3OKXH5.cjs","names":["Writable","WebSocket","WS_HANDSHAKE_TIMEOUT_MS","Readable"],"sources":["../src/channels.ts"],"sourcesContent":["import { Readable, Writable } from 'node:stream'\nimport { WebSocket } from 'ws'\nimport { WS_HANDSHAKE_TIMEOUT_MS } from './iii-constants'\nimport type { StreamChannelRef } from './iii-types'\n\n/**\n * Direction of a streaming channel endpoint. Mirrors the Rust SDK's\n * `ChannelDirection` enum and matches the literal values used by\n * {@link StreamChannelRef.direction}.\n */\nexport const ChannelDirection = {\n Read: 'read',\n Write: 'write',\n} as const\nexport type ChannelDirection = (typeof ChannelDirection)[keyof typeof ChannelDirection]\n\n/**\n * Discriminated runtime tag for an item observed on a streaming channel.\n * Mirrors the Rust SDK's `ChannelItem` enum (`Text` / `Binary`). Carrier for\n * factory + type-guard helpers so callers can construct and discriminate\n * channel items without depending on Rust-specific shape.\n */\nexport type ChannelItem =\n | { type: 'text'; value: string }\n | { type: 'binary'; value: Uint8Array }\n\nexport const ChannelItem = {\n /** Construct a text channel item. */\n Text(value: string): ChannelItem {\n return { type: 'text', value }\n },\n /** Construct a binary channel item. */\n Binary(value: Uint8Array): ChannelItem {\n return { type: 'binary', value }\n },\n} as const\n\n/**\n * Write end of a streaming channel. Provides both a Node.js `Writable` stream\n * and a `sendMessage` method for sending structured text messages.\n *\n * @example\n * ```typescript\n * import { createChannel } from 'iii-sdk/helpers'\n * const channel = await createChannel(iii)\n *\n * // Stream binary data\n * channel.writer.stream.write(Buffer.from('hello'))\n * channel.writer.stream.end()\n *\n * // Or send text messages\n * channel.writer.sendMessage(JSON.stringify({ type: 'event', data: 'test' }))\n * channel.writer.close()\n * ```\n */\nexport class ChannelWriter {\n private static readonly FRAME_SIZE = 64 * 1024\n private ws: WebSocket | null = null\n private wsReady = false\n private readonly pendingMessages: {\n data: Buffer | string\n callback: (err?: Error | null) => void\n }[] = []\n /** Node.js Writable stream for binary data. */\n public readonly stream: Writable\n private readonly url: string\n\n constructor(engineWsBase: string, ref: StreamChannelRef) {\n this.url = buildChannelUrl(engineWsBase, ref.channel_id, ref.access_key, 'write')\n\n this.stream = new Writable({\n write: (chunk: Buffer, _encoding, callback) => {\n const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)\n this.sendChunked(buf, callback)\n },\n final: (callback) => {\n if (!this.ws) {\n callback()\n return\n }\n // Delay the close frame slightly to allow the TCP stack to flush\n // all buffered send() data. Without this, the close frame can arrive\n // at the engine before all data frames, causing data truncation.\n const doClose = () => {\n if (this.ws) {\n this.ws.close(1000, 'stream_complete')\n }\n callback()\n }\n if (this.wsReady) {\n setTimeout(doClose, 10)\n } else {\n this.ws.on('open', () => setTimeout(doClose, 10))\n }\n },\n destroy: (err, callback) => {\n if (this.ws) this.ws.terminate()\n callback(err)\n },\n })\n }\n\n private ensureConnected(): void {\n // A non-null socket is only reusable while it is still connecting or open.\n // A CLOSING/CLOSED socket is dead and must be replaced — guarding solely on\n // `this.ws` being non-null would pin a terminated socket forever, so every\n // subsequent send would target a connection that can never deliver it.\n if (\n this.ws &&\n this.ws.readyState !== WebSocket.CLOSING &&\n this.ws.readyState !== WebSocket.CLOSED\n ) {\n return\n }\n this.wsReady = false\n this.ws = new WebSocket(this.url, { handshakeTimeout: WS_HANDSHAKE_TIMEOUT_MS })\n\n this.ws.on('open', () => {\n this.wsReady = true\n for (const { data, callback } of this.pendingMessages) {\n this.ws?.send(data, callback)\n }\n this.pendingMessages.length = 0\n })\n\n this.ws.on('error', (err) => {\n this.wsReady = false\n this.stream.destroy(err)\n })\n\n this.ws.on('close', () => {\n this.wsReady = false\n if (!this.stream.destroyed) {\n this.stream.destroy()\n }\n })\n }\n\n /** Send a text message through the channel. */\n sendMessage(msg: string): void {\n this.ensureConnected()\n this.sendRaw(msg, (err) => {\n if (err) this.stream.destroy(err)\n })\n }\n\n /** Close the channel writer. */\n close(): void {\n if (!this.ws) return\n const doClose = () => {\n if (this.ws) {\n this.ws.close(1000, 'channel_close')\n }\n }\n if (this.wsReady) {\n doClose()\n } else {\n this.ws.on('open', () => doClose())\n }\n }\n\n private sendChunked(data: Buffer, callback: (err?: Error | null) => void): void {\n let offset = 0\n const sendNext = (err?: Error | null): void => {\n if (err) {\n callback(err)\n return\n }\n\n if (offset >= data.length) {\n callback(null)\n return\n }\n\n const end = Math.min(offset + ChannelWriter.FRAME_SIZE, data.length)\n const part = data.subarray(offset, end)\n offset = end\n this.sendRaw(part, sendNext)\n }\n sendNext(null)\n }\n\n private sendRaw(data: Buffer | string, callback: (err?: Error | null) => void): void {\n this.ensureConnected()\n // Trust the socket's live readyState over the tracked `wsReady` flag, which\n // can stay true after a drop. Queue anything we cannot send right now — and\n // requeue a send() that throws because the socket died underneath us — so it\n // flushes on the next open instead of being lost or throwing to the caller.\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n try {\n this.ws.send(data, (err) => callback(err ?? null))\n } catch {\n this.pendingMessages.push({ data, callback })\n }\n } else {\n this.pendingMessages.push({ data, callback })\n }\n }\n}\n\n/**\n * Read end of a streaming channel. Provides both a Node.js `Readable` stream\n * for binary data and an `onMessage` callback for structured text messages.\n *\n * @example\n * ```typescript\n * import { createChannel } from 'iii-sdk/helpers'\n * const channel = await createChannel(iii)\n *\n * // Stream binary data\n * channel.reader.stream.on('data', (chunk) => console.log(chunk))\n *\n * // Or receive text messages\n * channel.reader.onMessage((msg) => console.log('Got:', msg))\n * ```\n */\nexport class ChannelReader {\n private ws: WebSocket | null = null\n private connected = false\n private readonly messageCallbacks: Array<(msg: string) => void> = []\n /** Node.js Readable stream for binary data. */\n public readonly stream: Readable\n private readonly url: string\n\n constructor(engineWsBase: string, ref: StreamChannelRef) {\n this.url = buildChannelUrl(engineWsBase, ref.channel_id, ref.access_key, 'read')\n\n const self = this\n this.stream = new Readable({\n read() {\n self.ensureConnected()\n if (self.ws) self.ws.resume()\n },\n destroy(err, callback) {\n if (self.ws && self.ws.readyState !== WebSocket.CLOSED) {\n self.ws.terminate()\n }\n self.ws = null\n callback(err)\n },\n })\n }\n\n private ensureConnected(): void {\n if (this.connected) return\n this.connected = true\n this.ws = new WebSocket(this.url, { handshakeTimeout: WS_HANDSHAKE_TIMEOUT_MS })\n\n this.ws.on('open', () => {\n ;(this.ws as unknown as { binaryType: string }).binaryType = 'nodebuffer'\n })\n\n this.ws.on('message', (data: Buffer, isBinary: boolean) => {\n if (isBinary) {\n if (!this.stream.push(data)) {\n this.ws?.pause()\n }\n } else {\n const msg = data.toString('utf-8')\n for (const cb of this.messageCallbacks) {\n cb(msg)\n }\n }\n })\n\n this.ws.on('close', () => {\n this.ws = null\n if (!this.stream.destroyed) this.stream.push(null)\n })\n\n this.ws.on('error', (err) => {\n this.stream.destroy(err)\n })\n }\n\n /** Register a callback to receive text messages from the channel. */\n onMessage(callback: (msg: string) => void): void {\n this.messageCallbacks.push(callback)\n }\n\n async readAll(): Promise<Buffer> {\n this.ensureConnected()\n const chunks: Buffer[] = []\n\n for await (const chunk of this.stream) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk))\n }\n\n return Buffer.concat(chunks)\n }\n\n close(): void {\n if (this.ws && this.ws.readyState !== WebSocket.CLOSED) {\n this.ws.close(1000, 'channel_close')\n }\n }\n}\n\nfunction buildChannelUrl(\n engineWsBase: string,\n channelId: string,\n accessKey: string,\n direction: 'read' | 'write',\n): string {\n const base = engineWsBase.replace(/\\/$/, '')\n return `${base}/ws/channels/${channelId}?key=${encodeURIComponent(accessKey)}&dir=${direction}`\n}\n"],"mappings":"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"}
|
package/dist/helpers.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
-
const require_utils = require('./utils-
|
|
3
|
-
const require_channels = require('./channels-
|
|
2
|
+
const require_utils = require('./utils-SpwzEB77.cjs');
|
|
3
|
+
const require_channels = require('./channels-Dn3OKXH5.cjs');
|
|
4
4
|
|
|
5
5
|
//#region src/helpers.ts
|
|
6
6
|
/**
|
package/dist/helpers.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as ChannelItem, t as ChannelDirection } from "./channels-
|
|
1
|
+
import { n as ChannelItem, t as ChannelDirection } from "./channels-Di68FfqY.mjs";
|
|
2
2
|
import { n as extractChannelRefs, r as isChannelRef } from "./utils-8G4hHqvZ.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/helpers.ts
|
package/dist/index.cjs
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
-
const require_utils = require('./utils-
|
|
2
|
+
const require_utils = require('./utils-SpwzEB77.cjs');
|
|
3
3
|
const require_errors = require('./errors.cjs');
|
|
4
4
|
const require_iii_constants = require('./iii-constants-M_BGfxGD.cjs');
|
|
5
|
-
const require_channels = require('./channels-
|
|
5
|
+
const require_channels = require('./channels-Dn3OKXH5.cjs');
|
|
6
6
|
const require_iii_types = require('./iii-types-BHasLgj1.cjs');
|
|
7
7
|
let _opentelemetry_api = require("@opentelemetry/api");
|
|
8
8
|
let node_module = require("node:module");
|
|
9
9
|
let node_os = require("node:os");
|
|
10
|
-
node_os = require_utils.__toESM(node_os);
|
|
10
|
+
node_os = require_utils.__toESM(node_os, 1);
|
|
11
11
|
let ws = require("ws");
|
|
12
12
|
let _iii_dev_helpers_observability = require("@iii-dev/helpers/observability");
|
|
13
13
|
let _iii_dev_helpers_observability_internal = require("@iii-dev/helpers/observability/internal");
|
|
@@ -84,11 +84,11 @@ var Sdk = class {
|
|
|
84
84
|
this.connectionState = "disconnected";
|
|
85
85
|
this.isShuttingDown = false;
|
|
86
86
|
this.registerTriggerType = (triggerType, handler) => {
|
|
87
|
-
this.sendMessage(
|
|
87
|
+
this.sendMessage("registertriggertype", triggerType, true);
|
|
88
88
|
this.triggerTypes.set(triggerType.id, {
|
|
89
89
|
message: {
|
|
90
90
|
...triggerType,
|
|
91
|
-
message_type:
|
|
91
|
+
message_type: "registertriggertype"
|
|
92
92
|
},
|
|
93
93
|
handler
|
|
94
94
|
});
|
|
@@ -120,7 +120,7 @@ var Sdk = class {
|
|
|
120
120
|
};
|
|
121
121
|
};
|
|
122
122
|
this.unregisterTriggerType = (triggerType) => {
|
|
123
|
-
this.sendMessage(
|
|
123
|
+
this.sendMessage("unregistertriggertype", triggerType, true);
|
|
124
124
|
this.triggerTypes.delete(triggerType.id);
|
|
125
125
|
};
|
|
126
126
|
this.registerTrigger = (trigger) => {
|
|
@@ -128,18 +128,18 @@ var Sdk = class {
|
|
|
128
128
|
const fullTrigger = {
|
|
129
129
|
...trigger,
|
|
130
130
|
id,
|
|
131
|
-
message_type:
|
|
131
|
+
message_type: "registertrigger",
|
|
132
132
|
...(() => {
|
|
133
133
|
const namespace = callNamespace(trigger.namespace, this.namespace, "RegisterTriggerInput.namespace");
|
|
134
134
|
return namespace !== void 0 ? { namespace } : {};
|
|
135
135
|
})()
|
|
136
136
|
};
|
|
137
|
-
this.sendMessage(
|
|
137
|
+
this.sendMessage("registertrigger", fullTrigger, true);
|
|
138
138
|
this.triggers.set(id, fullTrigger);
|
|
139
139
|
return { unregister: () => {
|
|
140
|
-
this.sendMessage(
|
|
140
|
+
this.sendMessage("unregistertrigger", {
|
|
141
141
|
id,
|
|
142
|
-
message_type:
|
|
142
|
+
message_type: "unregistertrigger",
|
|
143
143
|
type: fullTrigger.type
|
|
144
144
|
});
|
|
145
145
|
this.triggers.delete(id);
|
|
@@ -152,11 +152,11 @@ var Sdk = class {
|
|
|
152
152
|
const fullMessage = isHandler ? {
|
|
153
153
|
...options,
|
|
154
154
|
id: functionId,
|
|
155
|
-
message_type:
|
|
155
|
+
message_type: "registerfunction"
|
|
156
156
|
} : {
|
|
157
157
|
...options,
|
|
158
158
|
id: functionId,
|
|
159
|
-
message_type:
|
|
159
|
+
message_type: "registerfunction",
|
|
160
160
|
invocation: {
|
|
161
161
|
url: handlerOrInvocation.url,
|
|
162
162
|
method: handlerOrInvocation.method ?? "POST",
|
|
@@ -165,7 +165,7 @@ var Sdk = class {
|
|
|
165
165
|
auth: handlerOrInvocation.auth
|
|
166
166
|
}
|
|
167
167
|
};
|
|
168
|
-
this.sendMessage(
|
|
168
|
+
this.sendMessage("registerfunction", fullMessage, true);
|
|
169
169
|
if (isHandler) {
|
|
170
170
|
const handler = handlerOrInvocation;
|
|
171
171
|
this.functions.set(functionId, {
|
|
@@ -222,7 +222,7 @@ var Sdk = class {
|
|
|
222
222
|
return {
|
|
223
223
|
id: functionId,
|
|
224
224
|
unregister: () => {
|
|
225
|
-
this.sendMessage(
|
|
225
|
+
this.sendMessage("unregisterfunction", { id: functionId }, true);
|
|
226
226
|
this.functions.delete(functionId);
|
|
227
227
|
}
|
|
228
228
|
};
|
|
@@ -246,7 +246,7 @@ var Sdk = class {
|
|
|
246
246
|
if (action?.type === "void") {
|
|
247
247
|
const traceparent = (0, _iii_dev_helpers_observability.injectTraceparent)();
|
|
248
248
|
const baggage = (0, _iii_dev_helpers_observability.injectBaggage)();
|
|
249
|
-
this.sendMessage(
|
|
249
|
+
this.sendMessage("invokefunction", {
|
|
250
250
|
function_id,
|
|
251
251
|
data: payload,
|
|
252
252
|
traceparent,
|
|
@@ -283,7 +283,7 @@ var Sdk = class {
|
|
|
283
283
|
function_id,
|
|
284
284
|
timeout
|
|
285
285
|
});
|
|
286
|
-
this.sendMessage(
|
|
286
|
+
this.sendMessage("invokefunction", {
|
|
287
287
|
invocation_id,
|
|
288
288
|
function_id,
|
|
289
289
|
data: payload,
|
|
@@ -466,24 +466,24 @@ var Sdk = class {
|
|
|
466
466
|
this.ws?.on("pong", touch);
|
|
467
467
|
this.startHeartbeat();
|
|
468
468
|
if (this.workerId) this.sendMessageRaw(JSON.stringify({
|
|
469
|
-
type:
|
|
469
|
+
type: "reattach",
|
|
470
470
|
previous_worker_id: this.workerId,
|
|
471
471
|
reattach_token: this.reattachToken
|
|
472
472
|
}));
|
|
473
473
|
this.registerWorkerMetadata();
|
|
474
474
|
this.triggerTypes.forEach(({ message }) => {
|
|
475
|
-
this.sendMessage(
|
|
475
|
+
this.sendMessage("registertriggertype", message, true);
|
|
476
476
|
});
|
|
477
477
|
this.functions.forEach(({ message }) => {
|
|
478
|
-
this.sendMessage(
|
|
478
|
+
this.sendMessage("registerfunction", message, true);
|
|
479
479
|
});
|
|
480
480
|
this.triggers.forEach((trigger) => {
|
|
481
|
-
this.sendMessage(
|
|
481
|
+
this.sendMessage("registertrigger", trigger, true);
|
|
482
482
|
});
|
|
483
483
|
const pending = this.messagesToSend;
|
|
484
484
|
this.messagesToSend = [];
|
|
485
485
|
for (const message of pending) {
|
|
486
|
-
if (message.type ===
|
|
486
|
+
if (message.type === "invokefunction" && typeof message.invocation_id === "string" && !this.invocations.has(message.invocation_id)) continue;
|
|
487
487
|
this.sendMessageRaw(JSON.stringify(message));
|
|
488
488
|
}
|
|
489
489
|
}
|
|
@@ -501,7 +501,7 @@ var Sdk = class {
|
|
|
501
501
|
}
|
|
502
502
|
toWireFormat(messageType, message) {
|
|
503
503
|
const { message_type: _, ...rest } = message;
|
|
504
|
-
if (messageType ===
|
|
504
|
+
if (messageType === "registertrigger" && "type" in message) {
|
|
505
505
|
const { type: triggerType, ...triggerRest } = message;
|
|
506
506
|
return {
|
|
507
507
|
type: messageType,
|
|
@@ -509,7 +509,7 @@ var Sdk = class {
|
|
|
509
509
|
trigger_type: triggerType
|
|
510
510
|
};
|
|
511
511
|
}
|
|
512
|
-
if (messageType ===
|
|
512
|
+
if (messageType === "unregistertrigger" && "type" in message) {
|
|
513
513
|
const { type: triggerType, ...triggerRest } = message;
|
|
514
514
|
return {
|
|
515
515
|
type: messageType,
|
|
@@ -517,7 +517,7 @@ var Sdk = class {
|
|
|
517
517
|
trigger_type: triggerType
|
|
518
518
|
};
|
|
519
519
|
}
|
|
520
|
-
if (messageType ===
|
|
520
|
+
if (messageType === "triggerregistrationresult" && "type" in message) {
|
|
521
521
|
const { type: triggerType, ...resultRest } = message;
|
|
522
522
|
return {
|
|
523
523
|
type: messageType,
|
|
@@ -650,7 +650,7 @@ var Sdk = class {
|
|
|
650
650
|
}
|
|
651
651
|
try {
|
|
652
652
|
const result = await fn.handler(resolvedInput, metadata, traceparent, baggage);
|
|
653
|
-
this.sendMessage(
|
|
653
|
+
this.sendMessage("invocationresult", {
|
|
654
654
|
invocation_id,
|
|
655
655
|
function_id,
|
|
656
656
|
result,
|
|
@@ -659,7 +659,7 @@ var Sdk = class {
|
|
|
659
659
|
});
|
|
660
660
|
} catch (error) {
|
|
661
661
|
const isError = error instanceof Error;
|
|
662
|
-
this.sendMessage(
|
|
662
|
+
this.sendMessage("invocationresult", {
|
|
663
663
|
invocation_id,
|
|
664
664
|
function_id,
|
|
665
665
|
error: {
|
|
@@ -674,7 +674,7 @@ var Sdk = class {
|
|
|
674
674
|
} else {
|
|
675
675
|
const errorCode = fn ? "function_not_invokable" : "function_not_found";
|
|
676
676
|
const errorMessage = fn ? "Function is HTTP-invoked and cannot be invoked locally" : "Function not found";
|
|
677
|
-
if (invocation_id) this.sendMessage(
|
|
677
|
+
if (invocation_id) this.sendMessage("invocationresult", {
|
|
678
678
|
invocation_id,
|
|
679
679
|
function_id,
|
|
680
680
|
error: {
|
|
@@ -697,16 +697,16 @@ var Sdk = class {
|
|
|
697
697
|
metadata,
|
|
698
698
|
namespace
|
|
699
699
|
});
|
|
700
|
-
this.sendMessage(
|
|
700
|
+
this.sendMessage("triggerregistrationresult", {
|
|
701
701
|
id,
|
|
702
|
-
message_type:
|
|
702
|
+
message_type: "triggerregistrationresult",
|
|
703
703
|
type: trigger_type,
|
|
704
704
|
function_id
|
|
705
705
|
});
|
|
706
706
|
} catch (error) {
|
|
707
|
-
this.sendMessage(
|
|
707
|
+
this.sendMessage("triggerregistrationresult", {
|
|
708
708
|
id,
|
|
709
|
-
message_type:
|
|
709
|
+
message_type: "triggerregistrationresult",
|
|
710
710
|
type: trigger_type,
|
|
711
711
|
function_id,
|
|
712
712
|
error: {
|
|
@@ -715,9 +715,9 @@ var Sdk = class {
|
|
|
715
715
|
}
|
|
716
716
|
});
|
|
717
717
|
}
|
|
718
|
-
else this.sendMessage(
|
|
718
|
+
else this.sendMessage("triggerregistrationresult", {
|
|
719
719
|
id,
|
|
720
|
-
message_type:
|
|
720
|
+
message_type: "triggerregistrationresult",
|
|
721
721
|
type: trigger_type,
|
|
722
722
|
function_id,
|
|
723
723
|
error: {
|
|
@@ -760,22 +760,22 @@ var Sdk = class {
|
|
|
760
760
|
this.logError("Failed to parse incoming message", error);
|
|
761
761
|
return;
|
|
762
762
|
}
|
|
763
|
-
if (msgType ===
|
|
763
|
+
if (msgType === "invocationresult") {
|
|
764
764
|
const { invocation_id, result, error } = message;
|
|
765
765
|
this.onInvocationResult(invocation_id, result, error);
|
|
766
|
-
} else if (msgType ===
|
|
766
|
+
} else if (msgType === "invokefunction") {
|
|
767
767
|
const { invocation_id, function_id, data, metadata, traceparent, baggage } = message;
|
|
768
768
|
this.onInvokeFunction(invocation_id, function_id, data, metadata, traceparent, baggage);
|
|
769
|
-
} else if (msgType ===
|
|
770
|
-
else if (msgType ===
|
|
771
|
-
else if (msgType ===
|
|
772
|
-
else if (msgType ===
|
|
769
|
+
} else if (msgType === "registertrigger") this.onRegisterTrigger(message);
|
|
770
|
+
else if (msgType === "unregistertrigger") this.onUnregisterTrigger(message);
|
|
771
|
+
else if (msgType === "triggerregistrationresult") this.onTriggerRegistrationResult(message);
|
|
772
|
+
else if (msgType === "workerregistered") {
|
|
773
773
|
const { worker_id, reattach_token } = message;
|
|
774
774
|
this.workerId = worker_id;
|
|
775
775
|
this.reattachToken = reattach_token;
|
|
776
776
|
console.debug("[iii] Worker registered with ID:", worker_id);
|
|
777
777
|
this.startMetricsReporting();
|
|
778
|
-
} else if (msgType ===
|
|
778
|
+
} else if (msgType === "registrationrejected") {
|
|
779
779
|
const { code, namespace, worker_name, function_id, owner_worker_id } = message;
|
|
780
780
|
this.onRegistrationRejected({
|
|
781
781
|
code,
|
|
@@ -810,10 +810,25 @@ var Sdk = class {
|
|
|
810
810
|
* ```
|
|
811
811
|
*/
|
|
812
812
|
const TriggerAction = {
|
|
813
|
+
/**
|
|
814
|
+
* Routes the invocation through a named queue. The engine enqueues the job,
|
|
815
|
+
* acknowledges the caller with `{ messageReceiptId }`, and processes it
|
|
816
|
+
* asynchronously.
|
|
817
|
+
*
|
|
818
|
+
* Requires the `queue` worker in `worker-compose.yaml`.
|
|
819
|
+
* Without it the trigger rejects with `enqueue_error` (no queue provider).
|
|
820
|
+
*
|
|
821
|
+
* @param opts - Queue routing options.
|
|
822
|
+
* @param opts.queue - Name of the target queue.
|
|
823
|
+
*/
|
|
813
824
|
Enqueue: (opts) => ({
|
|
814
825
|
type: "enqueue",
|
|
815
826
|
...opts
|
|
816
827
|
}),
|
|
828
|
+
/**
|
|
829
|
+
* Fire-and-forget routing. The engine forwards the invocation without
|
|
830
|
+
* waiting for a response or queuing the job.
|
|
831
|
+
*/
|
|
817
832
|
Void: () => ({ type: "void" })
|
|
818
833
|
};
|
|
819
834
|
/**
|