@glw907/cairn-cms 0.41.0 → 0.51.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +82 -0
- package/README.md +2 -2
- package/dist/ambient.d.ts +9 -0
- package/dist/ambient.js +1 -0
- package/dist/components/AdminLayout.svelte +6 -8
- package/dist/components/CairnAdmin.svelte +67 -0
- package/dist/components/CairnAdmin.svelte.d.ts +35 -0
- package/dist/components/ConceptList.svelte +4 -5
- package/dist/components/ConceptList.svelte.d.ts +4 -8
- package/dist/components/ConfirmPage.svelte +1 -1
- package/dist/components/EditPage.svelte +107 -25
- package/dist/components/EditPage.svelte.d.ts +8 -10
- package/dist/components/EditorToolbar.svelte +79 -8
- package/dist/components/EditorToolbar.svelte.d.ts +10 -2
- package/dist/components/LoginPage.svelte +2 -2
- package/dist/components/LoginPage.svelte.d.ts +1 -1
- package/dist/components/ManageEditors.svelte +4 -3
- package/dist/components/ManageEditors.svelte.d.ts +2 -1
- package/dist/components/MarkdownEditor.svelte +20 -2
- package/dist/components/cairn-admin.css +57 -9
- package/dist/components/editor-highlight.d.ts +1 -0
- package/dist/components/editor-highlight.js +31 -8
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +1 -0
- package/dist/components/markdown-directives.d.ts +10 -0
- package/dist/components/markdown-directives.js +54 -1
- package/dist/components/markdown-format.d.ts +0 -8
- package/dist/components/markdown-format.js +0 -28
- package/dist/components/preview-doc.d.ts +27 -0
- package/dist/components/preview-doc.js +64 -0
- package/dist/content/compose.js +1 -0
- package/dist/content/links.d.ts +8 -0
- package/dist/content/links.js +28 -0
- package/dist/content/types.d.ts +35 -2
- package/dist/delivery/data.d.ts +3 -5
- package/dist/delivery/data.js +2 -3
- package/dist/delivery/feeds.js +1 -7
- package/dist/delivery/index.d.ts +2 -2
- package/dist/delivery/index.js +1 -1
- package/dist/delivery/manifest.d.ts +0 -5
- package/dist/delivery/manifest.js +5 -16
- package/dist/{sveltekit → delivery}/public-routes.d.ts +4 -4
- package/dist/{sveltekit → delivery}/public-routes.js +7 -7
- package/dist/delivery/site-indexes.d.ts +3 -3
- package/dist/delivery/site-indexes.js +3 -3
- package/dist/delivery/{site-index.d.ts → site-resolver.d.ts} +7 -3
- package/dist/delivery/{site-index.js → site-resolver.js} +13 -3
- package/dist/delivery/sitemap.js +1 -3
- package/dist/delivery/xml.d.ts +2 -0
- package/dist/delivery/xml.js +11 -0
- package/dist/diagnostics/conditions.js +24 -0
- package/dist/doctor/bin.js +30 -12
- package/dist/doctor/check-floors.d.ts +15 -0
- package/dist/doctor/check-floors.js +107 -0
- package/dist/doctor/check-probe.d.ts +3 -0
- package/dist/doctor/check-probe.js +123 -0
- package/dist/doctor/checks-github.js +1 -1
- package/dist/doctor/checks-local.d.ts +1 -0
- package/dist/doctor/checks-local.js +28 -2
- package/dist/doctor/cloudflare-api.js +2 -2
- package/dist/doctor/index.d.ts +28 -3
- package/dist/doctor/index.js +47 -6
- package/dist/doctor/types.d.ts +2 -0
- package/dist/doctor/wrangler-config.d.ts +4 -0
- package/dist/doctor/wrangler-config.js +11 -0
- package/dist/email.js +4 -11
- package/dist/env.d.ts +3 -2
- package/dist/env.js +12 -6
- package/dist/escape.d.ts +2 -0
- package/dist/escape.js +11 -0
- package/dist/github/credentials.d.ts +2 -1
- package/dist/github/credentials.js +10 -2
- package/dist/github/types.d.ts +2 -0
- package/dist/github/types.js +4 -0
- package/dist/index.d.ts +1 -1
- package/dist/log/events.d.ts +1 -1
- package/dist/nav/site-config.d.ts +2 -0
- package/dist/nav/site-config.js +2 -0
- package/dist/sveltekit/admin-dispatch.d.ts +28 -0
- package/dist/sveltekit/admin-dispatch.js +62 -0
- package/dist/sveltekit/cairn-admin.d.ts +94 -0
- package/dist/sveltekit/cairn-admin.js +126 -0
- package/dist/sveltekit/condition-response.d.ts +1 -0
- package/dist/sveltekit/condition-response.js +25 -0
- package/dist/sveltekit/content-routes.d.ts +39 -15
- package/dist/sveltekit/content-routes.js +84 -50
- package/dist/sveltekit/guard.d.ts +8 -2
- package/dist/sveltekit/guard.js +18 -4
- package/dist/sveltekit/https-required-page.js +2 -1
- package/dist/sveltekit/index.d.ts +3 -1
- package/dist/sveltekit/index.js +2 -0
- package/dist/sveltekit/nav-routes.d.ts +3 -1
- package/dist/sveltekit/nav-routes.js +22 -19
- package/dist/sveltekit/static-admin-page.d.ts +0 -2
- package/dist/sveltekit/static-admin-page.js +1 -8
- package/dist/sveltekit/types.d.ts +18 -11
- package/dist/vite/index.d.ts +16 -0
- package/dist/vite/index.js +57 -13
- package/package.json +6 -2
- package/src/lib/ambient.ts +19 -0
- package/src/lib/components/AdminLayout.svelte +6 -8
- package/src/lib/components/CairnAdmin.svelte +67 -0
- package/src/lib/components/ConceptList.svelte +4 -5
- package/src/lib/components/ConfirmPage.svelte +1 -1
- package/src/lib/components/EditPage.svelte +107 -25
- package/src/lib/components/EditorToolbar.svelte +79 -8
- package/src/lib/components/LoginPage.svelte +2 -2
- package/src/lib/components/ManageEditors.svelte +4 -3
- package/src/lib/components/MarkdownEditor.svelte +20 -2
- package/src/lib/components/cairn-admin.css +59 -0
- package/src/lib/components/editor-highlight.ts +32 -7
- package/src/lib/components/index.ts +1 -0
- package/src/lib/components/markdown-directives.ts +51 -1
- package/src/lib/components/markdown-format.ts +0 -27
- package/src/lib/components/preview-doc.ts +82 -0
- package/src/lib/content/compose.ts +1 -0
- package/src/lib/content/links.ts +28 -0
- package/src/lib/content/types.ts +34 -2
- package/src/lib/delivery/data.ts +3 -5
- package/src/lib/delivery/feeds.ts +1 -8
- package/src/lib/delivery/index.ts +2 -2
- package/src/lib/delivery/manifest.ts +5 -18
- package/src/lib/{sveltekit → delivery}/public-routes.ts +11 -11
- package/src/lib/delivery/site-indexes.ts +6 -6
- package/src/lib/delivery/{site-index.ts → site-resolver.ts} +20 -8
- package/src/lib/delivery/sitemap.ts +1 -4
- package/src/lib/delivery/xml.ts +12 -0
- package/src/lib/diagnostics/conditions.ts +24 -0
- package/src/lib/doctor/bin.ts +35 -10
- package/src/lib/doctor/check-floors.ts +124 -0
- package/src/lib/doctor/check-probe.ts +138 -0
- package/src/lib/doctor/checks-github.ts +3 -1
- package/src/lib/doctor/checks-local.ts +28 -2
- package/src/lib/doctor/cloudflare-api.ts +4 -2
- package/src/lib/doctor/index.ts +67 -6
- package/src/lib/doctor/types.ts +2 -0
- package/src/lib/doctor/wrangler-config.ts +11 -0
- package/src/lib/email.ts +4 -11
- package/src/lib/env.ts +12 -6
- package/src/lib/escape.ts +12 -0
- package/src/lib/github/credentials.ts +6 -2
- package/src/lib/github/types.ts +5 -0
- package/src/lib/index.ts +2 -0
- package/src/lib/log/events.ts +1 -0
- package/src/lib/nav/site-config.ts +3 -0
- package/src/lib/sveltekit/admin-dispatch.ts +75 -0
- package/src/lib/sveltekit/cairn-admin.ts +177 -0
- package/src/lib/sveltekit/condition-response.ts +27 -1
- package/src/lib/sveltekit/content-routes.ts +131 -62
- package/src/lib/sveltekit/guard.ts +20 -5
- package/src/lib/sveltekit/https-required-page.ts +2 -1
- package/src/lib/sveltekit/index.ts +6 -0
- package/src/lib/sveltekit/nav-routes.ts +24 -21
- package/src/lib/sveltekit/static-admin-page.ts +1 -9
- package/src/lib/sveltekit/types.ts +16 -7
- package/src/lib/vite/index.ts +71 -17
- package/dist/delivery/paginate.d.ts +0 -12
- package/dist/delivery/paginate.js +0 -20
- package/dist/render/index.d.ts +0 -5
- package/dist/render/index.js +0 -8
- package/src/lib/delivery/paginate.ts +0 -32
- package/src/lib/render/index.ts +0 -8
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ResolvedPreview } from '../content/types.js';
|
|
2
|
+
/** One width the preview frame can take. */
|
|
3
|
+
export interface PreviewDevice {
|
|
4
|
+
id: 'desktop' | 'tablet' | 'phone' | 'small';
|
|
5
|
+
/** The device menu label, also the frame caption's first half. */
|
|
6
|
+
label: string;
|
|
7
|
+
/** Frame width in CSS pixels; null fills the pane (Desktop). */
|
|
8
|
+
width: number | null;
|
|
9
|
+
}
|
|
10
|
+
/** A preview device's id, the value the page persists. */
|
|
11
|
+
export type PreviewDeviceId = PreviewDevice['id'];
|
|
12
|
+
/** The four widths the device menu offers, in menu order. Desktop leads as the default. */
|
|
13
|
+
export declare const previewDevices: PreviewDevice[];
|
|
14
|
+
/** The table row for a device id. The id type makes a miss impossible; the fallback satisfies find. */
|
|
15
|
+
export declare function previewDevice(id: PreviewDeviceId): PreviewDevice;
|
|
16
|
+
/** A device's user-facing text, shared by the toolbar's menu items and the frame caption: the
|
|
17
|
+
* label with its width when one is fixed, so the value reaches assistive tech at pick time. */
|
|
18
|
+
export declare function deviceLabel(d: PreviewDevice): string;
|
|
19
|
+
/**
|
|
20
|
+
* Build the preview iframe's srcdoc: a complete document linking the site's stylesheets around
|
|
21
|
+
* the rendered entry html. The html comes from the site's floored render pipeline, which already
|
|
22
|
+
* stripped scripts and event handlers, so it embeds unescaped; the frame's empty `sandbox` is
|
|
23
|
+
* belt and braces over that floor. The parameter is the flat `ResolvedPreview` shape `editLoad`
|
|
24
|
+
* ships, so the per-concept map can never reach the frame document by construction.
|
|
25
|
+
* `preview` null (a site without the adapter knob) yields a styleless but complete document.
|
|
26
|
+
*/
|
|
27
|
+
export declare function buildPreviewDoc(html: string, preview: ResolvedPreview | null): string;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// cairn-cms: the edit page's preview-frame document. The admin's chrome isolation keeps the
|
|
2
|
+
// site's CSS out of the admin document, so EditPage renders the preview inside a sandboxed
|
|
3
|
+
// iframe whose document links the site's own stylesheets from the adapter's preview knob. This
|
|
4
|
+
// module builds that iframe's srcdoc as one pure string, so its shape is unit-testable, and it
|
|
5
|
+
// carries the device table the frame's width control offers.
|
|
6
|
+
import { escapeHtml } from '../escape.js';
|
|
7
|
+
/** The four widths the device menu offers, in menu order. Desktop leads as the default. */
|
|
8
|
+
export const previewDevices = [
|
|
9
|
+
{ id: 'desktop', label: 'Desktop', width: null },
|
|
10
|
+
{ id: 'tablet', label: 'Tablet', width: 768 },
|
|
11
|
+
{ id: 'phone', label: 'Phone', width: 390 },
|
|
12
|
+
{ id: 'small', label: 'Small phone', width: 320 },
|
|
13
|
+
];
|
|
14
|
+
/** The table row for a device id. The id type makes a miss impossible; the fallback satisfies find. */
|
|
15
|
+
export function previewDevice(id) {
|
|
16
|
+
return previewDevices.find((d) => d.id === id) ?? previewDevices[0];
|
|
17
|
+
}
|
|
18
|
+
/** A device's user-facing text, shared by the toolbar's menu items and the frame caption: the
|
|
19
|
+
* label with its width when one is fixed, so the value reaches assistive tech at pick time. */
|
|
20
|
+
export function deviceLabel(d) {
|
|
21
|
+
return d.width === null ? d.label : `${d.label} · ${d.width} px`;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Build the preview iframe's srcdoc: a complete document linking the site's stylesheets around
|
|
25
|
+
* the rendered entry html. The html comes from the site's floored render pipeline, which already
|
|
26
|
+
* stripped scripts and event handlers, so it embeds unescaped; the frame's empty `sandbox` is
|
|
27
|
+
* belt and braces over that floor. The parameter is the flat `ResolvedPreview` shape `editLoad`
|
|
28
|
+
* ships, so the per-concept map can never reach the frame document by construction.
|
|
29
|
+
* `preview` null (a site without the adapter knob) yields a styleless but complete document.
|
|
30
|
+
*/
|
|
31
|
+
export function buildPreviewDoc(html, preview) {
|
|
32
|
+
const links = (preview?.stylesheets ?? [])
|
|
33
|
+
.map((href) => `<link rel="stylesheet" href="${escapeHtml(href)}">`)
|
|
34
|
+
.join('\n');
|
|
35
|
+
const bodyAttrs = preview?.bodyClass ? ` class="${escapeHtml(preview.bodyClass)}"` : '';
|
|
36
|
+
const content = preview?.containerClass
|
|
37
|
+
? `<div class="${escapeHtml(preview.containerClass)}">${html}</div>`
|
|
38
|
+
: html;
|
|
39
|
+
// The reset sits BEFORE the site links so the site's CSS wins every collision: it only clears
|
|
40
|
+
// the default body margin and pins a white ground for sheets that assume one.
|
|
41
|
+
//
|
|
42
|
+
// The base tag is what makes links inert. The empty sandbox alone does not: a sandboxed
|
|
43
|
+
// context may still navigate itself, and a srcdoc document resolves relative hrefs against the
|
|
44
|
+
// parent's base URL, so a clicked fragment or root link could render the admin login inside
|
|
45
|
+
// the frame. Targeting every link at a new tab turns each click into a popup, and the sandbox
|
|
46
|
+
// (which grants no allow-popups) blocks it, so a proofing click goes nowhere.
|
|
47
|
+
return [
|
|
48
|
+
'<!doctype html>',
|
|
49
|
+
'<html>',
|
|
50
|
+
'<head>',
|
|
51
|
+
'<meta charset="utf-8">',
|
|
52
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1">',
|
|
53
|
+
'<base target="_blank">',
|
|
54
|
+
'<style>body{margin:0;background:#fff}</style>',
|
|
55
|
+
links,
|
|
56
|
+
'</head>',
|
|
57
|
+
`<body${bodyAttrs}>`,
|
|
58
|
+
content,
|
|
59
|
+
'</body>',
|
|
60
|
+
'</html>',
|
|
61
|
+
]
|
|
62
|
+
.filter((line) => line !== '')
|
|
63
|
+
.join('\n');
|
|
64
|
+
}
|
package/dist/content/compose.js
CHANGED
package/dist/content/links.d.ts
CHANGED
|
@@ -18,3 +18,11 @@ export declare function escapeLinkText(text: string): string;
|
|
|
18
18
|
/** The cairn links a markdown body points at, in first-occurrence order, deduped by concept/id.
|
|
19
19
|
* Parses the body as mdast, so a token inside a code span or fence is never matched. */
|
|
20
20
|
export declare function extractCairnLinks(body: string): CairnRef[];
|
|
21
|
+
/**
|
|
22
|
+
* Rewrite every cairn: link whose href is exactly `oldHref` so its href becomes `newHref`, keeping
|
|
23
|
+
* the display text and any link title byte-for-byte. Rename calls this to repoint a renamed entry's
|
|
24
|
+
* inbound tokens. Parsed with the same remark pipeline as extractCairnLinks, so a token inside a code
|
|
25
|
+
* span is not a link node and is never touched. Each matching node's source span is rewritten from
|
|
26
|
+
* last to first, replacing only the `](oldHref` run so the label and title stay exact.
|
|
27
|
+
*/
|
|
28
|
+
export declare function rewriteCairnLink(doc: string, oldHref: string, newHref: string): string;
|
package/dist/content/links.js
CHANGED
|
@@ -50,3 +50,31 @@ export function extractCairnLinks(body) {
|
|
|
50
50
|
});
|
|
51
51
|
return refs;
|
|
52
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Rewrite every cairn: link whose href is exactly `oldHref` so its href becomes `newHref`, keeping
|
|
55
|
+
* the display text and any link title byte-for-byte. Rename calls this to repoint a renamed entry's
|
|
56
|
+
* inbound tokens. Parsed with the same remark pipeline as extractCairnLinks, so a token inside a code
|
|
57
|
+
* span is not a link node and is never touched. Each matching node's source span is rewritten from
|
|
58
|
+
* last to first, replacing only the `](oldHref` run so the label and title stay exact.
|
|
59
|
+
*/
|
|
60
|
+
export function rewriteCairnLink(doc, oldHref, newHref) {
|
|
61
|
+
const tree = unified().use(remarkParse).use(remarkGfm).parse(doc);
|
|
62
|
+
const spans = [];
|
|
63
|
+
visit(tree, 'link', (node) => {
|
|
64
|
+
if (node.url !== oldHref)
|
|
65
|
+
return;
|
|
66
|
+
const start = node.position?.start?.offset;
|
|
67
|
+
const end = node.position?.end?.offset;
|
|
68
|
+
if (start == null || end == null)
|
|
69
|
+
return;
|
|
70
|
+
spans.push({ start, end });
|
|
71
|
+
});
|
|
72
|
+
spans.sort((a, b) => b.start - a.start);
|
|
73
|
+
let out = doc;
|
|
74
|
+
for (const span of spans) {
|
|
75
|
+
const src = out.slice(span.start, span.end);
|
|
76
|
+
const rewritten = src.replace(`](${oldHref}`, `](${newHref}`);
|
|
77
|
+
out = out.slice(0, span.start) + rewritten + out.slice(span.end);
|
|
78
|
+
}
|
|
79
|
+
return out;
|
|
80
|
+
}
|
package/dist/content/types.d.ts
CHANGED
|
@@ -133,6 +133,34 @@ export interface NavMenuConfig {
|
|
|
133
133
|
/** Max nesting depth allowed in the editor; defaults to 2. */
|
|
134
134
|
maxDepth?: number;
|
|
135
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* How the edit page's preview frame reproduces the live site's content styling. The admin
|
|
138
|
+
* deliberately never loads the site's CSS (chrome isolation), so a design-accurate preview needs
|
|
139
|
+
* the site to name its stylesheets for the preview frame; without this knob the preview renders
|
|
140
|
+
* unstyled markup. The frame's srcdoc pins a white body background as a deliberately overridable
|
|
141
|
+
* default, so a site whose ground is not white should state its body background in its own
|
|
142
|
+
* stylesheet.
|
|
143
|
+
*/
|
|
144
|
+
export interface PreviewConfig {
|
|
145
|
+
/** Absolute or root-relative URLs of the site's compiled stylesheets, linked inside the
|
|
146
|
+
* preview document. A Vite `?url` import of the site's CSS resolves the hashed asset URL. */
|
|
147
|
+
stylesheets: string[];
|
|
148
|
+
/** Class list applied to the preview document's body, for theme or typography roots. */
|
|
149
|
+
bodyClass?: string;
|
|
150
|
+
/** Class list for a wrapper element around the rendered content, reproducing the site's
|
|
151
|
+
* content container (a prose or measure class). Omitted renders the content bare. */
|
|
152
|
+
containerClass?: string;
|
|
153
|
+
/** Per-concept overrides of bodyClass and containerClass, keyed by concept id. An entry's
|
|
154
|
+
* preview resolves the override for its concept over the top-level values; stylesheets are
|
|
155
|
+
* always shared. */
|
|
156
|
+
byConcept?: Record<string, {
|
|
157
|
+
bodyClass?: string;
|
|
158
|
+
containerClass?: string;
|
|
159
|
+
}>;
|
|
160
|
+
}
|
|
161
|
+
/** The flat preview shape `editLoad` ships to the edit page: the top-level `PreviewConfig`
|
|
162
|
+
* values with the entry's concept override applied, and no `byConcept` map. */
|
|
163
|
+
export type ResolvedPreview = Omit<PreviewConfig, 'byConcept'>;
|
|
136
164
|
/** Reserved asset slot (seam 4). Typed and unused in the rebuild; R7/R9 read it later with no contract change. */
|
|
137
165
|
export interface AssetConfig {
|
|
138
166
|
/** Repo-relative asset roots, e.g. ["static/images"]. */
|
|
@@ -154,8 +182,8 @@ export interface CairnAdapter {
|
|
|
154
182
|
backend: BackendConfig;
|
|
155
183
|
sender: SenderConfig;
|
|
156
184
|
/** The site's one renderer: the editor preview and every public page call it (design decision 4).
|
|
157
|
-
* `resolve` rewrites cairn: links to live permalinks; the build passes a site-
|
|
158
|
-
* preview a manifest one. */
|
|
185
|
+
* `resolve` rewrites cairn: links to live permalinks; the build passes a site-resolver-backed
|
|
186
|
+
* one, the preview a manifest one. */
|
|
159
187
|
render(md: string, opts?: {
|
|
160
188
|
stagger?: boolean;
|
|
161
189
|
resolve?: LinkResolve;
|
|
@@ -168,6 +196,9 @@ export interface CairnAdapter {
|
|
|
168
196
|
/** The site's glyph name to SVG path-data map, for the admin icon picker and the renderer. */
|
|
169
197
|
icons?: IconSet;
|
|
170
198
|
navMenu?: NavMenuConfig;
|
|
199
|
+
/** The live site's content styling for the preview frame. The admin's chrome isolation keeps
|
|
200
|
+
* the site's CSS out of the admin document, so the preview frame links these instead. */
|
|
201
|
+
preview?: PreviewConfig;
|
|
171
202
|
assets?: AssetConfig;
|
|
172
203
|
}
|
|
173
204
|
/**
|
|
@@ -263,6 +294,8 @@ export interface CairnRuntime {
|
|
|
263
294
|
/** The site's glyph name to SVG path-data map, for the admin icon picker and the renderer. */
|
|
264
295
|
icons?: IconSet;
|
|
265
296
|
navMenu?: NavMenuConfig;
|
|
297
|
+
/** The live site's content styling for the preview frame; passed through from the adapter. */
|
|
298
|
+
preview?: PreviewConfig;
|
|
266
299
|
assets?: AssetConfig;
|
|
267
300
|
/** Admin panels contributed by extensions (Mode 2). Empty until Plan 09 wires the dispatch route. */
|
|
268
301
|
adminPanels?: AdminPanel[];
|
package/dist/delivery/data.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { createContentIndex, fromGlob } from './content-index.js';
|
|
2
2
|
export type { RawFile, ContentSummary, ContentEntry, ContentIndex, ContentProblem } from './content-index.js';
|
|
3
|
-
export {
|
|
4
|
-
export type {
|
|
3
|
+
export { createSiteResolver, buildLinkResolver } from './site-resolver.js';
|
|
4
|
+
export type { SiteResolver, ConceptIndex } from './site-resolver.js';
|
|
5
5
|
export { createSiteIndexes } from './site-indexes.js';
|
|
6
6
|
export type { SiteIndexes, SiteGlobs } from './site-indexes.js';
|
|
7
7
|
export { siteDescriptors } from './site-descriptors.js';
|
|
@@ -15,9 +15,7 @@ export { buildSeoMeta } from './seo.js';
|
|
|
15
15
|
export type { SeoInput, SeoMeta } from './seo.js';
|
|
16
16
|
export { readSeoFields, resolveImageUrl } from './seo-fields.js';
|
|
17
17
|
export type { SeoFields } from './seo-fields.js';
|
|
18
|
-
export { paginate } from './paginate.js';
|
|
19
|
-
export type { Page } from './paginate.js';
|
|
20
18
|
export { rssResponse, jsonFeedResponse, sitemapResponse, robotsResponse } from './responses.js';
|
|
21
19
|
export { jsonLdScript } from './json-ld.js';
|
|
22
20
|
export { permalink } from '../content/permalink.js';
|
|
23
|
-
export { buildSiteManifest
|
|
21
|
+
export { buildSiteManifest } from './manifest.js';
|
package/dist/delivery/data.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// projections a SvelteKit site or a plain-Node tool reads, with no @sveltejs/kit and no .svelte in
|
|
3
3
|
// the graph. The full ./delivery barrel re-exports this and adds the route loaders.
|
|
4
4
|
export { createContentIndex, fromGlob } from './content-index.js';
|
|
5
|
-
export {
|
|
5
|
+
export { createSiteResolver, buildLinkResolver } from './site-resolver.js';
|
|
6
6
|
export { createSiteIndexes } from './site-indexes.js';
|
|
7
7
|
export { siteDescriptors } from './site-descriptors.js';
|
|
8
8
|
export { deriveExcerpt, wordCount } from './excerpt.js';
|
|
@@ -11,8 +11,7 @@ export { buildSitemap } from './sitemap.js';
|
|
|
11
11
|
export { buildRobots } from './robots.js';
|
|
12
12
|
export { buildSeoMeta } from './seo.js';
|
|
13
13
|
export { readSeoFields, resolveImageUrl } from './seo-fields.js';
|
|
14
|
-
export { paginate } from './paginate.js';
|
|
15
14
|
export { rssResponse, jsonFeedResponse, sitemapResponse, robotsResponse } from './responses.js';
|
|
16
15
|
export { jsonLdScript } from './json-ld.js';
|
|
17
16
|
export { permalink } from '../content/permalink.js';
|
|
18
|
-
export { buildSiteManifest
|
|
17
|
+
export { buildSiteManifest } from './manifest.js';
|
package/dist/delivery/feeds.js
CHANGED
|
@@ -2,13 +2,7 @@
|
|
|
2
2
|
// channel and a list of items, so they unit-test without a render or a network. The caller
|
|
3
3
|
// (a template +server.ts shim) assembles items from the content index and passes absolute
|
|
4
4
|
// URLs built from PUBLIC_ORIGIN.
|
|
5
|
-
|
|
6
|
-
return value
|
|
7
|
-
.replace(/&/g, '&')
|
|
8
|
-
.replace(/</g, '<')
|
|
9
|
-
.replace(/>/g, '>')
|
|
10
|
-
.replace(/"/g, '"');
|
|
11
|
-
}
|
|
5
|
+
import { escapeXml } from './xml.js';
|
|
12
6
|
/** Make a string safe inside a CDATA section by splitting any `]]>` across two sections. */
|
|
13
7
|
function cdataSafe(value) {
|
|
14
8
|
return value.replace(/]]>/g, ']]]]><![CDATA[>');
|
package/dist/delivery/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export * from './data.js';
|
|
2
|
-
export { createPublicRoutes } from '
|
|
3
|
-
export type { PublicRoutesDeps, ListData, TagData, TagIndexData, EntryData, } from '
|
|
2
|
+
export { createPublicRoutes } from './public-routes.js';
|
|
3
|
+
export type { PublicRoutesDeps, ListData, TagData, TagIndexData, EntryData, } from './public-routes.js';
|
package/dist/delivery/index.js
CHANGED
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
// lives at ./delivery/head. Importing this pulls @sveltejs/kit through the route loaders, so a
|
|
4
4
|
// plain-Node tool imports from ./delivery/data instead.
|
|
5
5
|
export * from './data.js';
|
|
6
|
-
export { createPublicRoutes } from '
|
|
6
|
+
export { createPublicRoutes } from './public-routes.js';
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import type { Manifest } from '../content/manifest.js';
|
|
2
|
-
import type { LinkResolve } from '../content/links.js';
|
|
3
|
-
import type { SiteIndex } from './site-index.js';
|
|
4
2
|
import type { SiteConfig } from '../nav/site-config.js';
|
|
5
3
|
import type { CairnAdapter } from '../content/types.js';
|
|
6
4
|
import type { SiteGlobs } from './site-indexes.js';
|
|
7
5
|
/** Build the whole-corpus manifest from a site's adapter, config, and per-concept globs. Drafts are
|
|
8
6
|
* included and flagged, so the admin picker and the guards see the full graph. */
|
|
9
7
|
export declare function buildSiteManifest<A extends CairnAdapter>(adapter: A, config: SiteConfig, globs: SiteGlobs<A>): Manifest;
|
|
10
|
-
/** A resolver backed by the site index, for the build. A miss throws, so a dangling cairn: token
|
|
11
|
-
* fails the prerender (the build backstop). The preview uses manifestLinkResolver, which marks. */
|
|
12
|
-
export declare function buildLinkResolver(site: SiteIndex): LinkResolve;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
// cairn-cms: the build-side manifest builder
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// the build (the backstop). The admin preview uses manifestLinkResolver instead.
|
|
1
|
+
// cairn-cms: the build-side manifest builder (content-graph design). buildSiteManifest mirrors
|
|
2
|
+
// createSiteIndexes: it maps the site descriptors over the per-concept globs and projects each
|
|
3
|
+
// file to a manifest row. The build-time cairn: link resolver lives beside the site resolver in
|
|
4
|
+
// site-resolver.ts; the admin preview uses manifestLinkResolver instead.
|
|
6
5
|
import { siteDescriptors } from './site-descriptors.js';
|
|
7
6
|
import { fromGlob } from './content-index.js';
|
|
8
7
|
import { parseMarkdown } from '../content/frontmatter.js';
|
|
@@ -15,7 +14,7 @@ export function buildSiteManifest(adapter, config, globs) {
|
|
|
15
14
|
for (const descriptor of siteDescriptors(adapter, config)) {
|
|
16
15
|
const record = globRecord[descriptor.id] ?? {};
|
|
17
16
|
for (const file of fromGlob(record)) {
|
|
18
|
-
// Validate the same way createContentIndex does, so the manifest and the site
|
|
17
|
+
// Validate the same way createContentIndex does, so the manifest and the site resolver agree on
|
|
19
18
|
// which entries exist. A validation failure is excluded from both; otherwise the preview would
|
|
20
19
|
// resolve a link the build then rejects as a missing target.
|
|
21
20
|
const { frontmatter, body } = parseMarkdown(file.raw);
|
|
@@ -26,13 +25,3 @@ export function buildSiteManifest(adapter, config, globs) {
|
|
|
26
25
|
}
|
|
27
26
|
return manifest;
|
|
28
27
|
}
|
|
29
|
-
/** A resolver backed by the site index, for the build. A miss throws, so a dangling cairn: token
|
|
30
|
-
* fails the prerender (the build backstop). The preview uses manifestLinkResolver, which marks. */
|
|
31
|
-
export function buildLinkResolver(site) {
|
|
32
|
-
return (ref) => {
|
|
33
|
-
const url = site.concept(ref.concept)?.byId(ref.id)?.permalink;
|
|
34
|
-
if (!url)
|
|
35
|
-
throw new Error(`cairn link target not found: cairn:${ref.concept}/${ref.id}`);
|
|
36
|
-
return url;
|
|
37
|
-
};
|
|
38
|
-
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type { ContentSummary, ContentEntry } from '
|
|
2
|
-
import type {
|
|
3
|
-
import type { SeoMeta } from '
|
|
1
|
+
import type { ContentSummary, ContentEntry } from './content-index.js';
|
|
2
|
+
import type { SiteResolver } from './site-resolver.js';
|
|
3
|
+
import type { SeoMeta } from './seo.js';
|
|
4
4
|
import type { LinkResolve } from '../content/links.js';
|
|
5
5
|
/** Injected dependencies for the public loaders. */
|
|
6
6
|
export interface PublicRoutesDeps {
|
|
7
|
-
site:
|
|
7
|
+
site: SiteResolver;
|
|
8
8
|
render: (md: string, opts?: {
|
|
9
9
|
stagger?: boolean;
|
|
10
10
|
resolve?: LinkResolve;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
// cairn-cms: public route loaders (dated-slug design). The factory closes over the site
|
|
2
|
-
//
|
|
1
|
+
// cairn-cms: public route loaders (dated-slug design). The factory closes over the site
|
|
2
|
+
// resolver, the runtime render, and the origin. entryLoad and entries are site-wide: one catch-all
|
|
3
3
|
// `[...path]` route resolves any concept by request path through `byPermalink`. The archive, tag,
|
|
4
|
-
// and tag-index loaders stay concept-scoped, keyed by concept id. The
|
|
5
|
-
// from globs, so it stays in the prerender graph and out of the runtime Worker.
|
|
4
|
+
// and tag-index loaders stay concept-scoped, keyed by concept id. The resolver is built in site
|
|
5
|
+
// code from globs, so it stays in the prerender graph and out of the runtime Worker.
|
|
6
6
|
import { error } from '@sveltejs/kit';
|
|
7
|
-
import { buildSeoMeta } from '
|
|
8
|
-
import { readSeoFields, resolveImageUrl } from '
|
|
9
|
-
import { buildLinkResolver } from '
|
|
7
|
+
import { buildSeoMeta } from './seo.js';
|
|
8
|
+
import { readSeoFields, resolveImageUrl } from './seo-fields.js';
|
|
9
|
+
import { buildLinkResolver } from './site-resolver.js';
|
|
10
10
|
/** Build the public loaders for a site's unified index. */
|
|
11
11
|
export function createPublicRoutes(deps) {
|
|
12
12
|
const { site, render, origin, siteName, description, feeds, defaultImage } = deps;
|
|
@@ -2,7 +2,7 @@ import type { CairnAdapter, ConceptConfig } from '../content/types.js';
|
|
|
2
2
|
import type { Infer } from '../content/schema.js';
|
|
3
3
|
import type { SiteConfig } from '../nav/site-config.js';
|
|
4
4
|
import type { ContentIndex } from './content-index.js';
|
|
5
|
-
import type {
|
|
5
|
+
import type { SiteResolver } from './site-resolver.js';
|
|
6
6
|
/** A per-concept raw glob record (`{ path: raw }`) keyed by concept id, from `import.meta.glob`. */
|
|
7
7
|
export type SiteGlobs<A extends CairnAdapter> = {
|
|
8
8
|
[K in keyof A['content']]?: Record<string, string>;
|
|
@@ -12,13 +12,13 @@ export type SiteGlobs<A extends CairnAdapter> = {
|
|
|
12
12
|
export type SiteIndexes<A extends CairnAdapter> = {
|
|
13
13
|
[K in keyof A['content']]: ContentIndex<NonNullable<A['content'][K]> extends ConceptConfig<infer S> ? Infer<S> : Record<string, unknown>>;
|
|
14
14
|
} & {
|
|
15
|
-
readonly site:
|
|
15
|
+
readonly site: SiteResolver;
|
|
16
16
|
};
|
|
17
17
|
/**
|
|
18
18
|
* Build typed per-concept indexes and a site resolver from one adapter. Pass the per-concept raw
|
|
19
19
|
* globs as `{ posts: import.meta.glob('...?raw', { eager: true }), ... }`; Vite needs the literal
|
|
20
20
|
* glob at the call site, so the engine cannot glob on the site's behalf. `validate: false` opts out
|
|
21
|
-
* of the build gate, exactly as on `
|
|
21
|
+
* of the build gate, exactly as on `createSiteResolver`.
|
|
22
22
|
*/
|
|
23
23
|
export declare function createSiteIndexes<const A extends CairnAdapter>(adapter: A, config: SiteConfig, globs: SiteGlobs<A>, opts?: {
|
|
24
24
|
validate?: boolean;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { siteDescriptors } from './site-descriptors.js';
|
|
2
2
|
import { createContentIndex, fromGlob } from './content-index.js';
|
|
3
|
-
import {
|
|
3
|
+
import { createSiteResolver } from './site-resolver.js';
|
|
4
4
|
/**
|
|
5
5
|
* Build typed per-concept indexes and a site resolver from one adapter. Pass the per-concept raw
|
|
6
6
|
* globs as `{ posts: import.meta.glob('...?raw', { eager: true }), ... }`; Vite needs the literal
|
|
7
7
|
* glob at the call site, so the engine cannot glob on the site's behalf. `validate: false` opts out
|
|
8
|
-
* of the build gate, exactly as on `
|
|
8
|
+
* of the build gate, exactly as on `createSiteResolver`.
|
|
9
9
|
*/
|
|
10
10
|
export function createSiteIndexes(adapter, config, globs, opts = {}) {
|
|
11
11
|
const descriptors = siteDescriptors(adapter, config);
|
|
@@ -25,6 +25,6 @@ export function createSiteIndexes(adapter, config, globs, opts = {}) {
|
|
|
25
25
|
byConcept[descriptor.id] = index;
|
|
26
26
|
conceptIndexes.push({ descriptor, index });
|
|
27
27
|
}
|
|
28
|
-
const site =
|
|
28
|
+
const site = createSiteResolver(conceptIndexes, opts);
|
|
29
29
|
return { ...byConcept, site };
|
|
30
30
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import type { ConceptDescriptor } from '../content/types.js';
|
|
2
2
|
import type { ContentEntry, ContentIndex, ContentSummary } from './content-index.js';
|
|
3
|
+
import type { LinkResolve } from '../content/links.js';
|
|
3
4
|
/** One concept's descriptor paired with its built index. */
|
|
4
5
|
export interface ConceptIndex {
|
|
5
6
|
descriptor: ConceptDescriptor;
|
|
6
7
|
index: ContentIndex;
|
|
7
8
|
}
|
|
8
9
|
/** The cross-concept query surface a catch-all route and the sitemap read. */
|
|
9
|
-
export interface
|
|
10
|
+
export interface SiteResolver {
|
|
10
11
|
/** Resolve a request path (with or without a trailing slash) to its entry, or undefined. */
|
|
11
12
|
byPermalink(path: string): ContentEntry | undefined;
|
|
12
13
|
/** Newer/older neighbors within the entry's own concept, for prev/next links. */
|
|
@@ -28,6 +29,9 @@ export interface SiteIndex {
|
|
|
28
29
|
* unless `validate` is `false`, on any non-draft entry whose frontmatter fails its concept's
|
|
29
30
|
* validator, so malformed content fails the build instead of shipping.
|
|
30
31
|
*/
|
|
31
|
-
export declare function
|
|
32
|
+
export declare function createSiteResolver(concepts: ConceptIndex[], opts?: {
|
|
32
33
|
validate?: boolean;
|
|
33
|
-
}):
|
|
34
|
+
}): SiteResolver;
|
|
35
|
+
/** A resolver backed by the site resolver, for the build. A miss throws, so a dangling cairn: token
|
|
36
|
+
* fails the prerender (the build backstop). The preview uses manifestLinkResolver, which marks. */
|
|
37
|
+
export declare function buildLinkResolver(site: SiteResolver): LinkResolve;
|
|
@@ -21,11 +21,11 @@ function siteProblems(concepts) {
|
|
|
21
21
|
* unless `validate` is `false`, on any non-draft entry whose frontmatter fails its concept's
|
|
22
22
|
* validator, so malformed content fails the build instead of shipping.
|
|
23
23
|
*/
|
|
24
|
-
export function
|
|
24
|
+
export function createSiteResolver(concepts, opts = {}) {
|
|
25
25
|
if (opts.validate !== false) {
|
|
26
26
|
const problems = siteProblems(concepts);
|
|
27
27
|
if (problems.length > 0) {
|
|
28
|
-
throw new Error(`site
|
|
28
|
+
throw new Error(`site resolver: ${problems.length} invalid frontmatter field(s):\n ${problems.join('\n ')}`);
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
const byPath = new Map();
|
|
@@ -35,7 +35,7 @@ export function createSiteIndex(concepts, opts = {}) {
|
|
|
35
35
|
for (const summary of index.all()) {
|
|
36
36
|
const existing = byPath.get(summary.permalink);
|
|
37
37
|
if (existing) {
|
|
38
|
-
throw new Error(`site
|
|
38
|
+
throw new Error(`site resolver: "${existing.id}" and "${summary.id}" both resolve to "${summary.permalink}"`);
|
|
39
39
|
}
|
|
40
40
|
byPath.set(summary.permalink, { index, id: summary.id });
|
|
41
41
|
}
|
|
@@ -60,3 +60,13 @@ export function createSiteIndex(concepts, opts = {}) {
|
|
|
60
60
|
},
|
|
61
61
|
};
|
|
62
62
|
}
|
|
63
|
+
/** A resolver backed by the site resolver, for the build. A miss throws, so a dangling cairn: token
|
|
64
|
+
* fails the prerender (the build backstop). The preview uses manifestLinkResolver, which marks. */
|
|
65
|
+
export function buildLinkResolver(site) {
|
|
66
|
+
return (ref) => {
|
|
67
|
+
const url = site.concept(ref.concept)?.byId(ref.id)?.permalink;
|
|
68
|
+
if (!url)
|
|
69
|
+
throw new Error(`cairn link target not found: cairn:${ref.concept}/${ref.id}`);
|
|
70
|
+
return url;
|
|
71
|
+
};
|
|
72
|
+
}
|
package/dist/delivery/sitemap.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
// cairn-cms: sitemap builder (public-delivery design). Pure over a URL list; the caller
|
|
2
2
|
// derives the list from the content index and the routable concepts.
|
|
3
|
-
|
|
4
|
-
return value.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
5
|
-
}
|
|
3
|
+
import { escapeXml } from './xml.js';
|
|
6
4
|
/** Build a sitemap XML document from a list of URLs. */
|
|
7
5
|
export function buildSitemap(urls) {
|
|
8
6
|
const entries = urls
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// cairn-cms: the one XML text escape the feed and sitemap builders share. The strongest of the
|
|
2
|
+
// two copies it replaced (the old sitemap copy skipped quotes), so both documents stay safe in
|
|
3
|
+
// element text and double-quoted attributes.
|
|
4
|
+
/** Escape the XML-significant characters for element text and double-quoted attribute values. */
|
|
5
|
+
export function escapeXml(value) {
|
|
6
|
+
return value
|
|
7
|
+
.replaceAll('&', '&')
|
|
8
|
+
.replaceAll('<', '<')
|
|
9
|
+
.replaceAll('>', '>')
|
|
10
|
+
.replaceAll('"', '"');
|
|
11
|
+
}
|
|
@@ -69,6 +69,14 @@ export const REGISTRY = {
|
|
|
69
69
|
remediation: "Set csrf: { checkOrigin: false } in svelte.config.js and wire createAuthGuard into src/hooks.server.ts; cairn's guard owns the Origin and double-submit token checks.",
|
|
70
70
|
docsAnchor: 'cloudflare-readiness.md#hand-cairn-the-csrf-authority',
|
|
71
71
|
},
|
|
72
|
+
'config.public-origin-invalid': {
|
|
73
|
+
id: 'config.public-origin-invalid',
|
|
74
|
+
severity: 'blocker',
|
|
75
|
+
title: 'PUBLIC_ORIGIN is missing or invalid',
|
|
76
|
+
why: 'PUBLIC_ORIGIN is unset, does not parse as a URL, or uses http on a non-local host. The magic-link confirmation links and the absolute feed URLs derive from it, config-only so a forged Host header cannot redirect a link, and sign-in cannot mint a usable link without it.',
|
|
77
|
+
remediation: "Set PUBLIC_ORIGIN to the site's canonical https origin in the wrangler config vars (with .dev.vars carrying the local http override), then re-deploy; http passes only on localhost or 127.0.0.1.",
|
|
78
|
+
docsAnchor: 'cloudflare-readiness.md#set-the-public-origin',
|
|
79
|
+
},
|
|
72
80
|
'config.site-config-invalid': {
|
|
73
81
|
id: 'config.site-config-invalid',
|
|
74
82
|
severity: 'blocker',
|
|
@@ -77,6 +85,14 @@ export const REGISTRY = {
|
|
|
77
85
|
remediation: 'Correct site.config.yaml; the parse or validation error names the failing field or URL-policy rule.',
|
|
78
86
|
docsAnchor: 'cloudflare-readiness.md#validate-the-site-config',
|
|
79
87
|
},
|
|
88
|
+
'config.dependency-floors-unmet': {
|
|
89
|
+
id: 'config.dependency-floors-unmet',
|
|
90
|
+
severity: 'blocker',
|
|
91
|
+
title: 'A framework dependency sits below the engine floor',
|
|
92
|
+
why: 'The lockfile resolves svelte or @sveltejs/kit below the range the engine declares as a peer. Consumer sites compile the shipped .svelte sources, so a below-floor compiler bites silently at build time; svelte 5.56.1 miscompiles parenthesized boolean groupings, which is why the svelte floor is ^5.56.3.',
|
|
93
|
+
remediation: "Raise the devDependency range in the site's package.json to the engine peer range and reinstall so the lockfile re-resolves, for example `npm install --save-dev svelte@^5.56.3`.",
|
|
94
|
+
docsAnchor: 'cloudflare-readiness.md#meet-the-dependency-floors',
|
|
95
|
+
},
|
|
80
96
|
'edge.hsts-off': {
|
|
81
97
|
id: 'edge.hsts-off',
|
|
82
98
|
severity: 'warning',
|
|
@@ -102,6 +118,14 @@ export const REGISTRY = {
|
|
|
102
118
|
docsAnchor: 'cloudflare-readiness.md#install-the-github-app',
|
|
103
119
|
logEvent: 'github.unreachable',
|
|
104
120
|
},
|
|
121
|
+
'admin.login-probe-failed': {
|
|
122
|
+
id: 'admin.login-probe-failed',
|
|
123
|
+
severity: 'blocker',
|
|
124
|
+
title: 'Live admin login probe failed',
|
|
125
|
+
why: 'A live request to the deployed admin did not answer with the working sign-in envelope (the login page, its CSRF cookie and hidden field, and the request action), so a real editor cannot sign in either. A probe failure has many possible causes; the detail line names the assertion that failed.',
|
|
126
|
+
remediation: 'Read the failed assertion in the detail line, run the full doctor against the same site, and work through the deploy guide; the other checks narrow the cause.',
|
|
127
|
+
docsAnchor: 'cloudflare-readiness.md#probe-the-deployed-admin',
|
|
128
|
+
},
|
|
105
129
|
};
|
|
106
130
|
// The registry is shared identity, never working state; freeze every entry and the map itself.
|
|
107
131
|
for (const entry of Object.values(REGISTRY))
|
package/dist/doctor/bin.js
CHANGED
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
// before the process ends.
|
|
8
8
|
import { readFile } from 'node:fs/promises';
|
|
9
9
|
import { resolve } from 'node:path';
|
|
10
|
+
import { liveProbeCheck } from './check-probe.js';
|
|
10
11
|
import { liveSendCheck } from './check-send.js';
|
|
11
|
-
import {
|
|
12
|
+
import { readWranglerConfig } from './wrangler-config.js';
|
|
13
|
+
import { contextFromEnv, defaultChecks, deriveMissingInputs, formatReport, parseArgs, runDoctor, } from './index.js';
|
|
12
14
|
async function main() {
|
|
13
15
|
let args;
|
|
14
16
|
try {
|
|
@@ -20,23 +22,39 @@ async function main() {
|
|
|
20
22
|
return;
|
|
21
23
|
}
|
|
22
24
|
const cwd = process.cwd();
|
|
25
|
+
const readFileUnderCwd = async (relPath) => {
|
|
26
|
+
try {
|
|
27
|
+
return await readFile(resolve(cwd, relPath), 'utf8');
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
if (err.code === 'ENOENT')
|
|
31
|
+
return null;
|
|
32
|
+
throw err;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
// Fill inputs the flags and env left missing from the repo itself: from and repo off the
|
|
36
|
+
// adapter (through the vite arm, which exists only on this bin path, never in a Worker)
|
|
37
|
+
// and the account id off the wrangler config. The API token stays env-only.
|
|
38
|
+
const derived = await deriveMissingInputs(contextFromEnv(process.env, args, cwd), {
|
|
39
|
+
adapterFacts: async () => {
|
|
40
|
+
const { readAdapterFacts } = await import('../vite/index.js');
|
|
41
|
+
return readAdapterFacts(cwd);
|
|
42
|
+
},
|
|
43
|
+
wranglerAccountId: async () => (await readWranglerConfig(readFileUnderCwd))?.accountId,
|
|
44
|
+
});
|
|
23
45
|
const ctx = {
|
|
24
|
-
...
|
|
46
|
+
...derived,
|
|
25
47
|
fetch: globalThis.fetch,
|
|
26
|
-
readFile:
|
|
27
|
-
try {
|
|
28
|
-
return await readFile(resolve(cwd, relPath), 'utf8');
|
|
29
|
-
}
|
|
30
|
-
catch (err) {
|
|
31
|
-
if (err.code === 'ENOENT')
|
|
32
|
-
return null;
|
|
33
|
-
throw err;
|
|
34
|
-
}
|
|
35
|
-
},
|
|
48
|
+
readFile: readFileUnderCwd,
|
|
36
49
|
};
|
|
37
50
|
const checks = defaultChecks();
|
|
38
51
|
if (args.sendTest)
|
|
39
52
|
checks.push(liveSendCheck(args.sendTest));
|
|
53
|
+
// The probe is an opt-in network POST against a live site, so it joins only on --probe;
|
|
54
|
+
// the bare flag hands the URL resolution (the PUBLIC_ORIGIN input) to the check itself.
|
|
55
|
+
if (args.probe !== undefined) {
|
|
56
|
+
checks.push(liveProbeCheck(args.probe === true ? undefined : args.probe));
|
|
57
|
+
}
|
|
40
58
|
const { results, failed } = await runDoctor(checks, ctx);
|
|
41
59
|
console.log(formatReport(results));
|
|
42
60
|
process.exitCode = failed > 0 ? 1 : 0;
|