@demiurgejs/core 0.2.0-beta.2 → 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/adapter/testing.js.map +1 -1
- 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-BGTx_t5K.js → image-codec-BmzWnH6W.js} +2 -2
- package/dist/chunks/{image-codec-BGTx_t5K.js.map → image-codec-BmzWnH6W.js.map} +1 -1
- package/dist/chunks/{images-B9MqcjSA.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-BQ1bykDo.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 +10 -0
- package/dist/data/cache.d.ts.map +1 -1
- package/dist/data/idempotency.d.ts.map +1 -1
- package/dist/data/index.d.ts +3 -1
- package/dist/data/index.d.ts.map +1 -1
- package/dist/data/testing.d.ts.map +1 -1
- package/dist/data/testing.js +94 -72
- package/dist/data/testing.js.map +1 -1
- package/dist/data/tiered.d.ts +7 -0
- package/dist/data/tiered.d.ts.map +1 -0
- 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/deferred-scripts.d.ts.map +1 -1
- package/dist/document/hydration.d.ts +9 -0
- package/dist/document/hydration.d.ts.map +1 -1
- package/dist/document/index.d.ts +5 -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/document/trusted-types.d.ts +10 -0
- package/dist/document/trusted-types.d.ts.map +1 -0
- 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 +15 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2078 -573
- package/dist/index.js.map +1 -1
- package/dist/internal/testing.js +11 -11
- package/dist/kv/index.d.ts +4 -0
- package/dist/kv/index.d.ts.map +1 -1
- package/dist/kv/index.js +180 -60
- package/dist/kv/index.js.map +1 -1
- package/dist/kv/rate-limit-store.d.ts +8 -0
- package/dist/kv/rate-limit-store.d.ts.map +1 -0
- package/dist/kv/session-store.d.ts +20 -0
- package/dist/kv/session-store.d.ts.map +1 -0
- package/dist/kv/store.d.ts.map +1 -1
- package/dist/manifest.d.ts +10 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/node/http.d.ts.map +1 -1
- 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/node/streaming.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/images.d.ts.map +1 -1
- package/dist/platform/index.d.ts +5 -1
- package/dist/platform/index.d.ts.map +1 -1
- package/dist/platform/observability.d.ts +2 -1
- package/dist/platform/observability.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-client.d.ts +32 -0
- package/dist/platform/web-vitals-client.d.ts.map +1 -0
- package/dist/platform/web-vitals.d.ts +78 -0
- package/dist/platform/web-vitals.d.ts.map +1 -0
- package/dist/redis/index.d.ts +4 -0
- package/dist/redis/index.d.ts.map +1 -1
- package/dist/redis/index.js +209 -54
- package/dist/redis/index.js.map +1 -1
- package/dist/redis/rate-limit-store.d.ts +8 -0
- package/dist/redis/rate-limit-store.d.ts.map +1 -0
- package/dist/redis/session-store.d.ts +9 -0
- package/dist/redis/session-store.d.ts.map +1 -0
- package/dist/redis/store.d.ts.map +1 -1
- package/dist/route/index.d.ts +2 -2
- package/dist/route/index.d.ts.map +1 -1
- package/dist/route/middleware.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/response.d.ts.map +1 -1
- package/dist/route/types.d.ts +6 -1
- package/dist/route/types.d.ts.map +1 -1
- package/dist/router/index.d.ts +1 -1
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/manifest.d.ts +9 -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/cookies.d.ts +26 -0
- package/dist/security/cookies.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/fetch-metadata.d.ts +29 -0
- package/dist/security/fetch-metadata.d.ts.map +1 -0
- package/dist/security/index.d.ts +14 -2
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/policy.d.ts.map +1 -1
- package/dist/security/rate-limit.d.ts +6 -3
- package/dist/security/rate-limit.d.ts.map +1 -1
- package/dist/security/report.d.ts.map +1 -1
- package/dist/security/request.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 +73 -3
- 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 +2 -1
- 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 +10 -2
- 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 +8 -3
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +22 -1196
- package/dist/vite/index.js.map +1 -1
- package/dist/vite/plugin.d.ts +8 -1
- package/dist/vite/plugin.d.ts.map +1 -1
- package/dist/vite/policy-verification.d.ts.map +1 -1
- package/dist/vite/route-audit.d.ts +50 -0
- package/dist/vite/route-audit.d.ts.map +1 -0
- package/package.json +19 -6
- package/dist/chunks/document-shell-D1xo6cEA.js +0 -35
- package/dist/chunks/document-shell-D1xo6cEA.js.map +0 -1
- package/dist/chunks/images-B9MqcjSA.js.map +0 -1
- package/dist/chunks/request-metadata-D5NrT8ks.js +0 -642
- package/dist/chunks/request-metadata-D5NrT8ks.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-BQ1bykDo.js.map +0 -1
- package/dist/chunks/testing-B_iMFYz_.js +0 -3321
- package/dist/chunks/testing-B_iMFYz_.js.map +0 -1
- package/dist/route/action.d.ts +0 -33
- package/dist/route/action.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Node process. Adapter boundaries support other deployment configurations.
|
|
|
8
8
|
|
|
9
9
|
- Node.js 22.13 or newer
|
|
10
10
|
- React and React DOM 19
|
|
11
|
-
- Vite 6 when
|
|
11
|
+
- Vite 6 and `@vitejs/plugin-react` when the application uses the framework build
|
|
12
12
|
|
|
13
13
|
## Install
|
|
14
14
|
|
|
@@ -36,20 +36,20 @@ export default function NotFound({ pathname }: { pathname: string }) {
|
|
|
36
36
|
}
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
Configure the application:
|
|
40
40
|
|
|
41
41
|
```ts
|
|
42
|
-
|
|
43
|
-
import { defineConfig } from "
|
|
44
|
-
import { demiurge } from "@demiurgejs/core/vite";
|
|
42
|
+
// demiurge.config.ts
|
|
43
|
+
import { defineConfig } from "@demiurgejs/core/config";
|
|
45
44
|
|
|
46
45
|
export default defineConfig({
|
|
47
|
-
|
|
46
|
+
routing: { typedRoutes: true },
|
|
48
47
|
});
|
|
49
48
|
```
|
|
50
49
|
|
|
51
|
-
Run `
|
|
52
|
-
|
|
50
|
+
Run `demiurge dev` for development or `demiurge build` for production output.
|
|
51
|
+
Demiurge generates the Vite configuration from `demiurge.config.ts`. There is
|
|
52
|
+
no `index.html` to write: the framework renders the document and attaches CSP
|
|
53
53
|
nonces to what it emits.
|
|
54
54
|
|
|
55
55
|
Run `demiurge build` for static production output. Run `demiurge preview` to
|
|
@@ -67,13 +67,15 @@ covers host allowlists, proxy trust, timeouts, and graceful shutdown.
|
|
|
67
67
|
- `@demiurgejs/core/node` — production Node HTTP, SSR, and static-file adapter
|
|
68
68
|
- `@demiurgejs/core/static` — static-output adapter
|
|
69
69
|
- `@demiurgejs/core/redis` — Redis-backed cache store with cross-instance tag invalidation
|
|
70
|
-
- `@demiurgejs/core/
|
|
70
|
+
- `@demiurgejs/core/config` — the application configuration contract
|
|
71
|
+
- `@demiurgejs/core/vite` — framework build internals
|
|
71
72
|
- `@demiurgejs/core/adapter/testing` — adapter capability conformance contract
|
|
73
|
+
- `@demiurgejs/core/deployment/testing` — deployment conformance kit for provider translation and production artifacts
|
|
72
74
|
- `@demiurgejs/core/data/testing` — cache-store conformance contract
|
|
73
75
|
- `@demiurgejs/core/internal/testing` — explicitly unstable test helpers
|
|
74
76
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
+
Vite is an optional peer dependency with `@vitejs/plugin-react`. A consumer
|
|
78
|
+
that only runs the built output does not install them.
|
|
77
79
|
|
|
78
80
|
## Support and license
|
|
79
81
|
|
package/bin/demiurge.mjs
CHANGED
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
import { resolve } from "node:path";
|
|
6
6
|
import {
|
|
7
|
-
buildStaticSite,
|
|
8
7
|
helpText,
|
|
9
8
|
parseCliArguments,
|
|
10
9
|
resolvePreviewOutputDirectory,
|
|
10
|
+
runBuild,
|
|
11
|
+
runDev,
|
|
11
12
|
} from "../dist/cli.js";
|
|
13
|
+
import { loadDemiurgeConfig } from "../dist/config/index.js";
|
|
12
14
|
import {
|
|
13
15
|
createStaticPreviewServer,
|
|
14
16
|
} from "../dist/static/index.js";
|
|
@@ -21,24 +23,40 @@ async function main() {
|
|
|
21
23
|
return;
|
|
22
24
|
}
|
|
23
25
|
|
|
26
|
+
const config = await loadDemiurgeConfig();
|
|
27
|
+
|
|
28
|
+
if (options.command === "dev") {
|
|
29
|
+
const server = await runDev(options, config);
|
|
30
|
+
server.printUrls();
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
24
34
|
if (options.command === "build") {
|
|
25
|
-
const
|
|
26
|
-
console.log(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
35
|
+
const result = await runBuild(options, config);
|
|
36
|
+
console.log(`Demiurge generated client output in ${result.outDir}.`);
|
|
37
|
+
if (result.serverOutDir) {
|
|
38
|
+
console.log(`Demiurge generated server output in ${result.serverOutDir}.`);
|
|
39
|
+
}
|
|
40
|
+
if (result.manifest) {
|
|
41
|
+
console.log(
|
|
42
|
+
`Demiurge generated ${result.manifest.entries.length} static artifacts in ${result.outDir}.`,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
if (result.deploymentOutDir) {
|
|
46
|
+
console.log(`Demiurge generated provider output in ${result.deploymentOutDir}.`);
|
|
31
47
|
}
|
|
32
48
|
return;
|
|
33
49
|
}
|
|
34
50
|
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
51
|
+
const outDir = resolvePreviewOutputDirectory(
|
|
52
|
+
config.root,
|
|
53
|
+
options.outDir,
|
|
54
|
+
config.deployment?.outDir,
|
|
55
|
+
);
|
|
56
|
+
const server = await createStaticPreviewServer({ ...options, outDir });
|
|
39
57
|
const address = server.address();
|
|
40
58
|
const port = typeof address === "object" && address ? address.port : options.port;
|
|
41
|
-
console.log(`Demiurge serves ${resolve(
|
|
59
|
+
console.log(`Demiurge serves ${resolve(outDir)} at http://${options.host}:${port}.`);
|
|
42
60
|
|
|
43
61
|
const close = () => server.close();
|
|
44
62
|
process.once("SIGINT", close);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.js","sources":["../../src/adapter/testing.ts"],"sourcesContent":["import { mkdtemp, readFile, rm } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport {\n verifyCacheStoreContract,\n verifyCacheStoreRefreshContract,\n type CacheStoreFactory,\n} from \"../data/testing\";\nimport type { Adapter, AdapterCapability } from \"./index\";\n\ntype MaybePromise<T> = T | Promise<T>;\n\n// A host answers a protocol upgrade before it has an HTTP response body, and\n// the Response constructor rejects status 101. A handshake is reported as a\n// plain status and header pair instead.\nexport type AdapterUpgradeResult = {\n headers?: Record<string, string>;\n status: number;\n};\n\nexport type AdapterBackgroundHost = {\n shutdown: () => Promise<void>;\n waitUntil: (promise: Promise<unknown>) => void;\n};\n\nexport type AdapterStaticOutputEntry = {\n file: string;\n headers: Record<string, string>;\n pathname: string;\n};\n\nexport type AdapterStaticOutput = {\n entries: readonly AdapterStaticOutputEntry[];\n};\n\n// One probe per capability. A probe runs the adapter through the smallest\n// deployment that can prove the capability, and the contract reads the result.\n// Every probe may be called more than once.\nexport type AdapterContractProbes = {\n backgroundLifetime?: () => MaybePromise<AdapterBackgroundHost>;\n crossOriginIsolationHeaders?: () => MaybePromise<Response>;\n nonceInjection?: () => MaybePromise<Response>;\n sharedCache?: CacheStoreFactory;\n staticOutput?: (outDir: string) => MaybePromise<AdapterStaticOutput>;\n streaming?: () => MaybePromise<Response>;\n webSocket?: (request: Request) => MaybePromise<AdapterUpgradeResult>;\n webTransport?: (request: Request) => MaybePromise<AdapterUpgradeResult>;\n};\n\nexport const adapterContractOrigin = \"https://adapter.contract.test\";\n\nconst foreignOrigin = \"https://foreign.contract.test\";\n\nconst contractCapabilities = [\n \"backgroundLifetime\",\n \"crossOriginIsolationHeaders\",\n \"nonceInjection\",\n \"sharedCache\",\n \"staticOutput\",\n \"streaming\",\n \"webSocket\",\n \"webTransport\",\n] as const satisfies readonly AdapterCapability[];\n\nconst noncePattern = /'nonce-([^']+)'/;\n\nexport async function verifyAdapterContract(\n adapter: Adapter,\n probes: AdapterContractProbes,\n) {\n assert(\n typeof adapter.name === \"string\" && adapter.name.length > 0,\n \"an adapter must carry a name\",\n );\n\n for (const capability of contractCapabilities) {\n const declared = adapter.capabilities[capability];\n const probe = probes[capability];\n\n assert(\n typeof declared === \"boolean\",\n `an adapter must declare capability \"${capability}\" as a boolean`,\n );\n assert(\n !declared || Boolean(probe),\n `capability \"${capability}\" is declared true and needs a probe that proves it`,\n );\n assert(\n declared || !probe,\n `capability \"${capability}\" has a probe but the adapter declares it false`,\n );\n }\n\n if (probes.backgroundLifetime) {\n await verifyBackgroundLifetimeContract(probes.backgroundLifetime);\n }\n\n if (probes.crossOriginIsolationHeaders) {\n await verifyCrossOriginIsolationContract(probes.crossOriginIsolationHeaders);\n }\n\n if (probes.nonceInjection) {\n await verifyNonceInjectionContract(probes.nonceInjection);\n }\n\n if (probes.sharedCache) {\n await verifySharedCacheContract(probes.sharedCache);\n }\n\n if (probes.staticOutput) {\n await verifyStaticOutputContract(probes.staticOutput);\n }\n\n if (probes.streaming) {\n await verifyStreamingContract(probes.streaming);\n }\n\n if (probes.webSocket) {\n await verifyUpgradeContract(probes.webSocket, \"websocket\", 101);\n }\n\n if (probes.webTransport) {\n await verifyUpgradeContract(probes.webTransport, \"webtransport\", 200);\n }\n}\n\n// A streamed body reaches the client before the render finishes, so it arrives\n// as more than one chunk. A buffered response fails this, and a client that\n// disconnects early must not fault the host.\nasync function verifyStreamingContract(\n probe: NonNullable<AdapterContractProbes[\"streaming\"]>,\n) {\n const response = await probe();\n\n assert(\n Boolean(response.body),\n \"a streaming response must expose a readable body stream\",\n );\n\n const reader = response.body!.getReader();\n const decoder = new TextDecoder();\n let chunks = 0;\n let body = \"\";\n\n while (true) {\n const chunk = await reader.read();\n\n if (chunk.done) {\n break;\n }\n\n chunks += 1;\n body += decoder.decode(chunk.value, { stream: true });\n }\n\n body += decoder.decode();\n\n assert(body.length > 0, \"a streaming response must deliver a body\");\n assert(\n chunks > 1,\n \"a streaming response must deliver its body in more than one chunk\",\n );\n\n const cancelled = await probe();\n\n try {\n await cancelled.body?.cancel();\n } catch (error) {\n throw contractError(\n `a streaming response body must accept cancellation, and it failed with ${describe(error)}`,\n );\n }\n}\n\n// A nonce is single use. Two responses cannot share one, the document must\n// carry the value the header declares, and a shared cache must not store it.\nasync function verifyNonceInjectionContract(\n probe: NonNullable<AdapterContractProbes[\"nonceInjection\"]>,\n) {\n const first = await probe();\n const second = await probe();\n const firstNonce = readCspNonce(first);\n const secondNonce = readCspNonce(second);\n\n await second.body?.cancel();\n\n assert(\n Boolean(firstNonce),\n \"a nonce-injecting response must declare a content-security-policy nonce source\",\n );\n assert(\n Boolean(secondNonce) && secondNonce !== firstNonce,\n \"every response must carry a freshly generated nonce\",\n );\n\n const document = await first.text();\n\n assert(\n document.includes(`nonce=\"${firstNonce}\"`),\n \"the rendered document must carry the nonce its header declares\",\n );\n\n const cacheControl = (first.headers.get(\"cache-control\") ?? \"\").toLowerCase();\n\n assert(\n cacheControl.includes(\"no-store\") || cacheControl.includes(\"private\"),\n \"a nonce-backed document must not be a shared cache representation\",\n );\n}\n\n// Cross-origin isolation needs both headers to survive the host response path.\n// A resource policy travels with them and keeps the isolated document from\n// leaking as a subresource.\nasync function verifyCrossOriginIsolationContract(\n probe: NonNullable<AdapterContractProbes[\"crossOriginIsolationHeaders\"]>,\n) {\n const response = await probe();\n const embedderPolicy = response.headers.get(\"cross-origin-embedder-policy\");\n\n await response.body?.cancel();\n\n assert(\n response.headers.get(\"cross-origin-opener-policy\") === \"same-origin\",\n \"an isolated response must carry cross-origin-opener-policy same-origin\",\n );\n assert(\n embedderPolicy === \"require-corp\" || embedderPolicy === \"credentialless\",\n \"an isolated response must carry cross-origin-embedder-policy require-corp or credentialless\",\n );\n assert(\n Boolean(response.headers.get(\"cross-origin-resource-policy\")),\n \"an isolated response must carry a cross-origin-resource-policy\",\n );\n}\n\n// Background work outlives the response that started it. Shutdown waits for\n// that work, a failed task never fails shutdown, and a repeated shutdown is\n// safe.\nasync function verifyBackgroundLifetimeContract(\n probe: NonNullable<AdapterContractProbes[\"backgroundLifetime\"]>,\n) {\n const host = await probe();\n let release = () => {};\n let settled = false;\n const work = new Promise<void>((resolveWork) => {\n release = () => {\n settled = true;\n resolveWork();\n };\n });\n\n host.waitUntil(work);\n\n let stopped = false;\n const shutdown = host.shutdown().then(() => {\n stopped = true;\n });\n\n await tick();\n\n assert(\n !stopped,\n \"shutdown must not complete while background work is pending\",\n );\n\n release();\n await shutdown;\n\n assert(\n settled && stopped,\n \"shutdown must complete once background work settles\",\n );\n\n const failingHost = await probe();\n failingHost.waitUntil(\n Promise.reject(new Error(\"Adapter contract background failure.\")),\n );\n\n try {\n await failingHost.shutdown();\n await failingHost.shutdown();\n } catch (error) {\n throw contractError(\n `failed background work must not fail shutdown, and it failed with ${describe(error)}`,\n );\n }\n}\n\n// A shared cache is the framework cache store contract seen from a host. The\n// refresh contract applies to a store that publishes the lease methods.\nasync function verifySharedCacheContract(\n probe: NonNullable<AdapterContractProbes[\"sharedCache\"]>,\n) {\n await verifyCacheStoreContract(probe);\n\n const store = await probe();\n\n if (store.acquireRefreshLease) {\n await verifyCacheStoreRefreshContract(probe);\n }\n}\n\n// A build artifact is a file on disk, and it holds no per-request state. The\n// same build run twice writes the same files, which is what makes a deployment\n// reproducible.\nasync function verifyStaticOutputContract(\n probe: NonNullable<AdapterContractProbes[\"staticOutput\"]>,\n) {\n const outDir = await mkdtemp(join(tmpdir(), \"demiurge-adapter-contract-\"));\n\n try {\n const first = await probe(outDir);\n\n assert(\n first.entries.length > 0,\n \"a static build must produce at least one output entry\",\n );\n\n const files = new Set<string>();\n const pathnames = new Set<string>();\n const contents = new Map<string, string>();\n\n for (const entry of first.entries) {\n const file = JSON.stringify(entry.file);\n\n assert(\n isContainedFile(entry.file),\n `every output file must stay inside the output directory, and ${file} does not`,\n );\n assert(\n !files.has(entry.file),\n `every output entry must own one file, and ${file} repeats`,\n );\n assert(\n !pathnames.has(entry.pathname),\n `every output entry must own one pathname, and ${JSON.stringify(entry.pathname)} repeats`,\n );\n\n files.add(entry.file);\n pathnames.add(entry.pathname);\n\n const headers = lowercaseHeaders(entry.headers);\n\n assert(\n headers[\"set-cookie\"] === undefined,\n `a build artifact must not carry per-request state, and ${file} sets a cookie`,\n );\n assert(\n !noncePattern.test(headers[\"content-security-policy\"] ?? \"\"),\n `a build artifact must not depend on a per-request nonce, and ${file} declares one`,\n );\n\n contents.set(entry.file, await readOutputFile(outDir, entry.file));\n }\n\n const second = await probe(outDir);\n\n assert(\n JSON.stringify(second.entries) === JSON.stringify(first.entries),\n \"a repeated static build must produce the same output entries\",\n );\n\n for (const entry of second.entries) {\n assert(\n await readOutputFile(outDir, entry.file) === contents.get(entry.file),\n `a repeated static build must write the same file, and ${JSON.stringify(entry.file)} changed`,\n );\n }\n } finally {\n await rm(outDir, { force: true, recursive: true });\n }\n}\n\n// A host that accepts a connection upgrade also has to refuse one. An origin\n// it does not own and a request without the upgrade never reach the accepted\n// handshake.\nasync function verifyUpgradeContract(\n probe: (request: Request) => MaybePromise<AdapterUpgradeResult>,\n protocol: \"websocket\" | \"webtransport\",\n acceptedStatus: 101 | 200,\n) {\n const accepted = await probe(\n upgradeRequest(protocol, adapterContractOrigin),\n );\n const negotiated = lowercaseHeaders(accepted.headers ?? {}).upgrade;\n\n assert(\n accepted.status === acceptedStatus,\n `an accepted ${protocol} handshake must answer with status ${acceptedStatus}`,\n );\n assert(\n negotiated?.toLowerCase() === protocol,\n `an accepted ${protocol} handshake must confirm the negotiated protocol`,\n );\n\n const foreign = await probe(upgradeRequest(protocol, foreignOrigin));\n\n assert(\n foreign.status >= 400 && foreign.status < 500,\n `a ${protocol} handshake from an origin the host does not own must be refused`,\n );\n\n const plain = await probe(\n new Request(`${adapterContractOrigin}/`, {\n headers: { origin: adapterContractOrigin },\n }),\n );\n\n assert(\n plain.status !== acceptedStatus,\n `a request without a ${protocol} upgrade must not receive a handshake`,\n );\n}\n\nfunction upgradeRequest(protocol: string, origin: string) {\n return new Request(`${adapterContractOrigin}/`, {\n headers: {\n connection: \"Upgrade\",\n origin,\n upgrade: protocol,\n },\n });\n}\n\nfunction readCspNonce(response: Response) {\n const csp = response.headers.get(\"content-security-policy\") ?? \"\";\n\n return noncePattern.exec(csp)?.[1];\n}\n\nasync function readOutputFile(outDir: string, file: string) {\n try {\n return await readFile(join(outDir, file), \"utf8\");\n } catch {\n throw contractError(\n `every output entry must write its file, and ${JSON.stringify(file)} is missing`,\n );\n }\n}\n\nfunction isContainedFile(file: string) {\n return Boolean(file) &&\n !file.startsWith(\"/\") &&\n !file.startsWith(\"\\\\\") &&\n !file.split(/[\\\\/]/).includes(\"..\");\n}\n\nfunction lowercaseHeaders(headers: Record<string, string>) {\n return Object.fromEntries(\n Object.entries(headers).map(([name, value]) => [\n name.toLowerCase(),\n value,\n ]),\n ) as Record<string, string | undefined>;\n}\n\nfunction tick() {\n return new Promise((resolveTick) => setTimeout(resolveTick, 0));\n}\n\nfunction describe(error: unknown) {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction assert(condition: boolean, requirement: string): asserts condition {\n if (!condition) {\n throw contractError(requirement);\n }\n}\n\nfunction contractError(requirement: string) {\n return new Error(`Adapter contract failed: ${requirement}.`);\n}\n"],"names":["adapterContractOrigin","foreignOrigin","contractCapabilities","noncePattern","verifyAdapterContract","adapter","probes","assert","capability","declared","probe","verifyBackgroundLifetimeContract","verifyCrossOriginIsolationContract","verifyNonceInjectionContract","verifySharedCacheContract","verifyStaticOutputContract","verifyStreamingContract","verifyUpgradeContract","response","reader","decoder","chunks","body","chunk","cancelled","error","contractError","describe","first","second","firstNonce","readCspNonce","secondNonce","document","cacheControl","embedderPolicy","host","release","settled","work","resolveWork","stopped","shutdown","tick","failingHost","verifyCacheStoreContract","verifyCacheStoreRefreshContract","outDir","mkdtemp","join","tmpdir","files","pathnames","contents","entry","file","isContainedFile","headers","lowercaseHeaders","readOutputFile","rm","protocol","acceptedStatus","accepted","upgradeRequest","negotiated","foreign","plain","origin","csp","readFile","name","value","resolveTick","condition","requirement"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"testing.js","sources":["../../src/adapter/testing.ts"],"sourcesContent":["import { mkdtemp, readFile, rm } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport {\n verifyCacheStoreContract,\n verifyCacheStoreRefreshContract,\n type CacheStoreFactory,\n} from \"../data/testing\";\nimport type { Adapter, AdapterCapability } from \"./index\";\n\ntype MaybePromise<T> = T | Promise<T>;\n\n// A host answers a protocol upgrade before it has an HTTP response body, and\n// the Response constructor rejects status 101. A handshake is reported as a\n// plain status and header pair instead.\nexport type AdapterUpgradeResult = {\n headers?: Record<string, string>;\n status: number;\n};\n\nexport type AdapterBackgroundHost = {\n shutdown: () => Promise<void>;\n waitUntil: (promise: Promise<unknown>) => void;\n};\n\nexport type AdapterStaticOutputEntry = {\n file: string;\n headers: Record<string, string>;\n pathname: string;\n};\n\nexport type AdapterStaticOutput = {\n entries: readonly AdapterStaticOutputEntry[];\n};\n\n// One probe per capability. A probe runs the adapter through the smallest\n// deployment that can prove the capability, and the contract reads the result.\n// Every probe may be called more than once.\nexport type AdapterContractProbes = {\n backgroundLifetime?: () => MaybePromise<AdapterBackgroundHost>;\n crossOriginIsolationHeaders?: () => MaybePromise<Response>;\n nonceInjection?: () => MaybePromise<Response>;\n sharedCache?: CacheStoreFactory;\n staticOutput?: (outDir: string) => MaybePromise<AdapterStaticOutput>;\n streaming?: () => MaybePromise<Response>;\n webSocket?: (request: Request) => MaybePromise<AdapterUpgradeResult>;\n webTransport?: (request: Request) => MaybePromise<AdapterUpgradeResult>;\n};\n\nexport const adapterContractOrigin = \"https://adapter.contract.test\";\n\nconst foreignOrigin = \"https://foreign.contract.test\";\n\nconst contractCapabilities = [\n \"backgroundLifetime\",\n \"crossOriginIsolationHeaders\",\n \"nonceInjection\",\n \"sharedCache\",\n \"staticOutput\",\n \"streaming\",\n \"webSocket\",\n \"webTransport\",\n] as const satisfies readonly AdapterCapability[];\n\nconst noncePattern = /'nonce-([^']+)'/;\n\nexport async function verifyAdapterContract(\n adapter: Adapter,\n probes: AdapterContractProbes,\n) {\n assert(\n typeof adapter.name === \"string\" && adapter.name.length > 0,\n \"an adapter must carry a name\",\n );\n\n for (const capability of contractCapabilities) {\n const declared = adapter.capabilities[capability];\n const probe = probes[capability];\n\n assert(\n typeof declared === \"boolean\",\n `an adapter must declare capability \"${capability}\" as a boolean`,\n );\n assert(\n !declared || Boolean(probe),\n `capability \"${capability}\" is declared true and needs a probe that proves it`,\n );\n assert(\n declared || !probe,\n `capability \"${capability}\" has a probe but the adapter declares it false`,\n );\n }\n\n if (probes.backgroundLifetime) {\n await verifyBackgroundLifetimeContract(probes.backgroundLifetime);\n }\n\n if (probes.crossOriginIsolationHeaders) {\n await verifyCrossOriginIsolationContract(probes.crossOriginIsolationHeaders);\n }\n\n if (probes.nonceInjection) {\n await verifyNonceInjectionContract(probes.nonceInjection);\n }\n\n if (probes.sharedCache) {\n await verifySharedCacheContract(probes.sharedCache);\n }\n\n if (probes.staticOutput) {\n await verifyStaticOutputContract(probes.staticOutput);\n }\n\n if (probes.streaming) {\n await verifyStreamingContract(probes.streaming);\n }\n\n if (probes.webSocket) {\n await verifyUpgradeContract(probes.webSocket, \"websocket\", 101);\n }\n\n if (probes.webTransport) {\n await verifyUpgradeContract(probes.webTransport, \"webtransport\", 200);\n }\n}\n\n// A streamed body reaches the client before the render finishes, so it arrives\n// as more than one chunk. A buffered response fails this, and a client that\n// disconnects early must not fault the host.\nasync function verifyStreamingContract(\n probe: NonNullable<AdapterContractProbes[\"streaming\"]>,\n) {\n const response = await probe();\n\n assert(\n Boolean(response.body),\n \"a streaming response must expose a readable body stream\",\n );\n\n const reader = response.body!.getReader();\n const decoder = new TextDecoder();\n let chunks = 0;\n let body = \"\";\n\n while (true) {\n const chunk = await reader.read();\n\n if (chunk.done) {\n break;\n }\n\n chunks += 1;\n body += decoder.decode(chunk.value, { stream: true });\n }\n\n body += decoder.decode();\n\n assert(body.length > 0, \"a streaming response must deliver a body\");\n assert(\n chunks > 1,\n \"a streaming response must deliver its body in more than one chunk\",\n );\n\n const cancelled = await probe();\n\n try {\n await cancelled.body?.cancel();\n } catch (error) {\n throw contractError(\n `a streaming response body must accept cancellation, and it failed with ${describe(error)}`,\n );\n }\n}\n\n// A nonce is single use. Two responses cannot share one, the document must\n// carry the value the header declares, and a shared cache must not store it.\nasync function verifyNonceInjectionContract(\n probe: NonNullable<AdapterContractProbes[\"nonceInjection\"]>,\n) {\n const first = await probe();\n const second = await probe();\n const firstNonce = readCspNonce(first);\n const secondNonce = readCspNonce(second);\n\n await second.body?.cancel();\n\n assert(\n Boolean(firstNonce),\n \"a nonce-injecting response must declare a content-security-policy nonce source\",\n );\n assert(\n Boolean(secondNonce) && secondNonce !== firstNonce,\n \"every response must carry a freshly generated nonce\",\n );\n\n const document = await first.text();\n\n assert(\n document.includes(`nonce=\"${firstNonce}\"`),\n \"the rendered document must carry the nonce its header declares\",\n );\n\n const cacheControl = (first.headers.get(\"cache-control\") ?? \"\").toLowerCase();\n\n assert(\n cacheControl.includes(\"no-store\") || cacheControl.includes(\"private\"),\n \"a nonce-backed document must not be a shared cache representation\",\n );\n}\n\n// Cross-origin isolation needs both headers to survive the host response path.\n// A resource policy travels with them and keeps the isolated document from\n// leaking as a subresource.\nasync function verifyCrossOriginIsolationContract(\n probe: NonNullable<AdapterContractProbes[\"crossOriginIsolationHeaders\"]>,\n) {\n const response = await probe();\n const embedderPolicy = response.headers.get(\"cross-origin-embedder-policy\");\n\n await response.body?.cancel();\n\n assert(\n response.headers.get(\"cross-origin-opener-policy\") === \"same-origin\",\n \"an isolated response must carry cross-origin-opener-policy same-origin\",\n );\n assert(\n embedderPolicy === \"require-corp\" || embedderPolicy === \"credentialless\",\n \"an isolated response must carry cross-origin-embedder-policy require-corp or credentialless\",\n );\n assert(\n Boolean(response.headers.get(\"cross-origin-resource-policy\")),\n \"an isolated response must carry a cross-origin-resource-policy\",\n );\n}\n\n// Background work outlives the response that started it. Shutdown waits for\n// that work, a failed task never fails shutdown, and a repeated shutdown is\n// safe.\nasync function verifyBackgroundLifetimeContract(\n probe: NonNullable<AdapterContractProbes[\"backgroundLifetime\"]>,\n) {\n const host = await probe();\n let release = () => {};\n let settled = false;\n const work = new Promise<void>((resolveWork) => {\n release = () => {\n settled = true;\n resolveWork();\n };\n });\n\n host.waitUntil(work);\n\n let stopped = false;\n const shutdown = host.shutdown().then(() => {\n stopped = true;\n });\n\n await tick();\n\n assert(\n !stopped,\n \"shutdown must not complete while background work is pending\",\n );\n\n release();\n await shutdown;\n\n assert(\n settled && stopped,\n \"shutdown must complete once background work settles\",\n );\n\n const failingHost = await probe();\n failingHost.waitUntil(\n Promise.reject(new Error(\"Adapter contract background failure.\")),\n );\n\n try {\n await failingHost.shutdown();\n await failingHost.shutdown();\n } catch (error) {\n throw contractError(\n `failed background work must not fail shutdown, and it failed with ${describe(error)}`,\n );\n }\n}\n\n// A shared cache is the framework cache store contract seen from a host. The\n// refresh contract applies to a store that publishes the lease methods.\nasync function verifySharedCacheContract(\n probe: NonNullable<AdapterContractProbes[\"sharedCache\"]>,\n) {\n await verifyCacheStoreContract(probe);\n\n const store = await probe();\n\n if (store.acquireRefreshLease) {\n await verifyCacheStoreRefreshContract(probe);\n }\n}\n\n// A build artifact is a file on disk, and it holds no per-request state. The\n// same build run twice writes the same files, which is what makes a deployment\n// reproducible.\nasync function verifyStaticOutputContract(\n probe: NonNullable<AdapterContractProbes[\"staticOutput\"]>,\n) {\n const outDir = await mkdtemp(join(tmpdir(), \"demiurge-adapter-contract-\"));\n\n try {\n const first = await probe(outDir);\n\n assert(\n first.entries.length > 0,\n \"a static build must produce at least one output entry\",\n );\n\n const files = new Set<string>();\n const pathnames = new Set<string>();\n const contents = new Map<string, string>();\n\n for (const entry of first.entries) {\n const file = JSON.stringify(entry.file);\n\n assert(\n isContainedFile(entry.file),\n `every output file must stay inside the output directory, and ${file} does not`,\n );\n assert(\n !files.has(entry.file),\n `every output entry must own one file, and ${file} repeats`,\n );\n assert(\n !pathnames.has(entry.pathname),\n `every output entry must own one pathname, and ${JSON.stringify(entry.pathname)} repeats`,\n );\n\n files.add(entry.file);\n pathnames.add(entry.pathname);\n\n const headers = lowercaseHeaders(entry.headers);\n\n assert(\n headers[\"set-cookie\"] === undefined,\n `a build artifact must not carry per-request state, and ${file} sets a cookie`,\n );\n assert(\n !noncePattern.test(headers[\"content-security-policy\"] ?? \"\"),\n `a build artifact must not depend on a per-request nonce, and ${file} declares one`,\n );\n\n contents.set(entry.file, await readOutputFile(outDir, entry.file));\n }\n\n const second = await probe(outDir);\n\n assert(\n JSON.stringify(second.entries) === JSON.stringify(first.entries),\n \"a repeated static build must produce the same output entries\",\n );\n\n for (const entry of second.entries) {\n assert(\n await readOutputFile(outDir, entry.file) === contents.get(entry.file),\n `a repeated static build must write the same file, and ${JSON.stringify(entry.file)} changed`,\n );\n }\n } finally {\n await rm(outDir, { force: true, recursive: true });\n }\n}\n\n// A host that accepts a connection upgrade also has to refuse one. An origin\n// it does not own and a request without the upgrade never reach the accepted\n// handshake.\nasync function verifyUpgradeContract(\n probe: (request: Request) => MaybePromise<AdapterUpgradeResult>,\n protocol: \"websocket\" | \"webtransport\",\n acceptedStatus: 101 | 200,\n) {\n const accepted = await probe(\n upgradeRequest(protocol, adapterContractOrigin),\n );\n const negotiated = lowercaseHeaders(accepted.headers ?? {}).upgrade;\n\n assert(\n accepted.status === acceptedStatus,\n `an accepted ${protocol} handshake must answer with status ${acceptedStatus}`,\n );\n assert(\n negotiated?.toLowerCase() === protocol,\n `an accepted ${protocol} handshake must confirm the negotiated protocol`,\n );\n\n const foreign = await probe(upgradeRequest(protocol, foreignOrigin));\n\n assert(\n foreign.status >= 400 && foreign.status < 500,\n `a ${protocol} handshake from an origin the host does not own must be refused`,\n );\n\n const plain = await probe(\n new Request(`${adapterContractOrigin}/`, {\n headers: { origin: adapterContractOrigin },\n }),\n );\n\n assert(\n plain.status !== acceptedStatus,\n `a request without a ${protocol} upgrade must not receive a handshake`,\n );\n}\n\nfunction upgradeRequest(protocol: string, origin: string) {\n return new Request(`${adapterContractOrigin}/`, {\n headers: {\n connection: \"Upgrade\",\n origin,\n upgrade: protocol,\n },\n });\n}\n\nfunction readCspNonce(response: Response) {\n const csp = response.headers.get(\"content-security-policy\") ?? \"\";\n\n return noncePattern.exec(csp)?.[1];\n}\n\nasync function readOutputFile(outDir: string, file: string) {\n try {\n return await readFile(join(outDir, file), \"utf8\");\n } catch {\n throw contractError(\n `every output entry must write its file, and ${JSON.stringify(file)} is missing`,\n );\n }\n}\n\nfunction isContainedFile(file: string) {\n return Boolean(file) &&\n !file.startsWith(\"/\") &&\n !file.startsWith(\"\\\\\") &&\n !file.split(/[\\\\/]/).includes(\"..\");\n}\n\nfunction lowercaseHeaders(headers: Record<string, string>) {\n // TYPE-EVIDENCE: Object.fromEntries builds a record with string keys and string values. The cast widens the values to allow undefined.\n return Object.fromEntries(\n Object.entries(headers).map(([name, value]) => [\n name.toLowerCase(),\n value,\n ]),\n ) as Record<string, string | undefined>;\n}\n\nfunction tick() {\n return new Promise((resolveTick) => setTimeout(resolveTick, 0));\n}\n\nfunction describe(error: unknown) {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction assert(condition: boolean, requirement: string): asserts condition {\n if (!condition) {\n throw contractError(requirement);\n }\n}\n\nfunction contractError(requirement: string) {\n return new Error(`Adapter contract failed: ${requirement}.`);\n}\n"],"names":["adapterContractOrigin","foreignOrigin","contractCapabilities","noncePattern","verifyAdapterContract","adapter","probes","assert","capability","declared","probe","verifyBackgroundLifetimeContract","verifyCrossOriginIsolationContract","verifyNonceInjectionContract","verifySharedCacheContract","verifyStaticOutputContract","verifyStreamingContract","verifyUpgradeContract","response","reader","decoder","chunks","body","chunk","cancelled","error","contractError","describe","first","second","firstNonce","readCspNonce","secondNonce","document","cacheControl","embedderPolicy","host","release","settled","work","resolveWork","stopped","shutdown","tick","failingHost","verifyCacheStoreContract","verifyCacheStoreRefreshContract","outDir","mkdtemp","join","tmpdir","files","pathnames","contents","entry","file","isContainedFile","headers","lowercaseHeaders","readOutputFile","rm","protocol","acceptedStatus","accepted","upgradeRequest","negotiated","foreign","plain","origin","csp","readFile","name","value","resolveTick","condition","requirement"],"mappings":"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"}
|