@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,249 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* lib/shim/storage/modules.js — Experiment module (.js text) storage helpers.
|
|
5
|
+
*
|
|
6
|
+
* Defines the Module envelope shape and CRUD helpers that operate on top of
|
|
7
|
+
* any StorageProvider (lib/shim/storage/index.js) using the "files" namespace.
|
|
8
|
+
*
|
|
9
|
+
* ── Module envelope ────────────────────────────────────────────────────────
|
|
10
|
+
*
|
|
11
|
+
* {
|
|
12
|
+
* id: string — unique identifier (newModuleId())
|
|
13
|
+
* name: string — human-readable module name
|
|
14
|
+
* language: 'javascript'
|
|
15
|
+
* source: string — the .js module text
|
|
16
|
+
* templateId: string|null — origin experiment id or null if blank
|
|
17
|
+
* kind: 'frqtl' | 'math' — which SDK the recipe targets (v2+)
|
|
18
|
+
* createdAt: string — ISO-8601 creation timestamp
|
|
19
|
+
* updatedAt: string — ISO-8601 last-updated timestamp
|
|
20
|
+
* schemaVersion: number — integer version (see MODULE_SCHEMA_VERSION,
|
|
21
|
+
* the authority; currently 4)
|
|
22
|
+
* scripts: string[] — v4+. OPAQUE frqtl-relative setup-factory
|
|
23
|
+
* delta the load path injects (engineRouter.resolve) BEFORE
|
|
24
|
+
* eval, so a ferried recipe whose source calls a setup factory
|
|
25
|
+
* (a walk-through rung) resolves that window global. `[]`/absent
|
|
26
|
+
* for an ordinary saved project (DX-3, the rung-source ferry).
|
|
27
|
+
* renderState: object|null — v3+. Studio's Save realignment
|
|
28
|
+
* (WS Studio Controls Realignment §2.6) capture bag —
|
|
29
|
+
* everything the .js recipe itself does NOT round-trip:
|
|
30
|
+
* renderer/seed (load-time), render capability
|
|
31
|
+
* values + camera + backdrop visibility + bind-backed
|
|
32
|
+
* page-level control values (runtime replay). Shape owned
|
|
33
|
+
* by `lib/features/ide/save-state.js`'s
|
|
34
|
+
* `normalizeSavedRenderState` — this module does NOT
|
|
35
|
+
* re-validate its internal shape (an optional, best-effort
|
|
36
|
+
* replay bag, never load-bearing for the recipe itself);
|
|
37
|
+
* `null`/absent is the pre-S6 legacy state and stays
|
|
38
|
+
* perfectly valid (R6 discipline extended to v3).
|
|
39
|
+
* }
|
|
40
|
+
*
|
|
41
|
+
* ── Schema migration ────────────────────────────────────────────────────────
|
|
42
|
+
*
|
|
43
|
+
* v1 → v2: `kind` field added. Legacy v1 envelopes (no `kind` field) are
|
|
44
|
+
* read without error and default to `kind:'frqtl'` (R6 binding: must never
|
|
45
|
+
* throw on legacy envelopes). The `kind` field is the ONLY new field (AM-2).
|
|
46
|
+
*
|
|
47
|
+
* v2 → v3: `renderState` field added (WS Studio Controls Realignment §2.6,
|
|
48
|
+
* Bite S6). Legacy v1/v2 envelopes (no `renderState` field) are read without
|
|
49
|
+
* error and default to `renderState: null` (R6 extended) — a saved project
|
|
50
|
+
* from before this bite simply replays with NO render/camera/page-control
|
|
51
|
+
* state (byte-identical to its own pre-S6 behavior), never a throw.
|
|
52
|
+
*
|
|
53
|
+
* ── Namespace ──────────────────────────────────────────────────────────────
|
|
54
|
+
*
|
|
55
|
+
* All helpers use the "files" namespace. Keys are module ids.
|
|
56
|
+
*
|
|
57
|
+
* ── Usage ──────────────────────────────────────────────────────────────────
|
|
58
|
+
*
|
|
59
|
+
* import { newModuleId, saveModule, loadModule,
|
|
60
|
+
* listModules, deleteModule } from 'lib/shim/storage/modules.js';
|
|
61
|
+
* import { getProvider } from 'lib/shim/storage';
|
|
62
|
+
*
|
|
63
|
+
* const provider = getProvider();
|
|
64
|
+
*
|
|
65
|
+
* const id = newModuleId();
|
|
66
|
+
* const mod = {
|
|
67
|
+
* id,
|
|
68
|
+
* name: 'My wave experiment',
|
|
69
|
+
* language: 'javascript',
|
|
70
|
+
* source: 'function buildExperiment(exp) { ... }',
|
|
71
|
+
* templateId: 'double-slit',
|
|
72
|
+
* kind: 'frqtl', // or 'math' for EGPTMath recipes
|
|
73
|
+
* createdAt: new Date().toISOString(),
|
|
74
|
+
* updatedAt: new Date().toISOString(),
|
|
75
|
+
* schemaVersion: 2,
|
|
76
|
+
* };
|
|
77
|
+
* saveModule(provider, mod);
|
|
78
|
+
*
|
|
79
|
+
* const loaded = loadModule(provider, id);
|
|
80
|
+
* const all = listModules(provider);
|
|
81
|
+
* deleteModule(provider, id);
|
|
82
|
+
*/
|
|
83
|
+
|
|
84
|
+
'use strict';
|
|
85
|
+
|
|
86
|
+
import { makeNamespaceStore } from './make-namespace-store.js';
|
|
87
|
+
|
|
88
|
+
/** @type {string} The storage namespace for all module records. */
|
|
89
|
+
export var MODULES_NAMESPACE = 'files';
|
|
90
|
+
|
|
91
|
+
/** @type {number} Current schema version. Increment on breaking envelope changes. */
|
|
92
|
+
export var MODULE_SCHEMA_VERSION = 4;
|
|
93
|
+
|
|
94
|
+
// ── Envelope validation ────────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
function _validateModule(mod) {
|
|
97
|
+
if (!mod || typeof mod !== 'object') {
|
|
98
|
+
throw new Error('[modules] module must be a non-null object');
|
|
99
|
+
}
|
|
100
|
+
if (typeof mod.id !== 'string' || mod.id.length === 0) {
|
|
101
|
+
throw new Error('[modules] module.id must be a non-empty string');
|
|
102
|
+
}
|
|
103
|
+
if (typeof mod.name !== 'string' || mod.name.length === 0) {
|
|
104
|
+
throw new Error('[modules] module.name must be a non-empty string');
|
|
105
|
+
}
|
|
106
|
+
if (typeof mod.source !== 'string') {
|
|
107
|
+
throw new Error('[modules] module.source must be a string');
|
|
108
|
+
}
|
|
109
|
+
if (mod.source.length === 0) {
|
|
110
|
+
throw new Error('[modules] module.source must not be empty');
|
|
111
|
+
}
|
|
112
|
+
if (mod.templateId !== null && typeof mod.templateId !== 'string') {
|
|
113
|
+
throw new Error('[modules] module.templateId must be a string or null');
|
|
114
|
+
}
|
|
115
|
+
if (mod.kind !== 'frqtl' && mod.kind !== 'math') {
|
|
116
|
+
throw new Error('[modules] module.kind must be "frqtl" or "math"');
|
|
117
|
+
}
|
|
118
|
+
if (typeof mod.schemaVersion !== 'number') {
|
|
119
|
+
throw new Error('[modules] module.schemaVersion must be a number');
|
|
120
|
+
}
|
|
121
|
+
// renderState (v3+) is OPTIONAL and best-effort (see envelope doc above) —
|
|
122
|
+
// the only shape check here is "object or null/undefined", never a deep
|
|
123
|
+
// validation of its internal fields (that tolerance lives in save-state.js's
|
|
124
|
+
// own normalizeSavedRenderState, applied at REPLAY time, not at save time).
|
|
125
|
+
if (mod.renderState !== undefined && mod.renderState !== null &&
|
|
126
|
+
(typeof mod.renderState !== 'object' || Array.isArray(mod.renderState))) {
|
|
127
|
+
throw new Error('[modules] module.renderState must be an object, null, or omitted');
|
|
128
|
+
}
|
|
129
|
+
// scripts (v4+) is OPTIONAL — an OPAQUE frqtl-relative setup-factory delta the
|
|
130
|
+
// load path injects (engineRouter.resolve) BEFORE eval, so a recipe whose source
|
|
131
|
+
// calls a setup factory (e.g. a ferried walk-through rung) resolves that global.
|
|
132
|
+
// Shape check only: an array of strings, or omitted. The loaders own the
|
|
133
|
+
// resolution + FAIL-LOUD injection, not this schema.
|
|
134
|
+
if (mod.scripts !== undefined && mod.scripts !== null) {
|
|
135
|
+
if (!Array.isArray(mod.scripts) || !mod.scripts.every(function (s) { return typeof s === 'string'; })) {
|
|
136
|
+
throw new Error('[modules] module.scripts must be an array of strings, null, or omitted');
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ── v1 → v2 migration ─────────────────────────────────────────────────────
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Migrate a raw stored envelope to the current schema version.
|
|
145
|
+
* Safe to call on already-current envelopes (idempotent).
|
|
146
|
+
*
|
|
147
|
+
* v1 → v2: default `kind` to `'frqtl'` when the field is absent.
|
|
148
|
+
* R6 (binding): this must NEVER throw on legacy v1 envelopes.
|
|
149
|
+
*
|
|
150
|
+
* v2 → v3: default `renderState` to `null` when the field is absent (WS
|
|
151
|
+
* Studio Controls Realignment §2.6, Bite S6). R6 extended: never throw on
|
|
152
|
+
* a legacy v1/v2 envelope with no `renderState` field.
|
|
153
|
+
*
|
|
154
|
+
* v3 → v4: default `scripts` to `[]` when the field is absent (ws-experiments-
|
|
155
|
+
* walkthrough DX-3 — the rung-source ferry). R6 extended again: never throw on
|
|
156
|
+
* a legacy envelope with no `scripts` field (an ordinary saved project has none).
|
|
157
|
+
*
|
|
158
|
+
* @param {object} mod Raw object from storage.
|
|
159
|
+
* @returns {object} Envelope at the current schema version.
|
|
160
|
+
*/
|
|
161
|
+
function _migrate(mod) {
|
|
162
|
+
if (!mod || typeof mod !== 'object') { return mod; }
|
|
163
|
+
var patch = {};
|
|
164
|
+
if (mod.kind === undefined || mod.kind === null) { patch.kind = 'frqtl'; }
|
|
165
|
+
if (mod.renderState === undefined) { patch.renderState = null; }
|
|
166
|
+
if (mod.scripts === undefined) { patch.scripts = []; }
|
|
167
|
+
if (Object.keys(patch).length === 0) { return mod; }
|
|
168
|
+
return Object.assign({}, mod, patch);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ── kind filter ────────────────────────────────────────────────────────────
|
|
172
|
+
|
|
173
|
+
function _kindFilter(mod, opts) {
|
|
174
|
+
var kindFilter = (opts && opts.kind) ? opts.kind : null;
|
|
175
|
+
if (kindFilter !== null && mod.kind !== kindFilter) { return false; }
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// ── Summary projection ─────────────────────────────────────────────────────
|
|
180
|
+
|
|
181
|
+
function _summaryFields(mod) {
|
|
182
|
+
// Skip malformed records that are missing required summary fields.
|
|
183
|
+
if (typeof mod.name !== 'string') { return null; }
|
|
184
|
+
return {
|
|
185
|
+
id: mod.id,
|
|
186
|
+
name: mod.name,
|
|
187
|
+
kind: mod.kind,
|
|
188
|
+
templateId: mod.templateId || null,
|
|
189
|
+
updatedAt: mod.updatedAt || '',
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// ── Store instance ─────────────────────────────────────────────────────────
|
|
194
|
+
|
|
195
|
+
var _store = makeNamespaceStore({
|
|
196
|
+
namespace: MODULES_NAMESPACE,
|
|
197
|
+
schemaVersion: MODULE_SCHEMA_VERSION,
|
|
198
|
+
idPrefix: 'mod_',
|
|
199
|
+
validate: _validateModule,
|
|
200
|
+
summaryFields: _summaryFields,
|
|
201
|
+
migrate: _migrate,
|
|
202
|
+
listFilter: _kindFilter,
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// ── Public API (names and signatures unchanged) ────────────────────────────
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Generate a new unique module id.
|
|
209
|
+
* @returns {string} e.g. "mod_lxyz0abc1234"
|
|
210
|
+
*/
|
|
211
|
+
export var newModuleId = _store.newId;
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Save (create or update) a module.
|
|
215
|
+
* @param {import('./index.js').StorageProvider} provider
|
|
216
|
+
* @param {object} mod
|
|
217
|
+
* @throws {Error} on validation failure or provider write failure.
|
|
218
|
+
*/
|
|
219
|
+
export var saveModule = _store.save;
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Load a module by id.
|
|
223
|
+
* Legacy v1 envelopes (no `kind` field) are migrated transparently (R6).
|
|
224
|
+
* @param {import('./index.js').StorageProvider} provider
|
|
225
|
+
* @param {string} id
|
|
226
|
+
* @returns {object|null}
|
|
227
|
+
*/
|
|
228
|
+
export var loadModule = _store.load;
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* List modules, returning summary objects suitable for a module picker.
|
|
232
|
+
*
|
|
233
|
+
* Returns an array of { id, name, kind, templateId, updatedAt } objects.
|
|
234
|
+
* Sorted by updatedAt descending.
|
|
235
|
+
* AM-2 (binding): filtering via opts.kind — pass to restrict, omit for all.
|
|
236
|
+
*
|
|
237
|
+
* @param {import('./index.js').StorageProvider} provider
|
|
238
|
+
* @param {object} [opts]
|
|
239
|
+
* @param {'frqtl'|'math'|undefined} [opts.kind]
|
|
240
|
+
* @returns {Array<{id: string, name: string, kind: string, templateId: string|null, updatedAt: string}>}
|
|
241
|
+
*/
|
|
242
|
+
export var listModules = _store.list;
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Delete a module by id. No-op if absent.
|
|
246
|
+
* @param {import('./index.js').StorageProvider} provider
|
|
247
|
+
* @param {string} id
|
|
248
|
+
*/
|
|
249
|
+
export var deleteModule = _store['delete'];
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* lib/shim/storage/projects.js — Project-envelope helpers.
|
|
5
|
+
*
|
|
6
|
+
* Defines the Project envelope shape and CRUD helpers that operate on top of
|
|
7
|
+
* any StorageProvider (lib/shim/storage/index.js) using the "projects"
|
|
8
|
+
* namespace.
|
|
9
|
+
*
|
|
10
|
+
* ── Project envelope ───────────────────────────────────────────────────────
|
|
11
|
+
*
|
|
12
|
+
* {
|
|
13
|
+
* id: string — UUID-style unique identifier (newProjectId())
|
|
14
|
+
* name: string — human-readable project name
|
|
15
|
+
* templateId: string|null — origin experiment id ("double-slit",
|
|
16
|
+
* "blackbody", etc.) or null if blank
|
|
17
|
+
* createdAt: string — ISO-8601 creation timestamp
|
|
18
|
+
* updatedAt: string — ISO-8601 last-updated timestamp
|
|
19
|
+
* schemaVersion: number — integer version; currently 1
|
|
20
|
+
* design: object — opaque JSON authored by AuthorView
|
|
21
|
+
* (do NOT interpret here — that is the
|
|
22
|
+
* AuthorView workstream's responsibility)
|
|
23
|
+
* }
|
|
24
|
+
*
|
|
25
|
+
* ── Namespace ──────────────────────────────────────────────────────────────
|
|
26
|
+
*
|
|
27
|
+
* All helpers use the "projects" namespace. Keys are project ids.
|
|
28
|
+
*
|
|
29
|
+
* ── Usage ──────────────────────────────────────────────────────────────────
|
|
30
|
+
*
|
|
31
|
+
* import { newProjectId, saveProject, loadProject,
|
|
32
|
+
* listProjects, deleteProject } from 'lib/shim/storage/projects.js';
|
|
33
|
+
* import { getProvider } from 'lib/shim/storage';
|
|
34
|
+
*
|
|
35
|
+
* const provider = getProvider();
|
|
36
|
+
*
|
|
37
|
+
* const id = newProjectId();
|
|
38
|
+
* const project = {
|
|
39
|
+
* id,
|
|
40
|
+
* name: 'My first project',
|
|
41
|
+
* templateId: 'double-slit',
|
|
42
|
+
* createdAt: new Date().toISOString(),
|
|
43
|
+
* updatedAt: new Date().toISOString(),
|
|
44
|
+
* schemaVersion: 1,
|
|
45
|
+
* design: {},
|
|
46
|
+
* };
|
|
47
|
+
* saveProject(provider, project);
|
|
48
|
+
*
|
|
49
|
+
* const loaded = loadProject(provider, id);
|
|
50
|
+
* const all = listProjects(provider);
|
|
51
|
+
* deleteProject(provider, id);
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
'use strict';
|
|
55
|
+
|
|
56
|
+
import { makeNamespaceStore } from './make-namespace-store.js';
|
|
57
|
+
|
|
58
|
+
/** @type {string} The storage namespace for all project records. */
|
|
59
|
+
export var PROJECTS_NAMESPACE = 'projects';
|
|
60
|
+
|
|
61
|
+
/** @type {number} Current schema version. Increment on breaking envelope changes. */
|
|
62
|
+
export var PROJECT_SCHEMA_VERSION = 1;
|
|
63
|
+
|
|
64
|
+
// ── Envelope validation ────────────────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
function _validateProject(project) {
|
|
67
|
+
if (!project || typeof project !== 'object') {
|
|
68
|
+
throw new Error('[projects] project must be a non-null object');
|
|
69
|
+
}
|
|
70
|
+
if (typeof project.id !== 'string' || project.id.length === 0) {
|
|
71
|
+
throw new Error('[projects] project.id must be a non-empty string');
|
|
72
|
+
}
|
|
73
|
+
if (typeof project.name !== 'string' || project.name.length === 0) {
|
|
74
|
+
throw new Error('[projects] project.name must be a non-empty string');
|
|
75
|
+
}
|
|
76
|
+
if (project.templateId !== null && typeof project.templateId !== 'string') {
|
|
77
|
+
throw new Error('[projects] project.templateId must be a string or null');
|
|
78
|
+
}
|
|
79
|
+
if (typeof project.schemaVersion !== 'number') {
|
|
80
|
+
throw new Error('[projects] project.schemaVersion must be a number');
|
|
81
|
+
}
|
|
82
|
+
if (typeof project.design !== 'object' || project.design === null) {
|
|
83
|
+
throw new Error('[projects] project.design must be a non-null object');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ── Summary projection ─────────────────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
function _summaryFields(project) {
|
|
90
|
+
// Skip malformed records that are missing required summary fields.
|
|
91
|
+
if (typeof project.name !== 'string') { return null; }
|
|
92
|
+
return {
|
|
93
|
+
id: project.id,
|
|
94
|
+
name: project.name,
|
|
95
|
+
templateId: project.templateId || null,
|
|
96
|
+
updatedAt: project.updatedAt || '',
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ── Store instance ─────────────────────────────────────────────────────────
|
|
101
|
+
|
|
102
|
+
var _store = makeNamespaceStore({
|
|
103
|
+
namespace: PROJECTS_NAMESPACE,
|
|
104
|
+
schemaVersion: PROJECT_SCHEMA_VERSION,
|
|
105
|
+
idPrefix: 'proj_',
|
|
106
|
+
validate: _validateProject,
|
|
107
|
+
summaryFields: _summaryFields,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// ── Public API (names and signatures unchanged) ────────────────────────────
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Generate a new unique project id.
|
|
114
|
+
* @returns {string} e.g. "proj_lxyz0abc1234"
|
|
115
|
+
*/
|
|
116
|
+
export var newProjectId = _store.newId;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Save (create or update) a project.
|
|
120
|
+
* @param {import('./index.js').StorageProvider} provider
|
|
121
|
+
* @param {object} project
|
|
122
|
+
* @throws {Error} on validation failure or provider write failure.
|
|
123
|
+
*/
|
|
124
|
+
export var saveProject = _store.save;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Load a project by id.
|
|
128
|
+
* @param {import('./index.js').StorageProvider} provider
|
|
129
|
+
* @param {string} id
|
|
130
|
+
* @returns {object|null}
|
|
131
|
+
*/
|
|
132
|
+
export var loadProject = _store.load;
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* List all projects, returning summary objects suitable for a project picker.
|
|
136
|
+
*
|
|
137
|
+
* Returns an array of { id, name, templateId, updatedAt } objects.
|
|
138
|
+
* Sorted by updatedAt descending.
|
|
139
|
+
*
|
|
140
|
+
* @param {import('./index.js').StorageProvider} provider
|
|
141
|
+
* @returns {Array<{id: string, name: string, templateId: string|null, updatedAt: string}>}
|
|
142
|
+
*/
|
|
143
|
+
export var listProjects = _store.list;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Delete a project by id. No-op if absent.
|
|
147
|
+
* @param {import('./index.js').StorageProvider} provider
|
|
148
|
+
* @param {string} id
|
|
149
|
+
*/
|
|
150
|
+
export var deleteProject = _store['delete'];
|
package/llms.txt
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# @descix/frqtl-node
|
|
2
|
+
|
|
3
|
+
> The FRQTL physics engine (compiled Rust → WASM) for node: the same modules as @descix/frqtl-web, plus
|
|
4
|
+
> `bootNode` and the hosted eyes. Pre-release 0.6.0-p0.1 on npm dist-tag `next`. Node ≥ 22.12.
|
|
5
|
+
|
|
6
|
+
- Install: `npm install @descix/frqtl-node@next`
|
|
7
|
+
- `import { bootNode, Wall } from '@descix/frqtl-node'` (require() works too); `await bootNode()` → the engine handle.
|
|
8
|
+
- `./eyes`: `openHostedEyes({ sceneRecord, sceneSource: { kind: 'project', files, entry }, pageUrl })`.
|
|
9
|
+
- `./project`: `linkProject` links a scene file against this package's own copy.
|
|
10
|
+
- `./register`: `node --import @descix/frqtl-node/register scene.mjs` resolves a scene's `@descix/frqtl-web/...` imports to this package.
|
|
11
|
+
- Full agent guide: https://cdn.jsdelivr.net/npm/@descix/frqtl-node@0.6.0-p0.1/CLAUDE.md
|
|
12
|
+
- Acceptance is visual; no CLI ships. No account, credits or tokens are needed.
|
package/package.json
CHANGED
|
@@ -1,6 +1,64 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@descix/frqtl-node",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.6.0-p0.1",
|
|
4
|
+
"description": "FRQTL for node \u2014 the discrete fractal physics engine (compiled Rust \u2192 WASM), its parts as ES modules, the node engine door (bootNode) and the hosted eyes.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=22.12"
|
|
8
|
+
},
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./dist/frqtl/node-entry.js",
|
|
11
|
+
"./eyes": "./dist/eyes/index.js",
|
|
12
|
+
"./parts": "./dist/frqtl/parts/index.js",
|
|
13
|
+
"./parts/*": "./dist/frqtl/parts/*",
|
|
14
|
+
"./project": "./dist/shim/project/index.js",
|
|
15
|
+
"./registry": "./dist/shim/registry/index.js",
|
|
16
|
+
"./register": "./dist/shim/project/register-node.mjs",
|
|
17
|
+
"./shim/controls/registry.js": "./dist/shim/controls/registry.js",
|
|
18
|
+
"./shim/render/sun-look.js": "./dist/shim/render/sun-look.js",
|
|
19
|
+
"./compat/renderer/display-slot.js": "./dist/frqtl/compat/renderer/display-slot.js",
|
|
20
|
+
"./compat/renderer/orbit-bag.js": "./dist/frqtl/compat/renderer/orbit-bag.js",
|
|
21
|
+
"./objects/setup/_collisionMatrixLanes.js": "./dist/frqtl/objects/setup/_collisionMatrixLanes.js",
|
|
22
|
+
"./compat/engine.js": "./dist/frqtl/compat/engine.js",
|
|
23
|
+
"./kinds": "./dist/frqtl/objects/kinds-entry.js",
|
|
24
|
+
"./dist-manifest.json": "./dist/dist-manifest.json",
|
|
25
|
+
"./package.json": "./package.json"
|
|
26
|
+
},
|
|
27
|
+
"exportsNotes": "Every target is a file the build places at its lib-identity path (dist/frqtl/..., dist/shim/...), the same files @descix/frqtl-web ships (one source built twice; the [SHARED] gate holds every shared placement byte-identical). `.` is the parts barrel + bootNode + the shared package core (lib/frqtl/node-entry.js); `require()` of it works from CommonJS (require(esm): nothing awaits at top level). `./eyes` is the hosted eyes (openHostedEyes), node-only, the one surface that pulls playwright. `./parts`, `./parts/*`, `./project`, `./registry` and `./dist-manifest.json` are the same rows as @descix/frqtl-web's. The expected shape of this map is owned by sdk/_build/exports-contract.mjs.",
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"README.md",
|
|
31
|
+
"llms.txt",
|
|
32
|
+
"CLAUDE.md"
|
|
33
|
+
],
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "node build.js",
|
|
36
|
+
"verify:packages": "node ../_build/verify-publish-surface.mjs",
|
|
37
|
+
"verify:build-closure": "node ../_build/verify-build-closure.mjs",
|
|
38
|
+
"verify:parity-core": "node ../_build/verify-parity-core.mjs",
|
|
39
|
+
"pack:dry-run": "npm pack --dry-run --ignore-scripts",
|
|
40
|
+
"prepack": "npm run build && npm run verify:packages && npm run verify:build-closure && npm run verify:parity-core"
|
|
41
|
+
},
|
|
42
|
+
"notes": "npm publish/pack runs from this directory. `prepack` rebuilds dist/ and re-verifies BOTH FRQTL packages (sdk/_build/verify-publish-surface.mjs), so sdk/frqtl-web must be built first.",
|
|
43
|
+
"keywords": [
|
|
44
|
+
"physics",
|
|
45
|
+
"engine",
|
|
46
|
+
"simulation",
|
|
47
|
+
"fractal",
|
|
48
|
+
"cellular-automata",
|
|
49
|
+
"particle",
|
|
50
|
+
"quantum",
|
|
51
|
+
"wave-particle-duality",
|
|
52
|
+
"emergent",
|
|
53
|
+
"discrete-physics",
|
|
54
|
+
"egpt",
|
|
55
|
+
"frqtl",
|
|
56
|
+
"descix"
|
|
57
|
+
],
|
|
58
|
+
"author": "Essam Abadir",
|
|
59
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"playwright": "1.61.0"
|
|
62
|
+
},
|
|
63
|
+
"dependencyNotes": "playwright is the DEFAULT STACK for exports[\"./eyes\"] (the hosted eyes): `composite` is the render stack's own output, so an agent must see it through a REAL browser or it is looking at a different picture than the user. It is therefore a REAL dependency, PINNED to an exact version (a range would let a browser build drift under a published corpus \u2014 the raster path is provenance, see build.gl), and deliberately NOT optionalDependencies (an optional dependency is a compat fence wearing a package-manager hat). It is reached only by the ./eyes entry \u2014 a consumer of `.` never loads it \u2014 and `browserType` is the BYO seam for a consumer who brings their own."
|
|
64
|
+
}
|