@lumencast/protocol 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.
Files changed (84) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +55 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/cli.d.ts +3 -0
  5. package/dist/cli.d.ts.map +1 -0
  6. package/dist/cli.js +162 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/codec.d.ts +15 -0
  9. package/dist/codec.d.ts.map +1 -0
  10. package/dist/codec.js +328 -0
  11. package/dist/codec.js.map +1 -0
  12. package/dist/conformance/bundle-hash.d.ts +4 -0
  13. package/dist/conformance/bundle-hash.d.ts.map +1 -0
  14. package/dist/conformance/bundle-hash.js +49 -0
  15. package/dist/conformance/bundle-hash.js.map +1 -0
  16. package/dist/conformance/control-client.d.ts +42 -0
  17. package/dist/conformance/control-client.d.ts.map +1 -0
  18. package/dist/conformance/control-client.js +63 -0
  19. package/dist/conformance/control-client.js.map +1 -0
  20. package/dist/conformance/harness.d.ts +41 -0
  21. package/dist/conformance/harness.d.ts.map +1 -0
  22. package/dist/conformance/harness.js +441 -0
  23. package/dist/conformance/harness.js.map +1 -0
  24. package/dist/conformance/index.d.ts +8 -0
  25. package/dist/conformance/index.d.ts.map +1 -0
  26. package/dist/conformance/index.js +12 -0
  27. package/dist/conformance/index.js.map +1 -0
  28. package/dist/conformance/loader.d.ts +9 -0
  29. package/dist/conformance/loader.d.ts.map +1 -0
  30. package/dist/conformance/loader.js +27 -0
  31. package/dist/conformance/loader.js.map +1 -0
  32. package/dist/conformance/match.d.ts +7 -0
  33. package/dist/conformance/match.d.ts.map +1 -0
  34. package/dist/conformance/match.js +82 -0
  35. package/dist/conformance/match.js.map +1 -0
  36. package/dist/conformance/placeholders.d.ts +2 -0
  37. package/dist/conformance/placeholders.d.ts.map +1 -0
  38. package/dist/conformance/placeholders.js +40 -0
  39. package/dist/conformance/placeholders.js.map +1 -0
  40. package/dist/conformance/scenario.d.ts +33 -0
  41. package/dist/conformance/scenario.d.ts.map +1 -0
  42. package/dist/conformance/scenario.js +26 -0
  43. package/dist/conformance/scenario.js.map +1 -0
  44. package/dist/envelope.d.ts +66 -0
  45. package/dist/envelope.d.ts.map +1 -0
  46. package/dist/envelope.js +111 -0
  47. package/dist/envelope.js.map +1 -0
  48. package/dist/errors.d.ts +25 -0
  49. package/dist/errors.d.ts.map +1 -0
  50. package/dist/errors.js +38 -0
  51. package/dist/errors.js.map +1 -0
  52. package/dist/index.d.ts +7 -0
  53. package/dist/index.d.ts.map +1 -0
  54. package/dist/index.js +8 -0
  55. package/dist/index.js.map +1 -0
  56. package/dist/leaf-path.d.ts +21 -0
  57. package/dist/leaf-path.d.ts.map +1 -0
  58. package/dist/leaf-path.js +51 -0
  59. package/dist/leaf-path.js.map +1 -0
  60. package/dist/sequence.d.ts +25 -0
  61. package/dist/sequence.d.ts.map +1 -0
  62. package/dist/sequence.js +51 -0
  63. package/dist/sequence.js.map +1 -0
  64. package/dist/types.d.ts +159 -0
  65. package/dist/types.d.ts.map +1 -0
  66. package/dist/types.js +13 -0
  67. package/dist/types.js.map +1 -0
  68. package/package.json +56 -0
  69. package/src/cli.ts +176 -0
  70. package/src/codec.ts +374 -0
  71. package/src/conformance/bundle-hash.ts +54 -0
  72. package/src/conformance/control-client.ts +93 -0
  73. package/src/conformance/harness.ts +492 -0
  74. package/src/conformance/index.ts +34 -0
  75. package/src/conformance/loader.ts +39 -0
  76. package/src/conformance/match.ts +92 -0
  77. package/src/conformance/placeholders.ts +45 -0
  78. package/src/conformance/scenario.ts +71 -0
  79. package/src/envelope.ts +180 -0
  80. package/src/errors.ts +55 -0
  81. package/src/index.ts +63 -0
  82. package/src/leaf-path.ts +58 -0
  83. package/src/sequence.ts +60 -0
  84. package/src/types.ts +201 -0
package/dist/codec.js ADDED
@@ -0,0 +1,328 @@
1
+ // JSON envelope codec for LSDP/1 frames.
2
+ // Hand-rolled type guards rather than a schema library — keeps the bundle
3
+ // weight off the runtime hot path and the surface auditable.
4
+ import { LumencastError } from "./errors.js";
5
+ import { isProtocolErrorCode } from "./errors.js";
6
+ import { PROTOCOL_VERSION, } from "./types.js";
7
+ /** Encode any LSDP frame to its on-wire JSON string. */
8
+ export function encodeFrame(frame) {
9
+ return JSON.stringify(frame);
10
+ }
11
+ /**
12
+ * Decode a JSON text frame into a typed `ServerFrame`.
13
+ *
14
+ * Throws `LumencastError` with `INTERNAL` if the bytes are not valid JSON,
15
+ * are not an object, or carry an unsupported `v`. Unknown `type` values
16
+ * resolve to `null` (forward-compatibility — receivers MUST ignore unknown
17
+ * frame types per LSDP/1 §13).
18
+ */
19
+ export function decodeServerFrame(raw) {
20
+ const parsed = parseJsonObject(raw);
21
+ validateEnvelope(parsed);
22
+ switch (parsed["type"]) {
23
+ case "snapshot":
24
+ return decodeSnapshot(parsed);
25
+ case "delta":
26
+ return decodeDelta(parsed);
27
+ case "scene_changed":
28
+ return decodeSceneChanged(parsed);
29
+ case "error":
30
+ return decodeError(parsed);
31
+ case "pong":
32
+ return decodePong(parsed);
33
+ default:
34
+ return null;
35
+ }
36
+ }
37
+ /** Decode a JSON text frame into a typed `ClientFrame`. Same forward-compat rule. */
38
+ export function decodeClientFrame(raw) {
39
+ const parsed = parseJsonObject(raw);
40
+ validateEnvelope(parsed);
41
+ switch (parsed["type"]) {
42
+ case "subscribe":
43
+ return decodeSubscribe(parsed);
44
+ case "input":
45
+ return decodeInput(parsed);
46
+ case "ping":
47
+ return decodePing(parsed);
48
+ case "unsubscribe":
49
+ return decodeUnsubscribe(parsed);
50
+ default:
51
+ return null;
52
+ }
53
+ }
54
+ // --- decoders ---------------------------------------------------------------
55
+ function decodeSnapshot(o) {
56
+ requireFields(o, ["seq", "scene_id", "scene_version", "state"]);
57
+ const state = o["state"];
58
+ if (!isPlainObject(state)) {
59
+ throw protocolError(`snapshot.state must be an object`);
60
+ }
61
+ for (const [path, value] of Object.entries(state)) {
62
+ assertLeafValue(value, `snapshot.state[${path}]`);
63
+ }
64
+ return {
65
+ v: PROTOCOL_VERSION,
66
+ type: "snapshot",
67
+ seq: assertInt(o["seq"], "snapshot.seq"),
68
+ scene_id: assertString(o["scene_id"], "snapshot.scene_id"),
69
+ scene_version: assertString(o["scene_version"], "snapshot.scene_version"),
70
+ state: state,
71
+ ...optionalTs(o),
72
+ };
73
+ }
74
+ function decodeDelta(o) {
75
+ requireFields(o, ["seq", "patches"]);
76
+ const frame = {
77
+ v: PROTOCOL_VERSION,
78
+ type: "delta",
79
+ seq: assertInt(o["seq"], "delta.seq"),
80
+ patches: assertPatches(o["patches"], "delta.patches"),
81
+ ...optionalTs(o),
82
+ };
83
+ if (o["cause"] !== undefined) {
84
+ frame.cause = assertCause(o["cause"], "delta.cause");
85
+ }
86
+ return frame;
87
+ }
88
+ function decodeSceneChanged(o) {
89
+ requireFields(o, ["seq", "scene_id", "scene_version"]);
90
+ const frame = {
91
+ v: PROTOCOL_VERSION,
92
+ type: "scene_changed",
93
+ seq: assertInt(o["seq"], "scene_changed.seq"),
94
+ scene_id: assertString(o["scene_id"], "scene_changed.scene_id"),
95
+ scene_version: assertString(o["scene_version"], "scene_changed.scene_version"),
96
+ ...optionalTs(o),
97
+ };
98
+ if (typeof o["from_scene_id"] === "string") {
99
+ frame.from_scene_id = o["from_scene_id"];
100
+ }
101
+ if (o["transition"] !== undefined) {
102
+ frame.transition = assertSceneTransition(o["transition"], "scene_changed.transition");
103
+ }
104
+ return frame;
105
+ }
106
+ function decodeError(o) {
107
+ requireFields(o, ["seq", "code", "message", "recoverable"]);
108
+ const code = o["code"];
109
+ if (!isProtocolErrorCode(code)) {
110
+ throw protocolError(`error.code is not in the closed taxonomy: ${String(code)}`);
111
+ }
112
+ const message = assertString(o["message"], "error.message");
113
+ if (typeof o["recoverable"] !== "boolean") {
114
+ throw protocolError(`error.recoverable must be boolean`);
115
+ }
116
+ const frame = {
117
+ v: PROTOCOL_VERSION,
118
+ type: "error",
119
+ seq: assertInt(o["seq"], "error.seq"),
120
+ code,
121
+ message,
122
+ recoverable: o["recoverable"],
123
+ ...optionalTs(o),
124
+ };
125
+ if (o["retry_after_ms"] !== undefined) {
126
+ frame.retry_after_ms = assertInt(o["retry_after_ms"], "error.retry_after_ms");
127
+ }
128
+ return frame;
129
+ }
130
+ function decodePong(o) {
131
+ const frame = { v: PROTOCOL_VERSION, type: "pong" };
132
+ if (typeof o["nonce"] === "string") {
133
+ frame.nonce = o["nonce"];
134
+ }
135
+ return frame;
136
+ }
137
+ function decodeSubscribe(o) {
138
+ requireFields(o, ["token"]);
139
+ const frame = {
140
+ v: PROTOCOL_VERSION,
141
+ type: "subscribe",
142
+ token: assertString(o["token"], "subscribe.token"),
143
+ };
144
+ if (o["scene"] !== undefined && o["scene"] !== null) {
145
+ frame.scene = assertString(o["scene"], "subscribe.scene");
146
+ }
147
+ if (o["session"] !== undefined && o["session"] !== null) {
148
+ frame.session = assertString(o["session"], "subscribe.session");
149
+ }
150
+ if (o["since_sequence"] !== undefined) {
151
+ frame.since_sequence = assertInt(o["since_sequence"], "subscribe.since_sequence");
152
+ }
153
+ return frame;
154
+ }
155
+ function decodeInput(o) {
156
+ requireFields(o, ["patches"]);
157
+ const frame = {
158
+ v: PROTOCOL_VERSION,
159
+ type: "input",
160
+ patches: assertPatches(o["patches"], "input.patches"),
161
+ };
162
+ if (typeof o["client_msg_id"] === "string") {
163
+ frame.client_msg_id = o["client_msg_id"];
164
+ }
165
+ return frame;
166
+ }
167
+ function decodePing(o) {
168
+ const frame = { v: PROTOCOL_VERSION, type: "ping" };
169
+ if (typeof o["nonce"] === "string") {
170
+ frame.nonce = o["nonce"];
171
+ }
172
+ return frame;
173
+ }
174
+ function decodeUnsubscribe(_o) {
175
+ return { v: PROTOCOL_VERSION, type: "unsubscribe" };
176
+ }
177
+ // --- envelope validation ----------------------------------------------------
178
+ function parseJsonObject(raw) {
179
+ let parsed;
180
+ try {
181
+ parsed = JSON.parse(raw);
182
+ }
183
+ catch {
184
+ throw protocolError(`frame is not valid JSON`);
185
+ }
186
+ if (!isPlainObject(parsed)) {
187
+ throw protocolError(`frame is not a JSON object`);
188
+ }
189
+ return parsed;
190
+ }
191
+ function validateEnvelope(o) {
192
+ if (o["v"] !== PROTOCOL_VERSION) {
193
+ throw protocolError(`envelope.v must be ${PROTOCOL_VERSION}, got ${String(o["v"])}`);
194
+ }
195
+ if (typeof o["type"] !== "string") {
196
+ throw protocolError(`envelope.type must be a string`);
197
+ }
198
+ }
199
+ // --- assertion primitives ---------------------------------------------------
200
+ function requireFields(o, fields) {
201
+ for (const f of fields) {
202
+ if (!(f in o)) {
203
+ throw protocolError(`missing required field: ${f}`);
204
+ }
205
+ }
206
+ }
207
+ function assertString(v, label) {
208
+ if (typeof v !== "string") {
209
+ throw protocolError(`${label} must be a string`);
210
+ }
211
+ return v;
212
+ }
213
+ function assertInt(v, label) {
214
+ if (typeof v !== "number" || !Number.isInteger(v)) {
215
+ throw protocolError(`${label} must be an integer`);
216
+ }
217
+ return v;
218
+ }
219
+ function assertPatches(v, label) {
220
+ if (!Array.isArray(v) || v.length === 0) {
221
+ throw protocolError(`${label} must be a non-empty array`);
222
+ }
223
+ return v.map((p, i) => {
224
+ if (!isPlainObject(p)) {
225
+ throw protocolError(`${label}[${i}] must be an object`);
226
+ }
227
+ if (typeof p["path"] !== "string") {
228
+ throw protocolError(`${label}[${i}].path must be a string`);
229
+ }
230
+ assertLeafValue(p["value"], `${label}[${i}].value`, p["path"]);
231
+ const patch = { path: p["path"], value: p["value"] };
232
+ if (p["transition"] !== undefined) {
233
+ patch.transition = assertTransitionSpec(p["transition"], `${label}[${i}].transition`);
234
+ }
235
+ return patch;
236
+ });
237
+ }
238
+ function assertTransitionSpec(v, label) {
239
+ if (!isPlainObject(v)) {
240
+ throw protocolError(`${label} must be an object`);
241
+ }
242
+ const kind = v["kind"];
243
+ if (kind !== "tween" && kind !== "spring" && kind !== "snap") {
244
+ throw protocolError(`${label}.kind must be one of "tween", "spring", "snap"`);
245
+ }
246
+ const spec = { kind };
247
+ if (v["duration_ms"] !== undefined) {
248
+ spec.duration_ms = assertInt(v["duration_ms"], `${label}.duration_ms`);
249
+ }
250
+ if (typeof v["easing"] === "string") {
251
+ const e = v["easing"];
252
+ if (e !== "linear" && e !== "ease-in" && e !== "ease-out" && e !== "ease-in-out") {
253
+ throw protocolError(`${label}.easing must be one of "linear", "ease-in", "ease-out", "ease-in-out"`);
254
+ }
255
+ spec.easing = e;
256
+ }
257
+ if (v["stiffness"] !== undefined) {
258
+ if (typeof v["stiffness"] !== "number") {
259
+ throw protocolError(`${label}.stiffness must be a number`);
260
+ }
261
+ spec.stiffness = v["stiffness"];
262
+ }
263
+ if (v["damping"] !== undefined) {
264
+ if (typeof v["damping"] !== "number") {
265
+ throw protocolError(`${label}.damping must be a number`);
266
+ }
267
+ spec.damping = v["damping"];
268
+ }
269
+ return spec;
270
+ }
271
+ function assertCause(v, label) {
272
+ if (!isPlainObject(v)) {
273
+ throw protocolError(`${label} must be an object`);
274
+ }
275
+ const source = v["source"];
276
+ if (typeof source !== "string") {
277
+ throw protocolError(`${label}.source must be a string`);
278
+ }
279
+ const cause = { source };
280
+ if (typeof v["input_id"] === "string") {
281
+ cause.input_id = v["input_id"];
282
+ }
283
+ return cause;
284
+ }
285
+ function assertSceneTransition(v, label) {
286
+ if (!isPlainObject(v)) {
287
+ throw protocolError(`${label} must be an object`);
288
+ }
289
+ if (typeof v["kind"] !== "string") {
290
+ throw protocolError(`${label}.kind must be a string`);
291
+ }
292
+ const t = { kind: v["kind"] };
293
+ if (v["duration_ms"] !== undefined) {
294
+ t.duration_ms = assertInt(v["duration_ms"], `${label}.duration_ms`);
295
+ }
296
+ return t;
297
+ }
298
+ function assertLeafValue(v, label, path) {
299
+ if (v === null)
300
+ return;
301
+ if (typeof v === "string" || typeof v === "number" || typeof v === "boolean")
302
+ return;
303
+ if (Array.isArray(v)) {
304
+ v.forEach((item, i) => assertLeafValue(item, `${label}[${i}]`, path));
305
+ return;
306
+ }
307
+ // Per LSDP/1.0.1 §3.2.1, objects are forbidden as patch values.
308
+ // The right error code is INVALID_VALUE (recoverable, path-scoped),
309
+ // not INTERNAL — see ERROR-CODES.md §1.4 + LSDP-1.md §3.4.1.
310
+ throw new LumencastError({
311
+ code: "INVALID_VALUE",
312
+ message: `${label}: objects are forbidden in patch values, push leaf-grain instead`,
313
+ recoverable: true,
314
+ ...(path !== undefined ? { path } : {}),
315
+ });
316
+ }
317
+ function isPlainObject(v) {
318
+ return typeof v === "object" && v !== null && !Array.isArray(v);
319
+ }
320
+ function optionalTs(o) {
321
+ if (typeof o["ts"] === "string")
322
+ return { ts: o["ts"] };
323
+ return {};
324
+ }
325
+ function protocolError(message) {
326
+ return new LumencastError({ code: "INTERNAL", message, recoverable: false });
327
+ }
328
+ //# sourceMappingURL=codec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codec.js","sourceRoot":"","sources":["../src/codec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export declare function canonicalize(value: unknown): string;
2
+ /** Hash an inline LSML bundle, returning the `sha256:<hex>` identity. */
3
+ export declare function hashInlineBundle(inline: unknown): Promise<string>;
4
+ //# sourceMappingURL=bundle-hash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundle-hash.d.ts","sourceRoot":"","sources":["../../src/conformance/bundle-hash.ts"],"names":[],"mappings":"AAMA,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEnD;AAED,yEAAyE;AACzE,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAWvE"}
@@ -0,0 +1,49 @@
1
+ // Canonical sha256 hashing per LSML 1.0 §3.
2
+ // Duplicated from @lumencast/compiler to avoid a circular workspace dep
3
+ // (compiler depends on protocol). Keep the two in sync.
4
+ const ZERO_HASH = "sha256:" + "0".repeat(64);
5
+ export function canonicalize(value) {
6
+ return stringify(value);
7
+ }
8
+ /** Hash an inline LSML bundle, returning the `sha256:<hex>` identity. */
9
+ export async function hashInlineBundle(inline) {
10
+ // Per spec: scene_version is set to all zeros during hashing.
11
+ const stub = typeof inline === "object" && inline !== null && !Array.isArray(inline)
12
+ ? { ...inline, scene_version: ZERO_HASH }
13
+ : inline;
14
+ const canonical = canonicalize(stub);
15
+ const bytes = new TextEncoder().encode(canonical);
16
+ const subtle = getSubtle();
17
+ const digest = await subtle.digest("SHA-256", bytes);
18
+ return "sha256:" + bytesToHex(new Uint8Array(digest));
19
+ }
20
+ function stringify(v) {
21
+ if (v === null)
22
+ return "null";
23
+ if (typeof v === "boolean" || typeof v === "number")
24
+ return JSON.stringify(v);
25
+ if (typeof v === "string")
26
+ return JSON.stringify(v);
27
+ if (Array.isArray(v))
28
+ return "[" + v.map(stringify).join(",") + "]";
29
+ if (typeof v === "object") {
30
+ const obj = v;
31
+ const keys = Object.keys(obj).sort();
32
+ return "{" + keys.map((k) => JSON.stringify(k) + ":" + stringify(obj[k])).join(",") + "}";
33
+ }
34
+ return "null";
35
+ }
36
+ function bytesToHex(bytes) {
37
+ let s = "";
38
+ for (const b of bytes)
39
+ s += b.toString(16).padStart(2, "0");
40
+ return s;
41
+ }
42
+ function getSubtle() {
43
+ const c = globalThis.crypto;
44
+ if (!c?.subtle) {
45
+ throw new Error("conformance: crypto.subtle not available — Node >= 18 or browser required");
46
+ }
47
+ return c.subtle;
48
+ }
49
+ //# sourceMappingURL=bundle-hash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundle-hash.js","sourceRoot":"","sources":["../../src/conformance/bundle-hash.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,wEAAwE;AACxE,wDAAwD;AAExD,MAAM,SAAS,GAAG,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAE7C,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC;AAC1B,CAAC;AAED,yEAAyE;AACzE,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,MAAe;IACpD,8DAA8D;IAC9D,MAAM,IAAI,GACR,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QACrE,CAAC,CAAC,EAAE,GAAI,MAAkC,EAAE,aAAa,EAAE,SAAS,EAAE;QACtE,CAAC,CAAC,MAAM,CAAC;IACb,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,KAAK,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAClD,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IACrD,OAAO,SAAS,GAAG,UAAU,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;AACxD,CAAC;AAED,SAAS,SAAS,CAAC,CAAU;IAC3B,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC;IAC9B,IAAI,OAAO,CAAC,KAAK,SAAS,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IAC9E,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IACpD,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAAE,OAAO,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;IACpE,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,CAA4B,CAAC;QACzC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QACrC,OAAO,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;IAC5F,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,UAAU,CAAC,KAAiB;IACnC,IAAI,CAAC,GAAG,EAAE,CAAC;IACX,KAAK,MAAM,CAAC,IAAI,KAAK;QAAE,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC5D,OAAO,CAAC,CAAC;AACX,CAAC;AAMD,SAAS,SAAS;IAChB,MAAM,CAAC,GAAI,UAA8D,CAAC,MAAM,CAAC;IACjF,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,2EAA2E,CAAC,CAAC;IAC/F,CAAC;IACD,OAAO,CAAC,CAAC,MAAM,CAAC;AAClB,CAAC"}
@@ -0,0 +1,42 @@
1
+ export interface SetupRequest {
2
+ scenario: string;
3
+ tokens: Record<string, string>;
4
+ bundles: Array<{
5
+ id: string;
6
+ hash: string;
7
+ inline: unknown;
8
+ }>;
9
+ initial_state: Record<string, unknown>;
10
+ }
11
+ export interface SetupResponse {
12
+ ws_url: string;
13
+ scene_id: string;
14
+ scene_version: string;
15
+ }
16
+ export interface StateResponse {
17
+ scene_id: string;
18
+ scene_version: string;
19
+ state: Record<string, unknown>;
20
+ }
21
+ export interface HealthResponse {
22
+ status: string;
23
+ control_plane_version: number;
24
+ server?: string;
25
+ }
26
+ export declare class ControlClient {
27
+ private readonly baseUrl;
28
+ constructor(baseUrl: string);
29
+ setup(req: SetupRequest): Promise<SetupResponse>;
30
+ reset(): Promise<void>;
31
+ state(): Promise<StateResponse>;
32
+ emit(patches: Array<{
33
+ path: string;
34
+ value: unknown;
35
+ }>): Promise<void>;
36
+ health(): Promise<HealthResponse>;
37
+ private postJson;
38
+ private postNoBody;
39
+ private getJson;
40
+ private toProblem;
41
+ }
42
+ //# sourceMappingURL=control-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"control-client.d.ts","sourceRoot":"","sources":["../../src/conformance/control-client.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,OAAO,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC9D,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,qBAAqB,EAAE,MAAM,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,aAAa;IACZ,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,MAAM;IAEtC,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC;IAIhD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,KAAK,IAAI,OAAO,CAAC,aAAa,CAAC;IAI/B,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrE,MAAM,IAAI,OAAO,CAAC,cAAc,CAAC;YAIzB,QAAQ;YAYR,UAAU;YAWV,OAAO;YAQP,SAAS;CAUxB"}
@@ -0,0 +1,63 @@
1
+ // HTTP client for the LSDP/1 interop test control plane.
2
+ // Spec: lumencast-protocol/interop/CONTROL.md
3
+ export class ControlClient {
4
+ baseUrl;
5
+ constructor(baseUrl) {
6
+ this.baseUrl = baseUrl;
7
+ }
8
+ async setup(req) {
9
+ return await this.postJson("/test/setup", req);
10
+ }
11
+ async reset() {
12
+ await this.postNoBody("/test/reset");
13
+ }
14
+ async state() {
15
+ return await this.getJson("/test/state");
16
+ }
17
+ async emit(patches) {
18
+ await this.postNoBody("/test/emit", { patches });
19
+ }
20
+ async health() {
21
+ return await this.getJson("/test/health");
22
+ }
23
+ async postJson(path, body) {
24
+ const res = await fetch(`${this.baseUrl}${path}`, {
25
+ method: "POST",
26
+ headers: { "content-type": "application/json" },
27
+ body: JSON.stringify(body),
28
+ });
29
+ if (res.status >= 400) {
30
+ throw await this.toProblem(res, path);
31
+ }
32
+ return (await res.json());
33
+ }
34
+ async postNoBody(path, body) {
35
+ const res = await fetch(`${this.baseUrl}${path}`, {
36
+ method: "POST",
37
+ headers: body !== undefined ? { "content-type": "application/json" } : {},
38
+ body: body !== undefined ? JSON.stringify(body) : undefined,
39
+ });
40
+ if (res.status >= 400) {
41
+ throw await this.toProblem(res, path);
42
+ }
43
+ }
44
+ async getJson(path) {
45
+ const res = await fetch(`${this.baseUrl}${path}`);
46
+ if (res.status >= 400) {
47
+ throw await this.toProblem(res, path);
48
+ }
49
+ return (await res.json());
50
+ }
51
+ async toProblem(res, path) {
52
+ let detail;
53
+ try {
54
+ const body = (await res.json());
55
+ detail = body.detail ?? body.title ?? `${res.status}`;
56
+ }
57
+ catch {
58
+ detail = `${res.status}`;
59
+ }
60
+ return new Error(`control: ${path} → ${res.status}: ${detail}`);
61
+ }
62
+ }
63
+ //# sourceMappingURL=control-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"control-client.js","sourceRoot":"","sources":["../../src/conformance/control-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ import type { Scenario, Tag, Target } from "./scenario.js";
2
+ export interface HarnessOptions {
3
+ /** ws://host:port/lsdp/v1 — the WS endpoint the server returns from /test/setup,
4
+ * OR (for servers that don't speak the control plane) the static endpoint. */
5
+ serverUrl?: string;
6
+ /** http://host:port — the test control plane root. Required for cross-language. */
7
+ controlUrl: string;
8
+ /** Token map used by /test/setup. Must include the canonical placeholders.
9
+ * Default: the canonical interop tokens (matches lumencast-protocol/interop/fixtures/). */
10
+ tokens?: Record<string, string>;
11
+ /** Per-step read timeout. Default 2_000 ms. */
12
+ stepTimeoutMs?: number;
13
+ }
14
+ export type Outcome = "PASS" | "FAIL" | "SKIP";
15
+ export interface ScenarioResult {
16
+ name: string;
17
+ tag: Tag;
18
+ target: Target;
19
+ outcome: Outcome;
20
+ reason?: string;
21
+ }
22
+ export interface Report {
23
+ total: number;
24
+ passed: number;
25
+ failed: number;
26
+ skipped: number;
27
+ results: ScenarioResult[];
28
+ }
29
+ export declare class Harness {
30
+ private readonly opts;
31
+ private readonly control;
32
+ private readonly tokens;
33
+ private readonly stepTimeoutMs;
34
+ constructor(opts: HarnessOptions);
35
+ /** Run a single scenario. */
36
+ runScenario(scenario: Scenario): Promise<ScenarioResult>;
37
+ /** Run every scenario in `scenarios` matching the tag filter (default required). */
38
+ runAll(scenarios: Scenario[], tagFilter?: Tag): Promise<Report>;
39
+ private computeBundleHashes;
40
+ }
41
+ //# sourceMappingURL=harness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../../src/conformance/harness.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAA4B,QAAQ,EAAQ,GAAG,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE3F,MAAM,WAAW,cAAc;IAC7B;mFAC+E;IAC/E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mFAAmF;IACnF,UAAU,EAAE,MAAM,CAAC;IACnB;gGAC4F;IAC5F,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,+CAA+C;IAC/C,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAUD,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAE/C,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,GAAG,CAAC;IACT,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,cAAc,EAAE,CAAC;CAC3B;AAED,qBAAa,OAAO;IAKN,OAAO,CAAC,QAAQ,CAAC,IAAI;IAJjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAyB;IAChD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;gBAEV,IAAI,EAAE,cAAc;IAMjD,6BAA6B;IACvB,WAAW,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC;IAuE9D,oFAAoF;IAC9E,MAAM,CAAC,SAAS,EAAE,QAAQ,EAAE,EAAE,SAAS,GAAE,GAAgB,GAAG,OAAO,CAAC,MAAM,CAAC;YAwBnE,mBAAmB;CASlC"}