@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/op.ts ADDED
@@ -0,0 +1,233 @@
1
+ /**
2
+ * `Op<A, E, R>` — `Effect.Effect` wrapper with named factories.
3
+ *
4
+ * @module
5
+ */
6
+
7
+ import type { Scope, Result } from 'effect';
8
+ import { Effect, Schedule, Duration } from 'effect';
9
+ import type { Millis } from './brands.js';
10
+
11
+ interface OpShape<A, E = never, R = never> {
12
+ readonly _tag: 'Op';
13
+ readonly effect: Effect.Effect<A, E, R>;
14
+ run(): Effect.Effect<A, E, R | Scope.Scope>;
15
+ map<B>(f: (a: A) => B): OpShape<B, E, R>;
16
+ flatMap<B, E2, R2>(f: (a: A) => OpShape<B, E2, R2>): OpShape<B, E | E2, R | R2>;
17
+ }
18
+
19
+ type OpValue<T extends OpShape<unknown, unknown, unknown>> = T extends OpShape<infer A, unknown, unknown> ? A : never;
20
+ type OpError<T extends OpShape<unknown, unknown, unknown>> = T extends OpShape<unknown, infer E, unknown> ? E : never;
21
+ type OpRequirement<T extends OpShape<unknown, unknown, unknown>> =
22
+ T extends OpShape<unknown, unknown, infer R> ? R : never;
23
+ type OpValues<T extends readonly OpShape<unknown, unknown, unknown>[]> = { [K in keyof T]: OpValue<T[K]> };
24
+ type SettledOpValues<T extends readonly OpShape<unknown, unknown, unknown>[]> = {
25
+ [K in keyof T]: Result.Result<OpValue<T[K]>, OpError<T[K]>>;
26
+ };
27
+
28
+ /**
29
+ * Wraps an Effect into an Op, providing `.map()` and `.flatMap()` chaining.
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * const op = Op.make(Effect.succeed(42));
34
+ * const doubled = op.map(n => n * 2);
35
+ * const result = Effect.runSync(doubled.run()); // 84
36
+ * ```
37
+ */
38
+ const _make = <A, E = never, R = never>(effect: Effect.Effect<A, E, R>): OpShape<A, E, R> => ({
39
+ _tag: 'Op' as const,
40
+ effect,
41
+ run: () => effect,
42
+ map: <B>(fn: (a: A) => B): OpShape<B, E, R> => _make(Effect.map(effect, fn)),
43
+ flatMap: <B, E2, R2>(fn: (a: A) => OpShape<B, E2, R2>): OpShape<B, E | E2, R | R2> =>
44
+ _make(Effect.flatMap(effect, (a) => fn(a).effect)),
45
+ });
46
+
47
+ /**
48
+ * Creates an Op from a Promise-returning function, catching errors as `Error`.
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * const op = Op.fromPromise(() => fetch('/api/data').then(r => r.json()));
53
+ * const result = await Effect.runPromise(op.run());
54
+ * console.log(result); // parsed JSON response
55
+ * ```
56
+ */
57
+ const _fromPromise = <A>(f: () => Promise<A>): OpShape<A, Error> =>
58
+ _make(
59
+ Effect.tryPromise({
60
+ try: f,
61
+ catch: (error) => (error instanceof Error ? error : new Error(String(error))),
62
+ }),
63
+ );
64
+
65
+ /**
66
+ * Creates an Op that immediately succeeds with the given value.
67
+ *
68
+ * @example
69
+ * ```ts
70
+ * const op = Op.succeed({ name: 'dark', contrast: 0.9 });
71
+ * const result = Effect.runSync(op.run()); // { name: 'dark', contrast: 0.9 }
72
+ * ```
73
+ */
74
+ const _succeed = <A>(value: A): OpShape<A> => _make(Effect.succeed(value));
75
+
76
+ /**
77
+ * Creates an Op that immediately fails with the given error.
78
+ *
79
+ * @example
80
+ * ```ts
81
+ * const op = Op.fail(new Error('GPU not available'));
82
+ * // Effect.runSync(op.run()) would throw
83
+ * ```
84
+ */
85
+ const _fail = <E>(error: E): OpShape<never, E> => _make(Effect.fail(error));
86
+
87
+ /**
88
+ * Runs all Ops concurrently and returns their results as a tuple.
89
+ * Fails if any Op fails.
90
+ *
91
+ * @example
92
+ * ```ts
93
+ * const a = Op.succeed(10);
94
+ * const b = Op.succeed('hello');
95
+ * const combined = Op.all([a, b] as const);
96
+ * const [num, str] = Effect.runSync(combined.run()); // [10, 'hello']
97
+ * ```
98
+ */
99
+ // Effect.all's overloads infer tuple results only when the input shape itself is tuple-typed.
100
+ // `tasks.map(...)` widens to unknown[], so we contain one cast into a typed wrapper and
101
+ // apply it at the boundary where the tuple→Op product is materialized.
102
+ const _all = <T extends readonly OpShape<unknown, unknown, unknown>[]>(
103
+ tasks: T,
104
+ ): OpShape<OpValues<T>, OpError<T[number]>, OpRequirement<T[number]>> => {
105
+ const effects = tasks.map((task) => task.effect);
106
+ const combined = Effect.all(effects, { concurrency: 'unbounded' }) as unknown as Effect.Effect<
107
+ OpValues<T>,
108
+ OpError<T[number]>,
109
+ OpRequirement<T[number]>
110
+ >;
111
+ return _make(combined);
112
+ };
113
+
114
+ /**
115
+ * Runs all Ops concurrently and returns a Result for each, never failing.
116
+ * Each result is either a success or a failure.
117
+ *
118
+ * @example
119
+ * ```ts
120
+ * const a = Op.succeed(1);
121
+ * const b = Op.fail(new Error('oops'));
122
+ * const settled = Op.allSettled([a, b] as const);
123
+ * const results = Effect.runSync(settled.run());
124
+ * // results[0] is Result.success(1), results[1] is Result.failure(Error)
125
+ * ```
126
+ */
127
+ // Mirrors _all: one boundary cast from unknown[] back to the tuple-projected product.
128
+ const _allSettled = <T extends readonly OpShape<unknown, unknown, unknown>[]>(
129
+ tasks: T,
130
+ ): OpShape<SettledOpValues<T>, never, OpRequirement<T[number]>> => {
131
+ const resultEffects = tasks.map((task) => Effect.result(task.effect));
132
+ const combined = Effect.all(resultEffects, { concurrency: 'unbounded' }) as unknown as Effect.Effect<
133
+ SettledOpValues<T>,
134
+ never,
135
+ OpRequirement<T[number]>
136
+ >;
137
+ return _make(combined);
138
+ };
139
+
140
+ /**
141
+ * Races multiple Ops concurrently, returning the first to complete.
142
+ * Fails with an error if the array is empty.
143
+ *
144
+ * @example
145
+ * ```ts
146
+ * const fast = Op.succeed('fast');
147
+ * const slow = Op.fromPromise(() => new Promise(r => setTimeout(() => r('slow'), 100)));
148
+ * const winner = Op.race([fast, slow]);
149
+ * const result = Effect.runSync(winner.run()); // 'fast'
150
+ * ```
151
+ */
152
+ const _race = <A, E, R>(tasks: ReadonlyArray<OpShape<A, E, R>>): OpShape<A, E | Error, R> => {
153
+ if (tasks.length === 0) {
154
+ return _fail(new Error('Op.race: empty array'));
155
+ }
156
+ if (tasks.length === 1) {
157
+ return tasks[0]!;
158
+ }
159
+ const effects = tasks.map((task) => task.effect);
160
+ const raced = effects.reduce((acc, effect) => Effect.race(acc, effect));
161
+ return _make(raced);
162
+ };
163
+
164
+ /**
165
+ * Retries a failing Op with exponential backoff.
166
+ *
167
+ * @example
168
+ * ```ts
169
+ * const flaky = Op.fromPromise(() => fetch('/unstable-api').then(r => r.json()));
170
+ * const resilient = Op.retry(flaky, { times: 3, delay: Millis(200), factor: 2 });
171
+ * const result = await Effect.runPromise(resilient.run());
172
+ * ```
173
+ */
174
+ const _retry = <A, E, R>(
175
+ task: OpShape<A, E, R>,
176
+ options: { times: number; delay?: Millis; factor?: number },
177
+ ): OpShape<A, E, R> => {
178
+ const delay = options.delay ?? 100;
179
+ const factor = options.factor ?? 2;
180
+
181
+ const schedule = Schedule.exponential(Duration.millis(delay), factor).pipe(
182
+ Schedule.both(Schedule.recurs(options.times)),
183
+ );
184
+
185
+ return _make(Effect.retry(task.effect, schedule));
186
+ };
187
+
188
+ /**
189
+ * Wraps an Op with a timeout, failing with an Error if it exceeds the given duration.
190
+ *
191
+ * @example
192
+ * ```ts
193
+ * const slow = Op.fromPromise(() => new Promise(r => setTimeout(() => r('done'), 5000)));
194
+ * const bounded = Op.timeout(slow, Millis(1000));
195
+ * // Will fail with Error('Op timed out after 1000ms') if not resolved in time
196
+ * ```
197
+ */
198
+ const _timeout = <A, E, R>(task: OpShape<A, E, R>, ms: Millis): OpShape<A, E | Error, R> =>
199
+ _make(
200
+ Effect.timeout(task.effect, Duration.millis(ms)).pipe(
201
+ Effect.catchTag('TimeoutError', () => Effect.fail(new Error(`Op timed out after ${ms}ms`))),
202
+ ),
203
+ );
204
+
205
+ /**
206
+ * Op -- Effect.Effect wrapper providing named factories and combinators
207
+ * for async operations with retry, timeout, race, and parallel execution.
208
+ *
209
+ * @example
210
+ * ```ts
211
+ * const op = Op.succeed(42).map(n => n * 2);
212
+ * const result = Effect.runSync(op.run()); // 84
213
+ *
214
+ * const tasks = Op.all([Op.succeed(1), Op.succeed(2)] as const);
215
+ * const [a, b] = Effect.runSync(tasks.run()); // [1, 2]
216
+ * ```
217
+ */
218
+ export const Op = {
219
+ make: _make,
220
+ fromPromise: _fromPromise,
221
+ succeed: _succeed,
222
+ fail: _fail,
223
+ all: _all,
224
+ allSettled: _allSettled,
225
+ race: _race,
226
+ retry: _retry,
227
+ timeout: _timeout,
228
+ };
229
+
230
+ export declare namespace Op {
231
+ /** Structural shape of an {@link Op}: a thin alias over `Effect.Effect<A, E, R>` produced by the `Op.*` factories. */
232
+ export type Shape<A, E = never, R = never> = OpShape<A, E, R>;
233
+ }
package/src/plan.ts ADDED
@@ -0,0 +1,317 @@
1
+ /**
2
+ * Plan -- plan IR builder for universal execution DAG.
3
+ *
4
+ * @module
5
+ */
6
+
7
+ /**
8
+ * Discriminated union describing the kind of work a `PlanStep` performs.
9
+ *
10
+ * `pure` and `effect` name an executable function; `spawn` references a child
11
+ * fiber/worker keyed by `key`; `domain` dispatches to an external domain's
12
+ * named operation; `choice` marks a branch point; `noop` is an explicit
13
+ * placeholder.
14
+ */
15
+ export type OpType =
16
+ | { readonly type: 'pure'; readonly fn?: string }
17
+ | { readonly type: 'effect'; readonly fn?: string }
18
+ | { readonly type: 'spawn'; readonly key: string; readonly spec: Record<string, unknown> }
19
+ | { readonly type: 'domain'; readonly domain: string; readonly op: string }
20
+ | { readonly type: 'choice'; readonly condition: unknown }
21
+ | { readonly type: 'noop' };
22
+
23
+ /**
24
+ * Edge flavor in a plan DAG: sequential (`seq`), parallel (`par`), or the two
25
+ * branches of a `choice` step (`choice_then` / `choice_else`).
26
+ */
27
+ export type EdgeType = 'seq' | 'par' | 'choice_then' | 'choice_else';
28
+
29
+ /** A single node in a {@link PlanIR}: an identifier, a display name, and its {@link OpType}. */
30
+ export interface PlanStep {
31
+ readonly id: string;
32
+ readonly name: string;
33
+ readonly opType: OpType;
34
+ readonly metadata?: Record<string, unknown>;
35
+ }
36
+
37
+ /** A directed edge between two {@link PlanStep}s, tagged by {@link EdgeType}. */
38
+ export interface PlanEdge {
39
+ readonly from: string;
40
+ readonly to: string;
41
+ readonly type: EdgeType;
42
+ }
43
+
44
+ /** Intermediate representation of a plan: named steps plus directed edges. */
45
+ export interface PlanIR {
46
+ readonly name: string;
47
+ readonly steps: readonly PlanStep[];
48
+ readonly edges: readonly PlanEdge[];
49
+ readonly metadata?: Record<string, unknown>;
50
+ }
51
+
52
+ /** Structural failure from {@link Plan.validate}: either a cycle or an edge pointing at a missing step. */
53
+ export type PlanValidationError =
54
+ | { readonly type: 'cycle'; readonly message: string; readonly stepIds?: readonly string[] }
55
+ | { readonly type: 'missing_step'; readonly message: string; readonly stepIds?: readonly string[] };
56
+
57
+ /** Result of {@link Plan.validate}: either the validated plan or a list of errors. */
58
+ export type PlanValidationResult =
59
+ | { readonly ok: true; readonly plan: PlanIR }
60
+ | { readonly ok: false; readonly errors: readonly PlanValidationError[] };
61
+
62
+ /**
63
+ * Result of {@link Plan.topoSort}: the sorted step IDs, optionally accompanied by
64
+ * the IDs that participated in a detected cycle.
65
+ */
66
+ export type TopoSortResult =
67
+ | { readonly sorted: readonly string[]; readonly cycle?: undefined }
68
+ | { readonly sorted: readonly string[]; readonly cycle: readonly string[] };
69
+
70
+ interface PlanBuilder {
71
+ step(name: string, opType: OpType, metadata?: Record<string, unknown>): PlanBuilder;
72
+ seq(fromId: string, toId: string): PlanBuilder;
73
+ par(fromId: string, toId: string): PlanBuilder;
74
+ choice(fromId: string, thenId: string, elseId: string): PlanBuilder;
75
+ build(): PlanIR;
76
+ }
77
+
78
+ class PlanBuilderImpl implements PlanBuilder {
79
+ private steps: PlanStep[] = [];
80
+ private edges: PlanEdge[] = [];
81
+
82
+ constructor(private readonly planName: string) {}
83
+
84
+ step(name: string, opType: OpType, metadata?: Record<string, unknown>): PlanBuilder {
85
+ const id = `step-${this.steps.length + 1}`;
86
+ this.steps.push({ id, name, opType, metadata });
87
+ return this;
88
+ }
89
+
90
+ seq(fromId: string, toId: string): PlanBuilder {
91
+ this.edges.push({ from: fromId, to: toId, type: 'seq' });
92
+ return this;
93
+ }
94
+
95
+ par(fromId: string, toId: string): PlanBuilder {
96
+ this.edges.push({ from: fromId, to: toId, type: 'par' });
97
+ return this;
98
+ }
99
+
100
+ choice(fromId: string, thenId: string, elseId: string): PlanBuilder {
101
+ this.edges.push({ from: fromId, to: thenId, type: 'choice_then' });
102
+ this.edges.push({ from: fromId, to: elseId, type: 'choice_else' });
103
+ return this;
104
+ }
105
+
106
+ build(): PlanIR {
107
+ return {
108
+ name: this.planName,
109
+ steps: [...this.steps],
110
+ edges: [...this.edges],
111
+ };
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Create a new PlanBuilder with the given plan name.
117
+ *
118
+ * Returns a fluent builder that supports chaining `.step()`, `.seq()`,
119
+ * `.par()`, and `.choice()` calls. Call `.build()` to produce the PlanIR.
120
+ *
121
+ * @example
122
+ * ```ts
123
+ * const plan = Plan.make('my-pipeline')
124
+ * .step('fetch', { type: 'effect' })
125
+ * .step('transform', { type: 'pure' })
126
+ * .seq('step-1', 'step-2')
127
+ * .build();
128
+ * // plan.name === 'my-pipeline'
129
+ * // plan.steps.length === 2
130
+ * // plan.edges.length === 1
131
+ * ```
132
+ */
133
+ function _make(name: string): PlanBuilder {
134
+ return new PlanBuilderImpl(name);
135
+ }
136
+
137
+ function hasCycle(planIR: PlanIR): boolean {
138
+ const visited = new Set<string>();
139
+ const recStack = new Set<string>();
140
+ const adjList = new Map<string, string[]>();
141
+
142
+ for (const step of planIR.steps) {
143
+ adjList.set(step.id, []);
144
+ }
145
+
146
+ for (const edge of planIR.edges) {
147
+ adjList.get(edge.from)?.push(edge.to);
148
+ }
149
+
150
+ function dfs(node: string): boolean {
151
+ visited.add(node);
152
+ recStack.add(node);
153
+ for (const neighbor of adjList.get(node) ?? []) {
154
+ if (!visited.has(neighbor)) {
155
+ if (dfs(neighbor)) return true;
156
+ } else if (recStack.has(neighbor)) {
157
+ return true;
158
+ }
159
+ }
160
+ recStack.delete(node);
161
+ return false;
162
+ }
163
+
164
+ for (const step of planIR.steps) {
165
+ if (!visited.has(step.id) && dfs(step.id)) return true;
166
+ }
167
+
168
+ return false;
169
+ }
170
+
171
+ /**
172
+ * Validate a PlanIR for structural correctness.
173
+ *
174
+ * Checks that all edges reference existing steps and that the graph is acyclic.
175
+ * Returns `{ ok: true, plan }` on success or `{ ok: false, errors }` with
176
+ * detailed validation errors.
177
+ *
178
+ * @example
179
+ * ```ts
180
+ * const plan = Plan.make('test').step('a', { type: 'noop' }).build();
181
+ * const result = Plan.validate(plan);
182
+ * // result.ok === true
183
+ * // result.plan === plan
184
+ * ```
185
+ */
186
+ function _validate(planIR: PlanIR): PlanValidationResult {
187
+ const errors: PlanValidationError[] = [];
188
+ const stepIds = new Set(planIR.steps.map((s) => s.id));
189
+
190
+ for (const edge of planIR.edges) {
191
+ if (!stepIds.has(edge.from)) {
192
+ errors.push({
193
+ type: 'missing_step',
194
+ message: `Edge references unknown step: ${edge.from}`,
195
+ stepIds: [edge.from],
196
+ });
197
+ }
198
+ if (!stepIds.has(edge.to)) {
199
+ errors.push({
200
+ type: 'missing_step',
201
+ message: `Edge references unknown step: ${edge.to}`,
202
+ stepIds: [edge.to],
203
+ });
204
+ }
205
+ }
206
+
207
+ if (hasCycle(planIR)) {
208
+ errors.push({ type: 'cycle', message: 'Plan contains a cycle' });
209
+ }
210
+
211
+ return errors.length === 0 ? { ok: true, plan: planIR } : { ok: false, errors };
212
+ }
213
+
214
+ /**
215
+ * Topologically sort the steps of a PlanIR using Kahn's algorithm.
216
+ *
217
+ * Returns `{ sorted }` on success. If a cycle exists, returns
218
+ * `{ sorted, cycle }` where `cycle` lists the step IDs involved.
219
+ *
220
+ * @example
221
+ * ```ts
222
+ * const plan = Plan.make('pipeline')
223
+ * .step('a', { type: 'pure' })
224
+ * .step('b', { type: 'pure' })
225
+ * .seq('step-1', 'step-2')
226
+ * .build();
227
+ * const result = Plan.topoSort(plan);
228
+ * // result.sorted === ['step-1', 'step-2']
229
+ * ```
230
+ */
231
+ function _topoSort(planIR: PlanIR): TopoSortResult {
232
+ const adjList = new Map<string, string[]>();
233
+ const inDegree = new Map<string, number>();
234
+
235
+ for (const step of planIR.steps) {
236
+ adjList.set(step.id, []);
237
+ inDegree.set(step.id, 0);
238
+ }
239
+
240
+ for (const edge of planIR.edges) {
241
+ adjList.get(edge.from)?.push(edge.to);
242
+ inDegree.set(edge.to, (inDegree.get(edge.to) ?? 0) + 1);
243
+ }
244
+
245
+ const queue: string[] = [];
246
+ const result: string[] = [];
247
+
248
+ for (const [id, degree] of inDegree) {
249
+ if (degree === 0) queue.push(id);
250
+ }
251
+
252
+ while (queue.length > 0) {
253
+ const current = queue.shift()!;
254
+ result.push(current);
255
+ for (const neighbor of adjList.get(current) ?? []) {
256
+ const newDegree = inDegree.get(neighbor)! - 1;
257
+ inDegree.set(neighbor, newDegree);
258
+ if (newDegree === 0) queue.push(neighbor);
259
+ }
260
+ }
261
+
262
+ if (result.length !== planIR.steps.length) {
263
+ const resultSet = new Set(result);
264
+ const cycleNodes = planIR.steps.filter((s) => !resultSet.has(s.id)).map((s) => s.id);
265
+ return { sorted: result, cycle: cycleNodes };
266
+ }
267
+
268
+ return { sorted: result };
269
+ }
270
+
271
+ /**
272
+ * Plan namespace -- plan IR builder for universal execution DAG.
273
+ *
274
+ * Build, validate, and topologically sort execution plans. Plans model
275
+ * computation graphs with sequential, parallel, and conditional edges.
276
+ *
277
+ * @example
278
+ * ```ts
279
+ * import { Plan } from '@czap/core';
280
+ *
281
+ * const plan = Plan.make('render-pipeline')
282
+ * .step('load', { type: 'effect' })
283
+ * .step('compile', { type: 'pure' })
284
+ * .step('emit', { type: 'effect' })
285
+ * .seq('step-1', 'step-2')
286
+ * .seq('step-2', 'step-3')
287
+ * .build();
288
+ * const valid = Plan.validate(plan);
289
+ * const order = Plan.topoSort(plan);
290
+ * // order.sorted === ['step-1', 'step-2', 'step-3']
291
+ * ```
292
+ */
293
+ export const Plan = {
294
+ /** Start a new fluent {@link Plan.Builder} with the given display name. */
295
+ make: _make,
296
+ /** Check that every edge references a known step and that the graph is acyclic. */
297
+ validate: _validate,
298
+ /** Kahn's-algorithm topological sort; surfaces cycle participants if the plan is not a DAG. */
299
+ topoSort: _topoSort,
300
+ };
301
+
302
+ export declare namespace Plan {
303
+ /** Alias for `PlanIR`. */
304
+ export type IR = PlanIR;
305
+ /** Alias for `PlanStep`. */
306
+ export type Step = PlanStep;
307
+ /** Alias for `PlanEdge`. */
308
+ export type Edge = PlanEdge;
309
+ /** Alias for `PlanValidationError`. */
310
+ export type ValidationError = PlanValidationError;
311
+ /** Alias for `PlanValidationResult`. */
312
+ export type ValidationResult = PlanValidationResult;
313
+ /** Alias for `TopoSortResult`. */
314
+ export type TopoSort = TopoSortResult;
315
+ /** Fluent builder interface returned by `Plan.make`. */
316
+ export type Builder = PlanBuilder;
317
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Protocol types -- CellEnvelope, CellKind, CellMeta.
3
+ *
4
+ * These types form the wire-level protocol for cells in the czap system.
5
+ * Every cell has a kind, content address, metadata, and value.
6
+ *
7
+ * @module
8
+ */
9
+
10
+ import type { ContentAddress, HLC } from './brands.js';
11
+
12
+ /**
13
+ * Discriminator tagging what a {@link CellEnvelope} carries — a boundary, a
14
+ * discrete state, a target output (CSS/GLSL/WGSL/ARIA/AI), or one of the
15
+ * other reactive shapes produced along the pipeline.
16
+ */
17
+ export type CellKind =
18
+ | 'boundary'
19
+ | 'state'
20
+ | 'output'
21
+ | 'signal'
22
+ | 'transition'
23
+ | 'timeline'
24
+ | 'compositor'
25
+ | 'blend'
26
+ | 'css'
27
+ | 'glsl'
28
+ | 'wgsl'
29
+ | 'aria'
30
+ | 'ai';
31
+
32
+ /** Protocol metadata attached to every {@link CellEnvelope}: HLC timestamps + monotonic version counter. */
33
+ export interface CellMeta {
34
+ readonly created: HLC;
35
+ readonly updated: HLC;
36
+ readonly version: number;
37
+ }
38
+
39
+ /**
40
+ * Wire-level envelope for a cell value: tagged by {@link CellKind}, identified
41
+ * by its content address, stamped with {@link CellMeta}, carrying the typed
42
+ * payload in `value`.
43
+ */
44
+ export interface CellEnvelope<K extends CellKind = CellKind, T = unknown> {
45
+ readonly kind: K;
46
+ readonly id: ContentAddress;
47
+ readonly meta: CellMeta;
48
+ readonly value: T;
49
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Quantizer interface -- the base contract for quantizer implementations.
3
+ *
4
+ * The canonical implementation lives in `@czap/quantizer` (`Q.from()` builder API).
5
+ *
6
+ * @module
7
+ */
8
+
9
+ import type { Effect, Stream } from 'effect';
10
+ import type { Boundary } from './boundary.js';
11
+ import type { StateUnion, BoundaryCrossing } from './type-utils.js';
12
+
13
+ /**
14
+ * Quantizer contract — the live evaluator that binds a {@link Boundary} to a signal source.
15
+ *
16
+ * A quantizer holds a boundary definition plus the reactive machinery to observe
17
+ * its current state and emit crossings when the underlying signal moves between
18
+ * bands. The concrete implementation is produced by `@czap/quantizer`'s `Q.from()`
19
+ * builder; consumers interact only via this structural interface.
20
+ */
21
+ export interface Quantizer<B extends Boundary.Shape = Boundary.Shape> {
22
+ readonly _tag: 'Quantizer';
23
+ readonly boundary: B;
24
+ readonly state: Effect.Effect<StateUnion<B>>;
25
+ /** Synchronous state accessor for hot paths (avoids Effect overhead). */
26
+ readonly stateSync?: () => StateUnion<B>;
27
+ readonly changes: Stream.Stream<BoundaryCrossing<StateUnion<B> & string>>;
28
+ evaluate(value: number): StateUnion<B>;
29
+ }