@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.
- package/LICENSE +21 -0
- package/README.md +19 -0
- package/dist/addressed-digest.d.ts +15 -0
- package/dist/addressed-digest.d.ts.map +1 -0
- package/dist/addressed-digest.js +35 -0
- package/dist/addressed-digest.js.map +1 -0
- package/dist/animation.d.ts +46 -0
- package/dist/animation.d.ts.map +1 -0
- package/dist/animation.js +70 -0
- package/dist/animation.js.map +1 -0
- package/dist/assembly.d.ts +25 -0
- package/dist/assembly.d.ts.map +1 -0
- package/dist/assembly.js +58 -0
- package/dist/assembly.js.map +1 -0
- package/dist/av-bridge.d.ts +74 -0
- package/dist/av-bridge.d.ts.map +1 -0
- package/dist/av-bridge.js +107 -0
- package/dist/av-bridge.js.map +1 -0
- package/dist/av-renderer.d.ts +56 -0
- package/dist/av-renderer.d.ts.map +1 -0
- package/dist/av-renderer.js +65 -0
- package/dist/av-renderer.js.map +1 -0
- package/dist/blend.d.ts +61 -0
- package/dist/blend.d.ts.map +1 -0
- package/dist/blend.js +100 -0
- package/dist/blend.js.map +1 -0
- package/dist/boundary.d.ts +154 -0
- package/dist/boundary.d.ts.map +1 -0
- package/dist/boundary.js +269 -0
- package/dist/boundary.js.map +1 -0
- package/dist/brands.d.ts +63 -0
- package/dist/brands.d.ts.map +1 -0
- package/dist/brands.js +31 -0
- package/dist/brands.js.map +1 -0
- package/dist/caps.d.ts +49 -0
- package/dist/caps.d.ts.map +1 -0
- package/dist/caps.js +73 -0
- package/dist/caps.js.map +1 -0
- package/dist/capsule.d.ts +77 -0
- package/dist/capsule.d.ts.map +1 -0
- package/dist/capsule.js +18 -0
- package/dist/capsule.js.map +1 -0
- package/dist/capsules/boundary-evaluate.d.ts +28 -0
- package/dist/capsules/boundary-evaluate.d.ts.map +1 -0
- package/dist/capsules/boundary-evaluate.js +117 -0
- package/dist/capsules/boundary-evaluate.js.map +1 -0
- package/dist/capsules/canonical-cbor.d.ts +13 -0
- package/dist/capsules/canonical-cbor.d.ts.map +1 -0
- package/dist/capsules/canonical-cbor.js +60 -0
- package/dist/capsules/canonical-cbor.js.map +1 -0
- package/dist/capsules/token-buffer.d.ts +24 -0
- package/dist/capsules/token-buffer.d.ts.map +1 -0
- package/dist/capsules/token-buffer.js +53 -0
- package/dist/capsules/token-buffer.js.map +1 -0
- package/dist/capture.d.ts +40 -0
- package/dist/capture.d.ts.map +1 -0
- package/dist/capture.js +10 -0
- package/dist/capture.js.map +1 -0
- package/dist/cbor.d.ts +33 -0
- package/dist/cbor.d.ts.map +1 -0
- package/dist/cbor.js +179 -0
- package/dist/cbor.js.map +1 -0
- package/dist/cell.d.ts +53 -0
- package/dist/cell.d.ts.map +1 -0
- package/dist/cell.js +83 -0
- package/dist/cell.js.map +1 -0
- package/dist/codec.d.ts +30 -0
- package/dist/codec.d.ts.map +1 -0
- package/dist/codec.js +25 -0
- package/dist/codec.js.map +1 -0
- package/dist/component.d.ts +52 -0
- package/dist/component.d.ts.map +1 -0
- package/dist/component.js +44 -0
- package/dist/component.js.map +1 -0
- package/dist/composable.d.ts +76 -0
- package/dist/composable.d.ts.map +1 -0
- package/dist/composable.js +221 -0
- package/dist/composable.js.map +1 -0
- package/dist/compositor-pool.d.ts +74 -0
- package/dist/compositor-pool.d.ts.map +1 -0
- package/dist/compositor-pool.js +119 -0
- package/dist/compositor-pool.js.map +1 -0
- package/dist/compositor.d.ts +90 -0
- package/dist/compositor.d.ts.map +1 -0
- package/dist/compositor.js +278 -0
- package/dist/compositor.js.map +1 -0
- package/dist/config.d.ts +72 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +97 -0
- package/dist/config.js.map +1 -0
- package/dist/dag.d.ts +251 -0
- package/dist/dag.d.ts.map +1 -0
- package/dist/dag.js +450 -0
- package/dist/dag.js.map +1 -0
- package/dist/defaults.d.ts +45 -0
- package/dist/defaults.d.ts.map +1 -0
- package/dist/defaults.js +45 -0
- package/dist/defaults.js.map +1 -0
- package/dist/derived.d.ts +34 -0
- package/dist/derived.d.ts.map +1 -0
- package/dist/derived.js +101 -0
- package/dist/derived.js.map +1 -0
- package/dist/diagnostics.d.ts +77 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +122 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/dirty.d.ts +55 -0
- package/dist/dirty.d.ts.map +1 -0
- package/dist/dirty.js +80 -0
- package/dist/dirty.js.map +1 -0
- package/dist/easing.d.ts +55 -0
- package/dist/easing.d.ts.map +1 -0
- package/dist/easing.js +291 -0
- package/dist/easing.js.map +1 -0
- package/dist/ecs.d.ts +105 -0
- package/dist/ecs.d.ts.map +1 -0
- package/dist/ecs.js +245 -0
- package/dist/ecs.js.map +1 -0
- package/dist/fnv.d.ts +14 -0
- package/dist/fnv.d.ts.map +1 -0
- package/dist/fnv.js +28 -0
- package/dist/fnv.js.map +1 -0
- package/dist/frame-budget.d.ts +73 -0
- package/dist/frame-budget.d.ts.map +1 -0
- package/dist/frame-budget.js +114 -0
- package/dist/frame-budget.js.map +1 -0
- package/dist/gen-frame.d.ts +102 -0
- package/dist/gen-frame.d.ts.map +1 -0
- package/dist/gen-frame.js +121 -0
- package/dist/gen-frame.js.map +1 -0
- package/dist/harness/arbitrary-from-schema.d.ts +28 -0
- package/dist/harness/arbitrary-from-schema.d.ts.map +1 -0
- package/dist/harness/arbitrary-from-schema.js +262 -0
- package/dist/harness/arbitrary-from-schema.js.map +1 -0
- package/dist/harness/cached-projection.d.ts +19 -0
- package/dist/harness/cached-projection.d.ts.map +1 -0
- package/dist/harness/cached-projection.js +39 -0
- package/dist/harness/cached-projection.js.map +1 -0
- package/dist/harness/index.d.ts +16 -0
- package/dist/harness/index.d.ts.map +1 -0
- package/dist/harness/index.js +15 -0
- package/dist/harness/index.js.map +1 -0
- package/dist/harness/policy-gate.d.ts +18 -0
- package/dist/harness/policy-gate.d.ts.map +1 -0
- package/dist/harness/policy-gate.js +46 -0
- package/dist/harness/policy-gate.js.map +1 -0
- package/dist/harness/pure-transform.d.ts +42 -0
- package/dist/harness/pure-transform.d.ts.map +1 -0
- package/dist/harness/pure-transform.js +76 -0
- package/dist/harness/pure-transform.js.map +1 -0
- package/dist/harness/receipted-mutation.d.ts +23 -0
- package/dist/harness/receipted-mutation.d.ts.map +1 -0
- package/dist/harness/receipted-mutation.js +52 -0
- package/dist/harness/receipted-mutation.js.map +1 -0
- package/dist/harness/scene-composition.d.ts +19 -0
- package/dist/harness/scene-composition.d.ts.map +1 -0
- package/dist/harness/scene-composition.js +47 -0
- package/dist/harness/scene-composition.js.map +1 -0
- package/dist/harness/site-adapter.d.ts +18 -0
- package/dist/harness/site-adapter.d.ts.map +1 -0
- package/dist/harness/site-adapter.js +38 -0
- package/dist/harness/site-adapter.js.map +1 -0
- package/dist/harness/state-machine.d.ts +19 -0
- package/dist/harness/state-machine.d.ts.map +1 -0
- package/dist/harness/state-machine.js +44 -0
- package/dist/harness/state-machine.js.map +1 -0
- package/dist/hlc.d.ts +99 -0
- package/dist/hlc.d.ts.map +1 -0
- package/dist/hlc.js +219 -0
- package/dist/hlc.js.map +1 -0
- package/dist/index.d.ts +104 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +137 -0
- package/dist/index.js.map +1 -0
- package/dist/interpolate.d.ts +14 -0
- package/dist/interpolate.d.ts.map +1 -0
- package/dist/interpolate.js +31 -0
- package/dist/interpolate.js.map +1 -0
- package/dist/live-cell.d.ts +46 -0
- package/dist/live-cell.d.ts.map +1 -0
- package/dist/live-cell.js +154 -0
- package/dist/live-cell.js.map +1 -0
- package/dist/op.d.ts +58 -0
- package/dist/op.d.ts.map +1 -0
- package/dist/op.js +171 -0
- package/dist/op.js.map +1 -0
- package/dist/plan.d.ts +195 -0
- package/dist/plan.d.ts.map +1 -0
- package/dist/plan.js +211 -0
- package/dist/plan.js.map +1 -0
- package/dist/protocol.d.ts +33 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +10 -0
- package/dist/protocol.js.map +1 -0
- package/dist/quantizer-types.d.ts +28 -0
- package/dist/quantizer-types.d.ts.map +1 -0
- package/dist/quantizer-types.js +9 -0
- package/dist/quantizer-types.js.map +1 -0
- package/dist/receipt.d.ts +294 -0
- package/dist/receipt.d.ts.map +1 -0
- package/dist/receipt.js +352 -0
- package/dist/receipt.js.map +1 -0
- package/dist/runtime-coordinator.d.ts +75 -0
- package/dist/runtime-coordinator.d.ts.map +1 -0
- package/dist/runtime-coordinator.js +149 -0
- package/dist/runtime-coordinator.js.map +1 -0
- package/dist/scheduler.d.ts +58 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +109 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/ship-capsule.d.ts +54 -0
- package/dist/ship-capsule.d.ts.map +1 -0
- package/dist/ship-capsule.js +142 -0
- package/dist/ship-capsule.js.map +1 -0
- package/dist/ship-manifest.d.ts +45 -0
- package/dist/ship-manifest.d.ts.map +1 -0
- package/dist/ship-manifest.js +175 -0
- package/dist/ship-manifest.js.map +1 -0
- package/dist/signal.d.ts +149 -0
- package/dist/signal.d.ts.map +1 -0
- package/dist/signal.js +277 -0
- package/dist/signal.js.map +1 -0
- package/dist/speculative.d.ts +67 -0
- package/dist/speculative.d.ts.map +1 -0
- package/dist/speculative.js +139 -0
- package/dist/speculative.js.map +1 -0
- package/dist/store.d.ts +39 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +42 -0
- package/dist/store.js.map +1 -0
- package/dist/style.d.ts +119 -0
- package/dist/style.d.ts.map +1 -0
- package/dist/style.js +168 -0
- package/dist/style.js.map +1 -0
- package/dist/testing.d.ts +14 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +14 -0
- package/dist/testing.js.map +1 -0
- package/dist/theme.d.ts +78 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +109 -0
- package/dist/theme.js.map +1 -0
- package/dist/timeline.d.ts +45 -0
- package/dist/timeline.d.ts.map +1 -0
- package/dist/timeline.js +101 -0
- package/dist/timeline.js.map +1 -0
- package/dist/token-buffer.d.ts +43 -0
- package/dist/token-buffer.d.ts.map +1 -0
- package/dist/token-buffer.js +112 -0
- package/dist/token-buffer.js.map +1 -0
- package/dist/token.d.ts +107 -0
- package/dist/token.d.ts.map +1 -0
- package/dist/token.js +143 -0
- package/dist/token.js.map +1 -0
- package/dist/tuple.d.ts +16 -0
- package/dist/tuple.d.ts.map +1 -0
- package/dist/tuple.js +16 -0
- package/dist/tuple.js.map +1 -0
- package/dist/type-utils.d.ts +41 -0
- package/dist/type-utils.d.ts.map +1 -0
- package/dist/type-utils.js +10 -0
- package/dist/type-utils.js.map +1 -0
- package/dist/typed-ref.d.ts +50 -0
- package/dist/typed-ref.d.ts.map +1 -0
- package/dist/typed-ref.js +59 -0
- package/dist/typed-ref.js.map +1 -0
- package/dist/ui-quality.d.ts +50 -0
- package/dist/ui-quality.d.ts.map +1 -0
- package/dist/ui-quality.js +64 -0
- package/dist/ui-quality.js.map +1 -0
- package/dist/validation-error.d.ts +25 -0
- package/dist/validation-error.d.ts.map +1 -0
- package/dist/validation-error.js +32 -0
- package/dist/validation-error.js.map +1 -0
- package/dist/vector-clock.d.ts +46 -0
- package/dist/vector-clock.d.ts.map +1 -0
- package/dist/vector-clock.js +91 -0
- package/dist/vector-clock.js.map +1 -0
- package/dist/video.d.ts +62 -0
- package/dist/video.d.ts.map +1 -0
- package/dist/video.js +59 -0
- package/dist/video.js.map +1 -0
- package/dist/wasm-dispatch.d.ts +52 -0
- package/dist/wasm-dispatch.d.ts.map +1 -0
- package/dist/wasm-dispatch.js +204 -0
- package/dist/wasm-dispatch.js.map +1 -0
- package/dist/wasm-fallback.d.ts +19 -0
- package/dist/wasm-fallback.d.ts.map +1 -0
- package/dist/wasm-fallback.js +93 -0
- package/dist/wasm-fallback.js.map +1 -0
- package/dist/wire.d.ts +49 -0
- package/dist/wire.d.ts.map +1 -0
- package/dist/wire.js +201 -0
- package/dist/wire.js.map +1 -0
- package/dist/zap.d.ts +42 -0
- package/dist/zap.d.ts.map +1 -0
- package/dist/zap.js +172 -0
- package/dist/zap.js.map +1 -0
- package/package.json +71 -0
- package/src/addressed-digest.ts +48 -0
- package/src/animation.ts +103 -0
- package/src/assembly.ts +76 -0
- package/src/av-bridge.ts +161 -0
- package/src/av-renderer.ts +118 -0
- package/src/blend.ts +135 -0
- package/src/boundary.ts +363 -0
- package/src/brands.ts +86 -0
- package/src/caps.ts +100 -0
- package/src/capsule.ts +95 -0
- package/src/capsules/boundary-evaluate.ts +128 -0
- package/src/capsules/canonical-cbor.ts +60 -0
- package/src/capsules/token-buffer.ts +57 -0
- package/src/capture.ts +48 -0
- package/src/cbor.ts +199 -0
- package/src/cell.ts +130 -0
- package/src/codec.ts +39 -0
- package/src/component.ts +102 -0
- package/src/composable.ts +328 -0
- package/src/compositor-pool.ts +162 -0
- package/src/compositor.ts +387 -0
- package/src/config.ts +157 -0
- package/src/dag.ts +527 -0
- package/src/defaults.ts +60 -0
- package/src/derived.ts +164 -0
- package/src/diagnostics.ts +186 -0
- package/src/dirty.ts +101 -0
- package/src/easing.ts +334 -0
- package/src/ecs.ts +382 -0
- package/src/fnv.ts +31 -0
- package/src/frame-budget.ts +149 -0
- package/src/gen-frame.ts +229 -0
- package/src/harness/arbitrary-from-schema.ts +270 -0
- package/src/harness/cached-projection.ts +46 -0
- package/src/harness/index.ts +16 -0
- package/src/harness/policy-gate.ts +51 -0
- package/src/harness/pure-transform.ts +121 -0
- package/src/harness/receipted-mutation.ts +59 -0
- package/src/harness/scene-composition.ts +54 -0
- package/src/harness/site-adapter.ts +43 -0
- package/src/harness/state-machine.ts +49 -0
- package/src/hlc.ts +238 -0
- package/src/index.ts +274 -0
- package/src/interpolate.ts +37 -0
- package/src/live-cell.ts +199 -0
- package/src/op.ts +233 -0
- package/src/plan.ts +317 -0
- package/src/protocol.ts +49 -0
- package/src/quantizer-types.ts +29 -0
- package/src/receipt.ts +444 -0
- package/src/runtime-coordinator.ts +230 -0
- package/src/scheduler.ts +161 -0
- package/src/ship-capsule.ts +191 -0
- package/src/signal.ts +345 -0
- package/src/speculative.ts +186 -0
- package/src/store.ts +77 -0
- package/src/style.ts +249 -0
- package/src/testing.ts +14 -0
- package/src/theme.ts +153 -0
- package/src/timeline.ts +146 -0
- package/src/token-buffer.ts +151 -0
- package/src/token.ts +197 -0
- package/src/tuple.ts +19 -0
- package/src/type-utils.ts +48 -0
- package/src/typed-ref.ts +79 -0
- package/src/ui-quality.ts +105 -0
- package/src/validation-error.ts +34 -0
- package/src/vector-clock.ts +111 -0
- package/src/video.ts +106 -0
- package/src/wasm-dispatch.ts +300 -0
- package/src/wasm-fallback.ts +102 -0
- package/src/wire.ts +274 -0
- package/src/zap.ts +241 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ComponentDef -- adaptive component primitive for constraint-based rendering.
|
|
3
|
+
*
|
|
4
|
+
* A component binds a boundary, styles, and named slots into a single
|
|
5
|
+
* content-addressed unit. Content-addressed via FNV-1a.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import { CanonicalCbor } from './cbor.js';
|
|
10
|
+
import { fnv1aBytes } from './fnv.js';
|
|
11
|
+
function deterministicId(name, boundaryId, stylesId, slots, defaultSlot) {
|
|
12
|
+
return fnv1aBytes(CanonicalCbor.encode({
|
|
13
|
+
_tag: 'ComponentDef',
|
|
14
|
+
_version: 1,
|
|
15
|
+
name,
|
|
16
|
+
boundaryId: boundaryId ?? null,
|
|
17
|
+
stylesId,
|
|
18
|
+
slots,
|
|
19
|
+
defaultSlot: defaultSlot ?? null,
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Component — the content-addressed unit that binds a {@link Boundary}, a
|
|
24
|
+
* {@link Style}, and named slots into a single declaration compilers can
|
|
25
|
+
* target. The optional boundary gates style variants; the slots describe
|
|
26
|
+
* the consumer-facing API.
|
|
27
|
+
*/
|
|
28
|
+
export const Component = {
|
|
29
|
+
make(config) {
|
|
30
|
+
const id = deterministicId(config.name, config.boundary?.id, config.styles.id, config.slots, config.defaultSlot);
|
|
31
|
+
const def = {
|
|
32
|
+
_tag: 'ComponentDef',
|
|
33
|
+
_version: 1,
|
|
34
|
+
id,
|
|
35
|
+
name: config.name,
|
|
36
|
+
...(config.boundary !== undefined ? { boundary: config.boundary } : {}),
|
|
37
|
+
styles: config.styles,
|
|
38
|
+
slots: config.slots,
|
|
39
|
+
...(config.defaultSlot !== undefined ? { defaultSlot: config.defaultSlot } : {}),
|
|
40
|
+
};
|
|
41
|
+
return Object.freeze(def);
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component.js","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAgCtC,SAAS,eAAe,CACtB,IAAY,EACZ,UAA8B,EAC9B,QAAgB,EAChB,KAAwD,EACxD,WAAoB;IAEpB,OAAO,UAAU,CACf,aAAa,CAAC,MAAM,CAAC;QACnB,IAAI,EAAE,cAAc;QACpB,QAAQ,EAAE,CAAC;QACX,IAAI;QACJ,UAAU,EAAE,UAAU,IAAI,IAAI;QAC9B,QAAQ;QACR,KAAK;QACL,WAAW,EAAE,WAAW,IAAI,IAAI;KACjC,CAAC,CACH,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,SAAS,GAAqB;IACzC,IAAI,CAA4E,MAM/E;QACC,MAAM,EAAE,GAAG,eAAe,CAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,QAAQ,EAAE,EAAE,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;QAErH,MAAM,GAAG,GAAwB;YAC/B,IAAI,EAAE,cAAc;YACpB,QAAQ,EAAE,CAAC;YACX,EAAE;YACF,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,GAAG,CAAC,MAAM,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACvE,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,GAAG,CAAC,MAAM,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACjF,CAAC;QACF,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composable -- ECS Composition over Existing Primitives
|
|
3
|
+
*
|
|
4
|
+
* Universal composition API leveraging existing deterministic primitives.
|
|
5
|
+
* Zero boilerplate, type-safe, content-addressed entity composition.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import type { ContentAddress } from './brands.js';
|
|
10
|
+
import type { Token } from './token.js';
|
|
11
|
+
import type { Style } from './style.js';
|
|
12
|
+
import type { World } from './ecs.js';
|
|
13
|
+
import { Boundary } from './boundary.js';
|
|
14
|
+
import { Part } from './ecs.js';
|
|
15
|
+
import { Effect } from 'effect';
|
|
16
|
+
/**
|
|
17
|
+
* Component map for a {@link ComposableEntity} — well-known slots for czap
|
|
18
|
+
* primitives plus arbitrary user-defined keys.
|
|
19
|
+
*/
|
|
20
|
+
export interface EntityComponents {
|
|
21
|
+
readonly boundary?: Boundary.Shape;
|
|
22
|
+
readonly token?: Token.Shape;
|
|
23
|
+
readonly style?: Style.Shape;
|
|
24
|
+
readonly [key: string]: unknown;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Content-addressed entity: the identity is an FNV-1a hash over its components,
|
|
28
|
+
* so two entities with structurally equal components share the same `id`.
|
|
29
|
+
*/
|
|
30
|
+
export interface ComposableEntity<T extends EntityComponents = EntityComponents> {
|
|
31
|
+
readonly id: ContentAddress;
|
|
32
|
+
readonly components: T;
|
|
33
|
+
readonly _tag: 'ComposableEntity';
|
|
34
|
+
}
|
|
35
|
+
interface ComposableFactory {
|
|
36
|
+
make<T extends EntityComponents>(components: T): ComposableEntity<T>;
|
|
37
|
+
compose<T extends EntityComponents>(entity1: ComposableEntity<T>, entity2: ComposableEntity<T>): ComposableEntity<T>;
|
|
38
|
+
merge<T extends EntityComponents>(...entities: ComposableEntity<T>[]): ComposableEntity<T>;
|
|
39
|
+
}
|
|
40
|
+
interface TypedComposableWorld<Schema extends EntityComponents = EntityComponents> {
|
|
41
|
+
spawn<T extends Schema>(components: T): Effect.Effect<ComposableEntity<T>>;
|
|
42
|
+
spawnWith<T extends Schema>(entity: ComposableEntity<T>): Effect.Effect<ComposableEntity<T>>;
|
|
43
|
+
query<K extends keyof Schema>(...componentTypes: K[]): Effect.Effect<readonly ComposableEntity<Pick<Schema, K>>[]>;
|
|
44
|
+
evaluate<T extends Schema>(entity: ComposableEntity<T>, input: Record<string, number>): Effect.Effect<Record<string, string>>;
|
|
45
|
+
}
|
|
46
|
+
declare function makeComposableWorld<Schema extends EntityComponents = EntityComponents>(world: World.Shape): TypedComposableWorld<Schema>;
|
|
47
|
+
interface ComposableDenseStore {
|
|
48
|
+
create(name: string, capacity: number): Effect.Effect<Part.Dense>;
|
|
49
|
+
store<T extends EntityComponents>(entity: ComposableEntity<T>, value: number): Effect.Effect<void>;
|
|
50
|
+
retrieve<T extends EntityComponents>(entity: ComposableEntity<T>): Effect.Effect<number | undefined>;
|
|
51
|
+
}
|
|
52
|
+
declare function makeComposableDenseStore(world: World.Shape): ComposableDenseStore;
|
|
53
|
+
/**
|
|
54
|
+
* Composable — content-addressed entity algebra over czap primitives.
|
|
55
|
+
*
|
|
56
|
+
* Build entities from a bag of components (boundaries, tokens, styles, …),
|
|
57
|
+
* merge them associatively via `Composable.compose` / `Composable.merge`, and
|
|
58
|
+
* rely on the content address to deduplicate structurally-equal entities.
|
|
59
|
+
*/
|
|
60
|
+
export declare const Composable: ComposableFactory;
|
|
61
|
+
/**
|
|
62
|
+
* Bridge between a raw ECS {@link World} and typed {@link ComposableEntity}
|
|
63
|
+
* operations (`spawn`, `query`, `evaluate`) plus a thin dense-store integration.
|
|
64
|
+
*/
|
|
65
|
+
export declare const ComposableWorld: {
|
|
66
|
+
/** Wrap a {@link World} with the typed composable-entity API. */
|
|
67
|
+
make: typeof makeComposableWorld;
|
|
68
|
+
/** Build a dense-store bridge over a {@link World} for per-entity numeric data. */
|
|
69
|
+
dense: typeof makeComposableDenseStore;
|
|
70
|
+
};
|
|
71
|
+
export declare namespace ComposableWorld {
|
|
72
|
+
/** Structural shape of the typed world returned by {@link ComposableWorld.make}. */
|
|
73
|
+
type Shape<Schema extends EntityComponents = EntityComponents> = TypedComposableWorld<Schema>;
|
|
74
|
+
}
|
|
75
|
+
export type { TypedComposableWorld as ComposableWorldShape };
|
|
76
|
+
//# sourceMappingURL=composable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composable.d.ts","sourceRoot":"","sources":["../src/composable.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composable -- ECS Composition over Existing Primitives
|
|
3
|
+
*
|
|
4
|
+
* Universal composition API leveraging existing deterministic primitives.
|
|
5
|
+
* Zero boilerplate, type-safe, content-addressed entity composition.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import { Token as TokenNS } from './token.js';
|
|
10
|
+
import { Style as StyleNS } from './style.js';
|
|
11
|
+
import { Boundary } from './boundary.js';
|
|
12
|
+
import { Part } from './ecs.js';
|
|
13
|
+
import { fnv1aBytes } from './fnv.js';
|
|
14
|
+
import { TypedRef } from './typed-ref.js';
|
|
15
|
+
import { Effect } from 'effect';
|
|
16
|
+
function makeEntityId(components) {
|
|
17
|
+
const canonical = canonicalizeForAddress(components);
|
|
18
|
+
return fnv1aBytes(TypedRef.canonicalize(canonical));
|
|
19
|
+
}
|
|
20
|
+
function canonicalizeForAddress(value) {
|
|
21
|
+
if (value === undefined) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
if (value === null || typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
if (Array.isArray(value)) {
|
|
28
|
+
return value.map((entry) => {
|
|
29
|
+
const canonical = canonicalizeForAddress(entry);
|
|
30
|
+
return canonical === undefined ? null : canonical;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
if (typeof value === 'object') {
|
|
34
|
+
const entries = Object.entries(value)
|
|
35
|
+
.filter(([, entry]) => entry !== undefined)
|
|
36
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
37
|
+
.map(([key, entry]) => [key, canonicalizeForAddress(entry)]);
|
|
38
|
+
return Object.fromEntries(entries);
|
|
39
|
+
}
|
|
40
|
+
return String(value);
|
|
41
|
+
}
|
|
42
|
+
function _make(components) {
|
|
43
|
+
const id = makeEntityId(components);
|
|
44
|
+
return {
|
|
45
|
+
id,
|
|
46
|
+
components,
|
|
47
|
+
_tag: 'ComposableEntity',
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function _compose(entity1, entity2) {
|
|
51
|
+
// Merge components with entity2 taking precedence
|
|
52
|
+
const merged = { ...entity1.components, ...entity2.components };
|
|
53
|
+
return _make(merged);
|
|
54
|
+
}
|
|
55
|
+
function _merge(...entities) {
|
|
56
|
+
if (entities.length === 0) {
|
|
57
|
+
throw new Error('Cannot merge zero entities');
|
|
58
|
+
}
|
|
59
|
+
const first = entities[0];
|
|
60
|
+
if (!first) {
|
|
61
|
+
throw new Error('First entity is undefined');
|
|
62
|
+
}
|
|
63
|
+
return entities.slice(1).reduce((acc, entity) => _compose(acc, entity), first);
|
|
64
|
+
}
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// ECS Integration
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
/**
|
|
69
|
+
* Convert a runtime `Map<string, unknown>` (from ECS query results) into a typed
|
|
70
|
+
* `Pick<Schema, K>`. The ECS query filters guarantee the required keys are present;
|
|
71
|
+
* this helper contains the one boundary cast where runtime shape joins the type lattice.
|
|
72
|
+
*/
|
|
73
|
+
function entriesToPick(components) {
|
|
74
|
+
return Object.fromEntries(components);
|
|
75
|
+
}
|
|
76
|
+
function makeComposableWorld(world) {
|
|
77
|
+
// Mapping from ContentAddress to ECS EntityId for query reconstruction
|
|
78
|
+
const addressToEntityId = new Map();
|
|
79
|
+
return {
|
|
80
|
+
spawn(components) {
|
|
81
|
+
return Effect.gen(function* () {
|
|
82
|
+
const entity = _make(components);
|
|
83
|
+
const ecsId = yield* world.spawn(components);
|
|
84
|
+
addressToEntityId.set(entity.id, ecsId);
|
|
85
|
+
return entity;
|
|
86
|
+
});
|
|
87
|
+
},
|
|
88
|
+
spawnWith(entity) {
|
|
89
|
+
return Effect.gen(function* () {
|
|
90
|
+
const ecsId = yield* world.spawn(entity.components);
|
|
91
|
+
addressToEntityId.set(entity.id, ecsId);
|
|
92
|
+
return entity;
|
|
93
|
+
});
|
|
94
|
+
},
|
|
95
|
+
query(...componentTypes) {
|
|
96
|
+
return Effect.gen(function* () {
|
|
97
|
+
const names = [...componentTypes].map((k) => String(k)).sort();
|
|
98
|
+
const entities = yield* world.query(...names);
|
|
99
|
+
return [...entities]
|
|
100
|
+
.sort((left, right) => left.id.localeCompare(right.id))
|
|
101
|
+
.map((entityShape) => {
|
|
102
|
+
// world.query guarantees entityShape.components contains at least the K keys
|
|
103
|
+
// that were queried for; convert the runtime Map<string, unknown> to the typed
|
|
104
|
+
// Pick<Schema, K> via a single contained cast (runtime shape is validated by
|
|
105
|
+
// the ECS query filter).
|
|
106
|
+
const components = entriesToPick(entityShape.components);
|
|
107
|
+
return _make(components);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
},
|
|
111
|
+
evaluate(entity, input) {
|
|
112
|
+
return Effect.gen(function* () {
|
|
113
|
+
const results = {};
|
|
114
|
+
// Evaluate boundary component: quantize continuous input to discrete state
|
|
115
|
+
let boundaryState;
|
|
116
|
+
if (entity.components.boundary) {
|
|
117
|
+
const boundary = entity.components.boundary;
|
|
118
|
+
const boundaryInput = input[boundary.input] ?? 0;
|
|
119
|
+
const state = Boundary.evaluate(boundary, boundaryInput);
|
|
120
|
+
results[boundary.input] = state;
|
|
121
|
+
boundaryState = state;
|
|
122
|
+
}
|
|
123
|
+
// Evaluate token component: resolve axis values or fall back
|
|
124
|
+
if (entity.components.token) {
|
|
125
|
+
const token = entity.components.token;
|
|
126
|
+
// Build axis values from input keys. Token.tap expects string axis values,
|
|
127
|
+
// so we convert matching numeric inputs to strings.
|
|
128
|
+
const axisValues = {};
|
|
129
|
+
for (const axis of token.axes) {
|
|
130
|
+
if (axis in input) {
|
|
131
|
+
axisValues[axis] = String(input[axis]);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Use Token.tap for proper axis-key lookup with fallback
|
|
135
|
+
const resolved = TokenNS.tap(token, axisValues);
|
|
136
|
+
results[token.name] = String(resolved);
|
|
137
|
+
}
|
|
138
|
+
// Evaluate style component: resolve properties for the current boundary state
|
|
139
|
+
if (entity.components.style) {
|
|
140
|
+
const style = entity.components.style;
|
|
141
|
+
const resolvedProps = StyleNS.tap(style, boundaryState);
|
|
142
|
+
for (const [prop, val] of Object.entries(resolvedProps)) {
|
|
143
|
+
results[prop] = val;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return results;
|
|
147
|
+
});
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function makeComposableDenseStore(world) {
|
|
152
|
+
// Maintain a mapping from ContentAddress to ECS EntityId for dense store ops
|
|
153
|
+
const addressToEntityId = new Map();
|
|
154
|
+
let denseStore;
|
|
155
|
+
return {
|
|
156
|
+
create(name, capacity) {
|
|
157
|
+
return Effect.gen(function* () {
|
|
158
|
+
const store = Part.dense(name, capacity);
|
|
159
|
+
yield* world.addDenseStore(store);
|
|
160
|
+
denseStore = store;
|
|
161
|
+
return store;
|
|
162
|
+
});
|
|
163
|
+
},
|
|
164
|
+
store(entity, value) {
|
|
165
|
+
return Effect.gen(function* () {
|
|
166
|
+
if (!denseStore) {
|
|
167
|
+
throw new Error('No dense store created. Call create() first.');
|
|
168
|
+
}
|
|
169
|
+
// Ensure we have an ECS EntityId for this composable entity
|
|
170
|
+
let ecsId = addressToEntityId.get(entity.id);
|
|
171
|
+
if (!ecsId) {
|
|
172
|
+
// Spawn into the world to get an EntityId, then track mapping
|
|
173
|
+
ecsId = yield* world.spawn(entity.components);
|
|
174
|
+
addressToEntityId.set(entity.id, ecsId);
|
|
175
|
+
}
|
|
176
|
+
denseStore.set(ecsId, value);
|
|
177
|
+
});
|
|
178
|
+
},
|
|
179
|
+
retrieve(entity) {
|
|
180
|
+
return Effect.gen(function* () {
|
|
181
|
+
if (!denseStore) {
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
const ecsId = addressToEntityId.get(entity.id);
|
|
185
|
+
if (!ecsId) {
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
return denseStore.get(ecsId);
|
|
189
|
+
});
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
// ---------------------------------------------------------------------------
|
|
194
|
+
// Exports
|
|
195
|
+
// ---------------------------------------------------------------------------
|
|
196
|
+
/**
|
|
197
|
+
* Composable — content-addressed entity algebra over czap primitives.
|
|
198
|
+
*
|
|
199
|
+
* Build entities from a bag of components (boundaries, tokens, styles, …),
|
|
200
|
+
* merge them associatively via `Composable.compose` / `Composable.merge`, and
|
|
201
|
+
* rely on the content address to deduplicate structurally-equal entities.
|
|
202
|
+
*/
|
|
203
|
+
export const Composable = {
|
|
204
|
+
/** Content-address a component bag into a {@link ComposableEntity}. */
|
|
205
|
+
make: _make,
|
|
206
|
+
/** Pairwise merge — right-biased; produces a new entity with a fresh content address. */
|
|
207
|
+
compose: _compose,
|
|
208
|
+
/** Variadic `Composable.compose`. Throws if called with zero entities. */
|
|
209
|
+
merge: _merge,
|
|
210
|
+
};
|
|
211
|
+
/**
|
|
212
|
+
* Bridge between a raw ECS {@link World} and typed {@link ComposableEntity}
|
|
213
|
+
* operations (`spawn`, `query`, `evaluate`) plus a thin dense-store integration.
|
|
214
|
+
*/
|
|
215
|
+
export const ComposableWorld = {
|
|
216
|
+
/** Wrap a {@link World} with the typed composable-entity API. */
|
|
217
|
+
make: makeComposableWorld,
|
|
218
|
+
/** Build a dense-store bridge over a {@link World} for per-entity numeric data. */
|
|
219
|
+
dense: makeComposableDenseStore,
|
|
220
|
+
};
|
|
221
|
+
//# sourceMappingURL=composable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composable.js","sourceRoot":"","sources":["../src/composable.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CompositorStatePool -- ring buffer of pre-allocated CompositeState objects.
|
|
3
|
+
*
|
|
4
|
+
* The compositor grabs one, writes into it, hands it to the renderer,
|
|
5
|
+
* which returns it after DOM application. Zero-allocation hot path.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import type { CompositeState } from './compositor.js';
|
|
10
|
+
interface CompositorStatePoolShape {
|
|
11
|
+
acquire(): CompositeState;
|
|
12
|
+
release(state: CompositeState): void;
|
|
13
|
+
readonly size: number;
|
|
14
|
+
readonly available: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Mutable views into a CompositeState's fields. `CompositeState` declares
|
|
18
|
+
* its fields as `readonly Record<...>`, but the compositor and the pool
|
|
19
|
+
* both need to mutate those records in place for the zero-allocation
|
|
20
|
+
* hot path. This helper centralises the single cast that strips
|
|
21
|
+
* `readonly` so callers can stay type-safe without re-casting at each
|
|
22
|
+
* write site.
|
|
23
|
+
*/
|
|
24
|
+
export interface MutableCompositeStateViews {
|
|
25
|
+
readonly discrete: Record<string, string>;
|
|
26
|
+
readonly blend: Record<string, Record<string, number>>;
|
|
27
|
+
readonly css: Record<string, number | string>;
|
|
28
|
+
readonly glsl: Record<string, number>;
|
|
29
|
+
readonly aria: Record<string, string>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Expose the inner `Record<…>`s of a {@link CompositeState} as mutable views
|
|
33
|
+
* for the zero-allocation hot path. Single sanctioned site that strips
|
|
34
|
+
* `readonly` — do not cast at other call sites.
|
|
35
|
+
*/
|
|
36
|
+
export declare function accessCompositeState(state: CompositeState): MutableCompositeStateViews;
|
|
37
|
+
/**
|
|
38
|
+
* Creates a ring-buffer pool of pre-allocated CompositeState objects.
|
|
39
|
+
* Acquire/release pattern avoids GC allocations on the hot render path.
|
|
40
|
+
* Default 8 slots -- enough for typical compositor with 4-6 quantizers + headroom.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* const pool = CompositorStatePool.make(4);
|
|
45
|
+
* const state = pool.acquire();
|
|
46
|
+
* state.discrete['theme'] = 'dark';
|
|
47
|
+
* state.outputs.css['--bg'] = '#000';
|
|
48
|
+
* pool.release(state); // resets and returns to pool
|
|
49
|
+
* pool.available; // 4
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
declare function _make(capacity?: number): CompositorStatePoolShape;
|
|
53
|
+
/**
|
|
54
|
+
* CompositorStatePool -- ring buffer of pre-allocated CompositeState objects.
|
|
55
|
+
* Zero-allocation hot path: acquire a state, write into it, render, then release.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```ts
|
|
59
|
+
* const pool = CompositorStatePool.make(8);
|
|
60
|
+
* const state = pool.acquire();
|
|
61
|
+
* // Write compositor output into state.discrete, state.blend, state.outputs
|
|
62
|
+
* pool.release(state); // resets and returns to pool
|
|
63
|
+
* console.log(pool.size, pool.available); // 8, 8
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export declare const CompositorStatePool: {
|
|
67
|
+
make: typeof _make;
|
|
68
|
+
};
|
|
69
|
+
export declare namespace CompositorStatePool {
|
|
70
|
+
/** Structural shape of a pool instance: `acquire`, `release`, `size`, `available`. */
|
|
71
|
+
type Shape = CompositorStatePoolShape;
|
|
72
|
+
}
|
|
73
|
+
export {};
|
|
74
|
+
//# sourceMappingURL=compositor-pool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compositor-pool.d.ts","sourceRoot":"","sources":["../src/compositor-pool.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAKtD,UAAU,wBAAwB;IAChC,OAAO,IAAI,cAAc,CAAC;IAC1B,OAAO,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACvD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACvC;AAUD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,cAAc,GAAG,0BAA0B,CAStF;AAoBD;;;;;;;;;;;;;;GAcG;AACH,iBAAS,KAAK,CAAC,QAAQ,SAAsB,GAAG,wBAAwB,CA+CvE;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,mBAAmB;;CAAkB,CAAC;AAEnD,MAAM,CAAC,OAAO,WAAW,mBAAmB,CAAC;IAC3C,sFAAsF;IACtF,KAAY,KAAK,GAAG,wBAAwB,CAAC;CAC9C"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CompositorStatePool -- ring buffer of pre-allocated CompositeState objects.
|
|
3
|
+
*
|
|
4
|
+
* The compositor grabs one, writes into it, hands it to the renderer,
|
|
5
|
+
* which returns it after DOM application. Zero-allocation hot path.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import { COMPOSITOR_POOL_CAP } from './defaults.js';
|
|
10
|
+
/**
|
|
11
|
+
* Expose the inner `Record<…>`s of a {@link CompositeState} as mutable views
|
|
12
|
+
* for the zero-allocation hot path. Single sanctioned site that strips
|
|
13
|
+
* `readonly` — do not cast at other call sites.
|
|
14
|
+
*/
|
|
15
|
+
export function accessCompositeState(state) {
|
|
16
|
+
const mutable = state;
|
|
17
|
+
return {
|
|
18
|
+
discrete: mutable.discrete,
|
|
19
|
+
blend: mutable.blend,
|
|
20
|
+
css: mutable.outputs.css,
|
|
21
|
+
glsl: mutable.outputs.glsl,
|
|
22
|
+
aria: mutable.outputs.aria,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function createMutableState() {
|
|
26
|
+
return {
|
|
27
|
+
discrete: {},
|
|
28
|
+
blend: {},
|
|
29
|
+
outputs: { css: {}, glsl: {}, aria: {} },
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function resetState(state) {
|
|
33
|
+
// Clear all fields in-place (cheaper than allocation)
|
|
34
|
+
const { discrete, blend, css, glsl, aria } = accessCompositeState(state);
|
|
35
|
+
for (const k of Object.keys(discrete))
|
|
36
|
+
delete discrete[k];
|
|
37
|
+
for (const k of Object.keys(blend))
|
|
38
|
+
delete blend[k];
|
|
39
|
+
for (const k of Object.keys(css))
|
|
40
|
+
delete css[k];
|
|
41
|
+
for (const k of Object.keys(glsl))
|
|
42
|
+
delete glsl[k];
|
|
43
|
+
for (const k of Object.keys(aria))
|
|
44
|
+
delete aria[k];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Creates a ring-buffer pool of pre-allocated CompositeState objects.
|
|
48
|
+
* Acquire/release pattern avoids GC allocations on the hot render path.
|
|
49
|
+
* Default 8 slots -- enough for typical compositor with 4-6 quantizers + headroom.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```ts
|
|
53
|
+
* const pool = CompositorStatePool.make(4);
|
|
54
|
+
* const state = pool.acquire();
|
|
55
|
+
* state.discrete['theme'] = 'dark';
|
|
56
|
+
* state.outputs.css['--bg'] = '#000';
|
|
57
|
+
* pool.release(state); // resets and returns to pool
|
|
58
|
+
* pool.available; // 4
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
function _make(capacity = COMPOSITOR_POOL_CAP) {
|
|
62
|
+
const buffer = [];
|
|
63
|
+
for (let i = 0; i < capacity; i++) {
|
|
64
|
+
buffer.push(createMutableState());
|
|
65
|
+
}
|
|
66
|
+
const free = new Array(capacity).fill(true);
|
|
67
|
+
let acquirePtr = 0;
|
|
68
|
+
return {
|
|
69
|
+
acquire() {
|
|
70
|
+
// Scan from current pointer for a free slot
|
|
71
|
+
for (let i = 0; i < capacity; i++) {
|
|
72
|
+
const idx = (acquirePtr + i) % capacity;
|
|
73
|
+
if (free[idx]) {
|
|
74
|
+
free[idx] = false;
|
|
75
|
+
acquirePtr = (idx + 1) % capacity;
|
|
76
|
+
return buffer[idx];
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// All slots in use — allocate overflow (cold path)
|
|
80
|
+
const overflow = createMutableState();
|
|
81
|
+
buffer.push(overflow);
|
|
82
|
+
free.push(false);
|
|
83
|
+
return overflow;
|
|
84
|
+
},
|
|
85
|
+
release(state) {
|
|
86
|
+
const idx = buffer.indexOf(state);
|
|
87
|
+
if (idx !== -1) {
|
|
88
|
+
resetState(state);
|
|
89
|
+
free[idx] = true;
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
get size() {
|
|
93
|
+
return buffer.length;
|
|
94
|
+
},
|
|
95
|
+
get available() {
|
|
96
|
+
let count = 0;
|
|
97
|
+
for (let i = 0; i < free.length; i++) {
|
|
98
|
+
if (free[i])
|
|
99
|
+
count++;
|
|
100
|
+
}
|
|
101
|
+
return count;
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* CompositorStatePool -- ring buffer of pre-allocated CompositeState objects.
|
|
107
|
+
* Zero-allocation hot path: acquire a state, write into it, render, then release.
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```ts
|
|
111
|
+
* const pool = CompositorStatePool.make(8);
|
|
112
|
+
* const state = pool.acquire();
|
|
113
|
+
* // Write compositor output into state.discrete, state.blend, state.outputs
|
|
114
|
+
* pool.release(state); // resets and returns to pool
|
|
115
|
+
* console.log(pool.size, pool.available); // 8, 8
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
export const CompositorStatePool = { make: _make };
|
|
119
|
+
//# sourceMappingURL=compositor-pool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compositor-pool.js","sourceRoot":"","sources":["../src/compositor-pool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compositor -- merge multiple quantizers into composite state.
|
|
3
|
+
*
|
|
4
|
+
* The compositor aggregates discrete + blended state from all
|
|
5
|
+
* active quantizers into a single CompositeState, producing
|
|
6
|
+
* typed output channels (css, glsl, aria).
|
|
7
|
+
*
|
|
8
|
+
* Wired: DirtyFlags (selective recomputation), CompositorStatePool
|
|
9
|
+
* (zero-allocation), FrameBudget (priority scheduling), microtask batching,
|
|
10
|
+
* and RuntimeCoordinator (Plan + ECS-backed runtime bookkeeping).
|
|
11
|
+
*
|
|
12
|
+
* Hot path (computeStateSync) is plain JS — no Effect overhead.
|
|
13
|
+
* Effect is used only for resource lifecycle (create/scope) and
|
|
14
|
+
* reactive stream (SubscriptionRef.changes).
|
|
15
|
+
*
|
|
16
|
+
* @module
|
|
17
|
+
*/
|
|
18
|
+
import type { Scope, Stream } from 'effect';
|
|
19
|
+
import { Effect } from 'effect';
|
|
20
|
+
import type { Boundary } from './boundary.js';
|
|
21
|
+
import type { FrameBudget } from './frame-budget.js';
|
|
22
|
+
import type { Quantizer } from './quantizer-types.js';
|
|
23
|
+
import { RuntimeCoordinator } from './runtime-coordinator.js';
|
|
24
|
+
/**
|
|
25
|
+
* Snapshot of the compositor's output per tick: discrete state names for each
|
|
26
|
+
* quantizer, their blend-weight vectors, and the compiled per-target output
|
|
27
|
+
* maps (`css` / `glsl` / `aria`).
|
|
28
|
+
*/
|
|
29
|
+
export interface CompositeState {
|
|
30
|
+
readonly discrete: Record<string, string>;
|
|
31
|
+
readonly blend: Record<string, Record<string, number>>;
|
|
32
|
+
readonly outputs: {
|
|
33
|
+
readonly css: Record<string, number | string>;
|
|
34
|
+
readonly glsl: Record<string, number>;
|
|
35
|
+
readonly aria: Record<string, string>;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Options accepted by `Compositor.create`: pool capacity, optional
|
|
40
|
+
* frame-budget gating, and whether to enable speculative pre-evaluation.
|
|
41
|
+
*/
|
|
42
|
+
export interface CompositorConfig {
|
|
43
|
+
readonly poolCapacity?: number;
|
|
44
|
+
readonly frameBudget?: FrameBudget.Shape;
|
|
45
|
+
readonly speculative?: boolean;
|
|
46
|
+
}
|
|
47
|
+
interface CompositorShape {
|
|
48
|
+
add<B extends Boundary.Shape>(name: string, quantizer: Quantizer<B>): Effect.Effect<void>;
|
|
49
|
+
remove(name: string): Effect.Effect<void>;
|
|
50
|
+
compute(): Effect.Effect<CompositeState>;
|
|
51
|
+
setBlendWeights(name: string, weights: Record<string, number>): Effect.Effect<void>;
|
|
52
|
+
evaluateSpeculative(name: string, value: number, velocity?: number): void;
|
|
53
|
+
scheduleBatch(): void;
|
|
54
|
+
readonly changes: Stream.Stream<CompositeState>;
|
|
55
|
+
readonly runtime: RuntimeCoordinator.Shape;
|
|
56
|
+
}
|
|
57
|
+
interface CompositorFactory {
|
|
58
|
+
create(config?: CompositorConfig): Effect.Effect<CompositorShape, never, Scope.Scope>;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Compositor — the live merge point for every attached {@link Quantizer}.
|
|
62
|
+
*
|
|
63
|
+
* `Compositor.create` hands back a scoped Effect that, when run inside a
|
|
64
|
+
* `Scope`, produces a compositor bound to a {@link RuntimeCoordinator}. Adding
|
|
65
|
+
* quantizers, marking dirty flags, and emitting CSS/GLSL/ARIA outputs all flow
|
|
66
|
+
* through the zero-allocation hot path backed by {@link CompositorStatePool}.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* import { Effect } from 'effect';
|
|
71
|
+
* import { Compositor } from '@czap/core';
|
|
72
|
+
*
|
|
73
|
+
* const program = Effect.scoped(Effect.gen(function* () {
|
|
74
|
+
* const compositor = yield* Compositor.create({ poolCapacity: 64, speculative: true });
|
|
75
|
+
* yield* compositor.add('viewport', viewportQuantizer);
|
|
76
|
+
* const state = yield* compositor.compute();
|
|
77
|
+
* // state.discrete.viewport === 'tablet'
|
|
78
|
+
* // state.outputs.css['--czap-viewport'] === 'tablet'
|
|
79
|
+
* }));
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
export declare const Compositor: CompositorFactory;
|
|
83
|
+
export declare namespace Compositor {
|
|
84
|
+
/** Structural shape of a live compositor instance. */
|
|
85
|
+
type Shape = CompositorShape;
|
|
86
|
+
/** Alias for {@link CompositorConfig}. */
|
|
87
|
+
type Config = CompositorConfig;
|
|
88
|
+
}
|
|
89
|
+
export {};
|
|
90
|
+
//# sourceMappingURL=compositor.d.ts.map
|