@peripheries/renderer 0.1.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 (57) hide show
  1. package/README.md +65 -0
  2. package/dist/chunks/create-store-CglsI7tw.js +90 -0
  3. package/dist/chunks/create-store-CglsI7tw.js.map +1 -0
  4. package/dist/chunks/load-6zEgvJ6I.js +60 -0
  5. package/dist/chunks/load-6zEgvJ6I.js.map +1 -0
  6. package/dist/chunks/page-bundles-C4VW3dsK.js +87 -0
  7. package/dist/chunks/page-bundles-C4VW3dsK.js.map +1 -0
  8. package/dist/chunks/projection-Dsy9ruj8.js +592 -0
  9. package/dist/chunks/projection-Dsy9ruj8.js.map +1 -0
  10. package/dist/core.js +740 -0
  11. package/dist/core.js.map +1 -0
  12. package/dist/react.js +357 -0
  13. package/dist/react.js.map +1 -0
  14. package/dist/runtime.js +91 -0
  15. package/dist/runtime.js.map +1 -0
  16. package/dist/styles.css +51 -0
  17. package/dist/testing.js +944 -0
  18. package/dist/testing.js.map +1 -0
  19. package/dist/types/core/bundles/load.d.ts +49 -0
  20. package/dist/types/core/bundles/page-bundles.d.ts +17 -0
  21. package/dist/types/core/bundles/refs.d.ts +41 -0
  22. package/dist/types/core/bundles/resolve.d.ts +25 -0
  23. package/dist/types/core/cell-props.d.ts +39 -0
  24. package/dist/types/core/cells.d.ts +39 -0
  25. package/dist/types/core/data-context.d.ts +38 -0
  26. package/dist/types/core/graph.d.ts +95 -0
  27. package/dist/types/core/index.d.ts +19 -0
  28. package/dist/types/core/layout.d.ts +12 -0
  29. package/dist/types/core/load-page.d.ts +72 -0
  30. package/dist/types/core/references.d.ts +43 -0
  31. package/dist/types/core/store/create-store.d.ts +26 -0
  32. package/dist/types/core/store/persistence.d.ts +9 -0
  33. package/dist/types/core/store/projection.d.ts +15 -0
  34. package/dist/types/core/store/scope-keys.d.ts +10 -0
  35. package/dist/types/core/store/types.d.ts +37 -0
  36. package/dist/types/core/structure.d.ts +72 -0
  37. package/dist/types/core/types.d.ts +30 -0
  38. package/dist/types/core/validate.d.ts +14 -0
  39. package/dist/types/react/cell-states.d.ts +25 -0
  40. package/dist/types/react/cell-view.d.ts +31 -0
  41. package/dist/types/react/index.d.ts +6 -0
  42. package/dist/types/react/page-renderer.d.ts +30 -0
  43. package/dist/types/react/structure-view.d.ts +30 -0
  44. package/dist/types/react/use-cell-store.d.ts +13 -0
  45. package/dist/types/react/use-container-size.d.ts +11 -0
  46. package/dist/types/runtime/boot.d.ts +7 -0
  47. package/dist/types/runtime/index.d.ts +4 -0
  48. package/dist/types/runtime/install.d.ts +21 -0
  49. package/dist/types/runtime/shims.d.ts +11 -0
  50. package/dist/types/runtime/store-export.d.ts +18 -0
  51. package/dist/types/testing/base-props.d.ts +3 -0
  52. package/dist/types/testing/components.d.ts +51 -0
  53. package/dist/types/testing/golden.d.ts +51 -0
  54. package/dist/types/testing/ids.d.ts +14 -0
  55. package/dist/types/testing/index.d.ts +5 -0
  56. package/dist/types/testing/sources.d.ts +25 -0
  57. package/package.json +51 -0
package/dist/core.js ADDED
@@ -0,0 +1,740 @@
1
+ import { B as hasRef, D as numberingFor, E as normalizeStructure, F as resolveReferences, H as resolveCell, I as settled, L as CONTENT_FIELDS, M as usesStructureLayout, N as cellResolutionContext, O as referencesFor, P as passesConditions, R as HYDRATED_CELL_FIELDS, S as deriveSectionReferences, T as nodePrintStyle, U as rootCells, V as normalizeCell, _ as projectCellData, a as projectSelections, b as deriveElementReferences, c as pageScopeKey, d as nearestThemeProvider, f as resolveComponent, g as assembleCellProps, h as pageContainerStyle, i as projectLive, j as structureIssues, k as sectionAnchor, l as projectScopeKey, m as cellPlacementStyle, n as attachViewStateSetters, o as projectViewState, p as cellAttributes, r as collectScopeIds, s as shallowEqualBundle, t as attachSelectionSetters, u as resolveScopeId, v as PAGE_DOCUMENT, w as mergeSectionReferences, x as deriveSectionNumbering, y as SECTION_KINDS, z as STORE_FIELDS } from "./chunks/projection-Dsy9ruj8.js";
2
+ import { a as projectModule, i as loadBundles, n as isComponentExport, o as runtimeReady, r as loadBundle, t as _clearBundleCache } from "./chunks/load-6zEgvJ6I.js";
3
+ import { a as bundleScope, i as PUBLIC, n as pageBundles, o as normalizeRef, r as MAIN, s as refKey, t as pageBundleRefs } from "./chunks/page-bundles-C4VW3dsK.js";
4
+ import { t as createStackStore } from "./chunks/create-store-CglsI7tw.js";
5
+ import { DEFAULT_STATE, STATE_NAMES, WALK_DEFAULTS, isStateName } from "@peripheries/runtime";
6
+ //#region packages/renderer/src/core/graph.ts
7
+ function systemsFromProcess(row) {
8
+ const out = {};
9
+ for (const name of STATE_NAMES) {
10
+ const id = row?.[`${name}_system_id`];
11
+ if (typeof id === "string" && id) out[name] = id;
12
+ }
13
+ return out;
14
+ }
15
+ function stateNames(state) {
16
+ const raw = state === void 0 || state === null ? [DEFAULT_STATE] : Array.isArray(state) ? state : [state];
17
+ const names = [];
18
+ const unknown = [];
19
+ for (const s of raw) if (!isStateName(s)) unknown.push(String(s));
20
+ else if (!names.includes(s)) names.push(s);
21
+ return {
22
+ names,
23
+ unknown
24
+ };
25
+ }
26
+ function readField(state, field) {
27
+ if (!field) return state;
28
+ return field.split(".").reduce((o, k) => o === null || o === void 0 ? void 0 : o[k], state);
29
+ }
30
+ function mergeStates(values) {
31
+ let merged = void 0;
32
+ for (const v of values) {
33
+ if (v === void 0) continue;
34
+ merged = isPlainObject(merged) && isPlainObject(v) ? {
35
+ ...merged,
36
+ ...v
37
+ } : v;
38
+ }
39
+ return merged;
40
+ }
41
+ function stateValue(state, names, field) {
42
+ const merged = mergeStates(names.map((n) => state[n] ? state[n].data : void 0));
43
+ return merged === void 0 ? void 0 : readField(merged, field);
44
+ }
45
+ var isPlainObject = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
46
+ function resolveWalkStart(cell, pageGraphNodeId) {
47
+ const own = cell.graph_node_id ?? cell.metadata?.graph_node_id;
48
+ if (own) return {
49
+ graphNodeId: own,
50
+ kind: "cell"
51
+ };
52
+ if (pageGraphNodeId) return {
53
+ graphNodeId: pageGraphNodeId,
54
+ kind: "page"
55
+ };
56
+ return null;
57
+ }
58
+ var RESERVED = new Set([
59
+ "_traversal",
60
+ "params",
61
+ "baseProps"
62
+ ]);
63
+ var STRICT_REF = /^@([A-Za-z0-9_$-]+)(\.[A-Za-z0-9_$[\]-]+)*$/;
64
+ function referencedLabels(cell) {
65
+ const out = new Set();
66
+ const visit = (v) => {
67
+ if (typeof v === "string") {
68
+ const m = STRICT_REF.exec(v);
69
+ if (m) out.add(m[1]);
70
+ } else if (Array.isArray(v)) v.forEach(visit);
71
+ else if (v && typeof v === "object") Object.values(v).forEach(visit);
72
+ };
73
+ visit(cell.props);
74
+ visit(cell.conditions);
75
+ return out;
76
+ }
77
+ function needsWalk(cell, explicit) {
78
+ for (const label of referencedLabels(cell)) {
79
+ if (label === "_traversal") return true;
80
+ if (RESERVED.has(label) || explicit.has(label) || cell.data?.queries?.[label]) continue;
81
+ return true;
82
+ }
83
+ return false;
84
+ }
85
+ var sortedOrNull = (v) => Array.isArray(v) && v.length ? [...new Set(v.map(String))].sort() : null;
86
+ function walkRequest(config, start, systems) {
87
+ const diagnostics = [];
88
+ const edgeTypes = sortedOrNull(config.edgeTypes);
89
+ if (!edgeTypes) return { diagnostics: [{
90
+ severity: "error",
91
+ code: "WALK_EDGE_TYPES_REQUIRED",
92
+ message: "traversal.edgeTypes is required: without it a walk follows every edge type"
93
+ }] };
94
+ const direction = config.direction ?? WALK_DEFAULTS.direction;
95
+ if (![
96
+ "incoming",
97
+ "outgoing",
98
+ "both"
99
+ ].includes(direction)) return { diagnostics: [{
100
+ severity: "error",
101
+ code: "WALK_BAD_DIRECTION",
102
+ message: `traversal.direction "${direction}" isn't incoming, outgoing or both`
103
+ }] };
104
+ const requested = config.maxNodes ?? WALK_DEFAULTS.maxNodes;
105
+ if (requested > WALK_DEFAULTS.maxNodesCeiling) diagnostics.push({
106
+ severity: "warning",
107
+ code: "WALK_MAX_NODES_CLAMPED",
108
+ message: `traversal.maxNodes ${requested} is above ${WALK_DEFAULTS.maxNodesCeiling}; clamped`
109
+ });
110
+ const { names, unknown } = stateNames(config.include_state);
111
+ for (const u of unknown) diagnostics.push({
112
+ severity: "error",
113
+ code: "STATE_UNKNOWN_NAME",
114
+ message: `include_state "${u}" isn't a system name (${STATE_NAMES.join(", ")})`
115
+ });
116
+ const states = [];
117
+ for (const name of names) {
118
+ const systemId = systems[name];
119
+ if (systemId) states.push({
120
+ name,
121
+ systemId
122
+ });
123
+ else diagnostics.push({
124
+ severity: "error",
125
+ code: "STATE_UNKNOWN_SYSTEM",
126
+ message: `the page's process has no ${name} system`
127
+ });
128
+ }
129
+ return {
130
+ request: {
131
+ start,
132
+ direction,
133
+ edgeTypes,
134
+ nodeTypes: sortedOrNull(config.nodeTypes),
135
+ maxDepth: config.maxDepth ?? WALK_DEFAULTS.maxDepth,
136
+ maxNodes: Math.min(requested, WALK_DEFAULTS.maxNodesCeiling),
137
+ states,
138
+ excludeNodeTypes: sortedOrNull(config.excludeNodeTypes),
139
+ excludeEdgeTypes: sortedOrNull(config.excludeEdgeTypes)
140
+ },
141
+ diagnostics
142
+ };
143
+ }
144
+ var walkKey = (r) => JSON.stringify([
145
+ r.start.graphNodeId,
146
+ r.direction,
147
+ r.edgeTypes,
148
+ r.nodeTypes,
149
+ r.maxDepth,
150
+ r.maxNodes,
151
+ r.states.map((s) => s.systemId),
152
+ r.excludeNodeTypes,
153
+ r.excludeEdgeTypes
154
+ ]);
155
+ function isNoise(node, start) {
156
+ if (node.graph_node_id === start.graphNodeId) return true;
157
+ if (node.node_type === "project_page_cell") return true;
158
+ return start.kind === "page" && node.content?.role === "page";
159
+ }
160
+ function readWalk(raw, request) {
161
+ const diagnostics = [];
162
+ if (raw.truncated) diagnostics.push({
163
+ severity: "warning",
164
+ code: "WALK_TRUNCATED",
165
+ message: `the walk stopped at maxNodes ${request.maxNodes}; nodes past it are missing`
166
+ });
167
+ const nodes = raw.nodes.filter((n) => !isNoise(n, request.start)).map((n) => {
168
+ const state = {};
169
+ for (const { name, systemId } of request.states) state[name] = n.state?.[systemId] ?? null;
170
+ return {
171
+ ...n,
172
+ state
173
+ };
174
+ });
175
+ const kept = new Set(nodes.map((n) => n.id));
176
+ const edges = raw.edges.filter((e) => kept.has(e.source) && kept.has(e.target));
177
+ const byLabel = new Map();
178
+ for (const n of nodes) {
179
+ const label = n.content?.binding_key;
180
+ if (typeof label !== "string" || !label) continue;
181
+ const field = typeof n.content?.field === "string" ? n.content.field : void 0;
182
+ byLabel.set(label, [...byLabel.get(label) ?? [], {
183
+ label,
184
+ graph_node_id: n.graph_node_id,
185
+ field
186
+ }]);
187
+ }
188
+ const bindings = [];
189
+ for (const [label, found] of byLabel) if (found.length === 1) bindings.push(found[0]);
190
+ else diagnostics.push({
191
+ severity: "error",
192
+ code: "BINDING_DUPLICATE_KEY",
193
+ label,
194
+ message: `${found.length} nodes the walk reached share binding_key "${label}"; it binds nothing`
195
+ });
196
+ return {
197
+ traversal: {
198
+ nodes,
199
+ edges
200
+ },
201
+ bindings,
202
+ diagnostics
203
+ };
204
+ }
205
+ //#endregion
206
+ //#region packages/renderer/src/core/data-context.ts
207
+ function resolveDeclarations(queries, systems, diagnostics = [], cellId) {
208
+ const out = {};
209
+ for (const [label, raw] of Object.entries(queries ?? {})) {
210
+ const decl = raw;
211
+ if (!decl?.graph_node_id) continue;
212
+ let reads;
213
+ if (decl.system_id) reads = [{ systemId: decl.system_id }];
214
+ else {
215
+ const { names, unknown } = stateNames(decl.state);
216
+ for (const u of unknown) diagnostics.push({
217
+ severity: "error",
218
+ code: "STATE_UNKNOWN_NAME",
219
+ label,
220
+ cellId,
221
+ message: `"${label}" names state "${u}", which isn't a system name`
222
+ });
223
+ reads = [];
224
+ for (const name of names) if (systems[name]) reads.push({
225
+ name,
226
+ systemId: systems[name]
227
+ });
228
+ else diagnostics.push({
229
+ severity: "error",
230
+ code: "STATE_UNKNOWN_SYSTEM",
231
+ label,
232
+ cellId,
233
+ message: `"${label}" reads ${name}, and the page's process has no ${name} system`
234
+ });
235
+ }
236
+ if (reads.length) out[label] = {
237
+ ...decl,
238
+ reads
239
+ };
240
+ }
241
+ return out;
242
+ }
243
+ var collectDeclarations = (page, systems, diagnostics = []) => resolveDeclarations(page.content?.data?.queries, systems, diagnostics);
244
+ function collectStatic(page) {
245
+ return { ...page.content?.data?.static ?? {} };
246
+ }
247
+ function buildDataContext(input) {
248
+ const ctx = {};
249
+ for (const [k, v] of Object.entries(input.static)) ctx[k] = settled(v);
250
+ for (const [label, decl] of Object.entries(input.declared)) {
251
+ if (decl.as === "record") {
252
+ ctx[label] = settled(input.records?.[decl.graph_node_id] ?? null);
253
+ continue;
254
+ }
255
+ const merged = mergeStates(decl.reads.map((r) => input.states.get(`${r.systemId}/${decl.graph_node_id}`)));
256
+ ctx[label] = settled(merged === void 0 ? null : readField(merged, decl.field) ?? null);
257
+ }
258
+ return ctx;
259
+ }
260
+ //#endregion
261
+ //#region packages/renderer/src/core/validate.ts
262
+ var BUCKETS = [
263
+ "narrow",
264
+ "compact",
265
+ "wide"
266
+ ];
267
+ var SCOPES = ["page", "project"];
268
+ var DIRECTIONS = [
269
+ "incoming",
270
+ "outgoing",
271
+ "both"
272
+ ];
273
+ function checkBindings(queries, path, push, cellId) {
274
+ if (queries === void 0) return;
275
+ if (!queries || typeof queries !== "object" || Array.isArray(queries)) {
276
+ push({
277
+ severity: "error",
278
+ code: "BINDING_INVALID",
279
+ cellId,
280
+ path,
281
+ message: "data.queries must be an object of label → binding"
282
+ });
283
+ return;
284
+ }
285
+ for (const [label, raw] of Object.entries(queries)) {
286
+ const at = `${path}.${label}`;
287
+ const b = raw;
288
+ if (!b || typeof b !== "object") {
289
+ push({
290
+ severity: "error",
291
+ code: "BINDING_INVALID",
292
+ cellId,
293
+ label,
294
+ path: at,
295
+ message: `"${label}" isn't a binding`
296
+ });
297
+ continue;
298
+ }
299
+ if (typeof b.registry === "string") {
300
+ push({
301
+ severity: "warning",
302
+ code: "UNSUPPORTED_REGISTRY_BINDING",
303
+ cellId,
304
+ label,
305
+ path: at,
306
+ message: `"${label}" names the registry query ${b.registry}, which the host no longer runs (R2 maps or retires it)`
307
+ });
308
+ continue;
309
+ }
310
+ if (typeof b.graph_node_id !== "string" || !b.graph_node_id) {
311
+ push({
312
+ severity: "error",
313
+ code: "BINDING_INVALID",
314
+ cellId,
315
+ label,
316
+ path: at,
317
+ message: `"${label}" names no graph_node_id`
318
+ });
319
+ continue;
320
+ }
321
+ if (b.as !== void 0 && b.as !== "value" && b.as !== "record") push({
322
+ severity: "error",
323
+ code: "BINDING_INVALID",
324
+ cellId,
325
+ label,
326
+ path: `${at}.as`,
327
+ message: `"${label}".as is "${String(b.as)}"; expected value or record`
328
+ });
329
+ for (const s of b.state === void 0 ? [] : Array.isArray(b.state) ? b.state : [b.state]) if (!isStateName(s)) push({
330
+ severity: "error",
331
+ code: "STATE_UNKNOWN_NAME",
332
+ cellId,
333
+ label,
334
+ path: `${at}.state`,
335
+ message: `"${label}" names state "${String(s)}"; expected one of ${STATE_NAMES.join(", ")}`
336
+ });
337
+ }
338
+ }
339
+ function checkTraversal(t, path, push, cellId) {
340
+ if (t === void 0) return;
341
+ const w = t;
342
+ if (!Array.isArray(w.edgeTypes) || w.edgeTypes.length === 0) push({
343
+ severity: "error",
344
+ code: "WALK_EDGE_TYPES_REQUIRED",
345
+ cellId,
346
+ path: `${path}.edgeTypes`,
347
+ message: "traversal.edgeTypes is required: without it a walk follows every edge type"
348
+ });
349
+ if (w.direction !== void 0 && !DIRECTIONS.includes(String(w.direction))) push({
350
+ severity: "error",
351
+ code: "WALK_BAD_DIRECTION",
352
+ cellId,
353
+ path: `${path}.direction`,
354
+ message: `traversal.direction "${String(w.direction)}" isn't incoming, outgoing or both`
355
+ });
356
+ if (typeof w.maxNodes === "number" && w.maxNodes > 500) push({
357
+ severity: "warning",
358
+ code: "WALK_MAX_NODES_CLAMPED",
359
+ cellId,
360
+ path: `${path}.maxNodes`,
361
+ message: `traversal.maxNodes ${w.maxNodes} is above 500; clamped`
362
+ });
363
+ for (const s of Array.isArray(w.include_state) ? w.include_state : []) if (!isStateName(s)) push({
364
+ severity: "error",
365
+ code: "STATE_UNKNOWN_NAME",
366
+ cellId,
367
+ path: `${path}.include_state`,
368
+ message: `include_state "${String(s)}" isn't a system name`
369
+ });
370
+ }
371
+ function checkStore(cell, path, push) {
372
+ for (const [kind, key] of [
373
+ ["selections", "channel"],
374
+ ["viewState", "key"],
375
+ ["live", "channel"]
376
+ ]) for (const [name, decl] of Object.entries(cell[kind] ?? {})) {
377
+ const at = `${path}.${kind}.${name}`;
378
+ if (typeof decl?.[key] !== "string" || !decl[key]) push({
379
+ severity: "error",
380
+ code: "STORE_INVALID",
381
+ cellId: cell.id,
382
+ path: at,
383
+ message: `${kind}.${name} names no ${key}`
384
+ });
385
+ if (kind !== "live" && !SCOPES.includes(String(decl?.scope))) push({
386
+ severity: "error",
387
+ code: "STORE_INVALID",
388
+ cellId: cell.id,
389
+ path: `${at}.scope`,
390
+ message: `${kind}.${name}.scope must be page or project`
391
+ });
392
+ }
393
+ }
394
+ function validateCell(cell, catalog, path = "cell", ids) {
395
+ const out = [];
396
+ const push = (d) => out.push(d);
397
+ if (typeof cell?.id !== "string" || !cell.id) {
398
+ push({
399
+ severity: "error",
400
+ code: "CELL_NO_ID",
401
+ path,
402
+ message: "a cell needs an id"
403
+ });
404
+ return out;
405
+ }
406
+ const cellId = cell.id;
407
+ const ref = cell.ref;
408
+ if (ref !== void 0 && (typeof ref?.graph_node_id !== "string" || !ref.graph_node_id)) push({
409
+ severity: "error",
410
+ code: "REF_INVALID",
411
+ cellId,
412
+ path: `${path}.ref`,
413
+ message: "ref names no graph_node_id"
414
+ });
415
+ const nodeManaged = Boolean(ref?.graph_node_id) || Boolean(cell.graph_node_id) && !cell.component;
416
+ if (!ref && cell.graph_node_id && !cell.component) push({
417
+ severity: "legacy",
418
+ code: "LEGACY_THIN_REF",
419
+ cellId,
420
+ path,
421
+ message: "a thin ref `{ id, graph_node_id }`: publish it as `{ id, ref: { graph_node_id } }`"
422
+ });
423
+ else if (!ref && cell.graph_node_id) push({
424
+ severity: "legacy",
425
+ code: "LEGACY_INLINE_NODE",
426
+ cellId,
427
+ path,
428
+ message: "an inline cell with a graph_node_id: its node only starts its walk. Give it a `ref` if the node should manage the cell"
429
+ });
430
+ if (!nodeManaged && !cell.component) push({
431
+ severity: "error",
432
+ code: "CELL_NO_COMPONENT",
433
+ cellId,
434
+ path,
435
+ message: "an inline cell needs a component"
436
+ });
437
+ if (cell.component && catalog && !cell.component.startsWith("@core/")) {
438
+ const entry = catalog.components[cell.component];
439
+ if (!entry) push({
440
+ severity: nodeManaged ? "warning" : "error",
441
+ code: "CELL_UNKNOWN_COMPONENT",
442
+ cellId,
443
+ path: `${path}.component`,
444
+ message: `no bundle in scope exports ${cell.component}`
445
+ });
446
+ else for (const prop of entry.props_schema?.required ?? []) if ((prop === "children" ? cell.props?.children ?? (cell.children?.length ? cell.children : void 0) : cell.props?.[prop]) === void 0) push({
447
+ severity: nodeManaged ? "warning" : "error",
448
+ code: "PROP_REQUIRED",
449
+ cellId,
450
+ path: `${path}.props.${prop}`,
451
+ message: `${cell.component} requires ${prop}`
452
+ });
453
+ }
454
+ for (const child of cell.children ?? []) if (child === cellId) push({
455
+ severity: "error",
456
+ code: "CELL_CHILD_CYCLE",
457
+ cellId,
458
+ path: `${path}.children`,
459
+ message: "a cell lists itself as a child"
460
+ });
461
+ else if (ids && !ids.has(child)) push({
462
+ severity: "error",
463
+ code: "CELL_UNKNOWN_CHILD",
464
+ cellId,
465
+ path: `${path}.children`,
466
+ message: `child cell "${child}" is not on the page`
467
+ });
468
+ checkBindings(cell.data?.queries, `${path}.data.queries`, push, cellId);
469
+ checkTraversal(cell.traversal, `${path}.traversal`, push, cellId);
470
+ checkStore(cell, path, push);
471
+ for (const key of [
472
+ "hideWhen",
473
+ "showWhen",
474
+ "stackWhen",
475
+ "truncateWhen"
476
+ ]) {
477
+ const v = cell.layout?.[key];
478
+ if (v !== void 0 && !BUCKETS.includes(v)) push({
479
+ severity: "warning",
480
+ code: "LAYOUT_INVALID",
481
+ cellId,
482
+ path: `${path}.layout.${key}`,
483
+ message: `layout.${key} "${v}" isn't narrow, compact or wide`
484
+ });
485
+ }
486
+ return out;
487
+ }
488
+ function validatePage(page, catalog) {
489
+ const out = [];
490
+ const cells = page.content?.cells;
491
+ if (cells !== void 0 && !Array.isArray(cells)) return [{
492
+ severity: "error",
493
+ code: "PAGE_INVALID",
494
+ path: "content.cells",
495
+ message: "content.cells must be a list"
496
+ }];
497
+ const list = cells ?? [];
498
+ const ids = new Set();
499
+ list.forEach((cell, i) => {
500
+ if (typeof cell?.id === "string" && ids.has(cell.id)) out.push({
501
+ severity: "error",
502
+ code: "CELL_DUPLICATE_ID",
503
+ cellId: cell.id,
504
+ path: `content.cells[${i}].id`,
505
+ message: `two cells share the id "${cell.id}"`
506
+ });
507
+ if (typeof cell?.id === "string") ids.add(cell.id);
508
+ });
509
+ list.forEach((cell, i) => out.push(...validateCell(cell, catalog, `content.cells[${i}]`, ids)));
510
+ checkBindings(page.content?.data?.queries, "content.data.queries", (d) => out.push(d));
511
+ if (usesStructureLayout(page.layout)) for (const issue of structureIssues(page.layout?.structure, list)) out.push({
512
+ severity: "error",
513
+ code: "STRUCTURE_INVALID",
514
+ path: "layout.structure",
515
+ message: issue
516
+ });
517
+ return out;
518
+ }
519
+ //#endregion
520
+ //#region packages/renderer/src/core/load-page.ts
521
+ async function resolveSystems(source, page) {
522
+ const systems = page.process_id ? systemsFromProcess(await source.processSystems(page.process_id)) : {};
523
+ if (!systems.eng_data) {
524
+ const meta = page.metadata ?? {};
525
+ const fallback = meta.pageProcess?.eng_data_system_id ?? meta.systemGraph?.system_id;
526
+ if (fallback) systems.eng_data = fallback;
527
+ }
528
+ return systems;
529
+ }
530
+ async function resolveDataSystem(source, page) {
531
+ return (await resolveSystems(source, page)).eng_data ?? null;
532
+ }
533
+ async function loadPageData(page, source) {
534
+ const diagnostics = validatePage(page);
535
+ const systems = await resolveSystems(source, page);
536
+ const engDataSystemId = systems.eng_data ?? null;
537
+ const entries = (page.content?.cells ?? []).map((c) => normalizeCell(c).cell);
538
+ const refsBySystem = new Map();
539
+ for (const c of entries) {
540
+ if (!hasRef(c)) continue;
541
+ const system = c.ref.system_id ?? engDataSystemId;
542
+ if (system) refsBySystem.set(system, [...refsBySystem.get(system) ?? [], c.ref.graph_node_id]);
543
+ }
544
+ const configs = new Map();
545
+ await Promise.all([...refsBySystem].map(async ([system, ids]) => {
546
+ const found = await source.cellConfigs(system, ids);
547
+ for (const [id, config] of Object.entries(found)) configs.set(`${system}/${id}`, config);
548
+ }));
549
+ const cells = entries.map((c) => {
550
+ if (!hasRef(c)) return resolveCell(c, null);
551
+ const system = c.ref.system_id ?? engDataSystemId;
552
+ const resolved = resolveCell(c, system ? configs.get(`${system}/${c.ref.graph_node_id}`) : null);
553
+ if (resolved.source === "fallback") diagnostics.push({
554
+ severity: "warning",
555
+ code: "REF_FALLBACK",
556
+ cellId: c.id,
557
+ message: `the node ${c.ref.graph_node_id} has no page_cell_config the viewer can read; the cell renders its own copy`
558
+ });
559
+ return resolved;
560
+ });
561
+ const unresolved = cells.filter((c) => c.source === "pending").map((c) => c.id);
562
+ const declared = collectDeclarations(page, systems, diagnostics);
563
+ const explicit = new Set(Object.keys(declared));
564
+ const cellDecls = new Map(cells.filter((c) => c.component).map((c) => [c.id, resolveDeclarations(c.data?.queries, systems, diagnostics, c.id)]));
565
+ const walking = [];
566
+ for (const cell of cells) {
567
+ if (!cell.traversal || !cell.component) continue;
568
+ const start = resolveWalkStart(cell, page.graph_node_id);
569
+ if (!start) {
570
+ diagnostics.push({
571
+ severity: "warning",
572
+ code: "WALK_NO_START",
573
+ cellId: cell.id,
574
+ message: "the cell has a traversal but neither it nor the page has a graph node to start from"
575
+ });
576
+ continue;
577
+ }
578
+ if (!needsWalk(cell, explicit)) continue;
579
+ const { request, diagnostics: found } = walkRequest(cell.traversal, start, systems);
580
+ diagnostics.push(...found.map((d) => ({
581
+ ...d,
582
+ cellId: cell.id
583
+ })));
584
+ if (request) walking.push({
585
+ cell,
586
+ request
587
+ });
588
+ }
589
+ const requests = new Map(walking.map(({ request }) => [walkKey(request), request]));
590
+ const bySystem = new Map();
591
+ const recordIds = new Set();
592
+ const recordSystems = new Set();
593
+ const allDecls = [declared, ...cellDecls.values()];
594
+ for (const decls of allDecls) for (const d of Object.values(decls)) {
595
+ if (d.as === "record") {
596
+ recordIds.add(d.graph_node_id);
597
+ for (const r of d.reads) recordSystems.add(r.systemId);
598
+ continue;
599
+ }
600
+ for (const r of d.reads) bySystem.set(r.systemId, (bySystem.get(r.systemId) ?? new Set()).add(d.graph_node_id));
601
+ }
602
+ const states = new Map();
603
+ const walks = new Map();
604
+ let records = {};
605
+ await Promise.all([
606
+ ...[...bySystem].map(async ([systemId, ids]) => {
607
+ const found = await source.latestStates(systemId, [...ids]);
608
+ for (const [nodeId, state] of Object.entries(found)) states.set(`${systemId}/${nodeId}`, state);
609
+ }),
610
+ ...[...requests].map(async ([key, request]) => {
611
+ try {
612
+ walks.set(key, await source.walk(request));
613
+ } catch (e) {
614
+ diagnostics.push({
615
+ severity: "error",
616
+ code: "WALK_FAILED",
617
+ message: `a walk from ${request.start.graphNodeId} failed: ${e.message}`
618
+ });
619
+ }
620
+ }),
621
+ recordIds.size ? (async () => {
622
+ records = recordsByName(await source.nodeRecords([...recordIds], [...recordSystems]), allDecls);
623
+ })() : null
624
+ ]);
625
+ const cellData = {};
626
+ const entry = (id) => cellData[id] ??= {
627
+ static: {},
628
+ declared: {}
629
+ };
630
+ for (const cell of cells) {
631
+ const decls = cellDecls.get(cell.id);
632
+ if (decls && Object.keys(decls).length) entry(cell.id).declared = buildDataContext({
633
+ static: {},
634
+ declared: decls,
635
+ states,
636
+ records
637
+ });
638
+ if (cell.data?.static && Object.keys(cell.data.static).length) entry(cell.id).static = buildDataContext({
639
+ static: cell.data.static,
640
+ declared: {},
641
+ states
642
+ });
643
+ }
644
+ for (const { cell, request } of walking) {
645
+ const raw = walks.get(walkKey(request));
646
+ if (!raw) continue;
647
+ const { traversal, bindings, diagnostics: found } = readWalk(raw, request);
648
+ diagnostics.push(...found.map((d) => ({
649
+ ...d,
650
+ cellId: cell.id
651
+ })));
652
+ const names = request.states.map((s) => s.name);
653
+ const labels = {};
654
+ for (const b of bindings) {
655
+ const node = traversal.nodes.find((n) => n.graph_node_id === b.graph_node_id);
656
+ labels[b.label] = settled((node && stateValue(node.state, names, b.field)) ?? null);
657
+ }
658
+ entry(cell.id).walk = {
659
+ traversal,
660
+ labels
661
+ };
662
+ }
663
+ return {
664
+ page,
665
+ cells,
666
+ unresolved,
667
+ data: buildDataContext({
668
+ static: collectStatic(page),
669
+ declared,
670
+ states,
671
+ records
672
+ }),
673
+ declared: [...explicit],
674
+ cellData,
675
+ systems,
676
+ engDataSystemId,
677
+ diagnostics: dedupe(diagnostics)
678
+ };
679
+ }
680
+ function dedupe(list) {
681
+ const seen = new Set();
682
+ return list.filter((d) => {
683
+ const key = `${d.code}|${d.cellId ?? ""}|${d.label ?? ""}`;
684
+ if (seen.has(key)) return false;
685
+ seen.add(key);
686
+ return true;
687
+ });
688
+ }
689
+ function recordsByName(found, declarations) {
690
+ const out = {};
691
+ for (const d of declarations.flatMap((decls) => Object.values(decls))) {
692
+ const row = found[d.graph_node_id];
693
+ if (d.as !== "record" || !row) continue;
694
+ const { stateBySystem, ...rest } = row;
695
+ const state = { ...out[d.graph_node_id]?.state };
696
+ for (const r of d.reads) if (r.name) state[r.name] = stateBySystem[r.systemId] ?? null;
697
+ out[d.graph_node_id] = {
698
+ ...rest,
699
+ state
700
+ };
701
+ }
702
+ return out;
703
+ }
704
+ //#endregion
705
+ //#region packages/renderer/src/core/store/persistence.ts
706
+ var KEY_PREFIX = "stack.store.";
707
+ function storageFor(mode) {
708
+ if (typeof window === "undefined" || mode === "none") return null;
709
+ try {
710
+ return mode === "session" ? window.sessionStorage : window.localStorage;
711
+ } catch {
712
+ return null;
713
+ }
714
+ }
715
+ function loadScope(scope, mode) {
716
+ const store = storageFor(mode);
717
+ if (!store) return null;
718
+ try {
719
+ const raw = store.getItem(KEY_PREFIX + scope);
720
+ return raw ? JSON.parse(raw) : null;
721
+ } catch {
722
+ return null;
723
+ }
724
+ }
725
+ function saveScope(scope, slice, mode) {
726
+ const store = storageFor(mode);
727
+ if (!store) return;
728
+ try {
729
+ store.setItem(KEY_PREFIX + scope, JSON.stringify(slice));
730
+ } catch {}
731
+ }
732
+ function clearScope(scope, mode) {
733
+ try {
734
+ storageFor(mode)?.removeItem(KEY_PREFIX + scope);
735
+ } catch {}
736
+ }
737
+ //#endregion
738
+ export { CONTENT_FIELDS, HYDRATED_CELL_FIELDS, MAIN, PAGE_DOCUMENT, PUBLIC, SECTION_KINDS, STORE_FIELDS, _clearBundleCache, assembleCellProps, attachSelectionSetters, attachViewStateSetters, buildDataContext, bundleScope, cellAttributes, cellPlacementStyle, cellResolutionContext, clearScope, collectDeclarations, collectScopeIds, collectStatic, createStackStore, deriveElementReferences, deriveSectionNumbering, deriveSectionReferences, hasRef, isComponentExport, loadBundle, loadBundles, loadPageData, loadScope, mergeSectionReferences, mergeStates, nearestThemeProvider, needsWalk, nodePrintStyle, normalizeCell, normalizeRef, normalizeStructure, numberingFor, pageBundleRefs, pageBundles, pageContainerStyle, pageScopeKey, passesConditions, projectCellData, projectLive, projectModule, projectScopeKey, projectSelections, projectViewState, readField, readWalk, refKey, referencedLabels, referencesFor, resolveCell, resolveComponent, resolveDataSystem, resolveDeclarations, resolveReferences, resolveScopeId, resolveSystems, resolveWalkStart, rootCells, runtimeReady, saveScope, sectionAnchor, settled, shallowEqualBundle, stateNames, stateValue, structureIssues, systemsFromProcess, usesStructureLayout, validateCell, validatePage, walkKey, walkRequest };
739
+
740
+ //# sourceMappingURL=core.js.map