@zap-proto/web 0.1.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/LICENSE +21 -0
- package/README.md +142 -0
- package/dist/auth.d.ts +22 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +4 -0
- package/dist/auth.js.map +1 -0
- package/dist/client.d.ts +50 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +105 -0
- package/dist/client.js.map +1 -0
- package/dist/conn.d.ts +60 -0
- package/dist/conn.d.ts.map +1 -0
- package/dist/conn.js +145 -0
- package/dist/conn.js.map +1 -0
- package/dist/errors.d.ts +21 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +27 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +35 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +101 -0
- package/dist/server.js.map +1 -0
- package/dist/transport.d.ts +46 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +140 -0
- package/dist/transport.js.map +1 -0
- package/package.json +76 -0
- package/src/auth.ts +25 -0
- package/src/client.ts +165 -0
- package/src/conn.ts +172 -0
- package/src/errors.ts +30 -0
- package/src/index.ts +36 -0
- package/src/server.ts +146 -0
- package/src/transport.ts +173 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (C) 2025, Lux Industries Inc.
|
|
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,142 @@
|
|
|
1
|
+
# @zap-proto/web
|
|
2
|
+
|
|
3
|
+
Browser-frontend RPC over **ZAP** — a drop-in tRPC replacement for Next.js /
|
|
4
|
+
Remix / SvelteKit. Native ZAP envelopes over WebSocket binary frames, layered
|
|
5
|
+
cleanly on [`@zap-proto/zap`](https://github.com/zap-proto/ts) (the ZAP wire
|
|
6
|
+
runtime). `v0.1.0` — pre-1.0 by policy; no version tags until the full chain
|
|
7
|
+
works end-to-end.
|
|
8
|
+
|
|
9
|
+
> **Note on naming.** The foundation runtime is `@zap-proto/zap` (the canonical
|
|
10
|
+
> TypeScript ZAP runtime, repo `zap-proto/ts`). This package — the WebSocket
|
|
11
|
+
> frontend layer — is `@zap-proto/web`, repo `zap-proto/web`. It is **not**
|
|
12
|
+
> Cap'n Proto; ZAP is a zero-copy, zero-dependency binary format byte-compatible
|
|
13
|
+
> with the Go runtime `github.com/zap-proto/go` and `github.com/luxfi/zap`.
|
|
14
|
+
|
|
15
|
+
## What @zap-proto/web IS
|
|
16
|
+
|
|
17
|
+
- A **drop-in replacement for tRPC** across Hanzo's TS apps (esign, console,
|
|
18
|
+
platform), using native ZAP RPC over WebSocket instead of JSON-over-HTTP.
|
|
19
|
+
- An **isomorphic transport** — the same `WsTransport` works in Node (post
|
|
20
|
+
HTTP-upgrade, over the `ws` package) and in the browser (native `WebSocket`).
|
|
21
|
+
- A Node **`serve(httpServer, opts)`** harness that attaches to an existing
|
|
22
|
+
`http.Server`, so Next.js (`app.getRequestHandler()`) or a Remix Node server
|
|
23
|
+
share one port — integration is one line.
|
|
24
|
+
- A browser **`connect(url, opts)`** that opens the WS, waits for `open`, and
|
|
25
|
+
returns the bootstrap call surface typed by your generated bindings.
|
|
26
|
+
- A **`mintCap` boundary hook** at the WS upgrade so apps plug their own
|
|
27
|
+
bearer→context minting logic.
|
|
28
|
+
|
|
29
|
+
## What @zap-proto/web is NOT
|
|
30
|
+
|
|
31
|
+
- ❌ **Not a Zod/procedure DSL** like tRPC. Service shape comes from `.zap`
|
|
32
|
+
schemas (`struct`s + method ordinals), codegen'd via
|
|
33
|
+
`zapgen --target=ts` from [`zap-proto/ts`](https://github.com/zap-proto/ts).
|
|
34
|
+
- ❌ **Not a JSON wrapper.** Wire format is ZAP envelopes over WS **binary**
|
|
35
|
+
messages. Text frames are a protocol violation and close the socket with WS
|
|
36
|
+
code `1003`.
|
|
37
|
+
- ❌ **Not Cap'n Proto.** ZAP is its own zero-copy format. The bootstrap is the
|
|
38
|
+
connection's call surface (`bootstrap.call(method, { payload })`), not a
|
|
39
|
+
Cap'n Proto capability object.
|
|
40
|
+
- ❌ **Not a bearer-JWT framework.** `mintCap` is a **slot** — apps provide a fn
|
|
41
|
+
`(upgradeReq) => Promise<Ctx | null>`. `v0` ships the slot; the canonical
|
|
42
|
+
ML-DSA-65 signed `Capability` mint (per
|
|
43
|
+
[`zap-spec/capabilities.zap`](https://github.com/zap-proto/spec) — the
|
|
44
|
+
3408-byte signature footer, BLAKE3 identity, attenuable/revocable authority)
|
|
45
|
+
lands in a later version. Until then, whatever non-null value `mintCap`
|
|
46
|
+
returns becomes the per-connection `ctx`.
|
|
47
|
+
|
|
48
|
+
## Install
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
pnpm i @zap-proto/web @zap-proto/zap
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
> `@zap-proto/zap` is not yet published to npm. The `dependencies` entry already
|
|
55
|
+
> ships plain semver (`^0.1.0`); until it lands on npm, local tests resolve the
|
|
56
|
+
> specifier to the sibling source tree (`../ts`, the `zap-proto/ts` repo) via the
|
|
57
|
+
> `vitest.config.ts` alias and the `tsconfig.json` path. No change needed once
|
|
58
|
+
> `@zap-proto/zap` ships — just drop the alias.
|
|
59
|
+
|
|
60
|
+
## Quick start — Next.js (≈30 lines)
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
// server.ts
|
|
64
|
+
import http from "node:http";
|
|
65
|
+
import next from "next";
|
|
66
|
+
import { serve } from "@zap-proto/web/server";
|
|
67
|
+
import { newGreeting, HelloArgs } from "./gen/hello_zap.js"; // zapgen --target=ts
|
|
68
|
+
|
|
69
|
+
const app = next({ dev: process.env.NODE_ENV !== "production" });
|
|
70
|
+
const handle = app.getRequestHandler();
|
|
71
|
+
|
|
72
|
+
await app.prepare();
|
|
73
|
+
const server = http.createServer((req, res) => handle(req, res));
|
|
74
|
+
|
|
75
|
+
serve(server, {
|
|
76
|
+
path: "/zap",
|
|
77
|
+
// mintCap SLOT: turn a bearer into your app's ctx. Return null → HTTP 401.
|
|
78
|
+
mintCap: async (req) => {
|
|
79
|
+
const auth = req.headers.authorization;
|
|
80
|
+
return auth ? { org: await orgFromBearer(auth) } : null;
|
|
81
|
+
},
|
|
82
|
+
// rootCap(ctx): dispatch every decoded ZAP Call for this connection.
|
|
83
|
+
rootCap: (ctx) => (call) => {
|
|
84
|
+
const args = HelloArgs.wrap(call.payload);
|
|
85
|
+
return {
|
|
86
|
+
status: 200,
|
|
87
|
+
promiseID: call.promiseID,
|
|
88
|
+
body: newGreeting({ text: `hi ${args.name} @ ${ctx.org}` }),
|
|
89
|
+
};
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
server.listen(3000);
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
// client.ts (browser)
|
|
98
|
+
import { connect } from "@zap-proto/web/client";
|
|
99
|
+
import { newHelloArgs, Greeting } from "./gen/hello_zap.js";
|
|
100
|
+
|
|
101
|
+
const { bootstrap, close } = await connect("wss://app.hanzo.ai/zap", {
|
|
102
|
+
bearer: sessionToken, // forwarded as Authorization on the upgrade (Node ws)
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const resp = await bootstrap.call(/* method */ 0, {
|
|
106
|
+
payload: newHelloArgs({ name: "ada" }),
|
|
107
|
+
});
|
|
108
|
+
console.log(Greeting.wrap(resp.body).text); // "hi ada @ acme"
|
|
109
|
+
close();
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
> **Browser bearer caveat.** The browser's native `WebSocket` cannot set an
|
|
113
|
+
> `Authorization` header. In the browser, `connect({ bearer })` falls back to a
|
|
114
|
+
> `?authorization=Bearer%20…` query param the server can read; prefer a cookie
|
|
115
|
+
> sent automatically on the upgrade for production. The header path applies to a
|
|
116
|
+
> header-capable `WebSocketImpl` (the `ws` package, used in Node/SSR/tests).
|
|
117
|
+
|
|
118
|
+
## Schema → code
|
|
119
|
+
|
|
120
|
+
Service shape is `.zap` schemas compiled with the ZAP compiler from
|
|
121
|
+
[`zap-proto/ts`](https://github.com/zap-proto/ts):
|
|
122
|
+
|
|
123
|
+
```sh
|
|
124
|
+
zapgen --target=ts -single hello.zap # → hello_zap.ts (views + builders)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Method ordinals (the interface method `@n`) are the integers you pass to
|
|
128
|
+
`bootstrap.call(method, …)` and match in your `rootCap` dispatch.
|
|
129
|
+
|
|
130
|
+
## API
|
|
131
|
+
|
|
132
|
+
| Export | Entry | Purpose |
|
|
133
|
+
| --- | --- | --- |
|
|
134
|
+
| `serve(httpServer, opts)` | `@zap-proto/web/server` | Attach a ZAP RPC endpoint to a Node `http.Server`. |
|
|
135
|
+
| `connect(url, opts?)` | `@zap-proto/web/client` | Open a ZAP RPC WebSocket; returns `{ bootstrap, close }`. |
|
|
136
|
+
| `nodeWsTransport(ws)` / `browserWsTransport(ws)` | `@zap-proto/web/transport` | Wrap a `ws`/native WebSocket as a `WsTransport`. |
|
|
137
|
+
| `MintCap<Ctx>` | `@zap-proto/web/auth` | The bearer→ctx slot. |
|
|
138
|
+
| `WebRpcError` | `@zap-proto/web` | Transport-level RPC failure (rejected upgrade, mid-call close, non-OK status). |
|
|
139
|
+
|
|
140
|
+
## License
|
|
141
|
+
|
|
142
|
+
MIT © Lux Industries Inc.
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { IncomingMessage } from "node:http";
|
|
2
|
+
/**
|
|
3
|
+
* MintCap is the boundary where a bearer (cookie, header, session) becomes a
|
|
4
|
+
* typed Capability that downstream RPC methods are authorized against.
|
|
5
|
+
*
|
|
6
|
+
* v0 ships the SLOT — apps supply their own fn. The canonical ML-DSA-65 signed
|
|
7
|
+
* Capability mint (per zap-spec/capabilities.zap — the 3408-byte signature
|
|
8
|
+
* footer, BLAKE3 object identity, attenuable/revocable authority) lands in a
|
|
9
|
+
* later version. Until then you can return any object: whatever non-null value
|
|
10
|
+
* you return becomes the `ctx` threaded into your `rootCap(ctx)` factory and
|
|
11
|
+
* carried on every {@link import("@zap-proto/zap").Call} as its capability buffer.
|
|
12
|
+
*
|
|
13
|
+
* Return `null` to reject the connection with HTTP 401 before the WebSocket
|
|
14
|
+
* upgrade completes — no socket is opened for an unauthorized request.
|
|
15
|
+
*
|
|
16
|
+
* @typeParam Ctx - the minted authority/context type your handlers consume.
|
|
17
|
+
* @param req - the raw HTTP upgrade request (read `req.headers.authorization`,
|
|
18
|
+
* cookies, etc.).
|
|
19
|
+
* @returns the minted context, or `null` to reject with 401.
|
|
20
|
+
*/
|
|
21
|
+
export type MintCap<Ctx> = (req: IncomingMessage) => Promise<Ctx | null>;
|
|
22
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAEjD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,eAAe,KAAK,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC"}
|
package/dist/auth.js
ADDED
package/dist/auth.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,4CAA4C"}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* client.ts — connect(url, opts): open a ZAP-over-WebSocket RPC connection from
|
|
3
|
+
* the browser (or Node, for tests) and return the bootstrap call surface.
|
|
4
|
+
*
|
|
5
|
+
* The transport is isomorphic: in the browser this uses the native WebSocket
|
|
6
|
+
* global; in Node you inject the `ws` package's WebSocket via
|
|
7
|
+
* {@link ConnectOptions.WebSocketImpl} (Node 22+ also has a built-in global
|
|
8
|
+
* WebSocket, but it cannot set request headers — see the bearer note below).
|
|
9
|
+
*
|
|
10
|
+
* Bearer note: the browser's native WebSocket constructor CANNOT set an
|
|
11
|
+
* Authorization header — the WS protocol gives JS no header control. So:
|
|
12
|
+
* - Node `ws` (tests, SSR): we pass `{ headers: { Authorization } }`, which
|
|
13
|
+
* `ws` forwards on the upgrade request, where serve()'s mintCap reads it.
|
|
14
|
+
* - browser: rely on a cookie sent automatically on the upgrade, or encode
|
|
15
|
+
* the bearer in a subprotocol; the Authorization-header path only applies
|
|
16
|
+
* to a header-capable WebSocketImpl. We feature-detect by arity and fall
|
|
17
|
+
* back to a query param (`?authorization=Bearer%20...`) the server can read.
|
|
18
|
+
*/
|
|
19
|
+
import { Conn } from "./conn.js";
|
|
20
|
+
/** Options for {@link connect}. */
|
|
21
|
+
export interface ConnectOptions {
|
|
22
|
+
/** Bearer token; forwarded as `Authorization: Bearer <bearer>` on upgrade. */
|
|
23
|
+
bearer?: string;
|
|
24
|
+
/**
|
|
25
|
+
* WebSocket constructor to use. Defaults to globalThis.WebSocket (browser /
|
|
26
|
+
* Node 22+). Inject the `ws` package's WebSocket in Node tests/SSR so the
|
|
27
|
+
* bearer can ride an Authorization header.
|
|
28
|
+
*/
|
|
29
|
+
WebSocketImpl?: typeof WebSocket;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The bootstrap call surface returned by {@link connect}. RootCap is the
|
|
33
|
+
* generated typed wrapper a consumer layers over the raw {@link Conn} call
|
|
34
|
+
* surface — `connect<MyRootCap>(...)` then exposes typed methods on `bootstrap`.
|
|
35
|
+
*
|
|
36
|
+
* On the real @zap-proto/zap foundation the bootstrap is the connection's call
|
|
37
|
+
* surface (method ordinal + ZAP payload), not a Cap'n Proto capability object.
|
|
38
|
+
* The default `RootCap = Conn` gives you raw `bootstrap.call(method, { payload })`;
|
|
39
|
+
* a zapgen-generated client wraps that into named, typed methods.
|
|
40
|
+
*/
|
|
41
|
+
export interface Connection<RootCap = Conn> {
|
|
42
|
+
bootstrap: RootCap;
|
|
43
|
+
close(): void;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Open a ZAP RPC WebSocket to `url`, wait for the connection to open, and
|
|
47
|
+
* return the bootstrap call surface. The default RootCap is the raw {@link Conn}.
|
|
48
|
+
*/
|
|
49
|
+
export declare function connect<RootCap = Conn>(url: string, opts?: ConnectOptions): Promise<Connection<RootCap>>;
|
|
50
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAIjC,mCAAmC;AACnC,MAAM,WAAW,cAAc;IAC7B,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,SAAS,CAAC;CAClC;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,UAAU,CAAC,OAAO,GAAG,IAAI;IACxC,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,IAAI,IAAI,CAAC;CACf;AAED;;;GAGG;AACH,wBAAsB,OAAO,CAAC,OAAO,GAAG,IAAI,EAC1C,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,cAAmB,GACxB,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAgD9B"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
|
|
2
|
+
// See the file LICENSE for licensing terms.
|
|
3
|
+
/**
|
|
4
|
+
* client.ts — connect(url, opts): open a ZAP-over-WebSocket RPC connection from
|
|
5
|
+
* the browser (or Node, for tests) and return the bootstrap call surface.
|
|
6
|
+
*
|
|
7
|
+
* The transport is isomorphic: in the browser this uses the native WebSocket
|
|
8
|
+
* global; in Node you inject the `ws` package's WebSocket via
|
|
9
|
+
* {@link ConnectOptions.WebSocketImpl} (Node 22+ also has a built-in global
|
|
10
|
+
* WebSocket, but it cannot set request headers — see the bearer note below).
|
|
11
|
+
*
|
|
12
|
+
* Bearer note: the browser's native WebSocket constructor CANNOT set an
|
|
13
|
+
* Authorization header — the WS protocol gives JS no header control. So:
|
|
14
|
+
* - Node `ws` (tests, SSR): we pass `{ headers: { Authorization } }`, which
|
|
15
|
+
* `ws` forwards on the upgrade request, where serve()'s mintCap reads it.
|
|
16
|
+
* - browser: rely on a cookie sent automatically on the upgrade, or encode
|
|
17
|
+
* the bearer in a subprotocol; the Authorization-header path only applies
|
|
18
|
+
* to a header-capable WebSocketImpl. We feature-detect by arity and fall
|
|
19
|
+
* back to a query param (`?authorization=Bearer%20...`) the server can read.
|
|
20
|
+
*/
|
|
21
|
+
import { Conn } from "./conn.js";
|
|
22
|
+
import { browserWsTransport } from "./transport.js";
|
|
23
|
+
import { WebRpcError } from "./errors.js";
|
|
24
|
+
/**
|
|
25
|
+
* Open a ZAP RPC WebSocket to `url`, wait for the connection to open, and
|
|
26
|
+
* return the bootstrap call surface. The default RootCap is the raw {@link Conn}.
|
|
27
|
+
*/
|
|
28
|
+
export async function connect(url, opts = {}) {
|
|
29
|
+
const Impl = opts.WebSocketImpl ?? globalThis.WebSocket;
|
|
30
|
+
if (!Impl) {
|
|
31
|
+
throw new WebRpcError("zap-web: no WebSocket implementation (pass opts.WebSocketImpl)");
|
|
32
|
+
}
|
|
33
|
+
const ws = openSocket(Impl, url, opts.bearer);
|
|
34
|
+
await new Promise((resolve, reject) => {
|
|
35
|
+
const onOpen = () => {
|
|
36
|
+
cleanup();
|
|
37
|
+
resolve();
|
|
38
|
+
};
|
|
39
|
+
const onErr = () => {
|
|
40
|
+
cleanup();
|
|
41
|
+
reject(new WebRpcError(`zap-web: failed to connect to ${url}`));
|
|
42
|
+
};
|
|
43
|
+
const onClose = (ev) => {
|
|
44
|
+
cleanup();
|
|
45
|
+
const code = ev?.code;
|
|
46
|
+
reject(new WebRpcError(`zap-web: upgrade rejected by ${url}`, code === 1006 ? 401 : code));
|
|
47
|
+
};
|
|
48
|
+
const cleanup = () => {
|
|
49
|
+
remove(ws, "open", onOpen);
|
|
50
|
+
remove(ws, "error", onErr);
|
|
51
|
+
remove(ws, "close", onClose);
|
|
52
|
+
};
|
|
53
|
+
add(ws, "open", onOpen);
|
|
54
|
+
add(ws, "error", onErr);
|
|
55
|
+
add(ws, "close", onClose);
|
|
56
|
+
});
|
|
57
|
+
const transport = browserWsTransport(ws);
|
|
58
|
+
const conn = new Conn(transport);
|
|
59
|
+
return {
|
|
60
|
+
bootstrap: conn,
|
|
61
|
+
close() {
|
|
62
|
+
conn.close(1000, "client closed");
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Construct a WebSocket, attaching the bearer the best way the impl allows.
|
|
68
|
+
* Node `ws` accepts a 3rd `options.headers` arg; the browser native WebSocket
|
|
69
|
+
* does not, so we fall back to a query param the server can read.
|
|
70
|
+
*/
|
|
71
|
+
function openSocket(Impl, url, bearer) {
|
|
72
|
+
if (!bearer)
|
|
73
|
+
return new Impl(url);
|
|
74
|
+
// Node `ws` WebSocket: third constructor arg is options { headers }.
|
|
75
|
+
// We detect header support by the constructor's declared arity (>= 3).
|
|
76
|
+
if (Impl.length >= 3) {
|
|
77
|
+
return new Impl(url, undefined, {
|
|
78
|
+
headers: { Authorization: `Bearer ${bearer}` },
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
// Browser native WebSocket: no header control — encode on the URL instead.
|
|
82
|
+
const u = new URL(url);
|
|
83
|
+
u.searchParams.set("authorization", `Bearer ${bearer}`);
|
|
84
|
+
return new Impl(u.toString());
|
|
85
|
+
}
|
|
86
|
+
// --- isomorphic add/remove listener (browser EventTarget vs `ws` emitter) ---
|
|
87
|
+
function add(ws, type, fn) {
|
|
88
|
+
const anyWs = ws;
|
|
89
|
+
if (typeof anyWs.addEventListener === "function") {
|
|
90
|
+
anyWs.addEventListener(type, fn);
|
|
91
|
+
}
|
|
92
|
+
else if (typeof anyWs.on === "function") {
|
|
93
|
+
anyWs.on(type, fn);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function remove(ws, type, fn) {
|
|
97
|
+
const anyWs = ws;
|
|
98
|
+
if (typeof anyWs.removeEventListener === "function") {
|
|
99
|
+
anyWs.removeEventListener(type, fn);
|
|
100
|
+
}
|
|
101
|
+
else if (typeof anyWs.off === "function") {
|
|
102
|
+
anyWs.off(type, fn);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/conn.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* conn.ts — a transport-agnostic ZAP RPC connection over a {@link WsTransport}.
|
|
3
|
+
*
|
|
4
|
+
* This is the shared core of both serve() and connect(). It mirrors the
|
|
5
|
+
* correlation logic of @zap-proto/zap's ZapClient (Node TCP), but rides a duplex
|
|
6
|
+
* WebSocket transport instead of a TCP socket and works in both directions:
|
|
7
|
+
*
|
|
8
|
+
* - client side: call() ships a request envelope and resolves its Response
|
|
9
|
+
* when the matching promiseID comes back.
|
|
10
|
+
* - server side: an inbound request envelope is decoded into a Call and
|
|
11
|
+
* handed to the registered handler, whose Response is shipped back.
|
|
12
|
+
*
|
|
13
|
+
* Each WS binary message is exactly one ZAP envelope (see transport.ts), so the
|
|
14
|
+
* envelope's own header tag (MSG_TYPE_ROUTER_BASE) distinguishes a request from
|
|
15
|
+
* a response and there is no extra framing. We disambiguate direction by trying
|
|
16
|
+
* the request decode first; a frame that parses as a request with a known
|
|
17
|
+
* method is dispatched as a server call, otherwise it is treated as a response
|
|
18
|
+
* to one of our outstanding promises.
|
|
19
|
+
*/
|
|
20
|
+
import { type Call, type Response } from "@zap-proto/zap";
|
|
21
|
+
import type { WsTransport } from "./transport.js";
|
|
22
|
+
/** A server-side request handler: a decoded Call in, a Response out. */
|
|
23
|
+
export type CallHandler = (call: Call) => Promise<Response> | Response;
|
|
24
|
+
/** Options for an outbound {@link Conn.call}. */
|
|
25
|
+
export interface CallOptions {
|
|
26
|
+
/** Pipeline this call off an earlier call's promiseID (0 = root). */
|
|
27
|
+
target?: number;
|
|
28
|
+
/** Capability buffer to carry on the call (defaults to the conn's cap). */
|
|
29
|
+
cap?: Uint8Array;
|
|
30
|
+
/** Method params, ZAP-encoded. */
|
|
31
|
+
payload?: Uint8Array;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Conn correlates ZAP request/response envelopes over a single duplex
|
|
35
|
+
* WebSocket transport. One Conn is FIFO per direction on the wire, matching the
|
|
36
|
+
* Go node's per-connection dispatch.
|
|
37
|
+
*/
|
|
38
|
+
export declare class Conn {
|
|
39
|
+
private readonly transport;
|
|
40
|
+
private readonly pending;
|
|
41
|
+
private promiseSeq;
|
|
42
|
+
private closed;
|
|
43
|
+
private handler;
|
|
44
|
+
private readonly cap;
|
|
45
|
+
constructor(transport: WsTransport, opts?: {
|
|
46
|
+
handler?: CallHandler;
|
|
47
|
+
cap?: Uint8Array;
|
|
48
|
+
});
|
|
49
|
+
/**
|
|
50
|
+
* Ship one Call and await its correlated Response. The promiseID is assigned
|
|
51
|
+
* here unless the caller pipelines via {@link CallOptions.target}.
|
|
52
|
+
*/
|
|
53
|
+
call(method: number, opts?: CallOptions): Promise<Response>;
|
|
54
|
+
/** Close the connection and fail all in-flight calls. */
|
|
55
|
+
close(code?: number, reason?: string): void;
|
|
56
|
+
private onFrame;
|
|
57
|
+
private dispatch;
|
|
58
|
+
private fail;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=conn.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conn.d.ts","sourceRoot":"","sources":["../src/conn.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAOL,KAAK,IAAI,EACT,KAAK,QAAQ,EACd,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAGlD,wEAAwE;AACxE,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAC;AAEvE,iDAAiD;AACjD,MAAM,WAAW,WAAW;IAC1B,qEAAqE;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2EAA2E;IAC3E,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,kCAAkC;IAClC,OAAO,CAAC,EAAE,UAAU,CAAC;CACtB;AAID;;;;GAIG;AACH,qBAAa,IAAI;IAQb,OAAO,CAAC,QAAQ,CAAC,SAAS;IAP5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4C;IACpE,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAa;gBAGd,SAAS,EAAE,WAAW,EACvC,IAAI,GAAE;QAAE,OAAO,CAAC,EAAE,WAAW,CAAC;QAAC,GAAG,CAAC,EAAE,UAAU,CAAA;KAAO;IAQxD;;;OAGG;IACH,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAwB/D,yDAAyD;IACzD,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAM3C,OAAO,CAAC,OAAO;YAmBD,QAAQ;IA8BtB,OAAO,CAAC,IAAI;CAcb"}
|
package/dist/conn.js
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
|
|
2
|
+
// See the file LICENSE for licensing terms.
|
|
3
|
+
/**
|
|
4
|
+
* conn.ts — a transport-agnostic ZAP RPC connection over a {@link WsTransport}.
|
|
5
|
+
*
|
|
6
|
+
* This is the shared core of both serve() and connect(). It mirrors the
|
|
7
|
+
* correlation logic of @zap-proto/zap's ZapClient (Node TCP), but rides a duplex
|
|
8
|
+
* WebSocket transport instead of a TCP socket and works in both directions:
|
|
9
|
+
*
|
|
10
|
+
* - client side: call() ships a request envelope and resolves its Response
|
|
11
|
+
* when the matching promiseID comes back.
|
|
12
|
+
* - server side: an inbound request envelope is decoded into a Call and
|
|
13
|
+
* handed to the registered handler, whose Response is shipped back.
|
|
14
|
+
*
|
|
15
|
+
* Each WS binary message is exactly one ZAP envelope (see transport.ts), so the
|
|
16
|
+
* envelope's own header tag (MSG_TYPE_ROUTER_BASE) distinguishes a request from
|
|
17
|
+
* a response and there is no extra framing. We disambiguate direction by trying
|
|
18
|
+
* the request decode first; a frame that parses as a request with a known
|
|
19
|
+
* method is dispatched as a server call, otherwise it is treated as a response
|
|
20
|
+
* to one of our outstanding promises.
|
|
21
|
+
*/
|
|
22
|
+
import { buildRequest, buildResponse, parseRequest, parseResponse, NO_TARGET, Status, } from "@zap-proto/zap";
|
|
23
|
+
import { WebRpcError } from "./errors.js";
|
|
24
|
+
const EMPTY = new Uint8Array(0);
|
|
25
|
+
/**
|
|
26
|
+
* Conn correlates ZAP request/response envelopes over a single duplex
|
|
27
|
+
* WebSocket transport. One Conn is FIFO per direction on the wire, matching the
|
|
28
|
+
* Go node's per-connection dispatch.
|
|
29
|
+
*/
|
|
30
|
+
export class Conn {
|
|
31
|
+
transport;
|
|
32
|
+
pending = new Map();
|
|
33
|
+
promiseSeq = 0;
|
|
34
|
+
closed = false;
|
|
35
|
+
handler = null;
|
|
36
|
+
cap;
|
|
37
|
+
constructor(transport, opts = {}) {
|
|
38
|
+
this.transport = transport;
|
|
39
|
+
this.handler = opts.handler ?? null;
|
|
40
|
+
this.cap = opts.cap ?? EMPTY;
|
|
41
|
+
transport.onMessage((bytes) => this.onFrame(bytes));
|
|
42
|
+
transport.onClose(() => this.fail());
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Ship one Call and await its correlated Response. The promiseID is assigned
|
|
46
|
+
* here unless the caller pipelines via {@link CallOptions.target}.
|
|
47
|
+
*/
|
|
48
|
+
call(method, opts = {}) {
|
|
49
|
+
if (this.closed) {
|
|
50
|
+
return Promise.reject(new WebRpcError("zap-web: connection closed"));
|
|
51
|
+
}
|
|
52
|
+
this.promiseSeq = (this.promiseSeq + 1) >>> 0 || 1;
|
|
53
|
+
const promiseID = this.promiseSeq;
|
|
54
|
+
const c = {
|
|
55
|
+
method,
|
|
56
|
+
promiseID,
|
|
57
|
+
target: opts.target ?? NO_TARGET,
|
|
58
|
+
cap: opts.cap ?? this.cap,
|
|
59
|
+
payload: opts.payload ?? EMPTY,
|
|
60
|
+
};
|
|
61
|
+
return new Promise((resolve, reject) => {
|
|
62
|
+
this.pending.set(promiseID, resolve);
|
|
63
|
+
try {
|
|
64
|
+
this.transport.send(buildRequest(c));
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
this.pending.delete(promiseID);
|
|
68
|
+
reject(err instanceof Error ? err : new WebRpcError(String(err)));
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/** Close the connection and fail all in-flight calls. */
|
|
73
|
+
close(code, reason) {
|
|
74
|
+
if (this.closed)
|
|
75
|
+
return;
|
|
76
|
+
this.transport.close(code, reason);
|
|
77
|
+
this.fail();
|
|
78
|
+
}
|
|
79
|
+
onFrame(bytes) {
|
|
80
|
+
if (this.handler) {
|
|
81
|
+
// Server side: every inbound frame is a request to dispatch.
|
|
82
|
+
this.dispatch(bytes);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
// Client side: every inbound frame is a response to one of our calls.
|
|
86
|
+
let resp;
|
|
87
|
+
try {
|
|
88
|
+
resp = parseResponse(bytes);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return; // malformed frame from peer — drop it.
|
|
92
|
+
}
|
|
93
|
+
const resolve = this.pending.get(resp.promiseID);
|
|
94
|
+
if (!resolve)
|
|
95
|
+
return;
|
|
96
|
+
this.pending.delete(resp.promiseID);
|
|
97
|
+
resolve(resp);
|
|
98
|
+
}
|
|
99
|
+
async dispatch(bytes) {
|
|
100
|
+
let call;
|
|
101
|
+
try {
|
|
102
|
+
call = parseRequest(bytes);
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return; // unparseable request — drop it.
|
|
106
|
+
}
|
|
107
|
+
const handler = this.handler;
|
|
108
|
+
if (!handler)
|
|
109
|
+
return;
|
|
110
|
+
let resp;
|
|
111
|
+
try {
|
|
112
|
+
resp = await handler(call);
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
const msg = err instanceof Error ? err.message : "internal handler error";
|
|
116
|
+
resp = {
|
|
117
|
+
status: Status.Internal,
|
|
118
|
+
promiseID: call.promiseID,
|
|
119
|
+
body: new TextEncoder().encode(JSON.stringify({ error: msg })),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
if (this.closed)
|
|
123
|
+
return;
|
|
124
|
+
try {
|
|
125
|
+
this.transport.send(buildResponse(resp.status, call.promiseID, resp.body));
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// peer went away mid-response — nothing to do.
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
fail() {
|
|
132
|
+
if (this.closed)
|
|
133
|
+
return;
|
|
134
|
+
this.closed = true;
|
|
135
|
+
for (const [, resolve] of this.pending) {
|
|
136
|
+
resolve({
|
|
137
|
+
status: 0,
|
|
138
|
+
promiseID: 0,
|
|
139
|
+
body: new TextEncoder().encode(JSON.stringify({ error: "connection closed" })),
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
this.pending.clear();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=conn.js.map
|
package/dist/conn.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conn.js","sourceRoot":"","sources":["../src/conn.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* errors.ts — error surface for the web RPC layer.
|
|
3
|
+
*
|
|
4
|
+
* Method/wire-level decode failures keep using @zap-proto/zap's ZapParseError (the
|
|
5
|
+
* runtime throws it on a bad magic/version/size); we re-export it so consumers
|
|
6
|
+
* have one import site. WebRpcError is new: it covers transport-level failures
|
|
7
|
+
* that only exist at the WebSocket boundary — a rejected upgrade, a socket that
|
|
8
|
+
* closes mid-call, or a non-OK RPC status surfaced to the caller.
|
|
9
|
+
*/
|
|
10
|
+
export { ZapParseError } from "@zap-proto/zap";
|
|
11
|
+
/**
|
|
12
|
+
* WebRpcError is raised for WebSocket-transport-level failures: the upgrade was
|
|
13
|
+
* rejected (401/other), the socket closed while a call was in flight, or a
|
|
14
|
+
* call returned a non-OK ZAP status. `status` carries the ZAP/HTTP status when
|
|
15
|
+
* one is known (e.g. 401 for a rejected mint, or the response envelope status).
|
|
16
|
+
*/
|
|
17
|
+
export declare class WebRpcError extends Error {
|
|
18
|
+
readonly status?: number;
|
|
19
|
+
constructor(message: string, status?: number);
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C;;;;;GAKG;AACH,qBAAa,WAAY,SAAQ,KAAK;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;gBAEb,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;CAK7C"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
|
|
2
|
+
// See the file LICENSE for licensing terms.
|
|
3
|
+
/**
|
|
4
|
+
* errors.ts — error surface for the web RPC layer.
|
|
5
|
+
*
|
|
6
|
+
* Method/wire-level decode failures keep using @zap-proto/zap's ZapParseError (the
|
|
7
|
+
* runtime throws it on a bad magic/version/size); we re-export it so consumers
|
|
8
|
+
* have one import site. WebRpcError is new: it covers transport-level failures
|
|
9
|
+
* that only exist at the WebSocket boundary — a rejected upgrade, a socket that
|
|
10
|
+
* closes mid-call, or a non-OK RPC status surfaced to the caller.
|
|
11
|
+
*/
|
|
12
|
+
export { ZapParseError } from "@zap-proto/zap";
|
|
13
|
+
/**
|
|
14
|
+
* WebRpcError is raised for WebSocket-transport-level failures: the upgrade was
|
|
15
|
+
* rejected (401/other), the socket closed while a call was in flight, or a
|
|
16
|
+
* call returned a non-OK ZAP status. `status` carries the ZAP/HTTP status when
|
|
17
|
+
* one is known (e.g. 401 for a rejected mint, or the response envelope status).
|
|
18
|
+
*/
|
|
19
|
+
export class WebRpcError extends Error {
|
|
20
|
+
status;
|
|
21
|
+
constructor(message, status) {
|
|
22
|
+
super(message);
|
|
23
|
+
this.name = "WebRpcError";
|
|
24
|
+
this.status = status;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,4CAA4C;AAE5C;;;;;;;;GAQG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,OAAO,WAAY,SAAQ,KAAK;IAC3B,MAAM,CAAU;IAEzB,YAAY,OAAe,EAAE,MAAe;QAC1C,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;QAC1B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF"}
|