@glw907/cairn-cms 0.60.0 → 0.62.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/CHANGELOG.md +82 -0
- package/dist/components/AdminLayout.svelte +152 -229
- package/dist/components/CairnAdmin.svelte +13 -42
- package/dist/components/CairnLogo.svelte +1 -6
- package/dist/components/CairnMediaLibrary.svelte +821 -1210
- package/dist/components/CairnTidySettings.svelte +194 -261
- package/dist/components/CairnTidySettings.svelte.d.ts +1 -1
- package/dist/components/ComponentForm.svelte +110 -185
- package/dist/components/ComponentInsertDialog.svelte +163 -283
- package/dist/components/ConceptList.svelte +111 -191
- package/dist/components/ConfirmPage.svelte +5 -12
- package/dist/components/CsrfField.svelte +5 -11
- package/dist/components/DeleteDialog.svelte +15 -42
- package/dist/components/EditPage.svelte +781 -1205
- package/dist/components/EditorToolbar.svelte +108 -170
- package/dist/components/HelpHome.svelte +824 -0
- package/dist/components/HelpHome.svelte.d.ts +22 -0
- package/dist/components/IconPicker.svelte +23 -53
- package/dist/components/LinkPicker.svelte +34 -58
- package/dist/components/LoginPage.svelte +14 -27
- package/dist/components/ManageEditors.svelte +3 -15
- package/dist/components/MarkdownEditor.svelte +689 -957
- package/dist/components/MarkdownHelpDialog.svelte +12 -27
- package/dist/components/MediaCaptureCard.svelte +18 -57
- package/dist/components/MediaFigureControl.svelte +32 -71
- package/dist/components/MediaHeroField.svelte +210 -329
- package/dist/components/MediaInsertPopover.svelte +156 -283
- package/dist/components/MediaPicker.svelte +67 -131
- package/dist/components/NavTree.svelte +46 -78
- package/dist/components/RenameDialog.svelte +16 -43
- package/dist/components/ShortcutsDialog.svelte +9 -13
- package/dist/components/ShortcutsGrid.svelte +1 -2
- package/dist/components/TidyReview.svelte +140 -248
- package/dist/components/WebLinkDialog.svelte +19 -40
- package/dist/components/cairn-admin.css +4 -0
- package/dist/components/client-ingest.d.ts +16 -8
- package/dist/components/client-ingest.js +12 -6
- package/dist/components/editor-media.js +16 -8
- package/dist/components/editor-placeholder.d.ts +4 -2
- package/dist/components/editor-tidy.d.ts +24 -12
- package/dist/components/editor-tidy.js +8 -4
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +1 -0
- package/dist/components/link-completion.d.ts +12 -6
- package/dist/components/link-completion.js +12 -6
- package/dist/components/markdown-directives.d.ts +9 -6
- package/dist/components/markdown-directives.js +9 -6
- package/dist/components/markdown-format.d.ts +7 -2
- package/dist/components/markdown-format.js +59 -28
- package/dist/components/markdown-reference.d.ts +8 -0
- package/dist/components/markdown-reference.js +22 -0
- package/dist/components/media-upload-outcome.d.ts +12 -6
- package/dist/components/objective-errors.d.ts +8 -4
- package/dist/components/objective-errors.js +8 -4
- package/dist/components/preview-doc.d.ts +4 -2
- package/dist/components/preview-doc.js +4 -2
- package/dist/components/spellcheck.d.ts +57 -29
- package/dist/components/spellcheck.js +50 -20
- package/dist/components/tidy-categorize.d.ts +20 -10
- package/dist/components/tidy-categorize.js +16 -8
- package/dist/components/tidy-validate.d.ts +12 -6
- package/dist/components/tidy-validate.js +20 -10
- package/dist/components/topbar-context.d.ts +4 -2
- package/dist/content/advisories.d.ts +51 -0
- package/dist/content/advisories.js +79 -0
- package/dist/content/compose.d.ts +4 -2
- package/dist/content/compose.js +1 -0
- package/dist/content/excerpt.js +4 -2
- package/dist/content/getting-started.d.ts +18 -0
- package/dist/content/getting-started.js +12 -0
- package/dist/content/links.d.ts +16 -8
- package/dist/content/links.js +12 -6
- package/dist/content/manifest.d.ts +36 -18
- package/dist/content/manifest.js +32 -16
- package/dist/content/media-refs.d.ts +4 -2
- package/dist/content/media-refs.js +4 -2
- package/dist/content/media-rewrite.d.ts +8 -4
- package/dist/content/media-rewrite.js +76 -38
- package/dist/content/schema.d.ts +20 -10
- package/dist/content/site-dictionary.d.ts +4 -2
- package/dist/content/site-dictionary.js +8 -4
- package/dist/content/types.d.ts +97 -42
- package/dist/delivery/CairnHead.svelte +8 -11
- package/dist/delivery/content-index.d.ts +16 -8
- package/dist/delivery/feeds.js +4 -2
- package/dist/delivery/json-ld.d.ts +3 -0
- package/dist/delivery/json-ld.js +3 -0
- package/dist/delivery/manifest.d.ts +4 -2
- package/dist/delivery/manifest.js +4 -2
- package/dist/delivery/public-routes.d.ts +12 -6
- package/dist/delivery/public-routes.js +4 -2
- package/dist/delivery/seo-fields.d.ts +12 -6
- package/dist/delivery/seo-fields.js +8 -4
- package/dist/delivery/site-indexes.d.ts +4 -2
- package/dist/delivery/site-resolver.d.ts +4 -2
- package/dist/delivery/site-resolver.js +4 -2
- package/dist/doctor/cloudflare-api.d.ts +6 -0
- package/dist/doctor/cloudflare-api.js +6 -0
- package/dist/doctor/index.d.ts +12 -6
- package/dist/doctor/report.d.ts +3 -0
- package/dist/doctor/report.js +3 -0
- package/dist/doctor/run.d.ts +3 -0
- package/dist/doctor/run.js +3 -0
- package/dist/doctor/types.d.ts +10 -2
- package/dist/doctor/types.js +6 -0
- package/dist/doctor/wrangler-config.d.ts +7 -2
- package/dist/doctor/wrangler-config.js +3 -0
- package/dist/email.d.ts +4 -2
- package/dist/env.d.ts +0 -3
- package/dist/env.js +0 -3
- package/dist/github/branches.d.ts +4 -2
- package/dist/github/branches.js +4 -2
- package/dist/github/signing.d.ts +1 -1
- package/dist/github/signing.js +2 -2
- package/dist/log/events.d.ts +1 -1
- package/dist/media/bulk-delete-plan.d.ts +8 -4
- package/dist/media/config.d.ts +12 -6
- package/dist/media/config.js +16 -8
- package/dist/media/delivery-bucket.d.ts +4 -2
- package/dist/media/library-entry.d.ts +4 -2
- package/dist/media/library-entry.js +4 -2
- package/dist/media/manifest.d.ts +29 -15
- package/dist/media/manifest.js +29 -16
- package/dist/media/naming.d.ts +12 -6
- package/dist/media/naming.js +24 -12
- package/dist/media/orphan-scan.d.ts +4 -2
- package/dist/media/reconcile.d.ts +21 -11
- package/dist/media/reconcile.js +12 -6
- package/dist/media/reference.d.ts +8 -4
- package/dist/media/reference.js +12 -6
- package/dist/media/rewrite-plan.d.ts +12 -6
- package/dist/media/sniff.d.ts +4 -2
- package/dist/media/sniff.js +28 -14
- package/dist/media/store.d.ts +16 -8
- package/dist/media/store.js +4 -2
- package/dist/media/transform-url.d.ts +12 -6
- package/dist/media/transform-url.js +8 -4
- package/dist/media/usage.d.ts +8 -4
- package/dist/nav/site-config.d.ts +16 -8
- package/dist/render/component-grammar.d.ts +23 -10
- package/dist/render/component-grammar.js +19 -8
- package/dist/render/component-insert.d.ts +8 -4
- package/dist/render/component-insert.js +4 -2
- package/dist/render/component-reference.d.ts +4 -2
- package/dist/render/component-reference.js +4 -2
- package/dist/render/component-validate.d.ts +3 -0
- package/dist/render/component-validate.js +3 -0
- package/dist/render/glyph.d.ts +4 -2
- package/dist/render/glyph.js +4 -2
- package/dist/render/pipeline.d.ts +20 -10
- package/dist/render/pipeline.js +4 -2
- package/dist/render/registry.d.ts +40 -20
- package/dist/render/registry.js +16 -8
- package/dist/render/rehype-dispatch.d.ts +22 -8
- package/dist/render/rehype-dispatch.js +22 -8
- package/dist/render/remark-directives.d.ts +3 -0
- package/dist/render/remark-directives.js +3 -0
- package/dist/render/remark-figure.d.ts +4 -2
- package/dist/render/remark-figure.js +4 -2
- package/dist/render/resolve-links.d.ts +4 -2
- package/dist/render/resolve-links.js +4 -2
- package/dist/render/resolve-media.d.ts +16 -8
- package/dist/render/resolve-media.js +12 -6
- package/dist/sveltekit/admin-dispatch.d.ts +2 -0
- package/dist/sveltekit/admin-dispatch.js +9 -3
- package/dist/sveltekit/auth-routes.d.ts +3 -0
- package/dist/sveltekit/auth-routes.js +3 -0
- package/dist/sveltekit/cairn-admin.d.ts +16 -5
- package/dist/sveltekit/cairn-admin.js +26 -10
- package/dist/sveltekit/content-routes.d.ts +191 -86
- package/dist/sveltekit/content-routes.js +295 -107
- package/dist/sveltekit/editors-routes.d.ts +3 -0
- package/dist/sveltekit/editors-routes.js +3 -0
- package/dist/sveltekit/guard.d.ts +4 -2
- package/dist/sveltekit/guard.js +4 -2
- package/dist/sveltekit/https-required-page.d.ts +1 -1
- package/dist/sveltekit/https-required-page.js +1 -1
- package/dist/sveltekit/index.d.ts +1 -1
- package/dist/sveltekit/media-route.d.ts +1 -2
- package/dist/sveltekit/media-route.js +13 -8
- package/dist/sveltekit/nav-routes.d.ts +7 -2
- package/dist/sveltekit/nav-routes.js +3 -0
- package/dist/sveltekit/types.d.ts +4 -2
- package/dist/vite/index.d.ts +32 -16
- package/dist/vite/index.js +52 -26
- package/dist/vite/resolve-root.d.ts +8 -4
- package/dist/vite/resolve-root.js +4 -2
- package/package.json +8 -2
- package/src/lib/components/AdminLayout.svelte +22 -0
- package/src/lib/components/CairnAdmin.svelte +3 -0
- package/src/lib/components/CairnTidySettings.svelte +2 -2
- package/src/lib/components/ComponentForm.svelte +0 -1
- package/src/lib/components/EditPage.svelte +133 -41
- package/src/lib/components/HelpHome.svelte +850 -0
- package/src/lib/components/MarkdownHelpDialog.svelte +4 -15
- package/src/lib/components/client-ingest.ts +20 -10
- package/src/lib/components/editor-media.ts +20 -10
- package/src/lib/components/editor-placeholder.ts +12 -6
- package/src/lib/components/editor-tidy.ts +28 -14
- package/src/lib/components/index.ts +1 -0
- package/src/lib/components/link-completion.ts +12 -6
- package/src/lib/components/markdown-directives.ts +13 -8
- package/src/lib/components/markdown-format.ts +63 -30
- package/src/lib/components/markdown-reference.ts +30 -0
- package/src/lib/components/media-upload-outcome.ts +12 -6
- package/src/lib/components/objective-errors.ts +16 -8
- package/src/lib/components/preview-doc.ts +4 -2
- package/src/lib/components/spellcheck.ts +92 -40
- package/src/lib/components/tidy-categorize.ts +28 -14
- package/src/lib/components/tidy-validate.ts +28 -14
- package/src/lib/components/topbar-context.ts +4 -2
- package/src/lib/content/advisories.ts +141 -0
- package/src/lib/content/compose.ts +5 -2
- package/src/lib/content/excerpt.ts +4 -2
- package/src/lib/content/getting-started.ts +31 -0
- package/src/lib/content/links.ts +16 -8
- package/src/lib/content/manifest.ts +36 -18
- package/src/lib/content/media-refs.ts +4 -2
- package/src/lib/content/media-rewrite.ts +100 -50
- package/src/lib/content/schema.ts +20 -10
- package/src/lib/content/site-dictionary.ts +8 -4
- package/src/lib/content/types.ts +97 -42
- package/src/lib/delivery/content-index.ts +16 -8
- package/src/lib/delivery/feeds.ts +4 -2
- package/src/lib/delivery/json-ld.ts +3 -0
- package/src/lib/delivery/manifest.ts +4 -2
- package/src/lib/delivery/public-routes.ts +16 -8
- package/src/lib/delivery/seo-fields.ts +12 -6
- package/src/lib/delivery/site-indexes.ts +4 -2
- package/src/lib/delivery/site-resolver.ts +4 -2
- package/src/lib/doctor/cloudflare-api.ts +6 -0
- package/src/lib/doctor/index.ts +12 -6
- package/src/lib/doctor/report.ts +3 -0
- package/src/lib/doctor/run.ts +3 -0
- package/src/lib/doctor/types.ts +10 -2
- package/src/lib/doctor/wrangler-config.ts +7 -2
- package/src/lib/email.ts +4 -2
- package/src/lib/env.ts +0 -3
- package/src/lib/github/branches.ts +4 -2
- package/src/lib/github/signing.ts +2 -2
- package/src/lib/log/events.ts +1 -0
- package/src/lib/media/bulk-delete-plan.ts +8 -4
- package/src/lib/media/config.ts +24 -12
- package/src/lib/media/delivery-bucket.ts +4 -2
- package/src/lib/media/library-entry.ts +4 -2
- package/src/lib/media/manifest.ts +33 -18
- package/src/lib/media/naming.ts +24 -12
- package/src/lib/media/orphan-scan.ts +4 -2
- package/src/lib/media/reconcile.ts +21 -11
- package/src/lib/media/reference.ts +12 -6
- package/src/lib/media/rewrite-plan.ts +12 -6
- package/src/lib/media/sniff.ts +28 -14
- package/src/lib/media/store.ts +16 -8
- package/src/lib/media/transform-url.ts +12 -6
- package/src/lib/media/usage.ts +8 -4
- package/src/lib/nav/site-config.ts +16 -8
- package/src/lib/render/component-grammar.ts +23 -10
- package/src/lib/render/component-insert.ts +8 -4
- package/src/lib/render/component-reference.ts +4 -2
- package/src/lib/render/component-validate.ts +3 -0
- package/src/lib/render/glyph.ts +4 -2
- package/src/lib/render/pipeline.ts +20 -10
- package/src/lib/render/registry.ts +44 -22
- package/src/lib/render/rehype-dispatch.ts +22 -8
- package/src/lib/render/remark-directives.ts +3 -0
- package/src/lib/render/remark-figure.ts +4 -2
- package/src/lib/render/resolve-links.ts +4 -2
- package/src/lib/render/resolve-media.ts +16 -8
- package/src/lib/sveltekit/admin-dispatch.ts +10 -4
- package/src/lib/sveltekit/auth-routes.ts +3 -0
- package/src/lib/sveltekit/cairn-admin.ts +37 -15
- package/src/lib/sveltekit/content-routes.ts +492 -197
- package/src/lib/sveltekit/editors-routes.ts +3 -0
- package/src/lib/sveltekit/guard.ts +4 -2
- package/src/lib/sveltekit/https-required-page.ts +1 -1
- package/src/lib/sveltekit/index.ts +3 -0
- package/src/lib/sveltekit/media-route.ts +13 -8
- package/src/lib/sveltekit/nav-routes.ts +7 -2
- package/src/lib/sveltekit/types.ts +4 -2
- package/src/lib/vite/index.ts +60 -30
- package/src/lib/vite/resolve-root.ts +8 -4
|
@@ -15,18 +15,22 @@ import { log } from '../log/index.js';
|
|
|
15
15
|
/** The VFile data key the renderer sets the per-call media resolver under. */
|
|
16
16
|
export const MEDIA_RESOLVE = 'mediaResolve';
|
|
17
17
|
|
|
18
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* Resolve a media reference to its delivery URL. `undefined` is a preview miss (the plugin marks
|
|
19
20
|
* the image broken); a resolver that throws is the build backstop (the error propagates out of
|
|
20
|
-
* render and fails the build), exactly like LinkResolve.
|
|
21
|
+
* render and fails the build), exactly like LinkResolve.
|
|
22
|
+
*/
|
|
21
23
|
export type MediaResolve = (ref: MediaRef) => string | undefined;
|
|
22
24
|
|
|
23
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* Build the per-call media resolver, closing over the manifest and the resolved config. The
|
|
24
27
|
* returned resolver looks a ref's content hash up in the manifest and builds the canonical delivery
|
|
25
28
|
* path from the manifest entry's slug and ext, not the token's, so a rename never breaks the
|
|
26
29
|
* reference. With a preset and zone transformations on it returns the variant URL; without a preset,
|
|
27
30
|
* or when transformations are off, it returns the bare full-size path so a fresh zone with Image
|
|
28
31
|
* Transformations disabled serves correct thumbnails rather than dead /cdn-cgi/image URLs. It returns
|
|
29
|
-
* undefined when media is off or no entry carries the hash (the preview-miss backstop).
|
|
32
|
+
* undefined when media is off or no entry carries the hash (the preview-miss backstop).
|
|
33
|
+
*/
|
|
30
34
|
export function makeMediaResolver(
|
|
31
35
|
manifest: MediaManifest,
|
|
32
36
|
resolved: ResolvedAssetConfig,
|
|
@@ -49,11 +53,13 @@ export function makeMediaResolver(
|
|
|
49
53
|
};
|
|
50
54
|
}
|
|
51
55
|
|
|
52
|
-
/**
|
|
56
|
+
/**
|
|
57
|
+
* A resolver backed by the lean `mediaTargets` projection, for the admin preview. It mirrors
|
|
53
58
|
* manifestLinkResolver: a hash present in the projection builds the slug delivery path
|
|
54
59
|
* (`/media/<slug>.<hash>.<ext>`); a miss returns undefined, so the render step marks the image
|
|
55
60
|
* broken rather than throwing. Pure over the projection, with no manifest and no config, so the
|
|
56
|
-
* edit page reaches it with the data it actually has.
|
|
61
|
+
* edit page reaches it with the data it actually has.
|
|
62
|
+
*/
|
|
57
63
|
export function manifestMediaResolver(
|
|
58
64
|
targets: Record<string, { slug: string; ext: string; contentType: string }>,
|
|
59
65
|
): MediaResolve {
|
|
@@ -69,9 +75,11 @@ interface ImageNode {
|
|
|
69
75
|
data?: { hProperties?: Record<string, unknown> };
|
|
70
76
|
}
|
|
71
77
|
|
|
72
|
-
/**
|
|
78
|
+
/**
|
|
79
|
+
* Resolve media: image nodes against the VFile's resolver. A non-media src and a malformed token
|
|
73
80
|
* pass through. A missing target is marked with the cairn-broken-media class (the resolver returns
|
|
74
|
-
* undefined) or, when the resolver throws, the error propagates and fails the build.
|
|
81
|
+
* undefined) or, when the resolver throws, the error propagates and fails the build.
|
|
82
|
+
*/
|
|
75
83
|
export function remarkResolveMedia() {
|
|
76
84
|
return (tree: unknown, file: VFile): void => {
|
|
77
85
|
const resolve = file.data[MEDIA_RESOLVE] as MediaResolve | undefined;
|
|
@@ -17,15 +17,18 @@ export type AdminView =
|
|
|
17
17
|
| { view: 'editors' }
|
|
18
18
|
| { view: 'nav' }
|
|
19
19
|
| { view: 'media' }
|
|
20
|
-
| { view: 'settings' }
|
|
20
|
+
| { view: 'settings' }
|
|
21
|
+
| { view: 'help' };
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* Fixed first segments that never resolve as concepts. The engine only allows posts and pages
|
|
24
25
|
* today, so no collision is possible, but the parser does not depend on that: a reserved
|
|
25
|
-
* segment wins before concept lookup. `
|
|
26
|
-
*
|
|
26
|
+
* segment wins before concept lookup. `nav` and `media` are decided as views below, so they are
|
|
27
|
+
* not in this no-view set. `settings` and `help` are decided as views below AND kept here, so a
|
|
28
|
+
* deeper path (or a future concept claiming the segment) can never reach the two-segment edit
|
|
29
|
+
* branch through them.
|
|
27
30
|
*/
|
|
28
|
-
const RESERVED_SEGMENTS = new Set(['login', 'auth', 'editors', 'nav', 'settings']);
|
|
31
|
+
const RESERVED_SEGMENTS = new Set(['login', 'auth', 'editors', 'nav', 'settings', 'help']);
|
|
29
32
|
|
|
30
33
|
/**
|
|
31
34
|
* Parse a raw `URL.pathname` (the caller passes `event.url.pathname`, never a SvelteKit rest
|
|
@@ -66,6 +69,9 @@ export function parseAdminPath(
|
|
|
66
69
|
// settings is its own view, a peer of editors and nav. /admin/settings/<anything> 404s naturally
|
|
67
70
|
// (the two-segment branch never matches settings), which is the correct shape.
|
|
68
71
|
if (head === 'settings') return { view: 'settings' };
|
|
72
|
+
// help is its own view (the Help home, editor-help Pass 2), decided here like settings. It is
|
|
73
|
+
// also in the reserved set so /admin/help/<anything> 404s and no concept can claim the segment.
|
|
74
|
+
if (head === 'help') return { view: 'help' };
|
|
69
75
|
if (RESERVED_SEGMENTS.has(head)) return null;
|
|
70
76
|
const concept = findConcept(concepts, head);
|
|
71
77
|
return concept ? { view: 'list', concept } : null;
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
type EditData,
|
|
17
17
|
type MediaLibraryData,
|
|
18
18
|
type SettingsData,
|
|
19
|
+
type HelpData,
|
|
19
20
|
} from './content-routes.js';
|
|
20
21
|
import { createEditorRoutes } from './editors-routes.js';
|
|
21
22
|
import { createNavRoutes, type NavLoadData } from './nav-routes.js';
|
|
@@ -35,15 +36,19 @@ export interface AdminEvent extends EventBase<GithubKeyEnv & AuthEnv> {
|
|
|
35
36
|
setHeaders(headers: Record<string, string>): void;
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
* Injectable dependencies. Branding defaults from the runtime's siteName and sender, so a
|
|
39
41
|
* site overrides it only to change the magic-link email identity; `send` and `mintToken`
|
|
40
|
-
* are the same seams the underlying factories take.
|
|
42
|
+
* are the same seams the underlying factories take.
|
|
43
|
+
*/
|
|
41
44
|
export interface CairnAdminDeps {
|
|
42
45
|
branding?: AuthBranding;
|
|
43
46
|
send?: SendMagicLink;
|
|
44
47
|
mintToken?: ContentRoutesDeps['mintToken'];
|
|
45
|
-
/**
|
|
46
|
-
*
|
|
48
|
+
/**
|
|
49
|
+
* Build the Anthropic client for the tidy action. Forwarded to the content routes; a site that
|
|
50
|
+
* enables tidy injects a stub here to avoid a real network call. Defaults to the real SDK client.
|
|
51
|
+
*/
|
|
47
52
|
anthropic?: ContentRoutesDeps['anthropic'];
|
|
48
53
|
/** The tidy action's own request deadline in milliseconds. Forwarded to the content routes. */
|
|
49
54
|
tidyTimeoutMs?: ContentRoutesDeps['tidyTimeoutMs'];
|
|
@@ -62,8 +67,12 @@ export type AdminData =
|
|
|
62
67
|
| { view: 'editors'; layout: LayoutData; page: { editors: Editor[]; self: string } }
|
|
63
68
|
| { view: 'nav'; layout: LayoutData; page: NavLoadData }
|
|
64
69
|
| { view: 'media'; layout: LayoutData; page: MediaLibraryData }
|
|
65
|
-
| { view: 'settings'; layout: LayoutData; page: SettingsData }
|
|
70
|
+
| { view: 'settings'; layout: LayoutData; page: SettingsData }
|
|
71
|
+
| { view: 'help'; layout: LayoutData; page: HelpData };
|
|
66
72
|
|
|
73
|
+
/**
|
|
74
|
+
*
|
|
75
|
+
*/
|
|
67
76
|
export function createCairnAdmin(runtime: CairnRuntime, deps: CairnAdminDeps = {}) {
|
|
68
77
|
// The runtime already composes the site name and the sender identity, so the magic-link
|
|
69
78
|
// branding needs no second copy of either unless a site overrides it.
|
|
@@ -82,11 +91,13 @@ export function createCairnAdmin(runtime: CairnRuntime, deps: CairnAdminDeps = {
|
|
|
82
91
|
// The nav surface exists only when the site configures a menu; without one its view is a 404.
|
|
83
92
|
const nav = runtime.navMenu ? createNavRoutes(runtime, { mintToken: deps.mintToken }) : null;
|
|
84
93
|
|
|
85
|
-
/**
|
|
94
|
+
/**
|
|
95
|
+
* Build the event a wrapped content load reads. The catch-all route carries only a rest
|
|
86
96
|
* param, so `concept` and `id` are synthesized from the parsed view. The override names
|
|
87
97
|
* each field explicitly rather than spreading: a real RequestEvent's fields can sit behind
|
|
88
98
|
* getters a bare spread copies poorly, and the structural ContentEvent contract needs only
|
|
89
|
-
* these.
|
|
99
|
+
* these.
|
|
100
|
+
*/
|
|
90
101
|
function contentEvent(event: AdminEvent, params: Record<string, string>): ContentEvent {
|
|
91
102
|
return {
|
|
92
103
|
url: event.url,
|
|
@@ -98,9 +109,11 @@ export function createCairnAdmin(runtime: CairnRuntime, deps: CairnAdminDeps = {
|
|
|
98
109
|
};
|
|
99
110
|
}
|
|
100
111
|
|
|
101
|
-
/**
|
|
112
|
+
/**
|
|
113
|
+
* Serve the admin view the pathname names, or a 404 for any shape the parser refuses.
|
|
102
114
|
* The authed views run the layout load and the view load concurrently; both mint a GitHub
|
|
103
|
-
* token, and the installation-token cache coalesces the mints into one signing.
|
|
115
|
+
* token, and the installation-token cache coalesces the mints into one signing.
|
|
116
|
+
*/
|
|
104
117
|
async function load(event: AdminEvent): Promise<AdminData> {
|
|
105
118
|
const view = parseAdminPath(event.url.pathname, runtime.concepts);
|
|
106
119
|
if (!view) throw error(404, 'Not found');
|
|
@@ -145,12 +158,19 @@ export function createCairnAdmin(runtime: CairnRuntime, deps: CairnAdminDeps = {
|
|
|
145
158
|
const [layout, page] = await Promise.all([content.layoutLoad(delegated), content.settingsLoad(delegated)]);
|
|
146
159
|
return { view: 'settings', layout, page };
|
|
147
160
|
}
|
|
161
|
+
case 'help': {
|
|
162
|
+
const delegated = contentEvent(event, {});
|
|
163
|
+
const [layout, page] = await Promise.all([content.layoutLoad(delegated), content.helpLoad(delegated)]);
|
|
164
|
+
return { view: 'help', layout, page };
|
|
165
|
+
}
|
|
148
166
|
}
|
|
149
167
|
}
|
|
150
168
|
|
|
151
|
-
/**
|
|
169
|
+
/**
|
|
170
|
+
* Wrap a delegate in the parse-and-check every action shares: parse the pathname exactly
|
|
152
171
|
* as load does, 404 on a null parse or a view outside the allowed set, then hand the
|
|
153
|
-
* narrowed view to the delegate.
|
|
172
|
+
* narrowed view to the delegate.
|
|
173
|
+
*/
|
|
154
174
|
function viewAction<V extends AdminView['view'], R>(
|
|
155
175
|
allowed: readonly V[],
|
|
156
176
|
delegate: (event: AdminEvent, view: Extract<AdminView, { view: V }>) => Promise<R>,
|
|
@@ -164,14 +184,16 @@ export function createCairnAdmin(runtime: CairnRuntime, deps: CairnAdminDeps = {
|
|
|
164
184
|
}
|
|
165
185
|
|
|
166
186
|
// The topbar posts publishAll from every authed admin page; login and confirm may not.
|
|
167
|
-
const authedViews = ['list', 'edit', 'editors', 'nav', 'media', 'settings'] as const;
|
|
187
|
+
const authedViews = ['list', 'edit', 'editors', 'nav', 'media', 'settings', 'help'] as const;
|
|
168
188
|
// An editor signs out from wherever they are, so logout accepts any parsed view.
|
|
169
|
-
const anyView = ['index', 'login', 'confirm', 'list', 'edit', 'editors', 'nav', 'media', 'settings'] as const;
|
|
189
|
+
const anyView = ['index', 'login', 'confirm', 'list', 'edit', 'editors', 'nav', 'media', 'settings', 'help'] as const;
|
|
170
190
|
|
|
171
|
-
/**
|
|
191
|
+
/**
|
|
192
|
+
* The full admin action vocabulary, one named async function per action, so a site's
|
|
172
193
|
* catch-all route exports `admin.actions` directly. Each wrapper stays thin: parse,
|
|
173
194
|
* validate the view, synthesize the params the wrapped action reads, delegate. The
|
|
174
|
-
* editor actions gate themselves with requireOwner, so no second gate is added here.
|
|
195
|
+
* editor actions gate themselves with requireOwner, so no second gate is added here.
|
|
196
|
+
*/
|
|
175
197
|
const actions = {
|
|
176
198
|
request: viewAction(['login'], (event) => auth.requestAction(event)),
|
|
177
199
|
confirm: viewAction(['confirm'], (event) => auth.confirmAction(event)),
|