@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
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
// not match, so the editor would otherwise hit an opaque 403. This page names the problem, says why
|
|
5
5
|
// https is needed, and gives the exact Cloudflare fix. The shared shell lives in
|
|
6
6
|
// static-admin-page.ts. See guard.ts.
|
|
7
|
-
import { escapeHtml
|
|
7
|
+
import { escapeHtml } from '../escape.js';
|
|
8
|
+
import { renderStaticAdminPage } from './static-admin-page.js';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Render the full HTML document for the HTTPS-required page.
|
|
@@ -12,9 +12,15 @@ export type {
|
|
|
12
12
|
EditData,
|
|
13
13
|
ContentEvent,
|
|
14
14
|
ContentRoutesDeps,
|
|
15
|
+
SaveFailure,
|
|
16
|
+
DeleteRefusal,
|
|
17
|
+
RenameFailure,
|
|
18
|
+
ContentFormFailure,
|
|
15
19
|
} from './content-routes.js';
|
|
16
20
|
export { createNavRoutes } from './nav-routes.js';
|
|
17
21
|
export type { NavLoadData, NavPageOption, NavRoutesDeps } from './nav-routes.js';
|
|
22
|
+
export { parseAdminPath, type AdminView } from './admin-dispatch.js';
|
|
23
|
+
export { createCairnAdmin, type CairnAdminDeps, type AdminData } from './cairn-admin.js';
|
|
18
24
|
export { healthLoad, type HealthData } from './health.js';
|
|
19
25
|
export type { RequestContext, CookieJar, HandleInput } from './types.js';
|
|
20
26
|
// Re-exported here, not from root, so the public ContentRoutesDeps consumer can name it.
|
|
@@ -5,12 +5,12 @@ import { redirect, error } from '@sveltejs/kit';
|
|
|
5
5
|
import { appCredentials, type GithubKeyEnv } from '../github/credentials.js';
|
|
6
6
|
import { cachedInstallationToken } from '../github/signing.js';
|
|
7
7
|
import { listMarkdown, readRaw, commitFile } from '../github/repo.js';
|
|
8
|
-
import {
|
|
8
|
+
import { isConflict } from '../github/types.js';
|
|
9
9
|
import { log } from '../log/index.js';
|
|
10
10
|
import { parseSiteConfig, extractMenu, validateNavTree, setMenu, type NavNode } from '../nav/site-config.js';
|
|
11
|
+
import { requireSession } from './guard.js';
|
|
11
12
|
import type { CairnRuntime } from '../content/types.js';
|
|
12
13
|
import type { ContentEvent } from './content-routes.js';
|
|
13
|
-
import type { Editor } from '../auth/types.js';
|
|
14
14
|
|
|
15
15
|
/** One page option for the URL picker datalist. */
|
|
16
16
|
export interface NavPageOption {
|
|
@@ -29,19 +29,9 @@ export interface NavLoadData {
|
|
|
29
29
|
|
|
30
30
|
/** Injectable dependencies; tests stub the token mint to avoid signing a real key. */
|
|
31
31
|
export interface NavRoutesDeps {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
/** The signed-in editor the guard resolved, or a login redirect. */
|
|
36
|
-
function sessionOf(event: ContentEvent): Editor {
|
|
37
|
-
const editor = event.locals.editor;
|
|
38
|
-
if (!editor) throw redirect(303, '/admin/login');
|
|
39
|
-
return editor;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/** Match a commit conflict by class and by name (bundling can alias the class identity). */
|
|
43
|
-
function isConflict(err: unknown): boolean {
|
|
44
|
-
return err instanceof CommitConflictError || (err as { name?: string } | null)?.name === 'CommitConflictError';
|
|
32
|
+
/** Mint a GitHub App installation token from the Worker env. Defaults to the real signer.
|
|
33
|
+
* A bare string works too; the routes await whatever comes back. */
|
|
34
|
+
mintToken?: (env: GithubKeyEnv) => string | Promise<string>;
|
|
45
35
|
}
|
|
46
36
|
|
|
47
37
|
export function createNavRoutes(runtime: CairnRuntime, deps: NavRoutesDeps = {}) {
|
|
@@ -66,7 +56,7 @@ export function createNavRoutes(runtime: CairnRuntime, deps: NavRoutesDeps = {})
|
|
|
66
56
|
|
|
67
57
|
/** Load the nav editor. A missing or unparsable config degrades to an empty tree so it still opens. */
|
|
68
58
|
async function navLoad(event: ContentEvent): Promise<NavLoadData> {
|
|
69
|
-
|
|
59
|
+
requireSession(event);
|
|
70
60
|
const config = runtime.navMenu;
|
|
71
61
|
if (!config) throw error(404, 'No navigation menu configured');
|
|
72
62
|
const maxDepth = config.maxDepth ?? 2;
|
|
@@ -80,12 +70,25 @@ export function createNavRoutes(runtime: CairnRuntime, deps: NavRoutesDeps = {})
|
|
|
80
70
|
}
|
|
81
71
|
|
|
82
72
|
let tree: NavNode[] = [];
|
|
73
|
+
let raw: string | null = null;
|
|
83
74
|
try {
|
|
84
|
-
|
|
85
|
-
if (raw !== null) tree = extractMenu(parseSiteConfig(raw), config.menuName, maxDepth);
|
|
75
|
+
raw = await readRaw(runtime.backend, config.configPath, token);
|
|
86
76
|
} catch {
|
|
87
|
-
//
|
|
88
|
-
|
|
77
|
+
// An unreadable config degrades to an empty tree; the first save writes a clean menu.
|
|
78
|
+
raw = null;
|
|
79
|
+
}
|
|
80
|
+
if (raw !== null) {
|
|
81
|
+
try {
|
|
82
|
+
tree = extractMenu(parseSiteConfig(raw), config.menuName, maxDepth);
|
|
83
|
+
} catch (err) {
|
|
84
|
+
// A malformed config keeps the same degrade (the nav page failing closed would be worse
|
|
85
|
+
// for the editor), but the swallow names the operator fault in the log.
|
|
86
|
+
log.error('config.invalid', {
|
|
87
|
+
conditionId: 'config.site-config-invalid',
|
|
88
|
+
error: String(err),
|
|
89
|
+
});
|
|
90
|
+
tree = [];
|
|
91
|
+
}
|
|
89
92
|
}
|
|
90
93
|
|
|
91
94
|
return {
|
|
@@ -99,7 +102,7 @@ export function createNavRoutes(runtime: CairnRuntime, deps: NavRoutesDeps = {})
|
|
|
99
102
|
|
|
100
103
|
/** Save the nav tree: validate, then read-modify-commit the one menu with the session editor as author. */
|
|
101
104
|
async function navSave(event: ContentEvent): Promise<never> {
|
|
102
|
-
const editor =
|
|
105
|
+
const editor = requireSession(event);
|
|
103
106
|
const config = runtime.navMenu;
|
|
104
107
|
if (!config) throw error(404, 'No navigation menu configured');
|
|
105
108
|
const maxDepth = config.maxDepth ?? 2;
|
|
@@ -2,15 +2,7 @@
|
|
|
2
2
|
// self-contained document with inlined Warm Stone tokens for both colour schemes and the system
|
|
3
3
|
// font stack, served raw before SvelteKit renders. The cairn glyph is the same public-domain
|
|
4
4
|
// Temaki mark the admin chrome uses. See docs/internal/admin-design-system.md.
|
|
5
|
-
|
|
6
|
-
/** Escape a string for safe interpolation into HTML text and double-quoted attributes. */
|
|
7
|
-
export function escapeHtml(value: string): string {
|
|
8
|
-
return value
|
|
9
|
-
.replace(/&/g, '&')
|
|
10
|
-
.replace(/</g, '<')
|
|
11
|
-
.replace(/>/g, '>')
|
|
12
|
-
.replace(/"/g, '"');
|
|
13
|
-
}
|
|
5
|
+
import { escapeHtml } from '../escape.js';
|
|
14
6
|
|
|
15
7
|
// The cairn stone-stack glyph (Temaki, CC0), drawn in currentColor like CairnLogo.svelte.
|
|
16
8
|
const CAIRN_GLYPH =
|
|
@@ -16,16 +16,25 @@ export interface CookieJar {
|
|
|
16
16
|
delete(name: string, opts: { path: string }): void;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
/** The Cloudflare platform wrapper an event carries; `context` is the legacy alias for `ctx`. */
|
|
20
|
+
export interface PlatformContext<Env> {
|
|
21
|
+
env?: Env;
|
|
22
|
+
ctx?: { waitUntil(promise: Promise<unknown>): void };
|
|
23
|
+
context?: { waitUntil(promise: Promise<unknown>): void };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** The structural core every engine event type extends, parameterized by the Worker env the
|
|
27
|
+
* surface reads. Each shared field is defined once here; the extensions add only what their
|
|
28
|
+
* surface needs (cookies, params, setHeaders). */
|
|
29
|
+
export interface EventBase<Env> {
|
|
20
30
|
url: URL;
|
|
21
31
|
request: Request;
|
|
22
|
-
cookies: CookieJar;
|
|
23
32
|
locals: { editor?: Editor | null };
|
|
24
|
-
platform?:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
33
|
+
platform?: PlatformContext<Env>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface RequestContext extends EventBase<AuthEnv> {
|
|
37
|
+
cookies: CookieJar;
|
|
29
38
|
// Required so a site cannot silently drop the confirm page's Referrer-Policy header
|
|
30
39
|
// (spec 7.1). A real SvelteKit RequestEvent always supplies it.
|
|
31
40
|
setHeaders(headers: Record<string, string>): void;
|
package/src/lib/vite/index.ts
CHANGED
|
@@ -61,21 +61,17 @@ export const result = ${resultExpr};
|
|
|
61
61
|
`;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
/** Evaluate
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
async function evalVirtual(
|
|
70
|
-
opts: CairnManifestOptions,
|
|
71
|
-
mode: 'verify' | 'write',
|
|
72
|
-
root: string,
|
|
73
|
-
): Promise<string> {
|
|
64
|
+
/** Evaluate a virtual module source inside the consumer's own Vite resolution, then return the
|
|
65
|
+
* module's `result`. It reuses the consumer's loaded config (so `$lib`, the config module,
|
|
66
|
+
* `import.meta.glob`, and `?raw` resolve exactly as the build does) and strips the cairnManifest
|
|
67
|
+
* plugin from the nested server's plugin list, so its buildStart never recurses. This runs at
|
|
68
|
+
* build time and in the bins, never in the request lifecycle. */
|
|
69
|
+
async function evalVirtual(source: string, root: string): Promise<string> {
|
|
74
70
|
const { createServer, loadConfigFromFile } = await import('vite');
|
|
75
71
|
// Load the consumer's real Vite config so the nested server inherits SvelteKit's resolution
|
|
76
72
|
// (the $lib alias, the app root, the ?raw and import.meta.glob handling). Drop cairnManifest from
|
|
77
73
|
// it so the nested server's buildStart does not recurse, and add a plugin that serves only the
|
|
78
|
-
// virtual module
|
|
74
|
+
// given virtual module source.
|
|
79
75
|
const loaded = await loadConfigFromFile({ command: 'build', mode: 'production' }, undefined, root);
|
|
80
76
|
const inlineConfig = loaded?.config ?? {};
|
|
81
77
|
const server = await createServer({
|
|
@@ -84,7 +80,7 @@ async function evalVirtual(
|
|
|
84
80
|
configFile: false,
|
|
85
81
|
logLevel: 'silent',
|
|
86
82
|
server: { middlewareMode: true, hmr: false, watch: null },
|
|
87
|
-
plugins: [...stripCairnManifest(inlineConfig.plugins ?? []), cairnVirtualOnly(
|
|
83
|
+
plugins: [...stripCairnManifest(inlineConfig.plugins ?? []), cairnVirtualOnly(source)],
|
|
88
84
|
});
|
|
89
85
|
try {
|
|
90
86
|
const mod = (await server.ssrLoadModule(VIRTUAL_ID)) as { result: string };
|
|
@@ -115,13 +111,13 @@ export function stripCairnManifest(plugins: PluginOption | PluginOption[]): Plug
|
|
|
115
111
|
/** Verify the committed manifest against the corpus from a Vite context, throwing on drift. The bin
|
|
116
112
|
* and the plugin share this; the spike proved it runs cleanly inside the consumer's config. */
|
|
117
113
|
export async function verifyManifestFromVite(opts: CairnManifestOptions, root: string): Promise<void> {
|
|
118
|
-
await evalVirtual(opts, 'verify', root);
|
|
114
|
+
await evalVirtual(virtualSource(opts, 'verify'), root);
|
|
119
115
|
}
|
|
120
116
|
|
|
121
117
|
/** Regenerate the serialized manifest from the corpus in a Vite context, sharing the build's
|
|
122
118
|
* resolution. The cairn-manifest bin (a later task) will call this and write the result. */
|
|
123
119
|
export async function buildManifestFromVite(opts: CairnManifestOptions, root: string): Promise<string> {
|
|
124
|
-
return evalVirtual(opts, 'write', root);
|
|
120
|
+
return evalVirtual(virtualSource(opts, 'write'), root);
|
|
125
121
|
}
|
|
126
122
|
|
|
127
123
|
/** The cairnManifest plugin. It serves the verify virtual module to the app graph and, in
|
|
@@ -198,16 +194,74 @@ function findCairnOptions(plugins: unknown): CairnManifestOptions | null {
|
|
|
198
194
|
return null;
|
|
199
195
|
}
|
|
200
196
|
|
|
201
|
-
/** A minimal plugin that serves only the virtual module
|
|
197
|
+
/** A minimal plugin that serves only the given virtual module source, for the nested SSR load. It
|
|
202
198
|
* carries no buildStart, so the nested server never recurses into the verify. */
|
|
203
|
-
function cairnVirtualOnly(
|
|
199
|
+
function cairnVirtualOnly(source: string): Plugin {
|
|
204
200
|
return {
|
|
205
201
|
name: 'cairn-manifest-virtual',
|
|
206
202
|
resolveId(id) {
|
|
207
203
|
if (id === VIRTUAL_ID) return RESOLVED_ID;
|
|
208
204
|
},
|
|
209
205
|
load(id) {
|
|
210
|
-
if (id === RESOLVED_ID) return
|
|
206
|
+
if (id === RESOLVED_ID) return source;
|
|
211
207
|
},
|
|
212
208
|
};
|
|
213
209
|
}
|
|
210
|
+
|
|
211
|
+
/** The repo and sender facts cairn-doctor derives off the consumer's adapter. */
|
|
212
|
+
export interface AdapterFacts {
|
|
213
|
+
/** `cairn.backend.owner`. */
|
|
214
|
+
owner?: string;
|
|
215
|
+
/** `cairn.backend.repo`. */
|
|
216
|
+
repo?: string;
|
|
217
|
+
/** `cairn.sender.from`. */
|
|
218
|
+
from?: string;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** Build the virtual module that reads only the adapter facts the doctor derives. It imports the
|
|
222
|
+
* configured config module and exports the string-typed `owner`, `repo`, and `from` as JSON, so
|
|
223
|
+
* nothing else of the adapter (least of all a secret) crosses the boundary. */
|
|
224
|
+
function adapterFactsSource(opts: CairnManifestOptions): string {
|
|
225
|
+
return `
|
|
226
|
+
import { cairn } from ${JSON.stringify(opts.configModule)};
|
|
227
|
+
const backend = cairn?.backend ?? {};
|
|
228
|
+
const sender = cairn?.sender ?? {};
|
|
229
|
+
const facts = {};
|
|
230
|
+
if (typeof backend.owner === 'string') facts.owner = backend.owner;
|
|
231
|
+
if (typeof backend.repo === 'string') facts.repo = backend.repo;
|
|
232
|
+
if (typeof sender.from === 'string') facts.from = sender.from;
|
|
233
|
+
export const result = JSON.stringify(facts);
|
|
234
|
+
`;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** Read `{ owner, repo, from }` off the consumer's adapter by evaluating a tiny virtual module
|
|
238
|
+
* through the consumer's own Vite resolution, the same machinery the cairn-manifest bin uses.
|
|
239
|
+
* cairn-doctor calls this to fill inputs the operator did not pass. Derivation is best-effort:
|
|
240
|
+
* any failure (no Vite config, no cairnManifest plugin, a config module that throws) returns
|
|
241
|
+
* null, so the doctor degrades to flags instead of crashing. This runs only on the bin path,
|
|
242
|
+
* never in a Worker. */
|
|
243
|
+
export async function readAdapterFacts(cwd: string = process.cwd()): Promise<AdapterFacts | null> {
|
|
244
|
+
try {
|
|
245
|
+
const { loadConfigFromFile } = await import('vite');
|
|
246
|
+
const loaded = await loadConfigFromFile(
|
|
247
|
+
{ command: 'build', mode: 'production' },
|
|
248
|
+
undefined,
|
|
249
|
+
cwd,
|
|
250
|
+
'silent',
|
|
251
|
+
);
|
|
252
|
+
if (!loaded) return null;
|
|
253
|
+
const opts = findCairnOptions(loaded.config.plugins);
|
|
254
|
+
if (!opts) return null;
|
|
255
|
+
const parsed = JSON.parse(await evalVirtual(adapterFactsSource(opts), cwd)) as Record<
|
|
256
|
+
string,
|
|
257
|
+
unknown
|
|
258
|
+
>;
|
|
259
|
+
const facts: AdapterFacts = {};
|
|
260
|
+
if (typeof parsed.owner === 'string') facts.owner = parsed.owner;
|
|
261
|
+
if (typeof parsed.repo === 'string') facts.repo = parsed.repo;
|
|
262
|
+
if (typeof parsed.from === 'string') facts.from = parsed.from;
|
|
263
|
+
return facts;
|
|
264
|
+
} catch {
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/** A page of items plus its navigation state. */
|
|
2
|
-
export interface Page<T> {
|
|
3
|
-
items: T[];
|
|
4
|
-
page: number;
|
|
5
|
-
perPage: number;
|
|
6
|
-
total: number;
|
|
7
|
-
totalPages: number;
|
|
8
|
-
hasPrev: boolean;
|
|
9
|
-
hasNext: boolean;
|
|
10
|
-
}
|
|
11
|
-
/** Slice `items` into the 1-based `page` of size `perPage`, clamping the page into bounds. */
|
|
12
|
-
export declare function paginate<T>(items: T[], page: number, perPage: number): Page<T>;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
// cairn-cms: pagination helper (public-delivery design). Pure slice math; the template renders
|
|
2
|
-
// the controls. An out-of-range page clamps into bounds.
|
|
3
|
-
/** Slice `items` into the 1-based `page` of size `perPage`, clamping the page into bounds. */
|
|
4
|
-
export function paginate(items, page, perPage) {
|
|
5
|
-
const total = items.length;
|
|
6
|
-
// A non-positive page size would make totalPages Infinity, so clamp it to one.
|
|
7
|
-
const size = Math.max(1, Math.floor(perPage) || 1);
|
|
8
|
-
const totalPages = Math.max(1, Math.ceil(total / size));
|
|
9
|
-
const current = Math.min(Math.max(1, Math.floor(page) || 1), totalPages);
|
|
10
|
-
const start = (current - 1) * size;
|
|
11
|
-
return {
|
|
12
|
-
items: items.slice(start, start + size),
|
|
13
|
-
page: current,
|
|
14
|
-
perPage: size,
|
|
15
|
-
total,
|
|
16
|
-
totalPages,
|
|
17
|
-
hasPrev: current > 1,
|
|
18
|
-
hasNext: current < totalPages,
|
|
19
|
-
};
|
|
20
|
-
}
|
package/dist/render/index.d.ts
DELETED
package/dist/render/index.js
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
// cairn-cms render engine: a directive-driven markdown to HTML pipeline whose
|
|
2
|
-
// component vocabulary is supplied by a site's component registry. The site owns the
|
|
3
|
-
// component builders, class names, icon set, and CSS; the engine owns the machinery.
|
|
4
|
-
export * from './registry.js';
|
|
5
|
-
export * from './glyph.js';
|
|
6
|
-
export * from './remark-directives.js';
|
|
7
|
-
export * from './rehype-dispatch.js';
|
|
8
|
-
export * from './pipeline.js';
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
// cairn-cms: pagination helper (public-delivery design). Pure slice math; the template renders
|
|
2
|
-
// the controls. An out-of-range page clamps into bounds.
|
|
3
|
-
|
|
4
|
-
/** A page of items plus its navigation state. */
|
|
5
|
-
export interface Page<T> {
|
|
6
|
-
items: T[];
|
|
7
|
-
page: number;
|
|
8
|
-
perPage: number;
|
|
9
|
-
total: number;
|
|
10
|
-
totalPages: number;
|
|
11
|
-
hasPrev: boolean;
|
|
12
|
-
hasNext: boolean;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/** Slice `items` into the 1-based `page` of size `perPage`, clamping the page into bounds. */
|
|
16
|
-
export function paginate<T>(items: T[], page: number, perPage: number): Page<T> {
|
|
17
|
-
const total = items.length;
|
|
18
|
-
// A non-positive page size would make totalPages Infinity, so clamp it to one.
|
|
19
|
-
const size = Math.max(1, Math.floor(perPage) || 1);
|
|
20
|
-
const totalPages = Math.max(1, Math.ceil(total / size));
|
|
21
|
-
const current = Math.min(Math.max(1, Math.floor(page) || 1), totalPages);
|
|
22
|
-
const start = (current - 1) * size;
|
|
23
|
-
return {
|
|
24
|
-
items: items.slice(start, start + size),
|
|
25
|
-
page: current,
|
|
26
|
-
perPage: size,
|
|
27
|
-
total,
|
|
28
|
-
totalPages,
|
|
29
|
-
hasPrev: current > 1,
|
|
30
|
-
hasNext: current < totalPages,
|
|
31
|
-
};
|
|
32
|
-
}
|
package/src/lib/render/index.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
// cairn-cms render engine: a directive-driven markdown to HTML pipeline whose
|
|
2
|
-
// component vocabulary is supplied by a site's component registry. The site owns the
|
|
3
|
-
// component builders, class names, icon set, and CSS; the engine owns the machinery.
|
|
4
|
-
export * from './registry.js';
|
|
5
|
-
export * from './glyph.js';
|
|
6
|
-
export * from './remark-directives.js';
|
|
7
|
-
export * from './rehype-dispatch.js';
|
|
8
|
-
export * from './pipeline.js';
|