@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.
Files changed (80) hide show
  1. package/CLAUDE.md +2 -2
  2. package/README.md +6 -4
  3. package/dist/dist-manifest.json +71 -65
  4. package/dist/eyes/index.js +5 -2
  5. package/dist/fat/pkg-node/package.json +2 -10
  6. package/dist/frqtl/compat/providers/wasm-engine.js +3 -4
  7. package/dist/frqtl/compat/renderer/providers/p5-provider.js +1 -1
  8. package/dist/frqtl/objects/FrqtlApp.js +33 -6
  9. package/dist/frqtl/objects/RecipeContext.js +284 -13
  10. package/dist/frqtl/objects/constants/WallBehaviors.js +1 -1
  11. package/dist/frqtl/objects/instruments/capture.js +1 -24
  12. package/dist/frqtl/objects/instruments/hosted-eyes.js +216 -29
  13. package/dist/frqtl/objects/objects/makeControlsForObject.js +5 -5
  14. package/dist/frqtl/objects/record/Record.js +346 -0
  15. package/dist/frqtl/objects/record/open-record.js +22 -0
  16. package/dist/frqtl/objects/schema/engine-scopes.js +2 -2
  17. package/dist/frqtl/objects/schema/scopes.js +2 -2
  18. package/dist/frqtl/objects/setup/_worldInit.js +1 -2
  19. package/dist/frqtl/objects/setup/setupBlackbodySubstrate.js +1 -2
  20. package/dist/frqtl/parts/CameraControls.js +8 -0
  21. package/dist/frqtl/parts/DetectorWall.js +1 -1
  22. package/dist/frqtl/parts/FrqtlVision.js +21 -6
  23. package/dist/frqtl/parts/GraphingDetectorWall.js +7 -7
  24. package/dist/frqtl/parts/LargeObject.js +2 -2
  25. package/dist/frqtl/parts/LiveChart.js +2 -2
  26. package/dist/frqtl/parts/LogicGate.js +3 -3
  27. package/dist/frqtl/parts/OvenBox.js +1 -1
  28. package/dist/frqtl/parts/PointSource.js +1 -1
  29. package/dist/frqtl/parts/SlitLightSource.js +1 -1
  30. package/dist/frqtl/parts/SlitScreen.js +1 -1
  31. package/dist/frqtl/parts/SphereEmitter.js +12 -12
  32. package/dist/frqtl/parts/Trace.js +1 -1
  33. package/dist/frqtl/parts/Tunnel.js +11 -11
  34. package/dist/frqtl/parts/Wall.js +20 -10
  35. package/dist/frqtl/scenes/circuit-sat.js +1 -0
  36. package/dist/frqtl/scenes/dse-one-emitter.js +32 -34
  37. package/dist/frqtl/scenes/dse-two-emitters.js +33 -34
  38. package/dist/frqtl/scenes/mirror-route.js +96 -79
  39. package/dist/shim/bundle/index.js +118 -96
  40. package/dist/shim/bundle/indexHtml.js +66 -39
  41. package/dist/shim/controls/index.js +1 -1
  42. package/dist/shim/engine/artifact-shell.js +1 -1
  43. package/dist/shim/engine/router.js +3 -3
  44. package/dist/shim/host/host-view.js +228 -0
  45. package/dist/shim/host/index.js +18 -0
  46. package/dist/shim/index.js +3 -5
  47. package/dist/shim/notebook/format.js +1 -1
  48. package/dist/shim/notebook/kernel.js +1 -1
  49. package/dist/shim/notebook/setup-builder.js +2 -2
  50. package/dist/shim/notebook/store.js +2 -3
  51. package/dist/shim/package.json +1 -0
  52. package/dist/shim/page-params/index.js +2 -30
  53. package/dist/shim/project/fork.js +293 -0
  54. package/dist/shim/project/identity.js +3 -1
  55. package/dist/shim/project/index.js +16 -3
  56. package/dist/shim/project/link.js +224 -46
  57. package/dist/shim/project/manifest.js +151 -0
  58. package/dist/shim/project/parts-writer.js +68 -7
  59. package/dist/shim/project/read-text.js +43 -0
  60. package/dist/shim/project/record-format.js +345 -0
  61. package/dist/shim/project/specifiers.js +104 -15
  62. package/dist/shim/registry/exhibits.js +15 -34
  63. package/dist/shim/registry/index.js +3 -3
  64. package/dist/shim/registry/scene-roster.js +35 -0
  65. package/dist/shim/registry/scene-source.js +48 -64
  66. package/dist/shim/registry/walkthrough.js +6 -6
  67. package/dist/shim/render-state/apply.js +66 -31
  68. package/dist/shim/render-state/index.js +6 -2
  69. package/dist/shim/runtime/index.js +4 -4
  70. package/dist/shim/storage/codec-chains.js +88 -0
  71. package/dist/shim/storage/folders.js +205 -0
  72. package/dist/shim/storage/index.js +64 -8
  73. package/dist/shim/storage/make-namespace-store.js +5 -5
  74. package/llms.txt +4 -3
  75. package/package.json +19 -1
  76. package/dist/shim/bundle/frqnb.js +0 -112
  77. package/dist/shim/bundle/manifest.js +0 -95
  78. package/dist/shim/registry/experiments.js +0 -215
  79. package/dist/shim/storage/modules.js +0 -249
  80. package/dist/shim/storage/projects.js +0 -150
@@ -0,0 +1,293 @@
1
+ /* Copyright 2023-2026 by Essam Abadir */
2
+
3
+ /**
4
+ * fork.js — THE fork transform: a part file's text becomes the text of its renamed copy, `parts/<To>.js`.
5
+ *
6
+ * forkPartText(text, { from, to, libPath, files }) → { text, imports }
7
+ * text the source part file's exact bytes, as text
8
+ * from the class name the file registers (its kind name)
9
+ * to the new class name (the fork's kind name)
10
+ * libPath where the source lives: a package lib-identity path ('frqtl/parts/Wall.js') or, for a fork of a
11
+ * fork, the project path of the held file ('parts/MyWall.js')
12
+ * files the project's files by path ({} when none) — a fork never overwrites one
13
+ * imports [{ from, to }] — each import specifier of the source and the specifier the copy writes
14
+ *
15
+ * The text is parsed once (parts-writer.js parseModule: the vendored acorn's one importer). The copy differs from the source
16
+ * in exactly three kinds of range, and every other byte (comments, docstrings, the schema, every string) is the
17
+ * source's byte:
18
+ * 1. THE CLASS BINDING. `to` replaces the module-scope binding `from`: its declaration's id and every reference
19
+ * that resolves to it (`registerKind(Wall, …)`, `Wall.prototype…`, `instanceof Wall`, `export { Wall }`). A
20
+ * same-named binding in an inner scope, a property key and a string are untouched.
21
+ * 2. THE PALETTE NAME. The registerKind row's `palette.displayName` string becomes `to`, so a palette lists the
22
+ * fork by its own name. Every other property of the row is the source's byte.
23
+ * 3. THE IMPORTS. A package part's relative import is rewritten to the package specifier naming the SAME file
24
+ * (specifiers.js specifierOfLibPath — a part file under frqtl/parts/ → the parts prefix row; frqtl/objects/
25
+ * kinds.js → @descix/frqtl-web/kinds when every imported name is in KINDS_EXPORTS; anything else its own
26
+ * row). A fork of a fork keeps its relative imports (they name project files beside it) and its package
27
+ * specifiers as they are. A fork never copies a non-part module: a second copy of a registry or a schema
28
+ * would split one fact.
29
+ *
30
+ * REFUSED (throws, naming the part): `to` is not an identifier or is a reserved word; `to` equals `from`; `to` is
31
+ * already a registered kind, or `parts/<to>.js` is already a project file; `from` is a kind FORK_REFUSED names
32
+ * (with its reason); the file declares no module-scope `from`; a relative import with no row (naming the import).
33
+ *
34
+ * FORK_REFUSED { <kind>: reason } — the kinds no fork copies, each with the census reason (fork.test.mjs
35
+ * G-CENSUS derives the set from the part files and holds it equal to this table)
36
+ */
37
+
38
+ import { kindOf } from '../../frqtl/objects/kinds.js';
39
+ import { KINDS_EXPORTS } from '../../frqtl/objects/kinds-entry.js';
40
+ import { specifierOfLibPath } from './specifiers.js';
41
+ import { isIdentifierName, parseModule } from './parts-writer.js';
42
+
43
+ /** The kinds a fork refuses, each with why (the census: a scope row, or the class name used as a string). */
44
+ export const FORK_REFUSED = Object.freeze({
45
+ LargeObject: 'LargeObject is a scope row (schema/scopes.js SCOPE_DESCRIPTORS) and passes \'LargeObject\' as the engine ' +
46
+ 'scope name to universe.updateObjectProperty — a copy would be a second scope row',
47
+ PointSource: 'PointSource numbers its default name with declaredCount(ctx.runner, \'PointSource\') — a copy would ' +
48
+ 'number itself in its parent\'s sequence (the class name used as a string)'
49
+ });
50
+
51
+ /** The lib-identity path of the parts directory, and the kinds registry a part imports. */
52
+ const PARTS_DIR = 'frqtl/parts/';
53
+ const KINDS_LIB = 'frqtl/objects/kinds.js';
54
+
55
+ function _fail(msg) {
56
+ throw new Error('[fork] ' + msg + ' (FAIL-LOUD.)');
57
+ }
58
+
59
+ /** The children of an AST node (every key holding a node or an array of nodes). */
60
+ function _children(n) {
61
+ const out = [];
62
+ for (const k of Object.keys(n)) {
63
+ if (k === 'type' || k === 'start' || k === 'end' || k === 'loc' || k === 'range') { continue; }
64
+ const v = n[k];
65
+ if (Array.isArray(v)) { for (const c of v) { if (c && typeof c.type === 'string') { out.push(c); } } }
66
+ else if (v && typeof v.type === 'string') { out.push(v); }
67
+ }
68
+ return out;
69
+ }
70
+
71
+ function _patternNames(p, out) {
72
+ if (!p) { return out; }
73
+ if (p.type === 'Identifier') { out.push(p.name); }
74
+ else if (p.type === 'ObjectPattern') { for (const q of p.properties) { _patternNames(q.type === 'RestElement' ? q.argument : q.value, out); } }
75
+ else if (p.type === 'ArrayPattern') { for (const e of p.elements) { _patternNames(e, out); } }
76
+ else if (p.type === 'RestElement') { _patternNames(p.argument, out); }
77
+ else if (p.type === 'AssignmentPattern') { _patternNames(p.left, out); }
78
+ return out;
79
+ }
80
+
81
+ const _FUNCTION = { FunctionDeclaration: 1, FunctionExpression: 1, ArrowFunctionExpression: 1 };
82
+
83
+ /** `var` names declared in a function body (not crossing a nested function). */
84
+ function _varNames(n, out) {
85
+ for (const c of _children(n)) {
86
+ if (_FUNCTION[c.type]) { continue; }
87
+ if (c.type === 'VariableDeclaration' && c.kind === 'var') {
88
+ for (const d of c.declarations) { _patternNames(d.id, out); }
89
+ }
90
+ _varNames(c, out);
91
+ }
92
+ return out;
93
+ }
94
+
95
+ /** Block-scoped names declared directly in a statement list (let/const/class/function). */
96
+ function _lexicalNames(body, out) {
97
+ for (const s of body) {
98
+ const d = (s.type === 'ExportNamedDeclaration' || s.type === 'ExportDefaultDeclaration') ? s.declaration : s;
99
+ if (!d) { continue; }
100
+ if (d.type === 'VariableDeclaration' && d.kind !== 'var') { for (const x of d.declarations) { _patternNames(x.id, out); } }
101
+ else if ((d.type === 'ClassDeclaration' || d.type === 'FunctionDeclaration') && d.id) { out.push(d.id.name); }
102
+ }
103
+ return out;
104
+ }
105
+
106
+ /** The names a node introduces into the scope it opens (null when it opens none). */
107
+ function _scopeNames(n, isModule) {
108
+ if (_FUNCTION[n.type]) {
109
+ const names = [];
110
+ for (const p of n.params) { _patternNames(p, names); }
111
+ if (n.type === 'FunctionExpression' && n.id) { names.push(n.id.name); }
112
+ if (n.body.type === 'BlockStatement') { _varNames(n.body, names); _lexicalNames(n.body.body, names); }
113
+ return names;
114
+ }
115
+ if (n.type === 'BlockStatement' || n.type === 'StaticBlock') { return isModule ? null : _lexicalNames(n.body, []); }
116
+ if (n.type === 'SwitchStatement') { return _lexicalNames(n.cases.flatMap(function (c) { return c.consequent; }), []); }
117
+ if (n.type === 'ForStatement' || n.type === 'ForInStatement' || n.type === 'ForOfStatement') {
118
+ const init = n.type === 'ForStatement' ? n.init : n.left;
119
+ if (!init || init.type !== 'VariableDeclaration' || init.kind === 'var') { return []; }
120
+ return init.declarations.reduce(function (acc, d) { return _patternNames(d.id, acc); }, []);
121
+ }
122
+ if (n.type === 'CatchClause') { return n.param ? _patternNames(n.param, []) : []; }
123
+ if (n.type === 'ClassExpression' && n.id) { return [n.id.name]; }
124
+ return null;
125
+ }
126
+
127
+ /**
128
+ * Every Identifier node that resolves to the MODULE-scope binding `name` (declarations and references).
129
+ * Positions that are not references (non-computed property keys and member properties, labels, an import's
130
+ * imported name, an export's exported name when it differs from the local) are skipped.
131
+ */
132
+ function _moduleBindingIds(ast, name) {
133
+ const hits = [];
134
+ function visit(n, shadowed, parent, key) {
135
+ if (n.type === 'Identifier') {
136
+ if (n.name !== name || shadowed) { return; }
137
+ if (parent) {
138
+ if (parent.type === 'MemberExpression' && key === 'property' && !parent.computed) { return; }
139
+ if ((parent.type === 'Property' || parent.type === 'MethodDefinition' || parent.type === 'PropertyDefinition') &&
140
+ key === 'key' && !parent.computed && !parent.shorthand) { return; }
141
+ if ((parent.type === 'LabeledStatement' || parent.type === 'BreakStatement' || parent.type === 'ContinueStatement') && key === 'label') { return; }
142
+ // An import's imported name and an export's exported name are names, not references: the local
143
+ // is the binding (acorn hands the SAME node to both keys of a shorthand specifier).
144
+ if (parent.type === 'ImportSpecifier' && key === 'imported') { return; }
145
+ if (parent.type === 'ExportSpecifier' && key === 'exported') { return; }
146
+ }
147
+ hits.push(n);
148
+ return;
149
+ }
150
+ const names = n === ast ? null : _scopeNames(n, false);
151
+ const sh = shadowed || !!(names && names.indexOf(name) !== -1);
152
+ for (const k of Object.keys(n)) {
153
+ if (k === 'type' || k === 'start' || k === 'end' || k === 'loc') { continue; }
154
+ const v = n[k];
155
+ if (Array.isArray(v)) { for (const c of v) { if (c && typeof c.type === 'string') { visit(c, _childShadow(n, k, c, sh, shadowed), n, k); } } }
156
+ else if (v && typeof v.type === 'string') { visit(v, _childShadow(n, k, v, sh, shadowed), n, k); }
157
+ }
158
+ }
159
+ // A function DECLARATION's id belongs to the enclosing scope, not the one the function opens.
160
+ function _childShadow(n, k, c, sh, outer) {
161
+ if (n.type === 'FunctionDeclaration' && k === 'id') { return outer; }
162
+ return sh;
163
+ }
164
+ visit(ast, false, null, null);
165
+ return hits;
166
+ }
167
+
168
+ /** The module-top binding of `name`: its declaration node, or null. */
169
+ function _topDeclaration(ast, name) {
170
+ for (const s of ast.body) {
171
+ const d = (s.type === 'ExportNamedDeclaration' || s.type === 'ExportDefaultDeclaration') ? s.declaration : s;
172
+ if (!d) { continue; }
173
+ if ((d.type === 'ClassDeclaration' || d.type === 'FunctionDeclaration') && d.id && d.id.name === name) { return d; }
174
+ if (d.type === 'VariableDeclaration' && d.declarations.some(function (x) { return _patternNames(x.id, []).indexOf(name) !== -1; })) { return d; }
175
+ }
176
+ return null;
177
+ }
178
+
179
+ /** The `palette.displayName` string Literal of the module's `registerKind(<from>, { … })` row, or null. */
180
+ function _paletteName(ast, from) {
181
+ let found = null;
182
+ (function visit(n) {
183
+ if (found) { return; }
184
+ if (n.type === 'CallExpression' && n.callee.type === 'Identifier' && n.callee.name === 'registerKind' &&
185
+ n.arguments[0] && n.arguments[0].type === 'Identifier' && n.arguments[0].name === from &&
186
+ n.arguments[1] && n.arguments[1].type === 'ObjectExpression') {
187
+ const pal = n.arguments[1].properties.find(function (p) { return p.type === 'Property' && !p.computed && p.key.name === 'palette'; });
188
+ if (pal && pal.value.type === 'ObjectExpression') {
189
+ const dn = pal.value.properties.find(function (p) { return p.type === 'Property' && !p.computed && p.key.name === 'displayName'; });
190
+ if (dn && dn.value.type === 'Literal' && typeof dn.value.value === 'string') { found = dn.value; }
191
+ }
192
+ return;
193
+ }
194
+ for (const c of _children(n)) { visit(c); }
195
+ })(ast);
196
+ return found;
197
+ }
198
+
199
+ /** The lib-identity (or project) path a relative specifier names from `fromPath`. */
200
+ function _resolve(fromPath, spec) {
201
+ const segs = fromPath.split('/');
202
+ segs.pop();
203
+ for (const s of spec.split('/')) {
204
+ if (s === '.' || s === '') { continue; }
205
+ if (s === '..') { segs.pop(); } else { segs.push(s); }
206
+ }
207
+ return segs.join('/');
208
+ }
209
+
210
+ function _quoteLike(original, value) {
211
+ const q = original[0] === '"' ? '"' : '\'';
212
+ return q + value.replace(/\\/g, '\\\\').split(q).join('\\' + q) + q;
213
+ }
214
+
215
+ /**
216
+ * @param {string} text — the source part file
217
+ * @param {{from: string, to: string, libPath: string, files: Object<string, string>}} opts
218
+ * @returns {{text: string, imports: Array<{from: string, to: string}>}}
219
+ */
220
+ export function forkPartText(text, opts) {
221
+ if (typeof text !== 'string' || !text) { _fail('forkPartText(text, opts): text is the part file (got ' + typeof text + ').'); }
222
+ const o = opts || {};
223
+ const from = o.from, to = o.to, libPath = o.libPath, files = o.files;
224
+ if (typeof from !== 'string' || !from) { _fail('forkPartText: `from` is the class name the part registers.'); }
225
+ if (typeof libPath !== 'string' || !libPath) { _fail('forkPartText(' + from + '): `libPath` names the source file.'); }
226
+ if (!files || typeof files !== 'object') { _fail('forkPartText(' + from + '): `files` is the project\'s file map ({} when none).'); }
227
+ if (typeof to !== 'string' || !isIdentifierName(to)) {
228
+ _fail('forkPartText(' + from + '): the new class name ' + JSON.stringify(to) + ' is not an identifier (or is a reserved word) ' +
229
+ '— the class name IS the kind name.');
230
+ }
231
+ if (to === from) { _fail('forkPartText(' + from + '): the new class name equals the part\'s own — a fork is a NEW kind; name it.'); }
232
+ if (kindOf(to)) {
233
+ _fail('forkPartText(' + from + '): "' + to + '" is already a registered kind (' + kindOf(to).from + ') — one name has one owner; ' +
234
+ 'choose another name.');
235
+ }
236
+ const target = 'parts/' + to + '.js';
237
+ if (Object.prototype.hasOwnProperty.call(files, target)) {
238
+ _fail('forkPartText(' + from + '): ' + target + ' is already a project file — a fork never overwrites one.');
239
+ }
240
+ if (Object.prototype.hasOwnProperty.call(FORK_REFUSED, from)) {
241
+ _fail('forkPartText(' + from + '): ' + from + ' is not forkable — ' + FORK_REFUSED[from] + '.');
242
+ }
243
+ const pkg = libPath.indexOf(PARTS_DIR) === 0;
244
+ let ast;
245
+ try { ast = parseModule(text); }
246
+ catch (err) { _fail('forkPartText(' + from + '): ' + libPath + ' does not parse (' + err.message + ').'); }
247
+ if (!_topDeclaration(ast, from)) {
248
+ _fail('forkPartText(' + from + '): ' + libPath + ' declares no module-scope ' + from + ' — the file a kind\'s `from` names declares its class.');
249
+ }
250
+
251
+ const patches = [];
252
+ const imports = [];
253
+ for (const s of ast.body) {
254
+ const src = (s.type === 'ImportDeclaration' || ((s.type === 'ExportNamedDeclaration' || s.type === 'ExportAllDeclaration') && s.source))
255
+ ? s.source : null;
256
+ if (!src) { continue; }
257
+ const spec = src.value;
258
+ if (!(spec.startsWith('./') || spec.startsWith('../'))) { imports.push({ from: spec, to: spec }); continue; }
259
+ if (!pkg) { imports.push({ from: spec, to: spec }); continue; } // a fork of a fork: project files beside it
260
+ const lp = _resolve(libPath, spec);
261
+ let out;
262
+ if (lp === KINDS_LIB) {
263
+ const names = (s.specifiers || []).map(function (x) {
264
+ return x.type === 'ImportSpecifier' ? (x.imported.name || x.imported.value) : '*';
265
+ });
266
+ const outside = names.filter(function (n) { return KINDS_EXPORTS.indexOf(n) === -1; });
267
+ if (outside.length) {
268
+ _fail('forkPartText(' + from + '): ' + libPath + ' imports ' + outside.join(', ') + ' from "' + spec + '" — the kinds ' +
269
+ 'door (@descix/frqtl-web/kinds) publishes ' + KINDS_EXPORTS.join(', ') + ' only.');
270
+ }
271
+ out = specifierOfLibPath('frqtl/objects/kinds-entry.js');
272
+ } else {
273
+ out = specifierOfLibPath(lp);
274
+ }
275
+ if (!out) {
276
+ _fail('forkPartText(' + from + '): ' + libPath + ' imports "' + spec + '" (' + lp + '), which no specifier row names — a ' +
277
+ 'fork imports every non-part module from the package (lib/shim/project/specifiers.js); it never copies one.');
278
+ }
279
+ patches.push({ start: src.start, end: src.end, text: _quoteLike(text.slice(src.start, src.end), out) });
280
+ imports.push({ from: spec, to: out });
281
+ }
282
+ for (const id of _moduleBindingIds(ast, from)) { patches.push({ start: id.start, end: id.end, text: to }); }
283
+ const dn = _paletteName(ast, from);
284
+ if (dn) { patches.push({ start: dn.start, end: dn.end, text: _quoteLike(text.slice(dn.start, dn.end), to) }); }
285
+
286
+ patches.sort(function (a, b) { return b.start - a.start; });
287
+ for (let i = 1; i < patches.length; i++) {
288
+ if (patches[i].end > patches[i - 1].start) { _fail('forkPartText(' + from + '): overlapping rewrites — a bug in the transform.'); }
289
+ }
290
+ let out = text;
291
+ for (const p of patches) { out = out.slice(0, p.start) + p.text + out.slice(p.end); }
292
+ return { text: out, imports: imports };
293
+ }
@@ -103,7 +103,9 @@ export async function projectIdentity(project) {
103
103
  for (const c of chunks) { stream.set(c, at); at += c.length; }
104
104
  const subtle = (typeof crypto !== 'undefined') ? crypto.subtle : null;
105
105
  if (!subtle || typeof subtle.digest !== 'function') {
106
- _fail('crypto.subtle.digest is unavailable — the project identity is a content hash with no second hashing path.');
106
+ _fail('crypto.subtle is unavailable — this page is not a secure context (isSecureContext ' + String(typeof isSecureContext === 'undefined' ? undefined : isSecureContext) +
107
+ '): serve the folder from https or from localhost (e.g. npx serve). The project identity is a content hash with no second ' +
108
+ 'hashing path.');
107
109
  }
108
110
  const view = new Uint8Array(await subtle.digest('SHA-256', stream));
109
111
  let hex = '';
@@ -5,17 +5,30 @@
5
5
  * project identity, re-exported from their owners (this file owns nothing).
6
6
  *
7
7
  * FRQTL_WEB_SPECIFIERS, libPathOf, importMapFor,
8
- * pageImportMapFor specifiers.js
8
+ * pageImportMapFor, FRQTL_WEB_HOST_SPECIFIERS specifiers.js (the host rows: a host page reads the
9
+ * dist root from its own map through them)
9
10
  * linkProject, sketchProject, SKETCH_ENTRY link.js
10
11
  * projectIdentity identity.js
12
+ * MANIFEST_FILE, MANIFEST_KEYS, normalizeManifest,
13
+ * parseManifest, serializeManifest, manifestFor manifest.js (THE owner of frqtl.json)
14
+ * RECORD_FILES, RECORD_KEYS, CHANGE_KEYS, OPS, BY,
15
+ * CLIP_SECONDS, CLIP_NAME, clipName, jsonText,
16
+ * recordFolderFaults record-format.js (THE owner of the Record folder's shape)
11
17
  *
12
18
  * PROJECT_EXPORTS is this entry's export list; a conformance test holds the module's exports equal to
13
19
  * it, and the package gate holds it equal across @descix/frqtl-web and @descix/frqtl-node.
14
20
  */
15
21
 
16
22
  export const PROJECT_EXPORTS = Object.freeze(['FRQTL_WEB_SPECIFIERS', 'libPathOf', 'importMapFor', 'pageImportMapFor',
17
- 'linkProject', 'sketchProject', 'SKETCH_ENTRY', 'projectIdentity']);
23
+ 'FRQTL_WEB_HOST_SPECIFIERS',
24
+ 'linkProject', 'sketchProject', 'SKETCH_ENTRY', 'projectIdentity',
25
+ 'MANIFEST_FILE', 'MANIFEST_KEYS', 'normalizeManifest', 'parseManifest', 'serializeManifest', 'manifestFor',
26
+ 'RECORD_FILES', 'RECORD_KEYS', 'CHANGE_KEYS', 'OPS', 'BY', 'CLIP_SECONDS', 'CLIP_NAME', 'clipName', 'jsonText',
27
+ 'recordFolderFaults']);
18
28
 
19
- export { FRQTL_WEB_SPECIFIERS, libPathOf, importMapFor, pageImportMapFor } from './specifiers.js';
29
+ export { FRQTL_WEB_SPECIFIERS, libPathOf, importMapFor, pageImportMapFor, FRQTL_WEB_HOST_SPECIFIERS } from './specifiers.js';
20
30
  export { linkProject, sketchProject, SKETCH_ENTRY } from './link.js';
21
31
  export { projectIdentity } from './identity.js';
32
+ export { MANIFEST_FILE, MANIFEST_KEYS, normalizeManifest, parseManifest, serializeManifest, manifestFor } from './manifest.js';
33
+ export { RECORD_FILES, RECORD_KEYS, CHANGE_KEYS, OPS, BY, CLIP_SECONDS, CLIP_NAME, clipName, jsonText,
34
+ recordFolderFaults } from './record-format.js';