@glw907/cairn-cms 0.5.1 → 0.6.0-rc.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/dist/auth/crypto.d.ts +13 -0
- package/dist/auth/crypto.d.ts.map +1 -0
- package/dist/auth/crypto.js +31 -0
- package/dist/auth/store.d.ts +41 -0
- package/dist/auth/store.d.ts.map +1 -0
- package/dist/auth/store.js +115 -0
- package/dist/auth/types.d.ts +25 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +1 -0
- package/dist/components/AdminLayout.svelte +58 -164
- package/dist/components/AdminLayout.svelte.d.ts +14 -18
- package/dist/components/AdminLayout.svelte.d.ts.map +1 -1
- package/dist/components/ComponentPalette.svelte +36 -20
- package/dist/components/ComponentPalette.svelte.d.ts +11 -4
- package/dist/components/ComponentPalette.svelte.d.ts.map +1 -1
- package/dist/components/ConceptList.svelte +81 -0
- package/dist/components/ConceptList.svelte.d.ts +13 -0
- package/dist/components/ConceptList.svelte.d.ts.map +1 -0
- package/dist/components/ConfirmPage.svelte +23 -20
- package/dist/components/ConfirmPage.svelte.d.ts +6 -0
- package/dist/components/ConfirmPage.svelte.d.ts.map +1 -1
- package/dist/components/EditPage.svelte +155 -136
- package/dist/components/EditPage.svelte.d.ts +16 -8
- package/dist/components/EditPage.svelte.d.ts.map +1 -1
- package/dist/components/LoginPage.svelte +42 -52
- package/dist/components/LoginPage.svelte.d.ts +12 -0
- package/dist/components/LoginPage.svelte.d.ts.map +1 -1
- package/dist/components/ManageEditors.svelte +81 -0
- package/dist/components/ManageEditors.svelte.d.ts +24 -0
- package/dist/components/ManageEditors.svelte.d.ts.map +1 -0
- package/dist/components/MarkdownEditor.svelte +81 -0
- package/dist/components/MarkdownEditor.svelte.d.ts +20 -0
- package/dist/components/MarkdownEditor.svelte.d.ts.map +1 -0
- package/dist/components/NavTree.svelte +73 -63
- package/dist/components/NavTree.svelte.d.ts +13 -4
- package/dist/components/NavTree.svelte.d.ts.map +1 -1
- package/dist/components/cairn-admin.css +42 -0
- package/dist/components/index.d.ts +3 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +5 -4
- package/dist/content/compose.d.ts +7 -0
- package/dist/content/compose.d.ts.map +1 -0
- package/dist/content/compose.js +32 -0
- package/dist/content/concepts.d.ts +17 -0
- package/dist/content/concepts.d.ts.map +1 -0
- package/dist/content/concepts.js +41 -0
- package/dist/content/frontmatter.d.ts +18 -0
- package/dist/content/frontmatter.d.ts.map +1 -0
- package/dist/content/frontmatter.js +58 -0
- package/dist/content/ids.d.ts +17 -0
- package/dist/content/ids.d.ts.map +1 -0
- package/dist/content/ids.js +33 -0
- package/dist/content/types.d.ts +210 -0
- package/dist/content/types.d.ts.map +1 -0
- package/dist/content/types.js +1 -0
- package/dist/content/validate.d.ts +13 -0
- package/dist/content/validate.d.ts.map +1 -0
- package/dist/content/validate.js +45 -0
- package/dist/email.d.ts +25 -12
- package/dist/email.d.ts.map +1 -1
- package/dist/email.js +24 -24
- package/dist/env.d.ts +24 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +29 -0
- package/dist/github/credentials.d.ts +12 -0
- package/dist/github/credentials.d.ts.map +1 -0
- package/dist/github/credentials.js +11 -0
- package/dist/github/repo.d.ts +49 -0
- package/dist/github/repo.d.ts.map +1 -0
- package/dist/github/repo.js +123 -0
- package/dist/github/signing.d.ts +17 -0
- package/dist/github/signing.d.ts.map +1 -0
- package/dist/github/signing.js +79 -0
- package/dist/github/types.d.ts +35 -0
- package/dist/github/types.d.ts.map +1 -0
- package/dist/github/types.js +19 -0
- package/dist/index.d.ts +27 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -10
- package/dist/{nav.d.ts → nav/site-config.d.ts} +16 -24
- package/dist/nav/site-config.d.ts.map +1 -0
- package/dist/{nav.js → nav/site-config.js} +27 -13
- package/dist/render/glyph.d.ts +1 -1
- package/dist/render/glyph.d.ts.map +1 -1
- package/dist/render/index.d.ts +5 -5
- package/dist/render/index.d.ts.map +1 -1
- package/dist/render/index.js +6 -6
- package/dist/render/pipeline.d.ts +3 -3
- package/dist/render/pipeline.d.ts.map +1 -1
- package/dist/render/pipeline.js +4 -4
- package/dist/render/registry.d.ts +6 -4
- package/dist/render/registry.d.ts.map +1 -1
- package/dist/render/registry.js +8 -6
- package/dist/render/rehype-dispatch.d.ts +1 -1
- package/dist/render/rehype-dispatch.d.ts.map +1 -1
- package/dist/render/remark-directives.d.ts +1 -1
- package/dist/render/remark-directives.d.ts.map +1 -1
- package/dist/render/sanitize.d.ts +8 -0
- package/dist/render/sanitize.d.ts.map +1 -0
- package/dist/render/sanitize.js +26 -0
- package/dist/sveltekit/auth-routes.d.ts +23 -0
- package/dist/sveltekit/auth-routes.d.ts.map +1 -0
- package/dist/sveltekit/auth-routes.js +85 -0
- package/dist/sveltekit/content-routes.d.ts +80 -0
- package/dist/sveltekit/content-routes.d.ts.map +1 -0
- package/dist/sveltekit/content-routes.js +183 -0
- package/dist/sveltekit/editors-routes.d.ts +24 -0
- package/dist/sveltekit/editors-routes.d.ts.map +1 -0
- package/dist/sveltekit/editors-routes.js +73 -0
- package/dist/sveltekit/guard.d.ts +9 -0
- package/dist/sveltekit/guard.d.ts.map +1 -0
- package/dist/sveltekit/guard.js +43 -0
- package/dist/sveltekit/health.d.ts +19 -0
- package/dist/sveltekit/health.d.ts.map +1 -0
- package/dist/sveltekit/health.js +12 -0
- package/dist/sveltekit/index.d.ts +9 -173
- package/dist/sveltekit/index.d.ts.map +1 -1
- package/dist/sveltekit/index.js +8 -348
- package/dist/sveltekit/nav-routes.d.ts +30 -0
- package/dist/sveltekit/nav-routes.d.ts.map +1 -0
- package/dist/sveltekit/nav-routes.js +103 -0
- package/dist/sveltekit/types.d.ts +32 -0
- package/dist/sveltekit/types.d.ts.map +1 -0
- package/dist/sveltekit/types.js +1 -0
- package/package.json +32 -57
- package/src/lib/auth/crypto.ts +37 -0
- package/src/lib/auth/store.ts +158 -0
- package/src/lib/auth/types.ts +27 -0
- package/src/lib/components/AdminLayout.svelte +58 -164
- package/src/lib/components/ComponentPalette.svelte +36 -20
- package/src/lib/components/ConceptList.svelte +81 -0
- package/src/lib/components/ConfirmPage.svelte +23 -20
- package/src/lib/components/EditPage.svelte +155 -136
- package/src/lib/components/LoginPage.svelte +42 -52
- package/src/lib/components/ManageEditors.svelte +81 -0
- package/src/lib/components/MarkdownEditor.svelte +81 -0
- package/src/lib/components/NavTree.svelte +73 -63
- package/src/lib/components/cairn-admin.css +42 -0
- package/src/lib/components/index.ts +5 -4
- package/src/lib/content/compose.ts +39 -0
- package/src/lib/content/concepts.ts +57 -0
- package/src/lib/content/frontmatter.ts +71 -0
- package/src/lib/content/ids.ts +38 -0
- package/src/lib/content/types.ts +235 -0
- package/src/lib/content/validate.ts +51 -0
- package/src/lib/email.ts +52 -38
- package/src/lib/env.ts +32 -0
- package/src/lib/github/credentials.ts +27 -0
- package/src/lib/github/repo.ts +138 -0
- package/src/lib/github/signing.ts +97 -0
- package/src/lib/github/types.ts +46 -0
- package/src/lib/index.ts +86 -10
- package/src/lib/{nav.ts → nav/site-config.ts} +31 -24
- package/src/lib/render/glyph.ts +6 -6
- package/src/lib/render/index.ts +6 -6
- package/src/lib/render/pipeline.ts +22 -22
- package/src/lib/render/registry.ts +33 -26
- package/src/lib/render/rehype-dispatch.ts +47 -47
- package/src/lib/render/remark-directives.ts +46 -46
- package/src/lib/render/sanitize.ts +27 -0
- package/src/lib/sveltekit/auth-routes.ts +107 -0
- package/src/lib/sveltekit/content-routes.ts +261 -0
- package/src/lib/sveltekit/editors-routes.ts +82 -0
- package/src/lib/sveltekit/guard.ts +47 -0
- package/src/lib/sveltekit/health.ts +24 -0
- package/src/lib/sveltekit/index.ts +19 -512
- package/src/lib/sveltekit/nav-routes.ts +139 -0
- package/src/lib/sveltekit/types.ts +33 -0
- package/dist/adapter.d.ts +0 -93
- package/dist/adapter.d.ts.map +0 -1
- package/dist/adapter.js +0 -30
- package/dist/auth/admins.d.ts +0 -33
- package/dist/auth/admins.d.ts.map +0 -1
- package/dist/auth/admins.js +0 -90
- package/dist/auth/capabilities.d.ts +0 -7
- package/dist/auth/capabilities.d.ts.map +0 -1
- package/dist/auth/capabilities.js +0 -26
- package/dist/auth/config.d.ts +0 -2097
- package/dist/auth/config.d.ts.map +0 -1
- package/dist/auth/config.js +0 -78
- package/dist/auth/guard.d.ts +0 -34
- package/dist/auth/guard.d.ts.map +0 -1
- package/dist/auth/guard.js +0 -47
- package/dist/auth/index.d.ts +0 -5
- package/dist/auth/index.d.ts.map +0 -1
- package/dist/auth/index.js +0 -7
- package/dist/auth/schema.d.ts +0 -750
- package/dist/auth/schema.d.ts.map +0 -1
- package/dist/auth/schema.js +0 -93
- package/dist/carta.d.ts +0 -39
- package/dist/carta.d.ts.map +0 -1
- package/dist/carta.js +0 -30
- package/dist/components/CollectionList.svelte +0 -96
- package/dist/components/CollectionList.svelte.d.ts +0 -8
- package/dist/components/CollectionList.svelte.d.ts.map +0 -1
- package/dist/components/ManageAdmins.svelte +0 -84
- package/dist/components/ManageAdmins.svelte.d.ts +0 -10
- package/dist/components/ManageAdmins.svelte.d.ts.map +0 -1
- package/dist/content.d.ts +0 -3
- package/dist/content.d.ts.map +0 -1
- package/dist/content.js +0 -10
- package/dist/editor.d.ts +0 -25
- package/dist/editor.d.ts.map +0 -1
- package/dist/editor.js +0 -20
- package/dist/frontmatter.d.ts +0 -3
- package/dist/frontmatter.d.ts.map +0 -1
- package/dist/frontmatter.js +0 -16
- package/dist/github.d.ts +0 -72
- package/dist/github.d.ts.map +0 -1
- package/dist/github.js +0 -171
- package/dist/nav.d.ts.map +0 -1
- package/dist/slug.d.ts +0 -7
- package/dist/slug.d.ts.map +0 -1
- package/dist/slug.js +0 -15
- package/dist/utils.d.ts +0 -3
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js +0 -11
- package/src/lib/adapter.ts +0 -144
- package/src/lib/auth/admins.ts +0 -106
- package/src/lib/auth/capabilities.ts +0 -35
- package/src/lib/auth/config.ts +0 -108
- package/src/lib/auth/guard.ts +0 -60
- package/src/lib/auth/index.ts +0 -7
- package/src/lib/auth/schema.ts +0 -112
- package/src/lib/carta.ts +0 -59
- package/src/lib/components/CollectionList.svelte +0 -96
- package/src/lib/components/ManageAdmins.svelte +0 -84
- package/src/lib/content.ts +0 -11
- package/src/lib/editor.ts +0 -38
- package/src/lib/frontmatter.ts +0 -17
- package/src/lib/github.ts +0 -220
- package/src/lib/slug.ts +0 -16
- package/src/lib/utils.ts +0 -12
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
// The admin content routes: the load and action functions a site's /admin/** shims call.
|
|
2
|
+
// A factory closes over the composed runtime and the GitHub token mint, so the read and
|
|
3
|
+
// commit paths are unit-testable against a fetch double with an injected token, mirroring the
|
|
4
|
+
// email `send` injection in auth-routes. A shim stays one line: `export const load = routes.editLoad`.
|
|
5
|
+
import { redirect, error } from '@sveltejs/kit';
|
|
6
|
+
import { findConcept } from '../content/concepts.js';
|
|
7
|
+
import { frontmatterFromForm, parseMarkdown, dateInputValue, serializeMarkdown } from '../content/frontmatter.js';
|
|
8
|
+
import { isValidId, slugify, filenameFromId } from '../content/ids.js';
|
|
9
|
+
import { appCredentials } from '../github/credentials.js';
|
|
10
|
+
import { listMarkdown, readRaw, commitFile } from '../github/repo.js';
|
|
11
|
+
import { installationToken } from '../github/signing.js';
|
|
12
|
+
import { CommitConflictError } from '../github/types.js';
|
|
13
|
+
/** The signed-in editor the guard resolved, or a login redirect. Kept local to decouple event shapes. */
|
|
14
|
+
function sessionOf(event) {
|
|
15
|
+
const editor = event.locals.editor;
|
|
16
|
+
if (!editor)
|
|
17
|
+
throw redirect(303, '/admin/login');
|
|
18
|
+
return editor;
|
|
19
|
+
}
|
|
20
|
+
/** Look up the concept named by the `[concept]` route param, or a 404. */
|
|
21
|
+
function conceptOf(runtime, params) {
|
|
22
|
+
const concept = findConcept(runtime.concepts, params.concept ?? '');
|
|
23
|
+
if (!concept)
|
|
24
|
+
throw error(404, `Unknown content type: ${params.concept ?? ''}`);
|
|
25
|
+
return concept;
|
|
26
|
+
}
|
|
27
|
+
export function createContentRoutes(runtime, deps = {}) {
|
|
28
|
+
const mintToken = deps.mintToken ?? ((env) => installationToken(appCredentials(runtime.backend, env)));
|
|
29
|
+
/** Layout load for every admin page: the nav, the user, and the active path. */
|
|
30
|
+
function layoutLoad(event) {
|
|
31
|
+
const editor = sessionOf(event);
|
|
32
|
+
return {
|
|
33
|
+
siteName: runtime.siteName,
|
|
34
|
+
user: { displayName: editor.displayName, role: editor.role },
|
|
35
|
+
concepts: runtime.concepts.map((c) => ({ id: c.id, label: c.label })),
|
|
36
|
+
pathname: event.url.pathname,
|
|
37
|
+
canManageEditors: editor.role === 'owner',
|
|
38
|
+
navLabel: runtime.navMenu?.label ?? null,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/** Redirect /admin to the first concept's list (spec §7.6: land on the first concept). */
|
|
42
|
+
function indexRedirect() {
|
|
43
|
+
const first = runtime.concepts[0];
|
|
44
|
+
if (!first)
|
|
45
|
+
throw error(404, 'No content types configured');
|
|
46
|
+
throw redirect(307, `/admin/${first.id}`);
|
|
47
|
+
}
|
|
48
|
+
/** Read a file's frontmatter for its list row, degrading to the id on any read failure. */
|
|
49
|
+
async function summarize(file, token) {
|
|
50
|
+
try {
|
|
51
|
+
const raw = await readRaw(runtime.backend, file.path, token);
|
|
52
|
+
if (raw === null)
|
|
53
|
+
return { id: file.id, title: file.id, date: null, draft: false };
|
|
54
|
+
const { frontmatter } = parseMarkdown(raw);
|
|
55
|
+
const title = typeof frontmatter.title === 'string' && frontmatter.title.trim() ? frontmatter.title : file.id;
|
|
56
|
+
const date = dateInputValue(frontmatter.date) || null;
|
|
57
|
+
return { id: file.id, title, date, draft: frontmatter.draft === true };
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return { id: file.id, title: file.id, date: null, draft: false };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/** List a concept's entries. A listing failure degrades to an inline error, not a thrown 500. */
|
|
64
|
+
async function listLoad(event) {
|
|
65
|
+
sessionOf(event);
|
|
66
|
+
const concept = conceptOf(runtime, event.params);
|
|
67
|
+
const formError = event.url.searchParams.get('error');
|
|
68
|
+
const base = { conceptId: concept.id, label: concept.label, dated: concept.routing.dated, formError };
|
|
69
|
+
let token;
|
|
70
|
+
try {
|
|
71
|
+
token = await mintToken(event.platform?.env ?? {});
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return { ...base, entries: [], error: 'Could not authenticate with GitHub.' };
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
const files = await listMarkdown(runtime.backend, concept.dir, token);
|
|
78
|
+
const entries = await Promise.all(files.map((f) => summarize(f, token)));
|
|
79
|
+
return { ...base, entries, error: null };
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return { ...base, entries: [], error: 'Could not load this content type from GitHub.' };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/** Create a new entry: validate the slug, refuse to clobber, and redirect to the editor. */
|
|
86
|
+
async function createAction(event) {
|
|
87
|
+
sessionOf(event);
|
|
88
|
+
const concept = conceptOf(runtime, event.params);
|
|
89
|
+
const form = await event.request.formData();
|
|
90
|
+
const raw = String(form.get('slug') ?? '').trim() || slugify(String(form.get('title') ?? ''));
|
|
91
|
+
const bounce = (msg) => {
|
|
92
|
+
throw redirect(303, `/admin/${concept.id}?error=${encodeURIComponent(msg)}`);
|
|
93
|
+
};
|
|
94
|
+
if (!isValidId(raw))
|
|
95
|
+
bounce('Enter a valid slug: lowercase letters, numbers, and hyphens.');
|
|
96
|
+
const token = await mintToken(event.platform?.env ?? {});
|
|
97
|
+
const existing = await readRaw(runtime.backend, `${concept.dir}/${filenameFromId(raw)}`, token);
|
|
98
|
+
if (existing !== null)
|
|
99
|
+
bounce('An entry with that slug already exists.');
|
|
100
|
+
throw redirect(303, `/admin/${concept.id}/${raw}?new=1`);
|
|
101
|
+
}
|
|
102
|
+
/** Coerce parsed frontmatter to the form-ready values the editor inputs expect. */
|
|
103
|
+
function formValues(fields, frontmatter) {
|
|
104
|
+
const out = {};
|
|
105
|
+
for (const field of fields) {
|
|
106
|
+
const value = frontmatter[field.name];
|
|
107
|
+
if (field.type === 'date')
|
|
108
|
+
out[field.name] = dateInputValue(value);
|
|
109
|
+
else if (field.type === 'boolean')
|
|
110
|
+
out[field.name] = value === true;
|
|
111
|
+
else if (field.type === 'tags' || field.type === 'freetags')
|
|
112
|
+
out[field.name] = Array.isArray(value) ? value.map(String) : [];
|
|
113
|
+
else
|
|
114
|
+
out[field.name] = typeof value === 'string' ? value : value == null ? '' : String(value);
|
|
115
|
+
}
|
|
116
|
+
return out;
|
|
117
|
+
}
|
|
118
|
+
/** Open a file for editing. A `?new=1` miss yields a blank document; any other miss is a 404. */
|
|
119
|
+
async function editLoad(event) {
|
|
120
|
+
sessionOf(event);
|
|
121
|
+
const concept = conceptOf(runtime, event.params);
|
|
122
|
+
const id = event.params.id ?? '';
|
|
123
|
+
if (!isValidId(id))
|
|
124
|
+
throw error(400, 'Invalid entry id');
|
|
125
|
+
const isNew = event.url.searchParams.get('new') === '1';
|
|
126
|
+
const token = await mintToken(event.platform?.env ?? {});
|
|
127
|
+
const raw = await readRaw(runtime.backend, `${concept.dir}/${filenameFromId(id)}`, token);
|
|
128
|
+
if (raw === null && !isNew)
|
|
129
|
+
throw error(404, 'Entry not found');
|
|
130
|
+
const parsed = raw === null ? { frontmatter: {}, body: '' } : parseMarkdown(raw);
|
|
131
|
+
const title = typeof parsed.frontmatter.title === 'string' && parsed.frontmatter.title.trim() ? parsed.frontmatter.title : id;
|
|
132
|
+
return {
|
|
133
|
+
conceptId: concept.id,
|
|
134
|
+
id,
|
|
135
|
+
label: concept.label,
|
|
136
|
+
fields: concept.fields,
|
|
137
|
+
frontmatter: formValues(concept.fields, parsed.frontmatter),
|
|
138
|
+
body: parsed.body,
|
|
139
|
+
title,
|
|
140
|
+
isNew,
|
|
141
|
+
saved: event.url.searchParams.get('saved') === '1',
|
|
142
|
+
error: event.url.searchParams.get('error'),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/** Match a commit conflict by class and by name (bundling can alias the class identity). */
|
|
146
|
+
function isConflict(err) {
|
|
147
|
+
return err instanceof CommitConflictError || err?.name === 'CommitConflictError';
|
|
148
|
+
}
|
|
149
|
+
/** Save an edit: validate, then commit with the session editor as author. Fails safe on 409. */
|
|
150
|
+
async function saveAction(event) {
|
|
151
|
+
const editor = sessionOf(event);
|
|
152
|
+
const concept = conceptOf(runtime, event.params);
|
|
153
|
+
const id = event.params.id ?? '';
|
|
154
|
+
// Confine the commit path to the concept dir, built from a validated id (the App token can
|
|
155
|
+
// write anywhere in the repo). Reject before touching GitHub.
|
|
156
|
+
if (!isValidId(id))
|
|
157
|
+
throw error(400, 'Invalid entry id');
|
|
158
|
+
const path = `${concept.dir}/${filenameFromId(id)}`;
|
|
159
|
+
const form = await event.request.formData();
|
|
160
|
+
const body = String(form.get('body') ?? '');
|
|
161
|
+
const isNew = form.get('new') === '1';
|
|
162
|
+
const suffix = isNew ? '&new=1' : '';
|
|
163
|
+
const result = concept.validate(frontmatterFromForm(concept.fields, form), body);
|
|
164
|
+
if (!result.ok) {
|
|
165
|
+
const message = Object.values(result.errors)[0] ?? 'Invalid frontmatter';
|
|
166
|
+
throw redirect(303, `/admin/${concept.id}/${id}?error=${encodeURIComponent(message)}${suffix}`);
|
|
167
|
+
}
|
|
168
|
+
const markdown = serializeMarkdown(result.data, body);
|
|
169
|
+
const token = await mintToken(event.platform?.env ?? {});
|
|
170
|
+
try {
|
|
171
|
+
await commitFile(runtime.backend, path, markdown, { message: `Update ${concept.label.toLowerCase()}: ${id}`, author: { name: editor.displayName, email: editor.email } }, token);
|
|
172
|
+
}
|
|
173
|
+
catch (err) {
|
|
174
|
+
if (isConflict(err)) {
|
|
175
|
+
const message = 'This file changed since you opened it. Reload and reapply your edits.';
|
|
176
|
+
throw redirect(303, `/admin/${concept.id}/${id}?error=${encodeURIComponent(message)}${suffix}`);
|
|
177
|
+
}
|
|
178
|
+
throw err;
|
|
179
|
+
}
|
|
180
|
+
throw redirect(303, `/admin/${concept.id}/${id}?saved=1`);
|
|
181
|
+
}
|
|
182
|
+
return { layoutLoad, indexRedirect, listLoad, createAction, editLoad, saveAction, mintToken };
|
|
183
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Editor } from '../auth/types.js';
|
|
2
|
+
import type { RequestContext } from './types.js';
|
|
3
|
+
export declare function createEditorRoutes(): {
|
|
4
|
+
editorsLoad: (event: RequestContext) => Promise<{
|
|
5
|
+
editors: Editor[];
|
|
6
|
+
self: string;
|
|
7
|
+
}>;
|
|
8
|
+
addEditorAction: (event: RequestContext) => Promise<import("@sveltejs/kit").ActionFailure<{
|
|
9
|
+
error: string;
|
|
10
|
+
}> | {
|
|
11
|
+
ok: true;
|
|
12
|
+
}>;
|
|
13
|
+
removeEditorAction: (event: RequestContext) => Promise<import("@sveltejs/kit").ActionFailure<{
|
|
14
|
+
error: string;
|
|
15
|
+
}> | {
|
|
16
|
+
ok: true;
|
|
17
|
+
}>;
|
|
18
|
+
setRoleAction: (event: RequestContext) => Promise<import("@sveltejs/kit").ActionFailure<{
|
|
19
|
+
error: string;
|
|
20
|
+
}> | {
|
|
21
|
+
ok: true;
|
|
22
|
+
}>;
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=editors-routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editors-routes.d.ts","sourceRoot":"","sources":["../../src/lib/sveltekit/editors-routes.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,MAAM,EAAQ,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAQjD,wBAAgB,kBAAkB;yBAEE,cAAc,KAAG,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;6BAOzD,cAAc;;;;;gCAcX,cAAc;;;;;2BAgBnB,cAAc;;;;;EAiBnD"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Owner-gated editor management. The editor table is the allowlist, so add and remove are
|
|
2
|
+
// insert and delete. The anti-lockout rule is the last remaining owner: the system refuses to
|
|
3
|
+
// drop below one owner (spec 7.1), enforced in the store by an atomic guarded write rather
|
|
4
|
+
// than a separate count, so concurrent removals cannot strand the allowlist at zero owners.
|
|
5
|
+
import { fail } from '@sveltejs/kit';
|
|
6
|
+
import { requireOwner } from './guard.js';
|
|
7
|
+
import { requireDb } from '../env.js';
|
|
8
|
+
import { listEditors, findEditor, insertEditor, deleteEditor, setEditorRole, removeOwnerIfNotLast, demoteOwnerIfNotLast, } from '../auth/store.js';
|
|
9
|
+
const EMAIL_RE = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
|
|
10
|
+
function parseRole(value) {
|
|
11
|
+
return value === 'owner' ? 'owner' : 'editor';
|
|
12
|
+
}
|
|
13
|
+
export function createEditorRoutes() {
|
|
14
|
+
/** GET /admin/editors. Owner-only. Returns the allowlist and the acting owner's email. */
|
|
15
|
+
async function editorsLoad(event) {
|
|
16
|
+
const owner = requireOwner(event);
|
|
17
|
+
const editors = await listEditors(requireDb(event.platform?.env ?? {}));
|
|
18
|
+
return { editors, self: owner.email };
|
|
19
|
+
}
|
|
20
|
+
/** POST add an editor. Owner-only. */
|
|
21
|
+
async function addEditorAction(event) {
|
|
22
|
+
requireOwner(event);
|
|
23
|
+
const db = requireDb(event.platform?.env ?? {});
|
|
24
|
+
const form = await event.request.formData();
|
|
25
|
+
const email = String(form.get('email') ?? '').trim().toLowerCase();
|
|
26
|
+
const name = String(form.get('name') ?? '').trim();
|
|
27
|
+
const role = parseRole(form.get('role'));
|
|
28
|
+
if (!EMAIL_RE.test(email) || !name)
|
|
29
|
+
return fail(400, { error: 'Enter a valid email and name' });
|
|
30
|
+
if (await findEditor(db, email))
|
|
31
|
+
return fail(400, { error: 'That editor already exists' });
|
|
32
|
+
await insertEditor(db, email, name, role, Date.now());
|
|
33
|
+
return { ok: true };
|
|
34
|
+
}
|
|
35
|
+
/** POST remove an editor. Owner-only. Refuses the last owner, atomically. */
|
|
36
|
+
async function removeEditorAction(event) {
|
|
37
|
+
requireOwner(event);
|
|
38
|
+
const db = requireDb(event.platform?.env ?? {});
|
|
39
|
+
const form = await event.request.formData();
|
|
40
|
+
const email = String(form.get('email') ?? '').trim().toLowerCase();
|
|
41
|
+
const target = await findEditor(db, email);
|
|
42
|
+
if (!target)
|
|
43
|
+
return fail(400, { error: 'No such editor' });
|
|
44
|
+
if (target.role === 'owner') {
|
|
45
|
+
if (!(await removeOwnerIfNotLast(db, email)))
|
|
46
|
+
return fail(400, { error: 'You cannot remove the last owner' });
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
await deleteEditor(db, email);
|
|
50
|
+
}
|
|
51
|
+
return { ok: true };
|
|
52
|
+
}
|
|
53
|
+
/** POST change an editor's role. Owner-only. Refuses demoting the last owner, atomically. */
|
|
54
|
+
async function setRoleAction(event) {
|
|
55
|
+
requireOwner(event);
|
|
56
|
+
const db = requireDb(event.platform?.env ?? {});
|
|
57
|
+
const form = await event.request.formData();
|
|
58
|
+
const email = String(form.get('email') ?? '').trim().toLowerCase();
|
|
59
|
+
const role = parseRole(form.get('role'));
|
|
60
|
+
const target = await findEditor(db, email);
|
|
61
|
+
if (!target)
|
|
62
|
+
return fail(400, { error: 'No such editor' });
|
|
63
|
+
if (role === 'editor' && target.role === 'owner') {
|
|
64
|
+
if (!(await demoteOwnerIfNotLast(db, email)))
|
|
65
|
+
return fail(400, { error: 'You cannot demote the last owner' });
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
await setEditorRole(db, email, role);
|
|
69
|
+
}
|
|
70
|
+
return { ok: true };
|
|
71
|
+
}
|
|
72
|
+
return { editorsLoad, addEditorAction, removeEditorAction, setRoleAction };
|
|
73
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Editor } from '../auth/types.js';
|
|
2
|
+
import type { HandleInput, RequestContext } from './types.js';
|
|
3
|
+
/** The SvelteKit `Handle` that guards `/admin/**`. */
|
|
4
|
+
export declare function createAuthGuard(): ({ event, resolve }: HandleInput) => Promise<Response>;
|
|
5
|
+
/** For a protected load/action: the session the guard already resolved, or a login redirect. */
|
|
6
|
+
export declare function requireSession(event: RequestContext): Editor;
|
|
7
|
+
/** For the management surface: a signed-in owner, or 403 for an editor. */
|
|
8
|
+
export declare function requireOwner(event: RequestContext): Editor;
|
|
9
|
+
//# sourceMappingURL=guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../src/lib/sveltekit/guard.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAW9D,sDAAsD;AACtD,wBAAgB,eAAe,KACA,oBAAoB,WAAW,KAAG,OAAO,CAAC,QAAQ,CAAC,CAYjF;AAED,gGAAgG;AAChG,wBAAgB,cAAc,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAI5D;AAED,2EAA2E;AAC3E,wBAAgB,YAAY,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAI1D"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// The /admin guard, plus the per-load owner/session gates. A site's hooks.server.ts sets
|
|
2
|
+
// `export const handle = createAuthGuard()`. Events are typed structurally, so the engine
|
|
3
|
+
// stays free of a site's App.* ambient types.
|
|
4
|
+
import { redirect, error } from '@sveltejs/kit';
|
|
5
|
+
import { resolveSession } from '../auth/store.js';
|
|
6
|
+
import { COOKIE_NAME } from '../auth/crypto.js';
|
|
7
|
+
/** The login page and the auth endpoints are public; everything else under /admin is gated. */
|
|
8
|
+
function isPublicAdminPath(pathname) {
|
|
9
|
+
return pathname === '/admin/login' || pathname.startsWith('/admin/auth/');
|
|
10
|
+
}
|
|
11
|
+
function isAdminPath(pathname) {
|
|
12
|
+
return pathname === '/admin' || pathname.startsWith('/admin/');
|
|
13
|
+
}
|
|
14
|
+
/** The SvelteKit `Handle` that guards `/admin/**`. */
|
|
15
|
+
export function createAuthGuard() {
|
|
16
|
+
return async function handle({ event, resolve }) {
|
|
17
|
+
const { pathname } = event.url;
|
|
18
|
+
if (!isAdminPath(pathname) || isPublicAdminPath(pathname)) {
|
|
19
|
+
return resolve(event);
|
|
20
|
+
}
|
|
21
|
+
const env = event.platform?.env ?? {};
|
|
22
|
+
const id = event.cookies.get(COOKIE_NAME);
|
|
23
|
+
const editor = id && env.AUTH_DB ? await resolveSession(env.AUTH_DB, id, Date.now()) : null;
|
|
24
|
+
if (!editor)
|
|
25
|
+
throw redirect(303, '/admin/login');
|
|
26
|
+
event.locals.editor = editor;
|
|
27
|
+
return resolve(event);
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/** For a protected load/action: the session the guard already resolved, or a login redirect. */
|
|
31
|
+
export function requireSession(event) {
|
|
32
|
+
const editor = event.locals.editor;
|
|
33
|
+
if (!editor)
|
|
34
|
+
throw redirect(303, '/admin/login');
|
|
35
|
+
return editor;
|
|
36
|
+
}
|
|
37
|
+
/** For the management surface: a signed-in owner, or 403 for an editor. */
|
|
38
|
+
export function requireOwner(event) {
|
|
39
|
+
const editor = requireSession(event);
|
|
40
|
+
if (editor.role !== 'owner')
|
|
41
|
+
throw error(403, 'Owner access required');
|
|
42
|
+
return editor;
|
|
43
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { CairnRuntime } from '../content/types.js';
|
|
2
|
+
import type { GithubKeyEnv } from '../github/credentials.js';
|
|
3
|
+
/** The `/admin/healthz` payload. */
|
|
4
|
+
export interface HealthData {
|
|
5
|
+
ok: boolean;
|
|
6
|
+
checks: {
|
|
7
|
+
githubAppSigning: {
|
|
8
|
+
ok: boolean;
|
|
9
|
+
detail?: string;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/** Run the signing self-test against the configured App id and the Worker's key secret. */
|
|
14
|
+
export declare function healthLoad(event: {
|
|
15
|
+
platform?: {
|
|
16
|
+
env?: GithubKeyEnv;
|
|
17
|
+
};
|
|
18
|
+
}, runtime: CairnRuntime): Promise<HealthData>;
|
|
19
|
+
//# sourceMappingURL=health.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/lib/sveltekit/health.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAE7D,oCAAoC;AACpC,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE;QAAE,gBAAgB,EAAE;YAAE,EAAE,EAAE,OAAO,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;CAChE;AAED,2FAA2F;AAC3F,wBAAsB,UAAU,CAC9B,KAAK,EAAE;IAAE,QAAQ,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,YAAY,CAAA;KAAE,CAAA;CAAE,EAC5C,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,UAAU,CAAC,CAMrB"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// GET /admin/healthz. Signs a dummy JWT through the real App-signing path so a broken
|
|
2
|
+
// PKCS#1-to-PKCS#8 conversion is caught early (spec §7.8). The payload is pass/fail and a
|
|
3
|
+
// coarse detail only; it never carries the key or a token.
|
|
4
|
+
import { signingSelfTest } from '../github/signing.js';
|
|
5
|
+
/** Run the signing self-test against the configured App id and the Worker's key secret. */
|
|
6
|
+
export async function healthLoad(event, runtime) {
|
|
7
|
+
const key = event.platform?.env?.GITHUB_APP_PRIVATE_KEY_B64;
|
|
8
|
+
const githubAppSigning = key
|
|
9
|
+
? await signingSelfTest(runtime.backend.appId, key)
|
|
10
|
+
: { ok: false, detail: 'GITHUB_APP_PRIVATE_KEY_B64 is not configured' };
|
|
11
|
+
return { ok: githubAppSigning.ok, checks: { githubAppSigning } };
|
|
12
|
+
}
|
|
@@ -1,174 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
interface PlatformEvent {
|
|
11
|
-
platform?: {
|
|
12
|
-
env?: AdminEnv;
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
/** A collection reduced to what the sidebar nav needs (no plugin graph crosses to the client). */
|
|
16
|
-
export interface NavCollection {
|
|
17
|
-
type: string;
|
|
18
|
-
label: string;
|
|
19
|
-
}
|
|
20
|
-
export interface AdminLayoutData {
|
|
21
|
-
user: CairnUser | null;
|
|
22
|
-
siteName: string;
|
|
23
|
-
pathname: string;
|
|
24
|
-
collections: NavCollection[];
|
|
25
|
-
/** Managed menus (name+label only) so the shell can show a Navigation entry. */
|
|
26
|
-
navMenus: {
|
|
27
|
-
name: string;
|
|
28
|
-
label: string;
|
|
29
|
-
}[];
|
|
30
|
-
/** Whether the viewer may manage navigation (gates the Navigation nav entry). */
|
|
31
|
-
canManageNav: boolean;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Branding, session, and collection nav for every admin page. `siteName` and the collection
|
|
35
|
-
* list flow from the adapter without pulling its plugin graph into client bundles (the import
|
|
36
|
-
* stays server-side in the layout load; only `{type,label}` crosses). `pathname` lets the
|
|
37
|
-
* shared shell highlight the active nav item without a `$app/*` import (those kit virtual
|
|
38
|
-
* modules have no types outside a kit app); reading `event.url` also opts the layout load into
|
|
39
|
-
* rerunning on navigation, keeping the active class correct.
|
|
40
|
-
*/
|
|
41
|
-
export declare function adminLayoutLoad(event: {
|
|
42
|
-
locals: {
|
|
43
|
-
user: CairnUser | null;
|
|
44
|
-
};
|
|
45
|
-
url: URL;
|
|
46
|
-
}, adapter: CairnAdapter): AdminLayoutData;
|
|
47
|
-
/**
|
|
48
|
-
* The `/admin` index has no content of its own now that each collection is its own page; send
|
|
49
|
-
* the editor straight to the first collection's entries list (a Sveltia-style landing).
|
|
50
|
-
*/
|
|
51
|
-
export declare function adminIndexRedirect(adapter: CairnAdapter): never;
|
|
52
|
-
/** One entry row: id (filename stem), display title, optional date, draft flag. */
|
|
53
|
-
export interface CollectionEntry {
|
|
54
|
-
id: string;
|
|
55
|
-
path: string;
|
|
56
|
-
title: string;
|
|
57
|
-
date: string | null;
|
|
58
|
-
draft: boolean;
|
|
59
|
-
}
|
|
60
|
-
export interface CollectionListData {
|
|
61
|
-
type: string;
|
|
62
|
-
label: string;
|
|
63
|
-
kind: 'page' | 'story';
|
|
64
|
-
entries: CollectionEntry[];
|
|
65
|
-
/** Set when the directory listing itself failed (rate limit, network). */
|
|
66
|
-
error?: string;
|
|
67
|
-
/** A create-flow error bounced back via `?error=` (an invalid or taken slug). */
|
|
68
|
-
formError: string | null;
|
|
69
|
-
/** Whether the viewer may create an entry in this collection (page-create is owner-only). */
|
|
70
|
-
canCreate: boolean;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* List one collection's entries, reading each file's frontmatter for the display title, date,
|
|
74
|
-
* and draft badge. Reads run in parallel; a single failed read degrades that row to the slug
|
|
75
|
-
* (rather than failing the page), and a failed directory listing returns an inline `error`.
|
|
76
|
-
* Collections are small here; the 1,000-entry / Git-Trees sharding concern is risk #11, deferred.
|
|
77
|
-
*/
|
|
78
|
-
export declare function collectionListLoad(event: PlatformEvent & {
|
|
79
|
-
params: {
|
|
80
|
-
collection: string;
|
|
81
|
-
};
|
|
82
|
-
url: URL;
|
|
83
|
-
locals: {
|
|
84
|
-
user: CairnUser | null;
|
|
85
|
-
};
|
|
86
|
-
}, adapter: CairnAdapter): Promise<CollectionListData>;
|
|
87
|
-
/**
|
|
88
|
-
* The "New entry" form action. Validates the requested slug, rejects one that already exists,
|
|
89
|
-
* then redirects into the editor in create mode (`?new=1`, where `editLoad` serves a blank
|
|
90
|
-
* document and `saveCommit`'s create path commits a new file). cairn is filename-based, so the
|
|
91
|
-
* slug is the filename stem the author types; a title-driven auto-slug is a later (Pass K) concern.
|
|
92
|
-
*/
|
|
93
|
-
export declare function createEntry(event: PlatformEvent & {
|
|
94
|
-
params: {
|
|
95
|
-
collection: string;
|
|
96
|
-
};
|
|
97
|
-
locals: {
|
|
98
|
-
user: CairnUser | null;
|
|
99
|
-
};
|
|
100
|
-
request: Request;
|
|
101
|
-
}, adapter: CairnAdapter): Promise<never>;
|
|
102
|
-
export interface EditData {
|
|
103
|
-
type: string;
|
|
104
|
-
id: string;
|
|
105
|
-
label: string;
|
|
106
|
-
kind: 'page' | 'story';
|
|
107
|
-
fields: CairnField[];
|
|
108
|
-
path: string;
|
|
109
|
-
body: string;
|
|
110
|
-
frontmatter: Record<string, unknown>;
|
|
111
|
-
title: string;
|
|
112
|
-
saved: boolean;
|
|
113
|
-
error: string | null;
|
|
114
|
-
/** True when editing a not-yet-committed new entry (reached via `?new=1`). */
|
|
115
|
-
isNew: boolean;
|
|
116
|
-
}
|
|
117
|
-
export declare function editLoad(event: PlatformEvent & {
|
|
118
|
-
params: {
|
|
119
|
-
type: string;
|
|
120
|
-
id: string;
|
|
121
|
-
};
|
|
122
|
-
url: URL;
|
|
123
|
-
}, adapter: CairnAdapter): Promise<EditData>;
|
|
124
|
-
export declare function saveCommit(event: PlatformEvent & {
|
|
125
|
-
request: Request;
|
|
126
|
-
locals: {
|
|
127
|
-
user: CairnUser | null;
|
|
128
|
-
};
|
|
129
|
-
}, adapter: CairnAdapter): Promise<never>;
|
|
130
|
-
/** A page the picker can insert: its display label and the URL the nav item points at. */
|
|
131
|
-
export interface NavPageOption {
|
|
132
|
-
label: string;
|
|
133
|
-
url: string;
|
|
134
|
-
}
|
|
135
|
-
export interface NavLoadData {
|
|
136
|
-
menu: {
|
|
137
|
-
name: string;
|
|
138
|
-
label: string;
|
|
139
|
-
maxDepth: number;
|
|
140
|
-
};
|
|
141
|
-
tree: NavNode[];
|
|
142
|
-
pages: NavPageOption[];
|
|
143
|
-
saved: boolean;
|
|
144
|
-
error: string | null;
|
|
145
|
-
}
|
|
146
|
-
export declare function navLoad(event: PlatformEvent & {
|
|
147
|
-
locals: {
|
|
148
|
-
user: CairnUser | null;
|
|
149
|
-
};
|
|
150
|
-
url: URL;
|
|
151
|
-
}, adapter: CairnAdapter): Promise<NavLoadData>;
|
|
152
|
-
export declare function navSave(event: PlatformEvent & {
|
|
153
|
-
locals: {
|
|
154
|
-
user: CairnUser | null;
|
|
155
|
-
};
|
|
156
|
-
request: Request;
|
|
157
|
-
}, adapter: CairnAdapter): Promise<never>;
|
|
158
|
-
export interface HealthData {
|
|
159
|
-
ok: boolean;
|
|
160
|
-
checks: {
|
|
161
|
-
githubAppSigning: {
|
|
162
|
-
ok: boolean;
|
|
163
|
-
detail?: string;
|
|
164
|
-
};
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* Deploy-time health check (M2): signs a dummy App JWT to prove the GitHub App key loads and
|
|
169
|
-
* the PKCS#1→PKCS#8 conversion still works, before an editor hits it on save. Behind the
|
|
170
|
-
* `/admin` guard (signed-in editors only); returns ok/fail with no secret in the body.
|
|
171
|
-
*/
|
|
172
|
-
export declare function healthLoad(event: PlatformEvent): Promise<HealthData>;
|
|
173
|
-
export {};
|
|
1
|
+
export { createAuthGuard, requireSession, requireOwner } from './guard.js';
|
|
2
|
+
export { createAuthRoutes, type AuthRoutesConfig } from './auth-routes.js';
|
|
3
|
+
export { createEditorRoutes } from './editors-routes.js';
|
|
4
|
+
export { createContentRoutes } from './content-routes.js';
|
|
5
|
+
export type { NavConcept, LayoutData, EntrySummary, ListData, EditData, ContentEvent, ContentRoutesDeps, } from './content-routes.js';
|
|
6
|
+
export { createNavRoutes } from './nav-routes.js';
|
|
7
|
+
export type { NavLoadData, NavPageOption, NavRoutesDeps } from './nav-routes.js';
|
|
8
|
+
export { healthLoad, type HealthData } from './health.js';
|
|
9
|
+
export type { RequestContext, CookieJar, HandleInput } from './types.js';
|
|
174
10
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/sveltekit/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/sveltekit/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,YAAY,EACV,UAAU,EACV,UAAU,EACV,YAAY,EACZ,QAAQ,EACR,QAAQ,EACR,YAAY,EACZ,iBAAiB,GAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACjF,OAAO,EAAE,UAAU,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAC1D,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
|