@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/src/dag.ts ADDED
@@ -0,0 +1,527 @@
1
+ /**
2
+ * DAG -- receipt DAG merge and canonical linearization.
3
+ *
4
+ * Salvaged from `@kit/core`.
5
+ *
6
+ * @module
7
+ */
8
+
9
+ import { compare as hlcCompare } from './hlc.js';
10
+ import type { ReceiptEnvelope } from './receipt.js';
11
+ import { GENESIS } from './receipt.js';
12
+
13
+ /** Single vertex in a {@link ReceiptDAG}: an envelope plus its parent and child hashes. */
14
+ export interface DAGNode {
15
+ readonly envelope: ReceiptEnvelope;
16
+ readonly parents: ReadonlyArray<string>;
17
+ readonly children: ReadonlyArray<string>;
18
+ }
19
+
20
+ /**
21
+ * Immutable snapshot of the receipt DAG: the set of known nodes, the current
22
+ * head(s), and the genesis anchor if any.
23
+ */
24
+ export interface ReceiptDAG {
25
+ readonly nodes: ReadonlyMap<string, DAGNode>;
26
+ readonly heads: ReadonlyArray<string>;
27
+ readonly genesis: string | null;
28
+ }
29
+
30
+ /** Result of a DAG merge: the updated graph, the hashes that were newly added, and whether a fork was observed. */
31
+ export interface MergeResult {
32
+ readonly dag: ReceiptDAG;
33
+ readonly added: ReadonlyArray<string>;
34
+ readonly forked: boolean;
35
+ }
36
+
37
+ /** Detail record describing a single-writer fork-rule violation. */
38
+ export interface ForkViolation {
39
+ readonly actor: string;
40
+ readonly prevHash: string;
41
+ readonly existing: string;
42
+ readonly attempted: string;
43
+ }
44
+
45
+ const parentsOf = (envelope: ReceiptEnvelope): ReadonlyArray<string> => {
46
+ if (Array.isArray(envelope.previous)) {
47
+ return (envelope.previous as readonly string[]).filter((p) => p !== GENESIS);
48
+ }
49
+ return envelope.previous === GENESIS ? [] : [envelope.previous as string];
50
+ };
51
+
52
+ const actorOf = (envelope: ReceiptEnvelope): string => envelope.subject.id;
53
+
54
+ /**
55
+ * Create an empty receipt DAG with no nodes or heads.
56
+ *
57
+ * @example
58
+ * ```ts
59
+ * const dag = DAG.empty();
60
+ * // dag.nodes.size === 0
61
+ * // dag.heads.length === 0
62
+ * ```
63
+ */
64
+ export const empty = (): ReceiptDAG => ({
65
+ nodes: new Map(),
66
+ heads: [],
67
+ genesis: null,
68
+ });
69
+
70
+ /**
71
+ * Ingest a single receipt envelope into the DAG.
72
+ *
73
+ * Adds the envelope as a node, wires parent/child edges, and recalculates
74
+ * head nodes. Idempotent -- returns the same DAG if the hash already exists.
75
+ *
76
+ * @example
77
+ * ```ts
78
+ * let dag = DAG.empty();
79
+ * dag = DAG.ingest(dag, envelope);
80
+ * // dag.nodes.size === 1
81
+ * ```
82
+ */
83
+ export const ingest = (dag: ReceiptDAG, envelope: ReceiptEnvelope): ReceiptDAG => {
84
+ const hash = envelope.hash;
85
+ if (dag.nodes.has(hash)) return dag;
86
+
87
+ const parents = parentsOf(envelope);
88
+ const newNode: DAGNode = { envelope, parents, children: [] };
89
+ const newNodes = new Map(dag.nodes);
90
+ newNodes.set(hash, newNode);
91
+
92
+ for (const parentHash of parents) {
93
+ const parentNode = newNodes.get(parentHash);
94
+ if (parentNode) {
95
+ newNodes.set(parentHash, { ...parentNode, children: [...parentNode.children, hash] });
96
+ }
97
+ }
98
+
99
+ for (const [existingHash, existingNode] of dag.nodes) {
100
+ if (existingNode.parents.includes(hash)) {
101
+ const updatedNewNode = newNodes.get(hash)!;
102
+ newNodes.set(hash, { ...updatedNewNode, children: [...updatedNewNode.children, existingHash] });
103
+ }
104
+ }
105
+
106
+ const heads: string[] = [];
107
+ for (const [h, node] of newNodes) {
108
+ if (node.children.length === 0) heads.push(h);
109
+ }
110
+
111
+ const isGenesisNode =
112
+ envelope.previous === GENESIS ||
113
+ (Array.isArray(envelope.previous) && (envelope.previous as readonly string[]).includes(GENESIS));
114
+ const genesis = isGenesisNode ? hash : dag.genesis;
115
+
116
+ return { nodes: newNodes, heads, genesis };
117
+ };
118
+
119
+ /**
120
+ * Ingest multiple receipt envelopes into the DAG in order.
121
+ *
122
+ * @example
123
+ * ```ts
124
+ * const dag = DAG.ingestAll(DAG.empty(), [envelope1, envelope2]);
125
+ * // dag.nodes.size === 2
126
+ * ```
127
+ */
128
+ export const ingestAll = (dag: ReceiptDAG, envelopes: ReadonlyArray<ReceiptEnvelope>): ReceiptDAG =>
129
+ envelopes.reduce((d, e) => ingest(d, e), dag);
130
+
131
+ /**
132
+ * Build a DAG from an array of receipt envelopes.
133
+ *
134
+ * @example
135
+ * ```ts
136
+ * const dag = DAG.fromReceipts(envelopes);
137
+ * // dag.nodes.size === envelopes.length
138
+ * ```
139
+ */
140
+ export const fromReceipts = (envelopes: ReadonlyArray<ReceiptEnvelope>): ReceiptDAG => ingestAll(empty(), envelopes);
141
+
142
+ /**
143
+ * Check whether ingesting an envelope would violate the anti-fork rule.
144
+ *
145
+ * The anti-fork rule prevents a single actor from creating two children
146
+ * of the same parent node. Returns a ForkViolation descriptor or null.
147
+ *
148
+ * @example
149
+ * ```ts
150
+ * const violation = DAG.checkForkRule(dag, envelope);
151
+ * if (violation) {
152
+ * console.error(`Fork by actor ${violation.actor}`);
153
+ * }
154
+ * ```
155
+ */
156
+ export const checkForkRule = (dag: ReceiptDAG, envelope: ReceiptEnvelope): ForkViolation | null => {
157
+ if (Array.isArray(envelope.previous)) return null;
158
+
159
+ const prevHash = envelope.previous as string;
160
+ const actor = actorOf(envelope);
161
+ const attemptedHash = envelope.hash;
162
+
163
+ if (prevHash === GENESIS) return null;
164
+
165
+ const parentNode = dag.nodes.get(prevHash);
166
+ if (!parentNode) return null;
167
+
168
+ for (const childHash of parentNode.children) {
169
+ const childNode = dag.nodes.get(childHash);
170
+ if (childNode && actorOf(childNode.envelope) === actor && childHash !== attemptedHash) {
171
+ return { actor, prevHash, existing: childHash, attempted: attemptedHash };
172
+ }
173
+ }
174
+
175
+ return null;
176
+ };
177
+
178
+ // Total ordering: HLC first (causal), then actor ID (deterministic across nodes),
179
+ // then hash (content-based last resort). Ensures identical linearization on every replica.
180
+ const tiebreak = (a: ReceiptEnvelope, b: ReceiptEnvelope): number => {
181
+ const hlcCmp = hlcCompare(a.timestamp, b.timestamp);
182
+ if (hlcCmp !== 0) return hlcCmp;
183
+ const actorA = actorOf(a);
184
+ const actorB = actorOf(b);
185
+ if (actorA < actorB) return -1;
186
+ if (actorA > actorB) return 1;
187
+ return +(a.hash > b.hash) - +(a.hash < b.hash);
188
+ };
189
+
190
+ const sortedInsert = (
191
+ arr: ReceiptEnvelope[],
192
+ item: ReceiptEnvelope,
193
+ cmp: (a: ReceiptEnvelope, b: ReceiptEnvelope) => number,
194
+ ): void => {
195
+ let lo = 0;
196
+ let hi = arr.length;
197
+ while (lo < hi) {
198
+ const mid = (lo + hi) >>> 1;
199
+ if (cmp(arr[mid]!, item) <= 0) lo = mid + 1;
200
+ else hi = mid;
201
+ }
202
+ arr.splice(lo, 0, item);
203
+ };
204
+
205
+ /**
206
+ * Produce a deterministic topological ordering of all envelopes in the DAG.
207
+ *
208
+ * Kahn's algorithm with stable ordering: sortedInsert maintains tiebreak order in the
209
+ * ready queue, guaranteeing deterministic topological sort across replicas.
210
+ *
211
+ * @example
212
+ * ```ts
213
+ * const dag = DAG.fromReceipts(envelopes);
214
+ * const ordered = DAG.linearize(dag);
215
+ * // ordered is a deterministic total order of all envelopes
216
+ * ```
217
+ */
218
+ export const linearize = (dag: ReceiptDAG): ReadonlyArray<ReceiptEnvelope> => {
219
+ if (dag.nodes.size === 0) return [];
220
+
221
+ const inDegree = new Map<string, number>();
222
+ for (const [hash, node] of dag.nodes) {
223
+ let degree = 0;
224
+ for (const parentHash of node.parents) {
225
+ if (dag.nodes.has(parentHash)) degree++;
226
+ }
227
+ inDegree.set(hash, degree);
228
+ }
229
+
230
+ const ready: ReceiptEnvelope[] = [];
231
+ for (const [hash, degree] of inDegree) {
232
+ if (degree === 0) {
233
+ const node = dag.nodes.get(hash)!;
234
+ sortedInsert(ready, node.envelope, tiebreak);
235
+ }
236
+ }
237
+
238
+ const result: ReceiptEnvelope[] = [];
239
+ while (ready.length > 0) {
240
+ const envelope = ready.shift()!;
241
+ result.push(envelope);
242
+
243
+ const node = dag.nodes.get(envelope.hash)!;
244
+ for (const childHash of node.children) {
245
+ const childDegree = inDegree.get(childHash);
246
+ if (childDegree !== undefined) {
247
+ const newDegree = childDegree - 1;
248
+ inDegree.set(childHash, newDegree);
249
+ if (newDegree === 0) {
250
+ const childNode = dag.nodes.get(childHash)!;
251
+ sortedInsert(ready, childNode.envelope, tiebreak);
252
+ }
253
+ }
254
+ }
255
+ }
256
+
257
+ return result;
258
+ };
259
+
260
+ /**
261
+ * Linearize the DAG and return only envelopes after a given hash.
262
+ *
263
+ * @example
264
+ * ```ts
265
+ * const newEntries = DAG.linearizeFrom(dag, lastSeenHash);
266
+ * // newEntries contains only envelopes after lastSeenHash
267
+ * ```
268
+ */
269
+ export const linearizeFrom = (dag: ReceiptDAG, afterHash: string): ReadonlyArray<ReceiptEnvelope> => {
270
+ const full = linearize(dag);
271
+ const idx = full.findIndex((e) => e.hash === afterHash);
272
+ if (idx === -1) return full;
273
+ return full.slice(idx + 1);
274
+ };
275
+
276
+ /**
277
+ * Get all head (childless) envelopes in the DAG.
278
+ *
279
+ * @example
280
+ * ```ts
281
+ * const heads = DAG.getHeads(dag);
282
+ * // heads.length > 0 for non-empty DAGs
283
+ * ```
284
+ */
285
+ export const getHeads = (dag: ReceiptDAG): ReadonlyArray<ReceiptEnvelope> => {
286
+ const result: ReceiptEnvelope[] = [];
287
+ for (const hash of dag.heads) {
288
+ const node = dag.nodes.get(hash);
289
+ if (node) result.push(node.envelope);
290
+ }
291
+ return result;
292
+ };
293
+
294
+ /**
295
+ * Get the single canonical head of the DAG via deterministic tiebreaking.
296
+ *
297
+ * @example
298
+ * ```ts
299
+ * const head = DAG.canonicalHead(dag);
300
+ * // head is the deterministically chosen head envelope, or null if empty
301
+ * ```
302
+ */
303
+ export const canonicalHead = (dag: ReceiptDAG): ReceiptEnvelope | null => {
304
+ const heads = getHeads(dag);
305
+ if (heads.length === 0) return null;
306
+ if (heads.length === 1) return heads[0]!;
307
+ const sorted = [...heads].sort(tiebreak);
308
+ return sorted[0]!;
309
+ };
310
+
311
+ /**
312
+ * Check whether the DAG has multiple heads (i.e., is in a forked state).
313
+ *
314
+ * @example
315
+ * ```ts
316
+ * if (DAG.isFork(dag)) {
317
+ * console.log('DAG has diverged, needs merge');
318
+ * }
319
+ * ```
320
+ */
321
+ export const isFork = (dag: ReceiptDAG): boolean => dag.heads.length > 1;
322
+
323
+ /**
324
+ * Get all ancestor hashes of a given node (transitive parents).
325
+ *
326
+ * @example
327
+ * ```ts
328
+ * const anc = DAG.ancestors(dag, headHash);
329
+ * // anc contains all hashes reachable by following parent edges
330
+ * ```
331
+ */
332
+ export const ancestors = (dag: ReceiptDAG, hash: string): ReadonlyArray<string> => {
333
+ const visited = new Set<string>();
334
+ const stack: string[] = [];
335
+
336
+ const node = dag.nodes.get(hash);
337
+ if (!node) return [];
338
+
339
+ for (const parentHash of node.parents) {
340
+ if (dag.nodes.has(parentHash)) stack.push(parentHash);
341
+ }
342
+
343
+ while (stack.length > 0) {
344
+ const current = stack.pop()!;
345
+ if (visited.has(current)) continue;
346
+ visited.add(current);
347
+
348
+ const currentNode = dag.nodes.get(current)!;
349
+ for (const parentHash of currentNode.parents) {
350
+ if (dag.nodes.has(parentHash) && !visited.has(parentHash)) stack.push(parentHash);
351
+ }
352
+ }
353
+
354
+ return Array.from(visited);
355
+ };
356
+
357
+ /**
358
+ * Check whether node `a` is an ancestor of node `b` in the DAG.
359
+ *
360
+ * @example
361
+ * ```ts
362
+ * const yes = DAG.isAncestor(dag, genesisHash, headHash);
363
+ * // yes === true (genesis is ancestor of everything)
364
+ * ```
365
+ */
366
+ export const isAncestor = (dag: ReceiptDAG, a: string, b: string): boolean => {
367
+ if (a === b) return false;
368
+ const visited = new Set<string>();
369
+ const queue: string[] = [];
370
+
371
+ const nodeB = dag.nodes.get(b);
372
+ if (!nodeB) return false;
373
+
374
+ for (const parentHash of nodeB.parents) {
375
+ if (dag.nodes.has(parentHash)) queue.push(parentHash);
376
+ }
377
+
378
+ while (queue.length > 0) {
379
+ const current = queue.shift()!;
380
+ if (current === a) return true;
381
+ if (visited.has(current)) continue;
382
+ visited.add(current);
383
+
384
+ const currentNode = dag.nodes.get(current)!;
385
+ for (const parentHash of currentNode.parents) {
386
+ if (dag.nodes.has(parentHash) && !visited.has(parentHash)) queue.push(parentHash);
387
+ }
388
+ }
389
+
390
+ return false;
391
+ };
392
+
393
+ /**
394
+ * Find the latest common ancestor of two nodes in the DAG.
395
+ *
396
+ * @example
397
+ * ```ts
398
+ * const lca = DAG.commonAncestor(dag, hashA, hashB);
399
+ * // lca is the hash of the most recent shared ancestor, or null
400
+ * ```
401
+ */
402
+ export const commonAncestor = (dag: ReceiptDAG, a: string, b: string): string | null => {
403
+ if (a === b) return a;
404
+ const ancestorsOfA = new Set<string>(ancestors(dag, a));
405
+ ancestorsOfA.add(a);
406
+ const ancestorsOfB = new Set<string>(ancestors(dag, b));
407
+ ancestorsOfB.add(b);
408
+
409
+ const common: string[] = [];
410
+ for (const hash of ancestorsOfA) {
411
+ if (ancestorsOfB.has(hash)) common.push(hash);
412
+ }
413
+
414
+ if (common.length === 0) return null;
415
+
416
+ const linearized = linearize(dag);
417
+ const linearOrder = new Map<string, number>();
418
+ for (let i = 0; i < linearized.length; i++) {
419
+ linearOrder.set(linearized[i]!.hash, i);
420
+ }
421
+
422
+ let bestHash: string | null = null;
423
+ let bestOrder = -1;
424
+ for (const hash of common) {
425
+ const order = linearOrder.get(hash);
426
+ if (order !== undefined && order > bestOrder) {
427
+ bestOrder = order;
428
+ bestHash = hash;
429
+ }
430
+ }
431
+
432
+ return bestHash;
433
+ };
434
+
435
+ /**
436
+ * Return the number of nodes in the DAG.
437
+ *
438
+ * @example
439
+ * ```ts
440
+ * const n = DAG.size(dag);
441
+ * // n === dag.nodes.size
442
+ * ```
443
+ */
444
+ export const size = (dag: ReceiptDAG): number => dag.nodes.size;
445
+
446
+ /**
447
+ * Merge remote envelopes into a local DAG, enforcing the anti-fork rule.
448
+ *
449
+ * Returns the updated DAG, list of newly added hashes, and whether the
450
+ * result is forked. Throws on anti-fork violations.
451
+ *
452
+ * @example
453
+ * ```ts
454
+ * const result = DAG.merge(localDag, remoteEnvelopes);
455
+ * // result.dag -- updated DAG
456
+ * // result.added -- newly ingested hashes
457
+ * // result.forked -- true if DAG has multiple heads
458
+ * ```
459
+ */
460
+ export const merge = (local: ReceiptDAG, remote: ReadonlyArray<ReceiptEnvelope>): MergeResult => {
461
+ const added: string[] = [];
462
+ let current = local;
463
+
464
+ for (const envelope of remote) {
465
+ if (current.nodes.has(envelope.hash)) continue;
466
+
467
+ const violation = checkForkRule(current, envelope);
468
+ if (violation !== null) {
469
+ throw new Error(
470
+ `Anti-fork violation: actor "${violation.actor}" attempted to fork from ` +
471
+ `prev-hash "${violation.prevHash}". Existing child: "${violation.existing}", ` +
472
+ `attempted: "${violation.attempted}" (each actor must have a single causal chain — use merge receipts to join branches).`,
473
+ );
474
+ }
475
+
476
+ current = ingest(current, envelope);
477
+ added.push(envelope.hash);
478
+ }
479
+
480
+ return { dag: current, added, forked: isFork(current) };
481
+ };
482
+
483
+ /**
484
+ * DAG namespace -- receipt DAG merge and canonical linearization.
485
+ *
486
+ * Build, query, and merge directed acyclic graphs of receipt envelopes.
487
+ * Supports deterministic linearization, fork detection, ancestor queries,
488
+ * and anti-fork rule enforcement.
489
+ *
490
+ * @example
491
+ * ```ts
492
+ * import { DAG } from '@czap/core';
493
+ *
494
+ * const dag = DAG.fromReceipts(envelopes);
495
+ * const ordered = DAG.linearize(dag);
496
+ * const forked = DAG.isFork(dag);
497
+ * const result = DAG.merge(dag, remoteEnvelopes);
498
+ * ```
499
+ */
500
+ export const DAG = {
501
+ empty,
502
+ ingest,
503
+ ingestAll,
504
+ fromReceipts,
505
+ checkForkRule,
506
+ linearize,
507
+ linearizeFrom,
508
+ getHeads,
509
+ canonicalHead,
510
+ isFork,
511
+ ancestors,
512
+ isAncestor,
513
+ commonAncestor,
514
+ size,
515
+ merge,
516
+ };
517
+
518
+ export declare namespace DAG {
519
+ /** Alias for {@link DAGNode}. */
520
+ export type Node = DAGNode;
521
+ /** Alias for {@link ReceiptDAG}. */
522
+ export type Graph = ReceiptDAG;
523
+ /** Alias for {@link MergeResult}. */
524
+ export type Merge = MergeResult;
525
+ /** Alias for {@link ForkViolation}. */
526
+ export type Fork = ForkViolation;
527
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Centralized default constants for `@czap/core` and downstream packages.
3
+ *
4
+ * Avoids magic numbers scattered across modules. Each constant documents
5
+ * its purpose and which modules consume it.
6
+ *
7
+ * @module
8
+ */
9
+
10
+ /** Default target frames per second for frame budget calculation. Used by: frame-budget.ts */
11
+ export const DEFAULT_TARGET_FPS = 60;
12
+
13
+ /** Milliseconds per second. Used by: frame-budget.ts, easing.ts */
14
+ export const MS_PER_SEC = 1000;
15
+
16
+ /** Default SSE message queue buffer size. Used by: web/stream/sse.ts */
17
+ export const SSE_BUFFER_SIZE = 100;
18
+
19
+ /** Default SSE heartbeat interval in ms. Used by: web/stream/sse.ts */
20
+ export const SSE_HEARTBEAT_MS = 30_000;
21
+
22
+ /** Default SSE initial reconnect delay in ms. Used by: web/stream/sse.ts */
23
+ export const SSE_RECONNECT_INITIAL_MS = 1_000;
24
+
25
+ /** Default SSE max reconnect delay in ms. Used by: web/stream/sse.ts */
26
+ export const SSE_RECONNECT_MAX_MS = 30_000;
27
+
28
+ /** Default compositor state pool capacity. Used by: compositor.ts, compositor-pool.ts */
29
+ export const COMPOSITOR_POOL_CAP = 8;
30
+
31
+ /** Maximum number of dirty flag keys (bitset width limit). Used by: dirty.ts */
32
+ export const DIRTY_FLAGS_MAX = 31;
33
+
34
+ /** WASM linear memory scratch offset for boundary/spring kernels. Used by: wasm-dispatch.ts */
35
+ export const WASM_SCRATCH_BASE = 32768;
36
+
37
+ /** Default keyframe interval in frames for video encoding. Used by: web/capture/webcodecs.ts */
38
+ export const CAPTURE_KEYFRAME_INTERVAL = 30;
39
+
40
+ /** Number of sub-steps for spring animation resolution. Used by: easing.ts */
41
+ export const EASING_SPRING_STEPS = 2000;
42
+
43
+ /** Default theme transition duration in ms. Used by: compiler/theme-css.ts */
44
+ export const THEME_TRANSITION_DURATION_MS = 200;
45
+
46
+ /** Default theme transition easing function. Used by: compiler/theme-css.ts */
47
+ export const THEME_TRANSITION_EASING = 'ease-in-out';
48
+
49
+ /** Default canvas fallback width when clientWidth is 0. Used by: astro/runtime/gpu.ts */
50
+ export const CANVAS_FALLBACK_WIDTH = 300;
51
+
52
+ /** Default canvas fallback height when clientHeight is 0. Used by: astro/runtime/gpu.ts */
53
+ export const CANVAS_FALLBACK_HEIGHT = 150;
54
+
55
+ /** Viewport breakpoints in CSS pixels. Used by: astro/quantize.ts */
56
+ export const VIEWPORT = {
57
+ mobile: 375,
58
+ tablet: 768,
59
+ desktop: 1280,
60
+ } as const;