@peripheries/renderer 0.22.0 → 0.23.1

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 (103) hide show
  1. package/README.md +125 -23
  2. package/dist/catalog.js +2 -6
  3. package/dist/chunks/forms-DxA9WzCt.js +336 -0
  4. package/dist/chunks/{load-CjPvISzH.js → load-b38vUiZM.js} +0 -4
  5. package/dist/chunks/{page-bundles-DLRIBP8g.js → page-bundles-CPj_vx-J.js} +1 -5
  6. package/dist/chunks/{persistence-DCMakM9H.js → persistence-hurkUUQ0.js} +0 -6
  7. package/dist/chunks/{projection-yvYKzMOS.js → projection-zhu32_KJ.js} +1 -15
  8. package/dist/chunks/{query-BPuCJC1C.js → query-D6xQbTFK.js} +166 -12
  9. package/dist/chunks/{refs-BLUokra5.js → refs-Csgh6rXq.js} +0 -4
  10. package/dist/chunks/{structure-o9pYoDjD.js → structure-CKBtQHJq.js} +1 -7
  11. package/dist/chunks/{validate-4LgH8QMJ.js → validate-BcB_PA1k.js} +20 -13
  12. package/dist/core.js +138 -47
  13. package/dist/forms.js +2 -0
  14. package/dist/print.js +0 -24
  15. package/dist/react.js +352 -22
  16. package/dist/runtime.js +2 -12
  17. package/dist/styles.css +9 -15
  18. package/dist/testing.js +214 -19
  19. package/dist/theme.js +0 -10
  20. package/dist/types/catalog/check.d.ts +0 -13
  21. package/dist/types/core/actions.d.ts +0 -24
  22. package/dist/types/core/bundles/load.d.ts +0 -23
  23. package/dist/types/core/bundles/page-bundles.d.ts +0 -9
  24. package/dist/types/core/bundles/refs.d.ts +1 -28
  25. package/dist/types/core/bundles/resolve.d.ts +0 -10
  26. package/dist/types/core/cell-props.d.ts +0 -20
  27. package/dist/types/core/cells.d.ts +0 -21
  28. package/dist/types/core/data-context.d.ts +4 -21
  29. package/dist/types/core/diagnostics.d.ts +20 -6
  30. package/dist/types/core/graph.d.ts +6 -44
  31. package/dist/types/core/index.d.ts +4 -3
  32. package/dist/types/core/layout.d.ts +0 -9
  33. package/dist/types/core/load-page.d.ts +30 -88
  34. package/dist/types/core/query.d.ts +6 -52
  35. package/dist/types/core/references.d.ts +0 -21
  36. package/dist/types/core/release.d.ts +11 -0
  37. package/dist/types/core/rules.d.ts +0 -1
  38. package/dist/types/core/selected.d.ts +0 -5
  39. package/dist/types/core/sources.d.ts +10 -63
  40. package/dist/types/core/store/create-store.d.ts +0 -6
  41. package/dist/types/core/store/persistence.d.ts +0 -1
  42. package/dist/types/core/store/projection.d.ts +0 -5
  43. package/dist/types/core/store/scope-keys.d.ts +0 -1
  44. package/dist/types/core/store/types.d.ts +0 -6
  45. package/dist/types/core/structure.d.ts +0 -33
  46. package/dist/types/core/types.d.ts +2 -13
  47. package/dist/types/core/validate.d.ts +0 -12
  48. package/dist/types/core/values.d.ts +0 -6
  49. package/dist/types/forms/conditions.d.ts +18 -0
  50. package/dist/types/forms/index.d.ts +3 -0
  51. package/dist/types/forms/model.d.ts +90 -0
  52. package/dist/types/forms/port.d.ts +47 -0
  53. package/dist/types/print/document-bindings.d.ts +0 -61
  54. package/dist/types/print/document-profile.d.ts +0 -137
  55. package/dist/types/print/map-staleness.d.ts +0 -66
  56. package/dist/types/print/page-map.d.ts +1 -334
  57. package/dist/types/print/paginate.d.ts +0 -190
  58. package/dist/types/print/print-layout.d.ts +0 -161
  59. package/dist/types/print/print-lint.d.ts +0 -109
  60. package/dist/types/print/print-policy.d.ts +0 -184
  61. package/dist/types/print/print-stylesheet.d.ts +0 -135
  62. package/dist/types/print/running-header.d.ts +0 -86
  63. package/dist/types/print/structure-print.d.ts +1 -243
  64. package/dist/types/react/cell-states.d.ts +0 -2
  65. package/dist/types/react/cell-view.d.ts +1 -15
  66. package/dist/types/react/index.d.ts +1 -0
  67. package/dist/types/react/page-renderer.d.ts +1 -33
  68. package/dist/types/react/structure-view.d.ts +0 -12
  69. package/dist/types/react/use-cell-store.d.ts +0 -5
  70. package/dist/types/react/use-container-size.d.ts +0 -5
  71. package/dist/types/react/use-forms-runtime.d.ts +147 -0
  72. package/dist/types/react/use-paging.d.ts +0 -9
  73. package/dist/types/runtime/boot.d.ts +0 -11
  74. package/dist/types/runtime/install.d.ts +0 -15
  75. package/dist/types/runtime/shims.d.ts +0 -8
  76. package/dist/types/runtime/store-export.d.ts +0 -7
  77. package/dist/types/testing/base-props.d.ts +0 -1
  78. package/dist/types/testing/components.d.ts +0 -8
  79. package/dist/types/testing/form-workbook.d.ts +103 -0
  80. package/dist/types/testing/golden.d.ts +0 -16
  81. package/dist/types/testing/ids.d.ts +0 -2
  82. package/dist/types/testing/index.d.ts +1 -0
  83. package/dist/types/testing/sources.d.ts +0 -7
  84. package/dist/types/theme/check.d.ts +0 -2
  85. package/dist/types/theme/color.d.ts +0 -12
  86. package/dist/types/theme/derive.d.ts +0 -12
  87. package/dist/types/theme/parse.d.ts +0 -7
  88. package/package.json +6 -2
  89. package/dist/catalog.js.map +0 -1
  90. package/dist/chunks/load-CjPvISzH.js.map +0 -1
  91. package/dist/chunks/page-bundles-DLRIBP8g.js.map +0 -1
  92. package/dist/chunks/persistence-DCMakM9H.js.map +0 -1
  93. package/dist/chunks/projection-yvYKzMOS.js.map +0 -1
  94. package/dist/chunks/query-BPuCJC1C.js.map +0 -1
  95. package/dist/chunks/refs-BLUokra5.js.map +0 -1
  96. package/dist/chunks/structure-o9pYoDjD.js.map +0 -1
  97. package/dist/chunks/validate-4LgH8QMJ.js.map +0 -1
  98. package/dist/core.js.map +0 -1
  99. package/dist/print.js.map +0 -1
  100. package/dist/react.js.map +0 -1
  101. package/dist/runtime.js.map +0 -1
  102. package/dist/testing.js.map +0 -1
  103. package/dist/theme.js.map +0 -1
package/README.md CHANGED
@@ -1,18 +1,18 @@
1
1
  # @peripheries/renderer
2
2
 
3
- The one renderer for pages built from federated component bundles
4
- ([design/renderer.md](../../docs/plan/design/renderer.md)). The host, the
5
- component repos' harness and the agency's preview all render through it, so a
6
- page looks the same in each.
3
+ The one renderer for pages built from federated component bundles. Hosts,
4
+ component repos' harnesses and previews all render through it, so a page looks
5
+ the same in each.
7
6
 
8
7
  | Entry | Holds |
9
8
  |---|---|
10
9
  | `@peripheries/renderer/core` | Framework-free: cells and thin-ref hydration, references and conditions, the data context, walks and state names, cell props, layout, document structure, bundle refs and resolution, the shared store, `loadPageData(page, source)`, validation, `DIAGNOSTICS` and `PAGE_RULES` |
11
- | `@peripheries/renderer/catalog` | `checkCatalog`: what a component repo runs on the knowledge it emits (design/catalog.md §5). The only entry with a JSON Schema validator (ajv) |
12
- | `@peripheries/renderer/react` | `PageRenderer`, `CellView`, `StructureView`, the cell states, `useContainerSize` |
10
+ | `@peripheries/renderer/catalog` | `checkCatalog`: what a component repo runs on the knowledge it emits. The only entry with a JSON Schema validator (ajv) |
11
+ | `@peripheries/renderer/react` | `PageRenderer`, `CellView`, `StructureView`, the cell states, `useContainerSize`, `useFormsRuntime` and its `FormSlotProps` and `FormAnswer` |
13
12
  | `@peripheries/renderer/runtime` | What a page boots with: the import map and boot script, the shims, `installRuntime`, the store bundles import |
14
- | `@peripheries/renderer/theme` | The org theme pipeline (design/core-and-tokens.md §6.2): `parseTheme` (version 1 or 2 in, version 2 out, with diagnostics), `themeTokens` (semantic overrides derived in OKLCH), `themeCss` (the `theme` layer), `checkTheme` (WCAG contrast) |
13
+ | `@peripheries/renderer/theme` | The org theme pipeline: `parseTheme` (version 1 or 2 in, version 2 out, with diagnostics), `themeTokens` (semantic overrides derived in OKLCH), `themeCss` (the `theme` layer), `checkTheme` (WCAG contrast) |
15
14
  | `@peripheries/renderer/print` | The print pipeline, framework-free: `collectBlocks` (the page map), `paginate`, `buildPrintLayout` (the packer's decision as CSS), `buildPageStylesheet` (`@page` geometry and margin boxes), running headers, `@printPolicy` parsing, print lint, `resolveDocumentProfile`, region bindings, map staleness, and the `layout.structure[].print` writer |
15
+ | `@peripheries/renderer/forms` | The forms runtime, framework-free: the steps overlay and its model, conditions, modes, navigation, progress, deep links, and the persistence port (`FormsPort`); the React hook is `useFormsRuntime` in `/react` |
16
16
  | `@peripheries/renderer/testing` | The golden pages, synthetic fixtures, and sources that serve them |
17
17
  | `@peripheries/renderer/styles.css` | Page and cell containers, the responsive buckets, placeholders and notices, in the `renderer` layer |
18
18
 
@@ -32,9 +32,18 @@ const runtime = await loadPageData(page, source, { params, baseProps, viewer, co
32
32
 
33
33
  `RendererEnvironment` takes, besides `core`, `navigate` and `store`:
34
34
 
35
- - `actionsFor(cell)`: the actions a cell gets. The host has none until R2's
36
- write rule; `projectCellActions(cell.actions, pageActions)` applies the PWA's
37
- lookup rule (binding key, then registry key, then the cell's own name).
35
+ - `actionsFor(cell)`: the actions a cell gets;
36
+ `projectCellActions(cell.actions, pageActions)` applies the lookup rule
37
+ (binding key, then registry key, then the cell's own name).
38
+ - `runAction`: runs a cell's declared action (the host's write path, or a
39
+ harness's mock); without it a declared action reports `FAILED`.
40
+ - `loadPage`: loads a paged label's next page.
41
+ - `onPropSources(cell, sources)`: each rendered cell's props, leaf by leaf,
42
+ with where each value came from (for an edit layer, with `editTarget`).
43
+ - `retry()`: reads the page again, for a failed cell's "Try again".
44
+ - `cellProps(cell)`: props from the page's runtime, merged last; on a form
45
+ page, `useFormsRuntime`'s `cellProps` gives each cell `form`, `answers` and
46
+ `answer`.
38
47
  - `onCellError(cellId, error)`: a component threw. The cell shows an error
39
48
  notice either way.
40
49
  - `onUnresolvedReference(cellId, ref)`: a prop's `@ref` resolved to nothing
@@ -52,8 +61,7 @@ sharing the channel reads it.
52
61
  ## The names a bundle may rely on
53
62
 
54
63
  What the renderer writes, for a bundle's CSS and checks. These are the only
55
- names: 0.8.0 dropped the old client-prefixed twins and container names
56
- (design/core-and-tokens.md §14).
64
+ names: 0.8.0 dropped the old client-prefixed twins and container names.
57
65
 
58
66
  | Where | Name |
59
67
  |---|---|
@@ -69,7 +77,102 @@ bundle's `ThemeProvider` export.
69
77
 
70
78
  ## Versions
71
79
 
72
- - **0.10.0** (data-render M2): actions run, documents edit, pages embed.
80
+ - **0.23.1** (2026-10-15): the published files carry no comments and no
81
+ source maps (declarations included); this README is the documentation.
82
+ No API change.
83
+
84
+ - **0.23.0** (2026-10-15): the
85
+ versions and forms release, in one minor.
86
+ - **Main only:** `prepareDocument` reads
87
+ main systems and placements only, adding `version: { is: NULL }` to
88
+ `systemCollection`, `graph_nodeCollection` and `graph_edgeCollection`
89
+ at any depth. New diagnostics: `SYSTEM_NOT_MAIN` (a warning: a page
90
+ names a system that isn't main, read as absent; `GraphSource.mainSystems`
91
+ says which are), `DESCRIPTOR_VERSION_FILTER` (a document filters on
92
+ `version` itself) and `DESCRIPTOR_STATE_UNSCOPED` (a document reads
93
+ states by node or unfiltered, so a branch's would be read too); both are
94
+ refused before a document is sent.
95
+ - **The version context:** `PageScope.version` (`VersionContext {
96
+ versionId, versionKind }`, from `@peripheries/runtime`) reaches every
97
+ `GraphSource` read as an optional trailing `ReadContext`, and
98
+ `@context.version` / `@context.versionKind` name it to a query. At main
99
+ the reads are 0.22's. The runtime freezes the read-side types
100
+ (`ResolvedVersionContext`, `StateAsOf`, `NodeStateView`, `Via`,
101
+ `ReleaseProblem`, `RecordedEmbed`).
102
+ - **Edits at a version:** a release or a channel refuses an edit before
103
+ it is sent (`VERSION_IS_RELEASE`); a branch's edit carries
104
+ `p_version_id`.
105
+ - **Embeds and print at a version:** `PageScope.recordedEmbeds` (a
106
+ release's `metadata.embeds`) makes an embed render the release its host
107
+ recorded, else the viewer's context; `PageRuntime.embedVersion` says
108
+ which (`recorded` | `context`), and `PageRuntime.readVersionIds` the
109
+ versions the states were read from, for print's and an embed's "Release
110
+ N". A state row may carry `versionId`, `unversioned` and `outOfScope`.
111
+ - **Render-time functions:** `RENDER_FUNCTIONS` adds the version reads
112
+ (`node_states_as_of`, `node_state_view`, `version_context_for`,
113
+ `version_scope_members`, `versions_for`, `version_diff`,
114
+ `auto_release_status`), each with the tables it reads for
115
+ invalidation.
116
+ - **Declared ahead:** `form_answers(p_structure_nodes: [UUID!]!)` is a
117
+ render function now, listed in `RENDER_FUNCTIONS_AHEAD` until the
118
+ database has it, so descriptors can read a workbook's answers without a
119
+ renderer release. Its JSON is a leaf:
120
+ `result.path` reads into it and nothing in it is flattened. An alias on
121
+ a render function's JSON root is refused (`DESCRIPTOR_ALIAS_JSON_ROOT`),
122
+ since its JSON is decoded by field name.
123
+ - **Systems from the graph:** `GraphSource.graphSystems(processId)` (the
124
+ main, keyless systems on the process's graphs, with each graph's type
125
+ and layer's role) is how `resolveSystems` finds a page's systems;
126
+ `processSystems` is optional and read only without it, and
127
+ `metadata.pageProcess` is no longer read. A state name is `<tier>_<role>`
128
+ for any registered layer's role (`eng_answer`), not only the four;
129
+ whether the page's process has it is `STATE_UNKNOWN_SYSTEM` at load.
130
+ - **Releases:** `validateManifest(pages)` returns `ReleaseProblem[]`,
131
+ advisory before a release; `manifestPageFrom(graphNodeId, data)` reads a
132
+ page artifact's state data as a manifest page. The runtime adds `PageView` and
133
+ `SubmissionData`.
134
+ - **The forms runtime** (`@peripheries/renderer/forms`): the steps
135
+ overlay, modes, navigation, saving, progress and deep links behind
136
+ `FormsPort { save, submit, visit, reopen }` (and, optional, `upload` and
137
+ `openFile` for file answers), with `useFormsRuntime` and `FormSlotProps`
138
+ in `/react`. Refusals are codes, never throws (`FormsRefusal`:
139
+ `CONFLICT`, `ANSWER_*`, `FORM_INCOMPLETE`, `FORM_NOT_OPEN`,
140
+ `FORM_COMPLETED`, `FORM_STEP_UNKNOWN`, `NO_CAPABILITY`, `NETWORK`).
141
+ `reopen` is the "Edit answers" slot on a submitted form. A phone opens
142
+ in question mode; the slot props carry the question shown (`question`,
143
+ its place and the next), `goToQuestion`, `progress.total` beside the
144
+ required count, a step's authored `label`, and stay one object while
145
+ what they show is unchanged. A step with only optional questions is
146
+ complete once seen. The slot props name every chapter, the submission's
147
+ `status`, `readOnly`, and the newest staff `review` (its note, status,
148
+ time and writer). The runtime adds `FileAnswer` and `AnswerValue`;
149
+ `SubmissionData` carries `reopens` and each note's `status`.
150
+ - **Form cells:** on a form page each cell gets, through
151
+ `RendererEnvironment.cellProps` (useFormsRuntime's `cellProps`), `form`
152
+ (the slot props), `answers` (its questions the conditions show, by key)
153
+ and, with one question, `answer`: a `FormAnswer { key, label, question,
154
+ value, change, readOnly, required, missing, refusal, inputId, upload?,
155
+ openFile? }`, in the slot props' words. `question` is the question's
156
+ definition (`FormQuestion.definition`); `inputId` is where
157
+ `goToQuestion` moves focus. In question mode `FormSlotProps.question`
158
+ carries the same `answer`.
159
+ - **For consumers** (nothing is required to keep 0.22's behaviour):
160
+ - a `GraphSource` adds `graphSystems(processId)` to find systems by
161
+ graph (else `processSystems` is read), `mainSystems(ids)` to read
162
+ main only (else every system is read), and takes the trailing
163
+ `ReadContext` to read at a version (else it reads main);
164
+ - a host passes `PageScope.version` (and `recordedEmbeds` at a release)
165
+ to read at a version, and refuses nothing else itself;
166
+ - a form host implements `FormsPort` (`upload` and `openFile` only if
167
+ it takes files) and passes `cellProps` from `useFormsRuntime` as
168
+ `RendererEnvironment.cellProps`; `review.by` is a display name or
169
+ null, never an email or a user id;
170
+ - a component reads `form`, `answers` and `answer` from its props on a
171
+ form page, and puts `answer.inputId` on its control.
172
+ - page ids (an embed's `page_id`, `PageScope`'s page) become anchor
173
+ ids; the renderer treats them as opaque, so nothing changes.
174
+
175
+ - **0.10.0:** actions run, documents edit, pages embed.
73
176
  - Declared actions run through the environment's `runAction`; the runtime's
74
177
  `ACTION_FIELDS` takes `subjects` and `audience` on `create`, `edit` and
75
178
  `append`, and `create` needs `edge_type` only under a parent.
@@ -85,9 +188,8 @@ bundle's `ThemeProvider` export.
85
188
  - A nullable query variable whose whole reference reads `@params`,
86
189
  `@context` or `@viewer` and resolves to nothing is sent as `null`.
87
190
  - PAGE_RULES is shorter (3780 characters) and describes embed cells.
88
- - **0.9.0** (data-render M1; prerelease `0.9.0-data-render.0`): queries
89
- (design/data.md §2–§6), realtime's inputs, the host contract retired, and
90
- the renderer half of actions (§9.2).
191
+ - **0.9.0:** queries, realtime's inputs, the host contract retired, and
192
+ the renderer half of actions.
91
193
  - A label can be `{ descriptor: QueryDescriptor }`, or a node binding to a
92
194
  live `query_data` node, whose descriptor runs instead of returning its
93
195
  state. `GraphSource` gains `query(descriptor, variables)`, which returns
@@ -118,7 +220,7 @@ bundle's `ThemeProvider` export.
118
220
 
119
221
  - **0.8.1:** `BaseProps` carries the org's `locale` and `currency` (from its
120
222
  org template's metadata), which `@stack/charts` formats with.
121
- - **0.8.0:** the old names go (design/core-and-tokens.md §14). The renderer
223
+ - **0.8.0:** the old names go. The renderer
122
224
  writes only the attributes and container names above, the runtime installs
123
225
  only `__STACK_RUNTIME__` and maps only `@stack/store`, and the store has no
124
226
  second hook name. A bundle's `ThemeProvider` is no longer lifted or wrapped
@@ -126,15 +228,15 @@ bundle's `ThemeProvider` export.
126
228
  gone). `@peripheries/runtime` 0.8.0 drops `LEGACY_RUNTIME_GLOBAL` and
127
229
  `SPECIFIER_ALIASES`.
128
230
 
129
- - **0.7.3:** the page container also carries `data-page-grid`, the name
130
- design/bundles.md §6 gives the old page-grid attribute, so every attribute
231
+ - **0.7.3:** the page container also carries `data-page-grid`, the
232
+ canonical name of the old page-grid attribute, so every attribute
131
233
  the renderer writes has its canonical name (above) before the old ones go.
132
234
 
133
235
  - **0.7.2:** the package ships its LICENSE: proprietary to Peripheries Studio
134
236
  Inc., all rights reserved.
135
237
  - **0.7.1:** `bundleScope` puts the advisor org's `main` after the shared
136
238
  lineages, so an advisor org's older copy of a core component never shadows
137
- core on a client org's page (design/core-and-tokens.md §3).
239
+ core on a client org's page.
138
240
  - **0.7.0:** shared lineages and the theme. `bundleScope` appends the
139
241
  platform's shared lineages, and the loader imports them by specifier;
140
242
  `@core/` resolves from `stack/core` (`host:` is retired); `importMapFor` and
@@ -174,7 +276,7 @@ bundle's `ThemeProvider` export.
174
276
  - **0.3.0:** `@peripheries/runtime/rank`, sibling order on `graph_node.rank`
175
277
  (`compareRank`, `sortByRank`, `rankBetween` and the rest), so no one
176
278
  vendors it; the `WALK_NODE_TYPES_EMPTY` warning, because `nodeTypes: []`
177
- filters nothing here where the PWA read it as "enter nothing".
279
+ filters nothing (it doesn't mean "enter nothing").
178
280
  - **0.2.0:** the hooks above, `pageRuntimeFromValues`, action projection,
179
281
  `data-missing-component` on a missing component's notice, the theme passed
180
282
  to `ThemeProvider`, store defaults seeded.
@@ -195,7 +297,7 @@ to fix it; `Diagnostic.code` is typed from that table. Tools show the fix with
195
297
  the diagnostic, so a writer learns a rule when they break it.
196
298
 
197
299
  **`PAGE_RULES`** (from `/core`) is the host's rules for composing a page, the
198
- short text a writer reads first (design/catalog.md §3.3). It is built from the
300
+ short text a writer reads first. It is built from the
199
301
  constants the renderer and validator use, so it changes when they do.
200
302
 
201
303
  `core` imports no React at runtime and never reaches the network, and the
@@ -206,4 +308,4 @@ code; the golden pages run over fixture sources in `react/golden-pages.test.tsx`
206
308
  publishes every package whose version isn't on npm yet, built output only;
207
309
  `.github/workflows/publish-packages.yml` stages each new version from
208
310
  `develop`, and a maintainer approves it on npmjs.com. Bump the version here to
209
- release (design/renderer.md §11).
311
+ release.
package/dist/catalog.js CHANGED
@@ -1,8 +1,7 @@
1
- import { b as isEach } from "./chunks/query-BPuCJC1C.js";
2
- import { i as eachFormProblems, s as validatePage } from "./chunks/validate-4LgH8QMJ.js";
1
+ import { x as isEach } from "./chunks/query-D6xQbTFK.js";
2
+ import { i as eachFormProblems, s as validatePage } from "./chunks/validate-BcB_PA1k.js";
3
3
  import { KNOWLEDGE_BUDGETS, KNOWLEDGE_KEY } from "@peripheries/runtime";
4
4
  import Ajv2020 from "ajv/dist/2020.js";
5
- //#region packages/renderer/src/catalog/check.ts
6
5
  var ref = (kind, key) => `${kind}:${key}`;
7
6
  var size = (v) => JSON.stringify(v ?? null).length;
8
7
  var isReference = (v) => typeof v === "string" && (v.startsWith("@") || v.includes("${"));
@@ -272,7 +271,4 @@ function validateLiteral(schema, value, path, push, ajv) {
272
271
  if (!ajv.validate(schema, value)) push("error", "PROP_INVALID", path, ajv.errorsText(ajv.errors, { dataVar: "" }).replace(/^\s+/, ""));
273
272
  } catch {}
274
273
  }
275
- //#endregion
276
274
  export { checkCatalog, validatePageProps };
277
-
278
- //# sourceMappingURL=catalog.js.map
@@ -0,0 +1,336 @@
1
+ var CONDITION_OPS = [
2
+ "eq",
3
+ "ne",
4
+ "in",
5
+ "not_in",
6
+ "gt",
7
+ "lt",
8
+ "gte",
9
+ "lte",
10
+ "contains",
11
+ "not_contains",
12
+ "empty",
13
+ "not_empty"
14
+ ];
15
+ var OPS = new Set(CONDITION_OPS);
16
+ function isConditionOp(op) {
17
+ return typeof op === "string" && OPS.has(op);
18
+ }
19
+ function isBlank(value) {
20
+ if (value === null || value === void 0) return true;
21
+ if (typeof value === "string") return value.trim() === "";
22
+ if (Array.isArray(value)) return value.length === 0;
23
+ return false;
24
+ }
25
+ function asNumber(value) {
26
+ if (typeof value === "number") return value;
27
+ const n = Number.parseFloat(String(value));
28
+ return Number.isFinite(n) ? n : NaN;
29
+ }
30
+ function looseEquals(a, b) {
31
+ if (a === b) return true;
32
+ if (a === null || a === void 0 || b === null || b === void 0) return false;
33
+ return String(a) === String(b);
34
+ }
35
+ function anyOverlap(actual, expected) {
36
+ const list = Array.isArray(expected) ? expected : [expected];
37
+ if (Array.isArray(actual)) return actual.some((a) => list.some((e) => looseEquals(a, e)));
38
+ return list.some((e) => looseEquals(actual, e));
39
+ }
40
+ function contains(actual, expected) {
41
+ if (Array.isArray(actual)) return actual.some((a) => looseEquals(a, expected));
42
+ return typeof actual === "string" && actual.includes(String(expected));
43
+ }
44
+ function evaluateClause(clause, answers, onProblem) {
45
+ const actual = answers[clause.q];
46
+ const expected = clause.value;
47
+ switch (clause.op) {
48
+ case "eq": return looseEquals(actual, expected);
49
+ case "ne": return !looseEquals(actual, expected);
50
+ case "in": return anyOverlap(actual, expected);
51
+ case "not_in": return !anyOverlap(actual, expected);
52
+ case "gt": return asNumber(actual) > asNumber(expected);
53
+ case "lt": return asNumber(actual) < asNumber(expected);
54
+ case "gte": return asNumber(actual) >= asNumber(expected);
55
+ case "lte": return asNumber(actual) <= asNumber(expected);
56
+ case "contains": return contains(actual, expected);
57
+ case "not_contains": return !contains(actual, expected);
58
+ case "empty": return isBlank(actual);
59
+ case "not_empty": return !isBlank(actual);
60
+ default:
61
+ onProblem?.(`unknown operator "${String(clause.op)}" on "${String(clause.q)}": the target is shown`);
62
+ return true;
63
+ }
64
+ }
65
+ function evaluateCondition(condition, answers, onProblem) {
66
+ if (!condition) return true;
67
+ const clauses = Array.isArray(condition.all) ? condition.all : [];
68
+ if (clauses.length === 0) return true;
69
+ let result;
70
+ try {
71
+ result = clauses.every((c) => evaluateClause(c, answers, onProblem));
72
+ } catch (error) {
73
+ onProblem?.(`evaluation failed, the target is shown: ${error instanceof Error ? error.message : String(error)}`);
74
+ return true;
75
+ }
76
+ return condition.not === true ? !result : result;
77
+ }
78
+ var LEGACY_OPS = {
79
+ eq: "eq",
80
+ equals: "eq",
81
+ equal: "eq",
82
+ ne: "ne",
83
+ neq: "ne",
84
+ notequals: "ne",
85
+ notequal: "ne",
86
+ in: "in",
87
+ notin: "not_in",
88
+ gt: "gt",
89
+ greaterthan: "gt",
90
+ lt: "lt",
91
+ lessthan: "lt",
92
+ gte: "gte",
93
+ greaterthanorequal: "gte",
94
+ greaterthanorequals: "gte",
95
+ greaterthanorequalto: "gte",
96
+ lte: "lte",
97
+ lessthanorequal: "lte",
98
+ lessthanorequals: "lte",
99
+ lessthanorequalto: "lte",
100
+ contains: "contains",
101
+ notcontains: "not_contains",
102
+ empty: "empty",
103
+ isempty: "empty",
104
+ notempty: "not_empty",
105
+ isnotempty: "not_empty"
106
+ };
107
+ function normalizeOperator(op) {
108
+ if (typeof op !== "string") return null;
109
+ return LEGACY_OPS[op.replace(/[_\-\s]/g, "").toLowerCase()] ?? null;
110
+ }
111
+ function toClause(raw, index, problems) {
112
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
113
+ problems.push(`clause ${index}: not an object, dropped`);
114
+ return null;
115
+ }
116
+ const r = raw;
117
+ const q = [
118
+ r.q,
119
+ r.questionId,
120
+ r.when
121
+ ].find((k) => typeof k === "string" && k !== "");
122
+ if (!q) {
123
+ problems.push(`clause ${index}: no question key, dropped`);
124
+ return null;
125
+ }
126
+ const value = "value" in r ? r.value : r.is;
127
+ const rawOp = r.op ?? r.operator;
128
+ if (rawOp === void 0 || rawOp === null || rawOp === "") return {
129
+ q,
130
+ op: "eq",
131
+ value
132
+ };
133
+ const op = normalizeOperator(rawOp);
134
+ if (op) return {
135
+ q,
136
+ op,
137
+ value
138
+ };
139
+ problems.push(`clause ${index} on "${q}": unknown operator "${String(rawOp)}", the target is shown`);
140
+ return {
141
+ q,
142
+ op: String(rawOp),
143
+ value
144
+ };
145
+ }
146
+ function normalizeCondition(legacy) {
147
+ const problems = [];
148
+ const none = () => ({
149
+ condition: null,
150
+ problems
151
+ });
152
+ if (legacy === null || legacy === void 0) return none();
153
+ let parsed = legacy;
154
+ if (typeof legacy === "string") {
155
+ const trimmed = legacy.trim();
156
+ if (trimmed === "") return none();
157
+ try {
158
+ parsed = JSON.parse(trimmed);
159
+ } catch {
160
+ problems.push("not JSON: ungated");
161
+ return none();
162
+ }
163
+ }
164
+ let rawClauses;
165
+ let not = false;
166
+ if (Array.isArray(parsed)) rawClauses = parsed;
167
+ else if (parsed && typeof parsed === "object") {
168
+ const obj = parsed;
169
+ const list = Array.isArray(obj.all) ? obj.all : Array.isArray(obj.conditions) ? obj.conditions : null;
170
+ if (!list) {
171
+ problems.push("no clause list (`all` or `conditions`): ungated");
172
+ return none();
173
+ }
174
+ rawClauses = list;
175
+ not = obj.not === true;
176
+ } else {
177
+ problems.push(`unrecognised condition (${typeof parsed}): ungated`);
178
+ return none();
179
+ }
180
+ const all = rawClauses.map((c, i) => toClause(c, i, problems)).filter((c) => c !== null);
181
+ if (all.length === 0) return none();
182
+ return {
183
+ condition: {
184
+ all,
185
+ not
186
+ },
187
+ problems
188
+ };
189
+ }
190
+ var answered = (v) => v !== void 0 && v !== null && !(typeof v === "string" && v.trim() === "") && !(Array.isArray(v) && v.length === 0);
191
+ function visibleSteps(model, values) {
192
+ const shows = (when) => evaluateCondition(when ?? null, values);
193
+ return model.steps.filter((s) => shows(s.when)).map((s) => ({
194
+ ...s,
195
+ items: s.items.filter((i) => shows(i.when)).map((i) => ({
196
+ ...i,
197
+ questions: i.questions.filter((q) => shows(q.when))
198
+ }))
199
+ })).filter((s) => s.items.length > 0);
200
+ }
201
+ var questionsOf = (step) => step.items.flatMap((i) => i.questions);
202
+ function stepKind(step, isLast) {
203
+ if (isLast && step.items.some((i) => i.kind === "review")) return "submit";
204
+ if (step.items.some((i) => i.kind === "upload")) return "upload";
205
+ if (questionsOf(step).length) return "answer";
206
+ if (step.items.some((i) => i.kind === "review")) return "review";
207
+ return "read";
208
+ }
209
+ function positionsOf(steps, mode) {
210
+ return steps.flatMap((s) => {
211
+ const qs = questionsOf(s);
212
+ return mode === "question" && qs.length ? qs.map((q) => ({
213
+ step: s.id,
214
+ question: q.key
215
+ })) : [{ step: s.id }];
216
+ });
217
+ }
218
+ var samePlace = (a, b) => a.step === b.step && (a.question ?? null) === (b.question ?? null);
219
+ function settle(steps, mode, at) {
220
+ const all = positionsOf(steps, mode);
221
+ if (!all.length) return null;
222
+ if (at) {
223
+ const exact = all.find((p) => samePlace(p, mode === "step" ? { step: at.step } : at));
224
+ if (exact) return exact;
225
+ const inStep = all.find((p) => p.step === at.step);
226
+ if (inStep) return inStep;
227
+ }
228
+ return all[0];
229
+ }
230
+ function move(steps, mode, at, by) {
231
+ const all = positionsOf(steps, mode);
232
+ return all[all.findIndex((p) => samePlace(p, at)) + by] ?? null;
233
+ }
234
+ function goTo(steps, mode, stepId, values) {
235
+ const step = steps.find((s) => s.id === stepId);
236
+ if (!step) return null;
237
+ if (mode === "step") return { step: step.id };
238
+ const qs = questionsOf(step);
239
+ if (!qs.length) return { step: step.id };
240
+ return {
241
+ step: step.id,
242
+ question: (qs.find((q) => !answered(values[q.key])) ?? qs[0]).key
243
+ };
244
+ }
245
+ function goToQuestion(steps, mode, key) {
246
+ const step = steps.find((s) => questionsOf(s).some((q) => q.key === key));
247
+ if (!step) return null;
248
+ return mode === "question" ? {
249
+ step: step.id,
250
+ question: key
251
+ } : { step: step.id };
252
+ }
253
+ function enterAdvances(step, mode, question) {
254
+ const qs = questionsOf(step);
255
+ if (mode === "question") return !!question && !!qs.find((q) => q.key === question)?.singleLine;
256
+ return qs.length === 1 && qs[0].singleLine;
257
+ }
258
+ function missing(steps, values, stepId) {
259
+ return steps.filter((s) => !stepId || s.id === stepId).flatMap(questionsOf).filter((q) => q.required && !answered(values[q.key]));
260
+ }
261
+ function progress(steps, values, visited) {
262
+ const required = steps.flatMap(questionsOf).filter((q) => q.required);
263
+ const done = required.filter((q) => answered(values[q.key])).length;
264
+ const perStep = {};
265
+ steps.forEach((s, i) => {
266
+ const qs = questionsOf(s);
267
+ if (!qs.length) {
268
+ const seen = visited.has(s.id) ? 1 : 0;
269
+ perStep[s.id] = {
270
+ answered: seen,
271
+ total: 1,
272
+ complete: seen === 1 || stepKind(s, i === steps.length - 1) === "submit"
273
+ };
274
+ return;
275
+ }
276
+ const n = qs.filter((q) => answered(values[q.key])).length;
277
+ const required = qs.some((q) => q.required);
278
+ perStep[s.id] = {
279
+ answered: n,
280
+ total: qs.length,
281
+ complete: qs.every((q) => !q.required || answered(values[q.key])) && (required || n > 0 || visited.has(s.id))
282
+ };
283
+ });
284
+ const all = steps.flatMap(questionsOf);
285
+ const total = {
286
+ answered: all.filter((q) => answered(values[q.key])).length,
287
+ count: all.length
288
+ };
289
+ return {
290
+ answered: done,
291
+ required: required.length,
292
+ ratio: required.length ? done / required.length : 1,
293
+ total,
294
+ perStep
295
+ };
296
+ }
297
+ var isReadOnlyStatus = (status) => status === "submitted" || status === "completed";
298
+ var defaultMode = (phone) => phone ? "question" : "step";
299
+ function fromPageSteps(page, itemOf) {
300
+ const layout = page.layout ?? {};
301
+ const steps = Array.isArray(layout.steps) ? layout.steps : [];
302
+ const chapters = Array.isArray(layout.chapters) ? layout.chapters : [];
303
+ const submit = layout.submit ?? {};
304
+ return {
305
+ title: page.name ?? "",
306
+ chapters,
307
+ steps: steps.map((s) => ({
308
+ id: s.id,
309
+ title: s.title ?? s.id,
310
+ ...s.chapter ? { chapter: s.chapter } : {},
311
+ ...typeof s.label === "string" && s.label ? { label: s.label } : {},
312
+ ...s.when ? { when: s.when } : {},
313
+ items: (s.cell_ids ?? []).map(itemOf).filter((i) => i !== null)
314
+ })),
315
+ submitRequired: submit.required === "block" ? "block" : "warn",
316
+ allowBack: layout.allow_back !== false
317
+ };
318
+ }
319
+ function positionFromLink(steps, mode, link) {
320
+ const step = link.section && steps.find((s) => s.id === link.section) || link.page && steps.find((s) => s.chapter === link.page) || steps[0];
321
+ if (!step) return null;
322
+ return settle(steps, mode, {
323
+ step: step.id,
324
+ ...link.question && link.section === step.id ? { question: link.question } : {}
325
+ });
326
+ }
327
+ function linkTo(steps, mode, at) {
328
+ const step = steps.find((s) => s.id === at.step);
329
+ return {
330
+ ...mode === "question" ? { mode: "question" } : {},
331
+ ...step?.chapter ? { page: step.chapter } : {},
332
+ section: at.step,
333
+ ...at.question ? { question: at.question } : {}
334
+ };
335
+ }
336
+ export { CONDITION_OPS as _, goToQuestion as a, normalizeCondition as b, missing as c, positionsOf as d, progress as f, visibleSteps as g, stepKind as h, goTo as i, move as l, settle as m, enterAdvances as n, isReadOnlyStatus as o, questionsOf as p, fromPageSteps as r, linkTo as s, defaultMode as t, positionFromLink as u, evaluateCondition as v, normalizeOperator as x, isConditionOp as y };
@@ -1,5 +1,4 @@
1
1
  import { READY_GLOBAL } from "@peripheries/runtime";
2
- //#region packages/renderer/src/core/bundles/load.ts
3
2
  function isComponentExport(value) {
4
3
  if (typeof value === "function") return true;
5
4
  if (!value || typeof value !== "object") return false;
@@ -66,7 +65,4 @@ async function loadBundles(rows, files) {
66
65
  function _clearBundleCache() {
67
66
  cache.clear();
68
67
  }
69
- //#endregion
70
68
  export { loadBundlesSettled as a, loadBundles as i, isComponentExport as n, projectModule as o, loadBundle as r, runtimeReady as s, _clearBundleCache as t };
71
-
72
- //# sourceMappingURL=load-CjPvISzH.js.map
@@ -1,5 +1,4 @@
1
- import { a as normalizeRef, o as refKey } from "./refs-BLUokra5.js";
2
- //#region packages/renderer/src/core/bundles/page-bundles.ts
1
+ import { a as normalizeRef, o as refKey } from "./refs-Csgh6rXq.js";
3
2
  function pageBundleRefs(scope, cells, pageOrgId) {
4
3
  const out = [...scope];
5
4
  const seen = new Set(out.map(refKey));
@@ -28,7 +27,4 @@ function pageBundles(scope, rows, loaded, pageOrgId, failed = false) {
28
27
  }
29
28
  };
30
29
  }
31
- //#endregion
32
30
  export { pageBundles as n, pageBundleRefs as t };
33
-
34
- //# sourceMappingURL=page-bundles-DLRIBP8g.js.map
@@ -1,5 +1,4 @@
1
1
  import { Store } from "@tanstack/store";
2
- //#region packages/renderer/src/core/store/create-store.ts
3
2
  function createStackStore(initial) {
4
3
  const store = new Store({
5
4
  selections: {},
@@ -89,8 +88,6 @@ function createStackStore(initial) {
89
88
  subscribe: (listener) => store.subscribe(() => listener(getState()))
90
89
  };
91
90
  }
92
- //#endregion
93
- //#region packages/renderer/src/core/store/persistence.ts
94
91
  var KEY_PREFIX = "stack.store.";
95
92
  function storageFor(mode) {
96
93
  if (typeof window === "undefined" || mode === "none") return null;
@@ -134,7 +131,4 @@ function clearAllScopes() {
134
131
  } catch {}
135
132
  }
136
133
  }
137
- //#endregion
138
134
  export { createStackStore as a, saveScope as i, clearScope as n, loadScope as r, clearAllScopes as t };
139
-
140
- //# sourceMappingURL=persistence-DCMakM9H.js.map