@descix/frqtl-node 0.7.0-p1 → 0.7.0-p3

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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  A discrete cellular-automata physics engine (compiled Rust → WASM) for node: the same modules as
4
4
  `@descix/frqtl-web` (one source, built twice), plus the node engine door `bootNode` and the hosted eyes.
5
- `0.7.0-p1` (npm dist-tag `next`). Node ≥ 22.12. See it run: https://egpt-frqtl.descix.net
5
+ `0.7.0-p3` (npm dist-tag `next`). Node ≥ 22.12. See it run: https://egpt-frqtl.descix.net
6
6
 
7
7
  ## Install
8
8
  npm install @descix/frqtl-node
package/README.md CHANGED
@@ -4,7 +4,7 @@ FRQTL for node: a discrete cellular-automata physics engine (compiled Rust → W
4
4
  local per-pixel rules — a frame moves one pixel per tick, single occupancy, momentum — rather than force
5
5
  equations. Interference, blackbody spectra and atom-like binding emerge from the automaton.
6
6
 
7
- `0.7.0-p1` (npm dist-tag `next`). Node ≥ 22.12. See it run: https://egpt-frqtl.descix.net (the showcase scenes in
7
+ `0.7.0-p3` (npm dist-tag `next`). Node ≥ 22.12. See it run: https://egpt-frqtl.descix.net (the showcase scenes in
8
8
  STUDIO and the GYM).
9
9
 
10
10
  ## Install
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@descix/frqtl-node",
3
- "version": "0.7.0-p1",
3
+ "version": "0.7.0-p3",
4
4
  "files": {
5
5
  "eyes/index.js": "b3563421030bb7fdd277eb697959fbe9107463cbb5478160edc7d887daa67be3",
6
6
  "fat/pkg-node/egpt_wasm.js": "5e6af1f6725c93c0e9ad7386f1785cbe639ce596a4caa19ca85729c1f2bea04d",
@@ -137,14 +137,15 @@
137
137
  "shim/controls/registry.js": "3df843d774968d18e012624f2aba2fb09b08ede9e21b6c1f6687d4f3e9b46d3e",
138
138
  "shim/engine/boot-phases.js": "c6e12e12824bcf6f1a289f46e41c898a5416c80c131594525145aae9a3eefbfa",
139
139
  "shim/engine/engine-stack-boot.js": "0abf57dcff2ca2bb025c787a17875d49d54d9218ac08c78cb9c678dbb6118d08",
140
- "shim/engine/router.js": "59092b6bf0474bbd0d55300dd8d3574ce6ad03fb55a0a0af5e7cceb6d365ac9c",
140
+ "shim/engine/router.js": "dcb0289143fc86dc74be1f5921695f75c026741496ac19e902b905be3ccf409a",
141
141
  "shim/engine/site-assemble.mjs": "37c9915c34b102daa8fe6389f8d66a1e3379e074e87d2ad79b0cff7a81a2d4b6",
142
142
  "shim/engine/site-graph.js": "4d5e5f9ecc2cefcf30eab25203178c0bb13230e9ce7f34bb3b36178f6b79dcb9",
143
143
  "shim/experiment-frame/index.js": "5a0219aaa225d8f808c405b08623ce85ada37d83fb4f55f983db3303e431e47f",
144
144
  "shim/expr/ast-schema.js": "4f1133b887639bbd86fab976292323bf4a955cdc873ff7ca8e8a8eb3b416923f",
145
145
  "shim/field-descriptor/index.js": "050091a9cab0bd37b32b5d8a52fc9bca3b42a089c188d7e49a11cf88b5c069f7",
146
146
  "shim/frame-data/index.js": "df6957b0b4a8e0ee0035bc970647b90089b421ae7f05911cc047d48457164be6",
147
- "shim/host/device.js": "281e7e7ec4d8164a5a9f6d8de64be7ca0781a262d23efa0b43b6cf1fdbfd797e",
147
+ "shim/host/device.js": "67bcc932f31ddd08ef7667344f5f8894badd4d9dfd3e767c72d9e1dd327f54bd",
148
+ "shim/host/host-auth.js": "dabf305a2329227f0592a588681e20dd78063a85919ee1685d2a9b01dfe5ebcf",
148
149
  "shim/host/host-view.js": "feec2f5fdc318e75cd01768c24891f85d2694a269696fcfffd9567d31983cf43",
149
150
  "shim/host/index.js": "fade4b171218965570aa2e67e841920ebb3d972ff15e1cfa0394fd3fa123d82c",
150
151
  "shim/index.js": "dd3708d4935e597d9ca46a23077064bd1f460bb1e86315372c9d147bdfb3f0d1",
@@ -186,7 +187,7 @@
186
187
  "shim/registry/roster-link.js": "7c079eec1350640c8399e16f2515e1cdfb5c6cd31f77c16dfda85ec782a538d0",
187
188
  "shim/registry/roster.js": "df0dcc70d4c2202913a1e3e8788e9f1907ad63e43150c85960ebdfe9864d53db",
188
189
  "shim/registry/scene-source.js": "913ef72fc7caeeafc2eb6a28a2dcced752579e63c83b48d51f5bf36f67c55a88",
189
- "shim/registry/studio-landing.js": "115294cd43b96c8a4892dd74eb58bdee096baf520cc655e589300f9bc1dfb724",
190
+ "shim/registry/studio-landing.js": "db2d76a241935828495ed063e6220026a13b80df402672b9ed960c128871b16e",
190
191
  "shim/render-state/apply.js": "94a9e60cb1722c9a82aa00f2f161fd65e6f438147333856a786e42a4ad5b7064",
191
192
  "shim/render-state/index.js": "72a6a3dcd8037893cb8fbd7ef32b126770e7efaec70847f8e4f2915ddb690b43",
192
193
  "shim/render/capture-envelope.js": "74db53dbc8c87d1b52452bcb858b1cc5210593062fec0b98609cd59cab63777a",
@@ -152,7 +152,7 @@ var DIST_PROFILE = buildDistProfile('../..');
152
152
  // lib/shim/engine/test/router.test.mjs asserts them against the two
153
153
  // package.json#version fields so a forgotten bump fails `make verify`, not a
154
154
  // silent stale-CDN-URL bug.
155
- var CDN_SDK_VERSION = '0.7.0-p1'; // sdk/frqtl-web/package.json#version (the ONE version owner) — bound by router.test.mjs
155
+ var CDN_SDK_VERSION = '0.7.0-p3'; // sdk/frqtl-web/package.json#version (the ONE version owner) — bound by router.test.mjs
156
156
  var CDN_MATH_SDK_VERSION = '0.3.3-p2.1'; // sdk/egpt-math-sdk/package.json#version — bound by router.test.mjs; bumped with each npm publish
157
157
 
158
158
  /**
@@ -526,7 +526,7 @@ var _assertProfileParity = (function () {
526
526
  * // CDN mode (static hosting — the artifact host shell has no server, no page anchor):
527
527
  * const cdnRouter = engineRouter({ mode: 'cdn', engine: 'wasm' });
528
528
  * console.log(cdnRouter.resolve('compat/engine.js'));
529
- * // 'https://cdn.jsdelivr.net/npm/@descix/frqtl-web@0.7.0-p1/dist/frqtl/compat/engine.js'
529
+ * // 'https://cdn.jsdelivr.net/npm/@descix/frqtl-web@0.7.0-p3/dist/frqtl/compat/engine.js'
530
530
  *
531
531
  * // CDN mode, self-hosted: a copy of dist/ served from the consumer's own origin:
532
532
  * const selfHosted = engineRouter({ mode: 'cdn', engine: 'wasm', base: 'https://example.org/vendor/frqtl-web/dist' });
@@ -11,7 +11,7 @@
11
11
  * page (an app framed by the DeSciX shell measures its own frame). A window without
12
12
  * matchMedia THROWS: every browser has it, so its absence is a caller passing the wrong object.
13
13
  *
14
- * Its three consumers ask it once each, at load:
14
+ * Its four consumers ask it once each, at load:
15
15
  * - the host-view call site (lib/features/ide/studio/host-view-entry.js) asks the shell for NoChrome on a phone, CodeSite
16
16
  * elsewhere;
17
17
  * - STUDIO (lib/features/ide/AuthorView.jsx) opens with its side panels collapsed on a phone and puts its Hand tool
@@ -19,6 +19,8 @@
19
19
  * surface (lib/features/ide/studio-try-it.js);
20
20
  * - FraqtlIde (lib/features/ide/index.jsx) opens the STUDIO tab card with the controls of that surface
21
21
  * (presets.js presetExplainerFor).
22
+ * - the IDE header's Sign In control (lib/features/ide/sign-in.js) shows only on a phone, where the shell's
23
+ * full-screen view hides the shell's own Sign In.
22
24
  * The static build compiles it into both: the stable host-view entry is bundled as its own self-contained module
23
25
  * (apps/ide/vite.config.js frqtl-host-view-entry), the IDE's rollup graph carries its own copy of the same source.
24
26
  *
@@ -0,0 +1,176 @@
1
+ /* Copyright 2023-2026 by Essam Abadir */
2
+
3
+ /**
4
+ * host-auth.js — the ONE follower of the DeSciX shell's session for an app DOCUMENT.
5
+ *
6
+ * Behind the DeSciX App Shell, `DeSciX.powch.on('auth', cb)` calls `cb` once at once with the current state and then
7
+ * on every change, with `{ signedIn: boolean, address: string|null }`; it returns the unsubscribe
8
+ * (@descix/app-sdk 0.8.0 APP_SHELL_API.md §"DeSciX.powch — identity and wallet"). `DeSciX.powch.login()` opens Powch;
9
+ * on success the SHELL routes itself, so the app never redirects. This module follows that session for a caller and
10
+ * records which host state it met on `document.documentElement.dataset.descixAuth` (a HOST_AUTH_STATES row). The
11
+ * control that shows it, and where, is the call site's policy (lib/features/ide/sign-in.js).
12
+ *
13
+ * Order of operations, from the published contract:
14
+ * - `powch` is read only after `ready()` resolved `mode:'shell'` AND its `members` names `powch` (the SDK's member
15
+ * getters console.error where the member is absent — the same rule lib/shim/host/host-view.js follows for `view`).
16
+ * - `powch` is read off the bus at the point of use (the bus object is replaced when the shell reaches READY), so
17
+ * `login` is looked up at the tap, never captured at load.
18
+ *
19
+ * A host with no session to follow is a STATE (no SDK, no shell, a shell without Powch), never an error. A host that
20
+ * breaks the published shape is a defect: it THROWS a TypeError naming the contract, and the record says
21
+ * `contract-violated`.
22
+ *
23
+ * Host-neutral ESM with no imports. It is NOT part of the stable host-view entry (that bundle carries host-view.js
24
+ * and device.js only).
25
+ */
26
+
27
+ /** Where the bridge calls this module relies on are published. */
28
+ export const HOST_AUTH_CONTRACT = '@descix/app-sdk 0.8.0 APP_SHELL_API.md §"DeSciX.powch — identity and wallet"';
29
+
30
+ /** The closed table of host session states. `session` is true where the shell holds a session the app can show. */
31
+ export const HOST_AUTH_STATES = Object.freeze([
32
+ { id: 'pending', session: false, means: 'awaiting DeSciX.ready()' },
33
+ { id: 'sdk-absent', session: false, means: 'window.DeSciX is undefined: no publisher-provided DeSciXAppSDK.js beside this page (dev, a plain static host)' },
34
+ { id: 'unmanaged', session: false, means: 'ready() resolved standalone or embedded-no-bridge: no readable shell above this page, nothing to sign into' },
35
+ { id: 'powch-unpublished', session: false, means: 'the shell resolved, but its members do not include powch' },
36
+ { id: 'signed-out', session: true, means: 'powch.on(auth) reported signedIn false' },
37
+ { id: 'signed-in', session: true, means: 'powch.on(auth) reported signedIn true' },
38
+ { id: 'contract-violated', session: false, means: 'the SDK or shell broke the published shape; the edge is in dataset.descixAuthDetail and a TypeError was thrown' },
39
+ ].map(Object.freeze));
40
+
41
+ const STATE_IDS = new Set(HOST_AUTH_STATES.map((r) => r.id));
42
+
43
+ function defaultRoot() {
44
+ return typeof document !== 'undefined' ? document.documentElement : null;
45
+ }
46
+
47
+ function record(root, id, detail) {
48
+ if (!STATE_IDS.has(id)) { throw new Error(`host-auth: '${id}' is not a HOST_AUTH_STATES row`); }
49
+ if (root) {
50
+ root.dataset.descixAuth = id;
51
+ if (detail === undefined) { delete root.dataset.descixAuthDetail; } else { root.dataset.descixAuthDetail = detail; }
52
+ }
53
+ return id;
54
+ }
55
+
56
+ function violation(root, edge) {
57
+ record(root, 'contract-violated', edge);
58
+ return new TypeError(`host-auth: ${edge} — the published contract is ${HOST_AUTH_CONTRACT}. (FAIL-LOUD.)`);
59
+ }
60
+
61
+ function liveMode(sdk) {
62
+ try { return sdk.mode; } catch (e) { return undefined; }
63
+ }
64
+
65
+ /** The auth payload, checked against the published shape. */
66
+ function sessionOf(s) {
67
+ if (!s || typeof s !== 'object' || typeof s.signedIn !== 'boolean') {
68
+ return { edge: 'powch.on(auth) delivered ' + JSON.stringify(s) + ', not { signedIn: boolean, address: string|null }' };
69
+ }
70
+ if (!(s.address === null || typeof s.address === 'string')) {
71
+ return { edge: 'powch.on(auth) delivered address ' + JSON.stringify(s.address) + ', not a string or null' };
72
+ }
73
+ return { signedIn: s.signedIn, address: s.address };
74
+ }
75
+
76
+ /**
77
+ * Follow the shell's session. `onState(id, session)` runs on every state change (`session` is
78
+ * `{ signedIn, address }` for the two session rows, else null).
79
+ *
80
+ * @param {object} args
81
+ * @param {object|undefined} args.sdk the app-side SDK object (`window.DeSciX`), or undefined
82
+ * @param {function(string, ?{signedIn: boolean, address: ?string}): void} args.onState
83
+ * @param {{dataset: object}} [args.root] where the record is written; defaults to `document.documentElement`
84
+ * @returns {{ stop: function(): void, settled: Promise<string> }} `stop` unsubscribes (idempotent; safe before
85
+ * ready() settles); `settled` resolves once the subscription is made (with the state the shell's immediate callback
86
+ * reported, else 'pending', or the non-session row met), and REJECTS with the TypeError on a contract violation.
87
+ */
88
+ export function followHostAuth({ sdk, onState, root } = {}) {
89
+ if (typeof onState !== 'function') { throw new TypeError('host-auth: followHostAuth needs an onState(id, session) function'); }
90
+ const target = root || defaultRoot();
91
+ let stopped = false;
92
+ let off = null;
93
+ const emit = (id, session = null, detail) => { if (!stopped) { onState(record(target, id, detail), session); } };
94
+ const stop = () => {
95
+ if (stopped) { return; }
96
+ stopped = true;
97
+ if (off) { const f = off; off = null; f(); }
98
+ };
99
+
100
+ if (sdk === undefined || sdk === null) {
101
+ emit('sdk-absent');
102
+ return { stop, settled: Promise.resolve('sdk-absent') };
103
+ }
104
+ if (typeof sdk.ready !== 'function') {
105
+ const err = violation(target, 'window.DeSciX is a ' + typeof sdk + ' without ready()');
106
+ return { stop, settled: Promise.reject(err) };
107
+ }
108
+
109
+ emit('pending');
110
+ const settled = (async () => {
111
+ let ready;
112
+ try {
113
+ ready = await sdk.ready();
114
+ } catch (e) {
115
+ if (liveMode(sdk) === 'embedded-no-bridge') { emit('unmanaged', null, 'ready() rejected: ' + (e && e.message)); return 'unmanaged'; }
116
+ throw violation(target, 'ready() rejected while the SDK reports mode ' + JSON.stringify(liveMode(sdk)) + ': ' + (e && e.message));
117
+ }
118
+ const mode = ready && ready.mode;
119
+ if (mode === 'standalone' || mode === 'embedded-no-bridge') { emit('unmanaged', null, 'ready() resolved ' + mode); return 'unmanaged'; }
120
+ if (mode !== 'shell') { throw violation(target, 'ready() resolved mode ' + JSON.stringify(mode)); }
121
+ if (!Array.isArray(ready.members)) { throw violation(target, 'ready() resolved shell without a members array'); }
122
+ if (!ready.members.includes('powch')) {
123
+ emit('powch-unpublished', null, 'members: ' + ready.members.join(','));
124
+ return 'powch-unpublished';
125
+ }
126
+ if (stopped) { return 'pending'; }
127
+ const powch = sdk.powch;
128
+ if (!powch) { throw violation(target, 'members names powch; the getter returned ' + String(powch)); }
129
+ if (typeof powch.on !== 'function') { throw violation(target, 'powch.on is ' + typeof powch.on); }
130
+
131
+ let fault = null;
132
+ let first = null;
133
+ const cb = (s) => {
134
+ const got = sessionOf(s);
135
+ if (got.edge) {
136
+ // The shell logs a throwing subscriber and keeps the others running (the published contract).
137
+ fault = violation(target, got.edge);
138
+ if (off) { const f = off; off = null; f(); }
139
+ if (!stopped) { onState('contract-violated', null); }
140
+ throw fault;
141
+ }
142
+ const id = got.signedIn ? 'signed-in' : 'signed-out';
143
+ if (first === null) { first = id; }
144
+ emit(id, got);
145
+ };
146
+ let unsubscribe;
147
+ try {
148
+ unsubscribe = powch.on('auth', cb);
149
+ } catch (e) {
150
+ if (fault) { throw fault; }
151
+ throw violation(target, 'powch.on(auth) threw: ' + (e && e.message));
152
+ }
153
+ if (typeof unsubscribe !== 'function') { throw violation(target, 'powch.on(auth) returned ' + typeof unsubscribe + ', not the unsubscribe function'); }
154
+ off = unsubscribe;
155
+ if (fault) { const f = off; off = null; f(); throw fault; }
156
+ if (stopped) { const f = off; off = null; f(); }
157
+ // The first state is whatever the shell's immediate callback reported; 'pending' until it arrives.
158
+ return first || 'pending';
159
+ })();
160
+ return { stop, settled };
161
+ }
162
+
163
+ /**
164
+ * Open Powch's sign-in through the shell. `powch` and its `login` are read HERE, at the tap.
165
+ * @param {object} sdk the app-side SDK object (`window.DeSciX`)
166
+ * @returns {Promise<unknown>} what `powch.login()` returns
167
+ * @throws {TypeError} a shell without a powch.login function (the control is only shown on a session row)
168
+ */
169
+ export function hostSignIn(sdk) {
170
+ const powch = sdk && sdk.powch;
171
+ if (!powch || typeof powch.login !== 'function') {
172
+ throw new TypeError('host-auth: hostSignIn found no DeSciX.powch.login function at the tap — the published ' +
173
+ 'contract is ' + HOST_AUTH_CONTRACT + '. (FAIL-LOUD.)');
174
+ }
175
+ return powch.login();
176
+ }
@@ -8,10 +8,13 @@
8
8
  * its error copy.
9
9
  *
10
10
  * STUDIO_LANDING_ATTR the <html> attribute carrying the outcome id
11
- * STUDIO_LANDING_OUTCOMES the closed table: one row per branch of STUDIO's link resolution, `{ id, runs, means }`;
12
- * `runs` says whether the scene that landing opens starts on load (through STUDIO's Run
13
- * control's own path) — true only on `ok`, a deep link that named a scene; every other
14
- * landing opens its scene in Design, waiting for Run
11
+ * STUDIO_LANDING_OUTCOMES the closed table: one row per branch of STUDIO's link resolution, `{ id, runs, cards,
12
+ * means }`; `runs` says whether the scene that landing opens starts on load (through
13
+ * STUDIO's Run control's own path) — true only on `ok`, a deep link that named a scene;
14
+ * every other landing opens its scene in Design, waiting for Run. `cards` says whether
15
+ * the landing offers the showcase cards (and the try-it) of the roster scene it opens —
16
+ * true on `ok` and on `first-contact`, so a bare STUDIO open offers what that scene's
17
+ * deep link offers; a landing that opens no roster scene offers none
15
18
  * recordStudioLanding(root, id) writes `id` on `root` (an Element, or null to only validate); an id outside the
16
19
  * table THROWS — that is a defect in the writer, never a landing
17
20
  *
@@ -32,31 +35,31 @@ export const STUDIO_LANDING_ATTR = 'data-frqtl-studio-landing';
32
35
 
33
36
  /** The closed table of STUDIO deep-link landings, in the order the resolver tests them (lib/features/ide/test/studio-landing-of.test.mjs holds it). */
34
37
  export const STUDIO_LANDING_OUTCOMES = Object.freeze([
35
- { id: 'refused-key', runs: false,
38
+ { id: 'refused-key', runs: false, cards: false,
36
39
  means: 'the link carries a key STUDIO does not own, or a deleted key (deep-link.js deepLinkRefusalOf): the refusal is on the deep-link strip and the opening scene opens' },
37
- { id: 'conflict', runs: false,
40
+ { id: 'conflict', runs: false, cards: false,
38
41
  means: 'the link names more than one scene source (two of walkthrough, project and scene), or a renderer beside a walkthrough or a project, which carry their own: nothing opens' },
39
- { id: 'mode-without-walkthrough', runs: false,
42
+ { id: 'mode-without-walkthrough', runs: false, cards: false,
40
43
  means: 'the link names a mode and no walkthrough: nothing opens' },
41
- { id: 'renderer-without-scene', runs: false,
44
+ { id: 'renderer-without-scene', runs: false, cards: false,
42
45
  means: 'the link names a renderer and no scene; a renderer is chosen only for a scene link: nothing opens' },
43
- { id: 'unknown-walkthrough', runs: false,
46
+ { id: 'unknown-walkthrough', runs: false, cards: false,
44
47
  means: 'the linked walkthrough is not a scene of the loaded roster (walkthroughById(roster, id)): nothing opens' },
45
- { id: 'unknown-mode', runs: false,
48
+ { id: 'unknown-mode', runs: false, cards: false,
46
49
  means: 'the linked mode is not one the linked scene declares (walkthroughModes): nothing opens' },
47
- { id: 'scene-refused-origin', runs: false,
50
+ { id: 'scene-refused-origin', runs: false, cards: false,
48
51
  means: 'the linked scene URL is on neither the page\'s origin nor the roster\'s (roster-link.js sceneOriginRefusalOf): the strip names the rule and both origins; nothing opens' },
49
- { id: 'scene-unreadable', runs: false,
52
+ { id: 'scene-unreadable', runs: false, cards: false,
50
53
  means: 'the linked scene URL is allowed but its read failed (a non-200, an empty body, or not a .js sketch): nothing opens' },
51
- { id: 'project-unreadable', runs: false,
54
+ { id: 'project-unreadable', runs: false, cards: false,
52
55
  means: 'the linked saved project is present but its folder is refused by the load: nothing opens' },
53
- { id: 'project-not-found', runs: false,
56
+ { id: 'project-not-found', runs: false, cards: false,
54
57
  means: 'no saved project of the linked name in this browser: nothing opens' },
55
- { id: 'ok', runs: true,
58
+ { id: 'ok', runs: true, cards: true,
56
59
  means: 'the linked walkthrough (in the linked mode, else its default), saved project or scene resolved and was handed to STUDIO\'s selection path; the scene starts on load' },
57
- { id: 'first-contact', runs: false,
58
- means: 'no STUDIO deep link: the roster\'s first-contact scene (firstContactOf(roster)) opens' },
59
- { id: 'first-contact-empty', runs: false,
60
+ { id: 'first-contact', runs: false, cards: true,
61
+ means: 'no STUDIO deep link: the roster\'s first-contact scene (firstContactOf(roster)) opens, offering its showcase cards and try-it as its deep link does' },
62
+ { id: 'first-contact-empty', runs: false, cards: false,
60
63
  means: 'no STUDIO deep link and the roster declares no first contact: the Empty Universe opens' },
61
64
  ].map(Object.freeze));
62
65
 
package/llms.txt CHANGED
@@ -1,7 +1,7 @@
1
1
  # @descix/frqtl-node
2
2
 
3
3
  > The FRQTL physics engine (compiled Rust → WASM) for node: the same modules as @descix/frqtl-web, plus
4
- > `bootNode` and the hosted eyes. 0.7.0-p1 on npm dist-tag `next`. Node ≥ 22.12.
4
+ > `bootNode` and the hosted eyes. 0.7.0-p3 on npm dist-tag `next`. Node ≥ 22.12.
5
5
 
6
6
  - Install: `npm install @descix/frqtl-node`
7
7
  - `import { bootNode, Wall } from '@descix/frqtl-node'` (require() works too); `await bootNode()` → the engine handle.
@@ -11,5 +11,5 @@
11
11
  - See it run: https://egpt-frqtl.descix.net
12
12
  - `./host-view`: `declareHostView`, `HOST_VIEW_OUTCOMES`, `CONTRACT_VIOLATIONS`, `HOST_SDK_SCRIPT` — the same declarer as @descix/frqtl-web/host-view, pure, for a node test of a call site.
13
13
  - `./register`: `node --import @descix/frqtl-node/register scene.mjs` resolves a scene's `@descix/frqtl-web/...` imports to this package.
14
- - Full agent guide: https://cdn.jsdelivr.net/npm/@descix/frqtl-node@0.7.0-p1/CLAUDE.md
14
+ - Full agent guide: https://cdn.jsdelivr.net/npm/@descix/frqtl-node@0.7.0-p3/CLAUDE.md
15
15
  - Acceptance is visual; no CLI ships. No account, credits or tokens are needed.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@descix/frqtl-node",
3
- "version": "0.7.0-p1",
3
+ "version": "0.7.0-p3",
4
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
5
  "type": "module",
6
6
  "engines": {