@takazudo/zfb-adapter-cloudflare 0.1.0-next.7 → 0.1.0-next.70
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/bin/cli.mjs +7 -12
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +8 -10
- package/dist/build.js.map +1 -1
- package/dist/emit-worker.mjs +38 -0
- package/dist/worker-wrapper.mjs +11 -2
- package/package.json +3 -2
- package/src/emit-worker.mjs +38 -0
- package/src/worker-wrapper.mjs +11 -2
package/bin/cli.mjs
CHANGED
|
@@ -15,9 +15,8 @@
|
|
|
15
15
|
// no TypeScript loader required) so there is a single source of truth.
|
|
16
16
|
// invariant: no runtime npm deps — see SECURITY-DEPS.md
|
|
17
17
|
|
|
18
|
-
import { copyFile, mkdir, writeFile } from "node:fs/promises";
|
|
19
18
|
import { realpathSync } from "node:fs";
|
|
20
|
-
import {
|
|
19
|
+
import { resolve } from "node:path";
|
|
21
20
|
import { fileURLToPath } from "node:url";
|
|
22
21
|
|
|
23
22
|
// ---------------------------------------------------------------------------
|
|
@@ -27,18 +26,14 @@ import { fileURLToPath } from "node:url";
|
|
|
27
26
|
import { WORKER_WRAPPER_SOURCE } from "../src/worker-wrapper.mjs";
|
|
28
27
|
export { WORKER_WRAPPER_SOURCE };
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
await mkdir(outdirAbs, { recursive: true });
|
|
35
|
-
const innerBundlePath = join(outdirAbs, "_zfb_inner.mjs");
|
|
36
|
-
await copyFile(inputAbs, innerBundlePath);
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// emitWorker — shared implementation, no runtime npm deps.
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
37
32
|
|
|
38
|
-
|
|
39
|
-
await writeFile(workerPath, WORKER_WRAPPER_SOURCE, "utf8");
|
|
33
|
+
import { emitWorker as _emitWorker } from "../src/emit-worker.mjs";
|
|
40
34
|
|
|
41
|
-
|
|
35
|
+
export async function emitWorker({ inputBundlePath, outdir }) {
|
|
36
|
+
return _emitWorker({ inputBundlePath, outdir, workerWrapperSource: WORKER_WRAPPER_SOURCE });
|
|
42
37
|
}
|
|
43
38
|
|
|
44
39
|
// ---------------------------------------------------------------------------
|
package/dist/build.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"AAiBA;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,EAAE,MAA2B,CAAC;AAEhE;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;;;OAMG;IACH,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAMlF"}
|
package/dist/build.js
CHANGED
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
// @ts-expect-error worker-wrapper.mjs has no declaration file; the export
|
|
11
11
|
// shape is narrowed explicitly below.
|
|
12
12
|
import { WORKER_WRAPPER_SOURCE as _wrapper } from "./worker-wrapper.mjs";
|
|
13
|
+
// @ts-expect-error emit-worker.mjs has no declaration file; the export
|
|
14
|
+
// shape is narrowed via EmitWorkerInput/EmitWorkerOutput below.
|
|
15
|
+
import { emitWorker as _emitWorker } from "./emit-worker.mjs";
|
|
13
16
|
/**
|
|
14
17
|
* The wrapper source written to `_worker.js`. Imported from the single
|
|
15
18
|
* canonical `.mjs` file so `src/build.ts` and `bin/cli.mjs` always stay
|
|
@@ -35,15 +38,10 @@ export const WORKER_WRAPPER_SOURCE = _wrapper;
|
|
|
35
38
|
* runtime — it is just `node:fs` glue.
|
|
36
39
|
*/
|
|
37
40
|
export async function emitWorker(input) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const innerBundlePath = join(outdir, "_zfb_inner.mjs");
|
|
44
|
-
await copyFile(inputBundle, innerBundlePath);
|
|
45
|
-
const workerPath = join(outdir, "_worker.js");
|
|
46
|
-
await writeFile(workerPath, WORKER_WRAPPER_SOURCE, "utf8");
|
|
47
|
-
return { workerPath, innerBundlePath };
|
|
41
|
+
return _emitWorker({
|
|
42
|
+
inputBundlePath: input.inputBundlePath,
|
|
43
|
+
outdir: input.outdir,
|
|
44
|
+
workerWrapperSource: WORKER_WRAPPER_SOURCE,
|
|
45
|
+
});
|
|
48
46
|
}
|
|
49
47
|
//# sourceMappingURL=build.js.map
|
package/dist/build.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.js","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,EAAE;AACF,0EAA0E;AAC1E,kEAAkE;AAClE,sEAAsE;AACtE,+DAA+D;AAC/D,EAAE;AACF,yEAAyE;AACzE,mEAAmE;AAEnE,0EAA0E;AAC1E,sCAAsC;AACtC,OAAO,EAAE,qBAAqB,IAAI,QAAQ,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,EAAE;AACF,0EAA0E;AAC1E,kEAAkE;AAClE,sEAAsE;AACtE,+DAA+D;AAC/D,EAAE;AACF,yEAAyE;AACzE,mEAAmE;AAEnE,0EAA0E;AAC1E,sCAAsC;AACtC,OAAO,EAAE,qBAAqB,IAAI,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACzE,uEAAuE;AACvE,gEAAgE;AAChE,OAAO,EAAE,UAAU,IAAI,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAE9D;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAW,QAAkB,CAAC;AA+BhE;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,KAAsB;IACrD,OAAO,WAAW,CAAC;QACjB,eAAe,EAAE,KAAK,CAAC,eAAe;QACtC,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,mBAAmB,EAAE,qBAAqB;KAC3C,CAA8B,CAAC;AAClC,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Shared, dependency-free implementation of `emitWorker`.
|
|
2
|
+
//
|
|
3
|
+
// Kept as a plain `.mjs` module (no TypeScript) so it can be imported from
|
|
4
|
+
// both the typed TypeScript surface (`src/build.ts`) and the dependency-free
|
|
5
|
+
// CLI binary (`bin/cli.mjs`) without needing a TypeScript loader.
|
|
6
|
+
//
|
|
7
|
+
// Do NOT duplicate this logic elsewhere. The two consumers always stay in
|
|
8
|
+
// sync by importing from here.
|
|
9
|
+
//
|
|
10
|
+
// invariant: no runtime npm deps — see SECURITY-DEPS.md
|
|
11
|
+
|
|
12
|
+
import { copyFile, mkdir, writeFile } from "node:fs/promises";
|
|
13
|
+
import { join, resolve } from "node:path";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Emit a Cloudflare Pages `_worker.js` that wraps the zfb input bundle.
|
|
17
|
+
*
|
|
18
|
+
* Output shape (two files in `outdir`):
|
|
19
|
+
*
|
|
20
|
+
* _worker.js — entry imported by Cloudflare Pages advanced mode
|
|
21
|
+
* _zfb_inner.mjs — the input bundle, copied verbatim
|
|
22
|
+
*
|
|
23
|
+
* @param {{ inputBundlePath: string; outdir: string; workerWrapperSource: string }} input
|
|
24
|
+
* @returns {Promise<{ workerPath: string; innerBundlePath: string }>}
|
|
25
|
+
*/
|
|
26
|
+
export async function emitWorker({ inputBundlePath, outdir, workerWrapperSource }) {
|
|
27
|
+
const outdirAbs = resolve(outdir);
|
|
28
|
+
const inputAbs = resolve(inputBundlePath);
|
|
29
|
+
|
|
30
|
+
await mkdir(outdirAbs, { recursive: true });
|
|
31
|
+
const innerBundlePath = join(outdirAbs, "_zfb_inner.mjs");
|
|
32
|
+
await copyFile(inputAbs, innerBundlePath);
|
|
33
|
+
|
|
34
|
+
const workerPath = join(outdirAbs, "_worker.js");
|
|
35
|
+
await writeFile(workerPath, workerWrapperSource, "utf8");
|
|
36
|
+
|
|
37
|
+
return { workerPath, innerBundlePath };
|
|
38
|
+
}
|
package/dist/worker-wrapper.mjs
CHANGED
|
@@ -31,8 +31,7 @@ export const WORKER_WRAPPER_SOURCE = `// AUTO-GENERATED by @takazudo/zfb-adapter
|
|
|
31
31
|
// islands-…">). If we let the inner Hono router handle them first,
|
|
32
32
|
// it would dynamic-SSR the page WITHOUT the prod head injection
|
|
33
33
|
// (which is a build-time post-process, not a runtime concern), and
|
|
34
|
-
// islands would never hydrate.
|
|
35
|
-
// the zudo-doc#1355 zfb-pipeline-gaps epic).
|
|
34
|
+
// islands would never hydrate.
|
|
36
35
|
// - On 404 from ASSETS, fall through to the inner zfb worker. This is
|
|
37
36
|
// where genuinely dynamic routes (\`prerender = false\`, e.g.
|
|
38
37
|
// \`pages/api/*.tsx\`) are served.
|
|
@@ -68,6 +67,16 @@ function isAssetProbeMethod(method) {
|
|
|
68
67
|
export default {
|
|
69
68
|
async fetch(request, env, ctx) {
|
|
70
69
|
if (isAssetProbeMethod(request.method) && canDelegateToAssets(env)) {
|
|
70
|
+
// Trade-off: every GET/HEAD request that matches a prerendered route pays
|
|
71
|
+
// the cost of one env.ASSETS.fetch() round-trip before the inner worker
|
|
72
|
+
// sees it. The upside is that CF Pages' asset server handles trailing-
|
|
73
|
+
// slash canonicalisation (e.g. /docs/foo → 308 → /docs/foo/) and serves
|
|
74
|
+
// the build-time head-injected HTML with the hashed <link>/<script> tags.
|
|
75
|
+
// If we skipped this probe, prerendered routes would be dynamic-SSR'd by
|
|
76
|
+
// the inner Hono router without the prod head injection, and islands would
|
|
77
|
+
// never hydrate. For purely dynamic apps (prerender=false everywhere) the
|
|
78
|
+
// extra round-trip is pure overhead; splitting the wrapper into two
|
|
79
|
+
// variants is the accepted future escape hatch for that case.
|
|
71
80
|
const assetResponse = await env.ASSETS.fetch(request);
|
|
72
81
|
if (assetResponse.status !== 404) {
|
|
73
82
|
return assetResponse;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@takazudo/zfb-adapter-cloudflare",
|
|
3
|
-
"version": "0.1.0-next.
|
|
3
|
+
"version": "0.1.0-next.70",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Rust-built static-site engine for Astro and Next.js users — millisecond rebuilds, single binary. Cloudflare Pages adapter.",
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
"dist",
|
|
45
45
|
"bin",
|
|
46
46
|
"src/worker-wrapper.mjs",
|
|
47
|
+
"src/emit-worker.mjs",
|
|
47
48
|
"README.md",
|
|
48
49
|
"CHANGELOG.md",
|
|
49
50
|
"LICENSE"
|
|
@@ -61,7 +62,7 @@
|
|
|
61
62
|
"vitest": "^2.1.9"
|
|
62
63
|
},
|
|
63
64
|
"scripts": {
|
|
64
|
-
"build": "tsc && cp src/worker-wrapper.mjs dist/worker-wrapper.mjs",
|
|
65
|
+
"build": "tsc && cp src/worker-wrapper.mjs dist/worker-wrapper.mjs && cp src/emit-worker.mjs dist/emit-worker.mjs",
|
|
65
66
|
"test": "vitest run",
|
|
66
67
|
"test:watch": "vitest",
|
|
67
68
|
"typecheck": "tsc --noEmit"
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Shared, dependency-free implementation of `emitWorker`.
|
|
2
|
+
//
|
|
3
|
+
// Kept as a plain `.mjs` module (no TypeScript) so it can be imported from
|
|
4
|
+
// both the typed TypeScript surface (`src/build.ts`) and the dependency-free
|
|
5
|
+
// CLI binary (`bin/cli.mjs`) without needing a TypeScript loader.
|
|
6
|
+
//
|
|
7
|
+
// Do NOT duplicate this logic elsewhere. The two consumers always stay in
|
|
8
|
+
// sync by importing from here.
|
|
9
|
+
//
|
|
10
|
+
// invariant: no runtime npm deps — see SECURITY-DEPS.md
|
|
11
|
+
|
|
12
|
+
import { copyFile, mkdir, writeFile } from "node:fs/promises";
|
|
13
|
+
import { join, resolve } from "node:path";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Emit a Cloudflare Pages `_worker.js` that wraps the zfb input bundle.
|
|
17
|
+
*
|
|
18
|
+
* Output shape (two files in `outdir`):
|
|
19
|
+
*
|
|
20
|
+
* _worker.js — entry imported by Cloudflare Pages advanced mode
|
|
21
|
+
* _zfb_inner.mjs — the input bundle, copied verbatim
|
|
22
|
+
*
|
|
23
|
+
* @param {{ inputBundlePath: string; outdir: string; workerWrapperSource: string }} input
|
|
24
|
+
* @returns {Promise<{ workerPath: string; innerBundlePath: string }>}
|
|
25
|
+
*/
|
|
26
|
+
export async function emitWorker({ inputBundlePath, outdir, workerWrapperSource }) {
|
|
27
|
+
const outdirAbs = resolve(outdir);
|
|
28
|
+
const inputAbs = resolve(inputBundlePath);
|
|
29
|
+
|
|
30
|
+
await mkdir(outdirAbs, { recursive: true });
|
|
31
|
+
const innerBundlePath = join(outdirAbs, "_zfb_inner.mjs");
|
|
32
|
+
await copyFile(inputAbs, innerBundlePath);
|
|
33
|
+
|
|
34
|
+
const workerPath = join(outdirAbs, "_worker.js");
|
|
35
|
+
await writeFile(workerPath, workerWrapperSource, "utf8");
|
|
36
|
+
|
|
37
|
+
return { workerPath, innerBundlePath };
|
|
38
|
+
}
|
package/src/worker-wrapper.mjs
CHANGED
|
@@ -31,8 +31,7 @@ export const WORKER_WRAPPER_SOURCE = `// AUTO-GENERATED by @takazudo/zfb-adapter
|
|
|
31
31
|
// islands-…">). If we let the inner Hono router handle them first,
|
|
32
32
|
// it would dynamic-SSR the page WITHOUT the prod head injection
|
|
33
33
|
// (which is a build-time post-process, not a runtime concern), and
|
|
34
|
-
// islands would never hydrate.
|
|
35
|
-
// the zudo-doc#1355 zfb-pipeline-gaps epic).
|
|
34
|
+
// islands would never hydrate.
|
|
36
35
|
// - On 404 from ASSETS, fall through to the inner zfb worker. This is
|
|
37
36
|
// where genuinely dynamic routes (\`prerender = false\`, e.g.
|
|
38
37
|
// \`pages/api/*.tsx\`) are served.
|
|
@@ -68,6 +67,16 @@ function isAssetProbeMethod(method) {
|
|
|
68
67
|
export default {
|
|
69
68
|
async fetch(request, env, ctx) {
|
|
70
69
|
if (isAssetProbeMethod(request.method) && canDelegateToAssets(env)) {
|
|
70
|
+
// Trade-off: every GET/HEAD request that matches a prerendered route pays
|
|
71
|
+
// the cost of one env.ASSETS.fetch() round-trip before the inner worker
|
|
72
|
+
// sees it. The upside is that CF Pages' asset server handles trailing-
|
|
73
|
+
// slash canonicalisation (e.g. /docs/foo → 308 → /docs/foo/) and serves
|
|
74
|
+
// the build-time head-injected HTML with the hashed <link>/<script> tags.
|
|
75
|
+
// If we skipped this probe, prerendered routes would be dynamic-SSR'd by
|
|
76
|
+
// the inner Hono router without the prod head injection, and islands would
|
|
77
|
+
// never hydrate. For purely dynamic apps (prerender=false everywhere) the
|
|
78
|
+
// extra round-trip is pure overhead; splitting the wrapper into two
|
|
79
|
+
// variants is the accepted future escape hatch for that case.
|
|
71
80
|
const assetResponse = await env.ASSETS.fetch(request);
|
|
72
81
|
if (assetResponse.status !== 404) {
|
|
73
82
|
return assetResponse;
|