@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.
- package/README.md +125 -23
- package/dist/catalog.js +2 -6
- package/dist/chunks/forms-DxA9WzCt.js +336 -0
- package/dist/chunks/{load-CjPvISzH.js → load-b38vUiZM.js} +0 -4
- package/dist/chunks/{page-bundles-DLRIBP8g.js → page-bundles-CPj_vx-J.js} +1 -5
- package/dist/chunks/{persistence-DCMakM9H.js → persistence-hurkUUQ0.js} +0 -6
- package/dist/chunks/{projection-yvYKzMOS.js → projection-zhu32_KJ.js} +1 -15
- package/dist/chunks/{query-BPuCJC1C.js → query-D6xQbTFK.js} +166 -12
- package/dist/chunks/{refs-BLUokra5.js → refs-Csgh6rXq.js} +0 -4
- package/dist/chunks/{structure-o9pYoDjD.js → structure-CKBtQHJq.js} +1 -7
- package/dist/chunks/{validate-4LgH8QMJ.js → validate-BcB_PA1k.js} +20 -13
- package/dist/core.js +138 -47
- package/dist/forms.js +2 -0
- package/dist/print.js +0 -24
- package/dist/react.js +352 -22
- package/dist/runtime.js +2 -12
- package/dist/styles.css +9 -15
- package/dist/testing.js +214 -19
- package/dist/theme.js +0 -10
- package/dist/types/catalog/check.d.ts +0 -13
- package/dist/types/core/actions.d.ts +0 -24
- package/dist/types/core/bundles/load.d.ts +0 -23
- package/dist/types/core/bundles/page-bundles.d.ts +0 -9
- package/dist/types/core/bundles/refs.d.ts +1 -28
- package/dist/types/core/bundles/resolve.d.ts +0 -10
- package/dist/types/core/cell-props.d.ts +0 -20
- package/dist/types/core/cells.d.ts +0 -21
- package/dist/types/core/data-context.d.ts +4 -21
- package/dist/types/core/diagnostics.d.ts +20 -6
- package/dist/types/core/graph.d.ts +6 -44
- package/dist/types/core/index.d.ts +4 -3
- package/dist/types/core/layout.d.ts +0 -9
- package/dist/types/core/load-page.d.ts +30 -88
- package/dist/types/core/query.d.ts +6 -52
- package/dist/types/core/references.d.ts +0 -21
- package/dist/types/core/release.d.ts +11 -0
- package/dist/types/core/rules.d.ts +0 -1
- package/dist/types/core/selected.d.ts +0 -5
- package/dist/types/core/sources.d.ts +10 -63
- package/dist/types/core/store/create-store.d.ts +0 -6
- package/dist/types/core/store/persistence.d.ts +0 -1
- package/dist/types/core/store/projection.d.ts +0 -5
- package/dist/types/core/store/scope-keys.d.ts +0 -1
- package/dist/types/core/store/types.d.ts +0 -6
- package/dist/types/core/structure.d.ts +0 -33
- package/dist/types/core/types.d.ts +2 -13
- package/dist/types/core/validate.d.ts +0 -12
- package/dist/types/core/values.d.ts +0 -6
- package/dist/types/forms/conditions.d.ts +18 -0
- package/dist/types/forms/index.d.ts +3 -0
- package/dist/types/forms/model.d.ts +90 -0
- package/dist/types/forms/port.d.ts +47 -0
- package/dist/types/print/document-bindings.d.ts +0 -61
- package/dist/types/print/document-profile.d.ts +0 -137
- package/dist/types/print/map-staleness.d.ts +0 -66
- package/dist/types/print/page-map.d.ts +1 -334
- package/dist/types/print/paginate.d.ts +0 -190
- package/dist/types/print/print-layout.d.ts +0 -161
- package/dist/types/print/print-lint.d.ts +0 -109
- package/dist/types/print/print-policy.d.ts +0 -184
- package/dist/types/print/print-stylesheet.d.ts +0 -135
- package/dist/types/print/running-header.d.ts +0 -86
- package/dist/types/print/structure-print.d.ts +1 -243
- package/dist/types/react/cell-states.d.ts +0 -2
- package/dist/types/react/cell-view.d.ts +1 -15
- package/dist/types/react/index.d.ts +1 -0
- package/dist/types/react/page-renderer.d.ts +1 -33
- package/dist/types/react/structure-view.d.ts +0 -12
- package/dist/types/react/use-cell-store.d.ts +0 -5
- package/dist/types/react/use-container-size.d.ts +0 -5
- package/dist/types/react/use-forms-runtime.d.ts +147 -0
- package/dist/types/react/use-paging.d.ts +0 -9
- package/dist/types/runtime/boot.d.ts +0 -11
- package/dist/types/runtime/install.d.ts +0 -15
- package/dist/types/runtime/shims.d.ts +0 -8
- package/dist/types/runtime/store-export.d.ts +0 -7
- package/dist/types/testing/base-props.d.ts +0 -1
- package/dist/types/testing/components.d.ts +0 -8
- package/dist/types/testing/form-workbook.d.ts +103 -0
- package/dist/types/testing/golden.d.ts +0 -16
- package/dist/types/testing/ids.d.ts +0 -2
- package/dist/types/testing/index.d.ts +1 -0
- package/dist/types/testing/sources.d.ts +0 -7
- package/dist/types/theme/check.d.ts +0 -2
- package/dist/types/theme/color.d.ts +0 -12
- package/dist/types/theme/derive.d.ts +0 -12
- package/dist/types/theme/parse.d.ts +0 -7
- package/package.json +6 -2
- package/dist/catalog.js.map +0 -1
- package/dist/chunks/load-CjPvISzH.js.map +0 -1
- package/dist/chunks/page-bundles-DLRIBP8g.js.map +0 -1
- package/dist/chunks/persistence-DCMakM9H.js.map +0 -1
- package/dist/chunks/projection-yvYKzMOS.js.map +0 -1
- package/dist/chunks/query-BPuCJC1C.js.map +0 -1
- package/dist/chunks/refs-BLUokra5.js.map +0 -1
- package/dist/chunks/structure-o9pYoDjD.js.map +0 -1
- package/dist/chunks/validate-4LgH8QMJ.js.map +0 -1
- package/dist/core.js.map +0 -1
- package/dist/print.js.map +0 -1
- package/dist/react.js.map +0 -1
- package/dist/runtime.js.map +0 -1
- package/dist/testing.js.map +0 -1
- 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
|
-
|
|
5
|
-
|
|
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
|
|
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
|
|
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
|
|
36
|
-
|
|
37
|
-
|
|
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.
|
|
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
|
|
89
|
-
|
|
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
|
|
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
|
|
130
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
311
|
+
release.
|
package/dist/catalog.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { i as eachFormProblems, s as validatePage } from "./chunks/validate-
|
|
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-
|
|
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
|