@descix/frqtl-node 0.6.0-p1.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +2 -2
- package/README.md +6 -4
- package/dist/dist-manifest.json +71 -65
- package/dist/eyes/index.js +5 -2
- package/dist/fat/pkg-node/package.json +2 -10
- package/dist/frqtl/compat/providers/wasm-engine.js +3 -4
- package/dist/frqtl/compat/renderer/providers/p5-provider.js +1 -1
- package/dist/frqtl/objects/FrqtlApp.js +33 -6
- package/dist/frqtl/objects/RecipeContext.js +284 -13
- package/dist/frqtl/objects/constants/WallBehaviors.js +1 -1
- package/dist/frqtl/objects/instruments/capture.js +1 -24
- package/dist/frqtl/objects/instruments/hosted-eyes.js +216 -29
- package/dist/frqtl/objects/objects/makeControlsForObject.js +5 -5
- package/dist/frqtl/objects/record/Record.js +346 -0
- package/dist/frqtl/objects/record/open-record.js +22 -0
- package/dist/frqtl/objects/schema/engine-scopes.js +2 -2
- package/dist/frqtl/objects/schema/scopes.js +2 -2
- package/dist/frqtl/objects/setup/_worldInit.js +1 -2
- package/dist/frqtl/objects/setup/setupBlackbodySubstrate.js +1 -2
- package/dist/frqtl/parts/CameraControls.js +8 -0
- package/dist/frqtl/parts/DetectorWall.js +1 -1
- package/dist/frqtl/parts/FrqtlVision.js +21 -6
- package/dist/frqtl/parts/GraphingDetectorWall.js +7 -7
- package/dist/frqtl/parts/LargeObject.js +2 -2
- package/dist/frqtl/parts/LiveChart.js +2 -2
- package/dist/frqtl/parts/LogicGate.js +3 -3
- package/dist/frqtl/parts/OvenBox.js +1 -1
- package/dist/frqtl/parts/PointSource.js +1 -1
- package/dist/frqtl/parts/SlitLightSource.js +1 -1
- package/dist/frqtl/parts/SlitScreen.js +1 -1
- package/dist/frqtl/parts/SphereEmitter.js +12 -12
- package/dist/frqtl/parts/Trace.js +1 -1
- package/dist/frqtl/parts/Tunnel.js +11 -11
- package/dist/frqtl/parts/Wall.js +20 -10
- package/dist/frqtl/scenes/circuit-sat.js +1 -0
- package/dist/frqtl/scenes/dse-one-emitter.js +32 -34
- package/dist/frqtl/scenes/dse-two-emitters.js +33 -34
- package/dist/frqtl/scenes/mirror-route.js +96 -79
- package/dist/shim/bundle/index.js +118 -96
- package/dist/shim/bundle/indexHtml.js +66 -39
- package/dist/shim/controls/index.js +1 -1
- package/dist/shim/engine/artifact-shell.js +1 -1
- package/dist/shim/engine/router.js +3 -3
- package/dist/shim/host/host-view.js +228 -0
- package/dist/shim/host/index.js +18 -0
- package/dist/shim/index.js +3 -5
- package/dist/shim/notebook/format.js +1 -1
- package/dist/shim/notebook/kernel.js +1 -1
- package/dist/shim/notebook/setup-builder.js +2 -2
- package/dist/shim/notebook/store.js +2 -3
- package/dist/shim/package.json +1 -0
- package/dist/shim/page-params/index.js +2 -30
- package/dist/shim/project/fork.js +293 -0
- package/dist/shim/project/identity.js +3 -1
- package/dist/shim/project/index.js +16 -3
- package/dist/shim/project/link.js +224 -46
- package/dist/shim/project/manifest.js +151 -0
- package/dist/shim/project/parts-writer.js +68 -7
- package/dist/shim/project/read-text.js +43 -0
- package/dist/shim/project/record-format.js +345 -0
- package/dist/shim/project/specifiers.js +104 -15
- package/dist/shim/registry/exhibits.js +15 -34
- package/dist/shim/registry/index.js +3 -3
- package/dist/shim/registry/scene-roster.js +35 -0
- package/dist/shim/registry/scene-source.js +48 -64
- package/dist/shim/registry/walkthrough.js +6 -6
- package/dist/shim/render-state/apply.js +66 -31
- package/dist/shim/render-state/index.js +6 -2
- package/dist/shim/runtime/index.js +4 -4
- package/dist/shim/storage/codec-chains.js +88 -0
- package/dist/shim/storage/folders.js +205 -0
- package/dist/shim/storage/index.js +64 -8
- package/dist/shim/storage/make-namespace-store.js +5 -5
- package/llms.txt +4 -3
- package/package.json +19 -1
- package/dist/shim/bundle/frqnb.js +0 -112
- package/dist/shim/bundle/manifest.js +0 -95
- package/dist/shim/registry/experiments.js +0 -215
- package/dist/shim/storage/modules.js +0 -249
- package/dist/shim/storage/projects.js +0 -150
|
@@ -164,7 +164,7 @@ export function renderArtifactShellHtml(opts) {
|
|
|
164
164
|
return (
|
|
165
165
|
'<!DOCTYPE html>\n' +
|
|
166
166
|
'<!-- Copyright 2023-2026 by Essam Abadir -->\n' +
|
|
167
|
-
'<!-- FRQTL — claude.ai Artifact host shell
|
|
167
|
+
'<!-- FRQTL — claude.ai Artifact host shell.\n' +
|
|
168
168
|
' Generated by lib/shim/engine/artifact-shell.js — DO NOT hand-edit.\n' +
|
|
169
169
|
' Paste this ENTIRE file into a claude.ai chat as an Artifact (type: HTML). -->\n' +
|
|
170
170
|
'<html lang="en">\n' +
|
|
@@ -148,7 +148,7 @@ var DIST_PROFILE = buildDistProfile('../..');
|
|
|
148
148
|
// lib/shim/engine/test/router.test.mjs asserts them against the two
|
|
149
149
|
// package.json#version fields so a forgotten bump fails `make verify`, not a
|
|
150
150
|
// silent stale-CDN-URL bug.
|
|
151
|
-
var CDN_SDK_VERSION = '0.6.0
|
|
151
|
+
var CDN_SDK_VERSION = '0.6.0'; // sdk/frqtl-web/package.json#version (the ONE version owner) — bound by router.test.mjs
|
|
152
152
|
var CDN_MATH_SDK_VERSION = '0.3.2'; // sdk/egpt-math-sdk/package.json#version — bound by router.test.mjs; bumped with each npm publish
|
|
153
153
|
|
|
154
154
|
/**
|
|
@@ -382,7 +382,7 @@ function documentSiteBuild() {
|
|
|
382
382
|
}
|
|
383
383
|
|
|
384
384
|
// The static IDE bundle's entry document, beside its front page. The bundle ships THIS name and no
|
|
385
|
-
// index.html: a site's index.html is the publisher's front page (host-view.js::HOST_FRONT_PAGE),
|
|
385
|
+
// index.html: a site's index.html is the publisher's front page (lib/shim/host/host-view.js::HOST_FRONT_PAGE),
|
|
386
386
|
// which the IDE brand links home to.
|
|
387
387
|
var STATIC_APP_PAGE = 'app.html';
|
|
388
388
|
|
|
@@ -507,7 +507,7 @@ var _assertProfileParity = (function () {
|
|
|
507
507
|
* // CDN mode (static hosting — the artifact host shell has no server, no page anchor):
|
|
508
508
|
* const cdnRouter = engineRouter({ mode: 'cdn', engine: 'wasm' });
|
|
509
509
|
* console.log(cdnRouter.resolve('compat/engine.js'));
|
|
510
|
-
* // 'https://cdn.jsdelivr.net/npm/@descix/frqtl-web@0.6.0
|
|
510
|
+
* // 'https://cdn.jsdelivr.net/npm/@descix/frqtl-web@0.6.0/dist/frqtl/compat/engine.js'
|
|
511
511
|
*
|
|
512
512
|
* // CDN mode, self-hosted: a copy of dist/ served from the consumer's own origin:
|
|
513
513
|
* const selfHosted = engineRouter({ mode: 'cdn', engine: 'wasm', base: 'https://example.org/vendor/frqtl-web/dist' });
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* host-view.js — the ONE declarer of the DeSciX host view for an app DOCUMENT.
|
|
5
|
+
*
|
|
6
|
+
* A DeSciX shell hosts the app beside its chat (SplitView, its default) unless the app asks for
|
|
7
|
+
* another view. This module asks — once, through the app-side SDK (`DeSciXAppSDK.js`, vendored
|
|
8
|
+
* by the publisher beside the page) — and RECORDS on `document.documentElement.dataset.descixView`
|
|
9
|
+
* which host state it met. The mode is a parameter; the policy lives at the call site.
|
|
10
|
+
*
|
|
11
|
+
* Every host state is a row of HOST_VIEW_OUTCOMES. No row throws and no row writes to
|
|
12
|
+
* console.error: the page's job is physics and layout is the host's, so a missing host is a
|
|
13
|
+
* truthful record, never a crash and never a cry-wolf console line. Whether an absence is
|
|
14
|
+
* LEGITIMATE is decided by a gate at the plane that knows (FRAQTL's static URL gate, egpt-frqtl's
|
|
15
|
+
* refresh-site refusal), not here.
|
|
16
|
+
*
|
|
17
|
+
* Two SDK rules shape the order of operations:
|
|
18
|
+
* - The SDK's `view` getter console.errors on a standalone page AND on a shell that does not
|
|
19
|
+
* publish `view`. So `view` is read only after `ready()` resolved `mode:'shell'` AND its
|
|
20
|
+
* `members` list names `view`.
|
|
21
|
+
* - `view.set()` on a host that renders a bare iframe validates and returns the mode while
|
|
22
|
+
* nothing moves. `view.available()` is the only instrument that tells the two apart, so it
|
|
23
|
+
* is consulted here, before `set`, and nowhere else.
|
|
24
|
+
*
|
|
25
|
+
* A caller that names a declared second choice (`fallback`) is answered off the shell's PUBLISHED list,
|
|
26
|
+
* `view.MODES`, read before any `set`: the asked mode when listed, else the fallback when listed (recorded as
|
|
27
|
+
* the `fallback` row, the refusal and the mode set in the detail), else nothing (`unoffered`). A `set` is never
|
|
28
|
+
* retried and never decided by its error: a shell that lists a mode and then refuses it is a contract violation.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The app-side SDK file this declarer consumes. It is PUBLISHER-provided: vendored beside the
|
|
33
|
+
* page by the site that publishes the static bundle, never shipped in the bundle.
|
|
34
|
+
*/
|
|
35
|
+
export const HOST_SDK_SCRIPT = 'DeSciXAppSDK.js';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The attribute that declares a file host-provided, on the element that consumes it, in the
|
|
39
|
+
* bytes the browser executes. Both the bundle gate (the file must be ABSENT from the bundle) and
|
|
40
|
+
* the publisher (the file must be PRESENT beside the page) read the set from this attribute.
|
|
41
|
+
*/
|
|
42
|
+
export const HOST_PROVIDED_ATTR = 'data-frqtl-host-provided';
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The publisher's front page, beside the IDE entry (router.js::STATIC_APP_PAGE). PUBLISHER-provided
|
|
46
|
+
* like HOST_SDK_SCRIPT: the static build declares it once as `<link rel="home" href=HOST_FRONT_PAGE
|
|
47
|
+
* HOST_PROVIDED_ATTR>` (apps/ide/vite.config.js), the bundle gate asserts the bundle does not ship
|
|
48
|
+
* it, and lib/features/ide/home-link.js::homeHrefOf reads the declaration back.
|
|
49
|
+
*/
|
|
50
|
+
export const HOST_FRONT_PAGE = 'index.html';
|
|
51
|
+
|
|
52
|
+
/** The closed table of host states. `setCalls` is how many times `view.set` runs in that row. */
|
|
53
|
+
export const HOST_VIEW_OUTCOMES = Object.freeze([
|
|
54
|
+
{ id: 'pending', setCalls: 0,
|
|
55
|
+
means: 'awaiting DeSciX.ready(); a ready() that never settles stays here' },
|
|
56
|
+
{ id: 'requested', setCalls: 1,
|
|
57
|
+
means: 'shell listening: view.available() true and view.set(mode) returned mode; recorded as requested:<mode>' },
|
|
58
|
+
{ id: 'fallback', setCalls: 1,
|
|
59
|
+
means: 'shell listening; view.MODES lacks the asked mode and offers the caller\'s declared fallback: view.set(fallback) returned it; recorded as fallback:<asked mode>, the refusal and the mode set in dataset.descixViewDetail' },
|
|
60
|
+
{ id: 'unoffered', setCalls: 0,
|
|
61
|
+
means: 'shell listening; view.MODES offers neither the asked mode nor the declared fallback; recorded as unoffered:<asked mode>, the list in dataset.descixViewDetail' },
|
|
62
|
+
{ id: 'not-listening', setCalls: 0,
|
|
63
|
+
means: 'shell publishes view but view.available() is false (bare-iframe host); the host owns the layout' },
|
|
64
|
+
{ id: 'view-unpublished', setCalls: 0,
|
|
65
|
+
means: 'shell resolved, but its members do not include view' },
|
|
66
|
+
{ id: 'standalone', setCalls: 0,
|
|
67
|
+
means: 'ready() resolved standalone: this page is the top window, no shell above it' },
|
|
68
|
+
{ id: 'embedded-no-bridge', setCalls: 0,
|
|
69
|
+
means: 'this page is framed and no ancestor carries a readable bus (the SDK rejects ready() here today)' },
|
|
70
|
+
{ id: 'sdk-absent', setCalls: 0,
|
|
71
|
+
means: 'window.DeSciX is undefined: the publisher-provided DeSciXAppSDK.js is not beside this page' },
|
|
72
|
+
{ id: 'contract-violated', setCalls: null,
|
|
73
|
+
means: 'the SDK or host broke its published shape; the edge is named in dataset.descixViewDetail (CONTRACT_VIOLATIONS)' },
|
|
74
|
+
].map(Object.freeze));
|
|
75
|
+
|
|
76
|
+
/** The closed edges of `contract-violated`, each with its own `view.set` call count. */
|
|
77
|
+
export const CONTRACT_VIOLATIONS = Object.freeze({
|
|
78
|
+
'no-ready': Object.freeze({ setCalls: 0, means: 'window.DeSciX has no ready() function' }),
|
|
79
|
+
'ready-rejected': Object.freeze({ setCalls: 0, means: 'ready() rejected while the SDK reports a mode other than embedded-no-bridge' }),
|
|
80
|
+
'ready-unknown-mode': Object.freeze({ setCalls: 0, means: 'ready() resolved a mode that is not shell, standalone or embedded-no-bridge' }),
|
|
81
|
+
'ready-no-members': Object.freeze({ setCalls: 0, means: 'ready() resolved shell without a members array' }),
|
|
82
|
+
'view-listed-absent': Object.freeze({ setCalls: 0, means: 'members names view but the view getter returned nothing' }),
|
|
83
|
+
'view-no-available': Object.freeze({ setCalls: 0, means: 'view has no available() function' }),
|
|
84
|
+
'view-no-set': Object.freeze({ setCalls: 0, means: 'view has no set() function' }),
|
|
85
|
+
'available-threw': Object.freeze({ setCalls: 0, means: 'view.available() threw' }),
|
|
86
|
+
'view-no-modes': Object.freeze({ setCalls: 0, means: 'a fallback was given but view.MODES is not published' }),
|
|
87
|
+
'set-threw': Object.freeze({ setCalls: 1, means: 'view.set(mode) threw (or view.set(fallback), the mode view.MODES offered)' }),
|
|
88
|
+
'set-returned-other': Object.freeze({ setCalls: 1, means: 'view.set(mode) returned something other than the mode it was asked for' }),
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const ROW_IDS = new Set(HOST_VIEW_OUTCOMES.map((r) => r.id));
|
|
92
|
+
|
|
93
|
+
function messageOf(e) {
|
|
94
|
+
return (e && e.message) ? String(e.message) : String(e);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Write a row to the document record. Refuses (throws) a row or edge the tables do not list:
|
|
99
|
+
* that is a defect in THIS module, never a host state.
|
|
100
|
+
*/
|
|
101
|
+
function record(root, row, { suffix = '', detail } = {}) {
|
|
102
|
+
if (!ROW_IDS.has(row)) { throw new Error(`host-view: '${row}' is not a HOST_VIEW_OUTCOMES row`); }
|
|
103
|
+
const value = row + suffix;
|
|
104
|
+
if (root) {
|
|
105
|
+
root.dataset.descixView = value;
|
|
106
|
+
if (detail === undefined) { delete root.dataset.descixViewDetail; } else { root.dataset.descixViewDetail = detail; }
|
|
107
|
+
}
|
|
108
|
+
return detail === undefined ? { row, record: value } : { row, record: value, detail };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function violated(root, edge, specifics) {
|
|
112
|
+
if (!Object.prototype.hasOwnProperty.call(CONTRACT_VIOLATIONS, edge)) {
|
|
113
|
+
throw new Error(`host-view: '${edge}' is not a CONTRACT_VIOLATIONS edge`);
|
|
114
|
+
}
|
|
115
|
+
return record(root, 'contract-violated', { detail: `${edge}: ${specifics}` });
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** The SDK's own live mode, read without touching any member getter. */
|
|
119
|
+
function liveMode(sdk) {
|
|
120
|
+
try { return sdk.mode; } catch (e) { return undefined; }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Ask the host for `mode`, once, and record which host state was met.
|
|
125
|
+
*
|
|
126
|
+
* @param {object} args
|
|
127
|
+
* @param {object|undefined} args.sdk the app-side SDK object (`window.DeSciX`), or undefined
|
|
128
|
+
* @param {string} args.mode the view to ask for: a mode the shell's `view.MODES` offers
|
|
129
|
+
* @param {string} [args.fallback] the caller's declared second choice, asked only when `view.MODES` does not
|
|
130
|
+
* offer `mode` (a non-empty string other than `mode`)
|
|
131
|
+
* @param {{dataset: object}} [args.root] where the record is written; defaults to
|
|
132
|
+
* `document.documentElement` when a document exists
|
|
133
|
+
* @returns {Promise<{row: string, record: string, detail?: string}>}
|
|
134
|
+
*/
|
|
135
|
+
export async function declareHostView({ sdk, mode, fallback, root } = {}) {
|
|
136
|
+
if (typeof mode !== 'string' || mode === '') {
|
|
137
|
+
throw new TypeError(`host-view: declareHostView needs a view mode string (got ${JSON.stringify(mode)})`);
|
|
138
|
+
}
|
|
139
|
+
if (fallback !== undefined && (typeof fallback !== 'string' || fallback === '' || fallback === mode)) {
|
|
140
|
+
throw new TypeError(`host-view: a fallback is a non-empty view mode string other than mode ${JSON.stringify(mode)} (got ${JSON.stringify(fallback)})`);
|
|
141
|
+
}
|
|
142
|
+
const target = root || (typeof document !== 'undefined' ? document.documentElement : null);
|
|
143
|
+
|
|
144
|
+
if (sdk === undefined || sdk === null) { return record(target, 'sdk-absent'); }
|
|
145
|
+
if (typeof sdk.ready !== 'function') {
|
|
146
|
+
return violated(target, 'no-ready', `window.DeSciX is a ${typeof sdk} without ready()`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
record(target, 'pending');
|
|
150
|
+
let ready;
|
|
151
|
+
try {
|
|
152
|
+
ready = await sdk.ready();
|
|
153
|
+
} catch (e) {
|
|
154
|
+
const now = liveMode(sdk);
|
|
155
|
+
if (now === 'embedded-no-bridge') {
|
|
156
|
+
return record(target, 'embedded-no-bridge', { detail: `ready() rejected: ${messageOf(e)}` });
|
|
157
|
+
}
|
|
158
|
+
return violated(target, 'ready-rejected', `SDK mode ${JSON.stringify(now)}; ${messageOf(e)}`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const resolved = ready && ready.mode;
|
|
162
|
+
if (resolved === 'standalone') { return record(target, 'standalone'); }
|
|
163
|
+
if (resolved === 'embedded-no-bridge') { return record(target, 'embedded-no-bridge'); }
|
|
164
|
+
if (resolved !== 'shell') {
|
|
165
|
+
return violated(target, 'ready-unknown-mode', `ready() resolved mode ${JSON.stringify(resolved)}`);
|
|
166
|
+
}
|
|
167
|
+
if (!Array.isArray(ready.members)) {
|
|
168
|
+
return violated(target, 'ready-no-members', 'ready() resolved shell without a members array');
|
|
169
|
+
}
|
|
170
|
+
if (!ready.members.includes('view')) {
|
|
171
|
+
return record(target, 'view-unpublished', { detail: `members: ${ready.members.join(',')}` });
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const view = sdk.view;
|
|
175
|
+
if (!view) { return violated(target, 'view-listed-absent', 'members names view; the getter returned ' + String(view)); }
|
|
176
|
+
if (typeof view.available !== 'function') { return violated(target, 'view-no-available', 'view.available is ' + typeof view.available); }
|
|
177
|
+
if (typeof view.set !== 'function') { return violated(target, 'view-no-set', 'view.set is ' + typeof view.set); }
|
|
178
|
+
|
|
179
|
+
let listening;
|
|
180
|
+
try {
|
|
181
|
+
listening = view.available();
|
|
182
|
+
} catch (e) {
|
|
183
|
+
return violated(target, 'available-threw', messageOf(e));
|
|
184
|
+
}
|
|
185
|
+
if (!listening) { return record(target, 'not-listening'); }
|
|
186
|
+
|
|
187
|
+
// With a declared fallback the published list decides which ONE mode is asked; without one, the asked mode is.
|
|
188
|
+
let ask = mode;
|
|
189
|
+
if (fallback !== undefined) {
|
|
190
|
+
const modes = view.MODES;
|
|
191
|
+
if (!modes || typeof modes !== 'object') {
|
|
192
|
+
return violated(target, 'view-no-modes', 'fallback given but view.MODES is not published (got ' + String(modes) + ')');
|
|
193
|
+
}
|
|
194
|
+
const offered = Object.values(modes);
|
|
195
|
+
const list = '[' + offered.join(', ') + ']';
|
|
196
|
+
if (!offered.includes(mode)) {
|
|
197
|
+
if (!offered.includes(fallback)) {
|
|
198
|
+
return record(target, 'unoffered', { suffix: ':' + mode,
|
|
199
|
+
detail: `view.MODES ${list} offers neither "${mode}" nor the fallback "${fallback}"` });
|
|
200
|
+
}
|
|
201
|
+
ask = fallback;
|
|
202
|
+
}
|
|
203
|
+
if (ask === fallback) {
|
|
204
|
+
let gotFallback;
|
|
205
|
+
try {
|
|
206
|
+
gotFallback = view.set(fallback);
|
|
207
|
+
} catch (e) {
|
|
208
|
+
return violated(target, 'set-threw', messageOf(e));
|
|
209
|
+
}
|
|
210
|
+
if (gotFallback !== fallback) {
|
|
211
|
+
return violated(target, 'set-returned-other', `asked ${fallback}, set returned ${JSON.stringify(gotFallback)}`);
|
|
212
|
+
}
|
|
213
|
+
return record(target, 'fallback', { suffix: ':' + mode,
|
|
214
|
+
detail: `refused: "${mode}" is not in view.MODES ${list}; set: ${fallback}` });
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
let got;
|
|
219
|
+
try {
|
|
220
|
+
got = view.set(ask);
|
|
221
|
+
} catch (e) {
|
|
222
|
+
return violated(target, 'set-threw', messageOf(e));
|
|
223
|
+
}
|
|
224
|
+
if (got !== ask) {
|
|
225
|
+
return violated(target, 'set-returned-other', `asked ${ask}, set returned ${JSON.stringify(got)}`);
|
|
226
|
+
}
|
|
227
|
+
return record(target, 'requested', { suffix: ':' + ask });
|
|
228
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* index.js — the published door to the host-view declarer: `@descix/frqtl-web/host-view` and
|
|
5
|
+
* `@descix/frqtl-node/host-view`. Exactly the names below, re-exported from THE declarer (host-view.js); its
|
|
6
|
+
* static-build vocabulary (HOST_PROVIDED_ATTR, HOST_FRONT_PAGE) stays FRAQTL's own.
|
|
7
|
+
*
|
|
8
|
+
* HOST_VIEW_EXPORTS the published names (the parity gate holds both packages' ./host-view to this table)
|
|
9
|
+
*
|
|
10
|
+
* A consuming app calls `declareHostView({ sdk: window.DeSciX, mode, fallback? })` once, under its own policy, and
|
|
11
|
+
* reads the outcome off `document.documentElement.dataset.descixView` (a HOST_VIEW_OUTCOMES row). HOST_SDK_SCRIPT is
|
|
12
|
+
* the publisher-provided SDK file the app vendors beside its page.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/** The names `./host-view` publishes. */
|
|
16
|
+
export const HOST_VIEW_EXPORTS = Object.freeze(['declareHostView', 'HOST_VIEW_OUTCOMES', 'CONTRACT_VIOLATIONS', 'HOST_SDK_SCRIPT']);
|
|
17
|
+
|
|
18
|
+
export { declareHostView, HOST_VIEW_OUTCOMES, CONTRACT_VIOLATIONS, HOST_SDK_SCRIPT } from './host-view.js';
|
package/dist/shim/index.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* FAIL-LOUD load error — no window.Common* ambient globals.
|
|
8
8
|
*
|
|
9
9
|
* Usage (under vite with alias 'lib' → repo-root/lib):
|
|
10
|
-
* import { Bus, Runtime,
|
|
10
|
+
* import { Bus, Runtime, sceneGroups, engineRouter } from 'lib/shim';
|
|
11
11
|
*
|
|
12
12
|
* Usage (direct relative path):
|
|
13
13
|
* import { Bus } from '../../lib/shim/index.js';
|
|
@@ -15,14 +15,12 @@
|
|
|
15
15
|
|
|
16
16
|
export { Bus } from './bus.js';
|
|
17
17
|
export { Runtime } from './runtime/index.js';
|
|
18
|
-
export {
|
|
19
|
-
isPublicExperiment, publicExperiments,
|
|
20
|
-
internalExperiments } from './registry/experiments.js';
|
|
18
|
+
export { sceneGroups, SHOWCASE_GROUP } from './registry/scene-roster.js';
|
|
21
19
|
export { WALKTHROUGH, walkthroughIs3D, walkthroughModes, walkthroughGroup,
|
|
22
20
|
walkthroughById,
|
|
23
21
|
FIRST_CONTACT_WALKTHROUGH_ID,
|
|
24
22
|
defaultWalkthroughModeId, effectiveRenderState } from './registry/walkthrough.js';
|
|
25
|
-
export {
|
|
23
|
+
export { projectOf, scenePath, EMPTY_SCENE } from './registry/scene-source.js';
|
|
26
24
|
// The host-side experiment-iframe guard (the false-200 trap). Exported from the
|
|
27
25
|
// barrel so every host that frames an experiment page (lib/features/ide's GYM)
|
|
28
26
|
// asserts against the SAME contract.
|
|
@@ -99,7 +99,7 @@ export const PROSE_CELL_TYPES = Object.freeze(['markdown']);
|
|
|
99
99
|
* e.g. `setupDoubleSlit`). Absent → `'factory'`, so every existing notebook is
|
|
100
100
|
* byte-identical.
|
|
101
101
|
* - `'recipe'`: the module's DEFAULT export is a `buildExperiment(exp)` RECIPE (a single
|
|
102
|
-
* RecipeContext arg — the sketch form
|
|
102
|
+
* RecipeContext arg — the sketch form the scene files
|
|
103
103
|
* write). The shim builds a
|
|
104
104
|
* `frqtl.RecipeContext` and runs `exp.build(buildExperiment)` (the canonical
|
|
105
105
|
* co-init call + `addObject()` recipe path), NOT the factory signature.
|
|
@@ -148,7 +148,7 @@ export class NotebookKernel {
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
/* ── scope ──────────────────────────────────────────────────────────────
|
|
151
|
-
* THE SCOPE RULE
|
|
151
|
+
* THE SCOPE RULE: a math value's scope IS this kernel's
|
|
152
152
|
* binding scope. On the shipped WASM dist an EGPT value is an arena handle,
|
|
153
153
|
* so if the arena is drained while a binding still names one, that binding is
|
|
154
154
|
* a dead handle and the next cell dies with `arena:stale-handle`. The two
|
|
@@ -288,7 +288,7 @@ export function parseSeed(seed) {
|
|
|
288
288
|
* canvas boots the same scene as every other surface.
|
|
289
289
|
*
|
|
290
290
|
* shape='recipe': the module's DEFAULT export is a `buildExperiment(exp)` RECIPE — a SINGLE
|
|
291
|
-
* RecipeContext arg (the sketch form
|
|
291
|
+
* RecipeContext arg (the sketch form the scene files and the parts writer write). Template:
|
|
292
292
|
* const buildExperiment = (await import('<module>')).default; // FAIL-LOUD if not a function
|
|
293
293
|
* const exp = frqtl.RecipeContext({ seed, deferCanvas: true }); // SIZELESS
|
|
294
294
|
* exp.build(buildExperiment); // runs its co-init call + addObject() — NO start
|
|
@@ -298,7 +298,7 @@ export function parseSeed(seed) {
|
|
|
298
298
|
* recipe takes NO knob bag — a non-empty `opts` on a recipe canvas FAILS LOUD
|
|
299
299
|
* (rather than silently dropping it).
|
|
300
300
|
*
|
|
301
|
-
* WHY the template checks `buildExperiment` but not `exp
|
|
301
|
+
* WHY the template checks `buildExperiment` but not `exp`: the two values
|
|
302
302
|
* have different OWNERS. `buildExperiment` is read off the module namespace by this template
|
|
303
303
|
* itself, so nothing but this template can vouch for it — hence the emitted FAIL-LOUD check.
|
|
304
304
|
* `exp` comes from `frqtl.RecipeContext(...)`, a caps.frqtl member: its owner
|
|
@@ -5,9 +5,8 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Thin CRUD helpers that persist a serialized <frqtl-notebook> document on top
|
|
7
7
|
* of any StorageProvider (lib/shim/storage/index.js) using the "notebooks"
|
|
8
|
-
* namespace
|
|
9
|
-
* lib/shim/storage/
|
|
10
|
-
* projects, Studio modules, and Notebooks.
|
|
8
|
+
* namespace, through the shared namespace-CRUD factory
|
|
9
|
+
* (lib/shim/storage/make-namespace-store.js).
|
|
11
10
|
*
|
|
12
11
|
* ── Why this lives in lib/shim ──────────────────────────────────────────────
|
|
13
12
|
*
|
package/dist/shim/package.json
CHANGED
|
@@ -64,8 +64,7 @@
|
|
|
64
64
|
var KEYS = Object.freeze({
|
|
65
65
|
SEED: 'seed',
|
|
66
66
|
RENDERER: 'renderer',
|
|
67
|
-
ENGINE: 'engine'
|
|
68
|
-
STEPPING: 'stepping'
|
|
67
|
+
ENGINE: 'engine'
|
|
69
68
|
});
|
|
70
69
|
|
|
71
70
|
/**
|
|
@@ -85,7 +84,7 @@ var KEYS = Object.freeze({
|
|
|
85
84
|
var PAGE_PARAMS = Object.freeze([
|
|
86
85
|
Object.freeze({
|
|
87
86
|
key: KEYS.SEED,
|
|
88
|
-
reader: 'lib/frqtl/experiments/
|
|
87
|
+
reader: 'lib/frqtl/experiments/empty/index.html',
|
|
89
88
|
forward: true
|
|
90
89
|
}),
|
|
91
90
|
Object.freeze({
|
|
@@ -93,33 +92,6 @@ var PAGE_PARAMS = Object.freeze([
|
|
|
93
92
|
reader: 'lib/frqtl/experiments/_shared/boot.js',
|
|
94
93
|
forward: true
|
|
95
94
|
}),
|
|
96
|
-
Object.freeze({
|
|
97
|
-
key: KEYS.STEPPING,
|
|
98
|
-
reader: 'lib/frqtl/experiments/single-source-interference/index.html',
|
|
99
|
-
forward: true
|
|
100
|
-
// Circulation §2: `stepping`
|
|
101
|
-
// selects the WAVE-WALK STEPPING SAMPLER for one universe —
|
|
102
|
-
// 'fixed' (deterministic error-accumulator walk, the engine's serde default)
|
|
103
|
-
// vs 'average' (the historic per-axis Bernoulli walk). It is a PER-UNIVERSE
|
|
104
|
-
// PHYSICS option, so it is read at the PAGE plane and ferried RAW into that
|
|
105
|
-
// page's own `universe.init(..., { stepping })` options — deliberately NOT by
|
|
106
|
-
// the boot module, which selects the BACKEND (`?engine=`, forward:false). Putting
|
|
107
|
-
// a physics knob in the backend selection would conflate the two axes.
|
|
108
|
-
//
|
|
109
|
-
// forward:true — unlike `?engine=`, no host owns a competing stepping authority,
|
|
110
|
-
// and a host embedding this page for an A/B (the GYM Compare shape) forwarding
|
|
111
|
-
// its own `?stepping=` is exactly the intended use (the `?renderer=`/`?seed=`
|
|
112
|
-
// precedent). The value is NEVER validated or defaulted page-side: an absent
|
|
113
|
-
// param means the key is omitted from the init bag so the ENGINE's default
|
|
114
|
-
// (the Rust `Stepping` serde enum, `Fixed`) is the ONE authority for it, and a
|
|
115
|
-
// garbage value rides through to `universe.init()`, which throws naming both
|
|
116
|
-
// legal samplers (one validator, zero second interpreters).
|
|
117
|
-
//
|
|
118
|
-
// SCALING (pre-registered, not built): a PAGE_PARAMS entry names ONE reader.
|
|
119
|
-
// When a SECOND page consumes `?stepping=`, HOIST the read into one shared
|
|
120
|
-
// reader module and repoint this entry — never a second entry for the same
|
|
121
|
-
// key, never a copy-pasted inline read.
|
|
122
|
-
}),
|
|
123
95
|
Object.freeze({
|
|
124
96
|
key: KEYS.ENGINE,
|
|
125
97
|
reader: 'lib/frqtl/experiments/_shared/boot.js',
|