@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
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Pure TypeScript fallback kernels for WASM compute functions.
3
+ *
4
+ * These produce bit-identical results (within float precision) to the
5
+ * Rust WASM kernels. When WASM is unavailable, these are used automatically.
6
+ *
7
+ * @module
8
+ */
9
+
10
+ import type { WASMKernels } from './wasm-dispatch.js';
11
+
12
+ function springCurve(stiffness: number, damping: number, mass: number, samples: number): Float32Array {
13
+ const m = mass <= 0 ? 1 : mass;
14
+ const count = Math.min(Math.max(0, samples | 0), 255);
15
+ const omega = Math.sqrt(stiffness / m);
16
+ const zeta = damping / (2 * Math.sqrt(stiffness * m));
17
+ const out = new Float32Array(count + 1);
18
+
19
+ for (let i = 0; i <= count; i++) {
20
+ const t = i / count;
21
+ if (t <= 0) {
22
+ out[i] = 0;
23
+ } else if (t >= 1) {
24
+ out[i] = 1;
25
+ } else if (zeta < 1) {
26
+ const omegaD = omega * Math.sqrt(1 - zeta * zeta);
27
+ out[i] =
28
+ 1 - Math.exp(-zeta * omega * t) * (Math.cos(omegaD * t) + ((zeta * omega) / omegaD) * Math.sin(omegaD * t));
29
+ } else if (zeta === 1) {
30
+ out[i] = 1 - (1 + omega * t) * Math.exp(-omega * t);
31
+ } else {
32
+ const s = Math.sqrt(zeta * zeta - 1);
33
+ const r1 = -omega * (zeta + s);
34
+ const r2 = -omega * (zeta - s);
35
+ const c1 = r2 / (r2 - r1);
36
+ const c2 = -r1 / (r2 - r1);
37
+ out[i] = 1 - (c1 * Math.exp(r1 * t) + c2 * Math.exp(r2 * t));
38
+ }
39
+ }
40
+
41
+ return out;
42
+ }
43
+
44
+ function batchBoundaryEval(thresholds: Float64Array, values: Float64Array): Uint32Array {
45
+ const tLen = thresholds.length;
46
+ const vLen = values.length;
47
+ const out = new Uint32Array(vLen);
48
+
49
+ for (let vi = 0; vi < vLen; vi++) {
50
+ const value = values[vi]!;
51
+ let stateIdx = 0;
52
+
53
+ for (let ti = tLen - 1; ti >= 0; ti--) {
54
+ if (value >= thresholds[ti]!) {
55
+ stateIdx = ti;
56
+ break;
57
+ }
58
+ }
59
+
60
+ out[vi] = stateIdx;
61
+ }
62
+
63
+ return out;
64
+ }
65
+
66
+ function blendNormalize(weights: Float32Array): Float32Array {
67
+ const len = weights.length;
68
+ if (len === 0) return weights;
69
+
70
+ let total = 0;
71
+ for (let i = 0; i < len; i++) {
72
+ let w = weights[i]!;
73
+ if (w < 0) {
74
+ w = 0;
75
+ weights[i] = 0;
76
+ }
77
+ total += w;
78
+ }
79
+
80
+ if (total > 0) {
81
+ const inv = 1 / total;
82
+ for (let i = 0; i < len; i++) {
83
+ weights[i] = weights[i]! * inv;
84
+ }
85
+ }
86
+
87
+ return weights;
88
+ }
89
+
90
+ /**
91
+ * Pure-JS implementation of the {@link WASMKernels} contract.
92
+ *
93
+ * Selected automatically by {@link WASMDispatch} when the Rust compute crate
94
+ * cannot be instantiated (e.g. missing `WebAssembly`, CSP restrictions, or
95
+ * startup failure). Produces results bit-identical to the WASM build within
96
+ * IEEE-754 precision limits.
97
+ */
98
+ export const fallbackKernels: WASMKernels = {
99
+ springCurve,
100
+ batchBoundaryEval,
101
+ blendNormalize,
102
+ };
package/src/wire.ts ADDED
@@ -0,0 +1,274 @@
1
+ /**
2
+ * `Wire<T, E>` — fluent stream wrapper.
3
+ *
4
+ * @module
5
+ */
6
+
7
+ import { Effect, Stream, Queue } from 'effect';
8
+ import type { Millis } from './brands.js';
9
+
10
+ // Fluent composition: chainable transform pipeline for Effect streams, enabling .map/.filter/.merge without breaking the Effect fiber model.
11
+
12
+ interface WireShape<T, E = never> {
13
+ readonly _tag: 'Wire';
14
+ readonly stream: Stream.Stream<T, E>;
15
+ map<B>(f: (a: T) => B): WireShape<B, E>;
16
+ filter(f: (a: T) => boolean): WireShape<T, E>;
17
+ take(n: number): WireShape<T, E>;
18
+ takeUntil(predicate: (a: T) => boolean): WireShape<T, E>;
19
+ debounce(ms: Millis): WireShape<T, E>;
20
+ throttle(ms: Millis): WireShape<T, E>;
21
+ scan<B>(initial: B, f: (acc: B, value: T) => B): WireShape<B, E>;
22
+ flatMap<B, E2>(f: (a: T) => WireShape<B, E2>): WireShape<B, E | E2>;
23
+ merge<B, E2>(other: WireShape<B, E2>): WireShape<T | B, E | E2>;
24
+ run(): Effect.Effect<void, E>;
25
+ runCollect(): Effect.Effect<T[], E>;
26
+ }
27
+
28
+ function wrap<T, E = never>(stream: Stream.Stream<T, E>): WireShape<T, E> {
29
+ const wireStream: WireShape<T, E> = {
30
+ _tag: 'Wire' as const,
31
+ stream,
32
+
33
+ map: <B>(f: (a: T) => B) => wrap<B, E>(Stream.map(stream, f)),
34
+
35
+ filter: (f: (a: T) => boolean) => wrap<T, E>(Stream.filter(stream, f)),
36
+
37
+ take: (n: number) => wrap<T, E>(Stream.take(stream, n)),
38
+
39
+ takeUntil: (predicate: (a: T) => boolean) => wrap<T, E>(Stream.takeUntil(stream, predicate)),
40
+
41
+ debounce: (ms: Millis) => wrap<T, E>(Stream.debounce(stream, ms)),
42
+
43
+ throttle: (ms: Millis) =>
44
+ wrap<T, E>(
45
+ Stream.throttle(stream, {
46
+ cost: () => 1,
47
+ units: 1,
48
+ duration: ms,
49
+ strategy: 'enforce',
50
+ }),
51
+ ),
52
+
53
+ scan: <B>(initial: B, f: (acc: B, value: T) => B) => wrap<B, E>(Stream.scan(stream, initial, f)),
54
+
55
+ flatMap: <B, E2>(f: (a: T) => WireShape<B, E2>) => wrap<B, E | E2>(Stream.flatMap(stream, (a) => f(a).stream)),
56
+
57
+ merge: <B, E2>(other: WireShape<B, E2>) => wrap<T | B, E | E2>(Stream.merge(stream, other.stream)),
58
+
59
+ run: () => Stream.runDrain(stream),
60
+
61
+ runCollect: () => Effect.map(Stream.runCollect(stream), (chunk) => Array.from<T>(chunk)),
62
+ };
63
+
64
+ return wireStream;
65
+ }
66
+
67
+ /**
68
+ * Wraps an Effect Stream into a fluent Wire with chainable operators.
69
+ *
70
+ * @example
71
+ * ```ts
72
+ * const wire = Wire.from(Stream.make(1, 2, 3));
73
+ * const doubled = wire.map(n => n * 2).filter(n => n > 2);
74
+ * const results = Effect.runSync(doubled.runCollect()); // [4, 6]
75
+ * ```
76
+ */
77
+ const _from = <T, E = never>(stream: Stream.Stream<T, E>): WireShape<T, E> => wrap(stream);
78
+
79
+ /**
80
+ * Creates a Wire from a Server-Sent Events endpoint.
81
+ * The EventSource is cleaned up when the stream finalizes.
82
+ *
83
+ * @example
84
+ * ```ts
85
+ * const wire = Wire.fromSSE('/api/events');
86
+ * const parsed = wire.map(evt => JSON.parse(evt.data));
87
+ * await Effect.runPromise(Wire.runForEach(parsed, msg => Effect.log(msg)));
88
+ * ```
89
+ */
90
+ const _fromSSE = (url: string, options?: EventSourceInit): WireShape<MessageEvent, Error> => {
91
+ const stream = Stream.callback<MessageEvent, Error>((queue) =>
92
+ Effect.gen(function* () {
93
+ const eventSource = new EventSource(url, options);
94
+ let closed = false;
95
+
96
+ const shutdown = (): void => {
97
+ if (closed) return;
98
+ closed = true;
99
+ Queue.shutdown(queue).pipe(Effect.runSync);
100
+ };
101
+
102
+ eventSource.onmessage = (event) => {
103
+ Queue.offerUnsafe(queue, event);
104
+ };
105
+
106
+ eventSource.onerror = () => {
107
+ eventSource.close();
108
+ shutdown();
109
+ };
110
+
111
+ yield* Effect.addFinalizer(() =>
112
+ Effect.sync(() => {
113
+ eventSource.close();
114
+ shutdown();
115
+ }),
116
+ );
117
+
118
+ yield* Effect.never;
119
+ }),
120
+ );
121
+
122
+ return wrap(stream);
123
+ };
124
+
125
+ /**
126
+ * Creates a Wire from a WebSocket connection.
127
+ * The socket is closed when the stream finalizes.
128
+ *
129
+ * @example
130
+ * ```ts
131
+ * const wire = Wire.fromWebSocket('wss://example.com/ws');
132
+ * const messages = wire.map(evt => evt.data as string);
133
+ * await Effect.runPromise(Wire.runForEach(messages, m => Effect.log(m)));
134
+ * ```
135
+ */
136
+ const _fromWebSocket = (url: string, protocols?: string | string[]): WireShape<MessageEvent, Error> => {
137
+ const stream = Stream.callback<MessageEvent, Error>((queue) =>
138
+ Effect.gen(function* () {
139
+ const ws = new WebSocket(url, protocols);
140
+ let closed = false;
141
+
142
+ const shutdown = (): void => {
143
+ if (closed) return;
144
+ closed = true;
145
+ Queue.shutdown(queue).pipe(Effect.runSync);
146
+ };
147
+
148
+ ws.onmessage = (event) => {
149
+ Queue.offerUnsafe(queue, event);
150
+ };
151
+
152
+ ws.onerror = () => {
153
+ shutdown();
154
+ };
155
+
156
+ ws.onclose = () => {
157
+ shutdown();
158
+ };
159
+
160
+ yield* Effect.addFinalizer(() =>
161
+ Effect.sync(() => {
162
+ if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
163
+ ws.close();
164
+ }
165
+ shutdown();
166
+ }),
167
+ );
168
+
169
+ yield* Effect.never;
170
+ }),
171
+ );
172
+
173
+ return wrap(stream);
174
+ };
175
+
176
+ /**
177
+ * Creates a Wire from any AsyncIterable source.
178
+ *
179
+ * @example
180
+ * ```ts
181
+ * async function* gen() { yield 1; yield 2; yield 3; }
182
+ * const wire = Wire.fromAsyncIterable(gen());
183
+ * const results = await Effect.runPromise(wire.runCollect()); // [1, 2, 3]
184
+ * ```
185
+ */
186
+ const _fromAsyncIterable = <T>(iterable: AsyncIterable<T>): WireShape<T, Error> => {
187
+ const stream = Stream.fromAsyncIterable(iterable, (e) => (e instanceof Error ? e : new Error(String(e))));
188
+ return wrap(stream);
189
+ };
190
+
191
+ /**
192
+ * Zips two Wires into a Wire of tuples, pairing elements pairwise.
193
+ *
194
+ * @example
195
+ * ```ts
196
+ * const a = Wire.from(Stream.make(1, 2));
197
+ * const b = Wire.from(Stream.make('a', 'b'));
198
+ * const zipped = Wire.zip(a, b);
199
+ * const results = Effect.runSync(zipped.runCollect()); // [[1,'a'], [2,'b']]
200
+ * ```
201
+ */
202
+ const _zip = <A, B>(a: WireShape<A>, b: WireShape<B>): WireShape<readonly [A, B]> =>
203
+ wrap(Stream.zip(a.stream, b.stream));
204
+
205
+ /**
206
+ * Merges multiple Wires into a single Wire, interleaving their emissions.
207
+ *
208
+ * @example
209
+ * ```ts
210
+ * const a = Wire.from(Stream.make(1, 2));
211
+ * const b = Wire.from(Stream.make(3, 4));
212
+ * const merged = Wire.merge([a, b]);
213
+ * const results = Effect.runSync(merged.runCollect()); // [1, 2, 3, 4] (order varies)
214
+ * ```
215
+ */
216
+ const _merge = <T, E>(streams: ReadonlyArray<WireShape<T, E>>): WireShape<T, E> => {
217
+ const effectStreams = streams.map((s) => s.stream);
218
+ const merged = Stream.mergeAll(effectStreams, { concurrency: 'unbounded' });
219
+ return wrap(merged);
220
+ };
221
+
222
+ /**
223
+ * Collects all values from a Wire into an array.
224
+ *
225
+ * @example
226
+ * ```ts
227
+ * const wire = Wire.from(Stream.make(10, 20, 30));
228
+ * const values = Effect.runSync(Wire.runCollect(wire)); // [10, 20, 30]
229
+ * ```
230
+ */
231
+ const _runCollect = <T, E>(stream: WireShape<T, E>): Effect.Effect<ReadonlyArray<T>, E> =>
232
+ Effect.map(Stream.runCollect(stream.stream), (chunk) => Array.from(chunk));
233
+
234
+ /**
235
+ * Runs an effectful function for each value emitted by the Wire.
236
+ *
237
+ * @example
238
+ * ```ts
239
+ * const wire = Wire.from(Stream.make('hello', 'world'));
240
+ * await Effect.runPromise(Wire.runForEach(wire, s => Effect.log(s)));
241
+ * // Logs: hello, world
242
+ * ```
243
+ */
244
+ const _runForEach = <T, SE, E, R>(
245
+ stream: WireShape<T, SE>,
246
+ fn: (t: T) => Effect.Effect<void, E, R>,
247
+ ): Effect.Effect<void, SE | E, R> => Stream.runForEach(stream.stream, fn);
248
+
249
+ /**
250
+ * Wire -- fluent stream wrapper with chainable operators for map, filter,
251
+ * scan, debounce, throttle, merge, and more. Wraps Effect Streams.
252
+ *
253
+ * @example
254
+ * ```ts
255
+ * const wire = Wire.from(Stream.make(1, 2, 3, 4, 5));
256
+ * const result = wire.filter(n => n > 2).map(n => n * 10);
257
+ * const values = Effect.runSync(result.runCollect()); // [30, 40, 50]
258
+ * ```
259
+ */
260
+ export const Wire = {
261
+ from: _from,
262
+ fromSSE: _fromSSE,
263
+ fromWebSocket: _fromWebSocket,
264
+ fromAsyncIterable: _fromAsyncIterable,
265
+ zip: _zip,
266
+ merge: _merge,
267
+ runCollect: _runCollect,
268
+ runForEach: _runForEach,
269
+ };
270
+
271
+ export declare namespace Wire {
272
+ /** Structural shape of a {@link Wire}: a fluent wrapper over `Stream.Stream<T, E>`. */
273
+ export type Shape<T, E = never> = WireShape<T, E>;
274
+ }
package/src/zap.ts ADDED
@@ -0,0 +1,241 @@
1
+ /**
2
+ * Zap<T> -- push-based event channel via PubSub.
3
+ *
4
+ * @module
5
+ */
6
+
7
+ import { Effect, Stream, PubSub, Fiber, Duration } from 'effect';
8
+ import type { Scope } from 'effect';
9
+ import type { Millis } from './brands.js';
10
+
11
+ interface ZapShape<T> {
12
+ readonly _tag: 'Zap';
13
+ readonly stream: Stream.Stream<T>;
14
+ emit(value: T): Effect.Effect<void>;
15
+ }
16
+
17
+ /**
18
+ * Creates a new push-based event channel backed by an unbounded PubSub.
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * const zap = await Effect.runPromise(Effect.scoped(Zap.make<number>()));
23
+ * Effect.runSync(zap.emit(42));
24
+ * // Subscribers on zap.stream will receive 42
25
+ * ```
26
+ */
27
+ const _make = <T>(): Effect.Effect<ZapShape<T>, never, Scope.Scope> =>
28
+ Effect.gen(function* () {
29
+ const pubsub = yield* PubSub.unbounded<T>();
30
+
31
+ yield* Effect.addFinalizer(() => PubSub.shutdown(pubsub));
32
+
33
+ return {
34
+ _tag: 'Zap' as const,
35
+ stream: Stream.fromPubSub(pubsub),
36
+ emit: (value: T) => PubSub.publish(pubsub, value),
37
+ };
38
+ });
39
+
40
+ /**
41
+ * Creates a Zap from a DOM event, auto-managing listener lifecycle via Scope.
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * const program = Effect.scoped(Effect.gen(function* () {
46
+ * const btn = document.getElementById('btn');
47
+ * if (!(btn instanceof HTMLElement)) return;
48
+ * const clicks = yield* Zap.fromDOMEvent(btn, 'click');
49
+ * // clicks.stream emits MouseEvents; listener removed when scope closes
50
+ * }));
51
+ * ```
52
+ */
53
+ const _fromDOMEvent = <K extends keyof HTMLElementEventMap>(
54
+ element: HTMLElement,
55
+ event: K,
56
+ ): Effect.Effect<ZapShape<HTMLElementEventMap[K]>, never, Scope.Scope> =>
57
+ Effect.gen(function* () {
58
+ const zap = yield* _make<HTMLElementEventMap[K]>();
59
+
60
+ const listener = (e: HTMLElementEventMap[K]): void => {
61
+ Effect.runSync(zap.emit(e));
62
+ };
63
+
64
+ yield* Effect.acquireRelease(
65
+ Effect.sync(() => element.addEventListener(event, listener)),
66
+ () => Effect.sync(() => element.removeEventListener(event, listener)),
67
+ );
68
+
69
+ return zap;
70
+ });
71
+
72
+ /**
73
+ * Merges multiple Zaps of the same type into a single Zap.
74
+ *
75
+ * @example
76
+ * ```ts
77
+ * const program = Effect.scoped(Effect.gen(function* () {
78
+ * const a = yield* Zap.make<number>();
79
+ * const b = yield* Zap.make<number>();
80
+ * const merged = yield* Zap.merge([a, b]);
81
+ * // merged.stream receives events from both a and b
82
+ * }));
83
+ * ```
84
+ */
85
+ const _merge = <T>(events: ReadonlyArray<ZapShape<T>>): Effect.Effect<ZapShape<T>, never, Scope.Scope> =>
86
+ Effect.gen(function* () {
87
+ const merged = yield* _make<T>();
88
+
89
+ yield* Effect.forkScoped(
90
+ Effect.all(
91
+ events.map((event) => Stream.runForEach(event.stream, (value) => merged.emit(value))),
92
+ { concurrency: 'unbounded' },
93
+ ),
94
+ );
95
+
96
+ return merged;
97
+ });
98
+
99
+ /**
100
+ * Transforms each value emitted by a Zap through a mapping function.
101
+ *
102
+ * @example
103
+ * ```ts
104
+ * const program = Effect.scoped(Effect.gen(function* () {
105
+ * const nums = yield* Zap.make<number>();
106
+ * const strs = yield* Zap.map(nums, n => `value: ${n}`);
107
+ * // strs.stream emits transformed strings
108
+ * }));
109
+ * ```
110
+ */
111
+ const _map = <A, B>(event: ZapShape<A>, f: (a: A) => B): Effect.Effect<ZapShape<B>, never, Scope.Scope> =>
112
+ Effect.gen(function* () {
113
+ const mapped = yield* _make<B>();
114
+
115
+ yield* Effect.forkScoped(Stream.runForEach(event.stream, (value) => mapped.emit(f(value))));
116
+
117
+ return mapped;
118
+ });
119
+
120
+ /**
121
+ * Filters a Zap, only forwarding values that satisfy the predicate.
122
+ *
123
+ * @example
124
+ * ```ts
125
+ * const program = Effect.scoped(Effect.gen(function* () {
126
+ * const nums = yield* Zap.make<number>();
127
+ * const evens = yield* Zap.filter(nums, n => n % 2 === 0);
128
+ * // evens.stream only receives even numbers
129
+ * }));
130
+ * ```
131
+ */
132
+ const _filter = <T>(
133
+ event: ZapShape<T>,
134
+ predicate: (value: T) => boolean,
135
+ ): Effect.Effect<ZapShape<T>, never, Scope.Scope> =>
136
+ Effect.gen(function* () {
137
+ const filtered = yield* _make<T>();
138
+
139
+ yield* Effect.forkScoped(
140
+ Stream.runForEach(event.stream, (value) => (predicate(value) ? filtered.emit(value) : Effect.void)),
141
+ );
142
+
143
+ return filtered;
144
+ });
145
+
146
+ /**
147
+ * Debounces a Zap, only emitting after `ms` milliseconds of silence.
148
+ *
149
+ * @example
150
+ * ```ts
151
+ * const program = Effect.scoped(Effect.gen(function* () {
152
+ * const input = yield* Zap.make<string>();
153
+ * const debounced = yield* Zap.debounce(input, Millis(300));
154
+ * // debounced.stream emits only after 300ms pause in input
155
+ * }));
156
+ * ```
157
+ */
158
+ const _debounce = <T>(event: ZapShape<T>, ms: Millis): Effect.Effect<ZapShape<T>, never, Scope.Scope> =>
159
+ Effect.gen(function* () {
160
+ const debounced = yield* _make<T>();
161
+ let pendingFiber: Fiber.Fiber<void> | null = null;
162
+
163
+ yield* Effect.forkScoped(
164
+ Stream.runForEach(event.stream, (value) =>
165
+ Effect.gen(function* () {
166
+ if (pendingFiber) {
167
+ yield* Fiber.interrupt(pendingFiber);
168
+ }
169
+ pendingFiber = yield* Effect.forkChild(
170
+ Effect.gen(function* () {
171
+ yield* Effect.sleep(Duration.millis(ms));
172
+ yield* debounced.emit(value);
173
+ }),
174
+ );
175
+ }),
176
+ ),
177
+ );
178
+
179
+ return debounced;
180
+ });
181
+
182
+ /**
183
+ * Throttles a Zap, allowing at most one emission per `ms` milliseconds.
184
+ *
185
+ * @example
186
+ * ```ts
187
+ * const program = Effect.scoped(Effect.gen(function* () {
188
+ * const scroll = yield* Zap.make<number>();
189
+ * const throttled = yield* Zap.throttle(scroll, Millis(16));
190
+ * // throttled.stream emits at most once every 16ms (~60fps)
191
+ * }));
192
+ * ```
193
+ */
194
+ const _throttle = <T>(event: ZapShape<T>, ms: Millis): Effect.Effect<ZapShape<T>, never, Scope.Scope> =>
195
+ Effect.gen(function* () {
196
+ const throttled = yield* _make<T>();
197
+ let lastEmitTime = 0;
198
+
199
+ yield* Effect.forkScoped(
200
+ Stream.runForEach(event.stream, (value) =>
201
+ Effect.gen(function* () {
202
+ const now = Date.now();
203
+ if (now - lastEmitTime >= ms) {
204
+ lastEmitTime = now;
205
+ yield* throttled.emit(value);
206
+ }
207
+ }),
208
+ ),
209
+ );
210
+
211
+ return throttled;
212
+ });
213
+
214
+ /**
215
+ * Zap -- push-based event channel backed by Effect PubSub.
216
+ * Provides reactive event streams with map, filter, merge, debounce, and throttle.
217
+ *
218
+ * @example
219
+ * ```ts
220
+ * const program = Effect.scoped(Effect.gen(function* () {
221
+ * const zap = yield* Zap.make<number>();
222
+ * const doubled = yield* Zap.map(zap, n => n * 2);
223
+ * yield* zap.emit(5);
224
+ * // doubled.stream receives 10
225
+ * }));
226
+ * ```
227
+ */
228
+ export const Zap = {
229
+ make: _make,
230
+ fromDOMEvent: _fromDOMEvent,
231
+ merge: _merge,
232
+ map: _map,
233
+ filter: _filter,
234
+ debounce: _debounce,
235
+ throttle: _throttle,
236
+ };
237
+
238
+ export declare namespace Zap {
239
+ /** Structural shape of a {@link Zap}: event-sourced reactive primitive exposing a discrete stream. */
240
+ export type Shape<T> = ZapShape<T>;
241
+ }