@graphorin/client 0.6.1 → 0.7.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphorin/client",
3
- "version": "0.6.1",
3
+ "version": "0.7.0",
4
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
5
  "license": "MIT",
6
6
  "author": "Oleksiy Stepurenko",
@@ -27,7 +27,7 @@
27
27
  ],
28
28
  "type": "module",
29
29
  "engines": {
30
- "node": ">=22.0.0"
30
+ "node": ">=22.12.0"
31
31
  },
32
32
  "main": "./dist/index.js",
33
33
  "module": "./dist/index.js",
@@ -36,35 +36,36 @@
36
36
  "exports": {
37
37
  ".": {
38
38
  "types": "./dist/index.d.ts",
39
- "import": "./dist/index.js"
39
+ "default": "./dist/index.js"
40
40
  },
41
41
  "./client": {
42
42
  "types": "./dist/graphorin-client.d.ts",
43
- "import": "./dist/graphorin-client.js"
43
+ "default": "./dist/graphorin-client.js"
44
44
  },
45
45
  "./transport": {
46
46
  "types": "./dist/transport/index.d.ts",
47
- "import": "./dist/transport/index.js"
47
+ "default": "./dist/transport/index.js"
48
48
  },
49
49
  "./reconnect": {
50
50
  "types": "./dist/reconnect.d.ts",
51
- "import": "./dist/reconnect.js"
51
+ "default": "./dist/reconnect.js"
52
52
  },
53
53
  "./errors": {
54
54
  "types": "./dist/errors.d.ts",
55
- "import": "./dist/errors.js"
55
+ "default": "./dist/errors.js"
56
56
  },
57
57
  "./package.json": "./package.json"
58
58
  },
59
59
  "files": [
60
60
  "dist",
61
+ "src",
61
62
  "README.md",
62
63
  "CHANGELOG.md",
63
64
  "LICENSE"
64
65
  ],
65
66
  "dependencies": {
66
67
  "zod": "^3.25.0",
67
- "@graphorin/protocol": "0.6.1"
68
+ "@graphorin/protocol": "0.7.0"
68
69
  },
69
70
  "publishConfig": {
70
71
  "access": "public",
@@ -73,9 +74,9 @@
73
74
  "devDependencies": {
74
75
  "@types/ws": "^8.18.1",
75
76
  "ws": "^8.20.1",
76
- "@graphorin/security": "0.6.1",
77
- "@graphorin/server": "0.6.1",
78
- "@graphorin/store-sqlite": "0.6.1"
77
+ "@graphorin/security": "0.7.0",
78
+ "@graphorin/server": "0.7.0",
79
+ "@graphorin/store-sqlite": "0.7.0"
79
80
  },
80
81
  "scripts": {
81
82
  "build": "tsdown",
package/src/errors.ts ADDED
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Typed error hierarchy surfaced by `@graphorin/client`. Every error
3
+ * class extends the JavaScript built-in `Error` and exposes a stable
4
+ * `kind` discriminator so consumers can pattern-match without
5
+ * relying on `instanceof` (which behaves badly across module-system
6
+ * boundaries when the package is dual-loaded).
7
+ *
8
+ * @packageDocumentation
9
+ */
10
+
11
+ import { RPC_ERROR_CODES } from '@graphorin/protocol';
12
+
13
+ /**
14
+ * IP-19: map a JSON-RPC error code from a server `RpcFailure` frame to the
15
+ * client's discriminated {@link GraphorinClientErrorKind}, so a rate-limited
16
+ * or scope-denied RPC is distinguishable from a genuine protocol violation.
17
+ */
18
+ export function kindForRpcCode(code: number): GraphorinClientErrorKind {
19
+ switch (code) {
20
+ case RPC_ERROR_CODES.RATE_LIMITED:
21
+ return 'rate-limited';
22
+ case RPC_ERROR_CODES.SCOPE_DENIED:
23
+ return 'scope-denied';
24
+ case RPC_ERROR_CODES.AUTH_REQUIRED:
25
+ case RPC_ERROR_CODES.AUTH_INVALID:
26
+ return 'auth-failed';
27
+ case RPC_ERROR_CODES.RUN_NOT_FOUND:
28
+ return 'run-not-found';
29
+ case RPC_ERROR_CODES.SUBSCRIPTION_NOT_FOUND:
30
+ return 'subscription-not-found';
31
+ case RPC_ERROR_CODES.INTERNAL_ERROR:
32
+ return 'server-error';
33
+ default:
34
+ // INVALID_REQUEST / INVALID_PARAMS / METHOD_NOT_FOUND / PROTOCOL_VIOLATION
35
+ // and any unknown code stay a protocol violation.
36
+ return 'protocol-violation';
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Discriminator union of every error kind raised by the client.
42
+ *
43
+ * @stable
44
+ */
45
+ export type GraphorinClientErrorKind =
46
+ | 'client-not-connected'
47
+ | 'transport-failed'
48
+ | 'subprotocol-mismatch'
49
+ | 'auth-failed'
50
+ | 'protocol-violation'
51
+ | 'subscription-not-found'
52
+ // IP-19: discriminated RPC-failure kinds so callers can branch on the
53
+ // server's error class instead of pattern-matching the message string.
54
+ | 'rate-limited'
55
+ | 'scope-denied'
56
+ | 'run-not-found'
57
+ | 'server-error'
58
+ | 'aborted'
59
+ /**
60
+ * W-152: the per-subscription client buffer hit
61
+ * `subscriptionQueueLimit` because the `for await` consumer fell
62
+ * behind the event flow; the subscription is closed with this typed
63
+ * error instead of growing the heap or silently dropping frames.
64
+ */
65
+ | 'flow-overflow'
66
+ | 'invalid-server-frame';
67
+
68
+ /**
69
+ * Base class for every error raised by `@graphorin/client`. Carries a
70
+ * stable {@link GraphorinClientErrorKind} discriminator and an
71
+ * optional `cause` chain.
72
+ *
73
+ * @stable
74
+ */
75
+ export class GraphorinClientError extends Error {
76
+ readonly kind: GraphorinClientErrorKind;
77
+
78
+ constructor(kind: GraphorinClientErrorKind, message: string, options: ErrorOptions = {}) {
79
+ super(message, options);
80
+ this.kind = kind;
81
+ this.name = 'GraphorinClientError';
82
+ }
83
+ }
84
+
85
+ /** @stable */
86
+ export class ClientNotConnectedError extends GraphorinClientError {
87
+ constructor(message = 'GraphorinClient is not connected. Call connect() first.') {
88
+ super('client-not-connected', message);
89
+ this.name = 'ClientNotConnectedError';
90
+ }
91
+ }
92
+
93
+ /** @stable */
94
+ export class TransportFailedError extends GraphorinClientError {
95
+ readonly code: number | undefined;
96
+
97
+ constructor(message: string, options: ErrorOptions & { readonly code?: number } = {}) {
98
+ super('transport-failed', message, options);
99
+ this.name = 'TransportFailedError';
100
+ this.code = options.code;
101
+ }
102
+ }
103
+
104
+ /** @stable */
105
+ export class SubprotocolMismatchError extends GraphorinClientError {
106
+ readonly expected: string;
107
+ readonly actual: string | null;
108
+
109
+ constructor(expected: string, actual: string | null) {
110
+ super(
111
+ 'subprotocol-mismatch',
112
+ `Server selected subprotocol '${actual ?? '<none>'}'; expected '${expected}'.`,
113
+ );
114
+ this.name = 'SubprotocolMismatchError';
115
+ this.expected = expected;
116
+ this.actual = actual;
117
+ }
118
+ }
119
+
120
+ /** @stable */
121
+ export class AuthFailedError extends GraphorinClientError {
122
+ constructor(message = 'Authentication failed. Re-mint a token or request a new ticket.') {
123
+ super('auth-failed', message);
124
+ this.name = 'AuthFailedError';
125
+ }
126
+ }
127
+
128
+ /** @stable */
129
+ export class ProtocolViolationError extends GraphorinClientError {
130
+ constructor(message: string, options: ErrorOptions = {}) {
131
+ super('protocol-violation', message, options);
132
+ this.name = 'ProtocolViolationError';
133
+ }
134
+ }
135
+
136
+ /** @stable */
137
+ export class SubscriptionNotFoundError extends GraphorinClientError {
138
+ readonly subscriptionId: string;
139
+
140
+ constructor(subscriptionId: string) {
141
+ super(
142
+ 'subscription-not-found',
143
+ `Subscription '${subscriptionId}' is not active on this client.`,
144
+ );
145
+ this.name = 'SubscriptionNotFoundError';
146
+ this.subscriptionId = subscriptionId;
147
+ }
148
+ }
149
+
150
+ /** @stable */
151
+ export class ClientAbortedError extends GraphorinClientError {
152
+ constructor(message = 'Operation aborted before completion.') {
153
+ super('aborted', message);
154
+ this.name = 'ClientAbortedError';
155
+ }
156
+ }
157
+
158
+ /** @stable */
159
+ export class InvalidServerFrameError extends GraphorinClientError {
160
+ readonly issues: ReadonlyArray<{ path: ReadonlyArray<string | number>; message: string }>;
161
+
162
+ constructor(
163
+ message: string,
164
+ issues: ReadonlyArray<{ path: ReadonlyArray<string | number>; message: string }>,
165
+ ) {
166
+ super('invalid-server-frame', message);
167
+ this.name = 'InvalidServerFrameError';
168
+ this.issues = issues;
169
+ }
170
+ }