@amalgm/live 0.2.26 → 0.2.28

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 (197) hide show
  1. package/PURPOSE.md +52 -29
  2. package/README.md +1 -2
  3. package/dist/contracts/index.d.ts +0 -1
  4. package/dist/contracts/index.d.ts.map +1 -1
  5. package/dist/contracts/index.js +0 -1
  6. package/dist/contracts/index.js.map +1 -1
  7. package/dist/detection/record-proposal.d.ts +3 -2
  8. package/dist/detection/record-proposal.d.ts.map +1 -1
  9. package/dist/detection/record-proposal.js +9 -7
  10. package/dist/detection/record-proposal.js.map +1 -1
  11. package/dist/index.d.ts +3 -2
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +3 -2
  14. package/dist/index.js.map +1 -1
  15. package/dist/machines/index.d.ts +0 -2
  16. package/dist/machines/index.d.ts.map +1 -1
  17. package/dist/machines/index.js +0 -2
  18. package/dist/machines/index.js.map +1 -1
  19. package/dist/records/authority.d.ts +23 -0
  20. package/dist/records/authority.d.ts.map +1 -0
  21. package/dist/records/authority.js +28 -0
  22. package/dist/records/authority.js.map +1 -0
  23. package/dist/records/codec.d.ts +6 -0
  24. package/dist/records/codec.d.ts.map +1 -0
  25. package/dist/records/codec.js +42 -0
  26. package/dist/records/codec.js.map +1 -0
  27. package/dist/records/contracts.d.ts +46 -0
  28. package/dist/records/contracts.d.ts.map +1 -0
  29. package/dist/records/contracts.js +114 -0
  30. package/dist/records/contracts.js.map +1 -0
  31. package/dist/records/inbox.d.ts +18 -0
  32. package/dist/records/inbox.d.ts.map +1 -0
  33. package/dist/records/inbox.js +34 -0
  34. package/dist/records/inbox.js.map +1 -0
  35. package/dist/records/index.d.ts +7 -0
  36. package/dist/records/index.d.ts.map +1 -0
  37. package/dist/records/index.js +7 -0
  38. package/dist/records/index.js.map +1 -0
  39. package/dist/records/outbox.d.ts +20 -0
  40. package/dist/records/outbox.d.ts.map +1 -0
  41. package/dist/records/outbox.js +44 -0
  42. package/dist/records/outbox.js.map +1 -0
  43. package/dist/registration/user-ground.d.ts +5 -2
  44. package/dist/registration/user-ground.d.ts.map +1 -1
  45. package/dist/registration/user-ground.js +14 -3
  46. package/dist/registration/user-ground.js.map +1 -1
  47. package/dist/watching/suspicion.d.ts.map +1 -1
  48. package/dist/watching/suspicion.js +29 -1
  49. package/dist/watching/suspicion.js.map +1 -1
  50. package/dist-cjs/contracts/binding.js +26 -0
  51. package/dist-cjs/contracts/binding.js.map +1 -0
  52. package/dist-cjs/contracts/content.js +99 -0
  53. package/dist-cjs/contracts/content.js.map +1 -0
  54. package/dist-cjs/contracts/docs.js +56 -0
  55. package/dist-cjs/contracts/docs.js.map +1 -0
  56. package/dist-cjs/contracts/events.js +44 -0
  57. package/dist-cjs/contracts/events.js.map +1 -0
  58. package/dist-cjs/contracts/frames/gateway.js +39 -0
  59. package/dist-cjs/contracts/frames/gateway.js.map +1 -0
  60. package/dist-cjs/contracts/frames/stream.js +20 -0
  61. package/dist-cjs/contracts/frames/stream.js.map +1 -0
  62. package/dist-cjs/contracts/frames/tunnel.js +60 -0
  63. package/dist-cjs/contracts/frames/tunnel.js.map +1 -0
  64. package/dist-cjs/contracts/index.js +34 -0
  65. package/dist-cjs/contracts/index.js.map +1 -0
  66. package/dist-cjs/contracts/presence.js +38 -0
  67. package/dist-cjs/contracts/presence.js.map +1 -0
  68. package/dist-cjs/contracts/resources.js +114 -0
  69. package/dist-cjs/contracts/resources.js.map +1 -0
  70. package/dist-cjs/contracts/snapshot.js +31 -0
  71. package/dist-cjs/contracts/snapshot.js.map +1 -0
  72. package/dist-cjs/contracts/validate.js +34 -0
  73. package/dist-cjs/contracts/validate.js.map +1 -0
  74. package/dist-cjs/detection/adapter-contract.js +168 -0
  75. package/dist-cjs/detection/adapter-contract.js.map +1 -0
  76. package/dist-cjs/detection/batch.js +71 -0
  77. package/dist-cjs/detection/batch.js.map +1 -0
  78. package/dist-cjs/detection/classify.js +48 -0
  79. package/dist-cjs/detection/classify.js.map +1 -0
  80. package/dist-cjs/detection/content.js +122 -0
  81. package/dist-cjs/detection/content.js.map +1 -0
  82. package/dist-cjs/detection/continuity.js +88 -0
  83. package/dist-cjs/detection/continuity.js.map +1 -0
  84. package/dist-cjs/detection/diff.js +193 -0
  85. package/dist-cjs/detection/diff.js.map +1 -0
  86. package/dist-cjs/detection/echo.js +25 -0
  87. package/dist-cjs/detection/echo.js.map +1 -0
  88. package/dist-cjs/detection/enrollment.js +189 -0
  89. package/dist-cjs/detection/enrollment.js.map +1 -0
  90. package/dist-cjs/detection/index.js +57 -0
  91. package/dist-cjs/detection/index.js.map +1 -0
  92. package/dist-cjs/detection/record-proposal.js +170 -0
  93. package/dist-cjs/detection/record-proposal.js.map +1 -0
  94. package/dist-cjs/detection/refusal.js +44 -0
  95. package/dist-cjs/detection/refusal.js.map +1 -0
  96. package/dist-cjs/detection/repository.js +67 -0
  97. package/dist-cjs/detection/repository.js.map +1 -0
  98. package/dist-cjs/detection/territory.js +82 -0
  99. package/dist-cjs/detection/territory.js.map +1 -0
  100. package/dist-cjs/detection/watch-boundary.js +51 -0
  101. package/dist-cjs/detection/watch-boundary.js.map +1 -0
  102. package/dist-cjs/entities/bytes.js +92 -0
  103. package/dist-cjs/entities/bytes.js.map +1 -0
  104. package/dist-cjs/entities/cloud.js +171 -0
  105. package/dist-cjs/entities/cloud.js.map +1 -0
  106. package/dist-cjs/entities/index.js +39 -0
  107. package/dist-cjs/entities/index.js.map +1 -0
  108. package/dist-cjs/entities/moves.js +85 -0
  109. package/dist-cjs/entities/moves.js.map +1 -0
  110. package/dist-cjs/entities/repo-states.js +256 -0
  111. package/dist-cjs/entities/repo-states.js.map +1 -0
  112. package/dist-cjs/entities/rules.js +216 -0
  113. package/dist-cjs/entities/rules.js.map +1 -0
  114. package/dist-cjs/entities/types.js +44 -0
  115. package/dist-cjs/entities/types.js.map +1 -0
  116. package/dist-cjs/files/commands.js +120 -0
  117. package/dist-cjs/files/commands.js.map +1 -0
  118. package/dist-cjs/files/index.js +19 -0
  119. package/dist-cjs/files/index.js.map +1 -0
  120. package/dist-cjs/index.js +39 -0
  121. package/dist-cjs/index.js.map +1 -0
  122. package/dist-cjs/machines/arbitration.js +41 -0
  123. package/dist-cjs/machines/arbitration.js.map +1 -0
  124. package/dist-cjs/machines/backoff.js +38 -0
  125. package/dist-cjs/machines/backoff.js.map +1 -0
  126. package/dist-cjs/machines/bootstrap.js +110 -0
  127. package/dist-cjs/machines/bootstrap.js.map +1 -0
  128. package/dist-cjs/machines/chunks.js +72 -0
  129. package/dist-cjs/machines/chunks.js.map +1 -0
  130. package/dist-cjs/machines/index.js +28 -0
  131. package/dist-cjs/machines/index.js.map +1 -0
  132. package/dist-cjs/machines/presence.js +53 -0
  133. package/dist-cjs/machines/presence.js.map +1 -0
  134. package/dist-cjs/merge/index.js +25 -0
  135. package/dist-cjs/merge/index.js.map +1 -0
  136. package/dist-cjs/merge/merge3.js +191 -0
  137. package/dist-cjs/merge/merge3.js.map +1 -0
  138. package/dist-cjs/package.json +1 -0
  139. package/dist-cjs/records/authority.js +31 -0
  140. package/dist-cjs/records/authority.js.map +1 -0
  141. package/dist-cjs/records/codec.js +48 -0
  142. package/dist-cjs/records/codec.js.map +1 -0
  143. package/dist-cjs/records/contracts.js +121 -0
  144. package/dist-cjs/records/contracts.js.map +1 -0
  145. package/dist-cjs/records/inbox.js +37 -0
  146. package/dist-cjs/records/inbox.js.map +1 -0
  147. package/dist-cjs/records/index.js +23 -0
  148. package/dist-cjs/records/index.js.map +1 -0
  149. package/dist-cjs/records/outbox.js +48 -0
  150. package/dist-cjs/records/outbox.js.map +1 -0
  151. package/dist-cjs/registration/index.js +19 -0
  152. package/dist-cjs/registration/index.js.map +1 -0
  153. package/dist-cjs/registration/user-ground.js +183 -0
  154. package/dist-cjs/registration/user-ground.js.map +1 -0
  155. package/dist-cjs/transfer/artifacts.js +75 -0
  156. package/dist-cjs/transfer/artifacts.js.map +1 -0
  157. package/dist-cjs/transfer/batches.js +44 -0
  158. package/dist-cjs/transfer/batches.js.map +1 -0
  159. package/dist-cjs/transfer/download.js +88 -0
  160. package/dist-cjs/transfer/download.js.map +1 -0
  161. package/dist-cjs/transfer/index.js +21 -0
  162. package/dist-cjs/transfer/index.js.map +1 -0
  163. package/dist-cjs/transfer/parallel.js +35 -0
  164. package/dist-cjs/transfer/parallel.js.map +1 -0
  165. package/dist-cjs/transfer/upload.js +161 -0
  166. package/dist-cjs/transfer/upload.js.map +1 -0
  167. package/dist-cjs/watching/coverage.js +54 -0
  168. package/dist-cjs/watching/coverage.js.map +1 -0
  169. package/dist-cjs/watching/index.js +24 -0
  170. package/dist-cjs/watching/index.js.map +1 -0
  171. package/dist-cjs/watching/suspicion.js +105 -0
  172. package/dist-cjs/watching/suspicion.js.map +1 -0
  173. package/dist-cjs/workspace/api.js +66 -0
  174. package/dist-cjs/workspace/api.js.map +1 -0
  175. package/dist-cjs/workspace/identity.js +32 -0
  176. package/dist-cjs/workspace/identity.js.map +1 -0
  177. package/dist-cjs/workspace/index.js +14 -0
  178. package/dist-cjs/workspace/index.js.map +1 -0
  179. package/dist-cjs/workspace/memory-store.js +21 -0
  180. package/dist-cjs/workspace/memory-store.js.map +1 -0
  181. package/dist-cjs/workspace/registry.js +98 -0
  182. package/dist-cjs/workspace/registry.js.map +1 -0
  183. package/dist-cjs/workspace/types.js +3 -0
  184. package/dist-cjs/workspace/types.js.map +1 -0
  185. package/package.json +30 -8
  186. package/dist/contracts/mutation.d.ts +0 -51
  187. package/dist/contracts/mutation.d.ts.map +0 -1
  188. package/dist/contracts/mutation.js +0 -55
  189. package/dist/contracts/mutation.js.map +0 -1
  190. package/dist/machines/journal.d.ts +0 -63
  191. package/dist/machines/journal.d.ts.map +0 -1
  192. package/dist/machines/journal.js +0 -71
  193. package/dist/machines/journal.js.map +0 -1
  194. package/dist/machines/ordering.d.ts +0 -53
  195. package/dist/machines/ordering.d.ts.map +0 -1
  196. package/dist/machines/ordering.js +0 -87
  197. package/dist/machines/ordering.js.map +0 -1
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ /**
3
+ * Versioned document writes — the contract for whole-text writers.
4
+ *
5
+ * `POST /state/docs/write` (local-live-protocol.md §8) exists so a writer
6
+ * that read a file, thought, and wrote back does not clobber live edits
7
+ * that landed in between. `baseHash` is the lowercase sha256 hex of the
8
+ * EXACT text the writer read; every response's `sha256` chains into the
9
+ * next write.
10
+ *
11
+ * The governing invariant — never a silent winner: any path that durably
12
+ * discards text preserves it in a sidecar first and surfaces the decision
13
+ * on the doc event.
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.isSha256Hex = exports.CONFLICT_REASONS = exports.MERGE_OUTCOMES = exports.RETENTION_RING = exports.MAX_DOC_BYTES = void 0;
17
+ exports.checkDocWriteRequest = checkDocWriteRequest;
18
+ const validate_js_1 = require("./validate.js");
19
+ /** Text over this limit is refused with 413. */
20
+ exports.MAX_DOC_BYTES = 2 * 1024 * 1024;
21
+ /** Bases resolve against a ring of recent disk-sync states, per doc. */
22
+ exports.RETENTION_RING = { states: 8, maxTotalBytes: 4 * 1024 * 1024, maxAgeMs: 10 * 60_000 };
23
+ /**
24
+ * Every outcome states what happens to the losing text in the same breath.
25
+ *
26
+ * | outcome | when | losing text |
27
+ * |----------------|----------------------------------------|------------------------|
28
+ * | `noop` | incoming === live | none exists |
29
+ * | `clean` | base === live | none exists |
30
+ * | `merged` | base in retention, no overlapping hunks| both sides land |
31
+ * | `conflict` | overlapping hunks | full incoming → sidecar|
32
+ * | `base-unknown` | base outside the retention window | live text → sidecar |
33
+ * | `overwrite` | no base sent | replaced text → sidecar unless nothing was deleted |
34
+ */
35
+ exports.MERGE_OUTCOMES = ['noop', 'clean', 'merged', 'conflict', 'base-unknown', 'overwrite'];
36
+ exports.CONFLICT_REASONS = ['overlapping-hunks', 'unversioned-overwrite', 'base-unknown'];
37
+ const REQUEST_EXAMPLE = '{ path: "notes.md", text: "full new contents", baseHash: "9f86d0…" }';
38
+ const isSha256Hex = (value) => typeof value === 'string' && /^[0-9a-f]{64}$/.test(value);
39
+ exports.isSha256Hex = isSha256Hex;
40
+ function checkDocWriteRequest(input) {
41
+ if (!(0, validate_js_1.isRecord)(input))
42
+ return (0, validate_js_1.failShape)(REQUEST_EXAMPLE, `got ${input === null ? 'null' : typeof input}`);
43
+ if (!(0, validate_js_1.isNonEmptyString)(input.path)) {
44
+ return (0, validate_js_1.failShape)(REQUEST_EXAMPLE, `path was ${JSON.stringify(input.path)}, not a non-empty string`);
45
+ }
46
+ if (typeof input.text !== 'string')
47
+ return (0, validate_js_1.failShape)(REQUEST_EXAMPLE, 'text was missing — this is a whole-text write');
48
+ if (input.baseHash !== undefined && !(0, exports.isSha256Hex)(input.baseHash)) {
49
+ return (0, validate_js_1.failShape)(REQUEST_EXAMPLE, `baseHash was ${JSON.stringify(input.baseHash)}, not lowercase sha256 hex`);
50
+ }
51
+ if (input.baseSeq !== undefined && !(0, validate_js_1.isSeq)(input.baseSeq)) {
52
+ return (0, validate_js_1.failShape)(REQUEST_EXAMPLE, `baseSeq was ${JSON.stringify(input.baseSeq)}, not a non-negative integer`);
53
+ }
54
+ return (0, validate_js_1.pass)(input);
55
+ }
56
+ //# sourceMappingURL=docs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docs.js","sourceRoot":"","sources":["../../src/contracts/docs.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;;;AA2DH,oDAaC;AAtED,+CAAiG;AAEjG,gDAAgD;AACnC,QAAA,aAAa,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AAE7C,wEAAwE;AAC3D,QAAA,cAAc,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC,GAAG,IAAI,GAAG,IAAI,EAAE,QAAQ,EAAE,EAAE,GAAG,MAAM,EAAW,CAAC;AAY5G;;;;;;;;;;;GAWG;AACU,QAAA,cAAc,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,CAAU,CAAC;AAG/F,QAAA,gBAAgB,GAAG,CAAC,mBAAmB,EAAE,uBAAuB,EAAE,cAAc,CAAU,CAAC;AAmBxG,MAAM,eAAe,GAAG,sEAAsE,CAAC;AAExF,MAAM,WAAW,GAAG,CAAC,KAAc,EAAmB,EAAE,CAC7D,OAAO,KAAK,KAAK,QAAQ,IAAI,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAD/C,QAAA,WAAW,eACoC;AAE5D,SAAgB,oBAAoB,CAAC,KAAc;IACjD,IAAI,CAAC,IAAA,sBAAQ,EAAC,KAAK,CAAC;QAAE,OAAO,IAAA,uBAAS,EAAC,eAAe,EAAE,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,KAAK,EAAE,CAAC,CAAC;IACzG,IAAI,CAAC,IAAA,8BAAgB,EAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAClC,OAAO,IAAA,uBAAS,EAAC,eAAe,EAAE,YAAY,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;IACtG,CAAC;IACD,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAA,uBAAS,EAAC,eAAe,EAAE,+CAA+C,CAAC,CAAC;IACvH,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,IAAI,CAAC,IAAA,mBAAW,EAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;QACjE,OAAO,IAAA,uBAAS,EAAC,eAAe,EAAE,gBAAgB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,4BAA4B,CAAC,CAAC;IAChH,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,CAAC,IAAA,mBAAK,EAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACzD,OAAO,IAAA,uBAAS,EAAC,eAAe,EAAE,eAAe,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,8BAA8B,CAAC,CAAC;IAChH,CAAC;IACD,OAAO,IAAA,kBAAI,EAAC,KAAmC,CAAC,CAAC;AACnD,CAAC"}
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ /**
3
+ * State events — the unit of change on the local wire.
4
+ *
5
+ * Every change is an event (local-live-protocol.md §1). Tables and disk are
6
+ * truth; the event log is a bounded (~15 min) delivery buffer, so a client
7
+ * that falls off the buffer re-snapshots rather than asking for history.
8
+ *
9
+ * `seq` is a strictly increasing integer per runtime. The two optional
10
+ * Wave 3 fields (`mutationId`, `sharedResourceId`) are additive: legacy
11
+ * rows omit both keys exactly as before, and validation here accepts both
12
+ * generations (axiom: additive-only evolution).
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.isNewerThan = exports.isWireOp = exports.WIRE_OPS = void 0;
16
+ exports.checkStateEvent = checkStateEvent;
17
+ const validate_js_1 = require("./validate.js");
18
+ /** The shipped wire operations. `move`/`delta` (realtime-sdk.md §3) ride as contract-specific patches until a wire rev adds them. */
19
+ exports.WIRE_OPS = ['insert', 'update', 'delete', 'replace', 'invalidate'];
20
+ const EXAMPLE = '{ seq: 42, ts: 1719000000000, resource: "tasks", op: "update", id: "t1", patch: {...} }';
21
+ const isWireOp = (value) => typeof value === 'string' && exports.WIRE_OPS.includes(value);
22
+ exports.isWireOp = isWireOp;
23
+ /**
24
+ * Validate one wire event. Unknown extra keys are preserved untouched —
25
+ * a newer peer may be speaking an additive extension we do not know yet.
26
+ */
27
+ function checkStateEvent(input) {
28
+ if (!(0, validate_js_1.isRecord)(input))
29
+ return (0, validate_js_1.failShape)(EXAMPLE, `got ${input === null ? 'null' : typeof input}`);
30
+ if (!(0, validate_js_1.isSeq)(input.seq))
31
+ return (0, validate_js_1.failShape)(EXAMPLE, `seq was ${JSON.stringify(input.seq)}, not a non-negative integer`);
32
+ if (typeof input.ts !== 'number')
33
+ return (0, validate_js_1.failShape)(EXAMPLE, `ts was ${JSON.stringify(input.ts)}, not a number`);
34
+ if (!(0, validate_js_1.isNonEmptyString)(input.resource))
35
+ return (0, validate_js_1.failShape)(EXAMPLE, `resource was ${JSON.stringify(input.resource)}, not a name`);
36
+ if (!(0, exports.isWireOp)(input.op)) {
37
+ return (0, validate_js_1.failShape)(EXAMPLE, `op was ${JSON.stringify(input.op)}, not one of ${exports.WIRE_OPS.join('|')}`);
38
+ }
39
+ return (0, validate_js_1.pass)(input);
40
+ }
41
+ /** Events apply in seq order; equal or older seq means already applied (client obligation, protocol §6). */
42
+ const isNewerThan = (event, appliedSeq) => event.seq > appliedSeq;
43
+ exports.isNewerThan = isNewerThan;
44
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/contracts/events.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;;AAsCH,0CASC;AA7CD,+CAAiG;AAGjG,qIAAqI;AACxH,QAAA,QAAQ,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,CAAU,CAAC;AAuBzF,MAAM,OAAO,GAAG,yFAAyF,CAAC;AAEnG,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAmB,EAAE,CAC1D,OAAO,KAAK,KAAK,QAAQ,IAAK,gBAA8B,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AADlE,QAAA,QAAQ,YAC0D;AAE/E;;;GAGG;AACH,SAAgB,eAAe,CAAC,KAAc;IAC5C,IAAI,CAAC,IAAA,sBAAQ,EAAC,KAAK,CAAC;QAAE,OAAO,IAAA,uBAAS,EAAC,OAAO,EAAE,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,KAAK,EAAE,CAAC,CAAC;IACjG,IAAI,CAAC,IAAA,mBAAK,EAAC,KAAK,CAAC,GAAG,CAAC;QAAE,OAAO,IAAA,uBAAS,EAAC,OAAO,EAAE,WAAW,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;IACrH,IAAI,OAAO,KAAK,CAAC,EAAE,KAAK,QAAQ;QAAE,OAAO,IAAA,uBAAS,EAAC,OAAO,EAAE,UAAU,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAAC,CAAC;IAChH,IAAI,CAAC,IAAA,8BAAgB,EAAC,KAAK,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAA,uBAAS,EAAC,OAAO,EAAE,gBAAgB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;IAC/H,IAAI,CAAC,IAAA,gBAAQ,EAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;QACxB,OAAO,IAAA,uBAAS,EAAC,OAAO,EAAE,UAAU,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,gBAAgB,gBAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACpG,CAAC;IACD,OAAO,IAAA,kBAAI,EAAC,KAA8B,CAAC,CAAC;AAC9C,CAAC;AAED,4GAA4G;AACrG,MAAM,WAAW,GAAG,CAAC,KAAiB,EAAE,UAAkB,EAAW,EAAE,CAAC,KAAK,CAAC,GAAG,GAAG,UAAU,CAAC;AAAzF,QAAA,WAAW,eAA8E"}
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ /**
3
+ * Gateway state socket — browser ⇄ Fly gateway
4
+ * (local-live-protocol.md §4).
5
+ *
6
+ * The barrier rule: in bootstrap mode the gateway MUST NOT send
7
+ * `state.connected` before `state.snapshot`. A browser treats
8
+ * `state.connected` as "delivery confirmed from here on" — and it is
9
+ * re-sent on every transport change, so `transport: 'poll'` is how the
10
+ * client learns it is degraded. Polling is a degraded state, never a
11
+ * destination: a polling session retries the runtime stream and promotes
12
+ * back.
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.POLL_INTERVAL_MS = exports.POLL_PROMOTE_INTERVAL_MS = exports.HEARTBEAT_INTERVAL_MS = exports.COMMAND_LIMITS = exports.CAPABILITY_STATE_COMMAND = void 0;
16
+ exports.checkCommand = checkCommand;
17
+ const validate_js_1 = require("../validate.js");
18
+ /** A client uses socket commands only when `state.connected.capabilities` advertises this. */
19
+ exports.CAPABILITY_STATE_COMMAND = 'state.command.v1';
20
+ /** Commands are a small allowlist, validated and bounded — never an arbitrary method/path. */
21
+ exports.COMMAND_LIMITS = { maxBodyBytes: 4 * 1024 * 1024, maxInFlight: 16 };
22
+ exports.HEARTBEAT_INTERVAL_MS = 25_000;
23
+ /** A polling session retries the runtime stream at this cadence (env AMALGM_STATE_STREAM_PROMOTE_MS). */
24
+ exports.POLL_PROMOTE_INTERVAL_MS = 30_000;
25
+ exports.POLL_INTERVAL_MS = 1_000;
26
+ const COMMAND_EXAMPLE = '{ type: "state.command", requestId: "r1", command: "doc.update", payload: { path, updates } }';
27
+ function checkCommand(input) {
28
+ if (!(0, validate_js_1.isRecord)(input) || input.type !== 'state.command') {
29
+ return (0, validate_js_1.failShape)(COMMAND_EXAMPLE, `type was ${(0, validate_js_1.isRecord)(input) ? JSON.stringify(input.type) : typeof input}`);
30
+ }
31
+ if (!(0, validate_js_1.isNonEmptyString)(input.requestId)) {
32
+ return (0, validate_js_1.failShape)(COMMAND_EXAMPLE, `requestId was ${JSON.stringify(input.requestId)}, not a non-empty string`);
33
+ }
34
+ if (!(0, validate_js_1.isNonEmptyString)(input.command)) {
35
+ return (0, validate_js_1.failShape)(COMMAND_EXAMPLE, `command was ${JSON.stringify(input.command)}, not a non-empty string`);
36
+ }
37
+ return (0, validate_js_1.pass)(input);
38
+ }
39
+ //# sourceMappingURL=gateway.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateway.js","sourceRoot":"","sources":["../../../src/contracts/frames/gateway.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;;AAuDH,oCAWC;AAhED,gDAA2F;AAI3F,8FAA8F;AACjF,QAAA,wBAAwB,GAAG,kBAAkB,CAAC;AAE3D,8FAA8F;AACjF,QAAA,cAAc,GAAG,EAAE,YAAY,EAAE,CAAC,GAAG,IAAI,GAAG,IAAI,EAAE,WAAW,EAAE,EAAE,EAAW,CAAC;AAE7E,QAAA,qBAAqB,GAAG,MAAM,CAAC;AAC5C,yGAAyG;AAC5F,QAAA,wBAAwB,GAAG,MAAM,CAAC;AAClC,QAAA,gBAAgB,GAAG,KAAK,CAAC;AAsCtC,MAAM,eAAe,GAAG,+FAA+F,CAAC;AAExH,SAAgB,YAAY,CAAC,KAAc;IACzC,IAAI,CAAC,IAAA,sBAAQ,EAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;QACvD,OAAO,IAAA,uBAAS,EAAC,eAAe,EAAE,YAAY,IAAA,sBAAQ,EAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,KAAK,EAAE,CAAC,CAAC;IAC/G,CAAC;IACD,IAAI,CAAC,IAAA,8BAAgB,EAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;QACvC,OAAO,IAAA,uBAAS,EAAC,eAAe,EAAE,iBAAiB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,0BAA0B,CAAC,CAAC;IAChH,CAAC;IACD,IAAI,CAAC,IAAA,8BAAgB,EAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACrC,OAAO,IAAA,uBAAS,EAAC,eAAe,EAAE,eAAe,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,0BAA0B,CAAC,CAAC;IAC5G,CAAC;IACD,OAAO,IAAA,kBAAI,EAAC,KAAoC,CAAC,CAAC;AACpD,CAAC"}
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ /**
3
+ * Runtime SSE stream frames — `GET /state/stream`
4
+ * (local-live-protocol.md §3).
5
+ *
6
+ * Two modes share one endpoint. Legacy: hello comment → backlog replay →
7
+ * live events, with the snapshot fetched separately (the race that creates
8
+ * is closed client-side by the full-snapshot barrier). Ordered-bootstrap
9
+ * (`bootstrap=1&resources=…`): the server registers the listener FIRST,
10
+ * then sends the snapshot as the first data frame, then flushes buffered
11
+ * events past its seq — the snapshot-buries-event race is impossible by
12
+ * construction because everything travels ordered on one channel.
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.BOOTSTRAP_SNAPSHOT_TIMEOUT_MS = exports.PING_INTERVAL_MS = void 0;
16
+ /** Sent every 25 seconds. */
17
+ exports.PING_INTERVAL_MS = 25_000;
18
+ /** A bootstrap client falls back to the legacy path when no snapshot frame arrives in time. */
19
+ exports.BOOTSTRAP_SNAPSHOT_TIMEOUT_MS = 5_000;
20
+ //# sourceMappingURL=stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.js","sourceRoot":"","sources":["../../../src/contracts/frames/stream.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;;AAgBH,6BAA6B;AAChB,QAAA,gBAAgB,GAAG,MAAM,CAAC;AACvC,+FAA+F;AAClF,QAAA,6BAA6B,GAAG,KAAK,CAAC"}
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ /**
3
+ * Tunnel frames — runtime ⇄ gateway, protocol v2
4
+ * (local-live-protocol.md §5).
5
+ *
6
+ * One outbound WebSocket per runtime. v2 is negotiated per connection: the
7
+ * runtime's hello carries `protocol_version: 2`; no `hello_ack` means the
8
+ * peer is v1 and only v1 frames may be sent. Slot arbitration is
9
+ * hello-driven and identity-based — the decision function itself lives in
10
+ * `machines/arbitration.ts`; this file owns the wire shapes and constants
11
+ * both sides must agree on.
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.hasUsableIdentity = exports.CHUNKING = exports.BACKPRESSURE = exports.CLOSE_CODES = exports.CAPABILITY_SHARED_MUTATION = exports.HELLO_INTERVAL_MS = exports.PROTOCOL_VERSION = void 0;
15
+ exports.checkHello = checkHello;
16
+ const validate_js_1 = require("../validate.js");
17
+ exports.PROTOCOL_VERSION = 2;
18
+ /** Sent on connect and every 15s. */
19
+ exports.HELLO_INTERVAL_MS = 15_000;
20
+ /** Advertised in hello acks; only then do upgraded runtimes use additive shared.resource.* / shared.mutation.* frames. */
21
+ exports.CAPABILITY_SHARED_MUTATION = 'shared.mutation.v1';
22
+ exports.CLOSE_CODES = {
23
+ /** The newcomer won the slot; the old connection is fenced before this close. */
24
+ replaced: 4000,
25
+ /** Refused: an older or equal boot_ts lost arbitration. Retry slowly, never stop. */
26
+ supersededInstance: 4009,
27
+ unauthorized: 4401,
28
+ };
29
+ exports.BACKPRESSURE = {
30
+ /** Frames above this queue behind bufferedAmount; control frames always send immediately. */
31
+ controlMaxBytes: 32 * 1024,
32
+ /** High-water mark on the socket buffer (AMALGM_TUNNEL_HWM_BYTES). */
33
+ highWaterMarkBytes: 1024 * 1024,
34
+ /** Bounded bulk queue (AMALGM_TUNNEL_BULK_MAX_BYTES); overflow drops the response as a 503 res frame — never a silent hang. */
35
+ bulkMaxBytes: 64 * 1024 * 1024,
36
+ };
37
+ exports.CHUNKING = {
38
+ /** Raw bodies above this are chunked to v2 peers. */
39
+ thresholdBytes: 256 * 1024,
40
+ /** ≤256KB raw per chunk, 0-based indexes. */
41
+ chunkMaxBytes: 256 * 1024,
42
+ };
43
+ const HELLO_EXAMPLE = '{ type: "hello", instance_id: "a1b2c3d4e5f60718", boot_ts: 1719000000000, runtime_version: "0.1.157", protocol_version: 2, runtime_label: "main" }';
44
+ function checkHello(input) {
45
+ if (!(0, validate_js_1.isRecord)(input) || input.type !== 'hello') {
46
+ return (0, validate_js_1.failShape)(HELLO_EXAMPLE, `type was ${(0, validate_js_1.isRecord)(input) ? JSON.stringify(input.type) : typeof input}`);
47
+ }
48
+ if (!(0, validate_js_1.isNonEmptyString)(input.runtime_label)) {
49
+ return (0, validate_js_1.failShape)(HELLO_EXAMPLE, `runtime_label was ${JSON.stringify(input.runtime_label)}, not a non-empty string`);
50
+ }
51
+ // Identity fields are additive v2: a v1 hello without them is still a valid hello.
52
+ return (0, validate_js_1.pass)(input);
53
+ }
54
+ /** Usable identity for arbitration: both fields present and well-formed. Missing/unusable identity falls back to legacy last-connect-wins. */
55
+ const hasUsableIdentity = (hello) => typeof hello.instance_id === 'string' &&
56
+ /^[0-9a-f]{16}$/.test(hello.instance_id) &&
57
+ typeof hello.boot_ts === 'number' &&
58
+ Number.isFinite(hello.boot_ts);
59
+ exports.hasUsableIdentity = hasUsableIdentity;
60
+ //# sourceMappingURL=tunnel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tunnel.js","sourceRoot":"","sources":["../../../src/contracts/frames/tunnel.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;AAgEH,gCASC;AAvED,gDAA2F;AAE9E,QAAA,gBAAgB,GAAG,CAAC,CAAC;AAClC,qCAAqC;AACxB,QAAA,iBAAiB,GAAG,MAAM,CAAC;AAExC,0HAA0H;AAC7G,QAAA,0BAA0B,GAAG,oBAAoB,CAAC;AAElD,QAAA,WAAW,GAAG;IACzB,iFAAiF;IACjF,QAAQ,EAAE,IAAI;IACd,qFAAqF;IACrF,kBAAkB,EAAE,IAAI;IACxB,YAAY,EAAE,IAAI;CACV,CAAC;AAEE,QAAA,YAAY,GAAG;IAC1B,6FAA6F;IAC7F,eAAe,EAAE,EAAE,GAAG,IAAI;IAC1B,sEAAsE;IACtE,kBAAkB,EAAE,IAAI,GAAG,IAAI;IAC/B,+HAA+H;IAC/H,YAAY,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI;CACtB,CAAC;AAEE,QAAA,QAAQ,GAAG;IACtB,qDAAqD;IACrD,cAAc,EAAE,GAAG,GAAG,IAAI;IAC1B,6CAA6C;IAC7C,aAAa,EAAE,GAAG,GAAG,IAAI;CACjB,CAAC;AA4BX,MAAM,aAAa,GACjB,oJAAoJ,CAAC;AAEvJ,SAAgB,UAAU,CAAC,KAAc;IACvC,IAAI,CAAC,IAAA,sBAAQ,EAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC/C,OAAO,IAAA,uBAAS,EAAC,aAAa,EAAE,YAAY,IAAA,sBAAQ,EAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,KAAK,EAAE,CAAC,CAAC;IAC7G,CAAC;IACD,IAAI,CAAC,IAAA,8BAAgB,EAAC,KAAK,CAAC,aAAa,CAAC,EAAE,CAAC;QAC3C,OAAO,IAAA,uBAAS,EAAC,aAAa,EAAE,qBAAqB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,aAAa,CAAC,0BAA0B,CAAC,CAAC;IACtH,CAAC;IACD,mFAAmF;IACnF,OAAO,IAAA,kBAAI,EAAC,KAA+B,CAAC,CAAC;AAC/C,CAAC;AAED,8IAA8I;AACvI,MAAM,iBAAiB,GAAG,CAAC,KAA2B,EAAW,EAAE,CACxE,OAAO,KAAK,CAAC,WAAW,KAAK,QAAQ;IACrC,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC;IACxC,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ;IACjC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AAJpB,QAAA,iBAAiB,qBAIG"}
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ /**
3
+ * The contracts layer: every wire shape, identity rule, and vocabulary the
4
+ * protocol documents define — as types, guards, and validators. Nothing in
5
+ * here performs I/O or holds state; the state machines live in
6
+ * `../machines`, and the ports a host injects live in `../adapters`.
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
20
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
21
+ };
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ __exportStar(require("./validate.js"), exports);
24
+ __exportStar(require("./resources.js"), exports);
25
+ __exportStar(require("./events.js"), exports);
26
+ __exportStar(require("./snapshot.js"), exports);
27
+ __exportStar(require("./binding.js"), exports);
28
+ __exportStar(require("./presence.js"), exports);
29
+ __exportStar(require("./docs.js"), exports);
30
+ __exportStar(require("./content.js"), exports);
31
+ __exportStar(require("./frames/stream.js"), exports);
32
+ __exportStar(require("./frames/gateway.js"), exports);
33
+ __exportStar(require("./frames/tunnel.js"), exports);
34
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/contracts/index.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;;;;;;;;;;;;;;AAEH,gDAA8B;AAC9B,iDAA+B;AAC/B,8CAA4B;AAC5B,gDAA8B;AAC9B,+CAA6B;AAC7B,gDAA8B;AAC9B,4CAA0B;AAC1B,+CAA6B;AAC7B,qDAAmC;AACnC,sDAAoC;AACpC,qDAAmC"}
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ /**
3
+ * Ephemeral presence — the lane that is allowed to lose data.
4
+ *
5
+ * Presence is authenticated and permission-gated but best-effort
6
+ * (local-live-protocol.md §2, realtime-sdk.md §6): TTL-bound, never
7
+ * journaled, never replayed after a gap, safe to drop under backpressure.
8
+ * Re-publishing IS the heartbeat; absence is the default that publishing
9
+ * re-earns. Identity (`user`) is stamped server-side from the enrolled
10
+ * account and never read from the client body.
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.MAX_BLOB_BYTES = exports.SERVER_TTL_MS = exports.PUBLISH_INTERVAL_MS = void 0;
14
+ exports.checkPresenceFrame = checkPresenceFrame;
15
+ const validate_js_1 = require("./validate.js");
16
+ /** Clients re-publish while bound and on every position change. */
17
+ exports.PUBLISH_INTERVAL_MS = 10_000;
18
+ /** The server expires any entry not re-published within this window (10s sweep). */
19
+ exports.SERVER_TTL_MS = 30_000;
20
+ /** The surface-defined opaque position blob is capped at 8KB. */
21
+ exports.MAX_BLOB_BYTES = 8 * 1024;
22
+ const EXAMPLE = '{ resource: "doc:...", peers: [{ session: "s1", user: "ada", at: 1719000000000 }] }';
23
+ function checkPresenceFrame(input) {
24
+ if (!(0, validate_js_1.isRecord)(input))
25
+ return (0, validate_js_1.failShape)(EXAMPLE, `got ${input === null ? 'null' : typeof input}`);
26
+ if (!(0, validate_js_1.isNonEmptyString)(input.resource)) {
27
+ return (0, validate_js_1.failShape)(EXAMPLE, `resource was ${JSON.stringify(input.resource)}, not a name`);
28
+ }
29
+ if (!Array.isArray(input.peers))
30
+ return (0, validate_js_1.failShape)(EXAMPLE, 'peers was not an array');
31
+ for (const peer of input.peers) {
32
+ if (!(0, validate_js_1.isRecord)(peer) || !(0, validate_js_1.isNonEmptyString)(peer.session) || !(0, validate_js_1.isNonEmptyString)(peer.user)) {
33
+ return (0, validate_js_1.failShape)(EXAMPLE, `peer ${JSON.stringify(peer)} is missing session or user`);
34
+ }
35
+ }
36
+ return (0, validate_js_1.pass)(input);
37
+ }
38
+ //# sourceMappingURL=presence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presence.js","sourceRoot":"","sources":["../../src/contracts/presence.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;AA2CH,gDAYC;AArDD,+CAA0F;AAE1F,mEAAmE;AACtD,QAAA,mBAAmB,GAAG,MAAM,CAAC;AAC1C,oFAAoF;AACvE,QAAA,aAAa,GAAG,MAAM,CAAC;AACpC,iEAAiE;AACpD,QAAA,cAAc,GAAG,CAAC,GAAG,IAAI,CAAC;AAgCvC,MAAM,OAAO,GAAG,qFAAqF,CAAC;AAEtG,SAAgB,kBAAkB,CAAC,KAAc;IAC/C,IAAI,CAAC,IAAA,sBAAQ,EAAC,KAAK,CAAC;QAAE,OAAO,IAAA,uBAAS,EAAC,OAAO,EAAE,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,KAAK,EAAE,CAAC,CAAC;IACjG,IAAI,CAAC,IAAA,8BAAgB,EAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;QACtC,OAAO,IAAA,uBAAS,EAAC,OAAO,EAAE,gBAAgB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;IAC1F,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;QAAE,OAAO,IAAA,uBAAS,EAAC,OAAO,EAAE,wBAAwB,CAAC,CAAC;IACrF,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAC/B,IAAI,CAAC,IAAA,sBAAQ,EAAC,IAAI,CAAC,IAAI,CAAC,IAAA,8BAAgB,EAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAA,8BAAgB,EAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACvF,OAAO,IAAA,uBAAS,EAAC,OAAO,EAAE,QAAQ,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;QACvF,CAAC;IACH,CAAC;IACD,OAAO,IAAA,kBAAI,EAAC,KAAiC,CAAC,CAAC;AACjD,CAAC"}
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ /**
3
+ * Resource identity — the names of live channels.
4
+ *
5
+ * A resource is a named channel over the local wire
6
+ * (local-live-protocol.md §1). The shipped grammar:
7
+ *
8
+ * tasks a well-known singleton table
9
+ * app:<appId>:<name> a per-app named resource
10
+ * doc:<base64url(path)> a live document, addressed by local path
11
+ * files:<base64url(dir)> a directory listing channel
12
+ *
13
+ * A shared resource additionally receives a cloud id that never depends on
14
+ * any user's local path (realtime-sdk.md §2); path-derived ids are for
15
+ * unshared local resources only. Renaming a materialized file must not
16
+ * change a shared id — which is exactly why the shared id is not derived
17
+ * from the path.
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.appResource = exports.filesResource = exports.docResource = void 0;
21
+ exports.parseResource = parseResource;
22
+ exports.encodeBase64Url = encodeBase64Url;
23
+ exports.decodeBase64Url = decodeBase64Url;
24
+ const validate_js_1 = require("./validate.js");
25
+ /** `doc:<base64url(path)>` for the document at a local path. */
26
+ const docResource = (path) => `doc:${encodeBase64Url(path)}`;
27
+ exports.docResource = docResource;
28
+ /** `files:<base64url(dir)>` for the listing channel of a directory. */
29
+ const filesResource = (dir) => `files:${encodeBase64Url(dir)}`;
30
+ exports.filesResource = filesResource;
31
+ /** `app:<appId>:<name>` for an app-scoped resource. */
32
+ const appResource = (appId, name) => `app:${appId}:${name}`;
33
+ exports.appResource = appResource;
34
+ /**
35
+ * Parse any shipped resource name into its addressed form.
36
+ *
37
+ * parseResource('doc:bm90ZXMubWQ') → { kind: 'doc', path: 'notes.md' }
38
+ * parseResource('app:canvas:board') → { kind: 'app', appId: 'canvas', name: 'board' }
39
+ * parseResource('tasks') → { kind: 'table', name: 'tasks' }
40
+ */
41
+ function parseResource(resource) {
42
+ if (!(0, validate_js_1.isNonEmptyString)(resource)) {
43
+ return (0, validate_js_1.failShape)(`a resource name like "tasks" or "doc:${encodeBase64Url('notes.md')}"`, `got ${typeof resource}`);
44
+ }
45
+ const [prefix] = resource.split(':', 1);
46
+ switch (prefix) {
47
+ case 'doc': {
48
+ const path = decodeBase64Url(resource.slice('doc:'.length));
49
+ return path === null
50
+ ? (0, validate_js_1.failShape)(`"doc:${encodeBase64Url('notes.md')}" (base64url of the path)`, `"${resource}" carries invalid base64url`)
51
+ : (0, validate_js_1.pass)({ kind: 'doc', path });
52
+ }
53
+ case 'files': {
54
+ const dir = decodeBase64Url(resource.slice('files:'.length));
55
+ return dir === null
56
+ ? (0, validate_js_1.failShape)(`"files:${encodeBase64Url('notes')}" (base64url of the directory)`, `"${resource}" carries invalid base64url`)
57
+ : (0, validate_js_1.pass)({ kind: 'files', dir });
58
+ }
59
+ case 'app': {
60
+ const rest = resource.slice('app:'.length);
61
+ const divider = rest.indexOf(':');
62
+ if (divider <= 0 || divider === rest.length - 1) {
63
+ return (0, validate_js_1.failShape)('"app:<appId>:<name>", e.g. "app:canvas:board"', `"${resource}" is missing the appId or name segment`);
64
+ }
65
+ return (0, validate_js_1.pass)({ kind: 'app', appId: rest.slice(0, divider), name: rest.slice(divider + 1) });
66
+ }
67
+ default:
68
+ return (0, validate_js_1.pass)({ kind: 'table', name: resource });
69
+ }
70
+ }
71
+ const BASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
72
+ /** UTF-8 → unpadded base64url. Portable: no Buffer, no btoa. */
73
+ function encodeBase64Url(text) {
74
+ const bytes = new TextEncoder().encode(text);
75
+ let out = '';
76
+ for (let i = 0; i < bytes.length; i += 3) {
77
+ const a = bytes[i];
78
+ const b = bytes[i + 1];
79
+ const c = bytes[i + 2];
80
+ out += BASE64_ALPHABET[a >> 2];
81
+ out += BASE64_ALPHABET[((a & 0x03) << 4) | ((b ?? 0) >> 4)];
82
+ if (b !== undefined)
83
+ out += BASE64_ALPHABET[((b & 0x0f) << 2) | ((c ?? 0) >> 6)];
84
+ if (c !== undefined)
85
+ out += BASE64_ALPHABET[c & 0x3f];
86
+ }
87
+ return out;
88
+ }
89
+ /** Unpadded base64url → UTF-8, or null when the input is not base64url. */
90
+ function decodeBase64Url(encoded) {
91
+ if (encoded.length % 4 === 1)
92
+ return null;
93
+ const bytes = [];
94
+ let buffer = 0;
95
+ let bits = 0;
96
+ for (const char of encoded) {
97
+ const value = BASE64_ALPHABET.indexOf(char);
98
+ if (value === -1)
99
+ return null;
100
+ buffer = (buffer << 6) | value;
101
+ bits += 6;
102
+ if (bits >= 8) {
103
+ bits -= 8;
104
+ bytes.push((buffer >> bits) & 0xff);
105
+ }
106
+ }
107
+ try {
108
+ return new TextDecoder('utf-8', { fatal: true }).decode(new Uint8Array(bytes));
109
+ }
110
+ catch {
111
+ return null;
112
+ }
113
+ }
114
+ //# sourceMappingURL=resources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resources.js","sourceRoot":"","sources":["../../src/contracts/resources.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ /**
3
+ * Snapshots — the resume point of every recovery path.
4
+ *
5
+ * A snapshot is the full value of each requested resource, consistent as of
6
+ * `seq` when `stable` is true (local-live-protocol.md §1). The builder may
7
+ * race writes; after 3 attempts it serves best-effort data with
8
+ * `stable: false`. Clients may treat an unstable snapshot like a stable one
9
+ * — the live stream repairs staleness — but must never cache it as a
10
+ * consistency proof.
11
+ *
12
+ * Recovery rule: every failure path (seq gap beyond the buffer, `reset`
13
+ * frame, failed backfill) converges on "fetch a snapshot and resume from
14
+ * its seq".
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.checkSnapshot = checkSnapshot;
18
+ const validate_js_1 = require("./validate.js");
19
+ const EXAMPLE = '{ seq: 42, stable: true, resources: { tasks: [...] } }';
20
+ function checkSnapshot(input) {
21
+ if (!(0, validate_js_1.isRecord)(input))
22
+ return (0, validate_js_1.failShape)(EXAMPLE, `got ${input === null ? 'null' : typeof input}`);
23
+ if (!(0, validate_js_1.isSeq)(input.seq))
24
+ return (0, validate_js_1.failShape)(EXAMPLE, `seq was ${JSON.stringify(input.seq)}, not a non-negative integer`);
25
+ if (!(0, validate_js_1.isRecord)(input.resources)) {
26
+ return (0, validate_js_1.failShape)(EXAMPLE, `resources was ${JSON.stringify(input.resources)}, not an object`);
27
+ }
28
+ const stable = input.stable !== false; // absent means stable: the field is an additive escape hatch
29
+ return (0, validate_js_1.pass)({ seq: input.seq, stable, resources: input.resources });
30
+ }
31
+ //# sourceMappingURL=snapshot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../src/contracts/snapshot.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;;AAcH,sCAQC;AApBD,+CAA+E;AAU/E,MAAM,OAAO,GAAG,wDAAwD,CAAC;AAEzE,SAAgB,aAAa,CAAC,KAAc;IAC1C,IAAI,CAAC,IAAA,sBAAQ,EAAC,KAAK,CAAC;QAAE,OAAO,IAAA,uBAAS,EAAC,OAAO,EAAE,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,KAAK,EAAE,CAAC,CAAC;IACjG,IAAI,CAAC,IAAA,mBAAK,EAAC,KAAK,CAAC,GAAG,CAAC;QAAE,OAAO,IAAA,uBAAS,EAAC,OAAO,EAAE,WAAW,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;IACrH,IAAI,CAAC,IAAA,sBAAQ,EAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;QAC/B,OAAO,IAAA,uBAAS,EAAC,OAAO,EAAE,iBAAiB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IAC/F,CAAC;IACD,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,6DAA6D;IACpG,OAAO,IAAA,kBAAI,EAAC,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC;AACtE,CAAC"}
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ /**
3
+ * Shared validation vocabulary for every contract in this package.
4
+ *
5
+ * Validators never throw on bad wire data — a peer being wrong is an
6
+ * expected condition, not an exception (local-live-protocol.md: unknown
7
+ * frames mean "the other end is older"). They return a `Checked<T>` so
8
+ * callers must look at the outcome, and every failure message is
9
+ * example-first: it states what a correct value looks like before saying
10
+ * what was wrong (see PURPOSE.md axiom on error shape).
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.isSeq = exports.isNonEmptyString = exports.isRecord = exports.failShape = exports.fail = exports.pass = void 0;
14
+ const pass = (value) => ({ ok: true, value });
15
+ exports.pass = pass;
16
+ const fail = (error) => ({ ok: false, error });
17
+ exports.fail = fail;
18
+ /**
19
+ * Build an example-first failure: `expected` is a correct literal shape,
20
+ * `problem` names the specific field that broke it.
21
+ *
22
+ * failShape('{ seq: 42, resource: "tasks", op: "update" }',
23
+ * 'op was "patch", not one of insert|update|delete|replace|invalidate')
24
+ */
25
+ const failShape = (expected, problem) => (0, exports.fail)(`expected ${expected} — ${problem}`);
26
+ exports.failShape = failShape;
27
+ const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
28
+ exports.isRecord = isRecord;
29
+ const isNonEmptyString = (value) => typeof value === 'string' && value.length > 0;
30
+ exports.isNonEmptyString = isNonEmptyString;
31
+ /** A strictly increasing wire sequence number: a non-negative safe integer. */
32
+ const isSeq = (value) => typeof value === 'number' && Number.isSafeInteger(value) && value >= 0;
33
+ exports.isSeq = isSeq;
34
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/contracts/validate.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;AAMI,MAAM,IAAI,GAAG,CAAI,KAAQ,EAAc,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAA1D,QAAA,IAAI,QAAsD;AAEhE,MAAM,IAAI,GAAG,CAAY,KAAa,EAAc,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;AAAxE,QAAA,IAAI,QAAoE;AAErF;;;;;;GAMG;AACI,MAAM,SAAS,GAAG,CAAY,QAAgB,EAAE,OAAe,EAAc,EAAE,CACpF,IAAA,YAAI,EAAC,YAAY,QAAQ,MAAM,OAAO,EAAE,CAAC,CAAC;AAD/B,QAAA,SAAS,aACsB;AAErC,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAoC,EAAE,CAC3E,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAD1D,QAAA,QAAQ,YACkD;AAEhE,MAAM,gBAAgB,GAAG,CAAC,KAAc,EAAmB,EAAE,CAClE,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;AADnC,QAAA,gBAAgB,oBACmB;AAEhD,+EAA+E;AACxE,MAAM,KAAK,GAAG,CAAC,KAAc,EAAmB,EAAE,CACvD,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AAD5D,QAAA,KAAK,SACuD"}