@edgeone/opennextjs-pages 0.2.7 → 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 +17 -37
- 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
|
}
|
|
@@ -119,45 +120,21 @@ function getServerRoutes(staticRoutes, prerenderManifest, basePath = "") {
|
|
|
119
120
|
}
|
|
120
121
|
return routes;
|
|
121
122
|
}
|
|
122
|
-
function
|
|
123
|
+
function getApiRoutes(appPathsManifest, basePath = "") {
|
|
123
124
|
if (!appPathsManifest) {
|
|
124
125
|
return [];
|
|
125
126
|
}
|
|
126
127
|
const routes = [];
|
|
127
|
-
const emitted = /* @__PURE__ */ new Set();
|
|
128
128
|
for (const routePath of Object.keys(appPathsManifest)) {
|
|
129
|
-
if (routePath.
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
} else {
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
140
|
-
pagePath = pagePath.replace(/\/\([^)]+\)/g, "");
|
|
141
|
-
if (!pagePath) pagePath = "/";
|
|
142
|
-
if (pagePath.includes("@")) {
|
|
143
|
-
continue;
|
|
144
|
-
}
|
|
145
|
-
if (pagePath.includes("[")) {
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
148
|
-
if (pagePath === "/") {
|
|
149
|
-
continue;
|
|
150
|
-
}
|
|
151
|
-
if (existingPaths.has(pagePath)) {
|
|
152
|
-
continue;
|
|
153
|
-
}
|
|
154
|
-
if (emitted.has(pagePath)) {
|
|
155
|
-
continue;
|
|
129
|
+
if (routePath.includes("/api/") && routePath.endsWith("/route")) {
|
|
130
|
+
const apiPath = routePath.replace(/\/route$/, "");
|
|
131
|
+
if (apiPath.includes("[")) {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
const escapedPath = apiPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
135
|
+
const src = `^${basePath}${escapedPath}$`;
|
|
136
|
+
routes.push({ src });
|
|
156
137
|
}
|
|
157
|
-
emitted.add(pagePath);
|
|
158
|
-
const escapedPath = pagePath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
159
|
-
const src = `^${basePath}${escapedPath}(?:/)?$`;
|
|
160
|
-
routes.push({ src });
|
|
161
138
|
}
|
|
162
139
|
return routes;
|
|
163
140
|
}
|
|
@@ -369,10 +346,9 @@ var createRouteMeta = async (ctx) => {
|
|
|
369
346
|
if (dataRoutes.length > 0) {
|
|
370
347
|
routes.push(...getDataRoutes(dataRoutes, basePath));
|
|
371
348
|
}
|
|
372
|
-
const
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
routes.push(...appRoutes);
|
|
349
|
+
const apiRoutes = getApiRoutes(appPathsManifest, basePath);
|
|
350
|
+
if (apiRoutes.length > 0) {
|
|
351
|
+
routes.push(...apiRoutes);
|
|
376
352
|
}
|
|
377
353
|
routes.push({
|
|
378
354
|
src: `^${basePath}/.*$`
|
|
@@ -397,6 +373,10 @@ var createRouteMeta = async (ctx) => {
|
|
|
397
373
|
"utf-8"
|
|
398
374
|
);
|
|
399
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`);
|
|
400
380
|
const middlewareConfig = await getMiddlewareConfig(ctx);
|
|
401
381
|
updateEdgeFunctionsConfigJson(middlewareConfig);
|
|
402
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")) {
|