@glw907/cairn-cms 0.60.1 → 0.62.2
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 +78 -0
- package/dist/components/AdminLayout.svelte +22 -0
- package/dist/components/CairnAdmin.svelte +3 -0
- package/dist/components/CairnTidySettings.svelte +2 -2
- package/dist/components/CairnTidySettings.svelte.d.ts +1 -1
- package/dist/components/EditPage.svelte +116 -39
- package/dist/components/HelpHome.svelte +824 -0
- package/dist/components/HelpHome.svelte.d.ts +22 -0
- package/dist/components/MarkdownHelpDialog.svelte +4 -15
- package/dist/components/client-ingest.d.ts +16 -8
- package/dist/components/client-ingest.js +12 -6
- package/dist/components/editor-media.js +16 -8
- package/dist/components/editor-placeholder.d.ts +4 -2
- package/dist/components/editor-tidy.d.ts +24 -12
- package/dist/components/editor-tidy.js +8 -4
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +1 -0
- package/dist/components/link-completion.d.ts +12 -6
- package/dist/components/link-completion.js +12 -6
- package/dist/components/markdown-directives.d.ts +9 -6
- package/dist/components/markdown-directives.js +9 -6
- package/dist/components/markdown-format.d.ts +7 -2
- package/dist/components/markdown-format.js +59 -28
- package/dist/components/markdown-reference.d.ts +8 -0
- package/dist/components/markdown-reference.js +22 -0
- package/dist/components/media-upload-outcome.d.ts +12 -6
- package/dist/components/objective-errors.d.ts +8 -4
- package/dist/components/objective-errors.js +8 -4
- package/dist/components/preview-doc.d.ts +4 -2
- package/dist/components/preview-doc.js +4 -2
- package/dist/components/spellcheck.d.ts +55 -29
- package/dist/components/spellcheck.js +39 -21
- package/dist/components/tidy-categorize.d.ts +20 -10
- package/dist/components/tidy-categorize.js +16 -8
- package/dist/components/tidy-validate.d.ts +12 -6
- package/dist/components/tidy-validate.js +20 -10
- package/dist/components/topbar-context.d.ts +4 -2
- package/dist/content/advisories.d.ts +56 -0
- package/dist/content/advisories.js +87 -0
- package/dist/content/compose.d.ts +4 -2
- package/dist/content/compose.js +1 -0
- package/dist/content/excerpt.js +4 -2
- package/dist/content/getting-started.d.ts +18 -0
- package/dist/content/getting-started.js +12 -0
- package/dist/content/links.d.ts +16 -8
- package/dist/content/links.js +12 -6
- package/dist/content/manifest.d.ts +36 -18
- package/dist/content/manifest.js +32 -16
- package/dist/content/media-refs.d.ts +4 -2
- package/dist/content/media-refs.js +4 -2
- package/dist/content/media-rewrite.d.ts +8 -4
- package/dist/content/media-rewrite.js +76 -38
- package/dist/content/schema.d.ts +20 -10
- package/dist/content/site-dictionary.d.ts +4 -2
- package/dist/content/site-dictionary.js +8 -4
- package/dist/content/types.d.ts +97 -42
- package/dist/delivery/content-index.d.ts +16 -8
- package/dist/delivery/feeds.js +4 -2
- package/dist/delivery/json-ld.d.ts +3 -0
- package/dist/delivery/json-ld.js +3 -0
- package/dist/delivery/manifest.d.ts +4 -2
- package/dist/delivery/manifest.js +4 -2
- package/dist/delivery/public-routes.d.ts +12 -6
- package/dist/delivery/public-routes.js +4 -2
- package/dist/delivery/seo-fields.d.ts +12 -6
- package/dist/delivery/seo-fields.js +8 -4
- package/dist/delivery/site-indexes.d.ts +4 -2
- package/dist/delivery/site-resolver.d.ts +4 -2
- package/dist/delivery/site-resolver.js +4 -2
- package/dist/doctor/cloudflare-api.d.ts +6 -0
- package/dist/doctor/cloudflare-api.js +6 -0
- package/dist/doctor/index.d.ts +12 -6
- package/dist/doctor/report.d.ts +3 -0
- package/dist/doctor/report.js +3 -0
- package/dist/doctor/run.d.ts +3 -0
- package/dist/doctor/run.js +3 -0
- package/dist/doctor/types.d.ts +10 -2
- package/dist/doctor/types.js +6 -0
- package/dist/doctor/wrangler-config.d.ts +7 -2
- package/dist/doctor/wrangler-config.js +3 -0
- package/dist/email.d.ts +4 -2
- package/dist/env.d.ts +0 -3
- package/dist/env.js +0 -3
- package/dist/github/branches.d.ts +4 -2
- package/dist/github/branches.js +4 -2
- package/dist/github/signing.d.ts +1 -1
- package/dist/github/signing.js +2 -2
- package/dist/log/events.d.ts +1 -1
- package/dist/media/bulk-delete-plan.d.ts +8 -4
- package/dist/media/config.d.ts +12 -6
- package/dist/media/config.js +16 -8
- package/dist/media/delivery-bucket.d.ts +4 -2
- package/dist/media/library-entry.d.ts +4 -2
- package/dist/media/library-entry.js +4 -2
- package/dist/media/manifest.d.ts +29 -15
- package/dist/media/manifest.js +29 -16
- package/dist/media/naming.d.ts +12 -6
- package/dist/media/naming.js +24 -12
- package/dist/media/orphan-scan.d.ts +4 -2
- package/dist/media/reconcile.d.ts +21 -11
- package/dist/media/reconcile.js +12 -6
- package/dist/media/reference.d.ts +8 -4
- package/dist/media/reference.js +12 -6
- package/dist/media/rewrite-plan.d.ts +12 -6
- package/dist/media/sniff.d.ts +4 -2
- package/dist/media/sniff.js +28 -14
- package/dist/media/store.d.ts +16 -8
- package/dist/media/store.js +4 -2
- package/dist/media/transform-url.d.ts +12 -6
- package/dist/media/transform-url.js +8 -4
- package/dist/media/usage.d.ts +8 -4
- package/dist/nav/site-config.d.ts +16 -8
- package/dist/render/component-grammar.d.ts +23 -10
- package/dist/render/component-grammar.js +19 -8
- package/dist/render/component-insert.d.ts +8 -4
- package/dist/render/component-insert.js +4 -2
- package/dist/render/component-reference.d.ts +4 -2
- package/dist/render/component-reference.js +4 -2
- package/dist/render/component-validate.d.ts +3 -0
- package/dist/render/component-validate.js +3 -0
- package/dist/render/glyph.d.ts +4 -2
- package/dist/render/glyph.js +4 -2
- package/dist/render/pipeline.d.ts +20 -10
- package/dist/render/pipeline.js +4 -2
- package/dist/render/registry.d.ts +40 -20
- package/dist/render/registry.js +16 -8
- package/dist/render/rehype-dispatch.d.ts +22 -8
- package/dist/render/rehype-dispatch.js +22 -8
- package/dist/render/remark-directives.d.ts +3 -0
- package/dist/render/remark-directives.js +3 -0
- package/dist/render/remark-figure.d.ts +4 -2
- package/dist/render/remark-figure.js +4 -2
- package/dist/render/resolve-links.d.ts +4 -2
- package/dist/render/resolve-links.js +4 -2
- package/dist/render/resolve-media.d.ts +16 -8
- package/dist/render/resolve-media.js +12 -6
- package/dist/sveltekit/admin-dispatch.d.ts +2 -0
- package/dist/sveltekit/admin-dispatch.js +9 -3
- package/dist/sveltekit/auth-routes.d.ts +3 -0
- package/dist/sveltekit/auth-routes.js +3 -0
- package/dist/sveltekit/cairn-admin.d.ts +16 -5
- package/dist/sveltekit/cairn-admin.js +26 -10
- package/dist/sveltekit/content-routes.d.ts +191 -86
- package/dist/sveltekit/content-routes.js +297 -107
- package/dist/sveltekit/editors-routes.d.ts +3 -0
- package/dist/sveltekit/editors-routes.js +3 -0
- package/dist/sveltekit/guard.d.ts +4 -2
- package/dist/sveltekit/guard.js +4 -2
- package/dist/sveltekit/https-required-page.d.ts +1 -1
- package/dist/sveltekit/https-required-page.js +1 -1
- package/dist/sveltekit/index.d.ts +1 -1
- package/dist/sveltekit/media-route.d.ts +1 -2
- package/dist/sveltekit/media-route.js +13 -8
- package/dist/sveltekit/nav-routes.d.ts +7 -2
- package/dist/sveltekit/nav-routes.js +3 -0
- package/dist/sveltekit/types.d.ts +4 -2
- package/dist/vite/index.d.ts +32 -16
- package/dist/vite/index.js +52 -26
- package/dist/vite/resolve-root.d.ts +8 -4
- package/dist/vite/resolve-root.js +4 -2
- package/package.json +7 -1
- package/src/lib/components/AdminLayout.svelte +22 -0
- package/src/lib/components/CairnAdmin.svelte +3 -0
- package/src/lib/components/CairnTidySettings.svelte +2 -2
- package/src/lib/components/ComponentForm.svelte +0 -1
- package/src/lib/components/EditPage.svelte +133 -41
- package/src/lib/components/HelpHome.svelte +850 -0
- package/src/lib/components/MarkdownHelpDialog.svelte +4 -15
- package/src/lib/components/client-ingest.ts +20 -10
- package/src/lib/components/editor-media.ts +20 -10
- package/src/lib/components/editor-placeholder.ts +12 -6
- package/src/lib/components/editor-tidy.ts +28 -14
- package/src/lib/components/index.ts +1 -0
- package/src/lib/components/link-completion.ts +12 -6
- package/src/lib/components/markdown-directives.ts +13 -8
- package/src/lib/components/markdown-format.ts +63 -30
- package/src/lib/components/markdown-reference.ts +30 -0
- package/src/lib/components/media-upload-outcome.ts +12 -6
- package/src/lib/components/objective-errors.ts +16 -8
- package/src/lib/components/preview-doc.ts +4 -2
- package/src/lib/components/spellcheck.ts +79 -41
- package/src/lib/components/tidy-categorize.ts +28 -14
- package/src/lib/components/tidy-validate.ts +28 -14
- package/src/lib/components/topbar-context.ts +4 -2
- package/src/lib/content/advisories.ts +150 -0
- package/src/lib/content/compose.ts +5 -2
- package/src/lib/content/excerpt.ts +4 -2
- package/src/lib/content/getting-started.ts +31 -0
- package/src/lib/content/links.ts +16 -8
- package/src/lib/content/manifest.ts +36 -18
- package/src/lib/content/media-refs.ts +4 -2
- package/src/lib/content/media-rewrite.ts +100 -50
- package/src/lib/content/schema.ts +20 -10
- package/src/lib/content/site-dictionary.ts +8 -4
- package/src/lib/content/types.ts +97 -42
- package/src/lib/delivery/content-index.ts +16 -8
- package/src/lib/delivery/feeds.ts +4 -2
- package/src/lib/delivery/json-ld.ts +3 -0
- package/src/lib/delivery/manifest.ts +4 -2
- package/src/lib/delivery/public-routes.ts +16 -8
- package/src/lib/delivery/seo-fields.ts +12 -6
- package/src/lib/delivery/site-indexes.ts +4 -2
- package/src/lib/delivery/site-resolver.ts +4 -2
- package/src/lib/doctor/cloudflare-api.ts +6 -0
- package/src/lib/doctor/index.ts +12 -6
- package/src/lib/doctor/report.ts +3 -0
- package/src/lib/doctor/run.ts +3 -0
- package/src/lib/doctor/types.ts +10 -2
- package/src/lib/doctor/wrangler-config.ts +7 -2
- package/src/lib/email.ts +4 -2
- package/src/lib/env.ts +0 -3
- package/src/lib/github/branches.ts +4 -2
- package/src/lib/github/signing.ts +2 -2
- package/src/lib/log/events.ts +1 -0
- package/src/lib/media/bulk-delete-plan.ts +8 -4
- package/src/lib/media/config.ts +24 -12
- package/src/lib/media/delivery-bucket.ts +4 -2
- package/src/lib/media/library-entry.ts +4 -2
- package/src/lib/media/manifest.ts +33 -18
- package/src/lib/media/naming.ts +24 -12
- package/src/lib/media/orphan-scan.ts +4 -2
- package/src/lib/media/reconcile.ts +21 -11
- package/src/lib/media/reference.ts +12 -6
- package/src/lib/media/rewrite-plan.ts +12 -6
- package/src/lib/media/sniff.ts +28 -14
- package/src/lib/media/store.ts +16 -8
- package/src/lib/media/transform-url.ts +12 -6
- package/src/lib/media/usage.ts +8 -4
- package/src/lib/nav/site-config.ts +16 -8
- package/src/lib/render/component-grammar.ts +23 -10
- package/src/lib/render/component-insert.ts +8 -4
- package/src/lib/render/component-reference.ts +4 -2
- package/src/lib/render/component-validate.ts +3 -0
- package/src/lib/render/glyph.ts +4 -2
- package/src/lib/render/pipeline.ts +20 -10
- package/src/lib/render/registry.ts +44 -22
- package/src/lib/render/rehype-dispatch.ts +22 -8
- package/src/lib/render/remark-directives.ts +3 -0
- package/src/lib/render/remark-figure.ts +4 -2
- package/src/lib/render/resolve-links.ts +4 -2
- package/src/lib/render/resolve-media.ts +16 -8
- package/src/lib/sveltekit/admin-dispatch.ts +10 -4
- package/src/lib/sveltekit/auth-routes.ts +3 -0
- package/src/lib/sveltekit/cairn-admin.ts +37 -15
- package/src/lib/sveltekit/content-routes.ts +494 -197
- package/src/lib/sveltekit/editors-routes.ts +3 -0
- package/src/lib/sveltekit/guard.ts +4 -2
- package/src/lib/sveltekit/https-required-page.ts +1 -1
- package/src/lib/sveltekit/index.ts +3 -0
- package/src/lib/sveltekit/media-route.ts +13 -8
- package/src/lib/sveltekit/nav-routes.ts +7 -2
- package/src/lib/sveltekit/types.ts +4 -2
- package/src/lib/vite/index.ts +60 -30
- package/src/lib/vite/resolve-root.ts +8 -4
package/dist/doctor/index.d.ts
CHANGED
|
@@ -5,8 +5,10 @@ export interface DoctorArgs {
|
|
|
5
5
|
from?: string;
|
|
6
6
|
repo?: string;
|
|
7
7
|
sendTest?: string;
|
|
8
|
-
/**
|
|
9
|
-
*
|
|
8
|
+
/**
|
|
9
|
+
* The live admin probe: a URL when --probe carried one, true for the bare flag (probe the
|
|
10
|
+
* PUBLIC_ORIGIN input), absent when the flag never appeared (the probe does not run).
|
|
11
|
+
*/
|
|
10
12
|
probe?: string | true;
|
|
11
13
|
}
|
|
12
14
|
/** Parse the bin's argv (long flags only). Throws with a usage line on anything unexpected. */
|
|
@@ -18,12 +20,16 @@ export declare function parseArgs(argv: string[]): DoctorArgs;
|
|
|
18
20
|
* readFile stay with the bin, which injects the real ones.
|
|
19
21
|
*/
|
|
20
22
|
export declare function contextFromEnv(env: Record<string, string | undefined>, args: DoctorArgs, cwd: string): Omit<DoctorContext, 'fetch' | 'readFile'>;
|
|
21
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* The lazy derivation sources the bin wires up: the adapter read through the consumer's own
|
|
22
25
|
* Vite resolution and the wrangler config's account_id. Each runs only when an input it feeds
|
|
23
|
-
* is still missing, so a doctor run with full flags touches neither.
|
|
26
|
+
* is still missing, so a doctor run with full flags touches neither.
|
|
27
|
+
*/
|
|
24
28
|
export interface DerivationSources {
|
|
25
|
-
/**
|
|
26
|
-
*
|
|
29
|
+
/**
|
|
30
|
+
* Returns `{ owner, repo, from, mediaBucketBinding }` off the adapter, or null when nothing is
|
|
31
|
+
* derivable.
|
|
32
|
+
*/
|
|
27
33
|
adapterFacts: () => Promise<{
|
|
28
34
|
owner?: string;
|
|
29
35
|
repo?: string;
|
package/dist/doctor/report.d.ts
CHANGED
package/dist/doctor/report.js
CHANGED
|
@@ -8,6 +8,9 @@ const TAG = {
|
|
|
8
8
|
fail: 'FAIL',
|
|
9
9
|
skip: 'SKIP',
|
|
10
10
|
};
|
|
11
|
+
/**
|
|
12
|
+
*
|
|
13
|
+
*/
|
|
11
14
|
export function formatReport(results) {
|
|
12
15
|
const lines = results.map(({ check, result }) => `${TAG[result.status]} ${check.title}: ${result.detail}`);
|
|
13
16
|
const failures = results.filter(({ result }) => result.status === 'fail');
|
package/dist/doctor/run.d.ts
CHANGED
package/dist/doctor/run.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
// The doctor's runner: every check executes, every result lands in the table. A throwing check
|
|
2
2
|
// records a fail and the run continues, so one broken probe never hides the rest of the picture.
|
|
3
3
|
import { fail } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
*/
|
|
4
7
|
export async function runDoctor(checks, ctx) {
|
|
5
8
|
const results = [];
|
|
6
9
|
let failed = 0;
|
package/dist/doctor/types.d.ts
CHANGED
|
@@ -6,7 +6,13 @@ export interface CheckResult {
|
|
|
6
6
|
}
|
|
7
7
|
/** Result constructors, so a check body reads one outcome per line instead of object literals. */
|
|
8
8
|
export declare function pass(detail: string): CheckResult;
|
|
9
|
+
/**
|
|
10
|
+
*
|
|
11
|
+
*/
|
|
9
12
|
export declare function fail(detail: string): CheckResult;
|
|
13
|
+
/**
|
|
14
|
+
*
|
|
15
|
+
*/
|
|
10
16
|
export declare function skip(detail: string): CheckResult;
|
|
11
17
|
export interface DoctorCheck {
|
|
12
18
|
/** Stable id, e.g. 'email.sender-onboarded'. */
|
|
@@ -30,8 +36,10 @@ export interface DoctorContext {
|
|
|
30
36
|
cfAccountId?: string;
|
|
31
37
|
/** PUBLIC_ORIGIN, the env fallback when the wrangler vars carry none. */
|
|
32
38
|
publicOrigin?: string;
|
|
33
|
-
/**
|
|
34
|
-
*
|
|
39
|
+
/**
|
|
40
|
+
* The adapter's media bucket binding (cairn.assets.bucketBinding), derived off the adapter.
|
|
41
|
+
* Undefined when the site declares no media assets; the media-bucket check skips in that case.
|
|
42
|
+
*/
|
|
35
43
|
mediaBucketBinding?: string;
|
|
36
44
|
/** GITHUB_APP_ID / GITHUB_APP_INSTALLATION_ID / GITHUB_APP_PRIVATE_KEY_B64. */
|
|
37
45
|
github?: {
|
package/dist/doctor/types.js
CHANGED
|
@@ -2,9 +2,15 @@
|
|
|
2
2
|
export function pass(detail) {
|
|
3
3
|
return { status: 'pass', detail };
|
|
4
4
|
}
|
|
5
|
+
/**
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
5
8
|
export function fail(detail) {
|
|
6
9
|
return { status: 'fail', detail };
|
|
7
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
*
|
|
13
|
+
*/
|
|
8
14
|
export function skip(detail) {
|
|
9
15
|
return { status: 'skip', detail };
|
|
10
16
|
}
|
|
@@ -12,8 +12,13 @@ export interface WranglerFacts {
|
|
|
12
12
|
publicOrigin?: string;
|
|
13
13
|
/** The top-level account_id, when declared; a fallback for CLOUDFLARE_ACCOUNT_ID. */
|
|
14
14
|
accountId?: string;
|
|
15
|
-
/**
|
|
16
|
-
*
|
|
15
|
+
/**
|
|
16
|
+
* The declared r2_buckets binding names; the conditional media check matches the adapter's
|
|
17
|
+
* bucketBinding against this. Not part of the hard config.bindings check (decision 9).
|
|
18
|
+
*/
|
|
17
19
|
r2Buckets: string[];
|
|
18
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
*
|
|
23
|
+
*/
|
|
19
24
|
export declare function readWranglerConfig(readFile: DoctorContext['readFile']): Promise<WranglerFacts | null>;
|
package/dist/email.d.ts
CHANGED
|
@@ -15,9 +15,11 @@ export interface AuthBranding {
|
|
|
15
15
|
from: string;
|
|
16
16
|
replyTo?: string;
|
|
17
17
|
}
|
|
18
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* The injected send. Production uses `cloudflareSend`; tests pass a sink. A thrown error's
|
|
19
20
|
* text reaches the structured log (scrubbed and truncated), so a custom sender must not embed
|
|
20
|
-
* the message body or the magic link in what it throws.
|
|
21
|
+
* the message body or the magic link in what it throws.
|
|
22
|
+
*/
|
|
21
23
|
export type SendMagicLink = (env: AuthEnv, message: MagicLinkMessage) => Promise<void>;
|
|
22
24
|
/** Build the confirmation email. The link is the only action; the copy stays plain. */
|
|
23
25
|
export declare function buildMagicLinkMessage(input: {
|
package/dist/env.d.ts
CHANGED
|
@@ -5,7 +5,6 @@ import type { DeliveryBucket } from './media/delivery-bucket.js';
|
|
|
5
5
|
*
|
|
6
6
|
* The origin is always config-derived, never read from a request header, so a
|
|
7
7
|
* forged Host header cannot redirect a magic link (spec 7.1, risk H3).
|
|
8
|
-
*
|
|
9
8
|
* @throws CairnError (`config.public-origin-invalid`) when `PUBLIC_ORIGIN` is unset or
|
|
10
9
|
* empty, fails to parse as a URL, or uses http on a non-local host.
|
|
11
10
|
*/
|
|
@@ -17,7 +16,6 @@ export declare function requireOrigin(env: {
|
|
|
17
16
|
*
|
|
18
17
|
* The handlers read D1 off `event.platform.env`; without this a misconfigured binding
|
|
19
18
|
* surfaces as a raw `TypeError` deep in a store call. This gives the failure a name.
|
|
20
|
-
*
|
|
21
19
|
* @throws CairnError (`config.bindings-missing`) when `AUTH_DB` is missing.
|
|
22
20
|
*/
|
|
23
21
|
export declare function requireDb(env: {
|
|
@@ -37,7 +35,6 @@ export declare function requireDb(env: {
|
|
|
37
35
|
* which is truthy but carries no callable `get`. Without that check the cast would succeed and the
|
|
38
36
|
* first `bucket.get(...)` would throw an uncaught 500 rather than the drained 503 a missing binding
|
|
39
37
|
* earns.
|
|
40
|
-
*
|
|
41
38
|
* @throws CairnError (`config.bindings-missing`) when the named binding is absent or not an R2 bucket.
|
|
42
39
|
*/
|
|
43
40
|
export declare function requireBucket(env: Record<string, unknown>, bindingName: string): DeliveryBucket;
|
package/dist/env.js
CHANGED
|
@@ -4,7 +4,6 @@ import { CairnError } from './diagnostics/index.js';
|
|
|
4
4
|
*
|
|
5
5
|
* The origin is always config-derived, never read from a request header, so a
|
|
6
6
|
* forged Host header cannot redirect a magic link (spec 7.1, risk H3).
|
|
7
|
-
*
|
|
8
7
|
* @throws CairnError (`config.public-origin-invalid`) when `PUBLIC_ORIGIN` is unset or
|
|
9
8
|
* empty, fails to parse as a URL, or uses http on a non-local host.
|
|
10
9
|
*/
|
|
@@ -38,7 +37,6 @@ export function requireOrigin(env) {
|
|
|
38
37
|
*
|
|
39
38
|
* The handlers read D1 off `event.platform.env`; without this a misconfigured binding
|
|
40
39
|
* surfaces as a raw `TypeError` deep in a store call. This gives the failure a name.
|
|
41
|
-
*
|
|
42
40
|
* @throws CairnError (`config.bindings-missing`) when `AUTH_DB` is missing.
|
|
43
41
|
*/
|
|
44
42
|
export function requireDb(env) {
|
|
@@ -61,7 +59,6 @@ export function requireDb(env) {
|
|
|
61
59
|
* which is truthy but carries no callable `get`. Without that check the cast would succeed and the
|
|
62
60
|
* first `bucket.get(...)` would throw an uncaught 500 rather than the drained 503 a missing binding
|
|
63
61
|
* earns.
|
|
64
|
-
*
|
|
65
62
|
* @throws CairnError (`config.bindings-missing`) when the named binding is absent or not an R2 bucket.
|
|
66
63
|
*/
|
|
67
64
|
export function requireBucket(env, bindingName) {
|
|
@@ -5,7 +5,9 @@ export declare function branchHeadSha(repo: RepoRef, branch: string, token: stri
|
|
|
5
5
|
export declare function createBranch(repo: RepoRef, branch: string, fromSha: string, token: string): Promise<void>;
|
|
6
6
|
/** Delete `branch`. A 404 (already gone) is success: the desired state holds. */
|
|
7
7
|
export declare function deleteBranch(repo: RepoRef, branch: string, token: string): Promise<void>;
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* Branch names under `prefix`, sorted. The matching-refs API paginates at 30 by default, so a
|
|
9
10
|
* site with 31+ pending entries would silently truncate; request the 100-per-page maximum and
|
|
10
|
-
* follow the Link rel="next" chain until exhausted.
|
|
11
|
+
* follow the Link rel="next" chain until exhausted.
|
|
12
|
+
*/
|
|
11
13
|
export declare function listBranches(repo: RepoRef, prefix: string, token: string): Promise<string[]>;
|
package/dist/github/branches.js
CHANGED
|
@@ -57,9 +57,11 @@ function nextPageUrl(link) {
|
|
|
57
57
|
}
|
|
58
58
|
return null;
|
|
59
59
|
}
|
|
60
|
-
/**
|
|
60
|
+
/**
|
|
61
|
+
* Branch names under `prefix`, sorted. The matching-refs API paginates at 30 by default, so a
|
|
61
62
|
* site with 31+ pending entries would silently truncate; request the 100-per-page maximum and
|
|
62
|
-
* follow the Link rel="next" chain until exhausted.
|
|
63
|
+
* follow the Link rel="next" chain until exhausted.
|
|
64
|
+
*/
|
|
63
65
|
export async function listBranches(repo, prefix, token) {
|
|
64
66
|
const names = [];
|
|
65
67
|
let url = `${gitUrl(repo, `matching-refs/heads/${prefix}`)}?per_page=100`;
|
package/dist/github/signing.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export declare function installationToken(creds: AppCredentials): Promise<string
|
|
|
7
7
|
* Build an installation-token cache. A module-global instance memoizes the minted token per
|
|
8
8
|
* installation for most of its one-hour life, so a warm Worker isolate reuses it across requests
|
|
9
9
|
* instead of re-signing and re-calling GitHub on every list and commit. A cold isolate re-mints,
|
|
10
|
-
* which is always safe. This mirrors the default of
|
|
10
|
+
* which is always safe. This mirrors the default of `@octokit/auth-app`, which caches installation
|
|
11
11
|
* tokens in memory and returns them until expiry. The TTL stays under GitHub's documented one-hour
|
|
12
12
|
* lifetime, so a fixed margin avoids parsing the API expiry. The cache holds the in-flight
|
|
13
13
|
* promise, not the resolved token, so a cold isolate's parallel loads coalesce into one mint;
|
package/dist/github/signing.js
CHANGED
|
@@ -10,7 +10,7 @@ function bytesToB64url(bytes) {
|
|
|
10
10
|
function buf(bytes) {
|
|
11
11
|
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
12
12
|
}
|
|
13
|
-
/** DER length octets for a value of `n` bytes (short form
|
|
13
|
+
/** DER length octets for a value of `n` bytes (short form `< 128`, else long form). */
|
|
14
14
|
function derLength(n) {
|
|
15
15
|
if (n < 0x80)
|
|
16
16
|
return [n];
|
|
@@ -63,7 +63,7 @@ export async function installationToken(creds) {
|
|
|
63
63
|
* Build an installation-token cache. A module-global instance memoizes the minted token per
|
|
64
64
|
* installation for most of its one-hour life, so a warm Worker isolate reuses it across requests
|
|
65
65
|
* instead of re-signing and re-calling GitHub on every list and commit. A cold isolate re-mints,
|
|
66
|
-
* which is always safe. This mirrors the default of
|
|
66
|
+
* which is always safe. This mirrors the default of `@octokit/auth-app`, which caches installation
|
|
67
67
|
* tokens in memory and returns them until expiry. The TTL stays under GitHub's documented one-hour
|
|
68
68
|
* lifetime, so a fixed margin avoids parsing the API expiry. The cache holds the in-flight
|
|
69
69
|
* promise, not the resolved token, so a cold isolate's parallel loads coalesce into one mint;
|
package/dist/log/events.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export type CairnLogEvent = 'auth.link.requested' | 'auth.link.send_failed' | 'auth.token.minted' | 'auth.token.confirmed' | 'auth.session.created' | 'auth.session.destroyed' | 'commit.succeeded' | 'commit.failed' | 'config.invalid' | 'entry.published' | 'entry.discarded' | 'publish.failed' | 'github.unreachable' | 'guard.rejected' | 'media.uploaded' | 'media.upload_failed' | 'media.delivery_failed' | 'media.orphan_reconcile' | 'media.resolve_missing' | 'media.deleted' | 'media.delete_blocked' | 'media.bulk_deleted' | 'media.orphans_purged' | 'media.replaced' | 'media.replace_blocked' | 'media.alt_propagated' | 'dictionary.added' | 'dictionary.add_conflict' | 'tidy.done' | 'tidy.error' | 'tidy.refused' | 'tidy.empty';
|
|
1
|
+
export type CairnLogEvent = 'auth.link.requested' | 'auth.link.send_failed' | 'auth.token.minted' | 'auth.token.confirmed' | 'auth.session.created' | 'auth.session.destroyed' | 'commit.succeeded' | 'commit.failed' | 'config.invalid' | 'entry.published' | 'entry.discarded' | 'publish.failed' | 'publish.address_collision' | 'github.unreachable' | 'guard.rejected' | 'media.uploaded' | 'media.upload_failed' | 'media.delivery_failed' | 'media.orphan_reconcile' | 'media.resolve_missing' | 'media.deleted' | 'media.delete_blocked' | 'media.bulk_deleted' | 'media.orphans_purged' | 'media.replaced' | 'media.replace_blocked' | 'media.alt_propagated' | 'dictionary.added' | 'dictionary.add_conflict' | 'tidy.done' | 'tidy.error' | 'tidy.refused' | 'tidy.empty';
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import type { UsageEntry, UsageIndex } from './usage.js';
|
|
2
2
|
import type { MediaManifest } from './manifest.js';
|
|
3
|
-
/**
|
|
4
|
-
*
|
|
3
|
+
/**
|
|
4
|
+
* One selected hash that is not deleted, with why and (for the where-used) its usage rows. The rows
|
|
5
|
+
* are present only for 'still-referenced'; an 'uncommitted' skip carries an empty list.
|
|
6
|
+
*/
|
|
5
7
|
export interface BulkDeleteSkip {
|
|
6
8
|
hash: string;
|
|
7
9
|
reason: 'still-referenced' | 'uncommitted';
|
|
8
10
|
usage: UsageEntry[];
|
|
9
11
|
}
|
|
10
|
-
/**
|
|
11
|
-
*
|
|
12
|
+
/**
|
|
13
|
+
* The partitioned selection: the hashes safe to purge and the hashes held back. Both arrays keep the
|
|
14
|
+
* input order of `selected` so the screen reports them in the order the user picked.
|
|
15
|
+
*/
|
|
12
16
|
export interface BulkDeletePlan {
|
|
13
17
|
deletable: string[];
|
|
14
18
|
skipped: BulkDeleteSkip[];
|
package/dist/media/config.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { AssetConfig } from '../content/types.js';
|
|
2
2
|
import type { VariantSpec } from './transform-url.js';
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* The resolved media config the engine serves from. When a site declares no assets block, media is
|
|
4
5
|
* off and the value is `{ enabled: false }`; otherwise every field is filled from the AssetConfig
|
|
5
|
-
* or its default.
|
|
6
|
+
* or its default.
|
|
7
|
+
*/
|
|
6
8
|
export type ResolvedAssetConfig = {
|
|
7
9
|
enabled: false;
|
|
8
10
|
} | {
|
|
@@ -13,12 +15,16 @@ export type ResolvedAssetConfig = {
|
|
|
13
15
|
maxUploadBytes: number;
|
|
14
16
|
allowedTypes: string[];
|
|
15
17
|
variants: Record<string, VariantSpec>;
|
|
16
|
-
/**
|
|
17
|
-
*
|
|
18
|
+
/**
|
|
19
|
+
* Whether Cloudflare Image Transformations are enabled for the zone. With it false, the media
|
|
20
|
+
* resolver serves the bare full-size delivery path and ignores any preset.
|
|
21
|
+
*/
|
|
18
22
|
transformations: boolean;
|
|
19
23
|
};
|
|
20
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* Validate a site's AssetConfig and resolve it into a ResolvedAssetConfig. An undefined block leaves
|
|
21
26
|
* media off and returns `{ enabled: false }` rather than throwing. A declared block must name its R2
|
|
22
27
|
* bucket and carry a known urlForm and valid variant fit and gravity values; each failure throws a
|
|
23
|
-
* cairn:-prefixed error. The named variants merge over the built-in presets.
|
|
28
|
+
* cairn:-prefixed error. The named variants merge over the built-in presets.
|
|
29
|
+
*/
|
|
24
30
|
export declare function normalizeAssets(assets: AssetConfig | undefined): ResolvedAssetConfig;
|
package/dist/media/config.js
CHANGED
|
@@ -4,8 +4,10 @@ const DEFAULT_PUBLIC_BASE = '/media';
|
|
|
4
4
|
const DEFAULT_MAX_UPLOAD_BYTES = 25 * 1024 * 1024;
|
|
5
5
|
/** The default accepted upload MIME types: the common web image formats. */
|
|
6
6
|
const DEFAULT_ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/webp', 'image/gif', 'image/avif'];
|
|
7
|
-
/**
|
|
8
|
-
*
|
|
7
|
+
/**
|
|
8
|
+
* The built-in named transform presets. A site's `variants` merge over these, so a caller preset of
|
|
9
|
+
* the same name overrides the built-in.
|
|
10
|
+
*/
|
|
9
11
|
const BUILT_IN_PRESETS = {
|
|
10
12
|
thumb: { width: 320, height: 320, fit: 'cover' },
|
|
11
13
|
inline: { width: 800 },
|
|
@@ -14,8 +16,10 @@ const BUILT_IN_PRESETS = {
|
|
|
14
16
|
};
|
|
15
17
|
/** The fit values Cloudflare Images accepts. A variant whose fit is set to anything else is rejected. */
|
|
16
18
|
const FIT_VALUES = new Set(['scale-down', 'contain', 'cover', 'crop', 'pad']);
|
|
17
|
-
/**
|
|
18
|
-
*
|
|
19
|
+
/**
|
|
20
|
+
* The named gravity keywords Cloudflare Images accepts. A gravity is also valid as a coordinate
|
|
21
|
+
* string; everything else is rejected.
|
|
22
|
+
*/
|
|
19
23
|
const GRAVITY_KEYWORDS = new Set([
|
|
20
24
|
'auto',
|
|
21
25
|
'face',
|
|
@@ -27,9 +31,11 @@ const GRAVITY_KEYWORDS = new Set([
|
|
|
27
31
|
]);
|
|
28
32
|
/** A gravity coordinate string, e.g. "0.5x0.5". */
|
|
29
33
|
const GRAVITY_COORD_RE = /^\d+(\.\d+)?x\d+(\.\d+)?$/;
|
|
30
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* Validate one variant's fit and gravity, throwing a cairn:-prefixed error naming the offending
|
|
31
36
|
* preset and value. The type system collapses VariantSpec.gravity to string, so the gravity check
|
|
32
|
-
* is the only guard against a bogus value reaching the transform URL.
|
|
37
|
+
* is the only guard against a bogus value reaching the transform URL.
|
|
38
|
+
*/
|
|
33
39
|
function validateVariant(name, spec) {
|
|
34
40
|
if (spec.fit !== undefined && !FIT_VALUES.has(spec.fit)) {
|
|
35
41
|
throw new Error(`cairn: media variant "${name}" has an unknown fit "${spec.fit}"`);
|
|
@@ -40,10 +46,12 @@ function validateVariant(name, spec) {
|
|
|
40
46
|
throw new Error(`cairn: media variant "${name}" has an unknown gravity "${spec.gravity}"`);
|
|
41
47
|
}
|
|
42
48
|
}
|
|
43
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* Validate a site's AssetConfig and resolve it into a ResolvedAssetConfig. An undefined block leaves
|
|
44
51
|
* media off and returns `{ enabled: false }` rather than throwing. A declared block must name its R2
|
|
45
52
|
* bucket and carry a known urlForm and valid variant fit and gravity values; each failure throws a
|
|
46
|
-
* cairn:-prefixed error. The named variants merge over the built-in presets.
|
|
53
|
+
* cairn:-prefixed error. The named variants merge over the built-in presets.
|
|
54
|
+
*/
|
|
47
55
|
export function normalizeAssets(assets) {
|
|
48
56
|
if (assets === undefined)
|
|
49
57
|
return { enabled: false };
|
|
@@ -6,9 +6,11 @@ export interface DeliveryObject {
|
|
|
6
6
|
httpEtag: string;
|
|
7
7
|
/** The full object size in bytes, the denominator of a `Content-Range`. */
|
|
8
8
|
size: number;
|
|
9
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* Present only on a ranged read: the served window, used to build the `Content-Range`. R2 fills
|
|
10
11
|
* both fields for a `bytes=start-end` request; each is typed optional so the route derives the
|
|
11
|
-
* range bounds defensively against `size`.
|
|
12
|
+
* range bounds defensively against `size`.
|
|
13
|
+
*/
|
|
12
14
|
range?: {
|
|
13
15
|
offset?: number;
|
|
14
16
|
length?: number;
|
|
@@ -24,7 +24,9 @@ export interface MediaLibraryEntry {
|
|
|
24
24
|
}
|
|
25
25
|
/** The projected library keyed by the 16-hex content hash, exactly EditData's `mediaLibrary`. */
|
|
26
26
|
export type MediaLibrary = Record<string, MediaLibraryEntry>;
|
|
27
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* Project a stored MediaEntry to the picker's MediaLibraryEntry, copying every display field and
|
|
28
29
|
* dropping the source-only sha256 and original filename. The single projection editLoad and
|
|
29
|
-
* mediaLibraryLoad both call, so the popover and the Library never diverge on the shared shape.
|
|
30
|
+
* mediaLibraryLoad both call, so the popover and the Library never diverge on the shared shape.
|
|
31
|
+
*/
|
|
30
32
|
export declare function mediaLibraryEntry(entry: MediaEntry): MediaLibraryEntry;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Project a stored MediaEntry to the picker's MediaLibraryEntry, copying every display field and
|
|
2
3
|
* dropping the source-only sha256 and original filename. The single projection editLoad and
|
|
3
|
-
* mediaLibraryLoad both call, so the popover and the Library never diverge on the shared shape.
|
|
4
|
+
* mediaLibraryLoad both call, so the popover and the Library never diverge on the shared shape.
|
|
5
|
+
*/
|
|
4
6
|
export function mediaLibraryEntry(entry) {
|
|
5
7
|
return {
|
|
6
8
|
hash: entry.hash,
|
package/dist/media/manifest.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* One stored asset's row: its content hash, its human layer, and its byte and pixel facts. The
|
|
2
3
|
* `contentType` is the stored MIME type, so the delivery route serves it verbatim rather than
|
|
3
4
|
* guessing from the extension. `width` and `height` are null when no dimensions are known (the
|
|
4
|
-
* client is the only dimension source and a Worker cannot re-derive them).
|
|
5
|
+
* client is the only dimension source and a Worker cannot re-derive them).
|
|
6
|
+
*/
|
|
5
7
|
export interface MediaEntry {
|
|
6
8
|
hash: string;
|
|
7
9
|
sha256: string;
|
|
@@ -18,27 +20,39 @@ export interface MediaEntry {
|
|
|
18
20
|
}
|
|
19
21
|
/** The whole stored-asset record, keyed by the 16-hex content-hash prefix. */
|
|
20
22
|
export type MediaManifest = Record<string, MediaEntry>;
|
|
21
|
-
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
23
|
+
/**
|
|
24
|
+
* Parse a committed media manifest. Tolerant: an empty, missing, null, or non-object input yields
|
|
25
|
+
* an empty manifest, so a first ingest into a site with no manifest file reads a clean `{}`. A valid
|
|
26
|
+
* object is returned as the manifest.
|
|
27
|
+
*/
|
|
24
28
|
export declare function parseMediaManifest(json: unknown): MediaManifest;
|
|
25
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* Parse the posted `media` field into a validated list of MediaEntry rows. The field arrives as a
|
|
26
31
|
* JSON string (the usual form-post shape), an already-parsed array, or junk. A string is JSON-parsed
|
|
27
32
|
* inside a try/catch that yields `[]` on a parse failure; a non-string array is taken directly;
|
|
28
33
|
* anything else yields `[]`. Each element is validated and a failing element is dropped, so a partly
|
|
29
34
|
* malformed post still lands its good rows. This is the trust boundary for the client's optimistic
|
|
30
|
-
* records.
|
|
35
|
+
* records.
|
|
36
|
+
*/
|
|
31
37
|
export declare function parseMediaEntries(value: unknown): MediaEntry[];
|
|
32
|
-
/**
|
|
33
|
-
*
|
|
38
|
+
/**
|
|
39
|
+
* The dedup lookup: the entry stored under the content-hash prefix, or undefined when no bytes with
|
|
40
|
+
* that hash are stored yet.
|
|
41
|
+
*/
|
|
34
42
|
export declare function findByHash(manifest: MediaManifest, hash: string): MediaEntry | undefined;
|
|
35
|
-
/**
|
|
36
|
-
*
|
|
43
|
+
/**
|
|
44
|
+
* Set the entry under its own hash, replacing any same-hash row. Returns a new manifest and leaves
|
|
45
|
+
* the input untouched, so a caller's prior manifest reference stays valid. The ingest path's patch.
|
|
46
|
+
*/
|
|
37
47
|
export declare function upsertMediaEntry(manifest: MediaManifest, entry: MediaEntry): MediaManifest;
|
|
38
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* Drop the entry under the given hash, returning a new manifest and leaving the input untouched.
|
|
39
50
|
* Removing an absent hash is a no-op that still returns an equivalent new manifest. The safe-delete
|
|
40
|
-
* path's patch.
|
|
51
|
+
* path's patch.
|
|
52
|
+
*/
|
|
41
53
|
export declare function removeMediaEntry(manifest: MediaManifest, hash: string): MediaManifest;
|
|
42
|
-
/**
|
|
43
|
-
*
|
|
54
|
+
/**
|
|
55
|
+
* Serialize canonically: the top-level hash keys sorted ascending, two-space pretty, and a trailing
|
|
56
|
+
* newline, so the committed file diffs cleanly in a PR and a re-serialization is byte-identical.
|
|
57
|
+
*/
|
|
44
58
|
export declare function serializeMediaManifest(manifest: MediaManifest): string;
|
package/dist/media/manifest.js
CHANGED
|
@@ -3,19 +3,22 @@
|
|
|
3
3
|
// the dedup lookup: an ingest checks the content-hash prefix here before storing, so the same bytes
|
|
4
4
|
// are never stored twice. It mirrors the content manifest in ../content/manifest.ts, keyed by the
|
|
5
5
|
// 16-hex content-hash prefix rather than concept and id.
|
|
6
|
-
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
/**
|
|
7
|
+
* Parse a committed media manifest. Tolerant: an empty, missing, null, or non-object input yields
|
|
8
|
+
* an empty manifest, so a first ingest into a site with no manifest file reads a clean `{}`. A valid
|
|
9
|
+
* object is returned as the manifest.
|
|
10
|
+
*/
|
|
9
11
|
export function parseMediaManifest(json) {
|
|
10
12
|
if (!json || typeof json !== 'object' || Array.isArray(json))
|
|
11
13
|
return {};
|
|
12
14
|
return json;
|
|
13
15
|
}
|
|
14
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Validate one posted value as a MediaEntry, returning it narrowed or undefined. The trust boundary
|
|
15
18
|
* for an optimistic record the client re-posts: the upload action server-owned each field at
|
|
16
19
|
* creation, but a re-post is untrusted, so every field is re-checked. A `hash` must be the 16-hex
|
|
17
|
-
* content-hash prefix
|
|
18
|
-
|
|
20
|
+
* content-hash prefix.
|
|
21
|
+
*/
|
|
19
22
|
function validateMediaEntry(value) {
|
|
20
23
|
if (!value || typeof value !== 'object')
|
|
21
24
|
return undefined;
|
|
@@ -51,12 +54,14 @@ function validateMediaEntry(value) {
|
|
|
51
54
|
createdAt: e.createdAt,
|
|
52
55
|
};
|
|
53
56
|
}
|
|
54
|
-
/**
|
|
57
|
+
/**
|
|
58
|
+
* Parse the posted `media` field into a validated list of MediaEntry rows. The field arrives as a
|
|
55
59
|
* JSON string (the usual form-post shape), an already-parsed array, or junk. A string is JSON-parsed
|
|
56
60
|
* inside a try/catch that yields `[]` on a parse failure; a non-string array is taken directly;
|
|
57
61
|
* anything else yields `[]`. Each element is validated and a failing element is dropped, so a partly
|
|
58
62
|
* malformed post still lands its good rows. This is the trust boundary for the client's optimistic
|
|
59
|
-
* records.
|
|
63
|
+
* records.
|
|
64
|
+
*/
|
|
60
65
|
export function parseMediaEntries(value) {
|
|
61
66
|
let raw = value;
|
|
62
67
|
if (typeof value === 'string') {
|
|
@@ -77,25 +82,33 @@ export function parseMediaEntries(value) {
|
|
|
77
82
|
}
|
|
78
83
|
return entries;
|
|
79
84
|
}
|
|
80
|
-
/**
|
|
81
|
-
*
|
|
85
|
+
/**
|
|
86
|
+
* The dedup lookup: the entry stored under the content-hash prefix, or undefined when no bytes with
|
|
87
|
+
* that hash are stored yet.
|
|
88
|
+
*/
|
|
82
89
|
export function findByHash(manifest, hash) {
|
|
83
90
|
return manifest[hash];
|
|
84
91
|
}
|
|
85
|
-
/**
|
|
86
|
-
*
|
|
92
|
+
/**
|
|
93
|
+
* Set the entry under its own hash, replacing any same-hash row. Returns a new manifest and leaves
|
|
94
|
+
* the input untouched, so a caller's prior manifest reference stays valid. The ingest path's patch.
|
|
95
|
+
*/
|
|
87
96
|
export function upsertMediaEntry(manifest, entry) {
|
|
88
97
|
return { ...manifest, [entry.hash]: entry };
|
|
89
98
|
}
|
|
90
|
-
/**
|
|
99
|
+
/**
|
|
100
|
+
* Drop the entry under the given hash, returning a new manifest and leaving the input untouched.
|
|
91
101
|
* Removing an absent hash is a no-op that still returns an equivalent new manifest. The safe-delete
|
|
92
|
-
* path's patch.
|
|
102
|
+
* path's patch.
|
|
103
|
+
*/
|
|
93
104
|
export function removeMediaEntry(manifest, hash) {
|
|
94
105
|
const { [hash]: _removed, ...rest } = manifest;
|
|
95
106
|
return rest;
|
|
96
107
|
}
|
|
97
|
-
/**
|
|
98
|
-
*
|
|
108
|
+
/**
|
|
109
|
+
* Serialize canonically: the top-level hash keys sorted ascending, two-space pretty, and a trailing
|
|
110
|
+
* newline, so the committed file diffs cleanly in a PR and a re-serialization is byte-identical.
|
|
111
|
+
*/
|
|
99
112
|
export function serializeMediaManifest(manifest) {
|
|
100
113
|
const sorted = {};
|
|
101
114
|
for (const hash of Object.keys(manifest).sort()) {
|
package/dist/media/naming.d.ts
CHANGED
|
@@ -2,17 +2,23 @@
|
|
|
2
2
|
export declare function hashBytes(bytes: Uint8Array): Promise<string>;
|
|
3
3
|
/** The first 16 characters of a full hex digest, the content-hash prefix media references commit to. */
|
|
4
4
|
export declare function shortHash(full: string): string;
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* The strict ingest transform from a raw filename to a slug that satisfies the media: slug grammar,
|
|
6
7
|
* or the literal `file`. Drops the extension, lowercases, transliterates accents, collapses non-alphanumeric runs
|
|
7
8
|
* to a single hyphen, trims, caps at 80 chars, screens Windows reserved names, and falls back to
|
|
8
|
-
* `file` when nothing usable is left.
|
|
9
|
+
* `file` when nothing usable is left.
|
|
10
|
+
*/
|
|
9
11
|
export declare function slugifyFilename(name: string): string;
|
|
10
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* The content-addressed R2 object key `media/<aa>/<shortHash>.<ext>`, fanned out on the first two
|
|
11
14
|
* hex chars of the short hash. No leading slash: this is an object key, not a URL. `ext` is bare
|
|
12
|
-
* (no dot), for example `webp`.
|
|
15
|
+
* (no dot), for example `webp`.
|
|
16
|
+
*/
|
|
13
17
|
export declare function r2Key(shortHash: string, ext: string): string;
|
|
14
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* The public delivery URL path, with a leading slash, under the delivery base (`publicBase`,
|
|
15
20
|
* default `/media`). The `slug` form is human-readable (`<base>/<slug>.<shortHash>.<ext>`, or
|
|
16
21
|
* `<base>/<shortHash>.<ext>` when the slug is null); the `opaque` form mirrors the R2 fan-out
|
|
17
|
-
* (`<base>/<aa>/<shortHash>.<ext>`) and ignores the slug.
|
|
22
|
+
* (`<base>/<aa>/<shortHash>.<ext>`) and ignores the slug.
|
|
23
|
+
*/
|
|
18
24
|
export declare function publicPath(slug: string | null, shortHash: string, ext: string, urlForm: 'slug' | 'opaque', publicBase?: string): string;
|