@graphorin/protocol 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 +15 -0
- package/LICENSE +21 -0
- package/README.md +71 -0
- package/dist/client-message.d.ts +478 -0
- package/dist/client-message.d.ts.map +1 -0
- package/dist/client-message.js +128 -0
- package/dist/client-message.js.map +1 -0
- package/dist/close-codes.d.ts +49 -0
- package/dist/close-codes.d.ts.map +1 -0
- package/dist/close-codes.js +41 -0
- package/dist/close-codes.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/server-message.d.ts +459 -0
- package/dist/server-message.d.ts.map +1 -0
- package/dist/server-message.js +185 -0
- package/dist/server-message.js.map +1 -0
- package/dist/subprotocol.d.ts +70 -0
- package/dist/subprotocol.d.ts.map +1 -0
- package/dist/subprotocol.js +87 -0
- package/dist/subprotocol.js.map +1 -0
- package/package.json +79 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
//#region src/subprotocol.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Subprotocol identifier + negotiation helpers for the Graphorin
|
|
4
|
+
* WebSocket protocol.
|
|
5
|
+
*
|
|
6
|
+
* Clients announce supported subprotocols in the
|
|
7
|
+
* `Sec-WebSocket-Protocol` upgrade header. The server is expected to
|
|
8
|
+
* pick exactly one and echo it back; mismatches abort the handshake
|
|
9
|
+
* per RFC 6455 § 4. Browsers also accept additional comma-separated
|
|
10
|
+
* tokens — Graphorin uses this slot to attach a single-use ticket
|
|
11
|
+
* via the `ticket.<value>` form (the WebSocket browser API does not
|
|
12
|
+
* accept arbitrary headers, so the ticket has to ride the
|
|
13
|
+
* subprotocol channel).
|
|
14
|
+
*
|
|
15
|
+
* @packageDocumentation
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Canonical subprotocol identifier for the v1 wire format.
|
|
19
|
+
*
|
|
20
|
+
* @stable
|
|
21
|
+
*/
|
|
22
|
+
declare const SUBPROTOCOL_NAME = "graphorin.protocol.v1";
|
|
23
|
+
/**
|
|
24
|
+
* Wire-format major version literal carried on every message body.
|
|
25
|
+
* The pair `(SUBPROTOCOL_NAME, PROTOCOL_VERSION)` is the binding
|
|
26
|
+
* contract a client commits to when it receives a successful upgrade.
|
|
27
|
+
*
|
|
28
|
+
* @stable
|
|
29
|
+
*/
|
|
30
|
+
declare const PROTOCOL_VERSION = "1";
|
|
31
|
+
/**
|
|
32
|
+
* Prefix for the single-use ticket that browser clients attach to
|
|
33
|
+
* the `Sec-WebSocket-Protocol` header. The server's upgrade handler
|
|
34
|
+
* splits the comma-separated list, finds the first
|
|
35
|
+
* `ticket.<value>` token, and validates the value against the
|
|
36
|
+
* in-memory ticket store before granting the connection.
|
|
37
|
+
*
|
|
38
|
+
* @stable
|
|
39
|
+
*/
|
|
40
|
+
declare const TICKET_SUBPROTOCOL_PREFIX = "ticket.";
|
|
41
|
+
/**
|
|
42
|
+
* Format a ticket value as a `Sec-WebSocket-Protocol` token suitable
|
|
43
|
+
* for browser clients (which cannot attach an `Authorization`
|
|
44
|
+
* header on the WebSocket upgrade). The companion server helper is
|
|
45
|
+
* {@link parseTicketSubprotocol}.
|
|
46
|
+
*
|
|
47
|
+
* @stable
|
|
48
|
+
*/
|
|
49
|
+
declare function formatTicketSubprotocol(ticket: string): string;
|
|
50
|
+
/**
|
|
51
|
+
* Extract the ticket value from a single comma-separated client list
|
|
52
|
+
* (e.g. `'graphorin.protocol.v1, ticket.abc-123'`). Returns
|
|
53
|
+
* `undefined` if no `ticket.*` token is present. Whitespace around
|
|
54
|
+
* each comma-separated token is ignored.
|
|
55
|
+
*
|
|
56
|
+
* @stable
|
|
57
|
+
*/
|
|
58
|
+
declare function parseTicketSubprotocol(clientList: string | ReadonlyArray<string>): string | undefined;
|
|
59
|
+
/**
|
|
60
|
+
* Pick the single subprotocol the server should echo back. Returns
|
|
61
|
+
* `SUBPROTOCOL_NAME` when the client offered it, or `null` when no
|
|
62
|
+
* compatible variant was advertised. The function ignores `ticket.*`
|
|
63
|
+
* tokens — those are handled separately via {@link parseTicketSubprotocol}.
|
|
64
|
+
*
|
|
65
|
+
* @stable
|
|
66
|
+
*/
|
|
67
|
+
declare function negotiateSubprotocol(clientList: string | ReadonlyArray<string>): string | null;
|
|
68
|
+
//#endregion
|
|
69
|
+
export { PROTOCOL_VERSION, SUBPROTOCOL_NAME, TICKET_SUBPROTOCOL_PREFIX, formatTicketSubprotocol, negotiateSubprotocol, parseTicketSubprotocol };
|
|
70
|
+
//# sourceMappingURL=subprotocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subprotocol.d.ts","names":[],"sources":["../src/subprotocol.ts"],"sourcesContent":[],"mappings":";;AAqBA;AASA;AAWA;AAUA;AAeA;AAoBA;;;;;;;;;;;;;;cAjEa,gBAAA;;;;;;;;cASA,gBAAA;;;;;;;;;;cAWA,yBAAA;;;;;;;;;iBAUG,uBAAA;;;;;;;;;iBAeA,sBAAA,sBACO;;;;;;;;;iBAmBP,oBAAA,sBAA0C"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
//#region src/subprotocol.ts
|
|
2
|
+
/**
|
|
3
|
+
* Subprotocol identifier + negotiation helpers for the Graphorin
|
|
4
|
+
* WebSocket protocol.
|
|
5
|
+
*
|
|
6
|
+
* Clients announce supported subprotocols in the
|
|
7
|
+
* `Sec-WebSocket-Protocol` upgrade header. The server is expected to
|
|
8
|
+
* pick exactly one and echo it back; mismatches abort the handshake
|
|
9
|
+
* per RFC 6455 § 4. Browsers also accept additional comma-separated
|
|
10
|
+
* tokens — Graphorin uses this slot to attach a single-use ticket
|
|
11
|
+
* via the `ticket.<value>` form (the WebSocket browser API does not
|
|
12
|
+
* accept arbitrary headers, so the ticket has to ride the
|
|
13
|
+
* subprotocol channel).
|
|
14
|
+
*
|
|
15
|
+
* @packageDocumentation
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Canonical subprotocol identifier for the v1 wire format.
|
|
19
|
+
*
|
|
20
|
+
* @stable
|
|
21
|
+
*/
|
|
22
|
+
const SUBPROTOCOL_NAME = "graphorin.protocol.v1";
|
|
23
|
+
/**
|
|
24
|
+
* Wire-format major version literal carried on every message body.
|
|
25
|
+
* The pair `(SUBPROTOCOL_NAME, PROTOCOL_VERSION)` is the binding
|
|
26
|
+
* contract a client commits to when it receives a successful upgrade.
|
|
27
|
+
*
|
|
28
|
+
* @stable
|
|
29
|
+
*/
|
|
30
|
+
const PROTOCOL_VERSION = "1";
|
|
31
|
+
/**
|
|
32
|
+
* Prefix for the single-use ticket that browser clients attach to
|
|
33
|
+
* the `Sec-WebSocket-Protocol` header. The server's upgrade handler
|
|
34
|
+
* splits the comma-separated list, finds the first
|
|
35
|
+
* `ticket.<value>` token, and validates the value against the
|
|
36
|
+
* in-memory ticket store before granting the connection.
|
|
37
|
+
*
|
|
38
|
+
* @stable
|
|
39
|
+
*/
|
|
40
|
+
const TICKET_SUBPROTOCOL_PREFIX = "ticket.";
|
|
41
|
+
/**
|
|
42
|
+
* Format a ticket value as a `Sec-WebSocket-Protocol` token suitable
|
|
43
|
+
* for browser clients (which cannot attach an `Authorization`
|
|
44
|
+
* header on the WebSocket upgrade). The companion server helper is
|
|
45
|
+
* {@link parseTicketSubprotocol}.
|
|
46
|
+
*
|
|
47
|
+
* @stable
|
|
48
|
+
*/
|
|
49
|
+
function formatTicketSubprotocol(ticket) {
|
|
50
|
+
if (typeof ticket !== "string" || ticket.length === 0) throw new TypeError("formatTicketSubprotocol: ticket must be a non-empty string.");
|
|
51
|
+
return `${TICKET_SUBPROTOCOL_PREFIX}${ticket}`;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Extract the ticket value from a single comma-separated client list
|
|
55
|
+
* (e.g. `'graphorin.protocol.v1, ticket.abc-123'`). Returns
|
|
56
|
+
* `undefined` if no `ticket.*` token is present. Whitespace around
|
|
57
|
+
* each comma-separated token is ignored.
|
|
58
|
+
*
|
|
59
|
+
* @stable
|
|
60
|
+
*/
|
|
61
|
+
function parseTicketSubprotocol(clientList) {
|
|
62
|
+
for (const token of normalizeTokens(clientList)) if (token.startsWith(TICKET_SUBPROTOCOL_PREFIX)) {
|
|
63
|
+
const value = token.slice(7);
|
|
64
|
+
if (value.length > 0) return value;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Pick the single subprotocol the server should echo back. Returns
|
|
69
|
+
* `SUBPROTOCOL_NAME` when the client offered it, or `null` when no
|
|
70
|
+
* compatible variant was advertised. The function ignores `ticket.*`
|
|
71
|
+
* tokens — those are handled separately via {@link parseTicketSubprotocol}.
|
|
72
|
+
*
|
|
73
|
+
* @stable
|
|
74
|
+
*/
|
|
75
|
+
function negotiateSubprotocol(clientList) {
|
|
76
|
+
for (const token of normalizeTokens(clientList)) if (token === SUBPROTOCOL_NAME) return SUBPROTOCOL_NAME;
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
function normalizeTokens(input) {
|
|
80
|
+
if (Array.isArray(input)) return input.flatMap((entry) => entry.split(",")).map((token) => token.trim()).filter((token) => token.length > 0);
|
|
81
|
+
if (typeof input !== "string") return [];
|
|
82
|
+
return input.split(",").map((token) => token.trim()).filter((token) => token.length > 0);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
//#endregion
|
|
86
|
+
export { PROTOCOL_VERSION, SUBPROTOCOL_NAME, TICKET_SUBPROTOCOL_PREFIX, formatTicketSubprotocol, negotiateSubprotocol, parseTicketSubprotocol };
|
|
87
|
+
//# sourceMappingURL=subprotocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subprotocol.js","names":[],"sources":["../src/subprotocol.ts"],"sourcesContent":["/**\n * Subprotocol identifier + negotiation helpers for the Graphorin\n * WebSocket protocol.\n *\n * Clients announce supported subprotocols in the\n * `Sec-WebSocket-Protocol` upgrade header. The server is expected to\n * pick exactly one and echo it back; mismatches abort the handshake\n * per RFC 6455 § 4. Browsers also accept additional comma-separated\n * tokens — Graphorin uses this slot to attach a single-use ticket\n * via the `ticket.<value>` form (the WebSocket browser API does not\n * accept arbitrary headers, so the ticket has to ride the\n * subprotocol channel).\n *\n * @packageDocumentation\n */\n\n/**\n * Canonical subprotocol identifier for the v1 wire format.\n *\n * @stable\n */\nexport const SUBPROTOCOL_NAME = 'graphorin.protocol.v1';\n\n/**\n * Wire-format major version literal carried on every message body.\n * The pair `(SUBPROTOCOL_NAME, PROTOCOL_VERSION)` is the binding\n * contract a client commits to when it receives a successful upgrade.\n *\n * @stable\n */\nexport const PROTOCOL_VERSION = '1';\n\n/**\n * Prefix for the single-use ticket that browser clients attach to\n * the `Sec-WebSocket-Protocol` header. The server's upgrade handler\n * splits the comma-separated list, finds the first\n * `ticket.<value>` token, and validates the value against the\n * in-memory ticket store before granting the connection.\n *\n * @stable\n */\nexport const TICKET_SUBPROTOCOL_PREFIX = 'ticket.';\n\n/**\n * Format a ticket value as a `Sec-WebSocket-Protocol` token suitable\n * for browser clients (which cannot attach an `Authorization`\n * header on the WebSocket upgrade). The companion server helper is\n * {@link parseTicketSubprotocol}.\n *\n * @stable\n */\nexport function formatTicketSubprotocol(ticket: string): string {\n if (typeof ticket !== 'string' || ticket.length === 0) {\n throw new TypeError('formatTicketSubprotocol: ticket must be a non-empty string.');\n }\n return `${TICKET_SUBPROTOCOL_PREFIX}${ticket}`;\n}\n\n/**\n * Extract the ticket value from a single comma-separated client list\n * (e.g. `'graphorin.protocol.v1, ticket.abc-123'`). Returns\n * `undefined` if no `ticket.*` token is present. Whitespace around\n * each comma-separated token is ignored.\n *\n * @stable\n */\nexport function parseTicketSubprotocol(\n clientList: string | ReadonlyArray<string>,\n): string | undefined {\n for (const token of normalizeTokens(clientList)) {\n if (token.startsWith(TICKET_SUBPROTOCOL_PREFIX)) {\n const value = token.slice(TICKET_SUBPROTOCOL_PREFIX.length);\n if (value.length > 0) return value;\n }\n }\n return undefined;\n}\n\n/**\n * Pick the single subprotocol the server should echo back. Returns\n * `SUBPROTOCOL_NAME` when the client offered it, or `null` when no\n * compatible variant was advertised. The function ignores `ticket.*`\n * tokens — those are handled separately via {@link parseTicketSubprotocol}.\n *\n * @stable\n */\nexport function negotiateSubprotocol(clientList: string | ReadonlyArray<string>): string | null {\n for (const token of normalizeTokens(clientList)) {\n if (token === SUBPROTOCOL_NAME) return SUBPROTOCOL_NAME;\n }\n return null;\n}\n\nfunction normalizeTokens(input: string | ReadonlyArray<string>): ReadonlyArray<string> {\n if (Array.isArray(input)) {\n return input\n .flatMap((entry) => entry.split(','))\n .map((token) => token.trim())\n .filter((token) => token.length > 0);\n }\n if (typeof input !== 'string') return [];\n return input\n .split(',')\n .map((token) => token.trim())\n .filter((token) => token.length > 0);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAqBA,MAAa,mBAAmB;;;;;;;;AAShC,MAAa,mBAAmB;;;;;;;;;;AAWhC,MAAa,4BAA4B;;;;;;;;;AAUzC,SAAgB,wBAAwB,QAAwB;AAC9D,KAAI,OAAO,WAAW,YAAY,OAAO,WAAW,EAClD,OAAM,IAAI,UAAU,8DAA8D;AAEpF,QAAO,GAAG,4BAA4B;;;;;;;;;;AAWxC,SAAgB,uBACd,YACoB;AACpB,MAAK,MAAM,SAAS,gBAAgB,WAAW,CAC7C,KAAI,MAAM,WAAW,0BAA0B,EAAE;EAC/C,MAAM,QAAQ,MAAM,MAAM,EAAiC;AAC3D,MAAI,MAAM,SAAS,EAAG,QAAO;;;;;;;;;;;AAcnC,SAAgB,qBAAqB,YAA2D;AAC9F,MAAK,MAAM,SAAS,gBAAgB,WAAW,CAC7C,KAAI,UAAU,iBAAkB,QAAO;AAEzC,QAAO;;AAGT,SAAS,gBAAgB,OAA8D;AACrF,KAAI,MAAM,QAAQ,MAAM,CACtB,QAAO,MACJ,SAAS,UAAU,MAAM,MAAM,IAAI,CAAC,CACpC,KAAK,UAAU,MAAM,MAAM,CAAC,CAC5B,QAAQ,UAAU,MAAM,SAAS,EAAE;AAExC,KAAI,OAAO,UAAU,SAAU,QAAO,EAAE;AACxC,QAAO,MACJ,MAAM,IAAI,CACV,KAAK,UAAU,MAAM,MAAM,CAAC,CAC5B,QAAQ,UAAU,MAAM,SAAS,EAAE"}
|
package/package.json
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@graphorin/protocol",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Wire protocol contract for the Graphorin framework: Zod schemas + TypeScript types for the WebSocket subprotocol `graphorin.protocol.v1`. Defines the `ClientMessage` and `ServerMessage` discriminated unions, the JSON-RPC-shaped control channel (`initialize` / `subscription.subscribe` / `subscription.unsubscribe` / `run.cancel` / `ping`), the typed event push frames (`{ kind: 'event', subject, type, payload, eventId }`), the asynchronous server-initiated error frames (`{ kind: 'error', code, message }`), the subprotocol negotiation helpers, and the close-code taxonomy (4001 auth.required, 4002 auth.invalid, 4003 auth.revoked, 4004 auth.scope_denied, 4005 rate.limited, 4006 flow.throttled, 4007 server.shutdown, 4008 protocol.violation). Browser-friendly: zero Node-only dependencies; the only runtime dependency is `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/protocol",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/o-stepper/graphorin.git",
|
|
11
|
+
"directory": "packages/protocol"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/o-stepper/graphorin/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"graphorin",
|
|
18
|
+
"ai",
|
|
19
|
+
"agents",
|
|
20
|
+
"framework",
|
|
21
|
+
"protocol",
|
|
22
|
+
"websocket",
|
|
23
|
+
"subprotocol",
|
|
24
|
+
"zod",
|
|
25
|
+
"wire-format",
|
|
26
|
+
"browser-friendly"
|
|
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-message": {
|
|
42
|
+
"types": "./dist/client-message.d.ts",
|
|
43
|
+
"import": "./dist/client-message.js"
|
|
44
|
+
},
|
|
45
|
+
"./server-message": {
|
|
46
|
+
"types": "./dist/server-message.d.ts",
|
|
47
|
+
"import": "./dist/server-message.js"
|
|
48
|
+
},
|
|
49
|
+
"./subprotocol": {
|
|
50
|
+
"types": "./dist/subprotocol.d.ts",
|
|
51
|
+
"import": "./dist/subprotocol.js"
|
|
52
|
+
},
|
|
53
|
+
"./close-codes": {
|
|
54
|
+
"types": "./dist/close-codes.d.ts",
|
|
55
|
+
"import": "./dist/close-codes.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
|
+
},
|
|
68
|
+
"publishConfig": {
|
|
69
|
+
"access": "public",
|
|
70
|
+
"provenance": true
|
|
71
|
+
},
|
|
72
|
+
"scripts": {
|
|
73
|
+
"build": "tsdown",
|
|
74
|
+
"typecheck": "tsc --noEmit && tsc -p tsconfig.tests.json",
|
|
75
|
+
"test": "vitest run",
|
|
76
|
+
"lint": "biome check .",
|
|
77
|
+
"clean": "rimraf dist .turbo *.tsbuildinfo"
|
|
78
|
+
}
|
|
79
|
+
}
|