@graphorin/client 0.5.0
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/CHANGELOG.md +17 -0
- package/LICENSE +21 -0
- package/README.md +92 -0
- package/dist/errors.d.ts +81 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +114 -0
- package/dist/errors.js.map +1 -0
- package/dist/graphorin-client.d.ts +180 -0
- package/dist/graphorin-client.d.ts.map +1 -0
- package/dist/graphorin-client.js +594 -0
- package/dist/graphorin-client.js.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/reconnect.d.ts +54 -0
- package/dist/reconnect.d.ts.map +1 -0
- package/dist/reconnect.js +53 -0
- package/dist/reconnect.js.map +1 -0
- package/dist/transport/index.d.ts +4 -0
- package/dist/transport/index.js +4 -0
- package/dist/transport/sse.d.ts +15 -0
- package/dist/transport/sse.d.ts.map +1 -0
- package/dist/transport/sse.js +149 -0
- package/dist/transport/sse.js.map +1 -0
- package/dist/transport/types.d.ts +93 -0
- package/dist/transport/types.d.ts.map +1 -0
- package/dist/transport/ws.d.ts +17 -0
- package/dist/transport/ws.d.ts.map +1 -0
- package/dist/transport/ws.js +149 -0
- package/dist/transport/ws.js.map +1 -0
- package/package.json +87 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { AuthFailedError, InvalidServerFrameError, ProtocolViolationError, SubprotocolMismatchError, TransportFailedError } from "../errors.js";
|
|
2
|
+
import { SUBPROTOCOL_NAME, ServerMessageSchema, closeCodeReason, formatTicketSubprotocol, isErrorFrame } from "@graphorin/protocol";
|
|
3
|
+
|
|
4
|
+
//#region src/transport/ws.ts
|
|
5
|
+
/**
|
|
6
|
+
* WebSocket transport for the {@link GraphorinClient}. Speaks the
|
|
7
|
+
* `graphorin.protocol.v1` subprotocol; honours the browser ticket
|
|
8
|
+
* flow when the auth strategy is `'ticket'`.
|
|
9
|
+
*
|
|
10
|
+
* @packageDocumentation
|
|
11
|
+
*/
|
|
12
|
+
const STATE_CONNECTING = 0;
|
|
13
|
+
const STATE_OPEN = 1;
|
|
14
|
+
/**
|
|
15
|
+
* Open a WebSocket transport. Resolves once the underlying socket
|
|
16
|
+
* fires `open` (i.e. the upgrade succeeded + the subprotocol matches
|
|
17
|
+
* `SUBPROTOCOL_NAME`); rejects with a typed
|
|
18
|
+
* {@link TransportFailedError} / {@link SubprotocolMismatchError} /
|
|
19
|
+
* {@link AuthFailedError} otherwise.
|
|
20
|
+
*
|
|
21
|
+
* @stable
|
|
22
|
+
*/
|
|
23
|
+
async function openWebSocketTransport(options, listeners) {
|
|
24
|
+
const WebSocketImpl = options.WebSocket ?? globalThis.WebSocket;
|
|
25
|
+
if (typeof WebSocketImpl !== "function") throw new TransportFailedError("No WebSocket implementation found. Pass `WebSocket` via the transport options or run on a runtime that ships one (Node 22+, modern browsers).");
|
|
26
|
+
const subprotocols = [SUBPROTOCOL_NAME];
|
|
27
|
+
if (options.auth.kind === "ticket") {
|
|
28
|
+
const ticket = await options.auth.ticketProvider();
|
|
29
|
+
subprotocols.push(formatTicketSubprotocol(ticket));
|
|
30
|
+
}
|
|
31
|
+
let socket;
|
|
32
|
+
if (options.auth.kind === "bearer") {
|
|
33
|
+
const Ctor = WebSocketImpl;
|
|
34
|
+
try {
|
|
35
|
+
socket = new Ctor(options.url, subprotocols, { headers: { Authorization: `Bearer ${options.auth.token}` } });
|
|
36
|
+
} catch {
|
|
37
|
+
try {
|
|
38
|
+
socket = new WebSocketImpl(options.url, subprotocols);
|
|
39
|
+
} catch (cause) {
|
|
40
|
+
throw new TransportFailedError(`Failed to construct WebSocket for '${options.url}'.`, cause instanceof Error ? { cause } : {});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
} else try {
|
|
44
|
+
socket = new WebSocketImpl(options.url, subprotocols);
|
|
45
|
+
} catch (cause) {
|
|
46
|
+
throw new TransportFailedError(`Failed to construct WebSocket for '${options.url}'.`, cause instanceof Error ? { cause } : {});
|
|
47
|
+
}
|
|
48
|
+
let lastEventId;
|
|
49
|
+
let closeFired = false;
|
|
50
|
+
let openFired = false;
|
|
51
|
+
return await new Promise((resolve, reject) => {
|
|
52
|
+
const fail = (err) => {
|
|
53
|
+
if (!openFired) {
|
|
54
|
+
cleanup();
|
|
55
|
+
reject(err);
|
|
56
|
+
} else listeners.onError(err);
|
|
57
|
+
};
|
|
58
|
+
const cleanup = () => {
|
|
59
|
+
socket.removeEventListener("open", onOpen);
|
|
60
|
+
socket.removeEventListener("message", onMessage);
|
|
61
|
+
socket.removeEventListener("error", onError);
|
|
62
|
+
socket.removeEventListener("close", onClose);
|
|
63
|
+
};
|
|
64
|
+
const onOpen = () => {
|
|
65
|
+
const negotiated = socket.protocol;
|
|
66
|
+
if (negotiated !== SUBPROTOCOL_NAME) {
|
|
67
|
+
socket.close(4008, "protocol.violation");
|
|
68
|
+
fail(new SubprotocolMismatchError(SUBPROTOCOL_NAME, negotiated || null));
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
openFired = true;
|
|
72
|
+
listeners.onOpen();
|
|
73
|
+
resolve({
|
|
74
|
+
kind: "ws",
|
|
75
|
+
url: options.url,
|
|
76
|
+
send(frame) {
|
|
77
|
+
if (socket.readyState !== STATE_OPEN) throw new TransportFailedError(`Cannot send: WebSocket is in state ${socket.readyState}.`);
|
|
78
|
+
socket.send(JSON.stringify(frame));
|
|
79
|
+
},
|
|
80
|
+
close(code, reason) {
|
|
81
|
+
if (socket.readyState !== STATE_CONNECTING && socket.readyState !== STATE_OPEN) return;
|
|
82
|
+
socket.close(code, reason);
|
|
83
|
+
},
|
|
84
|
+
get lastEventId() {
|
|
85
|
+
return lastEventId;
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
};
|
|
89
|
+
const onMessage = (event) => {
|
|
90
|
+
const raw = typeof event.data === "string" ? event.data : "";
|
|
91
|
+
let payload;
|
|
92
|
+
try {
|
|
93
|
+
payload = JSON.parse(raw);
|
|
94
|
+
} catch {
|
|
95
|
+
fail(new ProtocolViolationError("Server sent a non-JSON frame."));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const parsed = ServerMessageSchema.safeParse(payload);
|
|
99
|
+
if (!parsed.success) {
|
|
100
|
+
fail(new InvalidServerFrameError("Server frame failed schema validation.", parsed.error.issues.map((i) => ({
|
|
101
|
+
path: i.path,
|
|
102
|
+
message: i.message
|
|
103
|
+
}))));
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const frame = parsed.data;
|
|
107
|
+
if ("kind" in frame && frame.kind === "event") lastEventId = frame.eventId;
|
|
108
|
+
if (isErrorFrame(frame) && frame.fatal === true) {
|
|
109
|
+
listeners.onFrame(frame);
|
|
110
|
+
if (frame.code.startsWith("auth.")) fail(new AuthFailedError(frame.message));
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
listeners.onFrame(frame);
|
|
114
|
+
};
|
|
115
|
+
const onError = () => {
|
|
116
|
+
fail(new TransportFailedError("WebSocket error event fired."));
|
|
117
|
+
};
|
|
118
|
+
const onClose = (event) => {
|
|
119
|
+
if (closeFired) return;
|
|
120
|
+
closeFired = true;
|
|
121
|
+
cleanup();
|
|
122
|
+
const graphorinReason = closeCodeReason(event.code);
|
|
123
|
+
const reasonRecord = {
|
|
124
|
+
code: event.code,
|
|
125
|
+
reason: event.reason,
|
|
126
|
+
wasClean: event.wasClean,
|
|
127
|
+
...graphorinReason !== void 0 ? { graphorinReason } : {}
|
|
128
|
+
};
|
|
129
|
+
if (!openFired) {
|
|
130
|
+
const codeReason = closeCodeReason(event.code);
|
|
131
|
+
if (codeReason?.startsWith("auth.") === true) {
|
|
132
|
+
fail(new AuthFailedError(event.reason || codeReason));
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
fail(new TransportFailedError(event.reason || "WebSocket closed before open."));
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
listeners.onClose(reasonRecord);
|
|
139
|
+
};
|
|
140
|
+
socket.addEventListener("open", onOpen);
|
|
141
|
+
socket.addEventListener("message", onMessage);
|
|
142
|
+
socket.addEventListener("error", onError);
|
|
143
|
+
socket.addEventListener("close", onClose);
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
//#endregion
|
|
148
|
+
export { openWebSocketTransport };
|
|
149
|
+
//# sourceMappingURL=ws.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ws.js","names":["subprotocols: string[]","socket: WebSocket","lastEventId: string | undefined","payload: unknown","reasonRecord: TransportCloseReason"],"sources":["../../src/transport/ws.ts"],"sourcesContent":["/**\n * WebSocket transport for the {@link GraphorinClient}. Speaks the\n * `graphorin.protocol.v1` subprotocol; honours the browser ticket\n * flow when the auth strategy is `'ticket'`.\n *\n * @packageDocumentation\n */\n\nimport {\n type ClientMessage,\n closeCodeReason,\n formatTicketSubprotocol,\n isErrorFrame,\n ServerMessageSchema,\n SUBPROTOCOL_NAME,\n} from '@graphorin/protocol';\n\nimport {\n AuthFailedError,\n InvalidServerFrameError,\n ProtocolViolationError,\n SubprotocolMismatchError,\n TransportFailedError,\n} from '../errors.js';\nimport type {\n Transport,\n TransportCloseReason,\n TransportListeners,\n TransportOptions,\n} from './types.js';\n\nconst STATE_CONNECTING = 0;\nconst STATE_OPEN = 1;\n\n/**\n * Open a WebSocket transport. Resolves once the underlying socket\n * fires `open` (i.e. the upgrade succeeded + the subprotocol matches\n * `SUBPROTOCOL_NAME`); rejects with a typed\n * {@link TransportFailedError} / {@link SubprotocolMismatchError} /\n * {@link AuthFailedError} otherwise.\n *\n * @stable\n */\nexport async function openWebSocketTransport(\n options: TransportOptions,\n listeners: TransportListeners,\n): Promise<Transport> {\n const WebSocketImpl = options.WebSocket ?? globalThis.WebSocket;\n if (typeof WebSocketImpl !== 'function') {\n throw new TransportFailedError(\n 'No WebSocket implementation found. Pass `WebSocket` via the transport options or run on a runtime that ships one (Node 22+, modern browsers).',\n );\n }\n\n const subprotocols: string[] = [SUBPROTOCOL_NAME];\n if (options.auth.kind === 'ticket') {\n const ticket = await options.auth.ticketProvider();\n subprotocols.push(formatTicketSubprotocol(ticket));\n }\n\n // Bearer-token auth on Node SDKs is encoded as a custom header on\n // the upgrade. The browser WebSocket API does not expose headers,\n // so the SDK fallback path uses the ticket strategy instead. We\n // try the 3-arg `new WebSocket(url, protocols, opts)` overload\n // first (supported by `ws` on Node + most polyfills); if the\n // implementation rejects it, we fall back to the 2-arg signature\n // and rely on the server to surface auth failures via the close\n // code (the only viable path on plain browser WebSocket).\n let socket: WebSocket;\n if (options.auth.kind === 'bearer') {\n type WsCtorWithOptions = new (\n url: string,\n protocols: ReadonlyArray<string>,\n options: { readonly headers: Readonly<Record<string, string>> },\n ) => WebSocket;\n const Ctor = WebSocketImpl as unknown as WsCtorWithOptions;\n try {\n socket = new Ctor(options.url, subprotocols, {\n headers: { Authorization: `Bearer ${options.auth.token}` },\n });\n } catch {\n try {\n socket = new WebSocketImpl(options.url, subprotocols);\n } catch (cause) {\n throw new TransportFailedError(\n `Failed to construct WebSocket for '${options.url}'.`,\n cause instanceof Error ? { cause } : {},\n );\n }\n }\n } else {\n try {\n socket = new WebSocketImpl(options.url, subprotocols);\n } catch (cause) {\n throw new TransportFailedError(\n `Failed to construct WebSocket for '${options.url}'.`,\n cause instanceof Error ? { cause } : {},\n );\n }\n }\n\n let lastEventId: string | undefined;\n let closeFired = false;\n let openFired = false;\n return await new Promise<Transport>((resolve, reject) => {\n const fail = (err: Error): void => {\n if (!openFired) {\n cleanup();\n reject(err);\n } else {\n listeners.onError(err);\n }\n };\n const cleanup = (): void => {\n socket.removeEventListener('open', onOpen);\n socket.removeEventListener('message', onMessage);\n socket.removeEventListener('error', onError);\n socket.removeEventListener('close', onClose);\n };\n const onOpen = (): void => {\n const negotiated = socket.protocol;\n if (negotiated !== SUBPROTOCOL_NAME) {\n socket.close(4008, 'protocol.violation');\n fail(new SubprotocolMismatchError(SUBPROTOCOL_NAME, negotiated || null));\n return;\n }\n openFired = true;\n listeners.onOpen();\n resolve({\n kind: 'ws',\n url: options.url,\n send(frame: ClientMessage): void {\n if (socket.readyState !== STATE_OPEN) {\n throw new TransportFailedError(\n `Cannot send: WebSocket is in state ${socket.readyState}.`,\n );\n }\n socket.send(JSON.stringify(frame));\n },\n close(code?: number, reason?: string): void {\n if (socket.readyState !== STATE_CONNECTING && socket.readyState !== STATE_OPEN) {\n return;\n }\n socket.close(code, reason);\n },\n get lastEventId(): string | undefined {\n return lastEventId;\n },\n });\n };\n const onMessage = (event: MessageEvent): void => {\n const raw = typeof event.data === 'string' ? event.data : '';\n let payload: unknown;\n try {\n payload = JSON.parse(raw);\n } catch {\n fail(new ProtocolViolationError('Server sent a non-JSON frame.'));\n return;\n }\n const parsed = ServerMessageSchema.safeParse(payload);\n if (!parsed.success) {\n fail(\n new InvalidServerFrameError(\n 'Server frame failed schema validation.',\n parsed.error.issues.map((i) => ({ path: i.path, message: i.message })),\n ),\n );\n return;\n }\n const frame = parsed.data;\n if ('kind' in frame && frame.kind === 'event') {\n lastEventId = frame.eventId;\n }\n if (isErrorFrame(frame) && frame.fatal === true) {\n listeners.onFrame(frame);\n if (frame.code.startsWith('auth.')) fail(new AuthFailedError(frame.message));\n return;\n }\n listeners.onFrame(frame);\n };\n const onError = (): void => {\n fail(new TransportFailedError('WebSocket error event fired.'));\n };\n const onClose = (event: CloseEvent): void => {\n if (closeFired) return;\n closeFired = true;\n cleanup();\n const graphorinReason = closeCodeReason(event.code);\n const reasonRecord: TransportCloseReason = {\n code: event.code,\n reason: event.reason,\n wasClean: event.wasClean,\n ...(graphorinReason !== undefined ? { graphorinReason } : {}),\n };\n if (!openFired) {\n const codeReason = closeCodeReason(event.code);\n if (codeReason?.startsWith('auth.') === true) {\n fail(new AuthFailedError(event.reason || codeReason));\n return;\n }\n fail(new TransportFailedError(event.reason || 'WebSocket closed before open.'));\n return;\n }\n listeners.onClose(reasonRecord);\n };\n socket.addEventListener('open', onOpen);\n socket.addEventListener('message', onMessage);\n socket.addEventListener('error', onError);\n socket.addEventListener('close', onClose);\n });\n}\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@graphorin/client",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Reference TypeScript client for the Graphorin standalone server. Wraps the WebSocket subprotocol `graphorin.protocol.v1` (with the optional Server-Sent Events fallback for proxy-restricted environments) behind an ergonomic `GraphorinClient` class: `connect()`, `subscribe({ target, id })` returning an async-iterable subscription, `cancel(runId, opts)`, `resume(runId, directive)`, `ping()`, `disconnect()`. Handles the browser ticket flow (single-use ticket attached as a `ticket.<value>` `Sec-WebSocket-Protocol` token), exponential-backoff reconnect with `lastEventId` resume against the server replay buffer, and Zod-validated frame parsing on both directions. Browser-friendly: zero Node-only dependencies; the only runtime dependencies are `@graphorin/protocol` and `zod`. Created and maintained by Oleksiy Stepurenko.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Oleksiy Stepurenko",
|
|
7
|
+
"homepage": "https://github.com/o-stepper/graphorin/tree/main/packages/client",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/o-stepper/graphorin.git",
|
|
11
|
+
"directory": "packages/client"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/o-stepper/graphorin/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"graphorin",
|
|
18
|
+
"ai",
|
|
19
|
+
"agents",
|
|
20
|
+
"framework",
|
|
21
|
+
"client",
|
|
22
|
+
"websocket",
|
|
23
|
+
"sse",
|
|
24
|
+
"browser-friendly",
|
|
25
|
+
"ticket-flow",
|
|
26
|
+
"reconnect"
|
|
27
|
+
],
|
|
28
|
+
"type": "module",
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=22.0.0"
|
|
31
|
+
},
|
|
32
|
+
"main": "./dist/index.js",
|
|
33
|
+
"module": "./dist/index.js",
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"sideEffects": false,
|
|
36
|
+
"exports": {
|
|
37
|
+
".": {
|
|
38
|
+
"types": "./dist/index.d.ts",
|
|
39
|
+
"import": "./dist/index.js"
|
|
40
|
+
},
|
|
41
|
+
"./client": {
|
|
42
|
+
"types": "./dist/graphorin-client.d.ts",
|
|
43
|
+
"import": "./dist/graphorin-client.js"
|
|
44
|
+
},
|
|
45
|
+
"./transport": {
|
|
46
|
+
"types": "./dist/transport/index.d.ts",
|
|
47
|
+
"import": "./dist/transport/index.js"
|
|
48
|
+
},
|
|
49
|
+
"./reconnect": {
|
|
50
|
+
"types": "./dist/reconnect.d.ts",
|
|
51
|
+
"import": "./dist/reconnect.js"
|
|
52
|
+
},
|
|
53
|
+
"./errors": {
|
|
54
|
+
"types": "./dist/errors.d.ts",
|
|
55
|
+
"import": "./dist/errors.js"
|
|
56
|
+
},
|
|
57
|
+
"./package.json": "./package.json"
|
|
58
|
+
},
|
|
59
|
+
"files": [
|
|
60
|
+
"dist",
|
|
61
|
+
"README.md",
|
|
62
|
+
"CHANGELOG.md",
|
|
63
|
+
"LICENSE"
|
|
64
|
+
],
|
|
65
|
+
"dependencies": {
|
|
66
|
+
"zod": "^3.25.0",
|
|
67
|
+
"@graphorin/protocol": "0.5.0"
|
|
68
|
+
},
|
|
69
|
+
"publishConfig": {
|
|
70
|
+
"access": "public",
|
|
71
|
+
"provenance": true
|
|
72
|
+
},
|
|
73
|
+
"devDependencies": {
|
|
74
|
+
"@types/ws": "^8.18.1",
|
|
75
|
+
"ws": "^8.20.1",
|
|
76
|
+
"@graphorin/security": "0.5.0",
|
|
77
|
+
"@graphorin/server": "0.5.0",
|
|
78
|
+
"@graphorin/store-sqlite": "0.5.0"
|
|
79
|
+
},
|
|
80
|
+
"scripts": {
|
|
81
|
+
"build": "tsdown",
|
|
82
|
+
"typecheck": "tsc --noEmit && tsc -p tsconfig.tests.json",
|
|
83
|
+
"test": "vitest run",
|
|
84
|
+
"lint": "biome check .",
|
|
85
|
+
"clean": "rimraf dist .turbo *.tsbuildinfo"
|
|
86
|
+
}
|
|
87
|
+
}
|