@descix/frqtl-node 0.0.0-stage → 0.6.0-p0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. package/CLAUDE.md +87 -0
  2. package/LICENSE.md +664 -0
  3. package/README.md +84 -2
  4. package/dist/dist-manifest.json +218 -0
  5. package/dist/eyes/index.js +109 -0
  6. package/dist/fat/pkg-node/egpt_wasm.js +1277 -0
  7. package/dist/fat/pkg-node/egpt_wasm_bg.wasm +0 -0
  8. package/dist/fat/pkg-node/package.json +12 -0
  9. package/dist/frqtl/assets/vendor/chroma.mjs +64 -0
  10. package/dist/frqtl/compat/boot-node.js +34 -0
  11. package/dist/frqtl/compat/engine.js +127 -0
  12. package/dist/frqtl/compat/package-root.js +24 -0
  13. package/dist/frqtl/compat/providers/wasm-engine.js +2499 -0
  14. package/dist/frqtl/compat/providers/wasm-load-browser.js +134 -0
  15. package/dist/frqtl/compat/providers/wasm-load-node.js +59 -0
  16. package/dist/frqtl/compat/renderer/FieldBuilder.js +145 -0
  17. package/dist/frqtl/compat/renderer/Filters.js +163 -0
  18. package/dist/frqtl/compat/renderer/RenderFamily.js +84 -0
  19. package/dist/frqtl/compat/renderer/RenderMesh.js +99 -0
  20. package/dist/frqtl/compat/renderer/RenderShape.js +154 -0
  21. package/dist/frqtl/compat/renderer/camera2d-geometry.js +183 -0
  22. package/dist/frqtl/compat/renderer/captureStill-common.js +258 -0
  23. package/dist/frqtl/compat/renderer/display-slot.js +96 -0
  24. package/dist/frqtl/compat/renderer/lo-drawn-center.js +231 -0
  25. package/dist/frqtl/compat/renderer/orbit-bag.js +216 -0
  26. package/dist/frqtl/compat/renderer/providers/godrays-layer.js +213 -0
  27. package/dist/frqtl/compat/renderer/providers/index.js +22 -0
  28. package/dist/frqtl/compat/renderer/providers/p5-provider.js +861 -0
  29. package/dist/frqtl/compat/renderer/providers/three-camera-contract.js +160 -0
  30. package/dist/frqtl/compat/renderer/providers/three-camera-geometry.js +571 -0
  31. package/dist/frqtl/compat/renderer/providers/three-provider.js +2470 -0
  32. package/dist/frqtl/compat/renderer/renderCapabilities-common.js +186 -0
  33. package/dist/frqtl/compat/renderer/renderer-provider-contract.js +238 -0
  34. package/dist/frqtl/compat/renderer/unprojectStill.js +172 -0
  35. package/dist/frqtl/compat/vendor.js +196 -0
  36. package/dist/frqtl/node-entry.js +17 -0
  37. package/dist/frqtl/objects/FrqtlApp.js +2920 -0
  38. package/dist/frqtl/objects/ObjectFactory.js +296 -0
  39. package/dist/frqtl/objects/RecipeContext.js +322 -0
  40. package/dist/frqtl/objects/collectors/DataCollector.js +116 -0
  41. package/dist/frqtl/objects/constants/WallBehaviors.js +178 -0
  42. package/dist/frqtl/objects/geometry/SlabFace.js +258 -0
  43. package/dist/frqtl/objects/instruments/capture-host.js +453 -0
  44. package/dist/frqtl/objects/instruments/capture.js +710 -0
  45. package/dist/frqtl/objects/instruments/hosted-eyes.js +984 -0
  46. package/dist/frqtl/objects/kinds-entry.js +14 -0
  47. package/dist/frqtl/objects/kinds.js +190 -0
  48. package/dist/frqtl/objects/objects/Box.js +273 -0
  49. package/dist/frqtl/objects/objects/Bullet.js +155 -0
  50. package/dist/frqtl/objects/objects/CircuitGate.js +380 -0
  51. package/dist/frqtl/objects/objects/CircuitProbe.js +214 -0
  52. package/dist/frqtl/objects/objects/DirectedTunnel.js +411 -0
  53. package/dist/frqtl/objects/objects/EgptFractal.js +164 -0
  54. package/dist/frqtl/objects/objects/LightSource.js +351 -0
  55. package/dist/frqtl/objects/objects/QuantumEmitter.js +760 -0
  56. package/dist/frqtl/objects/objects/Tube.js +471 -0
  57. package/dist/frqtl/objects/objects/absorbRadiation.js +309 -0
  58. package/dist/frqtl/objects/objects/actionCompiler.js +1747 -0
  59. package/dist/frqtl/objects/objects/controlAction.js +89 -0
  60. package/dist/frqtl/objects/objects/controlBind.js +278 -0
  61. package/dist/frqtl/objects/objects/directedRedirectProgram.js +120 -0
  62. package/dist/frqtl/objects/objects/emitterProgram.js +209 -0
  63. package/dist/frqtl/objects/objects/frqtl-control-slider.js +197 -0
  64. package/dist/frqtl/objects/objects/frqtl-control-sync.js +272 -0
  65. package/dist/frqtl/objects/objects/frqtl-control-widgets.js +219 -0
  66. package/dist/frqtl/objects/objects/frqtl-control-write-queue.js +174 -0
  67. package/dist/frqtl/objects/objects/makeControlsForObject.js +820 -0
  68. package/dist/frqtl/objects/objects/makeControlsForRenderSurface.js +161 -0
  69. package/dist/frqtl/objects/objects/slitLinerScatter.js +261 -0
  70. package/dist/frqtl/objects/objects/sustainedSource.js +298 -0
  71. package/dist/frqtl/objects/objects/tagger.js +228 -0
  72. package/dist/frqtl/objects/schema/engine-scopes.js +468 -0
  73. package/dist/frqtl/objects/schema/index.js +256 -0
  74. package/dist/frqtl/objects/schema/scopes.js +457 -0
  75. package/dist/frqtl/objects/schema/validate-descriptor.js +49 -0
  76. package/dist/frqtl/objects/schema/valueNormalizer.js +366 -0
  77. package/dist/frqtl/objects/setup/_collisionMatrixLanes.js +66 -0
  78. package/dist/frqtl/objects/setup/_setupOptions.js +118 -0
  79. package/dist/frqtl/objects/setup/_worldInit.js +162 -0
  80. package/dist/frqtl/objects/setup/setupBlackbodySubstrate.js +242 -0
  81. package/dist/frqtl/objects/setup/setupCircuitBasic.js +447 -0
  82. package/dist/frqtl/objects/setup/setupCollisionMatrix.js +188 -0
  83. package/dist/frqtl/objects/setup/setupConeEmitter3D.js +250 -0
  84. package/dist/frqtl/objects/setup/setupDoubleSlit.js +326 -0
  85. package/dist/frqtl/objects/setup/setupDoubleSlit_Laser.js +229 -0
  86. package/dist/frqtl/objects/setup/setupEmpty.js +59 -0
  87. package/dist/frqtl/objects/setup/setupFxSmoke.js +209 -0
  88. package/dist/frqtl/objects/setup/setupG1Race.js +80 -0
  89. package/dist/frqtl/objects/setup/setupG2Sweep.js +89 -0
  90. package/dist/frqtl/objects/setup/setupG3Collide.js +177 -0
  91. package/dist/frqtl/objects/setup/setupLightSourceLab.js +277 -0
  92. package/dist/frqtl/objects/setup/setupOscillator.js +68 -0
  93. package/dist/frqtl/objects/setup/setupParticleWalk.js +93 -0
  94. package/dist/frqtl/objects/setup/setupParticleWalk3D.js +133 -0
  95. package/dist/frqtl/objects/setup/setupSphereEmitter3D.js +1258 -0
  96. package/dist/frqtl/objects/setup/setupTraceCircuit.js +239 -0
  97. package/dist/frqtl/objects/setup/setupTunnelLab.js +240 -0
  98. package/dist/frqtl/objects/setup/setupTunnelPointExit.js +306 -0
  99. package/dist/frqtl/objects/setup/setupWaveInterference.js +177 -0
  100. package/dist/frqtl/objects/v1-replacements.js +29 -0
  101. package/dist/frqtl/package-core.js +23 -0
  102. package/dist/frqtl/parts/CameraControls.js +643 -0
  103. package/dist/frqtl/parts/CameraPath.js +575 -0
  104. package/dist/frqtl/parts/CollisionActions.js +34 -0
  105. package/dist/frqtl/parts/CollisionTypes.js +30 -0
  106. package/dist/frqtl/parts/DetectorWall.js +496 -0
  107. package/dist/frqtl/parts/FrqtlControlOverlay.js +227 -0
  108. package/dist/frqtl/parts/FrqtlControlsPanel.js +415 -0
  109. package/dist/frqtl/parts/FrqtlVision.js +818 -0
  110. package/dist/frqtl/parts/GraphingDetectorWall.js +489 -0
  111. package/dist/frqtl/parts/LargeObject.js +405 -0
  112. package/dist/frqtl/parts/LightColor.js +255 -0
  113. package/dist/frqtl/parts/LiveChart.js +285 -0
  114. package/dist/frqtl/parts/LogicGate.js +717 -0
  115. package/dist/frqtl/parts/OvenBox.js +611 -0
  116. package/dist/frqtl/parts/PointSource.js +762 -0
  117. package/dist/frqtl/parts/Rectangle.js +200 -0
  118. package/dist/frqtl/parts/SlitLightSource.js +234 -0
  119. package/dist/frqtl/parts/SlitScreen.js +628 -0
  120. package/dist/frqtl/parts/SphereEmitter.js +946 -0
  121. package/dist/frqtl/parts/Trace.js +508 -0
  122. package/dist/frqtl/parts/Tunnel.js +1057 -0
  123. package/dist/frqtl/parts/Wall.js +1211 -0
  124. package/dist/frqtl/parts/index.js +33 -0
  125. package/dist/frqtl/scenes/absorbing-wall-3d.js +33 -0
  126. package/dist/frqtl/scenes/absorbing-wall.js +32 -0
  127. package/dist/frqtl/scenes/atomic-model.js +88 -0
  128. package/dist/frqtl/scenes/big-bang.js +46 -0
  129. package/dist/frqtl/scenes/blackbody.js +81 -0
  130. package/dist/frqtl/scenes/circuit-sat.js +127 -0
  131. package/dist/frqtl/scenes/collision-matrix-3d.js +107 -0
  132. package/dist/frqtl/scenes/counting-wall-3d.js +36 -0
  133. package/dist/frqtl/scenes/counting-wall.js +38 -0
  134. package/dist/frqtl/scenes/double-slit.js +31 -0
  135. package/dist/frqtl/scenes/dse-one-emitter.js +196 -0
  136. package/dist/frqtl/scenes/dse-one-slit.js +114 -0
  137. package/dist/frqtl/scenes/dse-photoelectric.js +70 -0
  138. package/dist/frqtl/scenes/dse-tunnel-rebuild.js +243 -0
  139. package/dist/frqtl/scenes/dse-two-emitters.js +196 -0
  140. package/dist/frqtl/scenes/dse-two-slits.js +132 -0
  141. package/dist/frqtl/scenes/empty.js +14 -0
  142. package/dist/frqtl/scenes/laser-buildup.js +49 -0
  143. package/dist/frqtl/scenes/mirror-3d.js +56 -0
  144. package/dist/frqtl/scenes/mirror-route.js +140 -0
  145. package/dist/frqtl/scenes/mirror.js +38 -0
  146. package/dist/frqtl/scenes/omni-3d.js +27 -0
  147. package/dist/frqtl/scenes/omni-source.js +22 -0
  148. package/dist/frqtl/scenes/self-interference.js +39 -0
  149. package/dist/frqtl/scenes/smoking-gun.js +122 -0
  150. package/dist/frqtl/scenes/straight-walk.js +22 -0
  151. package/dist/frqtl/scenes/sun.js +39 -0
  152. package/dist/frqtl/scenes/two-source.js +37 -0
  153. package/dist/frqtl/scenes/wave.js +26 -0
  154. package/dist/shim/bundle/frqnb.js +112 -0
  155. package/dist/shim/bundle/index.js +121 -0
  156. package/dist/shim/bundle/indexHtml.js +107 -0
  157. package/dist/shim/bundle/manifest.js +95 -0
  158. package/dist/shim/bundle/zip.js +243 -0
  159. package/dist/shim/bus.js +67 -0
  160. package/dist/shim/controls/index.js +627 -0
  161. package/dist/shim/controls/registry.js +274 -0
  162. package/dist/shim/engine/artifact-shell.js +211 -0
  163. package/dist/shim/engine/boot-phases.js +44 -0
  164. package/dist/shim/engine/engine-stack-boot.js +59 -0
  165. package/dist/shim/engine/router.js +690 -0
  166. package/dist/shim/experiment-frame/index.js +296 -0
  167. package/dist/shim/expr/ast-schema.js +369 -0
  168. package/dist/shim/field-descriptor/index.js +325 -0
  169. package/dist/shim/frame-data/index.js +224 -0
  170. package/dist/shim/index.js +67 -0
  171. package/dist/shim/notebook/canvas-guard.js +82 -0
  172. package/dist/shim/notebook/catalog.js +35 -0
  173. package/dist/shim/notebook/format.js +736 -0
  174. package/dist/shim/notebook/frqtl-setups-importmap.js +81 -0
  175. package/dist/shim/notebook/kernel.js +671 -0
  176. package/dist/shim/notebook/knob.js +150 -0
  177. package/dist/shim/notebook/module-resolver.js +260 -0
  178. package/dist/shim/notebook/notebook-knobs.js +65 -0
  179. package/dist/shim/notebook/produced-bindings.js +67 -0
  180. package/dist/shim/notebook/realm-registry.js +121 -0
  181. package/dist/shim/notebook/realm-transport.js +73 -0
  182. package/dist/shim/notebook/setup-builder.js +507 -0
  183. package/dist/shim/notebook/store.js +159 -0
  184. package/dist/shim/notebook/vendor-importmap.js +134 -0
  185. package/dist/shim/notebook/view-builder.js +189 -0
  186. package/dist/shim/notebook/view-ctx.js +100 -0
  187. package/dist/shim/notebook/view-handle.js +202 -0
  188. package/dist/shim/notebook/warmup.js +102 -0
  189. package/dist/shim/package.json +4 -0
  190. package/dist/shim/page-params/index.js +213 -0
  191. package/dist/shim/project/identity.js +112 -0
  192. package/dist/shim/project/index.js +19 -0
  193. package/dist/shim/project/link.js +402 -0
  194. package/dist/shim/project/node-specifier-hook.mjs +35 -0
  195. package/dist/shim/project/register-node.mjs +17 -0
  196. package/dist/shim/project/specifiers.js +106 -0
  197. package/dist/shim/registry/copy-gate.js +110 -0
  198. package/dist/shim/registry/exhibits.js +455 -0
  199. package/dist/shim/registry/experiments.js +215 -0
  200. package/dist/shim/registry/index.js +19 -0
  201. package/dist/shim/registry/scene-source.js +157 -0
  202. package/dist/shim/registry/walkthrough.js +804 -0
  203. package/dist/shim/render/capture-envelope.js +279 -0
  204. package/dist/shim/render/heat-colormap.js +92 -0
  205. package/dist/shim/render/render-stages.js +855 -0
  206. package/dist/shim/render/sun-look.js +33 -0
  207. package/dist/shim/render-state/apply.js +262 -0
  208. package/dist/shim/render-state/index.js +74 -0
  209. package/dist/shim/rpc/index.js +207 -0
  210. package/dist/shim/runtime/index.js +1023 -0
  211. package/dist/shim/storage/boot.js +69 -0
  212. package/dist/shim/storage/index.js +169 -0
  213. package/dist/shim/storage/local-storage-provider.js +230 -0
  214. package/dist/shim/storage/make-namespace-store.js +132 -0
  215. package/dist/shim/storage/modules.js +249 -0
  216. package/dist/shim/storage/projects.js +150 -0
  217. package/llms.txt +12 -0
  218. package/package.json +63 -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
+ * (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 (Studio Controls Realignment §2.6,
48
+ * 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 S6 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
151
+ * (Studio Controls Realignment §2.6, 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 (DX-3 —
155
+ * 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.2 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.2/CLAUDE.md
12
+ - Acceptance is visual; no CLI ships. No account, credits or tokens are needed.
package/package.json CHANGED
@@ -1,6 +1,65 @@
1
1
  {
2
2
  "name": "@descix/frqtl-node",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.6.0-p0.2",
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
+ "LICENSE.md"
34
+ ],
35
+ "scripts": {
36
+ "build": "node build.js",
37
+ "verify:packages": "node ../_build/verify-publish-surface.mjs",
38
+ "verify:build-closure": "node ../_build/verify-build-closure.mjs",
39
+ "verify:parity-core": "node ../_build/verify-parity-core.mjs",
40
+ "pack:dry-run": "npm pack --dry-run --ignore-scripts",
41
+ "prepack": "npm run build && npm run verify:packages && npm run verify:build-closure && npm run verify:parity-core"
42
+ },
43
+ "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.",
44
+ "keywords": [
45
+ "physics",
46
+ "engine",
47
+ "simulation",
48
+ "fractal",
49
+ "cellular-automata",
50
+ "particle",
51
+ "quantum",
52
+ "wave-particle-duality",
53
+ "emergent",
54
+ "discrete-physics",
55
+ "egpt",
56
+ "frqtl",
57
+ "descix"
58
+ ],
59
+ "author": "Essam Abadir",
60
+ "license": "SEE LICENSE IN LICENSE.md",
61
+ "dependencies": {
62
+ "playwright": "1.61.0"
63
+ },
64
+ "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."
65
+ }