@descix/frqtl-node 0.0.0-stage → 0.6.0-p0.1
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/CLAUDE.md +87 -0
- package/README.md +84 -2
- package/dist/dist-manifest.json +218 -0
- package/dist/eyes/index.js +109 -0
- package/dist/fat/pkg-node/egpt_wasm.js +1277 -0
- package/dist/fat/pkg-node/egpt_wasm_bg.wasm +0 -0
- package/dist/fat/pkg-node/package.json +12 -0
- package/dist/frqtl/assets/vendor/chroma.mjs +64 -0
- package/dist/frqtl/compat/boot-node.js +34 -0
- package/dist/frqtl/compat/engine.js +127 -0
- package/dist/frqtl/compat/package-root.js +24 -0
- package/dist/frqtl/compat/providers/wasm-engine.js +2501 -0
- package/dist/frqtl/compat/providers/wasm-load-browser.js +134 -0
- package/dist/frqtl/compat/providers/wasm-load-node.js +59 -0
- package/dist/frqtl/compat/renderer/FieldBuilder.js +145 -0
- package/dist/frqtl/compat/renderer/Filters.js +163 -0
- package/dist/frqtl/compat/renderer/RenderFamily.js +85 -0
- package/dist/frqtl/compat/renderer/RenderMesh.js +100 -0
- package/dist/frqtl/compat/renderer/RenderShape.js +154 -0
- package/dist/frqtl/compat/renderer/camera2d-geometry.js +183 -0
- package/dist/frqtl/compat/renderer/captureStill-common.js +259 -0
- package/dist/frqtl/compat/renderer/display-slot.js +96 -0
- package/dist/frqtl/compat/renderer/lo-drawn-center.js +231 -0
- package/dist/frqtl/compat/renderer/orbit-bag.js +216 -0
- package/dist/frqtl/compat/renderer/providers/godrays-layer.js +213 -0
- package/dist/frqtl/compat/renderer/providers/index.js +22 -0
- package/dist/frqtl/compat/renderer/providers/p5-provider.js +861 -0
- package/dist/frqtl/compat/renderer/providers/three-camera-contract.js +160 -0
- package/dist/frqtl/compat/renderer/providers/three-camera-geometry.js +571 -0
- package/dist/frqtl/compat/renderer/providers/three-provider.js +2474 -0
- package/dist/frqtl/compat/renderer/renderCapabilities-common.js +186 -0
- package/dist/frqtl/compat/renderer/renderer-provider-contract.js +238 -0
- package/dist/frqtl/compat/renderer/unprojectStill.js +172 -0
- package/dist/frqtl/compat/vendor.js +196 -0
- package/dist/frqtl/node-entry.js +17 -0
- package/dist/frqtl/objects/FrqtlApp.js +2922 -0
- package/dist/frqtl/objects/ObjectFactory.js +297 -0
- package/dist/frqtl/objects/RecipeContext.js +322 -0
- package/dist/frqtl/objects/collectors/DataCollector.js +116 -0
- package/dist/frqtl/objects/constants/WallBehaviors.js +178 -0
- package/dist/frqtl/objects/geometry/SlabFace.js +258 -0
- package/dist/frqtl/objects/instruments/capture-host.js +453 -0
- package/dist/frqtl/objects/instruments/capture.js +711 -0
- package/dist/frqtl/objects/instruments/hosted-eyes.js +985 -0
- package/dist/frqtl/objects/kinds-entry.js +14 -0
- package/dist/frqtl/objects/kinds.js +190 -0
- package/dist/frqtl/objects/objects/Box.js +273 -0
- package/dist/frqtl/objects/objects/Bullet.js +155 -0
- package/dist/frqtl/objects/objects/CircuitGate.js +380 -0
- package/dist/frqtl/objects/objects/CircuitProbe.js +214 -0
- package/dist/frqtl/objects/objects/DirectedTunnel.js +412 -0
- package/dist/frqtl/objects/objects/EgptFractal.js +164 -0
- package/dist/frqtl/objects/objects/LightSource.js +354 -0
- package/dist/frqtl/objects/objects/QuantumEmitter.js +760 -0
- package/dist/frqtl/objects/objects/Tube.js +471 -0
- package/dist/frqtl/objects/objects/absorbRadiation.js +310 -0
- package/dist/frqtl/objects/objects/actionCompiler.js +1747 -0
- package/dist/frqtl/objects/objects/controlAction.js +89 -0
- package/dist/frqtl/objects/objects/controlBind.js +278 -0
- package/dist/frqtl/objects/objects/directedRedirectProgram.js +120 -0
- package/dist/frqtl/objects/objects/emitterProgram.js +209 -0
- package/dist/frqtl/objects/objects/frqtl-control-slider.js +197 -0
- package/dist/frqtl/objects/objects/frqtl-control-sync.js +272 -0
- package/dist/frqtl/objects/objects/frqtl-control-widgets.js +219 -0
- package/dist/frqtl/objects/objects/frqtl-control-write-queue.js +174 -0
- package/dist/frqtl/objects/objects/makeControlsForObject.js +824 -0
- package/dist/frqtl/objects/objects/makeControlsForRenderSurface.js +161 -0
- package/dist/frqtl/objects/objects/slitLinerScatter.js +263 -0
- package/dist/frqtl/objects/objects/sustainedSource.js +299 -0
- package/dist/frqtl/objects/objects/tagger.js +228 -0
- package/dist/frqtl/objects/schema/engine-scopes.js +469 -0
- package/dist/frqtl/objects/schema/index.js +256 -0
- package/dist/frqtl/objects/schema/scopes.js +457 -0
- package/dist/frqtl/objects/schema/validate-descriptor.js +49 -0
- package/dist/frqtl/objects/schema/valueNormalizer.js +366 -0
- package/dist/frqtl/objects/setup/_collisionMatrixLanes.js +66 -0
- package/dist/frqtl/objects/setup/_setupOptions.js +118 -0
- package/dist/frqtl/objects/setup/_worldInit.js +162 -0
- package/dist/frqtl/objects/setup/setupBlackbodySubstrate.js +242 -0
- package/dist/frqtl/objects/setup/setupCircuitBasic.js +447 -0
- package/dist/frqtl/objects/setup/setupCollisionMatrix.js +188 -0
- package/dist/frqtl/objects/setup/setupConeEmitter3D.js +251 -0
- package/dist/frqtl/objects/setup/setupDoubleSlit.js +327 -0
- package/dist/frqtl/objects/setup/setupDoubleSlit_Laser.js +229 -0
- package/dist/frqtl/objects/setup/setupEmpty.js +59 -0
- package/dist/frqtl/objects/setup/setupFxSmoke.js +209 -0
- package/dist/frqtl/objects/setup/setupG1Race.js +80 -0
- package/dist/frqtl/objects/setup/setupG2Sweep.js +89 -0
- package/dist/frqtl/objects/setup/setupG3Collide.js +177 -0
- package/dist/frqtl/objects/setup/setupLightSourceLab.js +280 -0
- package/dist/frqtl/objects/setup/setupOscillator.js +68 -0
- package/dist/frqtl/objects/setup/setupParticleWalk.js +93 -0
- package/dist/frqtl/objects/setup/setupParticleWalk3D.js +133 -0
- package/dist/frqtl/objects/setup/setupSphereEmitter3D.js +1259 -0
- package/dist/frqtl/objects/setup/setupTraceCircuit.js +239 -0
- package/dist/frqtl/objects/setup/setupTunnelLab.js +242 -0
- package/dist/frqtl/objects/setup/setupTunnelPointExit.js +306 -0
- package/dist/frqtl/objects/setup/setupWaveInterference.js +178 -0
- package/dist/frqtl/objects/v1-replacements.js +30 -0
- package/dist/frqtl/package-core.js +23 -0
- package/dist/frqtl/parts/CameraControls.js +643 -0
- package/dist/frqtl/parts/CameraPath.js +575 -0
- package/dist/frqtl/parts/CollisionActions.js +35 -0
- package/dist/frqtl/parts/CollisionTypes.js +31 -0
- package/dist/frqtl/parts/DetectorWall.js +496 -0
- package/dist/frqtl/parts/FrqtlControlOverlay.js +227 -0
- package/dist/frqtl/parts/FrqtlControlsPanel.js +415 -0
- package/dist/frqtl/parts/FrqtlVision.js +818 -0
- package/dist/frqtl/parts/GraphingDetectorWall.js +490 -0
- package/dist/frqtl/parts/LargeObject.js +406 -0
- package/dist/frqtl/parts/LightColor.js +255 -0
- package/dist/frqtl/parts/LiveChart.js +285 -0
- package/dist/frqtl/parts/LogicGate.js +720 -0
- package/dist/frqtl/parts/OvenBox.js +612 -0
- package/dist/frqtl/parts/PointSource.js +763 -0
- package/dist/frqtl/parts/Rectangle.js +200 -0
- package/dist/frqtl/parts/SlitLightSource.js +234 -0
- package/dist/frqtl/parts/SlitScreen.js +628 -0
- package/dist/frqtl/parts/SphereEmitter.js +946 -0
- package/dist/frqtl/parts/Trace.js +508 -0
- package/dist/frqtl/parts/Tunnel.js +1057 -0
- package/dist/frqtl/parts/Wall.js +1211 -0
- package/dist/frqtl/parts/index.js +33 -0
- package/dist/frqtl/scenes/absorbing-wall-3d.js +33 -0
- package/dist/frqtl/scenes/absorbing-wall.js +32 -0
- package/dist/frqtl/scenes/atomic-model.js +88 -0
- package/dist/frqtl/scenes/big-bang.js +46 -0
- package/dist/frqtl/scenes/blackbody.js +81 -0
- package/dist/frqtl/scenes/circuit-sat.js +127 -0
- package/dist/frqtl/scenes/collision-matrix-3d.js +107 -0
- package/dist/frqtl/scenes/counting-wall-3d.js +36 -0
- package/dist/frqtl/scenes/counting-wall.js +38 -0
- package/dist/frqtl/scenes/double-slit.js +31 -0
- package/dist/frqtl/scenes/dse-one-emitter.js +196 -0
- package/dist/frqtl/scenes/dse-one-slit.js +114 -0
- package/dist/frqtl/scenes/dse-photoelectric.js +70 -0
- package/dist/frqtl/scenes/dse-tunnel-rebuild.js +244 -0
- package/dist/frqtl/scenes/dse-two-emitters.js +196 -0
- package/dist/frqtl/scenes/dse-two-slits.js +132 -0
- package/dist/frqtl/scenes/empty.js +14 -0
- package/dist/frqtl/scenes/laser-buildup.js +49 -0
- package/dist/frqtl/scenes/mirror-3d.js +56 -0
- package/dist/frqtl/scenes/mirror-route.js +140 -0
- package/dist/frqtl/scenes/mirror.js +38 -0
- package/dist/frqtl/scenes/omni-3d.js +27 -0
- package/dist/frqtl/scenes/omni-source.js +22 -0
- package/dist/frqtl/scenes/self-interference.js +39 -0
- package/dist/frqtl/scenes/smoking-gun.js +122 -0
- package/dist/frqtl/scenes/straight-walk.js +22 -0
- package/dist/frqtl/scenes/sun.js +39 -0
- package/dist/frqtl/scenes/two-source.js +37 -0
- package/dist/frqtl/scenes/wave.js +26 -0
- package/dist/shim/bundle/frqnb.js +112 -0
- package/dist/shim/bundle/index.js +121 -0
- package/dist/shim/bundle/indexHtml.js +107 -0
- package/dist/shim/bundle/manifest.js +95 -0
- package/dist/shim/bundle/zip.js +243 -0
- package/dist/shim/bus.js +67 -0
- package/dist/shim/controls/index.js +627 -0
- package/dist/shim/controls/registry.js +274 -0
- package/dist/shim/engine/artifact-shell.js +212 -0
- package/dist/shim/engine/boot-phases.js +44 -0
- package/dist/shim/engine/engine-stack-boot.js +59 -0
- package/dist/shim/engine/router.js +692 -0
- package/dist/shim/experiment-frame/index.js +296 -0
- package/dist/shim/expr/ast-schema.js +369 -0
- package/dist/shim/field-descriptor/index.js +326 -0
- package/dist/shim/frame-data/index.js +224 -0
- package/dist/shim/index.js +67 -0
- package/dist/shim/notebook/canvas-guard.js +82 -0
- package/dist/shim/notebook/catalog.js +35 -0
- package/dist/shim/notebook/format.js +736 -0
- package/dist/shim/notebook/frqtl-setups-importmap.js +81 -0
- package/dist/shim/notebook/kernel.js +672 -0
- package/dist/shim/notebook/knob.js +151 -0
- package/dist/shim/notebook/module-resolver.js +260 -0
- package/dist/shim/notebook/notebook-knobs.js +65 -0
- package/dist/shim/notebook/produced-bindings.js +67 -0
- package/dist/shim/notebook/realm-registry.js +121 -0
- package/dist/shim/notebook/realm-transport.js +73 -0
- package/dist/shim/notebook/setup-builder.js +510 -0
- package/dist/shim/notebook/store.js +159 -0
- package/dist/shim/notebook/vendor-importmap.js +134 -0
- package/dist/shim/notebook/view-builder.js +189 -0
- package/dist/shim/notebook/view-ctx.js +100 -0
- package/dist/shim/notebook/view-handle.js +202 -0
- package/dist/shim/notebook/warmup.js +102 -0
- package/dist/shim/package.json +4 -0
- package/dist/shim/page-params/index.js +214 -0
- package/dist/shim/project/identity.js +112 -0
- package/dist/shim/project/index.js +19 -0
- package/dist/shim/project/link.js +402 -0
- package/dist/shim/project/node-specifier-hook.mjs +35 -0
- package/dist/shim/project/register-node.mjs +17 -0
- package/dist/shim/project/specifiers.js +106 -0
- package/dist/shim/registry/copy-gate.js +110 -0
- package/dist/shim/registry/exhibits.js +455 -0
- package/dist/shim/registry/experiments.js +215 -0
- package/dist/shim/registry/index.js +19 -0
- package/dist/shim/registry/scene-source.js +157 -0
- package/dist/shim/registry/walkthrough.js +807 -0
- package/dist/shim/render/capture-envelope.js +279 -0
- package/dist/shim/render/heat-colormap.js +92 -0
- package/dist/shim/render/render-stages.js +855 -0
- package/dist/shim/render/sun-look.js +33 -0
- package/dist/shim/render-state/apply.js +262 -0
- package/dist/shim/render-state/index.js +74 -0
- package/dist/shim/rpc/index.js +207 -0
- package/dist/shim/runtime/index.js +1023 -0
- package/dist/shim/storage/boot.js +69 -0
- package/dist/shim/storage/index.js +169 -0
- package/dist/shim/storage/local-storage-provider.js +230 -0
- package/dist/shim/storage/make-namespace-store.js +132 -0
- package/dist/shim/storage/modules.js +249 -0
- package/dist/shim/storage/projects.js +150 -0
- package/llms.txt +12 -0
- package/package.json +62 -4
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* lib/shim/field-descriptor — the shared FIELD-SHAPE vocabulary contract.
|
|
5
|
+
*
|
|
6
|
+
* WS Studio Controls Realignment §7 r2 CEO call C1 (ratified 2026-07-07): the architect's
|
|
7
|
+
* escalation was that a single FIELD shape — `{name, type, constraint, unit,
|
|
8
|
+
* description, engineVisible, widgetHint, widgetConfig, default}` — and its
|
|
9
|
+
* closed TYPE vocabulary were about to be re-derived THREE times (physics
|
|
10
|
+
* object schema, the new render-capability descriptors, and a future Rosetta
|
|
11
|
+
* `paramsSchema`) instead of owned once. This module is that ONE owner — the
|
|
12
|
+
* peer of `lib/shim/controls` (which owns the CONTROL-descriptor shape one
|
|
13
|
+
* layer up) and `lib/shim/frame-data` (which owns the data-bubble shape) —
|
|
14
|
+
* same layout convention (closed vocabulary + a throw-or-apply `normalize`,
|
|
15
|
+
* named ES-module exports).
|
|
16
|
+
*
|
|
17
|
+
* ── Why this exists ──────────────────────────────────────────────────────────
|
|
18
|
+
*
|
|
19
|
+
* `lib/frqtl/objects/schema/index.js` (`ObjectSchema`) already declares
|
|
20
|
+
* ~200 fields across 16 kinds using EXACTLY this shape, documented only as a
|
|
21
|
+
* comment block (`type:'rect' → …`, `type:'rgb' → …`, …). Separately,
|
|
22
|
+
* `lib/frqtl/objects/objects/makeControlsForObject.js`'s `TYPE_DISPATCH` map
|
|
23
|
+
* independently enumerates the SAME 12 field types as its object keys — two
|
|
24
|
+
* hand-written lists that happen to agree today but have no shared owner
|
|
25
|
+
* enforcing that they always will (the exact schema-mirror-drift class root
|
|
26
|
+
* `CLAUDE.md` §Engineering Culture calls out: "the same data breaks in one
|
|
27
|
+
* consumer but not another"). `renderCapabilities()` (S5a item 2,
|
|
28
|
+
* `lib/frqtl/compat/renderer/providers/{p5,three}-provider.js`) needed the
|
|
29
|
+
* SAME field shape for a THIRD surface — the trigger for this extraction.
|
|
30
|
+
*
|
|
31
|
+
* ── Scope ─────────────────────────────────────────────────────────────────
|
|
32
|
+
*
|
|
33
|
+
* This module owns:
|
|
34
|
+
* - `FIELD_TYPES` — the closed field-TYPE vocabulary (12 members,
|
|
35
|
+
* mechanically identical to `ObjectSchema`'s documented comment list AND
|
|
36
|
+
* `makeControlsForObject.js`'s `TYPE_DISPATCH` keys — see the drift test).
|
|
37
|
+
* - `FIELD_ENVELOPE_KEYS` — the field-SHAPE envelope key list (documentation/
|
|
38
|
+
* introspection use — NOT a required-keys gate; most fields omit most of
|
|
39
|
+
* these optional keys, only `name`/`type`/`description` are mandatory).
|
|
40
|
+
* - `normalizeFieldDescriptor(partial)` — throw-or-apply validation of ONE
|
|
41
|
+
* field descriptor's envelope (name/type/description required; constraint/
|
|
42
|
+
* unit/engineVisible/widgetHint/widgetConfig/default validated IF present).
|
|
43
|
+
*
|
|
44
|
+
* This module does NOT:
|
|
45
|
+
* - Re-list or replace `ObjectSchema`'s ~200 existing per-kind field arrays —
|
|
46
|
+
* those stay EXACTLY as authored (byte-identical); `ObjectSchema` merely
|
|
47
|
+
* IMPORTS `FIELD_TYPES` from here to assert (at load time) that every field
|
|
48
|
+
* it declares uses a member of the ONE vocabulary, instead of hand-copying
|
|
49
|
+
* the type list into its own comment block as the only enforcement.
|
|
50
|
+
* - Replace `lib/shim/controls`'s CONTROL-descriptor shape (`{kind, id, label,
|
|
51
|
+
* min, max, step, value, fmt, onChange, bind, channel}`) — that is a
|
|
52
|
+
* DIFFERENT, downstream shape (a UI/live-knob descriptor), consumed by a
|
|
53
|
+
* factory that READS field descriptors of THIS shape and EMITS control
|
|
54
|
+
* descriptors of THAT shape. The two are peers, not the same contract.
|
|
55
|
+
*
|
|
56
|
+
* Usage:
|
|
57
|
+
* import { FIELD_TYPES, normalizeFieldDescriptor } from 'lib/shim/field-descriptor/index.js';
|
|
58
|
+
* var f = normalizeFieldDescriptor({
|
|
59
|
+
* name: 'godrays', type: 'number', unit: 'intensity',
|
|
60
|
+
* constraint: { min: 0, max: 3 }, default: 0,
|
|
61
|
+
* description: 'God-rays post-process intensity (render-only).'
|
|
62
|
+
* });
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
// ── Field type vocabulary (closed, 12 members) ──────────────────────────────
|
|
68
|
+
//
|
|
69
|
+
// Mechanically identical to the set actually used across ObjectSchema's field
|
|
70
|
+
// declarations (lib/frqtl/objects/schema/index.js) and
|
|
71
|
+
// makeControlsForObject.js's TYPE_DISPATCH keys — verified exhaustively by
|
|
72
|
+
// lib/shim/field-descriptor/test/field-descriptor.test.mjs's drift assertion.
|
|
73
|
+
// Adding a 13th type is a THREE-PLACE change (here, TYPE_DISPATCH, and any
|
|
74
|
+
// descriptor that uses it) — the drift test makes forgetting one of the three
|
|
75
|
+
// a CI failure rather than a silent runtime gap.
|
|
76
|
+
//
|
|
77
|
+
// number — numeric scalar; constraint: { min?, max?, integer? }
|
|
78
|
+
// boolean — true | false
|
|
79
|
+
// string — free string
|
|
80
|
+
// enum — one value from constraint.values object
|
|
81
|
+
// enum-set — array of zero or more values from constraint.values object
|
|
82
|
+
// rgb — [r, g, b] (0-255 integers)
|
|
83
|
+
// rect — { left, top, w, h } (+ optional z, d — WASM-only)
|
|
84
|
+
// rect-array — array of rect-shaped tuples
|
|
85
|
+
// readonly — derived/computed: inspectable but not editable
|
|
86
|
+
// forbidden — engine-owned topology: any write attempt throws
|
|
87
|
+
// object — an arbitrary structured bag (e.g. an op-program)
|
|
88
|
+
// writable — Frame-kind-only ACCESS-LEVEL tag (the actual value type is
|
|
89
|
+
// inferred from the live read — number/boolean/string/object)
|
|
90
|
+
var FIELD_TYPES = Object.freeze([
|
|
91
|
+
'number', 'boolean', 'string', 'enum', 'enum-set',
|
|
92
|
+
'rgb', 'rect', 'rect-array', 'readonly', 'forbidden',
|
|
93
|
+
'object', 'writable'
|
|
94
|
+
]);
|
|
95
|
+
|
|
96
|
+
// ── Field envelope keys (documentation/introspection — NOT a required list) ─
|
|
97
|
+
//
|
|
98
|
+
// Every field descriptor MAY carry these keys; only name/type/description are
|
|
99
|
+
// mandatory (validated by normalizeFieldDescriptor below). `default` is the
|
|
100
|
+
// C1 addition renderCapabilities() descriptors need (a physics ObjectSchema
|
|
101
|
+
// field's live value comes from the constructed object instead — `default`
|
|
102
|
+
// was rarely populated there and stays optional here for that reason).
|
|
103
|
+
var FIELD_ENVELOPE_KEYS = Object.freeze([
|
|
104
|
+
'name', 'type', 'constraint', 'unit', 'description',
|
|
105
|
+
'engineVisible', 'widgetHint', 'widgetConfig', 'default'
|
|
106
|
+
]);
|
|
107
|
+
|
|
108
|
+
function _isStr(v) { return typeof v === 'string' && v.length > 0; }
|
|
109
|
+
function _isPlainObject(v) { return v !== null && typeof v === 'object' && !Array.isArray(v); }
|
|
110
|
+
|
|
111
|
+
function _fail(name, msg) {
|
|
112
|
+
throw new Error('[lib/shim/field-descriptor] normalizeFieldDescriptor(name="' + String(name) +
|
|
113
|
+
'"): ' + msg + ' (FAIL-LOUD — throw-or-apply, never silently dropped/defaulted-around.)');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* normalizeFieldDescriptor(partial) → full bag.
|
|
118
|
+
*
|
|
119
|
+
* THROW-OR-APPLY: a malformed field descriptor throws here — never silently
|
|
120
|
+
* dropped or coerced. Mandatory: `name` (non-empty string), `type` (member of
|
|
121
|
+
* FIELD_TYPES), `description` (non-empty string — every ObjectSchema field
|
|
122
|
+
* already carries one; see schema.test.mjs assertion (a)). Optional, validated
|
|
123
|
+
* IF present: `unit` (string), `constraint`/`widgetConfig` (plain object),
|
|
124
|
+
* `engineVisible` (boolean), `widgetHint` (string). `default` accepts ANY
|
|
125
|
+
* value (its shape depends on `type` — a number, a string, an array for rgb/
|
|
126
|
+
* rect, …) so it is NOT type-checked here.
|
|
127
|
+
*
|
|
128
|
+
* @param {object} partial
|
|
129
|
+
* @returns {object} a NEW object (input never mutated) carrying every input
|
|
130
|
+
* key verbatim.
|
|
131
|
+
* @throws {Error} FAIL-LOUD on a missing/empty `name`, an unrecognized `type`,
|
|
132
|
+
* a missing/empty `description`, or a mistyped optional key.
|
|
133
|
+
*/
|
|
134
|
+
function normalizeFieldDescriptor(partial) {
|
|
135
|
+
if (!partial || typeof partial !== 'object') {
|
|
136
|
+
throw new Error('[lib/shim/field-descriptor] normalizeFieldDescriptor requires a field ' +
|
|
137
|
+
'descriptor object; got ' + (partial === null ? 'null' : typeof partial) +
|
|
138
|
+
'. (FAIL-LOUD.)');
|
|
139
|
+
}
|
|
140
|
+
var name = partial.name;
|
|
141
|
+
if (!_isStr(name)) {
|
|
142
|
+
_fail(name, '"name" must be a non-empty string.');
|
|
143
|
+
}
|
|
144
|
+
var type = partial.type;
|
|
145
|
+
if (FIELD_TYPES.indexOf(type) === -1) {
|
|
146
|
+
_fail(name, '"type" must be one of [' + FIELD_TYPES.join(', ') + ']; got "' + String(type) + '".');
|
|
147
|
+
}
|
|
148
|
+
if (!_isStr(partial.description)) {
|
|
149
|
+
_fail(name, '"description" must be a non-empty string.');
|
|
150
|
+
}
|
|
151
|
+
if (partial.unit !== undefined && !_isStr(partial.unit)) {
|
|
152
|
+
_fail(name, '"unit", when present, must be a non-empty string.');
|
|
153
|
+
}
|
|
154
|
+
if (partial.constraint !== undefined && !_isPlainObject(partial.constraint)) {
|
|
155
|
+
_fail(name, '"constraint", when present, must be a plain object.');
|
|
156
|
+
}
|
|
157
|
+
if (partial.engineVisible !== undefined && typeof partial.engineVisible !== 'boolean') {
|
|
158
|
+
_fail(name, '"engineVisible", when present, must be a boolean.');
|
|
159
|
+
}
|
|
160
|
+
if (partial.widgetHint !== undefined && !_isStr(partial.widgetHint)) {
|
|
161
|
+
_fail(name, '"widgetHint", when present, must be a non-empty string.');
|
|
162
|
+
}
|
|
163
|
+
if (partial.widgetConfig !== undefined && !_isPlainObject(partial.widgetConfig)) {
|
|
164
|
+
_fail(name, '"widgetConfig", when present, must be a plain object.');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Fresh bag — never mutate the caller's object (mirrors frame-data's
|
|
168
|
+
// normalizeDataBubble / controls' normalizeControlDescriptor convention).
|
|
169
|
+
var bag = {};
|
|
170
|
+
for (var k in partial) {
|
|
171
|
+
if (Object.prototype.hasOwnProperty.call(partial, k)) { bag[k] = partial[k]; }
|
|
172
|
+
}
|
|
173
|
+
bag.name = name;
|
|
174
|
+
bag.type = type;
|
|
175
|
+
return bag;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ── UX presentation vocabulary (ws-experiments-walkthrough Bite 5b A0) ──────
|
|
179
|
+
//
|
|
180
|
+
// A per-KIND `ux:{}` block rides ON the ObjectSchema descriptor (peer of `fields`
|
|
181
|
+
// — a presentation contract, NOT a parallel `UxSchema`; architect Q-A0). Its
|
|
182
|
+
// closed value vocabularies live HERE, the PEER of FIELD_TYPES, so STUDIO's
|
|
183
|
+
// overlay AND a headless game consume ONE list (the SAME hooks a game uses
|
|
184
|
+
// programmatically — CEO §9). The vocab is READ DOWNWARD ONLY (shim → objects/
|
|
185
|
+
// features); no lib/frqtl/objects file re-lists these strings, and
|
|
186
|
+
// author-bbox-overlay.js (lib/features) FERRIES `UX_HANDLE_ZONES` (asserting its
|
|
187
|
+
// own returned zone names are members) instead of being a second owner.
|
|
188
|
+
//
|
|
189
|
+
// R6 legacy tolerance: a descriptor with NO `ux` block ⇒ `normalizeUx(undefined)`
|
|
190
|
+
// ⇒ today's behavior (resizable everywhere, 2D-draggable, all handle zones live,
|
|
191
|
+
// marker shell design-only).
|
|
192
|
+
// normalizeUx NEVER throws on an absent block — only on an INVALID member.
|
|
193
|
+
|
|
194
|
+
// The bbox interaction zones hitBboxZone (author-bbox-overlay.js) can classify a
|
|
195
|
+
// pointer into. 'move' = interior drag; 'scale-*' = a corner/edge resize handle.
|
|
196
|
+
// This is the ONE owner; author-bbox-overlay.js ferries it (drift = load-time throw).
|
|
197
|
+
var UX_HANDLE_ZONES = Object.freeze([
|
|
198
|
+
'move',
|
|
199
|
+
'scale-tl', 'scale-tr', 'scale-bl', 'scale-br',
|
|
200
|
+
'scale-t', 'scale-b', 'scale-l', 'scale-r'
|
|
201
|
+
]);
|
|
202
|
+
|
|
203
|
+
// The render modes a part may be cursor-dragged in. Only 2D: a 3D scene never cursor-drags
|
|
204
|
+
// (reposition is the aim + SPACE carry), so there is no per-object 3D knob to declare.
|
|
205
|
+
var UX_DRAGGABLE_MODES = Object.freeze(['2d']);
|
|
206
|
+
|
|
207
|
+
// When a manipulable object's visual shell renders (generalizes the Bite-4b
|
|
208
|
+
// `_designMarker` boolean at the ONE _visibleLargeObjects authority):
|
|
209
|
+
// design — only while authoring (Design mode). The Bite-4b marker default.
|
|
210
|
+
// run — always visible (the CEO's "red light bulb" opt-in).
|
|
211
|
+
// never — never drawn.
|
|
212
|
+
var UX_SHELL_RENDER = Object.freeze(['design', 'run', 'never']);
|
|
213
|
+
|
|
214
|
+
// The `ux` envelope keys (documentation/introspection — NOT a required list; a
|
|
215
|
+
// partial ux block fills the rest from the legacy defaults below).
|
|
216
|
+
var UX_ENVELOPE_KEYS = Object.freeze([
|
|
217
|
+
'resizable', 'draggable', 'handles', 'shellRender'
|
|
218
|
+
]);
|
|
219
|
+
|
|
220
|
+
// Keys a ux block may NOT carry any more, each with what replaced it (FAIL-LOUD on use).
|
|
221
|
+
var UX_DELETED_KEYS = Object.freeze({
|
|
222
|
+
inspectorSections: 'the Inspector shows a DECLARED object through its own descriptor (no per-role section list)'
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
function _uxFail(msg) {
|
|
226
|
+
throw new Error('[lib/shim/field-descriptor] normalizeUx: ' + msg +
|
|
227
|
+
' (FAIL-LOUD — throw-or-apply, never silently dropped/defaulted-around.)');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* normalizeUx(partial) → full ux bag.
|
|
232
|
+
*
|
|
233
|
+
* THROW-OR-APPLY: an INVALID member throws here; an ABSENT block (undefined/null)
|
|
234
|
+
* yields the legacy defaults (today's behavior, R6). Every field is optional; a
|
|
235
|
+
* partial block fills the rest from the defaults.
|
|
236
|
+
*
|
|
237
|
+
* resizable — boolean (default true). false ⇒ scale handles are never
|
|
238
|
+
* offered even for the SELECTED object (its corners never
|
|
239
|
+
* resize; the first-and-every interaction is select/move).
|
|
240
|
+
* draggable — { '2d':boolean } (default true). false disables the 2D
|
|
241
|
+
* cursor-drag. (A 3D scene never cursor-drags; '3d' is not a key.)
|
|
242
|
+
* handles — string[] ⊆ UX_HANDLE_ZONES (default = ALL zones). The
|
|
243
|
+
* live interaction zones; a zone NOT listed is downgraded
|
|
244
|
+
* to 'move' by the overlay (never a resize on the selecting
|
|
245
|
+
* click). `resizable:false` is the ergonomic shorthand for
|
|
246
|
+
* handles:['move'].
|
|
247
|
+
* shellRender — one of UX_SHELL_RENDER (default 'design'). When the
|
|
248
|
+
* object's visual shell/marker paints.
|
|
249
|
+
*
|
|
250
|
+
* @param {object|null|undefined} partial
|
|
251
|
+
* @returns {{resizable:boolean, draggable:{'2d':boolean}, handles:string[], shellRender:string}}
|
|
252
|
+
*/
|
|
253
|
+
function normalizeUx(partial) {
|
|
254
|
+
var bag = {
|
|
255
|
+
resizable: true,
|
|
256
|
+
draggable: { '2d': true },
|
|
257
|
+
handles: UX_HANDLE_ZONES.slice(),
|
|
258
|
+
shellRender: 'design'
|
|
259
|
+
};
|
|
260
|
+
if (partial === null || partial === undefined) { return bag; }
|
|
261
|
+
if (typeof partial !== 'object' || Array.isArray(partial)) {
|
|
262
|
+
_uxFail('a ux block must be a plain object; got ' +
|
|
263
|
+
(Array.isArray(partial) ? 'array' : typeof partial) + '.');
|
|
264
|
+
}
|
|
265
|
+
Object.keys(UX_DELETED_KEYS).forEach(function (k) {
|
|
266
|
+
if (partial[k] !== undefined) { _uxFail('"' + k + '" is DELETED — ' + UX_DELETED_KEYS[k] + '.'); }
|
|
267
|
+
});
|
|
268
|
+
if (partial.resizable !== undefined) {
|
|
269
|
+
if (typeof partial.resizable !== 'boolean') { _uxFail('"resizable" must be a boolean.'); }
|
|
270
|
+
bag.resizable = partial.resizable;
|
|
271
|
+
}
|
|
272
|
+
if (partial.draggable !== undefined) {
|
|
273
|
+
if (!_isPlainObject(partial.draggable)) { _uxFail('"draggable" must be a { "2d" } object.'); }
|
|
274
|
+
Object.keys(partial.draggable).forEach(function (k) {
|
|
275
|
+
if (UX_DRAGGABLE_MODES.indexOf(k) === -1) {
|
|
276
|
+
_uxFail('"draggable.' + k + '" is not a draggable mode ([' + UX_DRAGGABLE_MODES.join(', ') +
|
|
277
|
+
']) — a 3D scene never cursor-drags.');
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
var dr = { '2d': true };
|
|
281
|
+
for (var m = 0; m < UX_DRAGGABLE_MODES.length; m++) {
|
|
282
|
+
var mode = UX_DRAGGABLE_MODES[m];
|
|
283
|
+
if (partial.draggable[mode] !== undefined) {
|
|
284
|
+
if (typeof partial.draggable[mode] !== 'boolean') {
|
|
285
|
+
_uxFail('"draggable.' + mode + '" must be a boolean.');
|
|
286
|
+
}
|
|
287
|
+
dr[mode] = partial.draggable[mode];
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
bag.draggable = dr;
|
|
291
|
+
}
|
|
292
|
+
if (partial.handles !== undefined) {
|
|
293
|
+
if (!Array.isArray(partial.handles)) { _uxFail('"handles" must be an array of zone names.'); }
|
|
294
|
+
for (var h = 0; h < partial.handles.length; h++) {
|
|
295
|
+
if (UX_HANDLE_ZONES.indexOf(partial.handles[h]) === -1) {
|
|
296
|
+
_uxFail('"handles" entry "' + String(partial.handles[h]) + '" is not a member of ' +
|
|
297
|
+
'UX_HANDLE_ZONES ([' + UX_HANDLE_ZONES.join(', ') + ']).');
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
bag.handles = partial.handles.slice();
|
|
301
|
+
} else if (partial.resizable === false) {
|
|
302
|
+
// Ergonomic shorthand: an explicitly non-resizable object offers ONLY move.
|
|
303
|
+
bag.handles = ['move'];
|
|
304
|
+
}
|
|
305
|
+
if (partial.shellRender !== undefined) {
|
|
306
|
+
if (UX_SHELL_RENDER.indexOf(partial.shellRender) === -1) {
|
|
307
|
+
_uxFail('"shellRender" must be one of [' + UX_SHELL_RENDER.join(', ') + ']; got "' +
|
|
308
|
+
String(partial.shellRender) + '".');
|
|
309
|
+
}
|
|
310
|
+
bag.shellRender = partial.shellRender;
|
|
311
|
+
}
|
|
312
|
+
return bag;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// ── Export ────────────────────────────────────────────────────────────────────
|
|
316
|
+
|
|
317
|
+
export {
|
|
318
|
+
FIELD_TYPES,
|
|
319
|
+
FIELD_ENVELOPE_KEYS,
|
|
320
|
+
normalizeFieldDescriptor,
|
|
321
|
+
UX_HANDLE_ZONES,
|
|
322
|
+
UX_DRAGGABLE_MODES,
|
|
323
|
+
UX_SHELL_RENDER,
|
|
324
|
+
UX_ENVELOPE_KEYS,
|
|
325
|
+
normalizeUx
|
|
326
|
+
};
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* lib/shim/frame-data — The data-bubble schema contract.
|
|
5
|
+
*
|
|
6
|
+
* Single authority for the FrameData.data bag shape (§6 of
|
|
7
|
+
* docs/internal/engine/ENGINE_CONTRACT.md). The bubble is GENERIC and engine-opaque:
|
|
8
|
+
* SECTION_KEYS = ['detection', 'string_map']. The engine interprets NOTHING in
|
|
9
|
+
* string_map — it ferries every key verbatim across the JS↔WASM boundary.
|
|
10
|
+
*
|
|
11
|
+
* The data bubble has two cross-engine reserved namespaces (+ a JS-only ext):
|
|
12
|
+
* detection — written by the engine (MARK/DETECT primitives). Contains
|
|
13
|
+
* detector_name and any future detection tags.
|
|
14
|
+
* string_map — op-program-writable via `set 0.data.<key> = StrLit(value)`;
|
|
15
|
+
* a flat { key: string, ... } the engine carries opaque. ALL
|
|
16
|
+
* render data rides here as opaque keys: color is
|
|
17
|
+
* string_map['render.color'] (CSV 'r,g,b,a'); outline is
|
|
18
|
+
* string_map['render.outline'] === 'true'. Sugar/renderer code
|
|
19
|
+
* serializes on write and deserializes on read — there is NO
|
|
20
|
+
* typed `render` section (RETIRED: FrameDataRender,
|
|
21
|
+
* FrameMetaData.render, render_color SoA field, normalizeRender).
|
|
22
|
+
* ext — developer-extensible; engine carries opaque, never reads.
|
|
23
|
+
* NOTE (D2 decision): ext is JS-only. The Rust side has no ext
|
|
24
|
+
* field — serde_wasm_bindgen v0.6 cannot round-trip a free-form
|
|
25
|
+
* map through JsValue without serde_json::Value (which drops its
|
|
26
|
+
* contents). ext writes are carried through JS-only paths and are
|
|
27
|
+
* not asserted in cross-engine conformance. If ext survives a
|
|
28
|
+
* JS→WASM→JS round-trip, treat it as undefined behavior.
|
|
29
|
+
*
|
|
30
|
+
* The canonical section key list drives conformance (see §5 — the data-bubble
|
|
31
|
+
* round-trip oracle iterates over SECTION_KEYS to assert completeness).
|
|
32
|
+
*
|
|
33
|
+
* Usage:
|
|
34
|
+
* import { SECTION_KEYS, normalizeDataBubble, RENDER_INTENT_KEYS,
|
|
35
|
+
* normalizeRenderIntent } from 'lib/shim/frame-data/index.js';
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Canonical section key table.
|
|
42
|
+
* Conformance iterates this list — adding a section here MUST be accompanied
|
|
43
|
+
* by a conformance assertion (cross-engine oracle, §5 item 2).
|
|
44
|
+
*
|
|
45
|
+
* Sections:
|
|
46
|
+
* detection — engine-writable (MARK/DETECT); { detector_name: string|null }
|
|
47
|
+
* string_map — op-program-writable via `set 0.data.<key> = StrLit(value)`;
|
|
48
|
+
* flat object { key: string, ... }. Absent → null in normalized form.
|
|
49
|
+
* Cross-engine (JS + WASM). Owner: this file + FrameMetaData (tick_data.rs).
|
|
50
|
+
* ALL render hints ride here as opaque keys (render.color, render.outline);
|
|
51
|
+
* there is no typed `render` section.
|
|
52
|
+
*/
|
|
53
|
+
// 'render' section RETIRED — color (and all render hints) are opaque user-space data on
|
|
54
|
+
// string_map; the engine never carries a typed render section. Owner: this file + FrameMetaData.
|
|
55
|
+
const SECTION_KEYS = ['detection', 'string_map'];
|
|
56
|
+
// NOTE: 'ext' is deliberately excluded because it is not cross-engine assertable.
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Normalize a partial data bag to the full canonical shape.
|
|
60
|
+
*
|
|
61
|
+
* Rules:
|
|
62
|
+
* - Each reserved section (detection, string_map) is present as an object or null.
|
|
63
|
+
* - Absent → null (mirrors the JS toFrameData() :2872 and Rust skip_serializing_if).
|
|
64
|
+
* - Extra keys inside a section are preserved (pass-through).
|
|
65
|
+
* - The ext key is passed through as-is (JS-only, not normalized).
|
|
66
|
+
*
|
|
67
|
+
* @param {object|null|undefined} partial — partial data bag from a frame or FrameData
|
|
68
|
+
* @returns {{ detection: object|null, string_map: object|null, ext: any }}
|
|
69
|
+
*/
|
|
70
|
+
function normalizeDataBubble(partial) {
|
|
71
|
+
if (partial === null || partial === undefined) {
|
|
72
|
+
return { detection: null, string_map: null };
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
detection: (partial.detection !== null && partial.detection !== undefined)
|
|
76
|
+
? partial.detection
|
|
77
|
+
: null,
|
|
78
|
+
string_map: (partial.string_map !== null && partial.string_map !== undefined &&
|
|
79
|
+
typeof partial.string_map === 'object')
|
|
80
|
+
? partial.string_map
|
|
81
|
+
: null,
|
|
82
|
+
ext: partial.ext !== undefined ? partial.ext : undefined,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Normalize the detection sub-section.
|
|
88
|
+
* detector_name: string|null
|
|
89
|
+
*/
|
|
90
|
+
function normalizeDetection(partial) {
|
|
91
|
+
if (!partial) return { detector_name: null };
|
|
92
|
+
return {
|
|
93
|
+
detector_name: (partial.detector_name !== null && partial.detector_name !== undefined)
|
|
94
|
+
? String(partial.detector_name)
|
|
95
|
+
: null,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ── Render-intent vocabulary (Part C) ────────────────────────────────────────
|
|
100
|
+
//
|
|
101
|
+
// Render-intent keys are opaque string_map keys that sugar objects and experiment
|
|
102
|
+
// setups write to describe per-frame render intent. Providers deserialize them on
|
|
103
|
+
// read — the engine carries them verbatim and interprets nothing (I1 preserved).
|
|
104
|
+
//
|
|
105
|
+
// Key list owned HERE (single source). Conformance assertions MUST be driven off
|
|
106
|
+
// this list so that adding a key automatically extends the test surface.
|
|
107
|
+
//
|
|
108
|
+
// Priority in any provider:
|
|
109
|
+
// render.color (exact CSV r,g,b,a) > render.colorScheme ramp via LightColor > renderer default
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* RENDER_INTENT_KEYS — the canonical set of render-intent string_map keys.
|
|
113
|
+
*
|
|
114
|
+
* Conformance tests iterate this list. To add a new render-intent key:
|
|
115
|
+
* 1. Add the key string here.
|
|
116
|
+
* 2. Add its allowed values to RENDER_INTENT_VALUES.
|
|
117
|
+
* 3. Update normalizeRenderIntent to pass it through.
|
|
118
|
+
* 4. Add a conformance assertion (cross-engine oracle).
|
|
119
|
+
* 5. Update both providers to consume it.
|
|
120
|
+
*
|
|
121
|
+
* Current keys:
|
|
122
|
+
* render.color — CSV 'r,g,b,a' exact RGBA override (highest priority); existing. OPEN-ID (no value set).
|
|
123
|
+
* render.outline — 'true' | 'false' (or absent); existing. ENUM.
|
|
124
|
+
* render.shape — 'ball' | 'box' | 'circle' | 'cube' | 'sphere' (per-frame shape; absent → renderer default). ENUM.
|
|
125
|
+
* 'sphere' = a TRUE 3D instanced-sphere mesh (three, _is3D), symmetric with 'cube';
|
|
126
|
+
* in 2D / p5 it degrades to the filled 'ball' disc (no per-provider map — RenderShape
|
|
127
|
+
* is the one authority; the three sphere path is gated on _is3D like the cube path).
|
|
128
|
+
* render.colorScheme — 'roygbiv' | 'warmcool' (named LightColor ramp; absent → default). ENUM.
|
|
129
|
+
* render.mesh — external-geometry asset id (e.g. an Objaverse glTF id); absent → sprite/cube path.
|
|
130
|
+
* OPEN-ID: no RENDER_INTENT_VALUES entry (accept-any-string); the renderer's RenderMesh
|
|
131
|
+
* resolver FAILS LOUD at the output boundary if the asset cannot load — never silent.
|
|
132
|
+
* render.meshScale — per-frame scale MULTIPLIER for the frame's render.mesh clone (a number as a string;
|
|
133
|
+
* local override of the experiment-wide setRenderOption('meshScale',…)). OPEN-ID.
|
|
134
|
+
* render.meshRotation— per-frame euler rotation for the frame's render.mesh clone, a CSV of THREE radians
|
|
135
|
+
* 'rx,ry,rz' (same unit as a LargeObject's lo.meshRotation → clone.rotation.set). OPEN-ID.
|
|
136
|
+
* (Bite 5b R3-3: previously written ad-hoc but undeclared + unread; declared
|
|
137
|
+
* here at the owner and read per-clone in three-provider's _layoutMeshClones.)
|
|
138
|
+
*
|
|
139
|
+
* Two contract shapes (architect ruling 2026-06-30):
|
|
140
|
+
* ENUM key — has a RENDER_INTENT_VALUES[] array; decoders ferry the array (no re-listing); conformance-validated.
|
|
141
|
+
* OPEN-ID key — has NO RENDER_INTENT_VALUES entry → assertRenderIntentConformance accepts any string; the
|
|
142
|
+
* decoder/resolver owns load-time validation and fails loud. Covers render.color and render.mesh.
|
|
143
|
+
*/
|
|
144
|
+
const RENDER_INTENT_KEYS = [
|
|
145
|
+
'render.color',
|
|
146
|
+
'render.outline',
|
|
147
|
+
'render.shape',
|
|
148
|
+
'render.colorScheme',
|
|
149
|
+
'render.mesh',
|
|
150
|
+
'render.meshScale', // OPEN-ID — per-frame mesh scale multiplier (R3-3)
|
|
151
|
+
'render.meshRotation' // OPEN-ID — per-frame mesh euler radians CSV (R3-3)
|
|
152
|
+
];
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* RENDER_INTENT_VALUES — allowed values for each key (for validation).
|
|
156
|
+
* Keys without an entry accept any string (e.g. render.color is a CSV).
|
|
157
|
+
*/
|
|
158
|
+
const RENDER_INTENT_VALUES = {
|
|
159
|
+
'render.shape': ['ball', 'box', 'circle', 'cube', 'sphere'],
|
|
160
|
+
'render.colorScheme': ['roygbiv', 'warmcool'],
|
|
161
|
+
'render.outline': ['true', 'false']
|
|
162
|
+
// NOTE: render.color, render.mesh, render.meshScale, render.meshRotation are OPEN-ID keys —
|
|
163
|
+
// deliberately NO entry here (accept-any-string: a CSV / an asset id / a number / an euler CSV).
|
|
164
|
+
// Their decoders/resolvers own validation and fail loud at the boundary.
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* normalizeRenderIntent — ferry a partial render-intent bag to the full canonical shape.
|
|
169
|
+
*
|
|
170
|
+
* Reads only the keys declared in RENDER_INTENT_KEYS; copies them into the normalized
|
|
171
|
+
* bag if present; absent keys are omitted (not defaulted — the renderer defaults apply).
|
|
172
|
+
* This is the "ferry the bag" pattern: the caller never re-enumerates the key list.
|
|
173
|
+
*
|
|
174
|
+
* @param {object|null|undefined} partial — partial string_map or explicit intent object
|
|
175
|
+
* @returns {{ 'render.color'?: string, 'render.outline'?: string,
|
|
176
|
+
* 'render.shape'?: string, 'render.colorScheme'?: string }}
|
|
177
|
+
*/
|
|
178
|
+
function normalizeRenderIntent(partial) {
|
|
179
|
+
var bag = {};
|
|
180
|
+
if (!partial || typeof partial !== 'object') return bag;
|
|
181
|
+
for (var i = 0; i < RENDER_INTENT_KEYS.length; i++) {
|
|
182
|
+
var k = RENDER_INTENT_KEYS[i];
|
|
183
|
+
if (partial[k] !== undefined && partial[k] !== null) {
|
|
184
|
+
bag[k] = String(partial[k]);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return bag;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* assertRenderIntentConformance — throw if any key in a string_map bag violates the contract.
|
|
192
|
+
*
|
|
193
|
+
* Validates values for keys whose allowed set is enumerated in RENDER_INTENT_VALUES.
|
|
194
|
+
* Keys with unconstrained values (render.color, render.outline) are passed through.
|
|
195
|
+
* Unknown keys outside RENDER_INTENT_KEYS are ignored (the engine carries all keys opaque).
|
|
196
|
+
*
|
|
197
|
+
* @param {object} bag — string_map object to validate
|
|
198
|
+
* @throws {Error} if a constrained key holds an unknown value
|
|
199
|
+
*/
|
|
200
|
+
function assertRenderIntentConformance(bag) {
|
|
201
|
+
if (!bag) return;
|
|
202
|
+
for (var i = 0; i < RENDER_INTENT_KEYS.length; i++) {
|
|
203
|
+
var k = RENDER_INTENT_KEYS[i];
|
|
204
|
+
var allowed = RENDER_INTENT_VALUES[k];
|
|
205
|
+
if (allowed && bag[k] !== undefined && allowed.indexOf(bag[k]) === -1) {
|
|
206
|
+
throw new Error(
|
|
207
|
+
'render-intent conformance: key "' + k + '" has invalid value "' + bag[k] +
|
|
208
|
+
'"; allowed: ' + allowed.join('|')
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// ── Export ────────────────────────────────────────────────────────────────────
|
|
215
|
+
|
|
216
|
+
export {
|
|
217
|
+
SECTION_KEYS,
|
|
218
|
+
normalizeDataBubble,
|
|
219
|
+
normalizeDetection,
|
|
220
|
+
RENDER_INTENT_KEYS,
|
|
221
|
+
RENDER_INTENT_VALUES,
|
|
222
|
+
normalizeRenderIntent,
|
|
223
|
+
assertRenderIntentConformance
|
|
224
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* lib/shim — the unified shim router (was lib/common).
|
|
5
|
+
*
|
|
6
|
+
* ESM barrel: re-exports all named exports. A missing import is a hard
|
|
7
|
+
* FAIL-LOUD load error — no window.Common* ambient globals.
|
|
8
|
+
*
|
|
9
|
+
* Usage (under vite with alias 'lib' → repo-root/lib):
|
|
10
|
+
* import { Bus, Runtime, EXPERIMENTS, engineRouter } from 'lib/shim';
|
|
11
|
+
*
|
|
12
|
+
* Usage (direct relative path):
|
|
13
|
+
* import { Bus } from '../../lib/shim/index.js';
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export { Bus } from './bus.js';
|
|
17
|
+
export { Runtime } from './runtime/index.js';
|
|
18
|
+
export { EXPERIMENTS, EXPERIMENT_TIERS, experimentById, experimentIs3D,
|
|
19
|
+
isPublicExperiment, publicExperiments,
|
|
20
|
+
internalExperiments } from './registry/experiments.js';
|
|
21
|
+
export { WALKTHROUGH, walkthroughIs3D, walkthroughModes, walkthroughGroup,
|
|
22
|
+
walkthroughById,
|
|
23
|
+
FIRST_CONTACT_WALKTHROUGH_ID,
|
|
24
|
+
defaultWalkthroughModeId, effectiveRenderState } from './registry/walkthrough.js';
|
|
25
|
+
export { loadSceneSource, scenePath, EMPTY_SCENE } from './registry/scene-source.js';
|
|
26
|
+
// The host-side experiment-iframe guard (the false-200 trap). Exported from the
|
|
27
|
+
// barrel so every host that frames an experiment page (lib/features/ide's GYM)
|
|
28
|
+
// asserts against the SAME contract.
|
|
29
|
+
export { EXPERIMENT_PAGE_MARKERS, HOST_SHELL_ROOT_SELECTOR, FRAME_VERDICTS,
|
|
30
|
+
DEFAULT_FRAME_TIMEOUT_MS, classifyFrameProbe, probeExperimentFrame,
|
|
31
|
+
paintFrameGuardOverlay, watchExperimentFrame } from './experiment-frame/index.js';
|
|
32
|
+
export { engineRouter, DEV_CORE_PROFILE, DIST_PROFILE } from './engine/router.js';
|
|
33
|
+
// Math backend registry — re-exported so lib/features can import from the shim
|
|
34
|
+
// barrel (the sanctioned route) without reaching past the shim into lib/egpt directly.
|
|
35
|
+
// Module identity is preserved: both the barrel re-export and any direct import of
|
|
36
|
+
// MathBackendRegistry.js resolve to the SAME singleton because ESM caches by
|
|
37
|
+
// canonical URL — there is only one MathBackendRegistry instance. The registry
|
|
38
|
+
// module has zero imports; the JS reference core never enters this graph.
|
|
39
|
+
export { MathBackendRegistry, MathBackendNotAvailableError } from '../egpt/js/model/backend/MathBackendRegistry.js';
|
|
40
|
+
// Notebook spine (WS-NOTEBOOK-A1): the native format parser/serializer and the
|
|
41
|
+
// per-notebook kernel. The kernel calls Runtime.runCell directly (no Bus
|
|
42
|
+
// executor). Consumers (the A2 notebook pane) import these from the barrel.
|
|
43
|
+
export { parse, serialize, normalize } from './notebook/format.js';
|
|
44
|
+
export { NotebookKernel, NOTEBOOK_CELL_STATUS } from './notebook/kernel.js';
|
|
45
|
+
// Cell-as-module resolver (WS-NOTEBOOK-IMPORT B2) — the host-neutral in-memory
|
|
46
|
+
// blob closure logic. Re-exported so tests + any host can build the closure
|
|
47
|
+
// (Runtime injects the browser blob minter; the resolver itself is DOM-free and
|
|
48
|
+
// must NOT assume a pane exists — model-gate ruling 6).
|
|
49
|
+
export {
|
|
50
|
+
canonKey, resolveRelativeKey, hasImport, splitImportsAndBody,
|
|
51
|
+
rewriteRelativeToSiblingBlobs, buildModuleClosure, compileEntryModuleSource
|
|
52
|
+
} from './notebook/module-resolver.js';
|
|
53
|
+
// Notebook webview-realm model (WS-NOTEBOOK-REALM): the FOURTH BYO pluggable
|
|
54
|
+
// surface. ONE registry authority + the frozen view-handle contract + the in-app
|
|
55
|
+
// transport (same-origin direct fast path). A realm pre-load
|
|
56
|
+
// is an above-the-shim renderer env; the frqtl pre-load co-locates the engine.
|
|
57
|
+
export * as RealmRegistry from './notebook/realm-registry.js';
|
|
58
|
+
export { makeViewHandle, isViewHandle, assertSerializable } from './notebook/view-handle.js';
|
|
59
|
+
export { inAppTransport } from './notebook/realm-transport.js';
|
|
60
|
+
// Realm import map — buildImportMap (vendor manifest → `{ imports }`), buildRealmImportMap
|
|
61
|
+
// (vendor + @frqtl/setups, through ONE router) and spliceRealmImportMap (the ONE splice the
|
|
62
|
+
// served hosts, lib/server/realm-html.js, and the static bake, apps/ide/vite.config.js, both
|
|
63
|
+
// call). PURE, DOM-free.
|
|
64
|
+
export { buildImportMap, buildRealmImportMap, spliceRealmImportMap } from './notebook/vendor-importmap.js';
|
|
65
|
+
// Canonical setup-factory bare-specifier surface — the manifest and its import-map fragment
|
|
66
|
+
// (merged by buildRealmImportMap above). PURE, DOM-free.
|
|
67
|
+
export { buildFrqtlSetupsImportMap, FRQTL_SETUPS_MANIFEST } from './notebook/frqtl-setups-importmap.js';
|