@demiurgejs/core 0.2.0-beta.3 → 0.2.0-beta.4
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 +13 -11
- package/bin/demiurge.mjs +30 -12
- package/dist/browser/file-router.d.ts +388 -5
- package/dist/browser/file-router.d.ts.map +1 -1
- package/dist/browser/focus.d.ts +20 -0
- package/dist/browser/focus.d.ts.map +1 -0
- package/dist/browser/index.d.ts +7 -1
- package/dist/browser/index.d.ts.map +1 -1
- package/dist/browser/mutation-action.d.ts +81 -0
- package/dist/browser/mutation-action.d.ts.map +1 -0
- package/dist/chunks/document-shell-CpJnFtSC.js +36 -0
- package/dist/chunks/document-shell-CpJnFtSC.js.map +1 -0
- package/dist/chunks/env-startup-DOBYWlZD.js +257 -0
- package/dist/chunks/env-startup-DOBYWlZD.js.map +1 -0
- package/dist/chunks/{image-codec-Di53WpcH.js → image-codec-BmzWnH6W.js} +2 -2
- package/dist/chunks/{image-codec-Di53WpcH.js.map → image-codec-BmzWnH6W.js.map} +1 -1
- package/dist/chunks/{images-Edar3eU1.js → images-BQ80950B.js} +99 -91
- package/dist/chunks/images-BQ80950B.js.map +1 -0
- package/dist/chunks/index-BVk9zWbe.js +832 -0
- package/dist/chunks/index-BVk9zWbe.js.map +1 -0
- package/dist/chunks/manifest-vO6c0G0W.js +19 -0
- package/dist/chunks/manifest-vO6c0G0W.js.map +1 -0
- package/dist/chunks/plugin-BQgMiHxa.js +1827 -0
- package/dist/chunks/plugin-BQgMiHxa.js.map +1 -0
- package/dist/chunks/session-store-ChModVcq.js +92 -0
- package/dist/chunks/session-store-ChModVcq.js.map +1 -0
- package/dist/chunks/static-files-ehaD1kE7.js +51 -0
- package/dist/chunks/static-files-ehaD1kE7.js.map +1 -0
- package/dist/chunks/{streaming-D0fGAuiO.js → streaming-BEbPN7Uk.js} +163 -163
- package/dist/chunks/streaming-BEbPN7Uk.js.map +1 -0
- package/dist/chunks/testing-DueXbMo4.js +4167 -0
- package/dist/chunks/testing-DueXbMo4.js.map +1 -0
- package/dist/chunks/vite-DO70kZA6.js +259 -0
- package/dist/chunks/vite-DO70kZA6.js.map +1 -0
- package/dist/cli.d.ts +17 -25
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +159 -124
- package/dist/cli.js.map +1 -1
- package/dist/config/define.d.ts +3 -0
- package/dist/config/define.d.ts.map +1 -0
- package/dist/config/index.d.ts +7 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +78 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/load.d.ts +12 -0
- package/dist/config/load.d.ts.map +1 -0
- package/dist/config/types.d.ts +64 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/validate.d.ts +9 -0
- package/dist/config/validate.d.ts.map +1 -0
- package/dist/config/vite.d.ts +12 -0
- package/dist/config/vite.d.ts.map +1 -0
- package/dist/data/cache.d.ts +2 -0
- package/dist/data/cache.d.ts.map +1 -1
- package/dist/data/idempotency.d.ts.map +1 -1
- package/dist/deployment/testing.d.ts +25 -0
- package/dist/deployment/testing.d.ts.map +1 -0
- package/dist/deployment/testing.js +171 -0
- package/dist/deployment/testing.js.map +1 -0
- package/dist/document/hydration.d.ts +9 -0
- package/dist/document/hydration.d.ts.map +1 -1
- package/dist/document/index.d.ts +4 -2
- package/dist/document/index.d.ts.map +1 -1
- package/dist/document/metadata.d.ts +8 -0
- package/dist/document/metadata.d.ts.map +1 -1
- package/dist/document/navigation.d.ts +28 -0
- package/dist/document/navigation.d.ts.map +1 -0
- package/dist/document/render.d.ts +3 -1
- package/dist/document/render.d.ts.map +1 -1
- package/dist/document/seo.d.ts.map +1 -1
- package/dist/edge/assets.d.ts.map +1 -1
- package/dist/edge/index.js +55 -73
- package/dist/edge/index.js.map +1 -1
- package/dist/index.d.ts +12 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1860 -691
- package/dist/index.js.map +1 -1
- package/dist/internal/testing.js +12 -12
- package/dist/kv/index.d.ts +2 -0
- package/dist/kv/index.d.ts.map +1 -1
- package/dist/kv/index.js +170 -73
- package/dist/kv/index.js.map +1 -1
- package/dist/kv/session-store.d.ts +20 -0
- package/dist/kv/session-store.d.ts.map +1 -0
- package/dist/manifest.d.ts +10 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/node/image.d.ts.map +1 -1
- package/dist/node/index.d.ts +3 -0
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +251 -178
- package/dist/node/index.js.map +1 -1
- package/dist/node/serve.d.ts +47 -0
- package/dist/node/serve.d.ts.map +1 -0
- package/dist/node/static.d.ts.map +1 -1
- package/dist/platform/fonts.d.ts.map +1 -1
- package/dist/platform/image-url.d.ts.map +1 -1
- package/dist/platform/url.d.ts +19 -0
- package/dist/platform/url.d.ts.map +1 -0
- package/dist/platform/web-vitals.d.ts.map +1 -1
- package/dist/redis/index.d.ts +2 -0
- package/dist/redis/index.d.ts.map +1 -1
- package/dist/redis/index.js +186 -69
- package/dist/redis/index.js.map +1 -1
- package/dist/redis/session-store.d.ts +9 -0
- package/dist/redis/session-store.d.ts.map +1 -0
- package/dist/route/index.d.ts +2 -2
- package/dist/route/index.d.ts.map +1 -1
- package/dist/route/mutation.d.ts +97 -0
- package/dist/route/mutation.d.ts.map +1 -0
- package/dist/route/types.d.ts +6 -1
- package/dist/route/types.d.ts.map +1 -1
- package/dist/router/manifest.d.ts +8 -0
- package/dist/router/manifest.d.ts.map +1 -1
- package/dist/router/navigation-data.d.ts +9 -1
- package/dist/router/navigation-data.d.ts.map +1 -1
- package/dist/routing/generate.d.ts +3 -0
- package/dist/routing/generate.d.ts.map +1 -1
- package/dist/routing/index.d.ts +3 -1
- package/dist/routing/index.d.ts.map +1 -1
- package/dist/routing/locale.d.ts +43 -0
- package/dist/routing/locale.d.ts.map +1 -0
- package/dist/routing/types.d.ts +12 -0
- package/dist/routing/types.d.ts.map +1 -1
- package/dist/security/cookie-session.d.ts +43 -0
- package/dist/security/cookie-session.d.ts.map +1 -0
- package/dist/security/csrf.d.ts +1 -1
- package/dist/security/csrf.d.ts.map +1 -1
- package/dist/security/env-startup.d.ts +15 -0
- package/dist/security/env-startup.d.ts.map +1 -0
- package/dist/security/env.d.ts +25 -6
- package/dist/security/env.d.ts.map +1 -1
- package/dist/security/index.d.ts +9 -1
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/report.d.ts.map +1 -1
- package/dist/security/session-manager.d.ts +32 -0
- package/dist/security/session-manager.d.ts.map +1 -0
- package/dist/security/session-store.d.ts +51 -0
- package/dist/security/session-store.d.ts.map +1 -0
- package/dist/security/testing.d.ts +10 -0
- package/dist/security/testing.d.ts.map +1 -0
- package/dist/security/testing.js +145 -0
- package/dist/security/testing.js.map +1 -0
- package/dist/security/types.d.ts +1 -0
- package/dist/security/types.d.ts.map +1 -1
- package/dist/server/document-shell.d.ts.map +1 -1
- package/dist/server/errors.d.ts +1 -0
- package/dist/server/errors.d.ts.map +1 -1
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/not-found.d.ts +1 -0
- package/dist/server/not-found.d.ts.map +1 -1
- package/dist/server/render-tree.d.ts.map +1 -1
- package/dist/server/request-handler.d.ts +4 -0
- package/dist/server/request-handler.d.ts.map +1 -1
- package/dist/server/request-metadata.d.ts +1 -0
- package/dist/server/request-metadata.d.ts.map +1 -1
- package/dist/server/ssr.d.ts +2 -0
- package/dist/server/ssr.d.ts.map +1 -1
- package/dist/static/index.d.ts +2 -0
- package/dist/static/index.d.ts.map +1 -1
- package/dist/static/index.js +380 -363
- package/dist/static/index.js.map +1 -1
- package/dist/static/preview.d.ts.map +1 -1
- package/dist/static/vercel.d.ts +11 -2
- package/dist/static/vercel.d.ts.map +1 -1
- package/dist/static-files.d.ts +12 -0
- package/dist/static-files.d.ts.map +1 -1
- package/dist/type-guards.d.ts +19 -0
- package/dist/type-guards.d.ts.map +1 -0
- package/dist/vite/env-boundary.d.ts +14 -0
- package/dist/vite/env-boundary.d.ts.map +1 -0
- package/dist/vite/index.d.ts +5 -3
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +22 -1560
- package/dist/vite/index.js.map +1 -1
- package/dist/vite/plugin.d.ts +6 -1
- package/dist/vite/plugin.d.ts.map +1 -1
- package/dist/vite/policy-verification.d.ts.map +1 -1
- package/package.json +19 -6
- package/dist/chunks/document-shell-WoM8yZlR.js +0 -35
- package/dist/chunks/document-shell-WoM8yZlR.js.map +0 -1
- package/dist/chunks/images-Edar3eU1.js.map +0 -1
- package/dist/chunks/request-metadata-DaIYq4Ea.js +0 -677
- package/dist/chunks/request-metadata-DaIYq4Ea.js.map +0 -1
- package/dist/chunks/static-files-DUjHw-4H.js +0 -11
- package/dist/chunks/static-files-DUjHw-4H.js.map +0 -1
- package/dist/chunks/streaming-D0fGAuiO.js.map +0 -1
- package/dist/chunks/testing-CRflAw4X.js +0 -3475
- package/dist/chunks/testing-CRflAw4X.js.map +0 -1
- package/dist/route/action.d.ts +0 -33
- package/dist/route/action.d.ts.map +0 -1
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { type MutationValidation } from "../route";
|
|
2
|
+
import { type MutationDataFor, type MutationFieldsFor, type MutationMethodFor, type MutationRoute, type PathValue, type PathVarsFor, type RoutePathVars } from "../routing";
|
|
3
|
+
declare const mutationFormActionMetadata: unique symbol;
|
|
4
|
+
declare const mutationFormActionResult: unique symbol;
|
|
5
|
+
export type MutationResult<TData = unknown, TField extends string = string> = {
|
|
6
|
+
version: 1;
|
|
7
|
+
status: "success";
|
|
8
|
+
data?: TData;
|
|
9
|
+
revalidate?: boolean;
|
|
10
|
+
} | {
|
|
11
|
+
version: 1;
|
|
12
|
+
status: "invalid";
|
|
13
|
+
validation: MutationValidation<TField>;
|
|
14
|
+
} | {
|
|
15
|
+
version: 1;
|
|
16
|
+
status: "redirect";
|
|
17
|
+
location: string;
|
|
18
|
+
history: "push" | "replace";
|
|
19
|
+
} | {
|
|
20
|
+
version: 1;
|
|
21
|
+
status: "failed";
|
|
22
|
+
message?: string;
|
|
23
|
+
};
|
|
24
|
+
export type MutationAction<TData = unknown, TField extends string = string> = (previousState: MutationResult<TData, TField> | undefined, formData: FormData) => Promise<MutationResult<TData, TField>>;
|
|
25
|
+
export type MutationFormAction<TData = unknown, TField extends string = string> = ((formData: FormData) => void) & {
|
|
26
|
+
readonly [mutationFormActionMetadata]: {
|
|
27
|
+
method: "POST";
|
|
28
|
+
url: string;
|
|
29
|
+
};
|
|
30
|
+
readonly [mutationFormActionResult]?: MutationResult<TData, TField>;
|
|
31
|
+
};
|
|
32
|
+
type MutationActionPath<TRoute extends MutationRoute> = TRoute extends keyof RoutePathVars & string ? keyof PathVarsFor<TRoute> extends never ? {
|
|
33
|
+
path?: never;
|
|
34
|
+
} : {
|
|
35
|
+
path: PathVarsFor<TRoute>;
|
|
36
|
+
} : {
|
|
37
|
+
path?: Record<string, PathValue>;
|
|
38
|
+
};
|
|
39
|
+
export type MutationActionOptions<TRoute extends MutationRoute, TMethod extends MutationMethodFor<TRoute> = MutationMethodFor<TRoute>> = {
|
|
40
|
+
method: TMethod;
|
|
41
|
+
route: TRoute;
|
|
42
|
+
} & MutationActionPath<TRoute>;
|
|
43
|
+
type MutationRouter = {
|
|
44
|
+
redirect(location: string, history: "push" | "replace", controller: AbortController): void;
|
|
45
|
+
refresh(): Promise<void>;
|
|
46
|
+
};
|
|
47
|
+
export declare function registerMutationRouter(router: MutationRouter): () => void;
|
|
48
|
+
export declare function abortMutationActions(except?: AbortController): void;
|
|
49
|
+
export declare function createMutationAction<TData = never, const TRoute extends MutationRoute = MutationRoute, const TMethod extends MutationMethodFor<TRoute> = MutationMethodFor<TRoute>>(options: MutationActionOptions<TRoute, TMethod>): MutationAction<[
|
|
50
|
+
TData
|
|
51
|
+
] extends [never] ? MutationDataFor<TRoute, TMethod> : TData, MutationFieldsFor<TRoute, TMethod>>;
|
|
52
|
+
export declare function useMutationAction<TData = never, const TRoute extends MutationRoute = MutationRoute, const TMethod extends MutationMethodFor<TRoute> = MutationMethodFor<TRoute>>(options: MutationActionOptions<TRoute, TMethod> & {
|
|
53
|
+
method: TMethod & "POST";
|
|
54
|
+
}, initialState?: MutationResult<[
|
|
55
|
+
TData
|
|
56
|
+
] extends [never] ? MutationDataFor<TRoute, TMethod> : TData, MutationFieldsFor<TRoute, TMethod>>): readonly [MutationResult<[TData] extends [never] ? MutationDataFor<TRoute, TMethod> : TData, MutationFieldsFor<TRoute, TMethod>> | undefined, MutationFormAction<[TData] extends [never] ? MutationDataFor<TRoute, TMethod> : TData, MutationFieldsFor<TRoute, TMethod>>, boolean];
|
|
57
|
+
export declare function mutationFormActionDetails(action: MutationFormAction): {
|
|
58
|
+
method: "POST";
|
|
59
|
+
url: string;
|
|
60
|
+
};
|
|
61
|
+
export declare function submitMutation<TData = unknown, TField extends string = string>(options: {
|
|
62
|
+
body: BodyInit;
|
|
63
|
+
contentType?: string;
|
|
64
|
+
method: string;
|
|
65
|
+
signal: AbortSignal;
|
|
66
|
+
url: string;
|
|
67
|
+
}): Promise<MutationResult<TData, TField>>;
|
|
68
|
+
export declare function performMutationRequest<TData = unknown, TField extends string = string>(options: {
|
|
69
|
+
body: BodyInit;
|
|
70
|
+
contentType?: string;
|
|
71
|
+
method: string;
|
|
72
|
+
signal: AbortSignal;
|
|
73
|
+
url: string;
|
|
74
|
+
}): Promise<{
|
|
75
|
+
response: Response;
|
|
76
|
+
result: MutationResult<TData, TField> | undefined;
|
|
77
|
+
}>;
|
|
78
|
+
export declare function readMutationResult<TData = unknown, TField extends string = string>(response: Response): Promise<MutationResult<TData, TField> | undefined>;
|
|
79
|
+
export declare function validateMutationRedirect(location: string): string | undefined;
|
|
80
|
+
export {};
|
|
81
|
+
//# sourceMappingURL=mutation-action.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mutation-action.d.ts","sourceRoot":"","sources":["../../src/browser/mutation-action.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { r as c } from "./index-BVk9zWbe.js";
|
|
2
|
+
const r = '<template data-demiurge-stream-root=""></template>';
|
|
3
|
+
async function i(t, e) {
|
|
4
|
+
const l = c({
|
|
5
|
+
body: { data: t.data, locale: e.locale, navigation: e.navigation },
|
|
6
|
+
dir: e.dir,
|
|
7
|
+
entrySrc: e.clientEntry,
|
|
8
|
+
lang: e.lang,
|
|
9
|
+
links: t.links,
|
|
10
|
+
metadata: t.metadata,
|
|
11
|
+
nonce: e.nonce,
|
|
12
|
+
scripts: e.scripts ?? t.scripts,
|
|
13
|
+
styles: e.styles,
|
|
14
|
+
title: e.title
|
|
15
|
+
});
|
|
16
|
+
if (!e.transformDocument)
|
|
17
|
+
return l;
|
|
18
|
+
const a = await e.transformDocument(
|
|
19
|
+
`${l.prefix}${r}${l.suffix}`
|
|
20
|
+
), n = a.indexOf(r);
|
|
21
|
+
if (n === -1 || a.indexOf(
|
|
22
|
+
r,
|
|
23
|
+
n + r.length
|
|
24
|
+
) !== -1)
|
|
25
|
+
throw new Error(
|
|
26
|
+
"Demiurge streaming document transform must preserve the root marker exactly once."
|
|
27
|
+
);
|
|
28
|
+
return {
|
|
29
|
+
prefix: a.slice(0, n),
|
|
30
|
+
suffix: a.slice(n + r.length)
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export {
|
|
34
|
+
i as c
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=document-shell-CpJnFtSC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document-shell-CpJnFtSC.js","sources":["../../src/server/document-shell.ts"],"sourcesContent":["import { renderDocumentShell } from \"../document/render\";\nimport type { ScriptTag } from \"../document/scripts\";\nimport type { LoadedRouteMatch } from \"../router\";\nimport type { SsrRenderOptions } from \"./ssr\";\n\n// A streaming render sends the document head before the body exists. A\n// transform that rewrites the document therefore needs a placeholder where the\n// streamed body will go, and it has to leave that placeholder in place.\nexport const STREAM_ROOT_MARKER =\n '<template data-demiurge-stream-root=\"\"></template>';\n\nexport type DocumentShell = {\n prefix: string;\n suffix: string;\n};\n\nexport async function createDocumentShell(\n match: LoadedRouteMatch,\n options: SsrRenderOptions & { scripts?: ScriptTag[] },\n): Promise<DocumentShell> {\n const shell = renderDocumentShell({\n body: { data: match.data, locale: options.locale, navigation: options.navigation },\n dir: options.dir,\n entrySrc: options.clientEntry,\n lang: options.lang,\n links: match.links,\n metadata: match.metadata,\n nonce: options.nonce,\n scripts: options.scripts ?? match.scripts,\n styles: options.styles,\n title: options.title,\n });\n\n if (!options.transformDocument) {\n return shell;\n }\n\n const transformed = await options.transformDocument(\n `${shell.prefix}${STREAM_ROOT_MARKER}${shell.suffix}`,\n );\n const markerIndex = transformed.indexOf(STREAM_ROOT_MARKER);\n\n if (\n markerIndex === -1 ||\n transformed.indexOf(\n STREAM_ROOT_MARKER,\n markerIndex + STREAM_ROOT_MARKER.length,\n ) !== -1\n ) {\n throw new Error(\n \"Demiurge streaming document transform must preserve the root marker exactly once.\",\n );\n }\n\n return {\n prefix: transformed.slice(0, markerIndex),\n suffix: transformed.slice(markerIndex + STREAM_ROOT_MARKER.length),\n };\n}\n"],"names":["STREAM_ROOT_MARKER","createDocumentShell","match","options","shell","renderDocumentShell","transformed","markerIndex"],"mappings":";AAQO,MAAMA,IACX;AAOF,eAAsBC,EACpBC,GACAC,GACwB;AACxB,QAAMC,IAAQC,EAAoB;AAAA,IAChC,MAAM,EAAE,MAAMH,EAAM,MAAM,QAAQC,EAAQ,QAAQ,YAAYA,EAAQ,WAAA;AAAA,IACtE,KAAKA,EAAQ;AAAA,IACb,UAAUA,EAAQ;AAAA,IAClB,MAAMA,EAAQ;AAAA,IACd,OAAOD,EAAM;AAAA,IACb,UAAUA,EAAM;AAAA,IAChB,OAAOC,EAAQ;AAAA,IACf,SAASA,EAAQ,WAAWD,EAAM;AAAA,IAClC,QAAQC,EAAQ;AAAA,IAChB,OAAOA,EAAQ;AAAA,EAAA,CAChB;AAED,MAAI,CAACA,EAAQ;AACX,WAAOC;AAGT,QAAME,IAAc,MAAMH,EAAQ;AAAA,IAChC,GAAGC,EAAM,MAAM,GAAGJ,CAAkB,GAAGI,EAAM,MAAM;AAAA,EAAA,GAE/CG,IAAcD,EAAY,QAAQN,CAAkB;AAE1D,MACEO,MAAgB,MAChBD,EAAY;AAAA,IACVN;AAAA,IACAO,IAAcP,EAAmB;AAAA,EAAA,MAC7B;AAEN,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAIJ,SAAO;AAAA,IACL,QAAQM,EAAY,MAAM,GAAGC,CAAW;AAAA,IACxC,QAAQD,EAAY,MAAMC,IAAcP,EAAmB,MAAM;AAAA,EAAA;AAErE;"}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
class g extends Error {
|
|
2
|
+
issues;
|
|
3
|
+
constructor(n) {
|
|
4
|
+
super(j(n)), this.name = "EnvValidationError", this.issues = n;
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
function L(e) {
|
|
8
|
+
return e;
|
|
9
|
+
}
|
|
10
|
+
function A(e, n) {
|
|
11
|
+
const r = [], t = {};
|
|
12
|
+
for (const i of Object.keys(e)) {
|
|
13
|
+
const s = e[i], a = n[String(i)];
|
|
14
|
+
if (a === void 0 || a === "") {
|
|
15
|
+
if (s.optional) {
|
|
16
|
+
t[i] = void 0;
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
r.push({
|
|
20
|
+
code: "missing",
|
|
21
|
+
key: String(i),
|
|
22
|
+
message: `Environment variable ${String(i)} is required.`
|
|
23
|
+
});
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
t[i] = s.parse(String(i), a);
|
|
28
|
+
} catch (u) {
|
|
29
|
+
r.push({
|
|
30
|
+
code: "invalid",
|
|
31
|
+
key: String(i),
|
|
32
|
+
message: u instanceof Error ? u.message : `Environment variable ${String(i)} is invalid.`
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (r.length)
|
|
37
|
+
throw new g(r);
|
|
38
|
+
return t;
|
|
39
|
+
}
|
|
40
|
+
function b(e = {}) {
|
|
41
|
+
return f(e, !1, (n, r) => {
|
|
42
|
+
if (r === "true" || r === "1")
|
|
43
|
+
return !0;
|
|
44
|
+
if (r === "false" || r === "0")
|
|
45
|
+
return !1;
|
|
46
|
+
throw new Error(`Environment variable ${n} must be true, false, 1, or 0.`);
|
|
47
|
+
}, "boolean");
|
|
48
|
+
}
|
|
49
|
+
function w(e, n = {}) {
|
|
50
|
+
return f(n, !1, (r, t) => {
|
|
51
|
+
if (e.includes(t))
|
|
52
|
+
return t;
|
|
53
|
+
throw new Error(
|
|
54
|
+
`Environment variable ${r} must be one of: ${e.join(", ")}.`
|
|
55
|
+
);
|
|
56
|
+
}, "enum", { values: [...e] });
|
|
57
|
+
}
|
|
58
|
+
function $(e = {}) {
|
|
59
|
+
return f(e, !1, (n, r) => {
|
|
60
|
+
if (!/^-?\d+$/.test(r))
|
|
61
|
+
throw new Error(`Environment variable ${n} must be an integer.`);
|
|
62
|
+
const t = Number(r);
|
|
63
|
+
if (!Number.isSafeInteger(t))
|
|
64
|
+
throw new Error(`Environment variable ${n} must be a safe integer.`);
|
|
65
|
+
if (e.min !== void 0 && t < e.min)
|
|
66
|
+
throw new Error(
|
|
67
|
+
`Environment variable ${n} must be greater than or equal to ${e.min}.`
|
|
68
|
+
);
|
|
69
|
+
if (e.max !== void 0 && t > e.max)
|
|
70
|
+
throw new Error(
|
|
71
|
+
`Environment variable ${n} must be less than or equal to ${e.max}.`
|
|
72
|
+
);
|
|
73
|
+
return t;
|
|
74
|
+
}, "integer");
|
|
75
|
+
}
|
|
76
|
+
function p(e = {}) {
|
|
77
|
+
return f(
|
|
78
|
+
e,
|
|
79
|
+
!0,
|
|
80
|
+
(n, r) => d(n, r, e),
|
|
81
|
+
"secret"
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
function k(e = {}) {
|
|
85
|
+
return f(
|
|
86
|
+
e,
|
|
87
|
+
!1,
|
|
88
|
+
(n, r) => d(n, r, e),
|
|
89
|
+
"string"
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
function S(e = {}) {
|
|
93
|
+
return f(e, !1, (n, r) => {
|
|
94
|
+
let t;
|
|
95
|
+
try {
|
|
96
|
+
t = new URL(r);
|
|
97
|
+
} catch {
|
|
98
|
+
throw new Error(`Environment variable ${n} must be a valid URL.`);
|
|
99
|
+
}
|
|
100
|
+
if (e.protocols?.length && !e.protocols.includes(t.protocol))
|
|
101
|
+
throw new Error(
|
|
102
|
+
`Environment variable ${n} must use one of these protocols: ${e.protocols.join(", ")}.`
|
|
103
|
+
);
|
|
104
|
+
return t;
|
|
105
|
+
}, "url");
|
|
106
|
+
}
|
|
107
|
+
const l = {
|
|
108
|
+
boolean: b,
|
|
109
|
+
enum: w,
|
|
110
|
+
integer: $,
|
|
111
|
+
secret: p,
|
|
112
|
+
string: k,
|
|
113
|
+
url: S
|
|
114
|
+
};
|
|
115
|
+
function f(e, n, r, t = "string", i = {}) {
|
|
116
|
+
const s = { ...e, ...i }, { client: a = !1, critical: u = !1, optional: o = !1, ...c } = s;
|
|
117
|
+
if (a && n)
|
|
118
|
+
throw new Error(
|
|
119
|
+
"A secret environment variable cannot reach client code. Declare a separate variable for the browser."
|
|
120
|
+
);
|
|
121
|
+
return {
|
|
122
|
+
client: a,
|
|
123
|
+
critical: u,
|
|
124
|
+
kind: t,
|
|
125
|
+
optional: o,
|
|
126
|
+
options: c,
|
|
127
|
+
parse: r,
|
|
128
|
+
sensitive: n
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function d(e, n, r) {
|
|
132
|
+
if (r.minLength !== void 0 && n.length < r.minLength)
|
|
133
|
+
throw new Error(
|
|
134
|
+
`Environment variable ${e} must be at least ${r.minLength} characters.`
|
|
135
|
+
);
|
|
136
|
+
return n;
|
|
137
|
+
}
|
|
138
|
+
function j(e) {
|
|
139
|
+
return e.length === 1 ? e[0].message : `Environment validation failed with ${e.length} issues.`;
|
|
140
|
+
}
|
|
141
|
+
const v = /* @__PURE__ */ new Map();
|
|
142
|
+
function D(e) {
|
|
143
|
+
return Object.fromEntries(
|
|
144
|
+
Object.entries(e).map(([n, r]) => [n, {
|
|
145
|
+
client: r.client,
|
|
146
|
+
critical: r.critical,
|
|
147
|
+
kind: r.kind,
|
|
148
|
+
optional: r.optional,
|
|
149
|
+
options: r.options,
|
|
150
|
+
sensitive: r.sensitive
|
|
151
|
+
}])
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
function y(e) {
|
|
155
|
+
return Object.fromEntries(
|
|
156
|
+
Object.entries(e).map(([n, r]) => [
|
|
157
|
+
n,
|
|
158
|
+
O(n, r)
|
|
159
|
+
])
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
function T(e, n = {}) {
|
|
163
|
+
const r = V(e) ? e : y(e), t = n.source ?? q(), i = [], s = [], a = {};
|
|
164
|
+
for (const [o, c] of Object.entries(r)) {
|
|
165
|
+
const m = t[o];
|
|
166
|
+
if (m === void 0 || m === "") {
|
|
167
|
+
if (a[o] = void 0, c.optional) continue;
|
|
168
|
+
E(c, i, s, {
|
|
169
|
+
code: "missing",
|
|
170
|
+
key: o,
|
|
171
|
+
message: `Environment variable ${o} is required.`
|
|
172
|
+
});
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
a[o] = c.parse(o, m);
|
|
177
|
+
} catch (h) {
|
|
178
|
+
a[o] = void 0, E(c, i, s, {
|
|
179
|
+
code: "invalid",
|
|
180
|
+
key: o,
|
|
181
|
+
message: h instanceof Error ? h.message : `Environment variable ${o} is invalid.`
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (i.length) throw new g(i);
|
|
186
|
+
const u = n.warn ?? x;
|
|
187
|
+
for (const o of s) u(o);
|
|
188
|
+
for (const [o, c] of Object.entries(a)) v.set(o, c);
|
|
189
|
+
return { values: a, warnings: s };
|
|
190
|
+
}
|
|
191
|
+
function z(e) {
|
|
192
|
+
const n = {};
|
|
193
|
+
for (const r of Object.keys(e)) {
|
|
194
|
+
if (!v.has(r))
|
|
195
|
+
throw new Error(
|
|
196
|
+
`Demiurge did not validate the environment variable ${r}. The framework validates the schema of demiurge.config.ts when the server starts.`
|
|
197
|
+
);
|
|
198
|
+
n[r] = v.get(r);
|
|
199
|
+
}
|
|
200
|
+
return n;
|
|
201
|
+
}
|
|
202
|
+
function N() {
|
|
203
|
+
v.clear();
|
|
204
|
+
}
|
|
205
|
+
function E(e, n, r, t) {
|
|
206
|
+
if (e.critical) {
|
|
207
|
+
n.push(t);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
r.push(
|
|
211
|
+
`Demiurge started without a valid value for ${t.key}. ${t.message} A request that needs this value fails.`
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
function O(e, n) {
|
|
215
|
+
const r = {
|
|
216
|
+
...n.options,
|
|
217
|
+
client: n.client,
|
|
218
|
+
critical: n.critical,
|
|
219
|
+
optional: n.optional
|
|
220
|
+
};
|
|
221
|
+
if (n.kind === "boolean") return l.boolean(r);
|
|
222
|
+
if (n.kind === "integer") return l.integer(r);
|
|
223
|
+
if (n.kind === "secret") return l.secret(r);
|
|
224
|
+
if (n.kind === "url") return l.url(r);
|
|
225
|
+
if (n.kind === "enum") {
|
|
226
|
+
const t = n.options.values;
|
|
227
|
+
if (!Array.isArray(t) || !t.length)
|
|
228
|
+
throw new Error(
|
|
229
|
+
`The environment variable ${e} does not have the values of its enum.`
|
|
230
|
+
);
|
|
231
|
+
return l.enum(t, r);
|
|
232
|
+
}
|
|
233
|
+
return l.string(r);
|
|
234
|
+
}
|
|
235
|
+
function V(e) {
|
|
236
|
+
return Object.values(e).every(
|
|
237
|
+
(n) => "parse" in n && typeof n.parse == "function"
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
function q() {
|
|
241
|
+
return globalThis.process?.env ?? {};
|
|
242
|
+
}
|
|
243
|
+
function x(e) {
|
|
244
|
+
globalThis.console?.warn?.(e);
|
|
245
|
+
}
|
|
246
|
+
export {
|
|
247
|
+
g as E,
|
|
248
|
+
y as a,
|
|
249
|
+
N as b,
|
|
250
|
+
T as c,
|
|
251
|
+
L as d,
|
|
252
|
+
l as e,
|
|
253
|
+
z as r,
|
|
254
|
+
D as s,
|
|
255
|
+
A as v
|
|
256
|
+
};
|
|
257
|
+
//# sourceMappingURL=env-startup-DOBYWlZD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-startup-DOBYWlZD.js","sources":["../../src/security/env.ts","../../src/security/env-startup.ts"],"sourcesContent":["export type EnvSource = Record<string, string | undefined>;\n\nexport type EnvValidationIssue = {\n code: \"invalid\" | \"missing\";\n key: string;\n message: string;\n};\n\nexport class EnvValidationError extends Error {\n issues: EnvValidationIssue[];\n\n constructor(issues: EnvValidationIssue[]) {\n super(formatEnvValidationError(issues));\n this.name = \"EnvValidationError\";\n this.issues = issues;\n }\n}\n\nexport type EnvVariableKind =\n | \"boolean\"\n | \"enum\"\n | \"integer\"\n | \"secret\"\n | \"string\"\n | \"url\";\n\n// The serializable description of one variable. The framework writes this\n// description into the generated server entry, so the schema of the\n// configuration file reaches the production process without the file.\nexport type EnvVariableDescriptor = {\n // A client variable reaches the browser bundle. The build refuses a client\n // bundle that reads a variable which this field does not mark.\n client: boolean;\n critical: boolean;\n kind: EnvVariableKind;\n optional: boolean;\n options: Record<string, unknown>;\n sensitive: boolean;\n};\n\nexport type EnvSchemaDescriptor = Record<string, EnvVariableDescriptor>;\n\nexport type EnvVariable<T, Optional extends boolean = false> =\n & Omit<EnvVariableDescriptor, \"optional\">\n & {\n optional: Optional;\n parse: (key: string, value: string) => T;\n };\n\nexport type EnvSchema = Record<string, EnvVariable<unknown, boolean>>;\n\nexport type InferEnvSchema<Schema extends EnvSchema> = {\n [Key in keyof Schema]: Schema[Key] extends EnvVariable<infer Value, infer Optional>\n ? Optional extends true\n ? Value | undefined\n : Value\n : never;\n};\n\ntype SharedEnvVariableOptions = {\n // A client variable reaches the browser bundle. The build inlines its value.\n // The default value is false, and the value then stays on the server.\n client?: boolean;\n // A critical variable stops the server start when it is absent or invalid.\n critical?: boolean;\n};\n\ntype RequiredEnvVariableOptions = SharedEnvVariableOptions & {\n optional?: false;\n};\n\ntype OptionalEnvVariableOptions = SharedEnvVariableOptions & {\n optional: true;\n};\n\ntype EnvVariableOptions = RequiredEnvVariableOptions | OptionalEnvVariableOptions;\n\ntype RequiredStringEnvOptions = RequiredEnvVariableOptions & {\n minLength?: number;\n};\n\ntype OptionalStringEnvOptions = OptionalEnvVariableOptions & {\n minLength?: number;\n};\n\ntype StringEnvOptions = RequiredStringEnvOptions | OptionalStringEnvOptions;\n\n// A secret variable never reaches the browser. The option is absent from the\n// type of the builder, and the builder also refuses it at run time.\ntype RequiredSecretEnvOptions = RequiredStringEnvOptions & { client?: never };\n\ntype OptionalSecretEnvOptions = OptionalStringEnvOptions & { client?: never };\n\ntype SecretEnvOptions = RequiredSecretEnvOptions | OptionalSecretEnvOptions;\n\ntype RequiredUrlEnvOptions = RequiredEnvVariableOptions & {\n protocols?: readonly string[];\n};\n\ntype OptionalUrlEnvOptions = OptionalEnvVariableOptions & {\n protocols?: readonly string[];\n};\n\ntype UrlEnvOptions = RequiredUrlEnvOptions | OptionalUrlEnvOptions;\n\ntype RequiredIntegerEnvOptions = RequiredEnvVariableOptions & {\n max?: number;\n min?: number;\n};\n\ntype OptionalIntegerEnvOptions = OptionalEnvVariableOptions & {\n max?: number;\n min?: number;\n};\n\ntype IntegerEnvOptions = RequiredIntegerEnvOptions | OptionalIntegerEnvOptions;\n\nexport function defineEnvSchema<Schema extends EnvSchema>(schema: Schema) {\n return schema;\n}\n\nexport function validateEnv<Schema extends EnvSchema>(\n schema: Schema,\n source: EnvSource,\n): InferEnvSchema<Schema> {\n const issues: EnvValidationIssue[] = [];\n const values: Partial<Record<keyof Schema, unknown>> = {};\n\n // TYPE-EVIDENCE: Object.keys returns the schema keys as strings. The cast narrows them to the schema key type because they come from the schema.\n for (const key of Object.keys(schema) as Array<keyof Schema>) {\n const variable = schema[key];\n const rawValue = source[String(key)];\n\n if (rawValue === undefined || rawValue === \"\") {\n if (variable.optional) {\n values[key] = undefined;\n continue;\n }\n\n issues.push({\n code: \"missing\",\n key: String(key),\n message: `Environment variable ${String(key)} is required.`,\n });\n continue;\n }\n\n try {\n values[key] = variable.parse(String(key), rawValue);\n } catch (error) {\n issues.push({\n code: \"invalid\",\n key: String(key),\n message: error instanceof Error\n ? error.message\n : `Environment variable ${String(key)} is invalid.`,\n });\n }\n }\n\n if (issues.length) {\n throw new EnvValidationError(issues);\n }\n\n // TYPE-EVIDENCE: the loop above populated values from the validated schema entries. The cast restores the inferred schema type.\n return values as InferEnvSchema<Schema>;\n}\n\nfunction booleanEnv(options: OptionalEnvVariableOptions): EnvVariable<boolean, true>;\nfunction booleanEnv(options?: RequiredEnvVariableOptions): EnvVariable<boolean, false>;\nfunction booleanEnv(options: EnvVariableOptions = {}) {\n return createVariable(options, false, (key, value) => {\n if (value === \"true\" || value === \"1\") {\n return true;\n }\n\n if (value === \"false\" || value === \"0\") {\n return false;\n }\n\n throw new Error(`Environment variable ${key} must be true, false, 1, or 0.`);\n }, \"boolean\");\n}\n\nfunction enumEnv<const Values extends readonly [string, ...string[]]>(\n values: Values,\n options: OptionalEnvVariableOptions,\n): EnvVariable<Values[number], true>;\nfunction enumEnv<const Values extends readonly [string, ...string[]]>(\n values: Values,\n options?: RequiredEnvVariableOptions,\n): EnvVariable<Values[number], false>;\nfunction enumEnv<const Values extends readonly [string, ...string[]]>(\n values: Values,\n options: EnvVariableOptions = {},\n) {\n return createVariable(options, false, (key, value) => {\n if (values.includes(value)) {\n return value;\n }\n\n throw new Error(\n `Environment variable ${key} must be one of: ${values.join(\", \")}.`,\n );\n }, \"enum\", { values: [...values] });\n}\n\nfunction integerEnv(options: OptionalIntegerEnvOptions): EnvVariable<number, true>;\nfunction integerEnv(options?: RequiredIntegerEnvOptions): EnvVariable<number, false>;\nfunction integerEnv(options: IntegerEnvOptions = {}) {\n return createVariable(options, false, (key, value) => {\n if (!/^-?\\d+$/.test(value)) {\n throw new Error(`Environment variable ${key} must be an integer.`);\n }\n\n const parsed = Number(value);\n\n if (!Number.isSafeInteger(parsed)) {\n throw new Error(`Environment variable ${key} must be a safe integer.`);\n }\n\n if (options.min !== undefined && parsed < options.min) {\n throw new Error(\n `Environment variable ${key} must be greater than or equal to ${options.min}.`,\n );\n }\n\n if (options.max !== undefined && parsed > options.max) {\n throw new Error(\n `Environment variable ${key} must be less than or equal to ${options.max}.`,\n );\n }\n\n return parsed;\n }, \"integer\");\n}\n\nfunction secretEnv(options: OptionalSecretEnvOptions): EnvVariable<string, true>;\nfunction secretEnv(options?: RequiredSecretEnvOptions): EnvVariable<string, false>;\nfunction secretEnv(options: SecretEnvOptions = {}) {\n return createVariable(\n options,\n true,\n (key, value) => parseString(key, value, options),\n \"secret\",\n );\n}\n\nfunction stringEnv(options: OptionalStringEnvOptions): EnvVariable<string, true>;\nfunction stringEnv(options?: RequiredStringEnvOptions): EnvVariable<string, false>;\nfunction stringEnv(options: StringEnvOptions = {}) {\n return createVariable(\n options,\n false,\n (key, value) => parseString(key, value, options),\n \"string\",\n );\n}\n\nfunction urlEnv(options: OptionalUrlEnvOptions): EnvVariable<URL, true>;\nfunction urlEnv(options?: RequiredUrlEnvOptions): EnvVariable<URL, false>;\nfunction urlEnv(options: UrlEnvOptions = {}) {\n return createVariable(options, false, (key, value) => {\n let url: URL;\n\n try {\n url = new URL(value);\n } catch {\n throw new Error(`Environment variable ${key} must be a valid URL.`);\n }\n\n if (\n options.protocols?.length &&\n !options.protocols.includes(url.protocol)\n ) {\n throw new Error(\n `Environment variable ${key} must use one of these protocols: ${options.protocols.join(\", \")}.`,\n );\n }\n\n return url;\n }, \"url\");\n}\n\nexport const env = {\n boolean: booleanEnv,\n enum: enumEnv,\n integer: integerEnv,\n secret: secretEnv,\n string: stringEnv,\n url: urlEnv,\n};\n\nfunction createVariable<T>(\n options: EnvVariableOptions,\n sensitive: boolean,\n parse: (key: string, value: string) => T,\n kind: EnvVariableKind = \"string\",\n extra: Record<string, unknown> = {},\n): EnvVariable<T, boolean> {\n // TYPE-EVIDENCE: each builder passes its own declared options and its own extra values. The cast reads the shared options and keeps the rest as the serializable description.\n const merged = { ...options, ...extra } as\n & Record<string, unknown>\n & EnvVariableOptions\n & { client?: boolean };\n const { client = false, critical = false, optional = false, ...rest } = merged;\n\n if (client && sensitive) {\n throw new Error(\n \"A secret environment variable cannot reach client code. Declare a separate variable for the browser.\",\n );\n }\n\n return {\n client,\n critical,\n kind,\n optional,\n options: rest,\n parse,\n sensitive,\n };\n}\n\nfunction parseString(\n key: string,\n value: string,\n options: StringEnvOptions,\n) {\n if (options.minLength !== undefined && value.length < options.minLength) {\n throw new Error(\n `Environment variable ${key} must be at least ${options.minLength} characters.`,\n );\n }\n\n return value;\n}\n\nfunction formatEnvValidationError(issues: EnvValidationIssue[]) {\n if (issues.length === 1) {\n return issues[0].message;\n }\n\n return `Environment validation failed with ${issues.length} issues.`;\n}\n","import {\n env,\n EnvValidationError,\n type EnvSchema,\n type EnvSchemaDescriptor,\n type EnvSource,\n type EnvValidationIssue,\n type EnvVariable,\n type EnvVariableDescriptor,\n type InferEnvSchema,\n} from \"./env\";\n\nexport type EnvStartupOptions = {\n source?: EnvSource;\n warn?: (message: string) => void;\n};\n\nexport type EnvStartupResult = {\n values: Record<string, unknown>;\n warnings: string[];\n};\n\nconst initialized = new Map<string, unknown>();\n\n// The configuration file declares the schema. The build writes this\n// description into the generated server entry, because the production process\n// does not read the configuration file.\nexport function serializeEnvSchema(schema: EnvSchema): EnvSchemaDescriptor {\n return Object.fromEntries(\n Object.entries(schema).map(([key, variable]) => [key, {\n client: variable.client,\n critical: variable.critical,\n kind: variable.kind,\n optional: variable.optional,\n options: variable.options,\n sensitive: variable.sensitive,\n }]),\n );\n}\n\nexport function deserializeEnvSchema(\n descriptor: EnvSchemaDescriptor,\n): EnvSchema {\n return Object.fromEntries(\n Object.entries(descriptor).map(([key, variable]) => [\n key,\n createVariableFromDescriptor(key, variable),\n ]),\n );\n}\n\n// The server entry calls this function while the module graph loads. A\n// critical failure therefore stops the process before it accepts traffic.\nexport function startEnvironment(\n schema: EnvSchema | EnvSchemaDescriptor,\n options: EnvStartupOptions = {},\n): EnvStartupResult {\n const resolved = isEnvSchema(schema) ? schema : deserializeEnvSchema(schema);\n const source = options.source ?? readProcessEnvironment();\n const critical: EnvValidationIssue[] = [];\n const warnings: string[] = [];\n const values: Record<string, unknown> = {};\n\n for (const [key, variable] of Object.entries(resolved)) {\n const rawValue = source[key];\n\n if (rawValue === undefined || rawValue === \"\") {\n values[key] = undefined;\n if (variable.optional) continue;\n record(variable, critical, warnings, {\n code: \"missing\",\n key,\n message: `Environment variable ${key} is required.`,\n });\n continue;\n }\n\n try {\n values[key] = variable.parse(key, rawValue);\n } catch (error) {\n values[key] = undefined;\n record(variable, critical, warnings, {\n code: \"invalid\",\n key,\n message: error instanceof Error\n ? error.message\n : `Environment variable ${key} is invalid.`,\n });\n }\n }\n\n if (critical.length) throw new EnvValidationError(critical);\n\n const warn = options.warn ?? defaultWarn;\n for (const warning of warnings) warn(warning);\n for (const [key, value] of Object.entries(values)) initialized.set(key, value);\n\n return { values, warnings };\n}\n\n// Applications read the validated values with the types that the schema\n// declares. This function is server-only.\nexport function readEnv<Schema extends EnvSchema>(\n schema: Schema,\n): InferEnvSchema<Schema> {\n const values: Record<string, unknown> = {};\n\n for (const key of Object.keys(schema)) {\n if (!initialized.has(key)) {\n throw new Error(\n `Demiurge did not validate the environment variable ${key}. The framework validates the schema of demiurge.config.ts when the server starts.`,\n );\n }\n values[key] = initialized.get(key);\n }\n\n // TYPE-EVIDENCE: startEnvironment parsed each value with the same schema. The cast restores the inferred schema type.\n return values as InferEnvSchema<Schema>;\n}\n\nexport function resetEnvironment() {\n initialized.clear();\n}\n\nfunction record(\n variable: { critical: boolean },\n critical: EnvValidationIssue[],\n warnings: string[],\n issue: EnvValidationIssue,\n) {\n if (variable.critical) {\n critical.push(issue);\n return;\n }\n warnings.push(\n `Demiurge started without a valid value for ${issue.key}. ${issue.message} A request that needs this value fails.`,\n );\n}\n\nfunction createVariableFromDescriptor(\n key: string,\n descriptor: EnvVariableDescriptor,\n): EnvVariable<unknown, boolean> {\n // TYPE-EVIDENCE: serializeEnvSchema wrote these options from a builder call. The cast returns them to the overloaded builder parameter.\n const options = {\n ...descriptor.options,\n client: descriptor.client,\n critical: descriptor.critical,\n optional: descriptor.optional,\n } as never;\n\n if (descriptor.kind === \"boolean\") return env.boolean(options);\n if (descriptor.kind === \"integer\") return env.integer(options);\n if (descriptor.kind === \"secret\") return env.secret(options);\n if (descriptor.kind === \"url\") return env.url(options);\n if (descriptor.kind === \"enum\") {\n const values = descriptor.options.values;\n if (!Array.isArray(values) || !values.length) {\n throw new Error(\n `The environment variable ${key} does not have the values of its enum.`,\n );\n }\n // TYPE-EVIDENCE: the descriptor keeps the declared enum values. The cast restores the tuple type that the builder needs.\n return env.enum(values as [string, ...string[]], options);\n }\n return env.string(options);\n}\n\nfunction isEnvSchema(\n schema: EnvSchema | EnvSchemaDescriptor,\n): schema is EnvSchema {\n return Object.values(schema).every(\n (variable) => \"parse\" in variable && typeof variable.parse === \"function\",\n );\n}\n\nfunction readProcessEnvironment(): EnvSource {\n // TYPE-EVIDENCE: the framework runs this function on a server runtime. The cast reads the process environment without a Node type dependency here.\n const runtime = globalThis as { process?: { env?: EnvSource } };\n return runtime.process?.env ?? {};\n}\n\nfunction defaultWarn(message: string) {\n // TYPE-EVIDENCE: a server runtime supplies console. The cast keeps the warning optional.\n const runtime = globalThis as {\n console?: { warn?: (message: string) => void };\n };\n runtime.console?.warn?.(message);\n}\n"],"names":["EnvValidationError","issues","formatEnvValidationError","defineEnvSchema","schema","validateEnv","source","values","key","variable","rawValue","error","booleanEnv","options","createVariable","value","enumEnv","integerEnv","parsed","secretEnv","parseString","stringEnv","urlEnv","url","env","sensitive","parse","kind","extra","merged","client","critical","optional","rest","initialized","serializeEnvSchema","deserializeEnvSchema","descriptor","createVariableFromDescriptor","startEnvironment","resolved","isEnvSchema","readProcessEnvironment","warnings","record","warn","defaultWarn","warning","readEnv","resetEnvironment","issue","message"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash as d } from "node:crypto";
|
|
2
2
|
import { readFile as h, mkdir as p, writeFile as F } from "node:fs/promises";
|
|
3
3
|
import { resolve as u, join as w, relative as b, sep as $ } from "node:path";
|
|
4
|
-
import { d as v, f as S, a as N, r as E, b as J, c as O, i as j } from "./images-
|
|
4
|
+
import { d as v, f as S, a as N, r as E, b as J, c as O, i as j } from "./images-BQ80950B.js";
|
|
5
5
|
const y = 5 * 1024 * 1024;
|
|
6
6
|
async function H(e) {
|
|
7
7
|
const t = e.fonts ?? [];
|
|
@@ -138,4 +138,4 @@ export {
|
|
|
138
138
|
H as r,
|
|
139
139
|
q as t
|
|
140
140
|
};
|
|
141
|
-
//# sourceMappingURL=image-codec-
|
|
141
|
+
//# sourceMappingURL=image-codec-BmzWnH6W.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image-codec-Di53WpcH.js","sources":["../../src/platform/font-assets.ts","../../src/platform/image-codec.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { join, relative, resolve, sep } from \"node:path\";\nimport {\n defaultFontPath,\n fontAssetFileName,\n fontFormat,\n fontMediaType,\n fontStylesheetUrl,\n renderFontFaceCss,\n type FontContribution,\n type FontDefinition,\n} from \"./fonts\";\n\nexport type FontAssetFetch = (\n input: string,\n init?: { signal?: AbortSignal },\n) => Promise<Response>;\n\nexport type FontAssetOptions = {\n basePath?: string;\n // Where a downloaded font file stays between builds. A build that already\n // downloaded a font reads it back and makes no network request.\n cacheDir?: string;\n fetch?: FontAssetFetch;\n fonts?: FontContribution;\n // The project directory that every local font source resolves against.\n root: string;\n};\n\nexport type FontAsset = {\n body: Uint8Array;\n contentType: string;\n // The output path, relative to the directory that the build publishes.\n file: string;\n url: string;\n};\n\nconst maximumFontBytes = 5 * 1024 * 1024;\n\n// One font declaration becomes one published file plus one stylesheet. The\n// build writes these files, and the Node handler serves the same set from\n// memory. Both derive every URL from the declaration, so they cannot drift.\nexport async function resolveFontAssets(\n options: FontAssetOptions,\n): Promise<FontAsset[]> {\n const fonts = options.fonts ?? [];\n\n if (fonts.length === 0) {\n return [];\n }\n\n const basePath = options.basePath ?? defaultFontPath;\n const root = resolve(options.root);\n const cacheDir = resolve(\n options.cacheDir ?? join(root, \"node_modules\", \".demiurge\", \"fonts\"),\n );\n const load = options.fetch ?? ((input, init) => fetch(input, init));\n const published = new Map<string, string>();\n const assets: FontAsset[] = [];\n\n for (const definition of fonts) {\n if (!definition.selfHost) {\n continue;\n }\n\n const name = fontAssetFileName(definition);\n const claimed = published.get(name);\n\n if (claimed !== undefined) {\n if (claimed !== definition.src) {\n throw new Error(\n `Font family ${JSON.stringify(definition.family)} declares two sources that publish the same file ${JSON.stringify(name)}. Give one of them a different weight or style.`,\n );\n }\n\n continue;\n }\n\n published.set(name, definition.src);\n assets.push({\n body: await readFontBody(definition, { cacheDir, load, root }),\n contentType: fontMediaType(definition.src),\n file: outputFile(basePath, name),\n url: `${basePath}/${name}`,\n });\n }\n\n const stylesheet = `${renderFontFaceCss(fonts, basePath)}\\n`;\n\n assets.push({\n body: new TextEncoder().encode(stylesheet),\n contentType: \"text/css; charset=utf-8\",\n file: outputFile(basePath, \"fonts.css\"),\n url: fontStylesheetUrl(basePath),\n });\n\n return assets;\n}\n\nasync function readFontBody(\n definition: FontDefinition,\n context: { cacheDir: string; load: FontAssetFetch; root: string },\n) {\n const body = definition.source === \"google\"\n ? await downloadFontFile(definition, context)\n : await readLocalFontFile(definition, context.root);\n\n if (body.byteLength === 0) {\n throw new Error(\n `Font family ${JSON.stringify(definition.family)} resolved to an empty file.`,\n );\n }\n\n if (body.byteLength > maximumFontBytes) {\n throw new Error(\n `Font family ${JSON.stringify(definition.family)} is larger than the ${maximumFontBytes} byte limit.`,\n );\n }\n\n return body;\n}\n\nasync function readLocalFontFile(definition: FontDefinition, root: string) {\n const file = resolve(root, definition.src.replace(/^\\/+/, \"\"));\n\n if (relative(root, file).split(sep).includes(\"..\")) {\n throw new Error(\n `Font source escaped the project directory: ${JSON.stringify(definition.src)}.`,\n );\n }\n\n try {\n return new Uint8Array(await readFile(file));\n } catch (error) {\n throw new Error(\n `Demiurge could not read the font file ${JSON.stringify(definition.src)} for family ${JSON.stringify(definition.family)}.`,\n { cause: error },\n );\n }\n}\n\n// A downloaded font stays in the cache directory. The first build reaches the\n// font host once, and every later build reads the file from disk. A build that\n// runs without network access therefore keeps working.\nasync function downloadFontFile(\n definition: FontDefinition,\n context: { cacheDir: string; load: FontAssetFetch; root: string },\n) {\n const digest = createHash(\"sha256\").update(definition.src).digest(\"hex\");\n const cacheFile = join(\n context.cacheDir,\n `${digest.slice(0, 32)}.${fontFormat(definition.src)}`,\n );\n\n try {\n return new Uint8Array(await readFile(cacheFile));\n } catch {\n // The cache holds no copy yet, so the font host answers this once.\n }\n\n let response: Response;\n\n try {\n response = await context.load(definition.src);\n } catch (error) {\n throw new Error(\n `Demiurge could not download the font for family ${JSON.stringify(definition.family)} from ${JSON.stringify(definition.src)}.`,\n { cause: error },\n );\n }\n\n if (!response.ok) {\n throw new Error(\n `The font host answered ${response.status} for family ${JSON.stringify(definition.family)} at ${JSON.stringify(definition.src)}.`,\n );\n }\n\n const body = new Uint8Array(await response.arrayBuffer());\n\n await mkdir(context.cacheDir, { recursive: true });\n await writeFile(cacheFile, body);\n\n return body;\n}\n\nfunction outputFile(basePath: string, name: string) {\n return `${basePath.replace(/^\\/+/, \"\")}/${name}`;\n}\n","import type { ImageFormat } from \"./images\";\nimport { imageSourceExtension, type ImageVariantDescriptor } from \"./image-url\";\n\nexport type EncodedImage = {\n body: Uint8Array;\n contentType: string;\n};\n\ntype SharpModule = {\n default: (input: Uint8Array) => SharpPipeline;\n};\n\ntype SharpPipeline = {\n avif: (options: { quality: number }) => SharpPipeline;\n jpeg: (options: { quality: number }) => SharpPipeline;\n png: () => SharpPipeline;\n resize: (options: {\n width: number;\n withoutEnlargement: boolean;\n }) => SharpPipeline;\n toBuffer: () => Promise<Buffer>;\n webp: (options: { quality: number }) => SharpPipeline;\n};\n\nconst contentTypes: Record<Exclude<ImageFormat, \"auto\">, string> = {\n avif: \"image/avif\",\n jpeg: \"image/jpeg\",\n png: \"image/png\",\n webp: \"image/webp\",\n};\n\nconst defaultQuality = 75;\n\nexport function imageContentType(format: Exclude<ImageFormat, \"auto\">) {\n return contentTypes[format];\n}\n\n// `auto` lets the browser take the smallest format that it accepts. A client\n// that sends no usable `accept` header keeps the source format.\nexport function negotiateImageFormat(\n descriptor: ImageVariantDescriptor,\n accept: string | null,\n): Exclude<ImageFormat, \"auto\"> {\n if (descriptor.format !== \"auto\") {\n return descriptor.format;\n }\n\n const accepted = (accept ?? \"\").toLowerCase();\n\n if (accepted.includes(\"image/avif\")) {\n return \"avif\";\n }\n\n if (accepted.includes(\"image/webp\")) {\n return \"webp\";\n }\n\n return sourceFormat(descriptor.src);\n}\n\nexport function sourceFormat(src: string): Exclude<ImageFormat, \"auto\"> {\n const extension = imageSourceExtension(src);\n\n if (extension === \"avif\") {\n return \"avif\";\n }\n\n if (extension === \"webp\") {\n return \"webp\";\n }\n\n if (extension === \"png\") {\n return \"png\";\n }\n\n return \"jpeg\";\n}\n\nexport async function transformImage(\n source: Uint8Array,\n options: {\n format: Exclude<ImageFormat, \"auto\">;\n quality?: number;\n width: number;\n },\n): Promise<EncodedImage> {\n const sharp = await loadImageCodec();\n const quality = options.quality ?? defaultQuality;\n const resized = sharp(source).resize({\n width: options.width,\n withoutEnlargement: true,\n });\n\n return {\n body: new Uint8Array(await encode(resized, options.format, quality)),\n contentType: contentTypes[options.format],\n };\n}\n\nfunction encode(\n pipeline: SharpPipeline,\n format: Exclude<ImageFormat, \"auto\">,\n quality: number,\n) {\n if (format === \"png\") {\n return pipeline.png().toBuffer();\n }\n\n if (format === \"jpeg\") {\n return pipeline.jpeg({ quality }).toBuffer();\n }\n\n if (format === \"avif\") {\n return pipeline.avif({ quality }).toBuffer();\n }\n\n return pipeline.webp({ quality }).toBuffer();\n}\n\n// `sharp` is an optional peer dependency. Only an application that optimizes\n// an image installs it, the same rule that ADR 0006 states for a host\n// adapter dependency.\nasync function loadImageCodec() {\n let module: SharpModule;\n\n try {\n // TYPE-EVIDENCE: sharp ships no types. The local type describes the methods the codec calls.\n module = (await import(\"sharp\")) as SharpModule;\n } catch (error) {\n throw new Error(\n \"Demiurge image optimization requires the optional peer dependency sharp. Install sharp in the application.\",\n { cause: error },\n );\n }\n\n return module.default;\n}\n"],"names":["maximumFontBytes","resolveFontAssets","options","fonts","basePath","defaultFontPath","root","resolve","cacheDir","join","load","input","init","published","assets","definition","name","fontAssetFileName","claimed","readFontBody","fontMediaType","outputFile","stylesheet","renderFontFaceCss","fontStylesheetUrl","context","body","downloadFontFile","readLocalFontFile","file","relative","sep","readFile","error","digest","createHash","cacheFile","fontFormat","response","mkdir","writeFile","contentTypes","defaultQuality","negotiateImageFormat","descriptor","accept","accepted","sourceFormat","src","extension","imageSourceExtension","transformImage","source","sharp","loadImageCodec","quality","resized","encode","pipeline","format","module"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"image-codec-BmzWnH6W.js","sources":["../../src/platform/font-assets.ts","../../src/platform/image-codec.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { join, relative, resolve, sep } from \"node:path\";\nimport {\n defaultFontPath,\n fontAssetFileName,\n fontFormat,\n fontMediaType,\n fontStylesheetUrl,\n renderFontFaceCss,\n type FontContribution,\n type FontDefinition,\n} from \"./fonts\";\n\nexport type FontAssetFetch = (\n input: string,\n init?: { signal?: AbortSignal },\n) => Promise<Response>;\n\nexport type FontAssetOptions = {\n basePath?: string;\n // Where a downloaded font file stays between builds. A build that already\n // downloaded a font reads it back and makes no network request.\n cacheDir?: string;\n fetch?: FontAssetFetch;\n fonts?: FontContribution;\n // The project directory that every local font source resolves against.\n root: string;\n};\n\nexport type FontAsset = {\n body: Uint8Array;\n contentType: string;\n // The output path, relative to the directory that the build publishes.\n file: string;\n url: string;\n};\n\nconst maximumFontBytes = 5 * 1024 * 1024;\n\n// One font declaration becomes one published file plus one stylesheet. The\n// build writes these files, and the Node handler serves the same set from\n// memory. Both derive every URL from the declaration, so they cannot drift.\nexport async function resolveFontAssets(\n options: FontAssetOptions,\n): Promise<FontAsset[]> {\n const fonts = options.fonts ?? [];\n\n if (fonts.length === 0) {\n return [];\n }\n\n const basePath = options.basePath ?? defaultFontPath;\n const root = resolve(options.root);\n const cacheDir = resolve(\n options.cacheDir ?? join(root, \"node_modules\", \".demiurge\", \"fonts\"),\n );\n const load = options.fetch ?? ((input, init) => fetch(input, init));\n const published = new Map<string, string>();\n const assets: FontAsset[] = [];\n\n for (const definition of fonts) {\n if (!definition.selfHost) {\n continue;\n }\n\n const name = fontAssetFileName(definition);\n const claimed = published.get(name);\n\n if (claimed !== undefined) {\n if (claimed !== definition.src) {\n throw new Error(\n `Font family ${JSON.stringify(definition.family)} declares two sources that publish the same file ${JSON.stringify(name)}. Give one of them a different weight or style.`,\n );\n }\n\n continue;\n }\n\n published.set(name, definition.src);\n assets.push({\n body: await readFontBody(definition, { cacheDir, load, root }),\n contentType: fontMediaType(definition.src),\n file: outputFile(basePath, name),\n url: `${basePath}/${name}`,\n });\n }\n\n const stylesheet = `${renderFontFaceCss(fonts, basePath)}\\n`;\n\n assets.push({\n body: new TextEncoder().encode(stylesheet),\n contentType: \"text/css; charset=utf-8\",\n file: outputFile(basePath, \"fonts.css\"),\n url: fontStylesheetUrl(basePath),\n });\n\n return assets;\n}\n\nasync function readFontBody(\n definition: FontDefinition,\n context: { cacheDir: string; load: FontAssetFetch; root: string },\n) {\n const body = definition.source === \"google\"\n ? await downloadFontFile(definition, context)\n : await readLocalFontFile(definition, context.root);\n\n if (body.byteLength === 0) {\n throw new Error(\n `Font family ${JSON.stringify(definition.family)} resolved to an empty file.`,\n );\n }\n\n if (body.byteLength > maximumFontBytes) {\n throw new Error(\n `Font family ${JSON.stringify(definition.family)} is larger than the ${maximumFontBytes} byte limit.`,\n );\n }\n\n return body;\n}\n\nasync function readLocalFontFile(definition: FontDefinition, root: string) {\n const file = resolve(root, definition.src.replace(/^\\/+/, \"\"));\n\n if (relative(root, file).split(sep).includes(\"..\")) {\n throw new Error(\n `Font source escaped the project directory: ${JSON.stringify(definition.src)}.`,\n );\n }\n\n try {\n return new Uint8Array(await readFile(file));\n } catch (error) {\n throw new Error(\n `Demiurge could not read the font file ${JSON.stringify(definition.src)} for family ${JSON.stringify(definition.family)}.`,\n { cause: error },\n );\n }\n}\n\n// A downloaded font stays in the cache directory. The first build reaches the\n// font host once, and every later build reads the file from disk. A build that\n// runs without network access therefore keeps working.\nasync function downloadFontFile(\n definition: FontDefinition,\n context: { cacheDir: string; load: FontAssetFetch; root: string },\n) {\n const digest = createHash(\"sha256\").update(definition.src).digest(\"hex\");\n const cacheFile = join(\n context.cacheDir,\n `${digest.slice(0, 32)}.${fontFormat(definition.src)}`,\n );\n\n try {\n return new Uint8Array(await readFile(cacheFile));\n } catch {\n // The cache holds no copy yet, so the font host answers this once.\n }\n\n let response: Response;\n\n try {\n response = await context.load(definition.src);\n } catch (error) {\n throw new Error(\n `Demiurge could not download the font for family ${JSON.stringify(definition.family)} from ${JSON.stringify(definition.src)}.`,\n { cause: error },\n );\n }\n\n if (!response.ok) {\n throw new Error(\n `The font host answered ${response.status} for family ${JSON.stringify(definition.family)} at ${JSON.stringify(definition.src)}.`,\n );\n }\n\n const body = new Uint8Array(await response.arrayBuffer());\n\n await mkdir(context.cacheDir, { recursive: true });\n await writeFile(cacheFile, body);\n\n return body;\n}\n\nfunction outputFile(basePath: string, name: string) {\n return `${basePath.replace(/^\\/+/, \"\")}/${name}`;\n}\n","import type { ImageFormat } from \"./images\";\nimport { imageSourceExtension, type ImageVariantDescriptor } from \"./image-url\";\n\nexport type EncodedImage = {\n body: Uint8Array;\n contentType: string;\n};\n\ntype SharpModule = {\n default: (input: Uint8Array) => SharpPipeline;\n};\n\ntype SharpPipeline = {\n avif: (options: { quality: number }) => SharpPipeline;\n jpeg: (options: { quality: number }) => SharpPipeline;\n png: () => SharpPipeline;\n resize: (options: {\n width: number;\n withoutEnlargement: boolean;\n }) => SharpPipeline;\n toBuffer: () => Promise<Buffer>;\n webp: (options: { quality: number }) => SharpPipeline;\n};\n\nconst contentTypes: Record<Exclude<ImageFormat, \"auto\">, string> = {\n avif: \"image/avif\",\n jpeg: \"image/jpeg\",\n png: \"image/png\",\n webp: \"image/webp\",\n};\n\nconst defaultQuality = 75;\n\nexport function imageContentType(format: Exclude<ImageFormat, \"auto\">) {\n return contentTypes[format];\n}\n\n// `auto` lets the browser take the smallest format that it accepts. A client\n// that sends no usable `accept` header keeps the source format.\nexport function negotiateImageFormat(\n descriptor: ImageVariantDescriptor,\n accept: string | null,\n): Exclude<ImageFormat, \"auto\"> {\n if (descriptor.format !== \"auto\") {\n return descriptor.format;\n }\n\n const accepted = (accept ?? \"\").toLowerCase();\n\n if (accepted.includes(\"image/avif\")) {\n return \"avif\";\n }\n\n if (accepted.includes(\"image/webp\")) {\n return \"webp\";\n }\n\n return sourceFormat(descriptor.src);\n}\n\nexport function sourceFormat(src: string): Exclude<ImageFormat, \"auto\"> {\n const extension = imageSourceExtension(src);\n\n if (extension === \"avif\") {\n return \"avif\";\n }\n\n if (extension === \"webp\") {\n return \"webp\";\n }\n\n if (extension === \"png\") {\n return \"png\";\n }\n\n return \"jpeg\";\n}\n\nexport async function transformImage(\n source: Uint8Array,\n options: {\n format: Exclude<ImageFormat, \"auto\">;\n quality?: number;\n width: number;\n },\n): Promise<EncodedImage> {\n const sharp = await loadImageCodec();\n const quality = options.quality ?? defaultQuality;\n const resized = sharp(source).resize({\n width: options.width,\n withoutEnlargement: true,\n });\n\n return {\n body: new Uint8Array(await encode(resized, options.format, quality)),\n contentType: contentTypes[options.format],\n };\n}\n\nfunction encode(\n pipeline: SharpPipeline,\n format: Exclude<ImageFormat, \"auto\">,\n quality: number,\n) {\n if (format === \"png\") {\n return pipeline.png().toBuffer();\n }\n\n if (format === \"jpeg\") {\n return pipeline.jpeg({ quality }).toBuffer();\n }\n\n if (format === \"avif\") {\n return pipeline.avif({ quality }).toBuffer();\n }\n\n return pipeline.webp({ quality }).toBuffer();\n}\n\n// `sharp` is an optional peer dependency. Only an application that optimizes\n// an image installs it, the same rule that ADR 0006 states for a host\n// adapter dependency.\nasync function loadImageCodec() {\n let module: SharpModule;\n\n try {\n // TYPE-EVIDENCE: sharp ships no types. The local type describes the methods the codec calls.\n module = (await import(\"sharp\")) as SharpModule;\n } catch (error) {\n throw new Error(\n \"Demiurge image optimization requires the optional peer dependency sharp. Install sharp in the application.\",\n { cause: error },\n );\n }\n\n return module.default;\n}\n"],"names":["maximumFontBytes","resolveFontAssets","options","fonts","basePath","defaultFontPath","root","resolve","cacheDir","join","load","input","init","published","assets","definition","name","fontAssetFileName","claimed","readFontBody","fontMediaType","outputFile","stylesheet","renderFontFaceCss","fontStylesheetUrl","context","body","downloadFontFile","readLocalFontFile","file","relative","sep","readFile","error","digest","createHash","cacheFile","fontFormat","response","mkdir","writeFile","contentTypes","defaultQuality","negotiateImageFormat","descriptor","accept","accepted","sourceFormat","src","extension","imageSourceExtension","transformImage","source","sharp","loadImageCodec","quality","resized","encode","pipeline","format","module"],"mappings":"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"}
|