@czap/core 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 (372) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +19 -0
  3. package/dist/addressed-digest.d.ts +15 -0
  4. package/dist/addressed-digest.d.ts.map +1 -0
  5. package/dist/addressed-digest.js +35 -0
  6. package/dist/addressed-digest.js.map +1 -0
  7. package/dist/animation.d.ts +46 -0
  8. package/dist/animation.d.ts.map +1 -0
  9. package/dist/animation.js +70 -0
  10. package/dist/animation.js.map +1 -0
  11. package/dist/assembly.d.ts +25 -0
  12. package/dist/assembly.d.ts.map +1 -0
  13. package/dist/assembly.js +58 -0
  14. package/dist/assembly.js.map +1 -0
  15. package/dist/av-bridge.d.ts +74 -0
  16. package/dist/av-bridge.d.ts.map +1 -0
  17. package/dist/av-bridge.js +107 -0
  18. package/dist/av-bridge.js.map +1 -0
  19. package/dist/av-renderer.d.ts +56 -0
  20. package/dist/av-renderer.d.ts.map +1 -0
  21. package/dist/av-renderer.js +65 -0
  22. package/dist/av-renderer.js.map +1 -0
  23. package/dist/blend.d.ts +61 -0
  24. package/dist/blend.d.ts.map +1 -0
  25. package/dist/blend.js +100 -0
  26. package/dist/blend.js.map +1 -0
  27. package/dist/boundary.d.ts +154 -0
  28. package/dist/boundary.d.ts.map +1 -0
  29. package/dist/boundary.js +269 -0
  30. package/dist/boundary.js.map +1 -0
  31. package/dist/brands.d.ts +63 -0
  32. package/dist/brands.d.ts.map +1 -0
  33. package/dist/brands.js +31 -0
  34. package/dist/brands.js.map +1 -0
  35. package/dist/caps.d.ts +49 -0
  36. package/dist/caps.d.ts.map +1 -0
  37. package/dist/caps.js +73 -0
  38. package/dist/caps.js.map +1 -0
  39. package/dist/capsule.d.ts +77 -0
  40. package/dist/capsule.d.ts.map +1 -0
  41. package/dist/capsule.js +18 -0
  42. package/dist/capsule.js.map +1 -0
  43. package/dist/capsules/boundary-evaluate.d.ts +28 -0
  44. package/dist/capsules/boundary-evaluate.d.ts.map +1 -0
  45. package/dist/capsules/boundary-evaluate.js +117 -0
  46. package/dist/capsules/boundary-evaluate.js.map +1 -0
  47. package/dist/capsules/canonical-cbor.d.ts +13 -0
  48. package/dist/capsules/canonical-cbor.d.ts.map +1 -0
  49. package/dist/capsules/canonical-cbor.js +60 -0
  50. package/dist/capsules/canonical-cbor.js.map +1 -0
  51. package/dist/capsules/token-buffer.d.ts +24 -0
  52. package/dist/capsules/token-buffer.d.ts.map +1 -0
  53. package/dist/capsules/token-buffer.js +53 -0
  54. package/dist/capsules/token-buffer.js.map +1 -0
  55. package/dist/capture.d.ts +40 -0
  56. package/dist/capture.d.ts.map +1 -0
  57. package/dist/capture.js +10 -0
  58. package/dist/capture.js.map +1 -0
  59. package/dist/cbor.d.ts +33 -0
  60. package/dist/cbor.d.ts.map +1 -0
  61. package/dist/cbor.js +179 -0
  62. package/dist/cbor.js.map +1 -0
  63. package/dist/cell.d.ts +53 -0
  64. package/dist/cell.d.ts.map +1 -0
  65. package/dist/cell.js +83 -0
  66. package/dist/cell.js.map +1 -0
  67. package/dist/codec.d.ts +30 -0
  68. package/dist/codec.d.ts.map +1 -0
  69. package/dist/codec.js +25 -0
  70. package/dist/codec.js.map +1 -0
  71. package/dist/component.d.ts +52 -0
  72. package/dist/component.d.ts.map +1 -0
  73. package/dist/component.js +44 -0
  74. package/dist/component.js.map +1 -0
  75. package/dist/composable.d.ts +76 -0
  76. package/dist/composable.d.ts.map +1 -0
  77. package/dist/composable.js +221 -0
  78. package/dist/composable.js.map +1 -0
  79. package/dist/compositor-pool.d.ts +74 -0
  80. package/dist/compositor-pool.d.ts.map +1 -0
  81. package/dist/compositor-pool.js +119 -0
  82. package/dist/compositor-pool.js.map +1 -0
  83. package/dist/compositor.d.ts +90 -0
  84. package/dist/compositor.d.ts.map +1 -0
  85. package/dist/compositor.js +278 -0
  86. package/dist/compositor.js.map +1 -0
  87. package/dist/config.d.ts +72 -0
  88. package/dist/config.d.ts.map +1 -0
  89. package/dist/config.js +97 -0
  90. package/dist/config.js.map +1 -0
  91. package/dist/dag.d.ts +251 -0
  92. package/dist/dag.d.ts.map +1 -0
  93. package/dist/dag.js +450 -0
  94. package/dist/dag.js.map +1 -0
  95. package/dist/defaults.d.ts +45 -0
  96. package/dist/defaults.d.ts.map +1 -0
  97. package/dist/defaults.js +45 -0
  98. package/dist/defaults.js.map +1 -0
  99. package/dist/derived.d.ts +34 -0
  100. package/dist/derived.d.ts.map +1 -0
  101. package/dist/derived.js +101 -0
  102. package/dist/derived.js.map +1 -0
  103. package/dist/diagnostics.d.ts +77 -0
  104. package/dist/diagnostics.d.ts.map +1 -0
  105. package/dist/diagnostics.js +122 -0
  106. package/dist/diagnostics.js.map +1 -0
  107. package/dist/dirty.d.ts +55 -0
  108. package/dist/dirty.d.ts.map +1 -0
  109. package/dist/dirty.js +80 -0
  110. package/dist/dirty.js.map +1 -0
  111. package/dist/easing.d.ts +55 -0
  112. package/dist/easing.d.ts.map +1 -0
  113. package/dist/easing.js +291 -0
  114. package/dist/easing.js.map +1 -0
  115. package/dist/ecs.d.ts +105 -0
  116. package/dist/ecs.d.ts.map +1 -0
  117. package/dist/ecs.js +245 -0
  118. package/dist/ecs.js.map +1 -0
  119. package/dist/fnv.d.ts +14 -0
  120. package/dist/fnv.d.ts.map +1 -0
  121. package/dist/fnv.js +28 -0
  122. package/dist/fnv.js.map +1 -0
  123. package/dist/frame-budget.d.ts +73 -0
  124. package/dist/frame-budget.d.ts.map +1 -0
  125. package/dist/frame-budget.js +114 -0
  126. package/dist/frame-budget.js.map +1 -0
  127. package/dist/gen-frame.d.ts +102 -0
  128. package/dist/gen-frame.d.ts.map +1 -0
  129. package/dist/gen-frame.js +121 -0
  130. package/dist/gen-frame.js.map +1 -0
  131. package/dist/harness/arbitrary-from-schema.d.ts +28 -0
  132. package/dist/harness/arbitrary-from-schema.d.ts.map +1 -0
  133. package/dist/harness/arbitrary-from-schema.js +262 -0
  134. package/dist/harness/arbitrary-from-schema.js.map +1 -0
  135. package/dist/harness/cached-projection.d.ts +19 -0
  136. package/dist/harness/cached-projection.d.ts.map +1 -0
  137. package/dist/harness/cached-projection.js +39 -0
  138. package/dist/harness/cached-projection.js.map +1 -0
  139. package/dist/harness/index.d.ts +16 -0
  140. package/dist/harness/index.d.ts.map +1 -0
  141. package/dist/harness/index.js +15 -0
  142. package/dist/harness/index.js.map +1 -0
  143. package/dist/harness/policy-gate.d.ts +18 -0
  144. package/dist/harness/policy-gate.d.ts.map +1 -0
  145. package/dist/harness/policy-gate.js +46 -0
  146. package/dist/harness/policy-gate.js.map +1 -0
  147. package/dist/harness/pure-transform.d.ts +42 -0
  148. package/dist/harness/pure-transform.d.ts.map +1 -0
  149. package/dist/harness/pure-transform.js +76 -0
  150. package/dist/harness/pure-transform.js.map +1 -0
  151. package/dist/harness/receipted-mutation.d.ts +23 -0
  152. package/dist/harness/receipted-mutation.d.ts.map +1 -0
  153. package/dist/harness/receipted-mutation.js +52 -0
  154. package/dist/harness/receipted-mutation.js.map +1 -0
  155. package/dist/harness/scene-composition.d.ts +19 -0
  156. package/dist/harness/scene-composition.d.ts.map +1 -0
  157. package/dist/harness/scene-composition.js +47 -0
  158. package/dist/harness/scene-composition.js.map +1 -0
  159. package/dist/harness/site-adapter.d.ts +18 -0
  160. package/dist/harness/site-adapter.d.ts.map +1 -0
  161. package/dist/harness/site-adapter.js +38 -0
  162. package/dist/harness/site-adapter.js.map +1 -0
  163. package/dist/harness/state-machine.d.ts +19 -0
  164. package/dist/harness/state-machine.d.ts.map +1 -0
  165. package/dist/harness/state-machine.js +44 -0
  166. package/dist/harness/state-machine.js.map +1 -0
  167. package/dist/hlc.d.ts +99 -0
  168. package/dist/hlc.d.ts.map +1 -0
  169. package/dist/hlc.js +219 -0
  170. package/dist/hlc.js.map +1 -0
  171. package/dist/index.d.ts +104 -0
  172. package/dist/index.d.ts.map +1 -0
  173. package/dist/index.js +137 -0
  174. package/dist/index.js.map +1 -0
  175. package/dist/interpolate.d.ts +14 -0
  176. package/dist/interpolate.d.ts.map +1 -0
  177. package/dist/interpolate.js +31 -0
  178. package/dist/interpolate.js.map +1 -0
  179. package/dist/live-cell.d.ts +46 -0
  180. package/dist/live-cell.d.ts.map +1 -0
  181. package/dist/live-cell.js +154 -0
  182. package/dist/live-cell.js.map +1 -0
  183. package/dist/op.d.ts +58 -0
  184. package/dist/op.d.ts.map +1 -0
  185. package/dist/op.js +171 -0
  186. package/dist/op.js.map +1 -0
  187. package/dist/plan.d.ts +195 -0
  188. package/dist/plan.d.ts.map +1 -0
  189. package/dist/plan.js +211 -0
  190. package/dist/plan.js.map +1 -0
  191. package/dist/protocol.d.ts +33 -0
  192. package/dist/protocol.d.ts.map +1 -0
  193. package/dist/protocol.js +10 -0
  194. package/dist/protocol.js.map +1 -0
  195. package/dist/quantizer-types.d.ts +28 -0
  196. package/dist/quantizer-types.d.ts.map +1 -0
  197. package/dist/quantizer-types.js +9 -0
  198. package/dist/quantizer-types.js.map +1 -0
  199. package/dist/receipt.d.ts +294 -0
  200. package/dist/receipt.d.ts.map +1 -0
  201. package/dist/receipt.js +352 -0
  202. package/dist/receipt.js.map +1 -0
  203. package/dist/runtime-coordinator.d.ts +75 -0
  204. package/dist/runtime-coordinator.d.ts.map +1 -0
  205. package/dist/runtime-coordinator.js +149 -0
  206. package/dist/runtime-coordinator.js.map +1 -0
  207. package/dist/scheduler.d.ts +58 -0
  208. package/dist/scheduler.d.ts.map +1 -0
  209. package/dist/scheduler.js +109 -0
  210. package/dist/scheduler.js.map +1 -0
  211. package/dist/ship-capsule.d.ts +54 -0
  212. package/dist/ship-capsule.d.ts.map +1 -0
  213. package/dist/ship-capsule.js +142 -0
  214. package/dist/ship-capsule.js.map +1 -0
  215. package/dist/ship-manifest.d.ts +45 -0
  216. package/dist/ship-manifest.d.ts.map +1 -0
  217. package/dist/ship-manifest.js +175 -0
  218. package/dist/ship-manifest.js.map +1 -0
  219. package/dist/signal.d.ts +149 -0
  220. package/dist/signal.d.ts.map +1 -0
  221. package/dist/signal.js +277 -0
  222. package/dist/signal.js.map +1 -0
  223. package/dist/speculative.d.ts +67 -0
  224. package/dist/speculative.d.ts.map +1 -0
  225. package/dist/speculative.js +139 -0
  226. package/dist/speculative.js.map +1 -0
  227. package/dist/store.d.ts +39 -0
  228. package/dist/store.d.ts.map +1 -0
  229. package/dist/store.js +42 -0
  230. package/dist/store.js.map +1 -0
  231. package/dist/style.d.ts +119 -0
  232. package/dist/style.d.ts.map +1 -0
  233. package/dist/style.js +168 -0
  234. package/dist/style.js.map +1 -0
  235. package/dist/testing.d.ts +14 -0
  236. package/dist/testing.d.ts.map +1 -0
  237. package/dist/testing.js +14 -0
  238. package/dist/testing.js.map +1 -0
  239. package/dist/theme.d.ts +78 -0
  240. package/dist/theme.d.ts.map +1 -0
  241. package/dist/theme.js +109 -0
  242. package/dist/theme.js.map +1 -0
  243. package/dist/timeline.d.ts +45 -0
  244. package/dist/timeline.d.ts.map +1 -0
  245. package/dist/timeline.js +101 -0
  246. package/dist/timeline.js.map +1 -0
  247. package/dist/token-buffer.d.ts +43 -0
  248. package/dist/token-buffer.d.ts.map +1 -0
  249. package/dist/token-buffer.js +112 -0
  250. package/dist/token-buffer.js.map +1 -0
  251. package/dist/token.d.ts +107 -0
  252. package/dist/token.d.ts.map +1 -0
  253. package/dist/token.js +143 -0
  254. package/dist/token.js.map +1 -0
  255. package/dist/tuple.d.ts +16 -0
  256. package/dist/tuple.d.ts.map +1 -0
  257. package/dist/tuple.js +16 -0
  258. package/dist/tuple.js.map +1 -0
  259. package/dist/type-utils.d.ts +41 -0
  260. package/dist/type-utils.d.ts.map +1 -0
  261. package/dist/type-utils.js +10 -0
  262. package/dist/type-utils.js.map +1 -0
  263. package/dist/typed-ref.d.ts +50 -0
  264. package/dist/typed-ref.d.ts.map +1 -0
  265. package/dist/typed-ref.js +59 -0
  266. package/dist/typed-ref.js.map +1 -0
  267. package/dist/ui-quality.d.ts +50 -0
  268. package/dist/ui-quality.d.ts.map +1 -0
  269. package/dist/ui-quality.js +64 -0
  270. package/dist/ui-quality.js.map +1 -0
  271. package/dist/validation-error.d.ts +25 -0
  272. package/dist/validation-error.d.ts.map +1 -0
  273. package/dist/validation-error.js +32 -0
  274. package/dist/validation-error.js.map +1 -0
  275. package/dist/vector-clock.d.ts +46 -0
  276. package/dist/vector-clock.d.ts.map +1 -0
  277. package/dist/vector-clock.js +91 -0
  278. package/dist/vector-clock.js.map +1 -0
  279. package/dist/video.d.ts +62 -0
  280. package/dist/video.d.ts.map +1 -0
  281. package/dist/video.js +59 -0
  282. package/dist/video.js.map +1 -0
  283. package/dist/wasm-dispatch.d.ts +52 -0
  284. package/dist/wasm-dispatch.d.ts.map +1 -0
  285. package/dist/wasm-dispatch.js +204 -0
  286. package/dist/wasm-dispatch.js.map +1 -0
  287. package/dist/wasm-fallback.d.ts +19 -0
  288. package/dist/wasm-fallback.d.ts.map +1 -0
  289. package/dist/wasm-fallback.js +93 -0
  290. package/dist/wasm-fallback.js.map +1 -0
  291. package/dist/wire.d.ts +49 -0
  292. package/dist/wire.d.ts.map +1 -0
  293. package/dist/wire.js +201 -0
  294. package/dist/wire.js.map +1 -0
  295. package/dist/zap.d.ts +42 -0
  296. package/dist/zap.d.ts.map +1 -0
  297. package/dist/zap.js +172 -0
  298. package/dist/zap.js.map +1 -0
  299. package/package.json +71 -0
  300. package/src/addressed-digest.ts +48 -0
  301. package/src/animation.ts +103 -0
  302. package/src/assembly.ts +76 -0
  303. package/src/av-bridge.ts +161 -0
  304. package/src/av-renderer.ts +118 -0
  305. package/src/blend.ts +135 -0
  306. package/src/boundary.ts +363 -0
  307. package/src/brands.ts +86 -0
  308. package/src/caps.ts +100 -0
  309. package/src/capsule.ts +95 -0
  310. package/src/capsules/boundary-evaluate.ts +128 -0
  311. package/src/capsules/canonical-cbor.ts +60 -0
  312. package/src/capsules/token-buffer.ts +57 -0
  313. package/src/capture.ts +48 -0
  314. package/src/cbor.ts +199 -0
  315. package/src/cell.ts +130 -0
  316. package/src/codec.ts +39 -0
  317. package/src/component.ts +102 -0
  318. package/src/composable.ts +328 -0
  319. package/src/compositor-pool.ts +162 -0
  320. package/src/compositor.ts +387 -0
  321. package/src/config.ts +157 -0
  322. package/src/dag.ts +527 -0
  323. package/src/defaults.ts +60 -0
  324. package/src/derived.ts +164 -0
  325. package/src/diagnostics.ts +186 -0
  326. package/src/dirty.ts +101 -0
  327. package/src/easing.ts +334 -0
  328. package/src/ecs.ts +382 -0
  329. package/src/fnv.ts +31 -0
  330. package/src/frame-budget.ts +149 -0
  331. package/src/gen-frame.ts +229 -0
  332. package/src/harness/arbitrary-from-schema.ts +270 -0
  333. package/src/harness/cached-projection.ts +46 -0
  334. package/src/harness/index.ts +16 -0
  335. package/src/harness/policy-gate.ts +51 -0
  336. package/src/harness/pure-transform.ts +121 -0
  337. package/src/harness/receipted-mutation.ts +59 -0
  338. package/src/harness/scene-composition.ts +54 -0
  339. package/src/harness/site-adapter.ts +43 -0
  340. package/src/harness/state-machine.ts +49 -0
  341. package/src/hlc.ts +238 -0
  342. package/src/index.ts +274 -0
  343. package/src/interpolate.ts +37 -0
  344. package/src/live-cell.ts +199 -0
  345. package/src/op.ts +233 -0
  346. package/src/plan.ts +317 -0
  347. package/src/protocol.ts +49 -0
  348. package/src/quantizer-types.ts +29 -0
  349. package/src/receipt.ts +444 -0
  350. package/src/runtime-coordinator.ts +230 -0
  351. package/src/scheduler.ts +161 -0
  352. package/src/ship-capsule.ts +191 -0
  353. package/src/signal.ts +345 -0
  354. package/src/speculative.ts +186 -0
  355. package/src/store.ts +77 -0
  356. package/src/style.ts +249 -0
  357. package/src/testing.ts +14 -0
  358. package/src/theme.ts +153 -0
  359. package/src/timeline.ts +146 -0
  360. package/src/token-buffer.ts +151 -0
  361. package/src/token.ts +197 -0
  362. package/src/tuple.ts +19 -0
  363. package/src/type-utils.ts +48 -0
  364. package/src/typed-ref.ts +79 -0
  365. package/src/ui-quality.ts +105 -0
  366. package/src/validation-error.ts +34 -0
  367. package/src/vector-clock.ts +111 -0
  368. package/src/video.ts +106 -0
  369. package/src/wasm-dispatch.ts +300 -0
  370. package/src/wasm-fallback.ts +102 -0
  371. package/src/wire.ts +274 -0
  372. package/src/zap.ts +241 -0
package/dist/dag.d.ts ADDED
@@ -0,0 +1,251 @@
1
+ /**
2
+ * DAG -- receipt DAG merge and canonical linearization.
3
+ *
4
+ * Salvaged from `@kit/core`.
5
+ *
6
+ * @module
7
+ */
8
+ import type { ReceiptEnvelope } from './receipt.js';
9
+ /** Single vertex in a {@link ReceiptDAG}: an envelope plus its parent and child hashes. */
10
+ export interface DAGNode {
11
+ readonly envelope: ReceiptEnvelope;
12
+ readonly parents: ReadonlyArray<string>;
13
+ readonly children: ReadonlyArray<string>;
14
+ }
15
+ /**
16
+ * Immutable snapshot of the receipt DAG: the set of known nodes, the current
17
+ * head(s), and the genesis anchor if any.
18
+ */
19
+ export interface ReceiptDAG {
20
+ readonly nodes: ReadonlyMap<string, DAGNode>;
21
+ readonly heads: ReadonlyArray<string>;
22
+ readonly genesis: string | null;
23
+ }
24
+ /** Result of a DAG merge: the updated graph, the hashes that were newly added, and whether a fork was observed. */
25
+ export interface MergeResult {
26
+ readonly dag: ReceiptDAG;
27
+ readonly added: ReadonlyArray<string>;
28
+ readonly forked: boolean;
29
+ }
30
+ /** Detail record describing a single-writer fork-rule violation. */
31
+ export interface ForkViolation {
32
+ readonly actor: string;
33
+ readonly prevHash: string;
34
+ readonly existing: string;
35
+ readonly attempted: string;
36
+ }
37
+ /**
38
+ * Create an empty receipt DAG with no nodes or heads.
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * const dag = DAG.empty();
43
+ * // dag.nodes.size === 0
44
+ * // dag.heads.length === 0
45
+ * ```
46
+ */
47
+ export declare const empty: () => ReceiptDAG;
48
+ /**
49
+ * Ingest a single receipt envelope into the DAG.
50
+ *
51
+ * Adds the envelope as a node, wires parent/child edges, and recalculates
52
+ * head nodes. Idempotent -- returns the same DAG if the hash already exists.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * let dag = DAG.empty();
57
+ * dag = DAG.ingest(dag, envelope);
58
+ * // dag.nodes.size === 1
59
+ * ```
60
+ */
61
+ export declare const ingest: (dag: ReceiptDAG, envelope: ReceiptEnvelope) => ReceiptDAG;
62
+ /**
63
+ * Ingest multiple receipt envelopes into the DAG in order.
64
+ *
65
+ * @example
66
+ * ```ts
67
+ * const dag = DAG.ingestAll(DAG.empty(), [envelope1, envelope2]);
68
+ * // dag.nodes.size === 2
69
+ * ```
70
+ */
71
+ export declare const ingestAll: (dag: ReceiptDAG, envelopes: ReadonlyArray<ReceiptEnvelope>) => ReceiptDAG;
72
+ /**
73
+ * Build a DAG from an array of receipt envelopes.
74
+ *
75
+ * @example
76
+ * ```ts
77
+ * const dag = DAG.fromReceipts(envelopes);
78
+ * // dag.nodes.size === envelopes.length
79
+ * ```
80
+ */
81
+ export declare const fromReceipts: (envelopes: ReadonlyArray<ReceiptEnvelope>) => ReceiptDAG;
82
+ /**
83
+ * Check whether ingesting an envelope would violate the anti-fork rule.
84
+ *
85
+ * The anti-fork rule prevents a single actor from creating two children
86
+ * of the same parent node. Returns a ForkViolation descriptor or null.
87
+ *
88
+ * @example
89
+ * ```ts
90
+ * const violation = DAG.checkForkRule(dag, envelope);
91
+ * if (violation) {
92
+ * console.error(`Fork by actor ${violation.actor}`);
93
+ * }
94
+ * ```
95
+ */
96
+ export declare const checkForkRule: (dag: ReceiptDAG, envelope: ReceiptEnvelope) => ForkViolation | null;
97
+ /**
98
+ * Produce a deterministic topological ordering of all envelopes in the DAG.
99
+ *
100
+ * Kahn's algorithm with stable ordering: sortedInsert maintains tiebreak order in the
101
+ * ready queue, guaranteeing deterministic topological sort across replicas.
102
+ *
103
+ * @example
104
+ * ```ts
105
+ * const dag = DAG.fromReceipts(envelopes);
106
+ * const ordered = DAG.linearize(dag);
107
+ * // ordered is a deterministic total order of all envelopes
108
+ * ```
109
+ */
110
+ export declare const linearize: (dag: ReceiptDAG) => ReadonlyArray<ReceiptEnvelope>;
111
+ /**
112
+ * Linearize the DAG and return only envelopes after a given hash.
113
+ *
114
+ * @example
115
+ * ```ts
116
+ * const newEntries = DAG.linearizeFrom(dag, lastSeenHash);
117
+ * // newEntries contains only envelopes after lastSeenHash
118
+ * ```
119
+ */
120
+ export declare const linearizeFrom: (dag: ReceiptDAG, afterHash: string) => ReadonlyArray<ReceiptEnvelope>;
121
+ /**
122
+ * Get all head (childless) envelopes in the DAG.
123
+ *
124
+ * @example
125
+ * ```ts
126
+ * const heads = DAG.getHeads(dag);
127
+ * // heads.length > 0 for non-empty DAGs
128
+ * ```
129
+ */
130
+ export declare const getHeads: (dag: ReceiptDAG) => ReadonlyArray<ReceiptEnvelope>;
131
+ /**
132
+ * Get the single canonical head of the DAG via deterministic tiebreaking.
133
+ *
134
+ * @example
135
+ * ```ts
136
+ * const head = DAG.canonicalHead(dag);
137
+ * // head is the deterministically chosen head envelope, or null if empty
138
+ * ```
139
+ */
140
+ export declare const canonicalHead: (dag: ReceiptDAG) => ReceiptEnvelope | null;
141
+ /**
142
+ * Check whether the DAG has multiple heads (i.e., is in a forked state).
143
+ *
144
+ * @example
145
+ * ```ts
146
+ * if (DAG.isFork(dag)) {
147
+ * console.log('DAG has diverged, needs merge');
148
+ * }
149
+ * ```
150
+ */
151
+ export declare const isFork: (dag: ReceiptDAG) => boolean;
152
+ /**
153
+ * Get all ancestor hashes of a given node (transitive parents).
154
+ *
155
+ * @example
156
+ * ```ts
157
+ * const anc = DAG.ancestors(dag, headHash);
158
+ * // anc contains all hashes reachable by following parent edges
159
+ * ```
160
+ */
161
+ export declare const ancestors: (dag: ReceiptDAG, hash: string) => ReadonlyArray<string>;
162
+ /**
163
+ * Check whether node `a` is an ancestor of node `b` in the DAG.
164
+ *
165
+ * @example
166
+ * ```ts
167
+ * const yes = DAG.isAncestor(dag, genesisHash, headHash);
168
+ * // yes === true (genesis is ancestor of everything)
169
+ * ```
170
+ */
171
+ export declare const isAncestor: (dag: ReceiptDAG, a: string, b: string) => boolean;
172
+ /**
173
+ * Find the latest common ancestor of two nodes in the DAG.
174
+ *
175
+ * @example
176
+ * ```ts
177
+ * const lca = DAG.commonAncestor(dag, hashA, hashB);
178
+ * // lca is the hash of the most recent shared ancestor, or null
179
+ * ```
180
+ */
181
+ export declare const commonAncestor: (dag: ReceiptDAG, a: string, b: string) => string | null;
182
+ /**
183
+ * Return the number of nodes in the DAG.
184
+ *
185
+ * @example
186
+ * ```ts
187
+ * const n = DAG.size(dag);
188
+ * // n === dag.nodes.size
189
+ * ```
190
+ */
191
+ export declare const size: (dag: ReceiptDAG) => number;
192
+ /**
193
+ * Merge remote envelopes into a local DAG, enforcing the anti-fork rule.
194
+ *
195
+ * Returns the updated DAG, list of newly added hashes, and whether the
196
+ * result is forked. Throws on anti-fork violations.
197
+ *
198
+ * @example
199
+ * ```ts
200
+ * const result = DAG.merge(localDag, remoteEnvelopes);
201
+ * // result.dag -- updated DAG
202
+ * // result.added -- newly ingested hashes
203
+ * // result.forked -- true if DAG has multiple heads
204
+ * ```
205
+ */
206
+ export declare const merge: (local: ReceiptDAG, remote: ReadonlyArray<ReceiptEnvelope>) => MergeResult;
207
+ /**
208
+ * DAG namespace -- receipt DAG merge and canonical linearization.
209
+ *
210
+ * Build, query, and merge directed acyclic graphs of receipt envelopes.
211
+ * Supports deterministic linearization, fork detection, ancestor queries,
212
+ * and anti-fork rule enforcement.
213
+ *
214
+ * @example
215
+ * ```ts
216
+ * import { DAG } from '@czap/core';
217
+ *
218
+ * const dag = DAG.fromReceipts(envelopes);
219
+ * const ordered = DAG.linearize(dag);
220
+ * const forked = DAG.isFork(dag);
221
+ * const result = DAG.merge(dag, remoteEnvelopes);
222
+ * ```
223
+ */
224
+ export declare const DAG: {
225
+ empty: () => ReceiptDAG;
226
+ ingest: (dag: ReceiptDAG, envelope: ReceiptEnvelope) => ReceiptDAG;
227
+ ingestAll: (dag: ReceiptDAG, envelopes: ReadonlyArray<ReceiptEnvelope>) => ReceiptDAG;
228
+ fromReceipts: (envelopes: ReadonlyArray<ReceiptEnvelope>) => ReceiptDAG;
229
+ checkForkRule: (dag: ReceiptDAG, envelope: ReceiptEnvelope) => ForkViolation | null;
230
+ linearize: (dag: ReceiptDAG) => ReadonlyArray<ReceiptEnvelope>;
231
+ linearizeFrom: (dag: ReceiptDAG, afterHash: string) => ReadonlyArray<ReceiptEnvelope>;
232
+ getHeads: (dag: ReceiptDAG) => ReadonlyArray<ReceiptEnvelope>;
233
+ canonicalHead: (dag: ReceiptDAG) => ReceiptEnvelope | null;
234
+ isFork: (dag: ReceiptDAG) => boolean;
235
+ ancestors: (dag: ReceiptDAG, hash: string) => ReadonlyArray<string>;
236
+ isAncestor: (dag: ReceiptDAG, a: string, b: string) => boolean;
237
+ commonAncestor: (dag: ReceiptDAG, a: string, b: string) => string | null;
238
+ size: (dag: ReceiptDAG) => number;
239
+ merge: (local: ReceiptDAG, remote: ReadonlyArray<ReceiptEnvelope>) => MergeResult;
240
+ };
241
+ export declare namespace DAG {
242
+ /** Alias for {@link DAGNode}. */
243
+ type Node = DAGNode;
244
+ /** Alias for {@link ReceiptDAG}. */
245
+ type Graph = ReceiptDAG;
246
+ /** Alias for {@link MergeResult}. */
247
+ type Merge = MergeResult;
248
+ /** Alias for {@link ForkViolation}. */
249
+ type Fork = ForkViolation;
250
+ }
251
+ //# sourceMappingURL=dag.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dag.d.ts","sourceRoot":"","sources":["../src/dag.ts"],"names":[],"mappings":"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"}
package/dist/dag.js ADDED
@@ -0,0 +1,450 @@
1
+ /**
2
+ * DAG -- receipt DAG merge and canonical linearization.
3
+ *
4
+ * Salvaged from `@kit/core`.
5
+ *
6
+ * @module
7
+ */
8
+ import { compare as hlcCompare } from './hlc.js';
9
+ import { GENESIS } from './receipt.js';
10
+ const parentsOf = (envelope) => {
11
+ if (Array.isArray(envelope.previous)) {
12
+ return envelope.previous.filter((p) => p !== GENESIS);
13
+ }
14
+ return envelope.previous === GENESIS ? [] : [envelope.previous];
15
+ };
16
+ const actorOf = (envelope) => envelope.subject.id;
17
+ /**
18
+ * Create an empty receipt DAG with no nodes or heads.
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * const dag = DAG.empty();
23
+ * // dag.nodes.size === 0
24
+ * // dag.heads.length === 0
25
+ * ```
26
+ */
27
+ export const empty = () => ({
28
+ nodes: new Map(),
29
+ heads: [],
30
+ genesis: null,
31
+ });
32
+ /**
33
+ * Ingest a single receipt envelope into the DAG.
34
+ *
35
+ * Adds the envelope as a node, wires parent/child edges, and recalculates
36
+ * head nodes. Idempotent -- returns the same DAG if the hash already exists.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * let dag = DAG.empty();
41
+ * dag = DAG.ingest(dag, envelope);
42
+ * // dag.nodes.size === 1
43
+ * ```
44
+ */
45
+ export const ingest = (dag, envelope) => {
46
+ const hash = envelope.hash;
47
+ if (dag.nodes.has(hash))
48
+ return dag;
49
+ const parents = parentsOf(envelope);
50
+ const newNode = { envelope, parents, children: [] };
51
+ const newNodes = new Map(dag.nodes);
52
+ newNodes.set(hash, newNode);
53
+ for (const parentHash of parents) {
54
+ const parentNode = newNodes.get(parentHash);
55
+ if (parentNode) {
56
+ newNodes.set(parentHash, { ...parentNode, children: [...parentNode.children, hash] });
57
+ }
58
+ }
59
+ for (const [existingHash, existingNode] of dag.nodes) {
60
+ if (existingNode.parents.includes(hash)) {
61
+ const updatedNewNode = newNodes.get(hash);
62
+ newNodes.set(hash, { ...updatedNewNode, children: [...updatedNewNode.children, existingHash] });
63
+ }
64
+ }
65
+ const heads = [];
66
+ for (const [h, node] of newNodes) {
67
+ if (node.children.length === 0)
68
+ heads.push(h);
69
+ }
70
+ const isGenesisNode = envelope.previous === GENESIS ||
71
+ (Array.isArray(envelope.previous) && envelope.previous.includes(GENESIS));
72
+ const genesis = isGenesisNode ? hash : dag.genesis;
73
+ return { nodes: newNodes, heads, genesis };
74
+ };
75
+ /**
76
+ * Ingest multiple receipt envelopes into the DAG in order.
77
+ *
78
+ * @example
79
+ * ```ts
80
+ * const dag = DAG.ingestAll(DAG.empty(), [envelope1, envelope2]);
81
+ * // dag.nodes.size === 2
82
+ * ```
83
+ */
84
+ export const ingestAll = (dag, envelopes) => envelopes.reduce((d, e) => ingest(d, e), dag);
85
+ /**
86
+ * Build a DAG from an array of receipt envelopes.
87
+ *
88
+ * @example
89
+ * ```ts
90
+ * const dag = DAG.fromReceipts(envelopes);
91
+ * // dag.nodes.size === envelopes.length
92
+ * ```
93
+ */
94
+ export const fromReceipts = (envelopes) => ingestAll(empty(), envelopes);
95
+ /**
96
+ * Check whether ingesting an envelope would violate the anti-fork rule.
97
+ *
98
+ * The anti-fork rule prevents a single actor from creating two children
99
+ * of the same parent node. Returns a ForkViolation descriptor or null.
100
+ *
101
+ * @example
102
+ * ```ts
103
+ * const violation = DAG.checkForkRule(dag, envelope);
104
+ * if (violation) {
105
+ * console.error(`Fork by actor ${violation.actor}`);
106
+ * }
107
+ * ```
108
+ */
109
+ export const checkForkRule = (dag, envelope) => {
110
+ if (Array.isArray(envelope.previous))
111
+ return null;
112
+ const prevHash = envelope.previous;
113
+ const actor = actorOf(envelope);
114
+ const attemptedHash = envelope.hash;
115
+ if (prevHash === GENESIS)
116
+ return null;
117
+ const parentNode = dag.nodes.get(prevHash);
118
+ if (!parentNode)
119
+ return null;
120
+ for (const childHash of parentNode.children) {
121
+ const childNode = dag.nodes.get(childHash);
122
+ if (childNode && actorOf(childNode.envelope) === actor && childHash !== attemptedHash) {
123
+ return { actor, prevHash, existing: childHash, attempted: attemptedHash };
124
+ }
125
+ }
126
+ return null;
127
+ };
128
+ // Total ordering: HLC first (causal), then actor ID (deterministic across nodes),
129
+ // then hash (content-based last resort). Ensures identical linearization on every replica.
130
+ const tiebreak = (a, b) => {
131
+ const hlcCmp = hlcCompare(a.timestamp, b.timestamp);
132
+ if (hlcCmp !== 0)
133
+ return hlcCmp;
134
+ const actorA = actorOf(a);
135
+ const actorB = actorOf(b);
136
+ if (actorA < actorB)
137
+ return -1;
138
+ if (actorA > actorB)
139
+ return 1;
140
+ return +(a.hash > b.hash) - +(a.hash < b.hash);
141
+ };
142
+ const sortedInsert = (arr, item, cmp) => {
143
+ let lo = 0;
144
+ let hi = arr.length;
145
+ while (lo < hi) {
146
+ const mid = (lo + hi) >>> 1;
147
+ if (cmp(arr[mid], item) <= 0)
148
+ lo = mid + 1;
149
+ else
150
+ hi = mid;
151
+ }
152
+ arr.splice(lo, 0, item);
153
+ };
154
+ /**
155
+ * Produce a deterministic topological ordering of all envelopes in the DAG.
156
+ *
157
+ * Kahn's algorithm with stable ordering: sortedInsert maintains tiebreak order in the
158
+ * ready queue, guaranteeing deterministic topological sort across replicas.
159
+ *
160
+ * @example
161
+ * ```ts
162
+ * const dag = DAG.fromReceipts(envelopes);
163
+ * const ordered = DAG.linearize(dag);
164
+ * // ordered is a deterministic total order of all envelopes
165
+ * ```
166
+ */
167
+ export const linearize = (dag) => {
168
+ if (dag.nodes.size === 0)
169
+ return [];
170
+ const inDegree = new Map();
171
+ for (const [hash, node] of dag.nodes) {
172
+ let degree = 0;
173
+ for (const parentHash of node.parents) {
174
+ if (dag.nodes.has(parentHash))
175
+ degree++;
176
+ }
177
+ inDegree.set(hash, degree);
178
+ }
179
+ const ready = [];
180
+ for (const [hash, degree] of inDegree) {
181
+ if (degree === 0) {
182
+ const node = dag.nodes.get(hash);
183
+ sortedInsert(ready, node.envelope, tiebreak);
184
+ }
185
+ }
186
+ const result = [];
187
+ while (ready.length > 0) {
188
+ const envelope = ready.shift();
189
+ result.push(envelope);
190
+ const node = dag.nodes.get(envelope.hash);
191
+ for (const childHash of node.children) {
192
+ const childDegree = inDegree.get(childHash);
193
+ if (childDegree !== undefined) {
194
+ const newDegree = childDegree - 1;
195
+ inDegree.set(childHash, newDegree);
196
+ if (newDegree === 0) {
197
+ const childNode = dag.nodes.get(childHash);
198
+ sortedInsert(ready, childNode.envelope, tiebreak);
199
+ }
200
+ }
201
+ }
202
+ }
203
+ return result;
204
+ };
205
+ /**
206
+ * Linearize the DAG and return only envelopes after a given hash.
207
+ *
208
+ * @example
209
+ * ```ts
210
+ * const newEntries = DAG.linearizeFrom(dag, lastSeenHash);
211
+ * // newEntries contains only envelopes after lastSeenHash
212
+ * ```
213
+ */
214
+ export const linearizeFrom = (dag, afterHash) => {
215
+ const full = linearize(dag);
216
+ const idx = full.findIndex((e) => e.hash === afterHash);
217
+ if (idx === -1)
218
+ return full;
219
+ return full.slice(idx + 1);
220
+ };
221
+ /**
222
+ * Get all head (childless) envelopes in the DAG.
223
+ *
224
+ * @example
225
+ * ```ts
226
+ * const heads = DAG.getHeads(dag);
227
+ * // heads.length > 0 for non-empty DAGs
228
+ * ```
229
+ */
230
+ export const getHeads = (dag) => {
231
+ const result = [];
232
+ for (const hash of dag.heads) {
233
+ const node = dag.nodes.get(hash);
234
+ if (node)
235
+ result.push(node.envelope);
236
+ }
237
+ return result;
238
+ };
239
+ /**
240
+ * Get the single canonical head of the DAG via deterministic tiebreaking.
241
+ *
242
+ * @example
243
+ * ```ts
244
+ * const head = DAG.canonicalHead(dag);
245
+ * // head is the deterministically chosen head envelope, or null if empty
246
+ * ```
247
+ */
248
+ export const canonicalHead = (dag) => {
249
+ const heads = getHeads(dag);
250
+ if (heads.length === 0)
251
+ return null;
252
+ if (heads.length === 1)
253
+ return heads[0];
254
+ const sorted = [...heads].sort(tiebreak);
255
+ return sorted[0];
256
+ };
257
+ /**
258
+ * Check whether the DAG has multiple heads (i.e., is in a forked state).
259
+ *
260
+ * @example
261
+ * ```ts
262
+ * if (DAG.isFork(dag)) {
263
+ * console.log('DAG has diverged, needs merge');
264
+ * }
265
+ * ```
266
+ */
267
+ export const isFork = (dag) => dag.heads.length > 1;
268
+ /**
269
+ * Get all ancestor hashes of a given node (transitive parents).
270
+ *
271
+ * @example
272
+ * ```ts
273
+ * const anc = DAG.ancestors(dag, headHash);
274
+ * // anc contains all hashes reachable by following parent edges
275
+ * ```
276
+ */
277
+ export const ancestors = (dag, hash) => {
278
+ const visited = new Set();
279
+ const stack = [];
280
+ const node = dag.nodes.get(hash);
281
+ if (!node)
282
+ return [];
283
+ for (const parentHash of node.parents) {
284
+ if (dag.nodes.has(parentHash))
285
+ stack.push(parentHash);
286
+ }
287
+ while (stack.length > 0) {
288
+ const current = stack.pop();
289
+ if (visited.has(current))
290
+ continue;
291
+ visited.add(current);
292
+ const currentNode = dag.nodes.get(current);
293
+ for (const parentHash of currentNode.parents) {
294
+ if (dag.nodes.has(parentHash) && !visited.has(parentHash))
295
+ stack.push(parentHash);
296
+ }
297
+ }
298
+ return Array.from(visited);
299
+ };
300
+ /**
301
+ * Check whether node `a` is an ancestor of node `b` in the DAG.
302
+ *
303
+ * @example
304
+ * ```ts
305
+ * const yes = DAG.isAncestor(dag, genesisHash, headHash);
306
+ * // yes === true (genesis is ancestor of everything)
307
+ * ```
308
+ */
309
+ export const isAncestor = (dag, a, b) => {
310
+ if (a === b)
311
+ return false;
312
+ const visited = new Set();
313
+ const queue = [];
314
+ const nodeB = dag.nodes.get(b);
315
+ if (!nodeB)
316
+ return false;
317
+ for (const parentHash of nodeB.parents) {
318
+ if (dag.nodes.has(parentHash))
319
+ queue.push(parentHash);
320
+ }
321
+ while (queue.length > 0) {
322
+ const current = queue.shift();
323
+ if (current === a)
324
+ return true;
325
+ if (visited.has(current))
326
+ continue;
327
+ visited.add(current);
328
+ const currentNode = dag.nodes.get(current);
329
+ for (const parentHash of currentNode.parents) {
330
+ if (dag.nodes.has(parentHash) && !visited.has(parentHash))
331
+ queue.push(parentHash);
332
+ }
333
+ }
334
+ return false;
335
+ };
336
+ /**
337
+ * Find the latest common ancestor of two nodes in the DAG.
338
+ *
339
+ * @example
340
+ * ```ts
341
+ * const lca = DAG.commonAncestor(dag, hashA, hashB);
342
+ * // lca is the hash of the most recent shared ancestor, or null
343
+ * ```
344
+ */
345
+ export const commonAncestor = (dag, a, b) => {
346
+ if (a === b)
347
+ return a;
348
+ const ancestorsOfA = new Set(ancestors(dag, a));
349
+ ancestorsOfA.add(a);
350
+ const ancestorsOfB = new Set(ancestors(dag, b));
351
+ ancestorsOfB.add(b);
352
+ const common = [];
353
+ for (const hash of ancestorsOfA) {
354
+ if (ancestorsOfB.has(hash))
355
+ common.push(hash);
356
+ }
357
+ if (common.length === 0)
358
+ return null;
359
+ const linearized = linearize(dag);
360
+ const linearOrder = new Map();
361
+ for (let i = 0; i < linearized.length; i++) {
362
+ linearOrder.set(linearized[i].hash, i);
363
+ }
364
+ let bestHash = null;
365
+ let bestOrder = -1;
366
+ for (const hash of common) {
367
+ const order = linearOrder.get(hash);
368
+ if (order !== undefined && order > bestOrder) {
369
+ bestOrder = order;
370
+ bestHash = hash;
371
+ }
372
+ }
373
+ return bestHash;
374
+ };
375
+ /**
376
+ * Return the number of nodes in the DAG.
377
+ *
378
+ * @example
379
+ * ```ts
380
+ * const n = DAG.size(dag);
381
+ * // n === dag.nodes.size
382
+ * ```
383
+ */
384
+ export const size = (dag) => dag.nodes.size;
385
+ /**
386
+ * Merge remote envelopes into a local DAG, enforcing the anti-fork rule.
387
+ *
388
+ * Returns the updated DAG, list of newly added hashes, and whether the
389
+ * result is forked. Throws on anti-fork violations.
390
+ *
391
+ * @example
392
+ * ```ts
393
+ * const result = DAG.merge(localDag, remoteEnvelopes);
394
+ * // result.dag -- updated DAG
395
+ * // result.added -- newly ingested hashes
396
+ * // result.forked -- true if DAG has multiple heads
397
+ * ```
398
+ */
399
+ export const merge = (local, remote) => {
400
+ const added = [];
401
+ let current = local;
402
+ for (const envelope of remote) {
403
+ if (current.nodes.has(envelope.hash))
404
+ continue;
405
+ const violation = checkForkRule(current, envelope);
406
+ if (violation !== null) {
407
+ throw new Error(`Anti-fork violation: actor "${violation.actor}" attempted to fork from ` +
408
+ `prev-hash "${violation.prevHash}". Existing child: "${violation.existing}", ` +
409
+ `attempted: "${violation.attempted}" (each actor must have a single causal chain — use merge receipts to join branches).`);
410
+ }
411
+ current = ingest(current, envelope);
412
+ added.push(envelope.hash);
413
+ }
414
+ return { dag: current, added, forked: isFork(current) };
415
+ };
416
+ /**
417
+ * DAG namespace -- receipt DAG merge and canonical linearization.
418
+ *
419
+ * Build, query, and merge directed acyclic graphs of receipt envelopes.
420
+ * Supports deterministic linearization, fork detection, ancestor queries,
421
+ * and anti-fork rule enforcement.
422
+ *
423
+ * @example
424
+ * ```ts
425
+ * import { DAG } from '@czap/core';
426
+ *
427
+ * const dag = DAG.fromReceipts(envelopes);
428
+ * const ordered = DAG.linearize(dag);
429
+ * const forked = DAG.isFork(dag);
430
+ * const result = DAG.merge(dag, remoteEnvelopes);
431
+ * ```
432
+ */
433
+ export const DAG = {
434
+ empty,
435
+ ingest,
436
+ ingestAll,
437
+ fromReceipts,
438
+ checkForkRule,
439
+ linearize,
440
+ linearizeFrom,
441
+ getHeads,
442
+ canonicalHead,
443
+ isFork,
444
+ ancestors,
445
+ isAncestor,
446
+ commonAncestor,
447
+ size,
448
+ merge,
449
+ };
450
+ //# sourceMappingURL=dag.js.map