@opennextjs/cloudflare 1.0.2 → 1.0.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/dist/api/config.d.ts +7 -0
- package/dist/api/durable-objects/queue.js +12 -8
- package/dist/api/durable-objects/queue.spec.js +0 -3
- package/dist/cli/build/open-next/createServerBundle.js +2 -0
- package/dist/cli/build/patches/plugins/instrumentation.js +2 -1
- package/dist/cli/build/patches/plugins/use-cache.d.ts +3 -0
- package/dist/cli/build/patches/plugins/use-cache.js +40 -0
- package/dist/cli/build/patches/plugins/use-cache.spec.d.ts +1 -0
- package/dist/cli/build/patches/plugins/use-cache.spec.js +156 -0
- package/dist/cli/build/patches/plugins/wrangler-external.js +2 -1
- package/dist/cli/build/utils/ensure-cf-config.js +7 -2
- package/dist/cli/commands/populate-cache.js +8 -3
- package/package.json +3 -3
package/dist/api/config.d.ts
CHANGED
|
@@ -46,6 +46,13 @@ interface OpenNextConfig extends AwsOpenNextConfig {
|
|
|
46
46
|
* @default true
|
|
47
47
|
*/
|
|
48
48
|
useWorkerdCondition?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Disable throwing an error when the config validation fails.
|
|
51
|
+
* This is useful for overriding some of the default provided by cloudflare.
|
|
52
|
+
* **USE AT YOUR OWN RISK**
|
|
53
|
+
* @default false
|
|
54
|
+
*/
|
|
55
|
+
dangerousDisableConfigValidation?: boolean;
|
|
49
56
|
};
|
|
50
57
|
}
|
|
51
58
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { debug, error } from "@opennextjs/aws/adapters/logger.js";
|
|
1
|
+
import { debug, error, warn } from "@opennextjs/aws/adapters/logger.js";
|
|
2
2
|
import { FatalError, IgnorableError, isOpenNextError, RecoverableError, } from "@opennextjs/aws/utils/error.js";
|
|
3
3
|
import { DurableObject } from "cloudflare:workers";
|
|
4
4
|
const DEFAULT_MAX_REVALIDATION = 5;
|
|
@@ -47,6 +47,9 @@ export class DOQueueHandler extends DurableObject {
|
|
|
47
47
|
debug(`Durable object initialized`);
|
|
48
48
|
}
|
|
49
49
|
async revalidate(msg) {
|
|
50
|
+
if (this.ongoingRevalidations.size > 2 * this.maxRevalidations) {
|
|
51
|
+
warn(`Your durable object has 2 times the maximum number of revalidations (${this.maxRevalidations}) in progress. If this happens often, you should consider increasing the NEXT_CACHE_DO_QUEUE_MAX_REVALIDATION or the number of durable objects with the MAX_REVALIDATE_CONCURRENCY env var.`);
|
|
52
|
+
}
|
|
50
53
|
// If there is already an ongoing revalidation, we don't need to revalidate again
|
|
51
54
|
if (this.ongoingRevalidations.has(msg.MessageDeduplicationId))
|
|
52
55
|
return;
|
|
@@ -59,19 +62,18 @@ export class DOQueueHandler extends DurableObject {
|
|
|
59
62
|
return;
|
|
60
63
|
if (this.ongoingRevalidations.size >= this.maxRevalidations) {
|
|
61
64
|
debug(`The maximum number of revalidations (${this.maxRevalidations}) is reached. Blocking until one of the revalidations finishes.`);
|
|
62
|
-
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
// TODO: need more investigation
|
|
66
|
+
// We don't use `blockConcurrencyWhile` here because it block the whole durable object for 30 seconds
|
|
67
|
+
// if we exceed the max revalidations too fast
|
|
68
|
+
while (this.ongoingRevalidations.size >= this.maxRevalidations) {
|
|
69
|
+
const ongoingRevalidations = this.ongoingRevalidations.values();
|
|
67
70
|
debug(`Waiting for one of the revalidations to finish`);
|
|
68
71
|
await Promise.race(ongoingRevalidations);
|
|
69
|
-
}
|
|
72
|
+
}
|
|
70
73
|
}
|
|
71
74
|
const revalidationPromise = this.executeRevalidation(msg);
|
|
72
75
|
// We store the promise to dedupe the revalidation
|
|
73
76
|
this.ongoingRevalidations.set(msg.MessageDeduplicationId, revalidationPromise);
|
|
74
|
-
// TODO: check if the object stays up during waitUntil so that the internal state is maintained
|
|
75
77
|
this.ctx.waitUntil(revalidationPromise);
|
|
76
78
|
}
|
|
77
79
|
async executeRevalidation(msg) {
|
|
@@ -86,6 +88,7 @@ export class DOQueueHandler extends DurableObject {
|
|
|
86
88
|
"x-prerender-revalidate": process.env.__NEXT_PREVIEW_MODE_ID,
|
|
87
89
|
"x-isr": "1",
|
|
88
90
|
},
|
|
91
|
+
// This one is kind of problematic, it will always show the wall time of the revalidation to `this.revalidationTimeout`
|
|
89
92
|
signal: AbortSignal.timeout(this.revalidationTimeout),
|
|
90
93
|
});
|
|
91
94
|
// Now we need to handle errors from the fetch
|
|
@@ -202,6 +205,7 @@ export class DOQueueHandler extends DurableObject {
|
|
|
202
205
|
// We create the sync table to handle eventually consistent incremental cache
|
|
203
206
|
this.sql.exec("CREATE TABLE IF NOT EXISTS sync (id TEXT PRIMARY KEY, lastSuccess INTEGER, buildId TEXT)");
|
|
204
207
|
// Before doing anything else, we clear the DB for any potential old data
|
|
208
|
+
// TODO: extract this to a function so that it could be called by the user at another time than init
|
|
205
209
|
this.sql.exec("DELETE FROM failed_state WHERE buildId != ?", process.env.__NEXT_BUILD_ID);
|
|
206
210
|
this.sql.exec("DELETE FROM sync WHERE buildId != ?", process.env.__NEXT_BUILD_ID);
|
|
207
211
|
const failedStateCursor = this.sql.exec("SELECT * FROM failed_state");
|
|
@@ -82,9 +82,6 @@ describe("DurableObjectQueue", () => {
|
|
|
82
82
|
expect(queue.ongoingRevalidations.size).toBe(queue.maxRevalidations);
|
|
83
83
|
expect(queue.ongoingRevalidations.has("id6")).toBe(false);
|
|
84
84
|
expect(Array.from(queue.ongoingRevalidations.keys())).toEqual(["id", "id2", "id3", "id4", "id5"]);
|
|
85
|
-
// BlockConcurrencyWhile is called twice here, first time during creation of the object and second time when we try to revalidate
|
|
86
|
-
// @ts-expect-error
|
|
87
|
-
expect(queue.ctx.blockConcurrencyWhile).toHaveBeenCalledTimes(2);
|
|
88
85
|
// Here we await the blocked request to ensure it's resolved
|
|
89
86
|
await blockedReq;
|
|
90
87
|
// We then need to await for the actual revalidation to finish
|
|
@@ -22,6 +22,7 @@ import { openNextResolvePlugin } from "@opennextjs/aws/plugins/resolve.js";
|
|
|
22
22
|
import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js";
|
|
23
23
|
import { getOpenNextConfig } from "../../../api/config.js";
|
|
24
24
|
import { patchResRevalidate } from "../patches/plugins/res-revalidate.js";
|
|
25
|
+
import { patchUseCacheIO } from "../patches/plugins/use-cache.js";
|
|
25
26
|
import { normalizePath } from "../utils/index.js";
|
|
26
27
|
import { copyWorkerdPackages } from "../utils/workerd.js";
|
|
27
28
|
export async function createServerBundle(options, codeCustomization) {
|
|
@@ -141,6 +142,7 @@ async function generateBundle(name, options, fnOptions, codeCustomization) {
|
|
|
141
142
|
patchBackgroundRevalidation,
|
|
142
143
|
// Cloudflare specific patches
|
|
143
144
|
patchResRevalidate,
|
|
145
|
+
patchUseCacheIO,
|
|
144
146
|
...additionalCodePatches,
|
|
145
147
|
]);
|
|
146
148
|
// Build Lambda code
|
|
@@ -2,6 +2,7 @@ import { existsSync } from "node:fs";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { getPackagePath } from "@opennextjs/aws/build/helper.js";
|
|
4
4
|
import { patchCode } from "@opennextjs/aws/build/patch/astCodePatcher.js";
|
|
5
|
+
import { normalizePath } from "../../utils/normalize-path.js";
|
|
5
6
|
export function patchInstrumentation(updater, buildOpts) {
|
|
6
7
|
const builtInstrumentationPath = getBuiltInstrumentationPath(buildOpts);
|
|
7
8
|
updater.updateContent("patch-instrumentation-next15", [
|
|
@@ -69,7 +70,7 @@ export function getNext14Rule(builtInstrumentationPath) {
|
|
|
69
70
|
function getBuiltInstrumentationPath(buildOpts) {
|
|
70
71
|
const { outputDir } = buildOpts;
|
|
71
72
|
const maybeBuiltInstrumentationPath = join(outputDir, "server-functions/default", getPackagePath(buildOpts), `.next/server/${INSTRUMENTATION_HOOK_FILENAME}.js`);
|
|
72
|
-
return existsSync(maybeBuiltInstrumentationPath) ? maybeBuiltInstrumentationPath : null;
|
|
73
|
+
return existsSync(maybeBuiltInstrumentationPath) ? normalizePath(maybeBuiltInstrumentationPath) : null;
|
|
73
74
|
}
|
|
74
75
|
/**
|
|
75
76
|
* Pattern to detect instrumentation hooks file
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { CodePatcher } from "@opennextjs/aws/build/patch/codePatcher.js";
|
|
2
|
+
export declare const rule = "\nrule:\n kind: if_statement\n inside:\n kind: function_declaration\n stopBy: end\n has:\n kind: identifier\n pattern: createSnapshot\nfix:\n '// Ignored snapshot'\n";
|
|
3
|
+
export declare const patchUseCacheIO: CodePatcher;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This patch will replace the createSnapshot function in the
|
|
3
|
+
* server/app-render/async-local-storage.js file to an empty string.
|
|
4
|
+
* This is necessary because the createSnapshot function is causing I/O issues for
|
|
5
|
+
* ISR/SSG revalidation in Cloudflare Workers.
|
|
6
|
+
* This is because by default it will use AsyncLocalStorage.snapshot() and it will
|
|
7
|
+
* bind everything to the initial request context.
|
|
8
|
+
* The downsides is that use cache function will have access to the full request
|
|
9
|
+
* ALS context from next (i.e. cookies, headers ...)
|
|
10
|
+
* TODO: Find a better fix for this issue.
|
|
11
|
+
*/
|
|
12
|
+
import { patchCode } from "@opennextjs/aws/build/patch/astCodePatcher.js";
|
|
13
|
+
import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js";
|
|
14
|
+
export const rule = `
|
|
15
|
+
rule:
|
|
16
|
+
kind: if_statement
|
|
17
|
+
inside:
|
|
18
|
+
kind: function_declaration
|
|
19
|
+
stopBy: end
|
|
20
|
+
has:
|
|
21
|
+
kind: identifier
|
|
22
|
+
pattern: createSnapshot
|
|
23
|
+
fix:
|
|
24
|
+
'// Ignored snapshot'
|
|
25
|
+
`;
|
|
26
|
+
export const patchUseCacheIO = {
|
|
27
|
+
name: "patch-use-cache",
|
|
28
|
+
patches: [
|
|
29
|
+
{
|
|
30
|
+
versions: ">=15.3.1",
|
|
31
|
+
field: {
|
|
32
|
+
pathFilter: getCrossPlatformPathRegex(String.raw `server/app-render/async-local-storage\.js$`, {
|
|
33
|
+
escape: false,
|
|
34
|
+
}),
|
|
35
|
+
contentFilter: /createSnapshot/,
|
|
36
|
+
patchCode: async ({ code }) => patchCode(code, rule),
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { patchCode } from "@opennextjs/aws/build/patch/astCodePatcher.js";
|
|
2
|
+
import { expect, test } from "vitest";
|
|
3
|
+
import { rule } from "./use-cache.js";
|
|
4
|
+
const codeToPatch = `"use strict";
|
|
5
|
+
Object.defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
0 && (module.exports = {
|
|
9
|
+
bindSnapshot: null,
|
|
10
|
+
createAsyncLocalStorage: null,
|
|
11
|
+
createSnapshot: null
|
|
12
|
+
});
|
|
13
|
+
function _export(target, all) {
|
|
14
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
get: all[name]
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
_export(exports, {
|
|
20
|
+
bindSnapshot: function() {
|
|
21
|
+
return bindSnapshot;
|
|
22
|
+
},
|
|
23
|
+
createAsyncLocalStorage: function() {
|
|
24
|
+
return createAsyncLocalStorage;
|
|
25
|
+
},
|
|
26
|
+
createSnapshot: function() {
|
|
27
|
+
return createSnapshot;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
const sharedAsyncLocalStorageNotAvailableError = Object.defineProperty(new Error('Invariant: AsyncLocalStorage accessed in runtime where it is not available'), "__NEXT_ERROR_CODE", {
|
|
31
|
+
value: "E504",
|
|
32
|
+
enumerable: false,
|
|
33
|
+
configurable: true
|
|
34
|
+
});
|
|
35
|
+
class FakeAsyncLocalStorage {
|
|
36
|
+
disable() {
|
|
37
|
+
throw sharedAsyncLocalStorageNotAvailableError;
|
|
38
|
+
}
|
|
39
|
+
getStore() {
|
|
40
|
+
// This fake implementation of AsyncLocalStorage always returns \`undefined\`.
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
run() {
|
|
44
|
+
throw sharedAsyncLocalStorageNotAvailableError;
|
|
45
|
+
}
|
|
46
|
+
exit() {
|
|
47
|
+
throw sharedAsyncLocalStorageNotAvailableError;
|
|
48
|
+
}
|
|
49
|
+
enterWith() {
|
|
50
|
+
throw sharedAsyncLocalStorageNotAvailableError;
|
|
51
|
+
}
|
|
52
|
+
static bind(fn) {
|
|
53
|
+
return fn;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const maybeGlobalAsyncLocalStorage = typeof globalThis !== 'undefined' && globalThis.AsyncLocalStorage;
|
|
57
|
+
function createAsyncLocalStorage() {
|
|
58
|
+
if (maybeGlobalAsyncLocalStorage) {
|
|
59
|
+
return new maybeGlobalAsyncLocalStorage();
|
|
60
|
+
}
|
|
61
|
+
return new FakeAsyncLocalStorage();
|
|
62
|
+
}
|
|
63
|
+
function bindSnapshot(fn) {
|
|
64
|
+
if (maybeGlobalAsyncLocalStorage) {
|
|
65
|
+
return maybeGlobalAsyncLocalStorage.bind(fn);
|
|
66
|
+
}
|
|
67
|
+
return FakeAsyncLocalStorage.bind(fn);
|
|
68
|
+
}
|
|
69
|
+
function createSnapshot() {
|
|
70
|
+
if (maybeGlobalAsyncLocalStorage) {
|
|
71
|
+
return maybeGlobalAsyncLocalStorage.snapshot();
|
|
72
|
+
}
|
|
73
|
+
return function(fn, ...args) {
|
|
74
|
+
return fn(...args);
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
//# sourceMappingURL=async-local-storage.js.map
|
|
79
|
+
`;
|
|
80
|
+
test("patch the createSnapshot function", () => {
|
|
81
|
+
const patchedCode = patchCode(codeToPatch, rule);
|
|
82
|
+
expect(patchedCode).toMatchInlineSnapshot(`""use strict";
|
|
83
|
+
Object.defineProperty(exports, "__esModule", {
|
|
84
|
+
value: true
|
|
85
|
+
});
|
|
86
|
+
0 && (module.exports = {
|
|
87
|
+
bindSnapshot: null,
|
|
88
|
+
createAsyncLocalStorage: null,
|
|
89
|
+
createSnapshot: null
|
|
90
|
+
});
|
|
91
|
+
function _export(target, all) {
|
|
92
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
93
|
+
enumerable: true,
|
|
94
|
+
get: all[name]
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
_export(exports, {
|
|
98
|
+
bindSnapshot: function() {
|
|
99
|
+
return bindSnapshot;
|
|
100
|
+
},
|
|
101
|
+
createAsyncLocalStorage: function() {
|
|
102
|
+
return createAsyncLocalStorage;
|
|
103
|
+
},
|
|
104
|
+
createSnapshot: function() {
|
|
105
|
+
return createSnapshot;
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
const sharedAsyncLocalStorageNotAvailableError = Object.defineProperty(new Error('Invariant: AsyncLocalStorage accessed in runtime where it is not available'), "__NEXT_ERROR_CODE", {
|
|
109
|
+
value: "E504",
|
|
110
|
+
enumerable: false,
|
|
111
|
+
configurable: true
|
|
112
|
+
});
|
|
113
|
+
class FakeAsyncLocalStorage {
|
|
114
|
+
disable() {
|
|
115
|
+
throw sharedAsyncLocalStorageNotAvailableError;
|
|
116
|
+
}
|
|
117
|
+
getStore() {
|
|
118
|
+
// This fake implementation of AsyncLocalStorage always returns \`undefined\`.
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
run() {
|
|
122
|
+
throw sharedAsyncLocalStorageNotAvailableError;
|
|
123
|
+
}
|
|
124
|
+
exit() {
|
|
125
|
+
throw sharedAsyncLocalStorageNotAvailableError;
|
|
126
|
+
}
|
|
127
|
+
enterWith() {
|
|
128
|
+
throw sharedAsyncLocalStorageNotAvailableError;
|
|
129
|
+
}
|
|
130
|
+
static bind(fn) {
|
|
131
|
+
return fn;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const maybeGlobalAsyncLocalStorage = typeof globalThis !== 'undefined' && globalThis.AsyncLocalStorage;
|
|
135
|
+
function createAsyncLocalStorage() {
|
|
136
|
+
if (maybeGlobalAsyncLocalStorage) {
|
|
137
|
+
return new maybeGlobalAsyncLocalStorage();
|
|
138
|
+
}
|
|
139
|
+
return new FakeAsyncLocalStorage();
|
|
140
|
+
}
|
|
141
|
+
function bindSnapshot(fn) {
|
|
142
|
+
if (maybeGlobalAsyncLocalStorage) {
|
|
143
|
+
return maybeGlobalAsyncLocalStorage.bind(fn);
|
|
144
|
+
}
|
|
145
|
+
return FakeAsyncLocalStorage.bind(fn);
|
|
146
|
+
}
|
|
147
|
+
function createSnapshot() {
|
|
148
|
+
// Ignored snapshot
|
|
149
|
+
return function(fn, ...args) {
|
|
150
|
+
return fn(...args);
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
//# sourceMappingURL=async-local-storage.js.map
|
|
155
|
+
"`);
|
|
156
|
+
});
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* ref: https://developers.cloudflare.com/workers/wrangler/bundling/
|
|
15
15
|
*/
|
|
16
16
|
import { dirname, resolve } from "node:path";
|
|
17
|
+
import { normalizePath } from "../../utils/normalize-path.js";
|
|
17
18
|
export function setWranglerExternal() {
|
|
18
19
|
return {
|
|
19
20
|
name: "wrangler-externals",
|
|
@@ -22,7 +23,7 @@ export function setWranglerExternal() {
|
|
|
22
23
|
//TODO: Ideally in the future we would like to analyze the files in case they are using wasm in a Node way (i.e. WebAssembly.instantiate)
|
|
23
24
|
build.onResolve({ filter: /(\.bin|\.wasm(\?module)?)$/ }, ({ path, importer }) => {
|
|
24
25
|
return {
|
|
25
|
-
path: resolve(dirname(importer), path),
|
|
26
|
+
path: normalizePath(resolve(dirname(importer), path)),
|
|
26
27
|
namespace,
|
|
27
28
|
external: true,
|
|
28
29
|
};
|
|
@@ -28,7 +28,7 @@ export function ensureCloudflareConfig(config) {
|
|
|
28
28
|
logger.warn("The direct mode queue is not recommended for use in production.");
|
|
29
29
|
}
|
|
30
30
|
if (Object.values(requirements).some((satisfied) => !satisfied)) {
|
|
31
|
-
|
|
31
|
+
const errorMessage = "The `open-next.config.ts` should have a default export like this:\n\n" +
|
|
32
32
|
`{
|
|
33
33
|
default: {
|
|
34
34
|
override: {
|
|
@@ -52,6 +52,11 @@ export function ensureCloudflareConfig(config) {
|
|
|
52
52
|
queue: "dummy" | "direct" | function,
|
|
53
53
|
},
|
|
54
54
|
},
|
|
55
|
-
}\n\n`.replace(/^ {8}/gm, "")
|
|
55
|
+
}\n\n`.replace(/^ {8}/gm, "");
|
|
56
|
+
if (config.cloudflare?.dangerousDisableConfigValidation) {
|
|
57
|
+
logger.warn(errorMessage);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
throw new Error(errorMessage);
|
|
56
61
|
}
|
|
57
62
|
}
|
|
@@ -9,6 +9,7 @@ import { BINDING_NAME as R2_CACHE_BINDING_NAME, NAME as R2_CACHE_NAME, PREFIX_EN
|
|
|
9
9
|
import { CACHE_DIR as STATIC_ASSETS_CACHE_DIR, NAME as STATIC_ASSETS_CACHE_NAME, } from "../../api/overrides/incremental-cache/static-assets-incremental-cache.js";
|
|
10
10
|
import { computeCacheKey } from "../../api/overrides/internal.js";
|
|
11
11
|
import { BINDING_NAME as D1_TAG_BINDING_NAME, NAME as D1_TAG_NAME, } from "../../api/overrides/tag-cache/d1-next-tag-cache.js";
|
|
12
|
+
import { normalizePath } from "../build/utils/normalize-path.js";
|
|
12
13
|
import { runWrangler } from "../utils/run-wrangler.js";
|
|
13
14
|
async function resolveCacheName(value) {
|
|
14
15
|
return typeof value === "function" ? (await value()).name : value;
|
|
@@ -20,8 +21,8 @@ export function getCacheAssets(opts) {
|
|
|
20
21
|
}).filter((f) => f.isFile());
|
|
21
22
|
const assets = [];
|
|
22
23
|
for (const file of allFiles) {
|
|
23
|
-
const fullPath = file.
|
|
24
|
-
const relativePath = path.relative(path.join(opts.outputDir, "cache"), fullPath);
|
|
24
|
+
const fullPath = file.fullpath();
|
|
25
|
+
const relativePath = normalizePath(path.relative(path.join(opts.outputDir, "cache"), fullPath));
|
|
25
26
|
if (relativePath.startsWith("__fetch")) {
|
|
26
27
|
const [__fetch, buildId, ...keyParts] = relativePath.split("/");
|
|
27
28
|
if (__fetch !== "__fetch" || buildId === undefined || keyParts.length === 0) {
|
|
@@ -74,7 +75,11 @@ async function populateR2IncrementalCache(options, populateCacheOptions) {
|
|
|
74
75
|
buildId,
|
|
75
76
|
cacheType: isFetch ? "fetch" : "cache",
|
|
76
77
|
});
|
|
77
|
-
runWrangler(options, [
|
|
78
|
+
runWrangler(options, [
|
|
79
|
+
"r2 object put",
|
|
80
|
+
quoteShellMeta(normalizePath(path.join(bucket, cacheKey))),
|
|
81
|
+
`--file ${quoteShellMeta(fullPath)}`,
|
|
82
|
+
],
|
|
78
83
|
// NOTE: R2 does not support the environment flag and results in the following error:
|
|
79
84
|
// Incorrect type for the 'cacheExpiry' field on 'HttpMetadata': the provided value is not of type 'date'.
|
|
80
85
|
{ target: populateCacheOptions.target, logging: "error" });
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opennextjs/cloudflare",
|
|
3
3
|
"description": "Cloudflare builder for next apps",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.4",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"opennextjs-cloudflare": "dist/cli/index.js"
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"homepage": "https://github.com/opennextjs/opennextjs-cloudflare",
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@dotenvx/dotenvx": "1.31.0",
|
|
46
|
-
"@opennextjs/aws": "^3.6.
|
|
46
|
+
"@opennextjs/aws": "^3.6.2",
|
|
47
47
|
"enquirer": "^2.4.1",
|
|
48
48
|
"glob": "^11.0.0",
|
|
49
49
|
"ts-tqdm": "^0.8.6"
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"@tsconfig/strictest": "^2.0.5",
|
|
55
55
|
"@types/mock-fs": "^4.13.4",
|
|
56
56
|
"@types/node": "^22.2.0",
|
|
57
|
-
"esbuild": "^0.
|
|
57
|
+
"esbuild": "^0.25.4",
|
|
58
58
|
"eslint": "^9.11.1",
|
|
59
59
|
"eslint-plugin-import": "^2.31.0",
|
|
60
60
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|