@north-light/crouter 0.3.197 → 0.3.198
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/dist/api/client.d.ts +2 -2
- package/dist/api/dto/inbox.d.ts +46 -0
- package/dist/builtin-memory/00-runtime-base.md +3 -3
- package/dist/builtin-memory/internal/nodes-and-canvas.md +1 -1
- package/dist/builtin-memory/internal/storage-tiers.md +1 -1
- package/dist/builtin-memory/wedged-child-on-runaway-bash.md +1 -1
- package/dist/clients/attach/render/chat-view.d.ts +3 -8
- package/dist/clients/attach/render/chat-view.js +3 -24
- package/dist/clients/attach/render/markdown-source.js +0 -6
- package/dist/clients/attach/render/tool-calls.js +16 -2
- package/dist/clients/attach/viewer.js +613 -602
- package/dist/clients/inbox/__tests__/serial/inbox-controller.test.js +2 -2
- package/dist/clients/inbox/__tests__/serial/mount-panel.test.js +11 -4
- package/dist/clients/inbox/controller.js +4 -4
- package/dist/clients/inbox/page-adapter.d.ts +2 -1
- package/dist/clients/inbox/tui/render.js +46 -25
- package/dist/clients/inbox/tui/slots.d.ts +1 -3
- package/dist/clients/inbox/tui/slots.js +8 -37
- package/dist/clients/inbox/tui/types.d.ts +4 -3
- package/dist/commands/__tests__/human.test.js +160 -23
- package/dist/commands/canvas-browse.js +1 -1
- package/dist/commands/human/doc.js +21 -74
- package/dist/commands/human/html.d.ts +2 -0
- package/dist/commands/human/html.js +81 -0
- package/dist/commands/human/prompts.d.ts +5 -4
- package/dist/commands/human/prompts.js +81 -153
- package/dist/commands/human/shared.d.ts +5 -6
- package/dist/commands/human/shared.js +13 -61
- package/dist/commands/human.js +7 -2
- package/dist/core/__tests__/daemon-boot.test.js +7 -4
- package/dist/core/__tests__/dead-node-policy-table.test.js +20 -36
- package/dist/core/__tests__/human-deliver.test.js +28 -37
- package/dist/core/__tests__/seam/broker-crash-teardown.test.js +13 -11
- package/dist/core/__tests__/seam/dormancy-release.test.js +63 -4
- package/dist/core/__tests__/serial/human-deliver-e2e.test.js +3 -13
- package/dist/core/canvas/__tests__/attention.test.js +2 -2
- package/dist/core/canvas/__tests__/render-remote.test.js +2 -2
- package/dist/core/canvas/browse/app.d.ts +1 -1
- package/dist/core/canvas/browse/app.js +21 -3
- package/dist/core/canvas/browse/render.d.ts +4 -0
- package/dist/core/canvas/browse/render.js +5 -1
- package/dist/core/canvas/canvas.d.ts +26 -0
- package/dist/core/canvas/canvas.js +55 -0
- package/dist/core/command.js +6 -11
- package/dist/core/config.js +3 -2
- package/dist/core/help.d.ts +3 -0
- package/dist/core/human/__tests__/html-markdown.test.d.ts +1 -0
- package/dist/core/human/__tests__/html-markdown.test.js +52 -0
- package/dist/core/human/__tests__/page-catalog.test.d.ts +1 -0
- package/dist/core/human/__tests__/page-catalog.test.js +16 -0
- package/dist/core/human/__tests__/page-render.test.js +40 -23
- package/dist/core/human/__tests__/page-tickets.test.js +48 -27
- package/dist/core/human/__tests__/page.test.js +48 -65
- package/dist/core/human/__tests__/serial/inbox-core.test.js +5 -15
- package/dist/core/human/answer-text.d.ts +3 -0
- package/dist/core/human/answer-text.js +98 -0
- package/dist/core/human/answer.d.ts +51 -0
- package/dist/core/human/answer.js +125 -0
- package/dist/core/human/component-docs.d.ts +14 -0
- package/dist/core/human/component-docs.js +161 -0
- package/dist/core/human/html-markdown.d.ts +3 -0
- package/dist/core/human/html-markdown.js +288 -0
- package/dist/core/human/page-catalog.d.ts +19 -3
- package/dist/core/human/page-catalog.js +73 -16
- package/dist/core/human/page-render.d.ts +3 -2
- package/dist/core/human/page-render.js +19 -45
- package/dist/core/human/page-schema.d.ts +108 -8
- package/dist/core/human/page-schema.js +88 -31
- package/dist/core/human/page-synth.d.ts +2 -4
- package/dist/core/human/page-synth.js +23 -7
- package/dist/core/human/page.d.ts +14 -7
- package/dist/core/human/page.js +178 -90
- package/dist/core/human/review-schema.d.ts +1 -0
- package/dist/core/human/review-schema.js +1 -1
- package/dist/core/human/scan.d.ts +3 -2
- package/dist/core/human/scan.js +7 -8
- package/dist/core/human/summary.d.ts +4 -7
- package/dist/core/human/summary.js +4 -60
- package/dist/core/human/tickets.d.ts +10 -9
- package/dist/core/human/tickets.js +5 -3
- package/dist/core/human/types.d.ts +4 -0
- package/dist/core/keybindings/catalog.d.ts +2 -2
- package/dist/core/keybindings/catalog.js +1 -0
- package/dist/core/review/realize.js +9 -1
- package/dist/core/runtime/boot-root.js +6 -1
- package/dist/core/runtime/broker.js +8 -6
- package/dist/core/runtime/busy.d.ts +4 -3
- package/dist/core/runtime/busy.js +4 -3
- package/dist/core/runtime/spawn.js +4 -4
- package/dist/core/user-settings.d.ts +17 -2
- package/dist/core/user-settings.js +32 -11
- package/dist/daemon/api/handlers/human.js +1 -1
- package/dist/daemon/api/handlers/inbox.js +22 -7
- package/dist/daemon/cron-run.js +16 -2
- package/dist/daemon/crtrd.js +1 -1
- package/dist/daemon/fleet.d.ts +22 -10
- package/dist/daemon/fleet.js +43 -24
- package/dist/daemon/human/finish.d.ts +2 -1
- package/dist/daemon/human/finish.js +45 -28
- package/dist/daemon/human/sweep.js +4 -2
- package/dist/daemon/reconcilers/broker-supervision.js +11 -8
- package/dist/daemon/reconcilers/dormant-inbox.js +11 -6
- package/dist/daemon/reconcilers/live-obligation.d.ts +21 -1
- package/dist/daemon/reconcilers/live-obligation.js +30 -2
- package/dist/daemon/reconcilers/storage-maintenance.d.ts +5 -0
- package/dist/daemon/reconcilers/storage-maintenance.js +28 -1
- package/dist/daemon/review/sweep.js +12 -1
- package/dist/pages/bundle.css +1 -1
- package/dist/pages/bundle.js +1008 -577
- package/dist/pages/comments.d.ts +29 -0
- package/dist/pages/comments.js +51 -0
- package/dist/pages/controls.d.ts +98 -0
- package/dist/pages/controls.js +261 -0
- package/dist/pages/elements/cards.d.ts +15 -4
- package/dist/pages/elements/cards.js +299 -186
- package/dist/pages/elements/chart.d.ts +22 -0
- package/dist/pages/elements/chart.js +107 -91
- package/dist/pages/elements/options.d.ts +9 -0
- package/dist/pages/elements/options.js +268 -155
- package/dist/pages/elements/pages.d.ts +6 -0
- package/dist/pages/elements/pages.js +60 -38
- package/dist/pages/elements/slot.d.ts +5 -0
- package/dist/pages/elements/slot.js +17 -10
- package/dist/pages/elements/table.d.ts +17 -0
- package/dist/pages/elements/table.js +336 -228
- package/dist/pages/elements/text.d.ts +9 -3
- package/dist/pages/elements/text.js +350 -143
- package/dist/pages/host.d.ts +0 -24
- package/dist/pages/host.js +0 -17
- package/dist/pages/readonly.d.ts +24 -0
- package/dist/pages/readonly.js +31 -0
- package/dist/pages/responses.d.ts +37 -0
- package/dist/pages/responses.js +56 -0
- package/dist/pi-extensions/canvas-bash-valve.d.ts +9 -1
- package/dist/pi-extensions/canvas-bash-valve.js +12 -8
- package/dist/types.d.ts +19 -5
- package/dist/types.js +2 -1
- package/package.json +1 -1
- package/runtime.lock.json +2 -2
- package/scripts/install-runtime.mjs +19 -24
- package/dist/clients/attach/render/html-markdown.d.ts +0 -13
- package/dist/clients/attach/render/html-markdown.js +0 -206
- package/dist/core/human/markdown-html.d.ts +0 -8
- package/dist/core/human/markdown-html.js +0 -581
|
@@ -1,61 +1,32 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
// here so an md page and an html page reach the runtime as ONE structure.
|
|
1
|
+
// One authored HTML document → the body-level HTML payload the page runtime
|
|
2
|
+
// (`src/pages`) boots against.
|
|
4
3
|
//
|
|
5
|
-
// What the payload carries: in-document chrome only — the page's own
|
|
6
|
-
//
|
|
7
|
-
// when the page bears a response.
|
|
4
|
+
// What the payload carries: in-document chrome only — the page's own HTML and
|
|
5
|
+
// `crtr-*` slot elements.
|
|
8
6
|
//
|
|
9
7
|
// What it deliberately does NOT carry: the runtime bundle's `<script>`/`<link>`
|
|
10
8
|
// tags (the consuming host injects its own bundle URLs), the page title and
|
|
11
9
|
// subtitle (host-rendered from the manifest), and any dismiss/save chrome
|
|
12
10
|
// (host-owned). The payload is a fragment, not a document: the host wraps it.
|
|
13
|
-
import {
|
|
14
|
-
import { parsePageSource } from './page.js';
|
|
15
|
-
import { isResponseBearingSlot } from './page-schema.js';
|
|
11
|
+
import { embedPageSlotConfigs, parsePageSource } from './page.js';
|
|
16
12
|
/** Render one page ticket's authored document as the runtime's HTML payload. */
|
|
17
13
|
export function renderPageHtml(manifest, document, productKinds = []) {
|
|
18
|
-
|
|
14
|
+
if (manifest.dialect !== 'html') {
|
|
15
|
+
throw new Error('legacy md pages render through the terminal inbox only');
|
|
16
|
+
}
|
|
17
|
+
// Re-projects the document and re-verifies it against the manifest, so the
|
|
19
18
|
// payload can never disagree with the slots the manifest publishes.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
return renderAuthoredHtml(document);
|
|
23
|
-
const body = projection
|
|
24
|
-
.map((block) => (block.kind === 'prose' ? renderMarkdownToHtml(block.markdown) : renderSlotElement(manifest.slots[block.slotIndex])))
|
|
25
|
-
.filter((part) => part !== '')
|
|
26
|
-
.join('\n');
|
|
27
|
-
// md is always one step, so the pager renders submit-only chrome — no step
|
|
28
|
-
// label is emitted, because `<crtr-page label>` only surfaces at steps > 1.
|
|
29
|
-
if (!manifest.slots.some(isResponseBearingSlot))
|
|
30
|
-
return body;
|
|
31
|
-
return `<crtr-pages>\n<crtr-page>\n${body}\n</crtr-page>\n</crtr-pages>`;
|
|
32
|
-
}
|
|
33
|
-
/** The pinned slot form the page runtime reads (`src/pages/slot-config.ts`). */
|
|
34
|
-
function renderSlotElement(slot) {
|
|
35
|
-
const tag = slot.unvalidated === true ? 'crtr-slot' : `crtr-${slot.kind}`;
|
|
36
|
-
const attributes = [
|
|
37
|
-
...(slot.unvalidated === true ? [` kind="${escapeHtmlAttribute(slot.kind)}"`] : []),
|
|
38
|
-
...(slot.id === undefined ? [] : [` slot-id="${escapeHtmlAttribute(slot.id)}"`]),
|
|
39
|
-
].join('');
|
|
40
|
-
return `<${tag}${attributes}><script type="application/json">${embedJson(slot.config)}</script></${tag}>`;
|
|
41
|
-
}
|
|
42
|
-
/** Serialize config for a `<script>` text node. Escaping every `<` as `\u003c`
|
|
43
|
-
* — legal JSON that parses back identically — makes `</script>`, `<!--`, and
|
|
44
|
-
* `<script` unrepresentable in the emitted text, so no config value can end
|
|
45
|
-
* the element early. */
|
|
46
|
-
function embedJson(config) {
|
|
47
|
-
return JSON.stringify(config).replace(/</g, '\\u003c');
|
|
19
|
+
parsePageSource(document, manifest, productKinds);
|
|
20
|
+
return renderAuthoredHtml(embedPageSlotConfigs(document, manifest));
|
|
48
21
|
}
|
|
49
22
|
// ---------------------------------------------------------------------------
|
|
50
23
|
// html dialect — normalized passthrough
|
|
51
24
|
// ---------------------------------------------------------------------------
|
|
52
|
-
/** The authored document's body,
|
|
53
|
-
* `<style>` elements from `<head>` are hoisted in front of it
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* `<head>` `<link>` and `<script>` elements do not survive — the page runtime
|
|
58
|
-
* is the only script a page needs. */
|
|
25
|
+
/** The authored document's body, after stored slot configs replace authored configs,
|
|
26
|
+
* `<style>` elements from `<head>` are hoisted in front of it, and every `crtr-*`
|
|
27
|
+
* slot element carrying an authored `id` gains the matching `slot-id` that the
|
|
28
|
+
* runtime reads. Authored `<head>` `<link>` and `<script>` elements do not survive:
|
|
29
|
+
* the page runtime is the only script a page needs. */
|
|
59
30
|
function renderAuthoredHtml(document) {
|
|
60
31
|
const styles = headStyles(document);
|
|
61
32
|
const body = normalizeSlotIds(bodyInnerHtml(document)).trim();
|
|
@@ -99,6 +70,9 @@ function headStyles(document) {
|
|
|
99
70
|
return [...head.matchAll(/<style\b[^>]*>[\s\S]*?<\/style>/gi)].map((match) => match[0]);
|
|
100
71
|
}
|
|
101
72
|
const PAGER_TAGS = new Set(['crtr-pages', 'crtr-page']);
|
|
73
|
+
function escapeHtmlAttribute(value) {
|
|
74
|
+
return value.replace(/[&<>"]/g, (character) => ({ '&': '&', '<': '<', '>': '>', '"': '"' })[character]);
|
|
75
|
+
}
|
|
102
76
|
function normalizeSlotIds(html) {
|
|
103
77
|
let out = '';
|
|
104
78
|
let cursor = 0;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { BUILTIN_PAGE_KINDS } from './page-catalog.js';
|
|
3
|
+
import type { ProductPageComponents } from '../../types.js';
|
|
2
4
|
export declare const PAGE_SLOT_ID_PATTERN: RegExp;
|
|
3
5
|
export declare const commentAnchorSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
4
6
|
kind: z.ZodLiteral<"whole">;
|
|
@@ -52,6 +54,7 @@ export declare const optionSchema: z.ZodObject<{
|
|
|
52
54
|
description: z.ZodOptional<z.ZodString>;
|
|
53
55
|
}, z.core.$strict>;
|
|
54
56
|
export declare const optionsConfigSchema: z.ZodObject<{
|
|
57
|
+
label: z.ZodOptional<z.ZodString>;
|
|
55
58
|
options: z.ZodArray<z.ZodObject<{
|
|
56
59
|
id: z.ZodString;
|
|
57
60
|
label: z.ZodString;
|
|
@@ -96,9 +99,11 @@ export declare const optionsResponseSchema: z.ZodObject<{
|
|
|
96
99
|
}, z.core.$strict>;
|
|
97
100
|
export type OptionsResponse = z.infer<typeof optionsResponseSchema>;
|
|
98
101
|
export declare const textConfigSchema: z.ZodObject<{
|
|
102
|
+
label: z.ZodOptional<z.ZodString>;
|
|
99
103
|
initialText: z.ZodString;
|
|
100
104
|
editable: z.ZodOptional<z.ZodBoolean>;
|
|
101
105
|
placeholder: z.ZodOptional<z.ZodString>;
|
|
106
|
+
singleLine: z.ZodOptional<z.ZodBoolean>;
|
|
102
107
|
}, z.core.$strict>;
|
|
103
108
|
export type TextConfig = z.infer<typeof textConfigSchema>;
|
|
104
109
|
export declare const textResponseSchema: z.ZodObject<{
|
|
@@ -144,7 +149,41 @@ export declare const tableRowSchema: z.ZodObject<{
|
|
|
144
149
|
id: z.ZodString;
|
|
145
150
|
cells: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>;
|
|
146
151
|
}, z.core.$strict>;
|
|
152
|
+
export declare const tableRowsSchema: z.ZodArray<z.ZodObject<{
|
|
153
|
+
id: z.ZodString;
|
|
154
|
+
cells: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>;
|
|
155
|
+
}, z.core.$strict>>;
|
|
156
|
+
/** Stored table config: its data is always embedded. */
|
|
147
157
|
export declare const tableConfigSchema: z.ZodObject<{
|
|
158
|
+
label: z.ZodOptional<z.ZodString>;
|
|
159
|
+
columns: z.ZodArray<z.ZodObject<{
|
|
160
|
+
id: z.ZodString;
|
|
161
|
+
label: z.ZodString;
|
|
162
|
+
align: z.ZodOptional<z.ZodEnum<{
|
|
163
|
+
right: "right";
|
|
164
|
+
left: "left";
|
|
165
|
+
}>>;
|
|
166
|
+
mono: z.ZodOptional<z.ZodBoolean>;
|
|
167
|
+
}, z.core.$strict>>;
|
|
168
|
+
rows: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
169
|
+
id: z.ZodString;
|
|
170
|
+
cells: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>;
|
|
171
|
+
}, z.core.$strict>>>;
|
|
172
|
+
rowSelect: z.ZodDefault<z.ZodEnum<{
|
|
173
|
+
none: "none";
|
|
174
|
+
single: "single";
|
|
175
|
+
multi: "multi";
|
|
176
|
+
}>>;
|
|
177
|
+
columnSelect: z.ZodDefault<z.ZodEnum<{
|
|
178
|
+
none: "none";
|
|
179
|
+
single: "single";
|
|
180
|
+
multi: "multi";
|
|
181
|
+
}>>;
|
|
182
|
+
}, z.core.$strict>;
|
|
183
|
+
/** Authored table config: `source` is resolved before the manifest is stored. */
|
|
184
|
+
export declare const authoredTableConfigSchema: z.ZodObject<{
|
|
185
|
+
source: z.ZodString;
|
|
186
|
+
label: z.ZodOptional<z.ZodString>;
|
|
148
187
|
columns: z.ZodArray<z.ZodObject<{
|
|
149
188
|
id: z.ZodString;
|
|
150
189
|
label: z.ZodString;
|
|
@@ -158,7 +197,6 @@ export declare const tableConfigSchema: z.ZodObject<{
|
|
|
158
197
|
id: z.ZodString;
|
|
159
198
|
cells: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>;
|
|
160
199
|
}, z.core.$strict>>>;
|
|
161
|
-
source: z.ZodOptional<z.ZodString>;
|
|
162
200
|
rowSelect: z.ZodDefault<z.ZodEnum<{
|
|
163
201
|
none: "none";
|
|
164
202
|
single: "single";
|
|
@@ -169,9 +207,9 @@ export declare const tableConfigSchema: z.ZodObject<{
|
|
|
169
207
|
single: "single";
|
|
170
208
|
multi: "multi";
|
|
171
209
|
}>>;
|
|
172
|
-
caption: z.ZodOptional<z.ZodString>;
|
|
173
210
|
}, z.core.$strict>;
|
|
174
211
|
export type TableConfig = z.infer<typeof tableConfigSchema>;
|
|
212
|
+
export type AuthoredTableConfig = TableConfig | z.infer<typeof authoredTableConfigSchema>;
|
|
175
213
|
export declare const tableResponseSchema: z.ZodObject<{
|
|
176
214
|
selectedRowIds: z.ZodArray<z.ZodString>;
|
|
177
215
|
selectedColumnIds: z.ZodArray<z.ZodString>;
|
|
@@ -208,7 +246,33 @@ export declare const cardSchema: z.ZodObject<{
|
|
|
208
246
|
body: z.ZodOptional<z.ZodString>;
|
|
209
247
|
tag: z.ZodOptional<z.ZodString>;
|
|
210
248
|
}, z.core.$strict>;
|
|
249
|
+
export declare const cardsSchema: z.ZodArray<z.ZodObject<{
|
|
250
|
+
id: z.ZodString;
|
|
251
|
+
title: z.ZodString;
|
|
252
|
+
subtitle: z.ZodOptional<z.ZodString>;
|
|
253
|
+
body: z.ZodOptional<z.ZodString>;
|
|
254
|
+
tag: z.ZodOptional<z.ZodString>;
|
|
255
|
+
}, z.core.$strict>>;
|
|
256
|
+
/** Stored cards config: its data is always embedded. */
|
|
211
257
|
export declare const cardsConfigSchema: z.ZodObject<{
|
|
258
|
+
label: z.ZodOptional<z.ZodString>;
|
|
259
|
+
cards: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
260
|
+
id: z.ZodString;
|
|
261
|
+
title: z.ZodString;
|
|
262
|
+
subtitle: z.ZodOptional<z.ZodString>;
|
|
263
|
+
body: z.ZodOptional<z.ZodString>;
|
|
264
|
+
tag: z.ZodOptional<z.ZodString>;
|
|
265
|
+
}, z.core.$strict>>>;
|
|
266
|
+
mode: z.ZodEnum<{
|
|
267
|
+
single: "single";
|
|
268
|
+
multi: "multi";
|
|
269
|
+
}>;
|
|
270
|
+
columns: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<2>, z.ZodLiteral<3>]>>;
|
|
271
|
+
}, z.core.$strict>;
|
|
272
|
+
/** Authored cards config: `source` is resolved before the manifest is stored. */
|
|
273
|
+
export declare const authoredCardsConfigSchema: z.ZodObject<{
|
|
274
|
+
source: z.ZodString;
|
|
275
|
+
label: z.ZodOptional<z.ZodString>;
|
|
212
276
|
cards: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
213
277
|
id: z.ZodString;
|
|
214
278
|
title: z.ZodString;
|
|
@@ -216,7 +280,6 @@ export declare const cardsConfigSchema: z.ZodObject<{
|
|
|
216
280
|
body: z.ZodOptional<z.ZodString>;
|
|
217
281
|
tag: z.ZodOptional<z.ZodString>;
|
|
218
282
|
}, z.core.$strict>>>;
|
|
219
|
-
source: z.ZodOptional<z.ZodString>;
|
|
220
283
|
mode: z.ZodEnum<{
|
|
221
284
|
single: "single";
|
|
222
285
|
multi: "multi";
|
|
@@ -224,6 +287,7 @@ export declare const cardsConfigSchema: z.ZodObject<{
|
|
|
224
287
|
columns: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<2>, z.ZodLiteral<3>]>>;
|
|
225
288
|
}, z.core.$strict>;
|
|
226
289
|
export type CardsConfig = z.infer<typeof cardsConfigSchema>;
|
|
290
|
+
export type AuthoredCardsConfig = CardsConfig | z.infer<typeof authoredCardsConfigSchema>;
|
|
227
291
|
export declare const cardsResponseSchema: z.ZodObject<{
|
|
228
292
|
selectedCardIds: z.ZodArray<z.ZodString>;
|
|
229
293
|
comments: z.ZodArray<z.ZodObject<{
|
|
@@ -256,6 +320,8 @@ export declare const chartSeriesSchema: z.ZodObject<{
|
|
|
256
320
|
field: z.ZodString;
|
|
257
321
|
label: z.ZodOptional<z.ZodString>;
|
|
258
322
|
}, z.core.$strict>;
|
|
323
|
+
export declare const chartDataRowsSchema: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
324
|
+
/** Stored chart config: its data is always embedded. */
|
|
259
325
|
export declare const chartConfigSchema: z.ZodObject<{
|
|
260
326
|
kind: z.ZodEnum<{
|
|
261
327
|
line: "line";
|
|
@@ -268,11 +334,28 @@ export declare const chartConfigSchema: z.ZodObject<{
|
|
|
268
334
|
label: z.ZodOptional<z.ZodString>;
|
|
269
335
|
}, z.core.$strict>>;
|
|
270
336
|
title: z.ZodOptional<z.ZodString>;
|
|
271
|
-
data: z.ZodOptional<z.ZodUnknown
|
|
272
|
-
|
|
337
|
+
data: z.ZodOptional<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
338
|
+
}, z.core.$strict>;
|
|
339
|
+
/** Authored chart config: `source` is resolved before the manifest is stored. */
|
|
340
|
+
export declare const authoredChartConfigSchema: z.ZodObject<{
|
|
341
|
+
source: z.ZodString;
|
|
342
|
+
kind: z.ZodEnum<{
|
|
343
|
+
line: "line";
|
|
344
|
+
bar: "bar";
|
|
345
|
+
area: "area";
|
|
346
|
+
}>;
|
|
347
|
+
x: z.ZodString;
|
|
348
|
+
y: z.ZodArray<z.ZodObject<{
|
|
349
|
+
field: z.ZodString;
|
|
350
|
+
label: z.ZodOptional<z.ZodString>;
|
|
351
|
+
}, z.core.$strict>>;
|
|
352
|
+
title: z.ZodOptional<z.ZodString>;
|
|
353
|
+
data: z.ZodOptional<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
273
354
|
}, z.core.$strict>;
|
|
274
355
|
export type ChartConfig = z.infer<typeof chartConfigSchema>;
|
|
356
|
+
export type AuthoredChartConfig = ChartConfig | z.infer<typeof authoredChartConfigSchema>;
|
|
275
357
|
export type SlotConfig = OptionsConfig | TextConfig | TableConfig | CardsConfig | ChartConfig | Record<string, unknown>;
|
|
358
|
+
export type AuthoredSlotConfig = OptionsConfig | TextConfig | AuthoredTableConfig | AuthoredCardsConfig | AuthoredChartConfig | Record<string, unknown>;
|
|
276
359
|
export type SlotResponse = OptionsResponse | TextResponse | TableResponse | CardsResponse | Record<string, unknown>;
|
|
277
360
|
export interface PageSlot {
|
|
278
361
|
id?: string;
|
|
@@ -280,11 +363,18 @@ export interface PageSlot {
|
|
|
280
363
|
step: number;
|
|
281
364
|
config: SlotConfig;
|
|
282
365
|
unvalidated?: true;
|
|
366
|
+
/** Product registration declares this slot display-only. */
|
|
367
|
+
display?: true;
|
|
368
|
+
}
|
|
369
|
+
/** A page before submit may contain filesystem `source` paths. */
|
|
370
|
+
export interface AuthoredPageSlot extends Omit<PageSlot, 'config'> {
|
|
371
|
+
config: AuthoredSlotConfig;
|
|
283
372
|
}
|
|
284
373
|
export interface PageSource {
|
|
285
374
|
sessionName?: string;
|
|
286
375
|
askedBy?: string;
|
|
287
376
|
blockedSince?: string;
|
|
377
|
+
profileName?: string;
|
|
288
378
|
nodeId?: string;
|
|
289
379
|
}
|
|
290
380
|
export interface PageManifest {
|
|
@@ -295,14 +385,24 @@ export interface PageManifest {
|
|
|
295
385
|
placement: 'inline' | 'panel';
|
|
296
386
|
document: 'page.md' | 'page.html';
|
|
297
387
|
steps: number;
|
|
388
|
+
/** Persisted slots never carry a source path. */
|
|
298
389
|
slots: PageSlot[];
|
|
299
390
|
source?: PageSource;
|
|
300
391
|
}
|
|
392
|
+
/** A page before submit may carry filesystem source paths in its built-in slots. */
|
|
393
|
+
export interface AuthoredPageManifest extends Omit<PageManifest, 'slots'> {
|
|
394
|
+
slots: AuthoredPageSlot[];
|
|
395
|
+
}
|
|
396
|
+
export declare const BUILTIN_PAGE_CONFIG_SCHEMAS: Record<(typeof BUILTIN_PAGE_KINDS)[number], z.ZodType>;
|
|
397
|
+
export declare const BUILTIN_PAGE_RESPONSE_SCHEMAS: Partial<Record<(typeof BUILTIN_PAGE_KINDS)[number], z.ZodType>>;
|
|
398
|
+
export declare function issueText(error: z.ZodError): string;
|
|
301
399
|
/** The canonical response-bearing predicate used by every page consumer. */
|
|
302
400
|
export declare function isResponseBearingSlot(slot: PageSlot): boolean;
|
|
303
|
-
/** Validate
|
|
304
|
-
export declare function validatePageManifest(parsed: unknown,
|
|
305
|
-
|
|
401
|
+
/** Validate a persisted manifest. Source paths are rejected: pages render only embedded data. */
|
|
402
|
+
export declare function validatePageManifest(parsed: unknown, productComponents?: ProductPageComponents): PageManifest;
|
|
403
|
+
/** Validate an authored manifest before submit. Source paths are resolved by `snapshotPageSources`. */
|
|
404
|
+
export declare function validateAuthoredPageManifest(parsed: unknown, productComponents?: ProductPageComponents): AuthoredPageManifest;
|
|
405
|
+
export declare function createPageManifestSchema(productComponents?: ProductPageComponents): z.ZodType<PageManifest>;
|
|
306
406
|
/** Built-in-only manifest schema; product-aware callers use createPageManifestSchema. */
|
|
307
407
|
export declare const pageManifestSchema: z.ZodType<PageManifest, unknown, z.core.$ZodTypeInternals<PageManifest, unknown>>;
|
|
308
408
|
export type PageResponses = Record<string, SlotResponse>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { BUILTIN_PAGE_KINDS,
|
|
2
|
+
import { BUILTIN_PAGE_KINDS, productPageComponent } from './page-catalog.js';
|
|
3
3
|
export const PAGE_SLOT_ID_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;
|
|
4
4
|
const idSchema = z.string().regex(PAGE_SLOT_ID_PATTERN, { error: 'slot id must match /^[A-Za-z0-9_-]{1,64}$/' });
|
|
5
5
|
const itemIdSchema = z.string().min(1);
|
|
@@ -27,6 +27,7 @@ export const commentAnchorSchema = z.discriminatedUnion('kind', [
|
|
|
27
27
|
export const commentSchema = z.object({ id: z.string().min(1), anchor: commentAnchorSchema, text: z.string() }).strict();
|
|
28
28
|
export const optionSchema = z.object({ id: itemIdSchema, label: z.string().min(1), description: z.string().optional() }).strict();
|
|
29
29
|
export const optionsConfigSchema = z.object({
|
|
30
|
+
label: z.string().optional(),
|
|
30
31
|
options: z.array(optionSchema).min(1).superRefine((items, ctx) => uniqueIds(items, ctx, 'option')),
|
|
31
32
|
mode: z.enum(['single', 'multi']),
|
|
32
33
|
allowFreetext: z.boolean().optional(),
|
|
@@ -34,45 +35,67 @@ export const optionsConfigSchema = z.object({
|
|
|
34
35
|
freetextPlaceholder: z.string().optional(),
|
|
35
36
|
}).strict();
|
|
36
37
|
export const optionsResponseSchema = z.object({ selectedOptionIds: z.array(itemIdSchema), comments: z.array(commentSchema), freetext: z.string().optional() }).strict();
|
|
37
|
-
|
|
38
|
+
// `singleLine` swaps the writing surface for one compact form field — a subject line, a name,
|
|
39
|
+
// a URL. It is an input by definition, so it is always editable and `editable: false` on it is
|
|
40
|
+
// an authoring mistake rather than a read-only one-liner.
|
|
41
|
+
export const textConfigSchema = z.object({ label: z.string().optional(), initialText: z.string(), editable: z.boolean().optional(), placeholder: z.string().optional(), singleLine: z.boolean().optional() }).strict().superRefine((config, ctx) => {
|
|
42
|
+
if (config.singleLine === true && config.editable === false)
|
|
43
|
+
ctx.addIssue({ code: 'custom', path: ['editable'], message: 'single-line text is always editable; drop editable: false' });
|
|
44
|
+
});
|
|
38
45
|
export const textResponseSchema = z.object({ text: z.string(), edited: z.boolean(), comments: z.array(commentSchema) }).strict();
|
|
39
46
|
export const tableColumnSchema = z.object({ id: itemIdSchema, label: z.string().min(1), align: z.enum(['left', 'right']).optional(), mono: z.boolean().optional() }).strict();
|
|
40
47
|
export const tableCellSchema = z.union([z.string(), z.number(), z.boolean(), z.null()]);
|
|
41
48
|
export const tableRowSchema = z.object({ id: itemIdSchema, cells: z.record(z.string(), tableCellSchema) }).strict();
|
|
42
|
-
export const
|
|
49
|
+
export const tableRowsSchema = z.array(tableRowSchema).superRefine((items, ctx) => uniqueIds(items, ctx, 'row'));
|
|
50
|
+
const tableConfigFields = {
|
|
51
|
+
label: z.string().optional(),
|
|
43
52
|
columns: z.array(tableColumnSchema).min(1).superRefine((items, ctx) => uniqueIds(items, ctx, 'column')),
|
|
44
|
-
rows:
|
|
45
|
-
source: z.string().optional(),
|
|
53
|
+
rows: tableRowsSchema.optional(),
|
|
46
54
|
rowSelect: selectModeSchema.default('none'),
|
|
47
55
|
columnSelect: selectModeSchema.default('none'),
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
56
|
+
};
|
|
57
|
+
/** Stored table config: its data is always embedded. */
|
|
58
|
+
export const tableConfigSchema = z.object(tableConfigFields).strict();
|
|
59
|
+
/** Authored table config: `source` is resolved before the manifest is stored. */
|
|
60
|
+
export const authoredTableConfigSchema = z.object({ ...tableConfigFields, source: z.string() }).strict().superRefine((config, ctx) => {
|
|
61
|
+
if (config.rows !== undefined)
|
|
51
62
|
ctx.addIssue({ code: 'custom', message: 'table rows and source are mutually exclusive' });
|
|
52
63
|
});
|
|
53
64
|
export const tableResponseSchema = z.object({ selectedRowIds: z.array(itemIdSchema), selectedColumnIds: z.array(itemIdSchema), comments: z.array(commentSchema) }).strict();
|
|
54
65
|
export const cardSchema = z.object({ id: itemIdSchema, title: z.string().min(1), subtitle: z.string().optional(), body: z.string().optional(), tag: z.string().optional() }).strict();
|
|
55
|
-
export const
|
|
56
|
-
|
|
57
|
-
|
|
66
|
+
export const cardsSchema = z.array(cardSchema).superRefine((items, ctx) => uniqueIds(items, ctx, 'card'));
|
|
67
|
+
const cardsConfigFields = {
|
|
68
|
+
label: z.string().optional(),
|
|
69
|
+
cards: cardsSchema.optional(),
|
|
58
70
|
mode: z.enum(['single', 'multi']),
|
|
59
71
|
columns: z.union([z.literal(2), z.literal(3)]).optional(),
|
|
60
|
-
}
|
|
61
|
-
|
|
72
|
+
};
|
|
73
|
+
/** Stored cards config: its data is always embedded. */
|
|
74
|
+
export const cardsConfigSchema = z.object(cardsConfigFields).strict();
|
|
75
|
+
/** Authored cards config: `source` is resolved before the manifest is stored. */
|
|
76
|
+
export const authoredCardsConfigSchema = z.object({ ...cardsConfigFields, source: z.string() }).strict().superRefine((config, ctx) => {
|
|
77
|
+
if (config.cards !== undefined)
|
|
62
78
|
ctx.addIssue({ code: 'custom', message: 'cards data and source are mutually exclusive' });
|
|
63
79
|
});
|
|
64
80
|
export const cardsResponseSchema = z.object({ selectedCardIds: z.array(itemIdSchema), comments: z.array(commentSchema) }).strict();
|
|
65
81
|
export const chartSeriesSchema = z.object({ field: z.string().min(1), label: z.string().optional() }).strict();
|
|
66
|
-
export const
|
|
82
|
+
export const chartDataRowsSchema = z.array(z.record(z.string(), z.unknown()));
|
|
83
|
+
const chartConfigFields = {
|
|
67
84
|
kind: z.enum(['line', 'bar', 'area']),
|
|
68
85
|
x: z.string().min(1),
|
|
69
86
|
y: z.array(chartSeriesSchema).min(1),
|
|
70
87
|
title: z.string().optional(),
|
|
71
|
-
data:
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
|
|
88
|
+
data: chartDataRowsSchema.optional(),
|
|
89
|
+
};
|
|
90
|
+
/** Stored chart config: its data is always embedded. */
|
|
91
|
+
export const chartConfigSchema = z.object(chartConfigFields).strict();
|
|
92
|
+
/** Authored chart config: `source` is resolved before the manifest is stored. */
|
|
93
|
+
export const authoredChartConfigSchema = z.object({ ...chartConfigFields, source: z.string() }).strict().superRefine((config, ctx) => {
|
|
94
|
+
if (config.data !== undefined)
|
|
95
|
+
ctx.addIssue({ code: 'custom', message: 'chart data and source are mutually exclusive' });
|
|
96
|
+
});
|
|
97
|
+
const sourceSchema = z.object({ sessionName: z.string().optional(), askedBy: z.string().optional(), blockedSince: z.string().optional(), profileName: z.string().optional(), nodeId: z.string().optional() }).strict();
|
|
98
|
+
const rawSlotSchema = z.object({ id: idSchema.optional(), kind: z.string().min(1), step: z.number().int().nonnegative(), config: jsonObjectSchema, unvalidated: z.literal(true).optional(), display: z.literal(true).optional() }).strict();
|
|
76
99
|
const rawPageManifestSchema = z.object({
|
|
77
100
|
schema: z.literal('crtr.page/v1'),
|
|
78
101
|
dialect: z.enum(['md', 'html']),
|
|
@@ -84,18 +107,33 @@ const rawPageManifestSchema = z.object({
|
|
|
84
107
|
slots: z.array(rawSlotSchema),
|
|
85
108
|
source: sourceSchema.optional(),
|
|
86
109
|
}).strict();
|
|
87
|
-
const
|
|
110
|
+
export const BUILTIN_PAGE_CONFIG_SCHEMAS = {
|
|
88
111
|
options: optionsConfigSchema,
|
|
89
112
|
text: textConfigSchema,
|
|
90
113
|
table: tableConfigSchema,
|
|
91
114
|
cards: cardsConfigSchema,
|
|
92
115
|
chart: chartConfigSchema,
|
|
93
116
|
};
|
|
94
|
-
|
|
117
|
+
const AUTHORED_PAGE_CONFIG_SCHEMAS = {
|
|
118
|
+
options: optionsConfigSchema,
|
|
119
|
+
text: textConfigSchema,
|
|
120
|
+
table: z.union([tableConfigSchema, authoredTableConfigSchema]),
|
|
121
|
+
cards: z.union([cardsConfigSchema, authoredCardsConfigSchema]),
|
|
122
|
+
chart: z.union([chartConfigSchema, authoredChartConfigSchema]),
|
|
123
|
+
};
|
|
124
|
+
export const BUILTIN_PAGE_RESPONSE_SCHEMAS = {
|
|
125
|
+
options: optionsResponseSchema,
|
|
126
|
+
text: textResponseSchema,
|
|
127
|
+
table: tableResponseSchema,
|
|
128
|
+
cards: cardsResponseSchema,
|
|
129
|
+
};
|
|
130
|
+
export function issueText(error) {
|
|
95
131
|
return error.issues.map((issue) => `${issue.path.length === 0 ? 'value' : issue.path.join('.')}: ${issue.message}`).join('; ');
|
|
96
132
|
}
|
|
97
133
|
/** The canonical response-bearing predicate used by every page consumer. */
|
|
98
134
|
export function isResponseBearingSlot(slot) {
|
|
135
|
+
if (slot.display === true)
|
|
136
|
+
return false;
|
|
99
137
|
if (slot.unvalidated === true)
|
|
100
138
|
return true;
|
|
101
139
|
if (slot.kind === 'options' || slot.kind === 'text' || slot.kind === 'cards')
|
|
@@ -105,8 +143,7 @@ export function isResponseBearingSlot(slot) {
|
|
|
105
143
|
const config = slot.config;
|
|
106
144
|
return (config.rowSelect ?? 'none') !== 'none' || (config.columnSelect ?? 'none') !== 'none';
|
|
107
145
|
}
|
|
108
|
-
|
|
109
|
-
export function validatePageManifest(parsed, productKinds = []) {
|
|
146
|
+
function validateManifest(parsed, productComponents, authored) {
|
|
110
147
|
const manifest = rawPageManifestSchema.parse(parsed);
|
|
111
148
|
if (manifest.document !== (manifest.dialect === 'md' ? 'page.md' : 'page.html'))
|
|
112
149
|
throw new Error(`page document "${manifest.document}" does not match ${manifest.dialect} dialect`);
|
|
@@ -120,20 +157,32 @@ export function validatePageManifest(parsed, productKinds = []) {
|
|
|
120
157
|
if (BUILTIN_PAGE_KINDS.includes(raw.kind)) {
|
|
121
158
|
if (raw.unvalidated === true)
|
|
122
159
|
throw new Error(`built-in slot kind "${raw.kind}" cannot be unvalidated`);
|
|
123
|
-
|
|
160
|
+
if (raw.display === true)
|
|
161
|
+
throw new Error(`built-in slot kind "${raw.kind}" cannot set display: true`);
|
|
162
|
+
const result = (authored ? AUTHORED_PAGE_CONFIG_SCHEMAS : BUILTIN_PAGE_CONFIG_SCHEMAS)[raw.kind].safeParse(raw.config);
|
|
124
163
|
if (!result.success)
|
|
125
164
|
throw new Error(`invalid config for slot ${raw.id === undefined ? `#${index}` : `"${raw.id}"`} (${raw.kind}): ${issueText(result.error)}`);
|
|
126
165
|
slot = { ...(raw.id === undefined ? {} : { id: raw.id }), kind: raw.kind, step: raw.step, config: result.data };
|
|
127
166
|
}
|
|
128
167
|
else {
|
|
129
|
-
|
|
168
|
+
const component = productPageComponent(raw.kind, productComponents);
|
|
169
|
+
if (component === undefined)
|
|
130
170
|
throw new Error(`unregistered page slot kind "${raw.kind}" at slot #${index}`);
|
|
131
171
|
if (raw.unvalidated !== true)
|
|
132
172
|
throw new Error(`registered product slot kind "${raw.kind}" must set unvalidated: true`);
|
|
133
|
-
|
|
173
|
+
if (!authored && raw.display !== (component.display === true ? true : undefined))
|
|
174
|
+
throw new Error(component.display === true
|
|
175
|
+
? `display product slot kind "${raw.kind}" must set display: true`
|
|
176
|
+
: `non-display product slot kind "${raw.kind}" cannot set display: true`);
|
|
177
|
+
if (authored && raw.display === true && component.display !== true)
|
|
178
|
+
throw new Error(`non-display product slot kind "${raw.kind}" cannot set display: true`);
|
|
179
|
+
slot = { ...(raw.id === undefined ? {} : { id: raw.id }), kind: raw.kind, step: raw.step, config: raw.config, unvalidated: true, ...(component.display === true ? { display: true } : {}) };
|
|
180
|
+
}
|
|
181
|
+
if (isResponseBearingSlot(slot) && slot.id === undefined) {
|
|
182
|
+
throw new Error(slot.unvalidated === true
|
|
183
|
+
? `product slot kind "${slot.kind}" at slot #${index} requires an id="…" attribute on its <crtr-slot> element`
|
|
184
|
+
: `response-bearing slot kind "${slot.kind}" at slot #${index} requires an id="…" attribute on its element`);
|
|
134
185
|
}
|
|
135
|
-
if (isResponseBearingSlot(slot) && slot.id === undefined)
|
|
136
|
-
throw new Error(`response-bearing slot kind "${slot.kind}" at slot #${index} requires an id`);
|
|
137
186
|
if (slot.id !== undefined) {
|
|
138
187
|
if (seen.has(slot.id))
|
|
139
188
|
throw new Error(`duplicate slot id "${slot.id}"`);
|
|
@@ -143,16 +192,24 @@ export function validatePageManifest(parsed, productKinds = []) {
|
|
|
143
192
|
});
|
|
144
193
|
return { ...manifest, slots };
|
|
145
194
|
}
|
|
146
|
-
|
|
195
|
+
/** Validate a persisted manifest. Source paths are rejected: pages render only embedded data. */
|
|
196
|
+
export function validatePageManifest(parsed, productComponents = []) {
|
|
197
|
+
return validateManifest(parsed, productComponents, false);
|
|
198
|
+
}
|
|
199
|
+
/** Validate an authored manifest before submit. Source paths are resolved by `snapshotPageSources`. */
|
|
200
|
+
export function validateAuthoredPageManifest(parsed, productComponents = []) {
|
|
201
|
+
return validateManifest(parsed, productComponents, true);
|
|
202
|
+
}
|
|
203
|
+
export function createPageManifestSchema(productComponents = []) {
|
|
147
204
|
return z.custom((value) => {
|
|
148
205
|
try {
|
|
149
|
-
validatePageManifest(value,
|
|
206
|
+
validatePageManifest(value, productComponents);
|
|
150
207
|
return true;
|
|
151
208
|
}
|
|
152
209
|
catch {
|
|
153
210
|
return false;
|
|
154
211
|
}
|
|
155
|
-
}, { error: 'invalid page manifest' }).transform((value) => validatePageManifest(value,
|
|
212
|
+
}, { error: 'invalid page manifest' }).transform((value) => validatePageManifest(value, productComponents));
|
|
156
213
|
}
|
|
157
214
|
/** Built-in-only manifest schema; product-aware callers use createPageManifestSchema. */
|
|
158
215
|
export const pageManifestSchema = createPageManifestSchema();
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
export declare const ASK_RESPONSE_SLOT = "```slot:text\n{ \"id\": \"response\", \"initialText\": \"\", \"editable\": true }\n```";
|
|
3
|
-
/** Build a page document from title/subtitle/body — the shape crtr synthesizes when no page file is authored. */
|
|
1
|
+
/** Build a complete HTML page from title, subtitle, and body for internal producers. */
|
|
4
2
|
export declare function synthesizedPageDocument(title: string, subtitle: string, body: string, responseBearing: boolean): string;
|
|
5
|
-
/** Materialize
|
|
3
|
+
/** Materialize an internally generated document into a private temp dir; the caller removes it after submission. */
|
|
6
4
|
export declare function writeSynthesizedPage(document: string): {
|
|
7
5
|
dir: string;
|
|
8
6
|
path: string;
|
|
@@ -1,17 +1,33 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
2
|
import { mkdtempSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
function escapeHtml(value) {
|
|
5
|
+
return value.replace(/[&<>"']/g, (character) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[character]);
|
|
6
|
+
}
|
|
7
|
+
function bodyHtml(body) {
|
|
8
|
+
if (body === '')
|
|
9
|
+
return '';
|
|
10
|
+
return `<p>${escapeHtml(body).replace(/\r?\n/g, '<br>')}</p>`;
|
|
11
|
+
}
|
|
12
|
+
/** Build a complete HTML page from title, subtitle, and body for internal producers. */
|
|
7
13
|
export function synthesizedPageDocument(title, subtitle, body, responseBearing) {
|
|
8
|
-
const
|
|
9
|
-
|
|
14
|
+
const contents = bodyHtml(body);
|
|
15
|
+
const interactive = responseBearing
|
|
16
|
+
? `<crtr-pages><crtr-page><crtr-text id="response"><script type="application/json">{"initialText":"","editable":true}</script></crtr-text></crtr-page></crtr-pages>`
|
|
17
|
+
: '';
|
|
18
|
+
return `<!doctype html>
|
|
19
|
+
<html><head>
|
|
20
|
+
<meta name="crtr-page-title" content="${escapeHtml(title)}">
|
|
21
|
+
<meta name="crtr-page-subtitle" content="${escapeHtml(subtitle)}">
|
|
22
|
+
</head><body>
|
|
23
|
+
${contents}${contents === '' || interactive === '' ? '' : '\n'}${interactive}
|
|
24
|
+
</body></html>
|
|
25
|
+
`;
|
|
10
26
|
}
|
|
11
|
-
/** Materialize
|
|
27
|
+
/** Materialize an internally generated document into a private temp dir; the caller removes it after submission. */
|
|
12
28
|
export function writeSynthesizedPage(document) {
|
|
13
29
|
const dir = mkdtempSync(join(tmpdir(), 'crtr-human-page-'));
|
|
14
|
-
const path = join(dir, 'page.
|
|
30
|
+
const path = join(dir, 'page.html');
|
|
15
31
|
writeFileSync(path, document, { encoding: 'utf8', mode: 0o600 });
|
|
16
32
|
return { dir, path };
|
|
17
33
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type { ProductPageComponents } from '../../types.js';
|
|
2
|
+
import { type AuthoredPageManifest, type PageManifest, type PageSource } from './page-schema.js';
|
|
3
|
+
/** Dialects retained in persisted manifests. New page authoring is HTML-only. */
|
|
2
4
|
export type PageDialect = 'md' | 'html';
|
|
5
|
+
export type AuthoredPageDialect = 'html';
|
|
3
6
|
export type PageSourceBlock = {
|
|
4
7
|
kind: 'prose';
|
|
5
8
|
markdown: string;
|
|
@@ -12,11 +15,15 @@ export type PageSourceProjection = PageSourceBlock[];
|
|
|
12
15
|
export declare class PageAuthoringError extends Error {
|
|
13
16
|
constructor(message: string);
|
|
14
17
|
}
|
|
15
|
-
/** Derive the transport manifest from one authored page document. */
|
|
16
|
-
export declare function derivePageManifestFromDocument(document: string, dialect:
|
|
17
|
-
/** Derive the transport manifest from one authored page source file. */
|
|
18
|
-
export declare function derivePageManifest(sourceFile: string, dialect:
|
|
18
|
+
/** Derive the transport manifest from one authored HTML page document. */
|
|
19
|
+
export declare function derivePageManifestFromDocument(document: string, dialect: AuthoredPageDialect, productComponents?: ProductPageComponents, source?: PageSource): AuthoredPageManifest;
|
|
20
|
+
/** Derive the transport manifest from one authored HTML page source file. */
|
|
21
|
+
export declare function derivePageManifest(sourceFile: string, dialect: AuthoredPageDialect, productComponents?: ProductPageComponents, source?: PageSource): AuthoredPageManifest;
|
|
22
|
+
/** Read source files into their built-in slots, returning a self-contained stored manifest. */
|
|
23
|
+
export declare function snapshotPageSources(manifest: AuthoredPageManifest, productComponents?: ProductPageComponents): PageManifest;
|
|
24
|
+
/** Replace every authored slot config with its stored, self-contained config for runtime rendering. */
|
|
25
|
+
export declare function embedPageSlotConfigs(document: string, manifest: PageManifest): string;
|
|
19
26
|
/** Read and validate page.json from a ticket directory. */
|
|
20
|
-
export declare function parsePage(dir: string,
|
|
27
|
+
export declare function parsePage(dir: string, productComponents?: ProductPageComponents): PageManifest;
|
|
21
28
|
/** Project authored source for local rendering and verify its slot references against the manifest. */
|
|
22
|
-
export declare function parsePageSource(document: string, manifest: PageManifest,
|
|
29
|
+
export declare function parsePageSource(document: string, manifest: PageManifest, productComponents?: ProductPageComponents): PageSourceProjection;
|