@edgeone/opennextjs-pages 0.2.6 → 0.2.8-beta.1
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/build/routes.js +5 -0
- package/dist/build/tag-manifest.js +57 -0
- package/dist/run/handlers/request-context.cjs +9 -0
- package/dist/run/handlers/server.js +3 -1
- package/dist/run/handlers/tag-manifest.cjs +91 -0
- package/dist/run/handlers/tags-handler.cjs +46 -17
- package/dist/run/headers.js +6 -1
- package/package.json +1 -1
package/dist/build/routes.js
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
convertHeaders,
|
|
16
16
|
convertTrailingSlash
|
|
17
17
|
} from "./route-utils.js";
|
|
18
|
+
import { generateTagManifest } from "./tag-manifest.js";
|
|
18
19
|
function hasAppRouter(appPathRoutesManifest) {
|
|
19
20
|
return appPathRoutesManifest && Object.keys(appPathRoutesManifest).length > 0;
|
|
20
21
|
}
|
|
@@ -372,6 +373,10 @@ var createRouteMeta = async (ctx) => {
|
|
|
372
373
|
"utf-8"
|
|
373
374
|
);
|
|
374
375
|
console.log(`[opennext] Generated ${configFilePath} with ${routes.length} routes`);
|
|
376
|
+
const tagManifest = generateTagManifest(prerenderManifest, ctx.publishDir);
|
|
377
|
+
const tagManifestPath = path.join(serverHandlerDir, "tag-manifest.json");
|
|
378
|
+
fs.writeFileSync(tagManifestPath, JSON.stringify(tagManifest, null, 2), "utf-8");
|
|
379
|
+
console.log(`[opennext] Generated ${tagManifestPath} with ${Object.keys(tagManifest.tags).length} tags`);
|
|
375
380
|
const middlewareConfig = await getMiddlewareConfig(ctx);
|
|
376
381
|
updateEdgeFunctionsConfigJson(middlewareConfig);
|
|
377
382
|
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
|
|
2
|
+
var require = await (async () => {
|
|
3
|
+
var { createRequire } = await import("node:module");
|
|
4
|
+
return createRequire(import.meta.url);
|
|
5
|
+
})();
|
|
6
|
+
|
|
7
|
+
import "../esm-chunks/chunk-6BT4RYQJ.js";
|
|
8
|
+
|
|
9
|
+
// src/build/tag-manifest.ts
|
|
10
|
+
import * as fs from "fs";
|
|
11
|
+
import * as path from "path";
|
|
12
|
+
function generateTagManifest(prerenderManifest, publishDir) {
|
|
13
|
+
const tags = {};
|
|
14
|
+
if (!prerenderManifest?.routes) {
|
|
15
|
+
return { version: 1, tags };
|
|
16
|
+
}
|
|
17
|
+
const routes = prerenderManifest.routes;
|
|
18
|
+
for (const [routePath, routeConfig] of Object.entries(routes)) {
|
|
19
|
+
if (typeof routeConfig.initialRevalidateSeconds !== "number") {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
const cacheTags = getCacheTagsForRoute(routePath, routeConfig, publishDir);
|
|
23
|
+
if (!cacheTags) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
const tagList = cacheTags.split(",").map((tag) => tag.trim().replace(/^_N_T_/, "")).filter((tag) => tag.length > 0);
|
|
27
|
+
for (const tag of tagList) {
|
|
28
|
+
if (!tags[tag]) {
|
|
29
|
+
tags[tag] = [];
|
|
30
|
+
}
|
|
31
|
+
if (!tags[tag].includes(routePath)) {
|
|
32
|
+
tags[tag].push(routePath);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return { version: 1, tags };
|
|
37
|
+
}
|
|
38
|
+
function getCacheTagsForRoute(routePath, routeConfig, publishDir) {
|
|
39
|
+
const metaFileName = routePath === "/" ? "index.meta" : `${routePath.slice(1)}.meta`;
|
|
40
|
+
const metaFilePath = path.join(publishDir, "server", "app", metaFileName);
|
|
41
|
+
if (fs.existsSync(metaFilePath)) {
|
|
42
|
+
try {
|
|
43
|
+
const metaContent = fs.readFileSync(metaFilePath, "utf-8");
|
|
44
|
+
const meta = JSON.parse(metaContent);
|
|
45
|
+
const tags = meta?.headers?.["x-next-cache-tags"];
|
|
46
|
+
if (tags) {
|
|
47
|
+
return tags;
|
|
48
|
+
}
|
|
49
|
+
} catch (err) {
|
|
50
|
+
console.warn(`[opennext] Warning: Failed to parse ${metaFilePath}: ${err}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return routeConfig.initialHeaders?.["x-next-cache-tags"] ?? null;
|
|
54
|
+
}
|
|
55
|
+
export {
|
|
56
|
+
generateTagManifest
|
|
57
|
+
};
|
|
@@ -99,12 +99,21 @@ var systemLogger = new SystemLogger();
|
|
|
99
99
|
var REQUEST_CONTEXT_GLOBAL_KEY = Symbol.for("nf-request-context-async-local-storage");
|
|
100
100
|
var REQUEST_COUNTER_KEY = Symbol.for("nf-request-counter");
|
|
101
101
|
var extendedGlobalThis = globalThis;
|
|
102
|
+
function getHostFromRequest(request) {
|
|
103
|
+
if (!request?.headers) return void 0;
|
|
104
|
+
const headers = request.headers;
|
|
105
|
+
if (typeof headers.get === "function") {
|
|
106
|
+
return headers.get("eo-pages-host") || headers.get("host") || void 0;
|
|
107
|
+
}
|
|
108
|
+
return headers["eo-pages-host"] || headers["host"] || void 0;
|
|
109
|
+
}
|
|
102
110
|
function createRequestContext(request, context) {
|
|
103
111
|
const backgroundWorkPromises = [];
|
|
104
112
|
const logger = systemLogger.withLogLevel(LogLevel.Log);
|
|
105
113
|
return {
|
|
106
114
|
isBackgroundRevalidation: false,
|
|
107
115
|
captureServerTiming: false,
|
|
116
|
+
host: getHostFromRequest(request),
|
|
108
117
|
trackBackgroundWork: (promise) => {
|
|
109
118
|
if (context?.waitUntil) {
|
|
110
119
|
context.waitUntil(promise);
|
|
@@ -3094,7 +3094,8 @@ function toComputeResponse(res) {
|
|
|
3094
3094
|
import { getRunConfig, setRunConfig } from "../config.js";
|
|
3095
3095
|
import {
|
|
3096
3096
|
setCacheControlHeaders,
|
|
3097
|
-
setCacheStatusHeader
|
|
3097
|
+
setCacheStatusHeader,
|
|
3098
|
+
setCacheTagsHeaders
|
|
3098
3099
|
} from "../headers.js";
|
|
3099
3100
|
import { nextResponseProxy } from "../revalidate.js";
|
|
3100
3101
|
import { setFetchBeforeNextPatchedIt } from "../storage/storage.cjs";
|
|
@@ -3235,6 +3236,7 @@ var server_default = async (request, _context, topLevelSpan, requestContext) =>
|
|
|
3235
3236
|
topLevelSpan.setAttribute("responseCacheKey", requestContext.responseCacheKey);
|
|
3236
3237
|
}
|
|
3237
3238
|
setCacheControlHeaders(response, request, requestContext);
|
|
3239
|
+
setCacheTagsHeaders(response.headers, requestContext);
|
|
3238
3240
|
setCacheStatusHeader(response.headers, null);
|
|
3239
3241
|
if (requestContext.isCacheableAppPage && response.status !== 304) {
|
|
3240
3242
|
const isRSCRequest = request.headers.get("rsc") === "1";
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/run/handlers/tag-manifest.cts
|
|
31
|
+
var tag_manifest_exports = {};
|
|
32
|
+
__export(tag_manifest_exports, {
|
|
33
|
+
expandTagsToPaths: () => expandTagsToPaths
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(tag_manifest_exports);
|
|
36
|
+
var fs = __toESM(require("fs"));
|
|
37
|
+
var path = __toESM(require("path"));
|
|
38
|
+
var cachedManifest = null;
|
|
39
|
+
function loadManifest() {
|
|
40
|
+
if (cachedManifest) {
|
|
41
|
+
return cachedManifest;
|
|
42
|
+
}
|
|
43
|
+
const emptyManifest = { version: 1, tags: {} };
|
|
44
|
+
const candidates = [
|
|
45
|
+
path.resolve(process.cwd(), "tag-manifest.json"),
|
|
46
|
+
path.resolve(__dirname, "..", "..", "tag-manifest.json"),
|
|
47
|
+
// Walk up from __dirname to find tag-manifest.json in SCF bundle root
|
|
48
|
+
...findAncestorPaths(__dirname, "tag-manifest.json")
|
|
49
|
+
];
|
|
50
|
+
for (const candidate of candidates) {
|
|
51
|
+
if (fs.existsSync(candidate)) {
|
|
52
|
+
try {
|
|
53
|
+
const content = fs.readFileSync(candidate, "utf-8");
|
|
54
|
+
cachedManifest = JSON.parse(content);
|
|
55
|
+
return cachedManifest;
|
|
56
|
+
} catch {
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
cachedManifest = emptyManifest;
|
|
61
|
+
return cachedManifest;
|
|
62
|
+
}
|
|
63
|
+
function findAncestorPaths(startDir, filename) {
|
|
64
|
+
const paths = [];
|
|
65
|
+
let dir = startDir;
|
|
66
|
+
for (let i = 0; i < 10; i++) {
|
|
67
|
+
const parent = path.dirname(dir);
|
|
68
|
+
if (parent === dir) break;
|
|
69
|
+
dir = parent;
|
|
70
|
+
paths.push(path.join(dir, filename));
|
|
71
|
+
}
|
|
72
|
+
return paths;
|
|
73
|
+
}
|
|
74
|
+
function expandTagsToPaths(tags) {
|
|
75
|
+
const manifest = loadManifest();
|
|
76
|
+
const paths = /* @__PURE__ */ new Set();
|
|
77
|
+
for (const tag of tags) {
|
|
78
|
+
if (manifest.tags[tag]) {
|
|
79
|
+
for (const p of manifest.tags[tag]) {
|
|
80
|
+
paths.add(p);
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
paths.add(tag);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return Array.from(paths);
|
|
87
|
+
}
|
|
88
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
89
|
+
0 && (module.exports = {
|
|
90
|
+
expandTagsToPaths
|
|
91
|
+
});
|
|
@@ -28,10 +28,11 @@ module.exports = __toCommonJS(tags_handler_exports);
|
|
|
28
28
|
|
|
29
29
|
// package.json
|
|
30
30
|
var name = "@edgeone/opennextjs-pages";
|
|
31
|
-
var version = "0.2.
|
|
31
|
+
var version = "0.2.8-beta.1";
|
|
32
32
|
|
|
33
33
|
// src/run/handlers/tags-handler.cts
|
|
34
34
|
var import_request_context = require("./request-context.cjs");
|
|
35
|
+
var import_tag_manifest = require("./tag-manifest.cjs");
|
|
35
36
|
var purgeCacheUserAgent = `${name}@${version}`;
|
|
36
37
|
function isAnyTagStale(tags, timestamp) {
|
|
37
38
|
return Promise.resolve(false);
|
|
@@ -44,24 +45,52 @@ async function purgeEdgeCache(tagOrTags) {
|
|
|
44
45
|
return tag.replace(/^_N_T_/, "").replace(/\/page|\/layout$/, "");
|
|
45
46
|
});
|
|
46
47
|
if (tags.length === 0) {
|
|
47
|
-
return
|
|
48
|
+
return;
|
|
48
49
|
}
|
|
49
|
-
(0,
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
|
|
50
|
+
const paths = (0, import_tag_manifest.expandTagsToPaths)(tags);
|
|
51
|
+
if (paths.length === 0) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const requestContext = (0, import_request_context.getRequestContext)();
|
|
55
|
+
const host = requestContext?.host || process.env.SITE_HOST;
|
|
56
|
+
if (!host) {
|
|
57
|
+
(0, import_request_context.getLogger)().error(`[NextRuntime] Cannot purge: no host available (request context or SITE_HOST env)`);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const fullUrls = paths.map((p) => {
|
|
61
|
+
const normalizedPath = p.startsWith("/") ? p : `/${p}`;
|
|
62
|
+
return `https://${host}${normalizedPath}`;
|
|
62
63
|
});
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
(0, import_request_context.getLogger)().debug(`[NextRuntime] Purging CDN cache for tags [${tags.join(", ")}] \u2192 urls [${fullUrls.join(", ")}]`);
|
|
65
|
+
const baseUrl = process.env.PURGE_API_URL || (process.env.IS_MAINLAND === "true" ? "https://pages-api.cloud.tencent.com" : "https://pages-api.edgeone.ai");
|
|
66
|
+
const token = process.env.PURGE_TOKEN;
|
|
67
|
+
const purgeEndpoint = `${baseUrl}/eo/pages/hook/eo_purge`;
|
|
68
|
+
try {
|
|
69
|
+
const requestBody = {
|
|
70
|
+
RequestId: `purge-${Date.now()}`,
|
|
71
|
+
Token: token,
|
|
72
|
+
paths: fullUrls
|
|
73
|
+
};
|
|
74
|
+
const res = await fetch(purgeEndpoint, {
|
|
75
|
+
method: "POST",
|
|
76
|
+
headers: {
|
|
77
|
+
"Content-Type": "application/json"
|
|
78
|
+
},
|
|
79
|
+
body: JSON.stringify(requestBody)
|
|
80
|
+
});
|
|
81
|
+
const text = await res.text();
|
|
82
|
+
let data;
|
|
83
|
+
try {
|
|
84
|
+
data = JSON.parse(text);
|
|
85
|
+
} catch {
|
|
86
|
+
(0, import_request_context.getLogger)().error(`[NextRuntime] Purge API returned non-JSON (${res.status}): ${text.slice(0, 200)}`);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (data?.error) {
|
|
90
|
+
(0, import_request_context.getLogger)().error(`[NextRuntime] Purge API error: ${JSON.stringify(data.error)}`);
|
|
91
|
+
}
|
|
92
|
+
} catch (error) {
|
|
93
|
+
(0, import_request_context.getLogger)().withError(error).error(`[NextRuntime] Purging CDN cache for [${fullUrls.join(",")}] failed`);
|
|
65
94
|
}
|
|
66
95
|
}
|
|
67
96
|
async function doRevalidateTagAndPurgeEdgeCache(tags) {
|
package/dist/run/headers.js
CHANGED
|
@@ -65,9 +65,14 @@ var setCacheControlHeaders = ({ headers, status }, request, requestContext) => {
|
|
|
65
65
|
}
|
|
66
66
|
};
|
|
67
67
|
var setCacheTagsHeaders = (headers, requestContext) => {
|
|
68
|
-
|
|
68
|
+
const nextCacheTags = headers.get("x-next-cache-tags");
|
|
69
|
+
if (!nextCacheTags) {
|
|
69
70
|
return;
|
|
70
71
|
}
|
|
72
|
+
const tags = nextCacheTags.split(",").map((tag) => tag.trim().replace(/^_N_T_/, "")).filter(Boolean).join(",");
|
|
73
|
+
if (tags) {
|
|
74
|
+
headers.set("cache-tag", tags);
|
|
75
|
+
}
|
|
71
76
|
};
|
|
72
77
|
var setCacheStatusHeader = (headers, nextCache) => {
|
|
73
78
|
if (headers.has("x-nextjs-cache")) {
|