wiki-formant 0.86.1 → 0.86.3
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/README.md +29 -39
- package/bin/_cli.mjs +1 -10
- package/dist/base.css +157 -144
- package/dist/blocks.d.ts +5 -2
- package/dist/headers.d.ts +5 -9
- package/dist/headers.js +12 -15
- package/dist/knip.d.ts +3 -3
- package/dist/knip.js +29 -28
- package/dist/link-check.d.ts +27 -26
- package/dist/link-check.js +44 -38
- package/dist/passkey.d.ts +1 -1
- package/dist/passkey.js +12 -8
- package/dist/scripts.d.ts +55 -148
- package/dist/scripts.js +81 -75
- package/dist/validation.d.ts +23 -30
- package/dist/validation.js +23 -17
- package/dist/well-known.d.ts +18 -15
- package/dist/well-known.js +20 -18
- package/package.json +1 -1
package/dist/knip.js
CHANGED
|
@@ -1,31 +1,32 @@
|
|
|
1
|
-
// knip.ts — the
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
'
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
entry,
|
|
1
|
+
// knip.ts — the knip configuration every app here shares: what the App
|
|
2
|
+
// Router loads by convention and nothing imports, the scripts the routines
|
|
3
|
+
// run from the command line, and the two dependencies no TS import names.
|
|
4
|
+
// An app's `knip.ts` spreads it and adds what is its own:
|
|
5
|
+
//
|
|
6
|
+
// import base from 'wiki-formant/knip';
|
|
7
|
+
// export default { ...base, ignoreBinaries: [...base.ignoreBinaries, 'ffmpeg'] };
|
|
8
|
+
const config = {
|
|
9
|
+
$schema: 'https://unpkg.com/knip@6/schema.json',
|
|
10
|
+
entry: [
|
|
11
|
+
'src/app/**/page.tsx',
|
|
12
|
+
'src/app/**/layout.tsx',
|
|
13
|
+
'src/app/**/route.{ts,tsx}',
|
|
14
|
+
'src/app/**/{error,not-found,loading,global-error}.tsx',
|
|
15
|
+
// A dotted directory: a glob that does not name it skips the agent surface.
|
|
16
|
+
'src/app/.well-known/**/route.ts',
|
|
17
|
+
'src/app/**/{sitemap,robots,manifest,opengraph-image,icon,apple-icon}.{ts,tsx}',
|
|
18
|
+
'src/**/*.test.ts',
|
|
19
|
+
'scripts/**/*.{ts,mts,mjs}',
|
|
20
|
+
'studio/**/*.mjs',
|
|
21
|
+
],
|
|
23
22
|
project: ['src/**/*.{ts,tsx}', 'scripts/**/*.{ts,mts,mjs}', 'studio/**/*.mjs'],
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
ignoreDependencies: [
|
|
24
|
+
// Reached through `@tailwindcss/postcss` and the `@import` in globals.css.
|
|
25
|
+
'tailwindcss',
|
|
26
|
+
// Imported by the generated Prisma client.
|
|
27
|
+
'@prisma/client',
|
|
28
|
+
],
|
|
29
|
+
// The Vercel CLI the scripts shell out to for a cache tag; a global, never a dependency.
|
|
30
30
|
ignoreBinaries: ['vercel'],
|
|
31
31
|
};
|
|
32
|
+
export default config;
|
package/dist/link-check.d.ts
CHANGED
|
@@ -76,51 +76,52 @@ export declare function collectBlockLinks<B extends {
|
|
|
76
76
|
}>(blocks: readonly B[], containers?: (block: B) => B[][] | null): BlockLinks;
|
|
77
77
|
/** `Promise.all` with a ceiling, preserving input order in the results. */
|
|
78
78
|
export declare function mapLimit<T, R>(items: readonly T[], limit: number, fn: (item: T, index: number) => Promise<R>): Promise<R[]>;
|
|
79
|
-
/** A page as
|
|
79
|
+
/** A page as the audit reads it: its site path and its stored blocks. */
|
|
80
80
|
export interface AuditPage {
|
|
81
81
|
path: string;
|
|
82
82
|
content: unknown;
|
|
83
83
|
metadata?: unknown;
|
|
84
84
|
}
|
|
85
|
-
export interface AuditOptions {
|
|
86
|
-
|
|
85
|
+
export interface AuditOptions<B extends {
|
|
86
|
+
type: string;
|
|
87
|
+
}> {
|
|
88
|
+
/** Site-relative paths that exist, with trailing slashes dropped. */
|
|
87
89
|
livePaths: ReadonlySet<string>;
|
|
88
|
-
/**
|
|
90
|
+
/** The probe for one external URL. Default `probeExternal`. */
|
|
89
91
|
probe?: (url: string) => Promise<Probe>;
|
|
90
|
-
/**
|
|
91
|
-
* Whether an internal path is the wiki's to check. One it is not (an app
|
|
92
|
-
* route, say) is kept in `skipped` and never reported broken. Default all.
|
|
93
|
-
*/
|
|
92
|
+
/** Whether a site-relative link is this checker's to verify. Default every one. */
|
|
94
93
|
internal?: (path: string) => boolean;
|
|
95
|
-
/** Default 12. */
|
|
96
94
|
concurrency?: number;
|
|
95
|
+
containers?: (block: B) => B[][] | null;
|
|
97
96
|
}
|
|
98
|
-
export type Referenced<T> = T & {
|
|
99
|
-
pages: string[];
|
|
100
|
-
};
|
|
101
97
|
export interface LinkAudit {
|
|
102
98
|
externalChecked: number;
|
|
103
|
-
brokenExternal:
|
|
104
|
-
|
|
99
|
+
brokenExternal: Array<Probe & {
|
|
100
|
+
pages: string[];
|
|
101
|
+
}>;
|
|
102
|
+
brokenInternal: Array<{
|
|
105
103
|
url: string;
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
/** Every embed, unprobed: a status check on one proves little on its own. */
|
|
104
|
+
pages: string[];
|
|
105
|
+
}>;
|
|
106
|
+
/** Every embed, each with the pages that carry it, for a checker that probes them. */
|
|
110
107
|
embeds: Map<string, {
|
|
111
108
|
kind: string;
|
|
112
109
|
pages: string[];
|
|
113
110
|
}>;
|
|
111
|
+
/** Internal links `internal` declined, each with its pages. */
|
|
112
|
+
skipped: Map<string, string[]>;
|
|
114
113
|
}
|
|
115
114
|
/**
|
|
116
|
-
* Every link on every page, probed once
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
115
|
+
* Every link on every page, probed once each, with the pages that carry it.
|
|
116
|
+
* Metadata strings that name a site count as external links: a wiki's facts
|
|
117
|
+
* panel cites its sources there, sometimes as a bare domain. Two checkers had each written this walk; what
|
|
118
|
+
* they still do themselves is decide the live paths, the probe and the report.
|
|
120
119
|
*/
|
|
121
|
-
export declare function auditLinks
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
120
|
+
export declare function auditLinks<B extends {
|
|
121
|
+
type: string;
|
|
122
|
+
}>(pages: readonly AuditPage[], opts: AuditOptions<B>): Promise<LinkAudit>;
|
|
123
|
+
/** The human report's first lines: what was checked, and what is broken. */
|
|
124
|
+
export declare function printLinkAudit(audit: LinkAudit, { scope, pagesScanned }: {
|
|
125
|
+
scope?: string;
|
|
125
126
|
pagesScanned: number;
|
|
126
127
|
}): void;
|
package/dist/link-check.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// link-check.ts — the dead-link probe wiki sweep scripts share.
|
|
2
2
|
//
|
|
3
|
-
// Node-only (fetch, AbortSignal, URL). No framework, no database:
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
3
|
+
// Node-only (fetch, AbortSignal, URL). No framework, no database: what a
|
|
4
|
+
// checker does with the verdicts — which pages to walk, what counts as an
|
|
5
|
+
// internal path, how to report — stays with the caller, because that is the
|
|
6
|
+
// half that is genuinely per-wiki.
|
|
7
7
|
//
|
|
8
8
|
// EVERY GUARD BELOW WAS PAID FOR BY A FALSE POSITIVE. npmjs.com 403s scripted
|
|
9
9
|
// requests, an expired certificate is not a dead host, a YouTube /embed/ URL
|
|
@@ -321,62 +321,68 @@ export async function mapLimit(items, limit, fn) {
|
|
|
321
321
|
await Promise.all(workers);
|
|
322
322
|
return results;
|
|
323
323
|
}
|
|
324
|
-
|
|
325
|
-
// rots like a citation, but lives outside the blocks; a bare domain counts.
|
|
324
|
+
/** A metadata value that names a site: an absolute URL as it is, a bare `www.` or `host.tld` as https. */
|
|
326
325
|
const metadataUrl = (v) => {
|
|
327
326
|
const s = typeof v === 'string' ? v.trim() : '';
|
|
328
327
|
if (/^https?:\/\//i.test(s))
|
|
329
328
|
return s;
|
|
330
329
|
return /^www\./i.test(s) || /^[a-z0-9.-]+\.[a-z]{2,}(\/|$)/i.test(s) ? `https://${s}` : null;
|
|
331
330
|
};
|
|
331
|
+
const push = (map, key, value) => {
|
|
332
|
+
const arr = map.get(key);
|
|
333
|
+
if (arr)
|
|
334
|
+
arr.push(value);
|
|
335
|
+
else
|
|
336
|
+
map.set(key, [value]);
|
|
337
|
+
};
|
|
332
338
|
/**
|
|
333
|
-
* Every link on every page, probed once
|
|
334
|
-
*
|
|
335
|
-
*
|
|
336
|
-
*
|
|
339
|
+
* Every link on every page, probed once each, with the pages that carry it.
|
|
340
|
+
* Metadata strings that name a site count as external links: a wiki's facts
|
|
341
|
+
* panel cites its sources there, sometimes as a bare domain. Two checkers had each written this walk; what
|
|
342
|
+
* they still do themselves is decide the live paths, the probe and the report.
|
|
337
343
|
*/
|
|
338
344
|
export async function auditLinks(pages, opts) {
|
|
339
|
-
const { livePaths, probe = probeExternal, internal = () => true, concurrency = 12 } = opts;
|
|
345
|
+
const { livePaths, probe = probeExternal, internal = () => true, concurrency = 12, containers = coreBlockShape().containers } = opts;
|
|
340
346
|
const external = new Map();
|
|
341
|
-
const
|
|
347
|
+
const internalLinks = new Map();
|
|
342
348
|
const skipped = new Map();
|
|
343
349
|
const embeds = new Map();
|
|
344
|
-
const cite = (map, key, page) => {
|
|
345
|
-
const refs = map.get(key);
|
|
346
|
-
if (!refs)
|
|
347
|
-
map.set(key, [page]);
|
|
348
|
-
else if (!refs.includes(page))
|
|
349
|
-
refs.push(page);
|
|
350
|
-
};
|
|
351
350
|
for (const page of pages) {
|
|
352
|
-
const
|
|
353
|
-
const
|
|
354
|
-
for (const
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
351
|
+
const blocks = Array.isArray(page.content) ? page.content : [];
|
|
352
|
+
const links = collectBlockLinks(blocks, containers);
|
|
353
|
+
for (const v of Object.values((page.metadata ?? {}))) {
|
|
354
|
+
const url = metadataUrl(v);
|
|
355
|
+
if (url)
|
|
356
|
+
links.external.push(url);
|
|
357
|
+
}
|
|
358
|
+
for (const u of new Set(links.external))
|
|
359
|
+
push(external, u, page.path);
|
|
360
|
+
for (const u of new Set(links.internal))
|
|
361
|
+
push(internal(u) ? internalLinks : skipped, u, page.path);
|
|
362
|
+
for (const e of links.embeds) {
|
|
363
|
+
const entry = embeds.get(e.url) ?? { kind: e.kind, pages: [] };
|
|
360
364
|
if (!entry.pages.includes(page.path))
|
|
361
365
|
entry.pages.push(page.path);
|
|
362
|
-
embeds.set(url, entry);
|
|
366
|
+
embeds.set(e.url, entry);
|
|
363
367
|
}
|
|
364
368
|
}
|
|
365
|
-
const
|
|
366
|
-
const
|
|
367
|
-
const path = url.split('?')[0];
|
|
368
|
-
return livePaths.has(path) || livePaths.has(path.replace(/\/(history|edit)$/, ''));
|
|
369
|
-
};
|
|
369
|
+
const urls = [...external.keys()];
|
|
370
|
+
const results = await mapLimit(urls, concurrency, url => probe(url));
|
|
370
371
|
return {
|
|
371
|
-
externalChecked:
|
|
372
|
+
externalChecked: urls.length,
|
|
372
373
|
brokenExternal: results.filter(r => !r.ok).map(r => ({ ...r, pages: external.get(r.url) })),
|
|
373
|
-
brokenInternal: [...
|
|
374
|
-
|
|
374
|
+
brokenInternal: [...internalLinks.entries()]
|
|
375
|
+
.filter(([u]) => {
|
|
376
|
+
const p = u.split('?')[0];
|
|
377
|
+
return !livePaths.has(p) && !livePaths.has(p.replace(/\/(history|edit)$/, ''));
|
|
378
|
+
})
|
|
379
|
+
.map(([url, pages]) => ({ url, pages })),
|
|
375
380
|
embeds,
|
|
381
|
+
skipped,
|
|
376
382
|
};
|
|
377
383
|
}
|
|
378
|
-
/** The human
|
|
379
|
-
export function printLinkAudit(audit, { scope, pagesScanned }) {
|
|
384
|
+
/** The human report's first lines: what was checked, and what is broken. */
|
|
385
|
+
export function printLinkAudit(audit, { scope = '(entire wiki)', pagesScanned }) {
|
|
380
386
|
console.log(`\nLink check — scope: ${scope}`);
|
|
381
387
|
console.log(`Pages scanned: ${pagesScanned} | external links checked: ${audit.externalChecked}`);
|
|
382
388
|
console.log(`\nBroken external (${audit.brokenExternal.length}):`);
|
package/dist/passkey.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export interface PasskeyGate {
|
|
|
14
14
|
cookie: string;
|
|
15
15
|
/** Whether a session cookie's value is a live session whose passkey is still saved. */
|
|
16
16
|
signedIn(session: string | undefined): Promise<boolean>;
|
|
17
|
-
/**
|
|
17
|
+
/** `signedIn` for a request's own cookie: the check a write route makes. */
|
|
18
18
|
allows(request: Request): Promise<boolean>;
|
|
19
19
|
/** A single-use invite token, good for `days`. The bin turns it into a link. */
|
|
20
20
|
invite(days?: number): Promise<string>;
|
package/dist/passkey.js
CHANGED
|
@@ -54,17 +54,20 @@ export function createPasskeyGate({ sql, rpName, cookie = 'passkey_session', ses
|
|
|
54
54
|
const value = await issue(sql, 'session', sessionDays * DAY, undefined, passkeyId);
|
|
55
55
|
return setCookie(value, sessionDays * 86_400, secure);
|
|
56
56
|
}
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
(
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
const cookieOf = (request) => request.headers
|
|
58
|
+
.get('cookie')
|
|
59
|
+
?.split(/;\s*/)
|
|
60
|
+
.find(c => c.startsWith(`${cookie}=`))
|
|
61
|
+
?.slice(cookie.length + 1);
|
|
62
|
+
const gate = {
|
|
62
63
|
cookie,
|
|
63
|
-
signedIn,
|
|
64
|
-
|
|
64
|
+
allows: request => gate.signedIn(cookieOf(request)),
|
|
65
|
+
signedIn: async (value) => !!value &&
|
|
66
|
+
(await sql(`SELECT 1 FROM "PasskeyToken" t JOIN "Passkey" p ON p.id = t."passkeyId"
|
|
67
|
+
WHERE t.hash = $1 AND t.kind = 'session' AND t."expiresAt" > now()`, await hash(value))).length > 0,
|
|
65
68
|
invite: (days = 1) => issue(sql, 'invite', days * DAY),
|
|
66
69
|
async signOut(request) {
|
|
67
|
-
const value =
|
|
70
|
+
const value = cookieOf(request);
|
|
68
71
|
if (value)
|
|
69
72
|
await spend(sql, 'session', value);
|
|
70
73
|
return setCookie('', 0, new URL(request.url).protocol === 'https:');
|
|
@@ -131,4 +134,5 @@ export function createPasskeyGate({ sql, rpName, cookie = 'passkey_session', ses
|
|
|
131
134
|
return session(passkey.id, protocol === 'https:');
|
|
132
135
|
},
|
|
133
136
|
};
|
|
137
|
+
return gate;
|
|
134
138
|
}
|
package/dist/scripts.d.ts
CHANGED
|
@@ -1,33 +1,20 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type SaveInput, type WikiPageRow, type WikiStore } from './store.js';
|
|
2
2
|
import { type ChangeType } from './versioning.js';
|
|
3
|
-
import
|
|
4
|
-
/** The slice of a `pg` client every helper here takes. Prisma fits it with `$queryRawUnsafe`. */
|
|
5
|
-
export interface ScriptClient {
|
|
6
|
-
query(text: string, values?: unknown[]): Promise<{
|
|
7
|
-
rows: unknown[];
|
|
8
|
-
}>;
|
|
9
|
-
}
|
|
10
|
-
type Block = {
|
|
11
|
-
id: string;
|
|
12
|
-
type: string;
|
|
13
|
-
};
|
|
3
|
+
import type { BannerVariant } from './text.js';
|
|
14
4
|
export declare const uid: () => string;
|
|
15
|
-
export declare const content: (
|
|
5
|
+
export declare const content: (text: string) => {
|
|
16
6
|
id: string;
|
|
17
7
|
type: "content";
|
|
18
8
|
text: string;
|
|
19
9
|
};
|
|
20
|
-
/**
|
|
21
|
-
* A notice. Always pass `text`: the variant's standard message lives in the
|
|
22
|
-
* renderer, not the row, so a banner without it is invisible to search.
|
|
23
|
-
*/
|
|
10
|
+
/** Always pass `text`: the renderer's default message lives in React, and search indexes only what the row holds. */
|
|
24
11
|
export declare const banner: (variant: BannerVariant, text: string) => {
|
|
25
12
|
id: string;
|
|
26
13
|
type: "banner";
|
|
27
14
|
variant: BannerVariant;
|
|
28
15
|
text: string;
|
|
29
16
|
};
|
|
30
|
-
/** A facts panel
|
|
17
|
+
/** A facts panel that leads an article: `table` is the inner `<table>` HTML. */
|
|
31
18
|
export declare const infobox: (table: string) => {
|
|
32
19
|
id: string;
|
|
33
20
|
type: "infobox";
|
|
@@ -38,152 +25,72 @@ export declare const infobox: (table: string) => {
|
|
|
38
25
|
}[];
|
|
39
26
|
};
|
|
40
27
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* nothing reports it. A throw inside `withClient` is a rollback and a failed run.
|
|
47
|
-
*/
|
|
48
|
-
export declare function assertLinkShapes(blocks: unknown, where?: string): void;
|
|
49
|
-
/**
|
|
50
|
-
* Mark everything cached under `tag` stale on the project `cwd` is linked to.
|
|
51
|
-
* A script's write reaches no route that would bust it, and pages hold a day.
|
|
28
|
+
* Refuse block content whose URL-bearing leaves are mis-shaped, before it
|
|
29
|
+
* reaches the database. `LinkGridView` and `ReferencesView` drop an entry whose
|
|
30
|
+
* URL fails `safeLinkHref` rather than rendering it inert, so a one-field slip
|
|
31
|
+
* (`url` for a link, whose shape is `{ label, href }`) shows as a heading with
|
|
32
|
+
* no pills and nothing reports it. Throws, so `withClient` rolls back.
|
|
52
33
|
*/
|
|
34
|
+
export declare function assertLinkShapes(blocks: readonly unknown[], where?: string): void;
|
|
35
|
+
/** `vercel cache invalidate --tag`, reported either way. */
|
|
53
36
|
export declare function invalidate(tag: string, cwd?: string): void;
|
|
54
|
-
/**
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
export
|
|
37
|
+
/** The slice of a `pg` client a script uses. */
|
|
38
|
+
export interface ScriptClient {
|
|
39
|
+
query(text: string, values?: unknown[]): Promise<{
|
|
40
|
+
rows: unknown[];
|
|
41
|
+
}>;
|
|
42
|
+
}
|
|
43
|
+
export interface WithClientOptions {
|
|
44
|
+
/** The cache tag a write expires. Default `wiki`. */
|
|
61
45
|
tag?: string;
|
|
46
|
+
/** Where the Vercel project is linked. Default the working directory. */
|
|
62
47
|
cwd?: string;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
offset?: number;
|
|
79
|
-
}) => Promise<import("./store.js").WikiPageListing[]>;
|
|
80
|
-
articles: (tagPath: string, { sort, limit, offset }?: {
|
|
81
|
-
sort?: import("./store.js").ListSort;
|
|
82
|
-
limit?: number;
|
|
83
|
-
offset?: number;
|
|
84
|
-
}) => Promise<WikiPageRow<Block>[]>;
|
|
85
|
-
subtree: (tagPath: string) => Promise<import("./store.js").WikiPageRef[]>;
|
|
86
|
-
recent: ({ tagPath, limit }?: {
|
|
87
|
-
tagPath?: string;
|
|
88
|
-
limit?: number;
|
|
89
|
-
}) => Promise<import("./store.js").WikiPageListing[]>;
|
|
90
|
-
byIds: (ids: readonly string[]) => Promise<import("./store.js").WikiPageListing[]>;
|
|
91
|
-
refs: () => Promise<Omit<WikiPageRow<unknown>, "content">[]>;
|
|
92
|
-
corpus: () => Promise<WikiPageRow<Block>[]>;
|
|
93
|
-
branch: ({ tagPath, sort, limit, offset, since }?: {
|
|
94
|
-
tagPath?: string;
|
|
95
|
-
sort?: import("./store.js").ListSort;
|
|
96
|
-
limit?: number;
|
|
97
|
-
offset?: number;
|
|
98
|
-
since?: Date;
|
|
99
|
-
}) => Promise<{
|
|
100
|
-
items: {
|
|
101
|
-
content: Block[];
|
|
102
|
-
title: string;
|
|
103
|
-
id: string;
|
|
104
|
-
metadata: PageMetadata;
|
|
105
|
-
tagPath: string;
|
|
106
|
-
slug: string;
|
|
107
|
-
siteId: string;
|
|
108
|
-
version: string;
|
|
109
|
-
authorId: string | null;
|
|
110
|
-
lastVerifiedAt: Date | null;
|
|
111
|
-
createdAt: Date;
|
|
112
|
-
updatedAt: Date;
|
|
113
|
-
snippet: string;
|
|
114
|
-
}[];
|
|
115
|
-
total: number;
|
|
116
|
-
}>;
|
|
117
|
-
counts: () => Promise<{
|
|
118
|
-
tagPath: string;
|
|
119
|
-
pages: number;
|
|
120
|
-
}[]>;
|
|
121
|
-
stats: () => Promise<{
|
|
122
|
-
pages: number;
|
|
123
|
-
updated: Date | null;
|
|
124
|
-
}>;
|
|
125
|
-
search: (query: string, { tagPath, skip, take, content: withContent }?: {
|
|
126
|
-
tagPath?: string | null;
|
|
127
|
-
skip?: number;
|
|
128
|
-
take?: number;
|
|
129
|
-
content?: boolean;
|
|
130
|
-
}) => Promise<{
|
|
131
|
-
items: (Omit<WikiPageRow<unknown>, "content"> & {
|
|
132
|
-
snippet: string;
|
|
133
|
-
} & {
|
|
134
|
-
score: number;
|
|
135
|
-
content?: Block[] | undefined;
|
|
136
|
-
})[];
|
|
137
|
-
total: number;
|
|
138
|
-
}>;
|
|
139
|
-
history: (pageId: string, { limit, offset }?: {
|
|
140
|
-
limit?: number;
|
|
141
|
-
offset?: number;
|
|
142
|
-
}) => Promise<import("./store.js").WikiRevisionEntry[]>;
|
|
143
|
-
revision: (pageId: string, revisionId: string) => Promise<import("./store.js").WikiRevisionRow<Block> | null>;
|
|
144
|
-
save: (input: import("./store.js").SaveInput<Block>) => Promise<import("./store.js").SaveResult<Block>>;
|
|
145
|
-
restore: (tagPath: string, slug: string, revisionId: string, by?: {
|
|
146
|
-
authorId?: string | null;
|
|
147
|
-
}) => Promise<import("./store.js").SaveResult<Block>>;
|
|
148
|
-
remove: (tagPath: string, slug: string) => Promise<boolean>;
|
|
149
|
-
verify: (tagPath: string, slug: string) => Promise<boolean>;
|
|
48
|
+
/** Default `DATABASE_URL`, read from the environment or `./.env`. */
|
|
49
|
+
url?: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Run `fn` against a connected client, then release the client and the pool.
|
|
53
|
+
* A throw rolls back whatever transaction was open and fails the process:
|
|
54
|
+
* every one of these scripts writes to the live database, so a half-applied
|
|
55
|
+
* edit must never be left behind and never look like a success. A run that
|
|
56
|
+
* wrote anything ends by expiring the wiki's cache tag, because a script
|
|
57
|
+
* bypasses the routes that purge the pages they change.
|
|
58
|
+
*/
|
|
59
|
+
export declare function withClient<T>(fn: (client: ScriptClient) => Promise<T>, { tag, cwd, url }?: WithClientOptions): Promise<T | undefined>;
|
|
60
|
+
type AnyBlock = {
|
|
61
|
+
id: string;
|
|
62
|
+
type: string;
|
|
150
63
|
};
|
|
64
|
+
/** The app's own store over a script's client, so a script writes exactly what the editor writes. */
|
|
65
|
+
export declare const storeFor: (client: ScriptClient, siteId?: string) => WikiStore<AnyBlock>;
|
|
151
66
|
/** A page row by path, every column: what `writeRevision` takes. Null when there is none. */
|
|
152
|
-
export declare const readPage: (client: ScriptClient, tagPath: string, slug: string, siteId?: string) => Promise<
|
|
153
|
-
revisionCount: number;
|
|
154
|
-
}> | null>;
|
|
67
|
+
export declare const readPage: (client: ScriptClient, tagPath: string, slug: string, siteId?: string) => Promise<WikiPageRow<AnyBlock> | null>;
|
|
155
68
|
export interface RevisionOptions {
|
|
156
|
-
/** The bump, and the revision's change type. Default minor. */
|
|
157
69
|
change?: Exclude<ChangeType, 'none'>;
|
|
158
|
-
message?: string;
|
|
159
|
-
/**
|
|
70
|
+
message?: string | null;
|
|
71
|
+
/** The writer's word that the page's facts were re-checked. */
|
|
160
72
|
verified?: boolean;
|
|
161
|
-
/** Write nothing; return the version
|
|
73
|
+
/** Write nothing; return the version the bump would give. */
|
|
162
74
|
dry?: boolean;
|
|
163
|
-
metadata?:
|
|
75
|
+
metadata?: Record<string, unknown>;
|
|
164
76
|
authorId?: string | null;
|
|
77
|
+
siteId?: string;
|
|
165
78
|
}
|
|
166
79
|
/**
|
|
167
|
-
* Write edited blocks back to a page
|
|
168
|
-
*
|
|
169
|
-
*
|
|
80
|
+
* Write edited blocks back to a page from `readPage` as one revision, through
|
|
81
|
+
* the store: the page and its revision in one statement, the version bumped
|
|
82
|
+
* from the stored one by `change`. The save names the version it read, so an
|
|
83
|
+
* edit landing in between is refused (409) instead of overwritten. Returns the
|
|
84
|
+
* new version.
|
|
170
85
|
*/
|
|
171
|
-
export declare function writeRevision(client: ScriptClient, page:
|
|
86
|
+
export declare function writeRevision(client: ScriptClient, page: WikiPageRow<AnyBlock>, blocks: AnyBlock[], { change, message, verified, dry, metadata, authorId, siteId }?: RevisionOptions): Promise<string>;
|
|
172
87
|
/**
|
|
173
|
-
* Create a page or
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
* always writes one.
|
|
88
|
+
* Create a page, or replace its title and blocks: a change writes a revision
|
|
89
|
+
* bumped from the page's own version, a re-run that changes nothing writes
|
|
90
|
+
* none, and a stated `change` always records one.
|
|
177
91
|
*/
|
|
178
|
-
export declare function upsertWikiPage(client: ScriptClient, input: {
|
|
179
|
-
|
|
180
|
-
slug: string;
|
|
181
|
-
title: string;
|
|
182
|
-
content: Block[];
|
|
183
|
-
metadata?: PageMetadata;
|
|
184
|
-
message?: string;
|
|
185
|
-
change?: Exclude<ChangeType, 'none'>;
|
|
186
|
-
authorId?: string | null;
|
|
92
|
+
export declare function upsertWikiPage(client: ScriptClient, input: SaveInput<AnyBlock> & {
|
|
93
|
+
content: AnyBlock[];
|
|
187
94
|
siteId?: string;
|
|
188
95
|
}): Promise<'created' | 'updated'>;
|
|
189
96
|
export {};
|