@tapflowio/protocol 0.17.0 → 0.19.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/dist/index.d.ts +748 -92
- package/dist/index.d.ts.map +1 -1
- package/dist/validate/assert.d.ts +68 -0
- package/dist/validate/assert.d.ts.map +1 -0
- package/dist/validate/assert.js +2 -0
- package/dist/validate/assert.js.map +1 -0
- package/dist/validate/index.d.ts +563 -0
- package/dist/validate/index.d.ts.map +1 -0
- package/dist/validate/index.js +364 -0
- package/dist/validate/index.js.map +1 -0
- package/package.json +19 -7
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime validation for everything the relay receives — the half of the contract types cannot hold.
|
|
3
|
+
*
|
|
4
|
+
* `../index.ts` is types only, and deliberately so: the dashboard consumes it with `import type`, so it
|
|
5
|
+
* must erase completely. This entry point is where the runtime lives, reached as
|
|
6
|
+
* `@tapflowio/protocol/validate`, and only the relay imports it. `../AGENTS.md` reserved this spot.
|
|
7
|
+
*
|
|
8
|
+
* ## Why this exists at all
|
|
9
|
+
*
|
|
10
|
+
* The outbound direction has been compile-checked since #419 — `sendTo` refuses a message outside its
|
|
11
|
+
* union. Nothing checked the inbound direction, so the relay reached for `msg.sessionId!` and
|
|
12
|
+
* `msg.payload as X` on values that arrived over a network. #550 asked for `RelayMessage` (a flat
|
|
13
|
+
* interface where `type` is the only required member) to become a discriminated union, and doing that
|
|
14
|
+
* with a bare `as` at the door would have been a **downgrade**: the visible cast at
|
|
15
|
+
* `RelayServer.ts:753` would have become an invisible `msg.payload`, with the compiler now vouching
|
|
16
|
+
* for an attacker's JSON. So the union has to be the product of a parse, and this is that parse.
|
|
17
|
+
*
|
|
18
|
+
* ## The rule the two tiers come from
|
|
19
|
+
*
|
|
20
|
+
* **A field that was not validated must not appear in the type.** Anything else reintroduces the lie
|
|
21
|
+
* in a quieter place.
|
|
22
|
+
*
|
|
23
|
+
* - **Validated** — the full schema; parsing yields the interface. Used where the relay reads or acts
|
|
24
|
+
* on the message.
|
|
25
|
+
* - **Envelope** — `type` plus whichever of `sessionId` / `requestId` the interface declares; parsing
|
|
26
|
+
* yields `EnvelopeOf<I>`, so the relay **cannot** read a payload it did not check. Used where the
|
|
27
|
+
* relay only forwards.
|
|
28
|
+
*
|
|
29
|
+
* ## Why the whole agent direction is Envelope except the six it consumes
|
|
30
|
+
*
|
|
31
|
+
* Not laziness, and not a deferral: agent-payload conformance is a property the protocol deliberately
|
|
32
|
+
* does not have. `AgentRegister.platform` is `string` — open, because a third-party platform registers
|
|
33
|
+
* through `AgentRegistry.register()` and the root AGENTS.md's OCP rule says that must work without
|
|
34
|
+
* modifying existing code — while `ChromePayload` is a **closed two-member union**. So a platform this
|
|
35
|
+
* repo promises to support has no valid `session:chrome` variant to send. Rejecting one would cost
|
|
36
|
+
* that platform its bezel and buttons permanently: the message arrives once per boot, a rejection
|
|
37
|
+
* skips `setChromeData` and therefore empties the re-join replay too, and there is no
|
|
38
|
+
* `session:chrome-error` for anyone to be told through.
|
|
39
|
+
*
|
|
40
|
+
* The six the relay consumes (`agent:register`, `agent:resources`, the two screenshot replies and the
|
|
41
|
+
* two ui-tree replies) forward nowhere, so rejecting them breaks no forward path — and their schemas
|
|
42
|
+
* carry a `.default()` for every field the relay currently reads through a `??`, which keeps today's
|
|
43
|
+
* tolerance for an older agent exactly as it is. `UIElement` is safe to validate where `ChromePayload`
|
|
44
|
+
* is not, for a reason worth stating: it is not a per-platform union but the normalized shape every
|
|
45
|
+
* platform maps *into*, so a third-party agent conforms by construction.
|
|
46
|
+
*
|
|
47
|
+
* ## What is not checked here, and where it is
|
|
48
|
+
*
|
|
49
|
+
* Role authorisation. `directionOf` answers which socket may send a type; deciding what to do about a
|
|
50
|
+
* mismatch (the relay closes a browser socket with 1008) stays in `RelayServer`, because it is a
|
|
51
|
+
* policy about a connection rather than a fact about a message.
|
|
52
|
+
*/
|
|
53
|
+
import * as z from 'zod';
|
|
54
|
+
// ── field helpers ────────────────────────────────────────────────────────────
|
|
55
|
+
//
|
|
56
|
+
// `.min(1)`, never a bare `z.string()`, on both correlation fields. The predicates this replaces
|
|
57
|
+
// (`isAddressed` / `isCorrelated`) rejected the empty string as well as the absent one, and dropping
|
|
58
|
+
// that half would be a silent regression: a `device:boot` carrying `requestId: ''` would pass the door
|
|
59
|
+
// and the relay would answer `device:boot-error` with `requestId: ''` — a frame whose required
|
|
60
|
+
// correlator is present-but-empty, which every correlating consumer discards. `.min(1)` does not
|
|
61
|
+
// change what `z.output` infers (`min(minLength): this` — zod 4.4.3 `v4/classic/schemas.d.ts:95`), so
|
|
62
|
+
// it costs the tier assertions nothing.
|
|
63
|
+
const sessionId = z.string().min(1);
|
|
64
|
+
const requestId = z.string().min(1);
|
|
65
|
+
const point = z.object({ x: z.number(), y: z.number() });
|
|
66
|
+
// ── envelope-tier builders ───────────────────────────────────────────────────
|
|
67
|
+
//
|
|
68
|
+
// Four shapes cover all 22 forward-only messages. Getting one wrong is a compile error at its
|
|
69
|
+
// assertion below rather than something that shows up as a dropped frame in production.
|
|
70
|
+
/** `{ type, sessionId }` — no correlator on the interface. */
|
|
71
|
+
const env = (type) => z.object({ type: z.literal(type), sessionId });
|
|
72
|
+
/** `{ type, sessionId, requestId }`. */
|
|
73
|
+
const envC = (type) => z.object({ type: z.literal(type), sessionId, requestId });
|
|
74
|
+
/** `{ type, sessionId, requestId? }` — the relay originates some of these itself, so the agent's copy
|
|
75
|
+
* is not always a reply. */
|
|
76
|
+
const envCo = (type) => z.object({ type: z.literal(type), sessionId, requestId: requestId.optional() });
|
|
77
|
+
// ── browser → relay ──────────────────────────────────────────────────────────
|
|
78
|
+
//
|
|
79
|
+
// Fully validated, and the only direction whose parse product is what gets forwarded. This is the
|
|
80
|
+
// attacker-controllable side: a viewer can send arbitrary frames from devtools. `z.object` strips
|
|
81
|
+
// unknown keys rather than rejecting them, so forwarding the product — not the original — is what
|
|
82
|
+
// makes a key an attacker appended disappear before any agent sees it. Nothing in the repo loses a
|
|
83
|
+
// field to that: the dashboard, `mcp-server` and `flow-runner` all send through a
|
|
84
|
+
// `send(msg: BrowserToRelay)` signature, so their frames are already compile-checked against exactly
|
|
85
|
+
// these shapes.
|
|
86
|
+
const BROWSER_INBOUND = {
|
|
87
|
+
'agents:list': z.object({ type: z.literal('agents:list') }),
|
|
88
|
+
'session:start': z.object({ type: z.literal('session:start'), sessionId }),
|
|
89
|
+
'session:end': z.object({ type: z.literal('session:end'), sessionId }),
|
|
90
|
+
'session:leave': z.object({ type: z.literal('session:leave'), sessionId }),
|
|
91
|
+
'device:boot': z.object({
|
|
92
|
+
type: z.literal('device:boot'),
|
|
93
|
+
sessionId,
|
|
94
|
+
requestId,
|
|
95
|
+
payload: z.object({
|
|
96
|
+
deviceId: z.string(),
|
|
97
|
+
resetMode: z.enum(['app-only', 'full-erase']).optional(),
|
|
98
|
+
acceptH264: z.boolean().optional(),
|
|
99
|
+
secureContext: z.boolean().optional(),
|
|
100
|
+
}),
|
|
101
|
+
}),
|
|
102
|
+
'device:shutdown': z.object({
|
|
103
|
+
type: z.literal('device:shutdown'),
|
|
104
|
+
sessionId,
|
|
105
|
+
requestId: requestId.optional(),
|
|
106
|
+
payload: z.object({ deviceId: z.string() }),
|
|
107
|
+
}),
|
|
108
|
+
// Strict, because `ANSWERABLE` below answers a bad one rather than dropping it. A draft carried it
|
|
109
|
+
// through as `NaN` so the handler's `Number.isInteger` could keep replying `Build not found` — which
|
|
110
|
+
// worked, and left this message as the single special case in a class the door now handles uniformly.
|
|
111
|
+
// It was also the wrong diagnosis: nothing was looked up, so "not found" describes a query that never
|
|
112
|
+
// ran.
|
|
113
|
+
'app:install': z.object({ type: z.literal('app:install'), sessionId, requestId, buildId: z.number().int() }),
|
|
114
|
+
'app:launch': z.object({ type: z.literal('app:launch'), sessionId, requestId, buildId: z.number().int() }),
|
|
115
|
+
'app:clear-state': z.object({
|
|
116
|
+
type: z.literal('app:clear-state'), sessionId, requestId,
|
|
117
|
+
payload: z.object({ bundleId: z.string() }),
|
|
118
|
+
}),
|
|
119
|
+
'open-url': z.object({
|
|
120
|
+
type: z.literal('open-url'), sessionId, requestId, payload: z.object({ url: z.string() }),
|
|
121
|
+
}),
|
|
122
|
+
'input:touch:start': z.object({ type: z.literal('input:touch:start'), sessionId, payload: point }),
|
|
123
|
+
'input:touch:move': z.object({ type: z.literal('input:touch:move'), sessionId, payload: point }),
|
|
124
|
+
// `payload` optional because that is what the wire carries — the dashboard omits it and the agents
|
|
125
|
+
// never read it. See the note on `InputTouchEnd`.
|
|
126
|
+
'input:touch:end': z.object({
|
|
127
|
+
type: z.literal('input:touch:end'), sessionId, requestId, payload: point.optional(),
|
|
128
|
+
}),
|
|
129
|
+
'input:pinch:start': z.object({
|
|
130
|
+
type: z.literal('input:pinch:start'), sessionId, payload: z.object({ f0: point, f1: point }),
|
|
131
|
+
}),
|
|
132
|
+
'input:pinch:move': z.object({
|
|
133
|
+
type: z.literal('input:pinch:move'), sessionId, payload: z.object({ f0: point, f1: point }),
|
|
134
|
+
}),
|
|
135
|
+
'input:pinch:end': z.object({ type: z.literal('input:pinch:end'), sessionId, requestId }),
|
|
136
|
+
// `modifiers` is a bitmap, not a list — see the note on `InputKey`.
|
|
137
|
+
'input:key': z.object({
|
|
138
|
+
type: z.literal('input:key'), sessionId, requestId,
|
|
139
|
+
payload: z.object({ code: z.string(), modifiers: z.number().optional() }),
|
|
140
|
+
}),
|
|
141
|
+
'input:type': z.object({
|
|
142
|
+
type: z.literal('input:type'), sessionId, requestId, payload: z.object({ text: z.string() }),
|
|
143
|
+
}),
|
|
144
|
+
'input:button': z.object({
|
|
145
|
+
type: z.literal('input:button'), sessionId, requestId,
|
|
146
|
+
payload: z.object({ name: z.string(), phase: z.enum(['down', 'up']).optional() }),
|
|
147
|
+
}),
|
|
148
|
+
'input:rotate': z.object({ type: z.literal('input:rotate'), sessionId }),
|
|
149
|
+
'input:keyboard:toggle': z.object({ type: z.literal('input:keyboard:toggle'), sessionId }),
|
|
150
|
+
'clipboard:read': z.object({
|
|
151
|
+
type: z.literal('clipboard:read'), sessionId, requestId,
|
|
152
|
+
payload: z.object({ press: z.enum(['copy', 'cut']).optional() }).optional(),
|
|
153
|
+
}),
|
|
154
|
+
'clipboard:write': z.object({
|
|
155
|
+
type: z.literal('clipboard:write'), sessionId, requestId,
|
|
156
|
+
payload: z.object({ text: z.string(), pasteAfter: z.boolean().optional() }),
|
|
157
|
+
}),
|
|
158
|
+
};
|
|
159
|
+
// ── agent → relay ────────────────────────────────────────────────────────────
|
|
160
|
+
/** The six the relay consumes. Every `.default()` below mirrors a `??` that is in `RelayServer` today,
|
|
161
|
+
* so an agent older than a field keeps working exactly as it does now — the default lands in the
|
|
162
|
+
* schema, where it is visible, instead of at the read site, where it read as defensive noise.
|
|
163
|
+
*
|
|
164
|
+
* This is what makes rejection affordable here: `z.input` stays as loose as the wire has ever been
|
|
165
|
+
* while `z.output` matches the interface, which is what the tier assertion compares. */
|
|
166
|
+
const AGENT_CONSUMED = {
|
|
167
|
+
'agent:register': z.object({
|
|
168
|
+
type: z.literal('agent:register'),
|
|
169
|
+
// **All four defaults come from a `??` in `RelayServer`, and the list is exhaustive by
|
|
170
|
+
// construction** — `agentId ?? agentName` for identity, `devices ?? []`, `capabilities ?? []`, and
|
|
171
|
+
// `agentName ?? agentId ?? 'unknown'` / `platform ?? 'unknown'` in the connect log. A first draft
|
|
172
|
+
// defaulted only `capabilities` and `devices` and required these two, which would have made an
|
|
173
|
+
// agent omitting either **never register at all**: the frame is refused, no `agent:registered`
|
|
174
|
+
// goes back, and the agent's handshake promise never resolves — the whole Mac and every device on
|
|
175
|
+
// it simply absent from the dashboard, with one relay-side warn as the only trace. That is the
|
|
176
|
+
// most expensive rejection in the protocol, so this message is the one to be most tolerant on.
|
|
177
|
+
//
|
|
178
|
+
// `''` rather than `undefined` because `z.output` must match the interface, and it is behaviourally
|
|
179
|
+
// the same everywhere it reaches: `identity` stays falsy so no eviction runs, and the log line uses
|
|
180
|
+
// `||` for exactly this.
|
|
181
|
+
platform: z.string().default(''),
|
|
182
|
+
// How a viewer tells an agent that predates a capability from one that has it.
|
|
183
|
+
capabilities: z.array(z.string()).default([]),
|
|
184
|
+
agentId: z.string().optional(),
|
|
185
|
+
agentName: z.string().default(''),
|
|
186
|
+
// Deduplication by device id stays in the handler — it is a policy about the *set*, not a shape.
|
|
187
|
+
devices: z.array(z.object({
|
|
188
|
+
id: z.string(), name: z.string(), platform: z.string(), status: z.string(),
|
|
189
|
+
osVersion: z.string().optional(),
|
|
190
|
+
})).default([]),
|
|
191
|
+
}),
|
|
192
|
+
'agent:resources': z.object({
|
|
193
|
+
type: z.literal('agent:resources'),
|
|
194
|
+
resources: z.object({
|
|
195
|
+
cpuPercent: z.number(), memUsedMB: z.number(), memTotalMB: z.number(),
|
|
196
|
+
slotsAvailable: z.number(), slotsTotal: z.number(), reportedAt: z.number(),
|
|
197
|
+
}),
|
|
198
|
+
}),
|
|
199
|
+
'screenshot:done': z.object({
|
|
200
|
+
type: z.literal('screenshot:done'), sessionId, requestId,
|
|
201
|
+
// The claim, not the truth — the relay sniffs the bytes and logs a mismatch rather than
|
|
202
|
+
// overwriting this, because only the agent knows what it produced (#508).
|
|
203
|
+
format: z.enum(['png', 'jpeg']).default('png'),
|
|
204
|
+
data: z.string().default(''),
|
|
205
|
+
}),
|
|
206
|
+
'screenshot:error': z.object({
|
|
207
|
+
type: z.literal('screenshot:error'), sessionId, requestId, message: z.string().default(''),
|
|
208
|
+
}),
|
|
209
|
+
'ui:tree:response': z.object({
|
|
210
|
+
type: z.literal('ui:tree:response'), sessionId, requestId,
|
|
211
|
+
// Safe to validate where `ChromePayload` is not: a normalized shape every platform maps into,
|
|
212
|
+
// not a union with one member per platform.
|
|
213
|
+
elements: z.array(z.object({
|
|
214
|
+
role: z.enum([
|
|
215
|
+
'button', 'text', 'input', 'image', 'checkbox', 'switch', 'slider', 'list', 'cell', 'tab', 'other',
|
|
216
|
+
]),
|
|
217
|
+
label: z.string(),
|
|
218
|
+
identifier: z.string().optional(),
|
|
219
|
+
frame: z.object({ x: z.number(), y: z.number(), width: z.number(), height: z.number() }),
|
|
220
|
+
enabled: z.boolean(),
|
|
221
|
+
rawRole: z.string().optional(),
|
|
222
|
+
})).default([]),
|
|
223
|
+
}),
|
|
224
|
+
'ui:tree:error': z.object({
|
|
225
|
+
type: z.literal('ui:tree:error'), sessionId, requestId, message: z.string().default(''),
|
|
226
|
+
}),
|
|
227
|
+
};
|
|
228
|
+
/** The 22 the relay only forwards. Envelope tier — see the header for why there is no exception. */
|
|
229
|
+
const AGENT_FORWARDED = {
|
|
230
|
+
'session:chrome': env('session:chrome'),
|
|
231
|
+
'session:deviceInfo': env('session:deviceInfo'),
|
|
232
|
+
// The only member whose `sessionId` is optional, and it is a documented deferral rather than an
|
|
233
|
+
// oversight — stamping it would make a replayed ready satisfy an in-flight boot. See `DeviceReady`.
|
|
234
|
+
'device:ready': z.object({
|
|
235
|
+
type: z.literal('device:ready'),
|
|
236
|
+
sessionId: sessionId.optional(),
|
|
237
|
+
requestId: requestId.optional(),
|
|
238
|
+
}),
|
|
239
|
+
'device:booting': env('device:booting'),
|
|
240
|
+
'device:shutdown-done': envCo('device:shutdown-done'),
|
|
241
|
+
'device:boot-error': envCo('device:boot-error'),
|
|
242
|
+
'app:install-done': envC('app:install-done'),
|
|
243
|
+
'app:install-error': envC('app:install-error'),
|
|
244
|
+
'app:launch-done': envC('app:launch-done'),
|
|
245
|
+
'app:launch-error': envC('app:launch-error'),
|
|
246
|
+
'app:clear-state-done': envC('app:clear-state-done'),
|
|
247
|
+
'app:clear-state-error': envC('app:clear-state-error'),
|
|
248
|
+
'open-url:done': envC('open-url:done'),
|
|
249
|
+
'open-url:error': envC('open-url:error'),
|
|
250
|
+
'input:done': envC('input:done'),
|
|
251
|
+
'input:error': envC('input:error'),
|
|
252
|
+
'input:type-done': envC('input:type-done'),
|
|
253
|
+
'input:type-error': envC('input:type-error'),
|
|
254
|
+
'keyboard:toggled': env('keyboard:toggled'),
|
|
255
|
+
'clipboard:data': envC('clipboard:data'),
|
|
256
|
+
'clipboard:write-done': envC('clipboard:write-done'),
|
|
257
|
+
'clipboard:error': envC('clipboard:error'),
|
|
258
|
+
};
|
|
259
|
+
const AGENT_INBOUND = { ...AGENT_CONSUMED, ...AGENT_FORWARDED };
|
|
260
|
+
// ── stream → relay ───────────────────────────────────────────────────────────
|
|
261
|
+
//
|
|
262
|
+
// One message, and its own direction on purpose: the relay assigns the role `'stream'` from it, so
|
|
263
|
+
// merging it into the agent direction would let a control socket claim to be a session's stream
|
|
264
|
+
// socket. Everything else on that socket is binary and never reaches this parser.
|
|
265
|
+
const STREAM_INBOUND = {
|
|
266
|
+
'stream:register': z.object({ type: z.literal('stream:register'), sessionId }),
|
|
267
|
+
};
|
|
268
|
+
const INBOUND = { ...BROWSER_INBOUND, ...AGENT_INBOUND, ...STREAM_INBOUND };
|
|
269
|
+
// ── the requests whose payload failure can be answered ───────────────────────
|
|
270
|
+
//
|
|
271
|
+
// **The envelope is judged separately from the payload, and that is what makes an answer possible.**
|
|
272
|
+
// A frame whose `sessionId` and `requestId` are both good and whose payload is not carries everything
|
|
273
|
+
// a reply needs: an address and a correlator. Refusing it wholesale would be the regression this door
|
|
274
|
+
// otherwise ships — today a malformed `open-url` reaches the agent, which answers `open-url:error`
|
|
275
|
+
// from its own guard (`IOSAgent.ts` says so in writing: "validating third-party frames at the relay's
|
|
276
|
+
// door is #444, which will take this over"). Taking it over must not mean losing the answer.
|
|
277
|
+
//
|
|
278
|
+
// The cost of losing it is worst on the inputs, and not obviously: `awaitInputAck` reports silence
|
|
279
|
+
// from a session that has never acked as **success** (#457), so a dropped `input:key` would be
|
|
280
|
+
// reported to an MCP caller as an input that landed.
|
|
281
|
+
//
|
|
282
|
+
// Exactly the twelve browser requests that declare a required `requestId`. The relay maps each to the
|
|
283
|
+
// reply its own waiter reads; `scripts/__tests__/correlatedRequestsGated.test.mjs` derives that set
|
|
284
|
+
// from the protocol and holds all three lists to it.
|
|
285
|
+
const ANSWERABLE = {
|
|
286
|
+
'device:boot': envC('device:boot'),
|
|
287
|
+
'app:install': envC('app:install'),
|
|
288
|
+
'app:launch': envC('app:launch'),
|
|
289
|
+
'app:clear-state': envC('app:clear-state'),
|
|
290
|
+
'open-url': envC('open-url'),
|
|
291
|
+
'input:touch:end': envC('input:touch:end'),
|
|
292
|
+
'input:pinch:end': envC('input:pinch:end'),
|
|
293
|
+
'input:key': envC('input:key'),
|
|
294
|
+
'input:button': envC('input:button'),
|
|
295
|
+
'input:type': envC('input:type'),
|
|
296
|
+
'clipboard:read': envC('clipboard:read'),
|
|
297
|
+
'clipboard:write': envC('clipboard:write'),
|
|
298
|
+
};
|
|
299
|
+
const DIRECTIONS = new Map([
|
|
300
|
+
...Object.keys(BROWSER_INBOUND).map((t) => [t, 'browser']),
|
|
301
|
+
...Object.keys(AGENT_INBOUND).map((t) => [t, 'agent']),
|
|
302
|
+
...Object.keys(STREAM_INBOUND).map((t) => [t, 'stream']),
|
|
303
|
+
]);
|
|
304
|
+
/**
|
|
305
|
+
* Which socket role is allowed to send this type.
|
|
306
|
+
*
|
|
307
|
+
* This replaces `AGENT_MSG_TYPE_LIST` — 29 literals hand-copied into `RelayServer` and held by two
|
|
308
|
+
* type assertions. It is derived from the map above, which the assertions at the bottom of this file
|
|
309
|
+
* tie back to the protocol's own direction unions, so the copy is gone rather than moved.
|
|
310
|
+
*
|
|
311
|
+
* A runtime set cannot come out of a union directly (types erase), which is why it comes out of the
|
|
312
|
+
* schema map's keys. That makes the relay's role gate depend on this map existing — a real coupling,
|
|
313
|
+
* and the reason the coverage assertions below are not optional.
|
|
314
|
+
*/
|
|
315
|
+
export function directionOf(type) {
|
|
316
|
+
// Non-null: `DIRECTIONS` is built from the same keys `InboundType` is derived from, and the
|
|
317
|
+
// coverage assertions below make a divergence a compile error rather than a runtime miss.
|
|
318
|
+
return DIRECTIONS.get(type);
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Parse a frame the relay received.
|
|
322
|
+
*
|
|
323
|
+
* **Takes no role.** A first design did, and it could not be called: `classifyConnection` returns
|
|
324
|
+
* `role: 'first-message'` for every local connection and every remote agent-scoped PAT, so the role is
|
|
325
|
+
* assigned from the first message's own `type`. `agent:register` is the message that *creates* the
|
|
326
|
+
* role, so selecting a schema by role to validate it is circular — and defaulting the unknown role to
|
|
327
|
+
* `'browser'`, which is what the relay does for a socket that skips the handshake, would have closed
|
|
328
|
+
* every agent's socket on its opening frame. The order is parse, then role, then gate.
|
|
329
|
+
*/
|
|
330
|
+
export function parseInbound(raw) {
|
|
331
|
+
// `JSON.parse` returns bare `null`, numbers and strings without throwing, and a caller that reads
|
|
332
|
+
// `.type` off one of those is the reason this is checked before anything else.
|
|
333
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
|
|
334
|
+
return { ok: false, reason: 'not-an-object' };
|
|
335
|
+
const frame = raw;
|
|
336
|
+
const type = frame['type'];
|
|
337
|
+
if (typeof type !== 'string' || !Object.hasOwn(INBOUND, type)) {
|
|
338
|
+
return { ok: false, reason: 'unknown-type', type: typeof type === 'string' ? type : String(type) };
|
|
339
|
+
}
|
|
340
|
+
const known = type;
|
|
341
|
+
// **The universe is the inbound map, not `AnyWireMessage`.** Eleven literals belong to no inbound
|
|
342
|
+
// direction — `session:joined`, `error`, `agents:listed` and the rest the relay produces. A browser
|
|
343
|
+
// that sends one today reaches the switch, matches no case and is ignored. Measuring `unknown-type`
|
|
344
|
+
// against every declared literal instead would classify those as a direction violation, and the
|
|
345
|
+
// relay closes a direction violation with 1008 — disconnecting a dashboard over a frame that is
|
|
346
|
+
// inert today.
|
|
347
|
+
const result = INBOUND[known].safeParse(frame);
|
|
348
|
+
if (!result.success) {
|
|
349
|
+
const detail = z.prettifyError(result.error);
|
|
350
|
+
// Second stage, and only for the twelve. If the envelope stands on its own, the failure is in the
|
|
351
|
+
// payload and the relay has an address and a correlator to answer with — see `ANSWERABLE`.
|
|
352
|
+
if (Object.hasOwn(ANSWERABLE, known)) {
|
|
353
|
+
const answerable = known;
|
|
354
|
+
const envelope = ANSWERABLE[answerable].safeParse(frame);
|
|
355
|
+
if (envelope.success) {
|
|
356
|
+
const { sessionId: s, requestId: r } = envelope.data;
|
|
357
|
+
return { ok: false, reason: 'bad-payload', type: answerable, sessionId: s, requestId: r, detail };
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return { ok: false, reason: 'bad-shape', type: known, detail };
|
|
361
|
+
}
|
|
362
|
+
return { ok: true, msg: result.data, raw: frame };
|
|
363
|
+
}
|
|
364
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/validate/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAexB,gFAAgF;AAChF,EAAE;AACF,iGAAiG;AACjG,qGAAqG;AACrG,uGAAuG;AACvG,+FAA+F;AAC/F,iGAAiG;AACjG,sGAAsG;AACtG,wCAAwC;AACxC,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;AACnC,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;AAEnC,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;AAExD,gFAAgF;AAChF,EAAE;AACF,8FAA8F;AAC9F,wFAAwF;AAExF,8DAA8D;AAC9D,MAAM,GAAG,GAAG,CAAmB,IAAO,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,CAAC,CAAA;AACzF,wCAAwC;AACxC,MAAM,IAAI,GAAG,CAAmB,IAAO,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,CAAA;AACrG;6BAC6B;AAC7B,MAAM,KAAK,GAAG,CAAmB,IAAO,EAAE,EAAE,CAC1C,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAA;AAEjF,gFAAgF;AAChF,EAAE;AACF,kGAAkG;AAClG,kGAAkG;AAClG,kGAAkG;AAClG,mGAAmG;AACnG,kFAAkF;AAClF,qGAAqG;AACrG,gBAAgB;AAEhB,MAAM,eAAe,GAAG;IACtB,aAAa,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;IAC3D,eAAe,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC,EAAE,SAAS,EAAE,CAAC;IAC1E,aAAa,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,SAAS,EAAE,CAAC;IACtE,eAAe,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC,EAAE,SAAS,EAAE,CAAC;IAC1E,aAAa,EAAE,CAAC,CAAC,MAAM,CAAC;QACtB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC;QAC9B,SAAS;QACT,SAAS;QACT,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC;YAChB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;YACpB,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC,CAAC,QAAQ,EAAE;YACxD,UAAU,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;YAClC,aAAa,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;SACtC,CAAC;KACH,CAAC;IACF,iBAAiB,EAAE,CAAC,CAAC,MAAM,CAAC;QAC1B,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAAC;QAClC,SAAS;QACT,SAAS,EAAE,SAAS,CAAC,QAAQ,EAAE;QAC/B,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;KAC5C,CAAC;IACF,mGAAmG;IACnG,qGAAqG;IACrG,sGAAsG;IACtG,sGAAsG;IACtG,OAAO;IACP,aAAa,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC;IAC5G,YAAY,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC;IAC1G,iBAAiB,EAAE,CAAC,CAAC,MAAM,CAAC;QAC1B,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAAC,EAAE,SAAS,EAAE,SAAS;QACxD,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;KAC5C,CAAC;IACF,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC;QACnB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;KAC1F,CAAC;IACF,mBAAmB,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,mBAAmB,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAClG,kBAAkB,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAChG,mGAAmG;IACnG,kDAAkD;IAClD,iBAAiB,EAAE,CAAC,CAAC,MAAM,CAAC;QAC1B,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,QAAQ,EAAE;KACpF,CAAC;IACF,mBAAmB,EAAE,CAAC,CAAC,MAAM,CAAC;QAC5B,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,mBAAmB,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;KAC7F,CAAC;IACF,kBAAkB,EAAE,CAAC,CAAC,MAAM,CAAC;QAC3B,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;KAC5F,CAAC;IACF,iBAAiB,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;IACzF,oEAAoE;IACpE,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;QACpB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,SAAS,EAAE,SAAS;QAClD,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC;KAC1E,CAAC;IACF,YAAY,EAAE,CAAC,CAAC,MAAM,CAAC;QACrB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;KAC7F,CAAC;IACF,cAAc,EAAE,CAAC,CAAC,MAAM,CAAC;QACvB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,SAAS,EAAE,SAAS;QACrD,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC;KAClF,CAAC;IACF,cAAc,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,SAAS,EAAE,CAAC;IACxE,uBAAuB,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,uBAAuB,CAAC,EAAE,SAAS,EAAE,CAAC;IAC1F,gBAAgB,EAAE,CAAC,CAAC,MAAM,CAAC;QACzB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,EAAE,SAAS,EAAE,SAAS;QACvD,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,QAAQ,EAAE;KAC5E,CAAC;IACF,iBAAiB,EAAE,CAAC,CAAC,MAAM,CAAC;QAC1B,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAAC,EAAE,SAAS,EAAE,SAAS;QACxD,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC;KAC5E,CAAC;CACM,CAAA;AAEV,gFAAgF;AAEhF;;;;;yFAKyF;AACzF,MAAM,cAAc,GAAG;IACrB,gBAAgB,EAAE,CAAC,CAAC,MAAM,CAAC;QACzB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC;QACjC,uFAAuF;QACvF,mGAAmG;QACnG,kGAAkG;QAClG,+FAA+F;QAC/F,+FAA+F;QAC/F,kGAAkG;QAClG,+FAA+F;QAC/F,+FAA+F;QAC/F,EAAE;QACF,oGAAoG;QACpG,oGAAoG;QACpG,yBAAyB;QACzB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;QAChC,+EAA+E;QAC/E,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7C,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC9B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;QACjC,iGAAiG;QACjG,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;YACxB,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;YAC1E,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;SACjC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;KAChB,CAAC;IACF,iBAAiB,EAAE,CAAC,CAAC,MAAM,CAAC;QAC1B,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAAC;QAClC,SAAS,EAAE,CAAC,CAAC,MAAM,CAAC;YAClB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;YACrE,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;SAC3E,CAAC;KACH,CAAC;IACF,iBAAiB,EAAE,CAAC,CAAC,MAAM,CAAC;QAC1B,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAAC,EAAE,SAAS,EAAE,SAAS;QACxD,wFAAwF;QACxF,0EAA0E;QAC1E,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;QAC9C,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;KAC7B,CAAC;IACF,kBAAkB,EAAE,CAAC,CAAC,MAAM,CAAC;QAC3B,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;KAC3F,CAAC;IACF,kBAAkB,EAAE,CAAC,CAAC,MAAM,CAAC;QAC3B,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAAC,EAAE,SAAS,EAAE,SAAS;QACzD,8FAA8F;QAC9F,4CAA4C;QAC5C,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;YACzB,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;gBACX,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;aACnG,CAAC;YACF,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;YACjB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;YACjC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;YACxF,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;YACpB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;SAC/B,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;KAChB,CAAC;IACF,eAAe,EAAE,CAAC,CAAC,MAAM,CAAC;QACxB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;KACxF,CAAC;CACM,CAAA;AAEV,oGAAoG;AACpG,MAAM,eAAe,GAAG;IACtB,gBAAgB,EAAE,GAAG,CAAC,gBAAgB,CAAC;IACvC,oBAAoB,EAAE,GAAG,CAAC,oBAAoB,CAAC;IAC/C,gGAAgG;IAChG,oGAAoG;IACpG,cAAc,EAAE,CAAC,CAAC,MAAM,CAAC;QACvB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC;QAC/B,SAAS,EAAE,SAAS,CAAC,QAAQ,EAAE;QAC/B,SAAS,EAAE,SAAS,CAAC,QAAQ,EAAE;KAChC,CAAC;IACF,gBAAgB,EAAE,GAAG,CAAC,gBAAgB,CAAC;IACvC,sBAAsB,EAAE,KAAK,CAAC,sBAAsB,CAAC;IACrD,mBAAmB,EAAE,KAAK,CAAC,mBAAmB,CAAC;IAC/C,kBAAkB,EAAE,IAAI,CAAC,kBAAkB,CAAC;IAC5C,mBAAmB,EAAE,IAAI,CAAC,mBAAmB,CAAC;IAC9C,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,CAAC;IAC1C,kBAAkB,EAAE,IAAI,CAAC,kBAAkB,CAAC;IAC5C,sBAAsB,EAAE,IAAI,CAAC,sBAAsB,CAAC;IACpD,uBAAuB,EAAE,IAAI,CAAC,uBAAuB,CAAC;IACtD,eAAe,EAAE,IAAI,CAAC,eAAe,CAAC;IACtC,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,CAAC;IACxC,YAAY,EAAE,IAAI,CAAC,YAAY,CAAC;IAChC,aAAa,EAAE,IAAI,CAAC,aAAa,CAAC;IAClC,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,CAAC;IAC1C,kBAAkB,EAAE,IAAI,CAAC,kBAAkB,CAAC;IAC5C,kBAAkB,EAAE,GAAG,CAAC,kBAAkB,CAAC;IAC3C,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,CAAC;IACxC,sBAAsB,EAAE,IAAI,CAAC,sBAAsB,CAAC;IACpD,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,CAAC;CAClC,CAAA;AAEV,MAAM,aAAa,GAAG,EAAE,GAAG,cAAc,EAAE,GAAG,eAAe,EAAW,CAAA;AAExE,gFAAgF;AAChF,EAAE;AACF,mGAAmG;AACnG,gGAAgG;AAChG,kFAAkF;AAClF,MAAM,cAAc,GAAG;IACrB,iBAAiB,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAAC,EAAE,SAAS,EAAE,CAAC;CACtE,CAAA;AAEV,MAAM,OAAO,GAAG,EAAE,GAAG,eAAe,EAAE,GAAG,aAAa,EAAE,GAAG,cAAc,EAAW,CAAA;AAEpF,gFAAgF;AAChF,EAAE;AACF,qGAAqG;AACrG,sGAAsG;AACtG,sGAAsG;AACtG,mGAAmG;AACnG,sGAAsG;AACtG,6FAA6F;AAC7F,EAAE;AACF,mGAAmG;AACnG,+FAA+F;AAC/F,qDAAqD;AACrD,EAAE;AACF,sGAAsG;AACtG,oGAAoG;AACpG,qDAAqD;AACrD,MAAM,UAAU,GAAG;IACjB,aAAa,EAAE,IAAI,CAAC,aAAa,CAAC;IAClC,aAAa,EAAE,IAAI,CAAC,aAAa,CAAC;IAClC,YAAY,EAAE,IAAI,CAAC,YAAY,CAAC;IAChC,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,CAAC;IAC1C,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC;IAC5B,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,CAAC;IAC1C,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,CAAC;IAC1C,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC;IAC9B,cAAc,EAAE,IAAI,CAAC,cAAc,CAAC;IACpC,YAAY,EAAE,IAAI,CAAC,YAAY,CAAC;IAChC,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,CAAC;IACxC,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,CAAC;CAClC,CAAA;AA+CV,MAAM,UAAU,GAA0C,IAAI,GAAG,CAAC;IAChE,GAAG,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,CAAU,CAAC;IACnE,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,CAAU,CAAC;IAC/D,GAAG,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAU,CAAC;CAClE,CAAC,CAAA;AAEF;;;;;;;;;;GAUG;AACH,MAAM,UAAU,WAAW,CAAC,IAAiB;IAC3C,4FAA4F;IAC5F,0FAA0F;IAC1F,OAAO,UAAU,CAAC,GAAG,CAAC,IAAI,CAAE,CAAA;AAC9B,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,YAAY,CAAC,GAAY;IACvC,kGAAkG;IAClG,+EAA+E;IAC/E,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,CAAA;IAChH,MAAM,KAAK,GAAG,GAA8B,CAAA;IAC5C,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAA;IAC1B,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;QAC9D,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAA;IACpG,CAAC;IACD,MAAM,KAAK,GAAG,IAAmB,CAAA;IACjC,kGAAkG;IAClG,oGAAoG;IACpG,oGAAoG;IACpG,gGAAgG;IAChG,gGAAgG;IAChG,eAAe;IACf,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;IAC9C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,MAAM,GAAG,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QAC5C,kGAAkG;QAClG,2FAA2F;QAC3F,IAAI,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC;YACrC,MAAM,UAAU,GAAG,KAAuB,CAAA;YAC1C,MAAM,QAAQ,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;YACxD,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;gBACrB,MAAM,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,GAAG,QAAQ,CAAC,IAAI,CAAA;gBACpD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM,EAAE,CAAA;YACnG,CAAC;QACH,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAA;IAChE,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,CAAC,IAAqB,EAAE,GAAG,EAAE,KAAK,EAAE,CAAA;AACpE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tapflowio/protocol",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "Wire protocol contract for tapflow — the WebSocket message types exchanged between browser, relay, and device agents",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"tapflow",
|
|
@@ -28,6 +28,13 @@
|
|
|
28
28
|
"tsx": "./src/index.ts",
|
|
29
29
|
"import": "./dist/index.js",
|
|
30
30
|
"default": "./dist/index.js"
|
|
31
|
+
},
|
|
32
|
+
"./validate": {
|
|
33
|
+
"types": "./dist/validate/index.d.ts",
|
|
34
|
+
"source": "./src/validate/index.ts",
|
|
35
|
+
"tsx": "./src/validate/index.ts",
|
|
36
|
+
"import": "./dist/validate/index.js",
|
|
37
|
+
"default": "./dist/validate/index.js"
|
|
31
38
|
}
|
|
32
39
|
},
|
|
33
40
|
"files": [
|
|
@@ -36,17 +43,22 @@
|
|
|
36
43
|
"LICENSE"
|
|
37
44
|
],
|
|
38
45
|
"engines": {
|
|
39
|
-
"node": ">=
|
|
46
|
+
"node": ">=22.0.0"
|
|
40
47
|
},
|
|
41
48
|
"publishConfig": {
|
|
42
49
|
"access": "public"
|
|
43
50
|
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"zod": "^4.4.3"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"typescript": "^5.0.0",
|
|
56
|
+
"vitest": "^4.1.10"
|
|
57
|
+
},
|
|
44
58
|
"scripts": {
|
|
45
59
|
"build": "tsc -b",
|
|
46
|
-
"typecheck": "tsc -b && tsc -p tsconfig.assertions.json",
|
|
60
|
+
"typecheck": "tsc -b && tsc -p tsconfig.assertions.json && tsc -p src/__tests__/tsconfig.json",
|
|
61
|
+
"test": "vitest run",
|
|
47
62
|
"lint": "eslint src"
|
|
48
|
-
},
|
|
49
|
-
"devDependencies": {
|
|
50
|
-
"typescript": "^5.0.0"
|
|
51
63
|
}
|
|
52
|
-
}
|
|
64
|
+
}
|