@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
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# @graphorin/client
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Initial Phase 14b release: reference TypeScript client for the
|
|
8
|
+
Graphorin standalone server. Wraps the `graphorin.protocol.v1`
|
|
9
|
+
WebSocket subprotocol (with an optional SSE fallback for
|
|
10
|
+
proxy-restricted environments) behind an ergonomic
|
|
11
|
+
`GraphorinClient` class: `connect()`, `subscribe({ target, id })`
|
|
12
|
+
returning an async-iterable subscription, `cancel(runId, opts)`,
|
|
13
|
+
`resume(runId, directive)`, `ping()`, `disconnect()`. Handles the
|
|
14
|
+
browser ticket flow, exponential-backoff reconnect with replay
|
|
15
|
+
buffer resume, and Zod-validated frame parsing on both
|
|
16
|
+
directions. Browser-friendly: zero Node-only dependencies. Created
|
|
17
|
+
and maintained by Oleksiy Stepurenko.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Oleksiy Stepurenko
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# @graphorin/client
|
|
2
|
+
|
|
3
|
+
> Reference TypeScript client for the [Graphorin](https://github.com/o-stepper/graphorin) standalone server.
|
|
4
|
+
|
|
5
|
+
[](./LICENSE)
|
|
6
|
+
[](https://nodejs.org)
|
|
7
|
+
|
|
8
|
+
- **Version:** v0.5.0
|
|
9
|
+
- **License:** [MIT](./LICENSE) (© 2026 Oleksiy Stepurenko)
|
|
10
|
+
- **Repository:** <https://github.com/o-stepper/graphorin/tree/main/packages/client>
|
|
11
|
+
- **Issues:** <https://github.com/o-stepper/graphorin/issues>
|
|
12
|
+
|
|
13
|
+
`@graphorin/client` is the reference TypeScript client for the Graphorin standalone server. It wraps the WebSocket subprotocol `graphorin.protocol.v1` (with an optional Server-Sent Events fallback for proxy-restricted environments) behind an ergonomic `GraphorinClient` class.
|
|
14
|
+
|
|
15
|
+
## What ships in v0.1 (Phase 14b)
|
|
16
|
+
|
|
17
|
+
| Capability | Detail |
|
|
18
|
+
|---|---|
|
|
19
|
+
| **WebSocket transport** | Honours the `graphorin.protocol.v1` subprotocol; supports both bearer-token (Node SDK) and ticket-flow (browser) authentication. |
|
|
20
|
+
| **SSE fallback** | Read-only `EventSource` transport for environments that block WebSocket upgrades. Control-plane operations (`subscribe`, `cancel`, `resume`) fall back to REST. |
|
|
21
|
+
| **Async-iterable subscriptions** | `for await (const event of sub.events())` — typed `AgentEvent` / `WorkflowEvent` payload via `@graphorin/protocol`. |
|
|
22
|
+
| **Reconnect** | Exponential backoff with full jitter; resubscribes with the recorded `lastEventId` so the server replays buffered events. |
|
|
23
|
+
| **Bundle hygiene** | Browser-friendly. Zero Node-only dependencies; runtime depends only on `@graphorin/protocol` and `zod`. |
|
|
24
|
+
|
|
25
|
+
## Direct dependencies
|
|
26
|
+
|
|
27
|
+
- [`@graphorin/protocol`](../protocol/README.md) — single source of truth for the wire format.
|
|
28
|
+
- [`zod`](https://zod.dev) (`^3.25.0`) — schema validation re-exported transitively through `@graphorin/protocol`.
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pnpm add @graphorin/client @graphorin/protocol zod
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
> Other npm-registry-compatible package managers (`npm`, `yarn`, `bun`) work identically.
|
|
37
|
+
|
|
38
|
+
## Usage
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { GraphorinClient } from '@graphorin/client';
|
|
42
|
+
|
|
43
|
+
const client = new GraphorinClient({
|
|
44
|
+
baseUrl: 'wss://graphorin.example.com',
|
|
45
|
+
auth: { kind: 'bearer', token: process.env.GRAPHORIN_TOKEN ?? '' },
|
|
46
|
+
transport: 'auto', // try WebSocket first, fall back to SSE
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
await client.connect();
|
|
50
|
+
|
|
51
|
+
const subscription = await client.subscribe({
|
|
52
|
+
target: 'agent',
|
|
53
|
+
id: 'echo',
|
|
54
|
+
runId: 'run-123',
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
for await (const event of subscription.events()) {
|
|
58
|
+
console.log(event.type, event.payload);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
await client.cancel('run-123', { drain: false });
|
|
62
|
+
await client.disconnect();
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Browser ticket flow
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
const client = new GraphorinClient({
|
|
69
|
+
baseUrl: 'https://graphorin.example.com',
|
|
70
|
+
auth: {
|
|
71
|
+
kind: 'ticket',
|
|
72
|
+
ticketProvider: async () => {
|
|
73
|
+
const res = await fetch('/v1/session/ws-ticket', {
|
|
74
|
+
method: 'POST',
|
|
75
|
+
headers: { Authorization: `Bearer ${browserToken}` },
|
|
76
|
+
});
|
|
77
|
+
const body = (await res.json()) as { ticket: string };
|
|
78
|
+
return body.ticket;
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The client attaches the ticket as a second `Sec-WebSocket-Protocol` token (`ticket.<value>`), per the wire contract documented in [`@graphorin/protocol`](../protocol/README.md).
|
|
85
|
+
|
|
86
|
+
## License
|
|
87
|
+
|
|
88
|
+
MIT © 2026 Oleksiy Stepurenko. See [`LICENSE`](./LICENSE).
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
**Project Graphorin** · v0.5.0 · MIT License · © 2026 Oleksiy Stepurenko · <https://github.com/o-stepper/graphorin>
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
//#region src/errors.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Typed error hierarchy surfaced by `@graphorin/client`. Every error
|
|
4
|
+
* class extends the JavaScript built-in `Error` and exposes a stable
|
|
5
|
+
* `kind` discriminator so consumers can pattern-match without
|
|
6
|
+
* relying on `instanceof` (which behaves badly across module-system
|
|
7
|
+
* boundaries when the package is dual-loaded).
|
|
8
|
+
*
|
|
9
|
+
* @packageDocumentation
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* IP-19: map a JSON-RPC error code from a server `RpcFailure` frame to the
|
|
13
|
+
* client's discriminated {@link GraphorinClientErrorKind}, so a rate-limited
|
|
14
|
+
* or scope-denied RPC is distinguishable from a genuine protocol violation.
|
|
15
|
+
*/
|
|
16
|
+
declare function kindForRpcCode(code: number): GraphorinClientErrorKind;
|
|
17
|
+
/**
|
|
18
|
+
* Discriminator union of every error kind raised by the client.
|
|
19
|
+
*
|
|
20
|
+
* @stable
|
|
21
|
+
*/
|
|
22
|
+
type GraphorinClientErrorKind = 'client-not-connected' | 'transport-failed' | 'subprotocol-mismatch' | 'auth-failed' | 'protocol-violation' | 'subscription-not-found' | 'rate-limited' | 'scope-denied' | 'run-not-found' | 'server-error' | 'aborted' | 'invalid-server-frame';
|
|
23
|
+
/**
|
|
24
|
+
* Base class for every error raised by `@graphorin/client`. Carries a
|
|
25
|
+
* stable {@link GraphorinClientErrorKind} discriminator and an
|
|
26
|
+
* optional `cause` chain.
|
|
27
|
+
*
|
|
28
|
+
* @stable
|
|
29
|
+
*/
|
|
30
|
+
declare class GraphorinClientError extends Error {
|
|
31
|
+
readonly kind: GraphorinClientErrorKind;
|
|
32
|
+
constructor(kind: GraphorinClientErrorKind, message: string, options?: ErrorOptions);
|
|
33
|
+
}
|
|
34
|
+
/** @stable */
|
|
35
|
+
declare class ClientNotConnectedError extends GraphorinClientError {
|
|
36
|
+
constructor(message?: string);
|
|
37
|
+
}
|
|
38
|
+
/** @stable */
|
|
39
|
+
declare class TransportFailedError extends GraphorinClientError {
|
|
40
|
+
readonly code: number | undefined;
|
|
41
|
+
constructor(message: string, options?: ErrorOptions & {
|
|
42
|
+
readonly code?: number;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/** @stable */
|
|
46
|
+
declare class SubprotocolMismatchError extends GraphorinClientError {
|
|
47
|
+
readonly expected: string;
|
|
48
|
+
readonly actual: string | null;
|
|
49
|
+
constructor(expected: string, actual: string | null);
|
|
50
|
+
}
|
|
51
|
+
/** @stable */
|
|
52
|
+
declare class AuthFailedError extends GraphorinClientError {
|
|
53
|
+
constructor(message?: string);
|
|
54
|
+
}
|
|
55
|
+
/** @stable */
|
|
56
|
+
declare class ProtocolViolationError extends GraphorinClientError {
|
|
57
|
+
constructor(message: string, options?: ErrorOptions);
|
|
58
|
+
}
|
|
59
|
+
/** @stable */
|
|
60
|
+
declare class SubscriptionNotFoundError extends GraphorinClientError {
|
|
61
|
+
readonly subscriptionId: string;
|
|
62
|
+
constructor(subscriptionId: string);
|
|
63
|
+
}
|
|
64
|
+
/** @stable */
|
|
65
|
+
declare class ClientAbortedError extends GraphorinClientError {
|
|
66
|
+
constructor(message?: string);
|
|
67
|
+
}
|
|
68
|
+
/** @stable */
|
|
69
|
+
declare class InvalidServerFrameError extends GraphorinClientError {
|
|
70
|
+
readonly issues: ReadonlyArray<{
|
|
71
|
+
path: ReadonlyArray<string | number>;
|
|
72
|
+
message: string;
|
|
73
|
+
}>;
|
|
74
|
+
constructor(message: string, issues: ReadonlyArray<{
|
|
75
|
+
path: ReadonlyArray<string | number>;
|
|
76
|
+
message: string;
|
|
77
|
+
}>);
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
80
|
+
export { AuthFailedError, ClientAbortedError, ClientNotConnectedError, GraphorinClientError, GraphorinClientErrorKind, InvalidServerFrameError, ProtocolViolationError, SubprotocolMismatchError, SubscriptionNotFoundError, TransportFailedError, kindForRpcCode };
|
|
81
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","names":[],"sources":["../src/errors.ts"],"sourcesContent":[],"mappings":";;AAiBA;AA2BA;AAuBA;;;;;;AAWA;AAQA;AAWA;AAgBA;AAQA;AAQa,iBAhHG,cAAA,CAgHuB,IAAQ,EAAA,MAAA,CAAA,EAhHD,wBAgHqB;AAcnE;AAQA;;;;AAKY,KAhHA,wBAAA,GAgHA,sBAAA,GAAA,kBAAA,GAAA,sBAAA,GAAA,aAAA,GAAA,oBAAA,GAAA,wBAAA,GAAA,cAAA,GAAA,cAAA,GAAA,eAAA,GAAA,cAAA,GAAA,SAAA,GAAA,sBAAA;;;;;;;;cAzFC,oBAAA,SAA6B,KAAA;iBACzB;oBAEG,qDAAoD;;;cAQ3D,uBAAA,SAAgC,oBAAA;;;;cAQhC,oBAAA,SAA6B,oBAAA;;yCAGF;;;;;cAQ3B,wBAAA,SAAiC,oBAAA;;;;;;cAgBjC,eAAA,SAAwB,oBAAA;;;;cAQxB,sBAAA,SAA+B,oBAAA;yCACJ;;;cAO3B,yBAAA,SAAkC,oBAAA;;;;;cAclC,kBAAA,SAA2B,oBAAA;;;;cAQ3B,uBAAA,SAAgC,oBAAA;mBAC1B;UAAsB;;;uCAI7B;UAAsB"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { RPC_ERROR_CODES } from "@graphorin/protocol";
|
|
2
|
+
|
|
3
|
+
//#region src/errors.ts
|
|
4
|
+
/**
|
|
5
|
+
* Typed error hierarchy surfaced by `@graphorin/client`. Every error
|
|
6
|
+
* class extends the JavaScript built-in `Error` and exposes a stable
|
|
7
|
+
* `kind` discriminator so consumers can pattern-match without
|
|
8
|
+
* relying on `instanceof` (which behaves badly across module-system
|
|
9
|
+
* boundaries when the package is dual-loaded).
|
|
10
|
+
*
|
|
11
|
+
* @packageDocumentation
|
|
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
|
+
function kindForRpcCode(code) {
|
|
19
|
+
switch (code) {
|
|
20
|
+
case RPC_ERROR_CODES.RATE_LIMITED: return "rate-limited";
|
|
21
|
+
case RPC_ERROR_CODES.SCOPE_DENIED: return "scope-denied";
|
|
22
|
+
case RPC_ERROR_CODES.AUTH_REQUIRED:
|
|
23
|
+
case RPC_ERROR_CODES.AUTH_INVALID: return "auth-failed";
|
|
24
|
+
case RPC_ERROR_CODES.RUN_NOT_FOUND: return "run-not-found";
|
|
25
|
+
case RPC_ERROR_CODES.SUBSCRIPTION_NOT_FOUND: return "subscription-not-found";
|
|
26
|
+
case RPC_ERROR_CODES.INTERNAL_ERROR: return "server-error";
|
|
27
|
+
default: return "protocol-violation";
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Base class for every error raised by `@graphorin/client`. Carries a
|
|
32
|
+
* stable {@link GraphorinClientErrorKind} discriminator and an
|
|
33
|
+
* optional `cause` chain.
|
|
34
|
+
*
|
|
35
|
+
* @stable
|
|
36
|
+
*/
|
|
37
|
+
var GraphorinClientError = class extends Error {
|
|
38
|
+
kind;
|
|
39
|
+
constructor(kind, message, options = {}) {
|
|
40
|
+
super(message, options);
|
|
41
|
+
this.kind = kind;
|
|
42
|
+
this.name = "GraphorinClientError";
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
/** @stable */
|
|
46
|
+
var ClientNotConnectedError = class extends GraphorinClientError {
|
|
47
|
+
constructor(message = "GraphorinClient is not connected. Call connect() first.") {
|
|
48
|
+
super("client-not-connected", message);
|
|
49
|
+
this.name = "ClientNotConnectedError";
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
/** @stable */
|
|
53
|
+
var TransportFailedError = class extends GraphorinClientError {
|
|
54
|
+
code;
|
|
55
|
+
constructor(message, options = {}) {
|
|
56
|
+
super("transport-failed", message, options);
|
|
57
|
+
this.name = "TransportFailedError";
|
|
58
|
+
this.code = options.code;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
/** @stable */
|
|
62
|
+
var SubprotocolMismatchError = class extends GraphorinClientError {
|
|
63
|
+
expected;
|
|
64
|
+
actual;
|
|
65
|
+
constructor(expected, actual) {
|
|
66
|
+
super("subprotocol-mismatch", `Server selected subprotocol '${actual ?? "<none>"}'; expected '${expected}'.`);
|
|
67
|
+
this.name = "SubprotocolMismatchError";
|
|
68
|
+
this.expected = expected;
|
|
69
|
+
this.actual = actual;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
/** @stable */
|
|
73
|
+
var AuthFailedError = class extends GraphorinClientError {
|
|
74
|
+
constructor(message = "Authentication failed. Re-mint a token or request a new ticket.") {
|
|
75
|
+
super("auth-failed", message);
|
|
76
|
+
this.name = "AuthFailedError";
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
/** @stable */
|
|
80
|
+
var ProtocolViolationError = class extends GraphorinClientError {
|
|
81
|
+
constructor(message, options = {}) {
|
|
82
|
+
super("protocol-violation", message, options);
|
|
83
|
+
this.name = "ProtocolViolationError";
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
/** @stable */
|
|
87
|
+
var SubscriptionNotFoundError = class extends GraphorinClientError {
|
|
88
|
+
subscriptionId;
|
|
89
|
+
constructor(subscriptionId) {
|
|
90
|
+
super("subscription-not-found", `Subscription '${subscriptionId}' is not active on this client.`);
|
|
91
|
+
this.name = "SubscriptionNotFoundError";
|
|
92
|
+
this.subscriptionId = subscriptionId;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
/** @stable */
|
|
96
|
+
var ClientAbortedError = class extends GraphorinClientError {
|
|
97
|
+
constructor(message = "Operation aborted before completion.") {
|
|
98
|
+
super("aborted", message);
|
|
99
|
+
this.name = "ClientAbortedError";
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
/** @stable */
|
|
103
|
+
var InvalidServerFrameError = class extends GraphorinClientError {
|
|
104
|
+
issues;
|
|
105
|
+
constructor(message, issues) {
|
|
106
|
+
super("invalid-server-frame", message);
|
|
107
|
+
this.name = "InvalidServerFrameError";
|
|
108
|
+
this.issues = issues;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
//#endregion
|
|
113
|
+
export { AuthFailedError, ClientAbortedError, ClientNotConnectedError, GraphorinClientError, InvalidServerFrameError, ProtocolViolationError, SubprotocolMismatchError, SubscriptionNotFoundError, TransportFailedError, kindForRpcCode };
|
|
114
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Typed error hierarchy surfaced by `@graphorin/client`. Every error\n * class extends the JavaScript built-in `Error` and exposes a stable\n * `kind` discriminator so consumers can pattern-match without\n * relying on `instanceof` (which behaves badly across module-system\n * boundaries when the package is dual-loaded).\n *\n * @packageDocumentation\n */\n\nimport { RPC_ERROR_CODES } from '@graphorin/protocol';\n\n/**\n * IP-19: map a JSON-RPC error code from a server `RpcFailure` frame to the\n * client's discriminated {@link GraphorinClientErrorKind}, so a rate-limited\n * or scope-denied RPC is distinguishable from a genuine protocol violation.\n */\nexport function kindForRpcCode(code: number): GraphorinClientErrorKind {\n switch (code) {\n case RPC_ERROR_CODES.RATE_LIMITED:\n return 'rate-limited';\n case RPC_ERROR_CODES.SCOPE_DENIED:\n return 'scope-denied';\n case RPC_ERROR_CODES.AUTH_REQUIRED:\n case RPC_ERROR_CODES.AUTH_INVALID:\n return 'auth-failed';\n case RPC_ERROR_CODES.RUN_NOT_FOUND:\n return 'run-not-found';\n case RPC_ERROR_CODES.SUBSCRIPTION_NOT_FOUND:\n return 'subscription-not-found';\n case RPC_ERROR_CODES.INTERNAL_ERROR:\n return 'server-error';\n default:\n // INVALID_REQUEST / INVALID_PARAMS / METHOD_NOT_FOUND / PROTOCOL_VIOLATION\n // and any unknown code stay a protocol violation.\n return 'protocol-violation';\n }\n}\n\n/**\n * Discriminator union of every error kind raised by the client.\n *\n * @stable\n */\nexport type GraphorinClientErrorKind =\n | 'client-not-connected'\n | 'transport-failed'\n | 'subprotocol-mismatch'\n | 'auth-failed'\n | 'protocol-violation'\n | 'subscription-not-found'\n // IP-19: discriminated RPC-failure kinds so callers can branch on the\n // server's error class instead of pattern-matching the message string.\n | 'rate-limited'\n | 'scope-denied'\n | 'run-not-found'\n | 'server-error'\n | 'aborted'\n | 'invalid-server-frame';\n\n/**\n * Base class for every error raised by `@graphorin/client`. Carries a\n * stable {@link GraphorinClientErrorKind} discriminator and an\n * optional `cause` chain.\n *\n * @stable\n */\nexport class GraphorinClientError extends Error {\n readonly kind: GraphorinClientErrorKind;\n\n constructor(kind: GraphorinClientErrorKind, message: string, options: ErrorOptions = {}) {\n super(message, options);\n this.kind = kind;\n this.name = 'GraphorinClientError';\n }\n}\n\n/** @stable */\nexport class ClientNotConnectedError extends GraphorinClientError {\n constructor(message = 'GraphorinClient is not connected. Call connect() first.') {\n super('client-not-connected', message);\n this.name = 'ClientNotConnectedError';\n }\n}\n\n/** @stable */\nexport class TransportFailedError extends GraphorinClientError {\n readonly code: number | undefined;\n\n constructor(message: string, options: ErrorOptions & { readonly code?: number } = {}) {\n super('transport-failed', message, options);\n this.name = 'TransportFailedError';\n this.code = options.code;\n }\n}\n\n/** @stable */\nexport class SubprotocolMismatchError extends GraphorinClientError {\n readonly expected: string;\n readonly actual: string | null;\n\n constructor(expected: string, actual: string | null) {\n super(\n 'subprotocol-mismatch',\n `Server selected subprotocol '${actual ?? '<none>'}'; expected '${expected}'.`,\n );\n this.name = 'SubprotocolMismatchError';\n this.expected = expected;\n this.actual = actual;\n }\n}\n\n/** @stable */\nexport class AuthFailedError extends GraphorinClientError {\n constructor(message = 'Authentication failed. Re-mint a token or request a new ticket.') {\n super('auth-failed', message);\n this.name = 'AuthFailedError';\n }\n}\n\n/** @stable */\nexport class ProtocolViolationError extends GraphorinClientError {\n constructor(message: string, options: ErrorOptions = {}) {\n super('protocol-violation', message, options);\n this.name = 'ProtocolViolationError';\n }\n}\n\n/** @stable */\nexport class SubscriptionNotFoundError extends GraphorinClientError {\n readonly subscriptionId: string;\n\n constructor(subscriptionId: string) {\n super(\n 'subscription-not-found',\n `Subscription '${subscriptionId}' is not active on this client.`,\n );\n this.name = 'SubscriptionNotFoundError';\n this.subscriptionId = subscriptionId;\n }\n}\n\n/** @stable */\nexport class ClientAbortedError extends GraphorinClientError {\n constructor(message = 'Operation aborted before completion.') {\n super('aborted', message);\n this.name = 'ClientAbortedError';\n }\n}\n\n/** @stable */\nexport class InvalidServerFrameError extends GraphorinClientError {\n readonly issues: ReadonlyArray<{ path: ReadonlyArray<string | number>; message: string }>;\n\n constructor(\n message: string,\n issues: ReadonlyArray<{ path: ReadonlyArray<string | number>; message: string }>,\n ) {\n super('invalid-server-frame', message);\n this.name = 'InvalidServerFrameError';\n this.issues = issues;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAiBA,SAAgB,eAAe,MAAwC;AACrE,SAAQ,MAAR;EACE,KAAK,gBAAgB,aACnB,QAAO;EACT,KAAK,gBAAgB,aACnB,QAAO;EACT,KAAK,gBAAgB;EACrB,KAAK,gBAAgB,aACnB,QAAO;EACT,KAAK,gBAAgB,cACnB,QAAO;EACT,KAAK,gBAAgB,uBACnB,QAAO;EACT,KAAK,gBAAgB,eACnB,QAAO;EACT,QAGE,QAAO;;;;;;;;;;AAgCb,IAAa,uBAAb,cAA0C,MAAM;CAC9C,AAAS;CAET,YAAY,MAAgC,SAAiB,UAAwB,EAAE,EAAE;AACvF,QAAM,SAAS,QAAQ;AACvB,OAAK,OAAO;AACZ,OAAK,OAAO;;;;AAKhB,IAAa,0BAAb,cAA6C,qBAAqB;CAChE,YAAY,UAAU,2DAA2D;AAC/E,QAAM,wBAAwB,QAAQ;AACtC,OAAK,OAAO;;;;AAKhB,IAAa,uBAAb,cAA0C,qBAAqB;CAC7D,AAAS;CAET,YAAY,SAAiB,UAAqD,EAAE,EAAE;AACpF,QAAM,oBAAoB,SAAS,QAAQ;AAC3C,OAAK,OAAO;AACZ,OAAK,OAAO,QAAQ;;;;AAKxB,IAAa,2BAAb,cAA8C,qBAAqB;CACjE,AAAS;CACT,AAAS;CAET,YAAY,UAAkB,QAAuB;AACnD,QACE,wBACA,gCAAgC,UAAU,SAAS,eAAe,SAAS,IAC5E;AACD,OAAK,OAAO;AACZ,OAAK,WAAW;AAChB,OAAK,SAAS;;;;AAKlB,IAAa,kBAAb,cAAqC,qBAAqB;CACxD,YAAY,UAAU,mEAAmE;AACvF,QAAM,eAAe,QAAQ;AAC7B,OAAK,OAAO;;;;AAKhB,IAAa,yBAAb,cAA4C,qBAAqB;CAC/D,YAAY,SAAiB,UAAwB,EAAE,EAAE;AACvD,QAAM,sBAAsB,SAAS,QAAQ;AAC7C,OAAK,OAAO;;;;AAKhB,IAAa,4BAAb,cAA+C,qBAAqB;CAClE,AAAS;CAET,YAAY,gBAAwB;AAClC,QACE,0BACA,iBAAiB,eAAe,iCACjC;AACD,OAAK,OAAO;AACZ,OAAK,iBAAiB;;;;AAK1B,IAAa,qBAAb,cAAwC,qBAAqB;CAC3D,YAAY,UAAU,wCAAwC;AAC5D,QAAM,WAAW,QAAQ;AACzB,OAAK,OAAO;;;;AAKhB,IAAa,0BAAb,cAA6C,qBAAqB;CAChE,AAAS;CAET,YACE,SACA,QACA;AACA,QAAM,wBAAwB,QAAQ;AACtC,OAAK,OAAO;AACZ,OAAK,SAAS"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { BackoffPolicy } from "./reconnect.js";
|
|
2
|
+
import { TransportAuth, TransportKind } from "./transport/types.js";
|
|
3
|
+
import "./transport/index.js";
|
|
4
|
+
import { ServerEventFrame } from "@graphorin/protocol";
|
|
5
|
+
|
|
6
|
+
//#region src/graphorin-client.d.ts
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Discriminator for the subscription target. Mirrors the strict
|
|
10
|
+
* subject grammar enforced by the server:
|
|
11
|
+
* - `'session'`/`<id>` ⇒ `'session:<id>/events'`
|
|
12
|
+
* - `'agent'`/`<id>` + `runId` ⇒ `'agent:<id>/runs/<runId>/events'`
|
|
13
|
+
* - `'run'`/`<runId>` ⇒ `'session:<sessionId>/runs/<runId>/events'`
|
|
14
|
+
* (when `sessionId` is provided)
|
|
15
|
+
* - `'workflow'`/`<id>` ⇒ `'workflow:<id>/events'`
|
|
16
|
+
*
|
|
17
|
+
* @stable
|
|
18
|
+
*/
|
|
19
|
+
type SubscriptionTarget = {
|
|
20
|
+
readonly target: 'session';
|
|
21
|
+
readonly id: string;
|
|
22
|
+
} | {
|
|
23
|
+
readonly target: 'agent';
|
|
24
|
+
readonly id: string;
|
|
25
|
+
readonly runId: string;
|
|
26
|
+
} | {
|
|
27
|
+
readonly target: 'run';
|
|
28
|
+
readonly runId: string;
|
|
29
|
+
readonly sessionId?: string;
|
|
30
|
+
} | {
|
|
31
|
+
readonly target: 'workflow';
|
|
32
|
+
readonly id: string;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Transport selector. `'auto'` (default) attempts a WebSocket
|
|
36
|
+
* handshake first and falls back to SSE on failure.
|
|
37
|
+
*
|
|
38
|
+
* @stable
|
|
39
|
+
*/
|
|
40
|
+
type TransportPreference = TransportKind | 'auto';
|
|
41
|
+
/**
|
|
42
|
+
* Public configuration accepted by {@link GraphorinClient}.
|
|
43
|
+
*
|
|
44
|
+
* @stable
|
|
45
|
+
*/
|
|
46
|
+
interface GraphorinClientOptions {
|
|
47
|
+
/**
|
|
48
|
+
* Session bound to the SSE fallback (IP-3): substituted into the
|
|
49
|
+
* `:sessionId` slot of `sseSessionPath`. Required to connect over
|
|
50
|
+
* SSE — the old client sent the literal template and could never
|
|
51
|
+
* receive an event.
|
|
52
|
+
*/
|
|
53
|
+
readonly sessionId?: string;
|
|
54
|
+
/**
|
|
55
|
+
* Server base URL. Examples: `'wss://graphorin.example.com'` or
|
|
56
|
+
* `'http://localhost:8080'`. The path `/v1/ws` is appended for
|
|
57
|
+
* the WebSocket transport.
|
|
58
|
+
*/
|
|
59
|
+
readonly baseUrl: string;
|
|
60
|
+
readonly auth: TransportAuth;
|
|
61
|
+
readonly transport?: TransportPreference;
|
|
62
|
+
/** Override the WS path (default `'/v1/ws'`). */
|
|
63
|
+
readonly wsPath?: string;
|
|
64
|
+
/**
|
|
65
|
+
* SSE path template. The placeholder `:sessionId` is replaced at
|
|
66
|
+
* subscribe time. Default: `'/v1/sessions/:sessionId/events'`.
|
|
67
|
+
* Required when the transport is `'sse'` or when the WS handshake
|
|
68
|
+
* fails on `'auto'`.
|
|
69
|
+
*/
|
|
70
|
+
readonly sseSessionPath?: string;
|
|
71
|
+
readonly reconnect?: BackoffPolicy;
|
|
72
|
+
/** Inject a `WebSocket` constructor (Node SDKs / tests). */
|
|
73
|
+
readonly WebSocket?: typeof WebSocket;
|
|
74
|
+
/** Inject an `EventSource` constructor (Node SDKs / tests). */
|
|
75
|
+
readonly EventSource?: typeof EventSource;
|
|
76
|
+
/** Inject a `fetch` implementation (defaults to `globalThis.fetch`). */
|
|
77
|
+
readonly fetch?: typeof fetch;
|
|
78
|
+
/** Optional client identifier surfaced on diagnostics. */
|
|
79
|
+
readonly clientId?: string;
|
|
80
|
+
/**
|
|
81
|
+
* IP-19: per-RPC reply timeout in milliseconds. When set (and > 0), an RPC
|
|
82
|
+
* that receives no matching reply within this window rejects with a
|
|
83
|
+
* {@link TransportFailedError} instead of hanging forever on a
|
|
84
|
+
* non-responsive server. Default: unset — no timeout, so a legitimately
|
|
85
|
+
* slow server reply is never aborted (opt-in).
|
|
86
|
+
*/
|
|
87
|
+
readonly rpcTimeoutMs?: number;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Snapshot returned by {@link Subscription.metadata}.
|
|
91
|
+
*
|
|
92
|
+
* @stable
|
|
93
|
+
*/
|
|
94
|
+
interface SubscriptionMetadata {
|
|
95
|
+
readonly id: string;
|
|
96
|
+
readonly subject: string;
|
|
97
|
+
readonly target: SubscriptionTarget;
|
|
98
|
+
readonly snapshotEventId: string | undefined;
|
|
99
|
+
readonly lastEventId: string | undefined;
|
|
100
|
+
readonly closed: boolean;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Public surface returned by {@link GraphorinClient.subscribe}.
|
|
104
|
+
*
|
|
105
|
+
* @stable
|
|
106
|
+
*/
|
|
107
|
+
interface Subscription {
|
|
108
|
+
readonly subscriptionId: string;
|
|
109
|
+
readonly subject: string;
|
|
110
|
+
events(): AsyncIterable<ServerEventFrame>;
|
|
111
|
+
/**
|
|
112
|
+
* Close the subscription on the server. Idempotent.
|
|
113
|
+
*/
|
|
114
|
+
unsubscribe(): Promise<void>;
|
|
115
|
+
metadata(): SubscriptionMetadata;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* @stable
|
|
119
|
+
*/
|
|
120
|
+
declare class GraphorinClient {
|
|
121
|
+
#private;
|
|
122
|
+
constructor(options: GraphorinClientOptions);
|
|
123
|
+
/**
|
|
124
|
+
* Open the underlying transport. Resolves once the server has
|
|
125
|
+
* accepted the handshake (`'open'`); rejects with a typed
|
|
126
|
+
* {@link GraphorinClientError} otherwise.
|
|
127
|
+
*
|
|
128
|
+
* Calling `connect()` while already connected is a no-op; calling
|
|
129
|
+
* it during another `connect()` returns the same promise.
|
|
130
|
+
*/
|
|
131
|
+
connect(): Promise<void>;
|
|
132
|
+
/** Send a `ping` RPC and resolve when the server replies with `pong`. */
|
|
133
|
+
ping(): Promise<void>;
|
|
134
|
+
/**
|
|
135
|
+
* Subscribe to a server-side event stream. Resolves with a
|
|
136
|
+
* {@link Subscription} once the server confirms with the matching
|
|
137
|
+
* `subscribed` frame; rejects when the server returns an
|
|
138
|
+
* `error` instead.
|
|
139
|
+
*/
|
|
140
|
+
subscribe(target: SubscriptionTarget, opts?: {
|
|
141
|
+
readonly sinceEventId?: string;
|
|
142
|
+
}): Promise<Subscription>;
|
|
143
|
+
/**
|
|
144
|
+
* Cancel a server-side run. Sends the `run.cancel` RPC and
|
|
145
|
+
* resolves with the server's `result` payload (typically
|
|
146
|
+
* `{ cancelled: true, partialStateAvailable: true }`).
|
|
147
|
+
*/
|
|
148
|
+
cancel(runId: string, opts?: {
|
|
149
|
+
readonly drain?: boolean;
|
|
150
|
+
readonly reason?: string;
|
|
151
|
+
readonly onPendingApprovals?: 'deny' | 'preserve';
|
|
152
|
+
}): Promise<unknown>;
|
|
153
|
+
/**
|
|
154
|
+
* Resume a paused (HITL) run. The WebSocket protocol intentionally
|
|
155
|
+
* does NOT carry a `resume` control message — resumes are durable
|
|
156
|
+
* + idempotent + body-carrying, which maps onto the REST endpoint
|
|
157
|
+
* `POST /v1/runs/:runId/resume`. NOTE (IP-14): the server endpoint
|
|
158
|
+
* currently answers **501** — server-side durable resume is not
|
|
159
|
+
* implemented yet. Library-mode callers resume directly:
|
|
160
|
+
* `agent.run(result.state, { directive })`.
|
|
161
|
+
*/
|
|
162
|
+
resume(runId: string, directive?: unknown, opts?: {
|
|
163
|
+
readonly idempotencyKey?: string;
|
|
164
|
+
}): Promise<unknown>;
|
|
165
|
+
/**
|
|
166
|
+
* Send an MCP-compatible cancellation notification. Does not wait
|
|
167
|
+
* for a server reply (notifications have no `id`).
|
|
168
|
+
*/
|
|
169
|
+
cancelNotify(requestId: string): void;
|
|
170
|
+
/**
|
|
171
|
+
* Disconnect the underlying transport and abort every pending RPC
|
|
172
|
+
* + subscription. Idempotent.
|
|
173
|
+
*/
|
|
174
|
+
disconnect(): Promise<void>;
|
|
175
|
+
/** Return the active transport kind (or `undefined` if not connected). */
|
|
176
|
+
get transportKind(): TransportKind | undefined;
|
|
177
|
+
}
|
|
178
|
+
//#endregion
|
|
179
|
+
export { GraphorinClient, GraphorinClientOptions, Subscription, SubscriptionMetadata, SubscriptionTarget, TransportPreference };
|
|
180
|
+
//# sourceMappingURL=graphorin-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graphorin-client.d.ts","names":[],"sources":["../src/graphorin-client.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;;KAsEY,kBAAA;;;;;;;;;;;;;;;;;;;;;KAoBA,mBAAA,GAAsB;;;;;;UAOjB,sBAAA;;;;;;;;;;;;;;iBAcA;uBACM;;;;;;;;;;uBAUA;;8BAEO;;gCAEE;;0BAEN;;;;;;;;;;;;;;;;;UAkBT,oBAAA;;;mBAGE;;;;;;;;;;UAWF,YAAA;;;YAGL,cAAc;;;;iBAIT;cACH;;;;;cAmBD,eAAA;;uBAYU;;;;;;;;;aAeJ;;UAkCH;;;;;;;oBAWJ;;MAEP,QAAQ;;;;;;;;;;MAoER;;;;;;;;;;;;MA+BA;;;;;;;;;;gBA4DiB;;uBAwBC"}
|