@gigadrive/network-config 3.4.17 → 4.1.0
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/index.d.mts +81 -61
- package/dist/index.d.ts +81 -61
- package/dist/index.js +140 -174
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +140 -174
- package/dist/index.mjs.map +1 -1
- package/dist/nextjs-adapter.d.mts +16 -4
- package/dist/nextjs-adapter.d.ts +17 -3
- package/dist/nextjs-adapter.js +81 -260
- package/dist/nextjs-adapter.js.map +1 -1
- package/dist/nextjs-adapter.mjs +78 -261
- package/dist/nextjs-adapter.mjs.map +1 -1
- package/dist/nextjs-cache-components-handler.js +15 -0
- package/dist/nextjs-cache-components-handler.js.map +1 -1
- package/dist/nextjs-cache-components-handler.mjs +15 -0
- package/dist/nextjs-cache-components-handler.mjs.map +1 -1
- package/dist/nextjs-cache-handler.d.mts +35 -0
- package/dist/nextjs-cache-handler.d.ts +35 -0
- package/dist/nextjs-cache-handler.js +95 -8
- package/dist/nextjs-cache-handler.js.map +1 -1
- package/dist/nextjs-cache-handler.mjs +85 -8
- package/dist/nextjs-cache-handler.mjs.map +1 -1
- package/package.json +3 -8
- package/dist/nextjs-entrypoint-templates/edge-web.mjs +0 -90
- package/dist/nextjs-entrypoint-templates/node-middleware-web.mjs +0 -98
- package/dist/nextjs-entrypoint-templates/node-route.mjs +0 -73
- package/dist/nextjs-ppr-runtime.d.mts +0 -46
- package/dist/nextjs-ppr-runtime.d.ts +0 -46
- package/dist/nextjs-ppr-runtime.js +0 -177
- package/dist/nextjs-ppr-runtime.js.map +0 -1
- package/dist/nextjs-ppr-runtime.mjs +0 -149
- package/dist/nextjs-ppr-runtime.mjs.map +0 -1
|
@@ -8,6 +8,13 @@ var encodeBinary = (value, seen = /* @__PURE__ */ new WeakSet()) => {
|
|
|
8
8
|
if (value instanceof ArrayBuffer) {
|
|
9
9
|
return { __gigadriveType: "bytes", value: Buffer.from(value).toString("base64") };
|
|
10
10
|
}
|
|
11
|
+
if (value instanceof Map) {
|
|
12
|
+
if (seen.has(value)) throw new Error("Next.js cache entry contains a circular value");
|
|
13
|
+
seen.add(value);
|
|
14
|
+
const entries = [...value.entries()].map(([key, item]) => [encodeBinary(key, seen), encodeBinary(item, seen)]);
|
|
15
|
+
seen.delete(value);
|
|
16
|
+
return { __gigadriveType: "map", value: entries };
|
|
17
|
+
}
|
|
11
18
|
if (Array.isArray(value)) return value.map((item) => encodeBinary(item, seen));
|
|
12
19
|
if (typeof value !== "object" || value === null) return value;
|
|
13
20
|
if (seen.has(value)) throw new Error("Next.js cache entry contains a circular value");
|
|
@@ -23,6 +30,14 @@ var decodeBinary = (value) => {
|
|
|
23
30
|
const record = value;
|
|
24
31
|
if (record.__gigadriveType === "bytes" && typeof record.value === "string")
|
|
25
32
|
return Buffer.from(record.value, "base64");
|
|
33
|
+
if (record.__gigadriveType === "map" && Array.isArray(record.value)) {
|
|
34
|
+
return new Map(
|
|
35
|
+
record.value.map((entry) => {
|
|
36
|
+
const [key, item] = entry;
|
|
37
|
+
return [decodeBinary(key), decodeBinary(item)];
|
|
38
|
+
})
|
|
39
|
+
);
|
|
40
|
+
}
|
|
26
41
|
return Object.fromEntries(Object.entries(record).map(([key, item]) => [key, decodeBinary(item)]));
|
|
27
42
|
};
|
|
28
43
|
var getAccessToken = async () => {
|
|
@@ -115,22 +130,84 @@ var getRuntimeCacheTagState = async (kind, tags) => {
|
|
|
115
130
|
};
|
|
116
131
|
|
|
117
132
|
// src/nextjs-cache-handler.ts
|
|
133
|
+
var NEXT_CACHE_TAGS_HEADER = "x-next-cache-tags";
|
|
118
134
|
var isIncrementalCacheEntry = (value) => typeof value === "object" && value !== null && typeof value.lastModified === "number" && "value" in value;
|
|
135
|
+
var valueTags = (value) => {
|
|
136
|
+
const header = value?.headers?.[NEXT_CACHE_TAGS_HEADER];
|
|
137
|
+
if (typeof header !== "string") return [];
|
|
138
|
+
return header.split(",").map((tag) => tag.trim()).filter(Boolean);
|
|
139
|
+
};
|
|
140
|
+
var collectTags = (entryTags, value, context = {}) => [
|
|
141
|
+
.../* @__PURE__ */ new Set([...entryTags ?? [], ...context.tags ?? [], ...context.softTags ?? [], ...valueTags(value)])
|
|
142
|
+
];
|
|
143
|
+
var isBuildPhase = () => process.env.GIGADRIVE_NEXT_BUILD === "1";
|
|
119
144
|
var GigadriveNextCacheHandler = class {
|
|
120
145
|
buildEntries = /* @__PURE__ */ new Map();
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
146
|
+
/**
|
|
147
|
+
* Next's own on-disk reader, pointed at the build output that ships inside the
|
|
148
|
+
* deployment bundle (`writeStandaloneDirectory` recursively copies
|
|
149
|
+
* `.next/server/{app,pages}`). Injecting a `cacheHandler` replaces Next's
|
|
150
|
+
* FileSystemCache outright, which would otherwise strand every build-time
|
|
151
|
+
* prerender and force a cold re-render of each page on first request.
|
|
152
|
+
* Delegating to Next's reader keeps us off its private on-disk format.
|
|
153
|
+
*/
|
|
154
|
+
buildOutputCache;
|
|
155
|
+
context;
|
|
156
|
+
constructor(context = {}) {
|
|
157
|
+
this.context = context;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Loads Next's on-disk reader lazily, memoized across requests. A Next release
|
|
161
|
+
* that moves or renames the module degrades to "no build-output fallback" —
|
|
162
|
+
* the remote cache still serves everything that has been regenerated.
|
|
163
|
+
*/
|
|
164
|
+
loadBuildOutputCache() {
|
|
165
|
+
this.buildOutputCache ??= (async () => {
|
|
166
|
+
if (!this.context.fs || !this.context.serverDistDir) return void 0;
|
|
167
|
+
try {
|
|
168
|
+
const module = await import("next/dist/server/lib/incremental-cache/file-system-cache.js");
|
|
169
|
+
let candidate = module;
|
|
170
|
+
for (let hop = 0; hop < 3 && candidate && typeof candidate !== "function"; hop += 1) {
|
|
171
|
+
candidate = candidate.default;
|
|
172
|
+
}
|
|
173
|
+
if (typeof candidate !== "function") return void 0;
|
|
174
|
+
const FileSystemCache = candidate;
|
|
175
|
+
return new FileSystemCache(this.context);
|
|
176
|
+
} catch {
|
|
177
|
+
return void 0;
|
|
178
|
+
}
|
|
179
|
+
})();
|
|
180
|
+
return this.buildOutputCache;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Applies remote tag state to an entry.
|
|
184
|
+
*
|
|
185
|
+
* @returns the entry, a stale-marked copy, or `null` when a tag expired.
|
|
186
|
+
*/
|
|
187
|
+
async applyTagState(entry, tags) {
|
|
188
|
+
if (tags.length === 0) return entry;
|
|
126
189
|
const tagState = await getRuntimeCacheTagState("incremental", tags);
|
|
127
190
|
if (tagState.expired > entry.lastModified) return null;
|
|
128
191
|
return tagState.stale > entry.lastModified ? { ...entry, lastModified: 1 } : entry;
|
|
129
192
|
}
|
|
193
|
+
async get(key, context = {}) {
|
|
194
|
+
if (isBuildPhase()) {
|
|
195
|
+
const buildEntry = this.buildEntries.get(key) ?? null;
|
|
196
|
+
return isIncrementalCacheEntry(buildEntry) ? buildEntry : null;
|
|
197
|
+
}
|
|
198
|
+
const remote = await readRuntimeCache("incremental", key);
|
|
199
|
+
if (isIncrementalCacheEntry(remote)) {
|
|
200
|
+
return this.applyTagState(remote, collectTags(remote.tags, remote.value, context));
|
|
201
|
+
}
|
|
202
|
+
const buildOutputCache = await this.loadBuildOutputCache();
|
|
203
|
+
const fromDisk = await buildOutputCache?.get(key, context).catch(() => null);
|
|
204
|
+
if (!isIncrementalCacheEntry(fromDisk)) return null;
|
|
205
|
+
return this.applyTagState(fromDisk, collectTags(fromDisk.tags, fromDisk.value, context));
|
|
206
|
+
}
|
|
130
207
|
async set(key, data, context) {
|
|
131
|
-
const tags =
|
|
208
|
+
const tags = collectTags(void 0, data, context);
|
|
132
209
|
const entry = { lastModified: Date.now(), value: data, tags, cacheControl: context.cacheControl };
|
|
133
|
-
if (
|
|
210
|
+
if (isBuildPhase()) {
|
|
134
211
|
this.buildEntries.set(key, entry);
|
|
135
212
|
return;
|
|
136
213
|
}
|
|
@@ -138,7 +215,7 @@ var GigadriveNextCacheHandler = class {
|
|
|
138
215
|
}
|
|
139
216
|
async revalidateTag(tags, durations) {
|
|
140
217
|
const normalizedTags = Array.isArray(tags) ? tags : [tags];
|
|
141
|
-
if (
|
|
218
|
+
if (isBuildPhase()) {
|
|
142
219
|
for (const [key, entry] of this.buildEntries) {
|
|
143
220
|
if (entry.tags?.some((tag) => normalizedTags.includes(tag))) this.buildEntries.delete(key);
|
|
144
221
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/nextjs-runtime-cache-client.ts","../src/nextjs-cache-handler.ts"],"sourcesContent":["interface RuntimeCacheEnvelope {\n value: unknown;\n}\n\ninterface AccessTokenResponse {\n access_token: string;\n expires_in?: number;\n}\n\nexport interface RuntimeCacheTagState {\n stale: number;\n expired: number;\n}\n\nlet cachedToken: { value: string; expiresAt: number } | undefined;\n\nconst apiBaseUrl = (): string => (process.env.GIGADRIVE_API_URL ?? 'https://api.gigadrive.network').replace(/\\/$/, '');\n\nconst encodeBinary = (value: unknown, seen = new WeakSet<object>()): unknown => {\n if (value instanceof Uint8Array) {\n return { __gigadriveType: 'bytes', value: Buffer.from(value).toString('base64') };\n }\n if (value instanceof ArrayBuffer) {\n return { __gigadriveType: 'bytes', value: Buffer.from(value).toString('base64') };\n }\n if (Array.isArray(value)) return value.map((item) => encodeBinary(item, seen));\n if (typeof value !== 'object' || value === null) return value;\n if (seen.has(value)) throw new Error('Next.js cache entry contains a circular value');\n seen.add(value);\n const result: Record<string, unknown> = {};\n for (const [key, item] of Object.entries(value)) result[key] = encodeBinary(item, seen);\n seen.delete(value);\n return result;\n};\n\nconst decodeBinary = (value: unknown): unknown => {\n if (Array.isArray(value)) return value.map(decodeBinary);\n if (typeof value !== 'object' || value === null) return value;\n const record = value as Record<string, unknown>;\n if (record.__gigadriveType === 'bytes' && typeof record.value === 'string')\n return Buffer.from(record.value, 'base64');\n return Object.fromEntries(Object.entries(record).map(([key, item]) => [key, decodeBinary(item)]));\n};\n\nconst getAccessToken = async (): Promise<string> => {\n if (cachedToken && cachedToken.expiresAt > Date.now() + 30_000) return cachedToken.value;\n const clientId = process.env.GIGADRIVE_CLIENT_ID;\n const clientSecret = process.env.GIGADRIVE_CLIENT_SECRET;\n if (!clientId || !clientSecret) throw new Error('Gigadrive workload credentials are unavailable');\n\n const response = await fetch(`${apiBaseUrl()}/oauth2/token`, {\n method: 'POST',\n headers: {\n authorization: `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString('base64')}`,\n 'content-type': 'application/x-www-form-urlencoded',\n },\n body: 'grant_type=client_credentials',\n });\n if (!response.ok) throw new Error(`Gigadrive workload authentication failed with status ${response.status}`);\n const token = (await response.json()) as AccessTokenResponse;\n if (!token.access_token) throw new Error('Gigadrive workload authentication returned no access token');\n cachedToken = {\n value: token.access_token,\n expiresAt: Date.now() + Math.max(60, token.expires_in ?? 300) * 1000,\n };\n return token.access_token;\n};\n\nconst request = async (pathname: string, init: RequestInit, attempt = 0): Promise<Response> => {\n const token = await getAccessToken();\n try {\n const response = await fetch(`${apiBaseUrl()}${pathname}`, {\n ...init,\n headers: {\n authorization: `Bearer ${token}`,\n 'content-type': 'application/json',\n 'x-gigadrive-deployment-id': process.env.GIGADRIVE_DEPLOYMENT_ID ?? process.env.NEBULA_DEPLOYMENT_ID ?? '',\n 'x-gigadrive-build-id': process.env.NEXT_BUILD_ID ?? '',\n ...init.headers,\n },\n });\n if (response.status === 401 && attempt === 0) {\n cachedToken = undefined;\n return request(pathname, init, attempt + 1);\n }\n if (response.status >= 500 && attempt < 2) return request(pathname, init, attempt + 1);\n return response;\n } catch (error) {\n if (attempt < 2) return request(pathname, init, attempt + 1);\n throw error;\n }\n};\n\nconst cachePath = (kind: string, key: string): string =>\n `/internal/runtime-cache/v1/entries/${encodeURIComponent(kind)}/${encodeURIComponent(key)}`;\n\nexport const readRuntimeCache = async (kind: string, key: string): Promise<unknown | undefined> => {\n try {\n const response = await request(cachePath(kind, key), { method: 'GET' });\n if (response.status === 404) return undefined;\n if (!response.ok) return undefined;\n const envelope = (await response.json()) as RuntimeCacheEnvelope;\n return decodeBinary(envelope.value);\n } catch {\n return undefined;\n }\n};\n\nexport const writeRuntimeCache = async (\n kind: string,\n key: string,\n value: unknown,\n tags: readonly string[] = []\n): Promise<void> => {\n const response = await request(cachePath(kind, key), {\n method: 'PUT',\n body: JSON.stringify({ value: encodeBinary(value), tags }),\n });\n if (!response.ok) throw new Error(`Gigadrive runtime cache write failed with status ${response.status}`);\n};\n\nexport const revalidateRuntimeCacheTags = async (\n kind: string,\n tags: readonly string[],\n durations?: { expire?: number }\n): Promise<void> => {\n const response = await request('/internal/runtime-cache/v1/tags/revalidate', {\n method: 'POST',\n body: JSON.stringify({ kind, tags, durations }),\n });\n if (!response.ok) throw new Error(`Gigadrive runtime cache revalidation failed with status ${response.status}`);\n};\n\nexport const getRuntimeCacheTagExpiration = async (kind: string, tags: readonly string[]): Promise<number> => {\n try {\n const response = await request('/internal/runtime-cache/v1/tags/expiration', {\n method: 'POST',\n body: JSON.stringify({ kind, tags }),\n });\n if (!response.ok) return 0;\n const value = (await response.json()) as { expiration?: unknown };\n return typeof value.expiration === 'number' ? value.expiration : 0;\n } catch {\n return 0;\n }\n};\n\nexport const getRuntimeCacheTagState = async (kind: string, tags: readonly string[]): Promise<RuntimeCacheTagState> => {\n try {\n const response = await request('/internal/runtime-cache/v1/tags/state', {\n method: 'POST',\n body: JSON.stringify({ kind, tags }),\n });\n if (!response.ok) return { stale: 0, expired: 0 };\n const value = (await response.json()) as { stale?: unknown; expired?: unknown };\n return {\n stale: typeof value.stale === 'number' ? value.stale : 0,\n expired: typeof value.expired === 'number' ? value.expired : 0,\n };\n } catch {\n return { stale: 0, expired: 0 };\n }\n};\n\nexport const resetRuntimeCacheClientForTests = (): void => {\n cachedToken = undefined;\n};\n","import {\n getRuntimeCacheTagState,\n readRuntimeCache,\n revalidateRuntimeCacheTags,\n writeRuntimeCache,\n} from './nextjs-runtime-cache-client';\n\ninterface IncrementalCacheContext {\n tags?: string[];\n softTags?: string[];\n cacheControl?: unknown;\n}\n\ninterface IncrementalCacheEntry {\n lastModified: number;\n value: unknown | null;\n tags?: string[];\n cacheControl?: unknown;\n}\n\nconst isIncrementalCacheEntry = (value: unknown): value is IncrementalCacheEntry =>\n typeof value === 'object' &&\n value !== null &&\n typeof (value as Partial<IncrementalCacheEntry>).lastModified === 'number' &&\n 'value' in value;\n\n/** Shared Network cache handler for ISR, server responses, fetches, and image metadata. */\nexport default class GigadriveNextCacheHandler {\n private readonly buildEntries = new Map<string, IncrementalCacheEntry>();\n\n async get(key: string, context: IncrementalCacheContext = {}): Promise<IncrementalCacheEntry | null> {\n const entry =\n process.env.GIGADRIVE_NEXT_BUILD === '1'\n ? (this.buildEntries.get(key) ?? null)\n : await readRuntimeCache('incremental', key);\n if (!isIncrementalCacheEntry(entry)) return null;\n const tags = [...new Set([...(entry.tags ?? []), ...(context.tags ?? []), ...(context.softTags ?? [])])];\n if (process.env.GIGADRIVE_NEXT_BUILD === '1' || tags.length === 0) return entry;\n const tagState = await getRuntimeCacheTagState('incremental', tags);\n if (tagState.expired > entry.lastModified) return null;\n return tagState.stale > entry.lastModified ? { ...entry, lastModified: 1 } : entry;\n }\n\n async set(key: string, data: unknown, context: IncrementalCacheContext): Promise<void> {\n const tags = context.tags ?? [];\n const entry = { lastModified: Date.now(), value: data, tags, cacheControl: context.cacheControl };\n if (process.env.GIGADRIVE_NEXT_BUILD === '1') {\n this.buildEntries.set(key, entry);\n return;\n }\n await writeRuntimeCache('incremental', key, entry, tags);\n }\n\n async revalidateTag(tags: string | string[], durations?: { expire?: number }): Promise<void> {\n const normalizedTags = Array.isArray(tags) ? tags : [tags];\n if (process.env.GIGADRIVE_NEXT_BUILD === '1') {\n for (const [key, entry] of this.buildEntries) {\n if (entry.tags?.some((tag) => normalizedTags.includes(tag))) this.buildEntries.delete(key);\n }\n return;\n }\n await revalidateRuntimeCacheTags('incremental', normalizedTags, durations);\n }\n\n resetRequestCache(): void {}\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/nextjs-runtime-cache-client.ts","../src/nextjs-cache-handler.ts"],"sourcesContent":["interface RuntimeCacheEnvelope {\n value: unknown;\n}\n\ninterface AccessTokenResponse {\n access_token: string;\n expires_in?: number;\n}\n\nexport interface RuntimeCacheTagState {\n stale: number;\n expired: number;\n}\n\nlet cachedToken: { value: string; expiresAt: number } | undefined;\n\nconst apiBaseUrl = (): string => (process.env.GIGADRIVE_API_URL ?? 'https://api.gigadrive.network').replace(/\\/$/, '');\n\nconst encodeBinary = (value: unknown, seen = new WeakSet<object>()): unknown => {\n if (value instanceof Uint8Array) {\n return { __gigadriveType: 'bytes', value: Buffer.from(value).toString('base64') };\n }\n if (value instanceof ArrayBuffer) {\n return { __gigadriveType: 'bytes', value: Buffer.from(value).toString('base64') };\n }\n // Next stores App Router RSC segment payloads as a `Map<string, Buffer>`\n // (`segmentData`). `Object.entries` on a Map yields nothing, so without an\n // explicit branch the whole map would serialize to `{}` and every prefetched\n // segment would be silently lost on the next read.\n if (value instanceof Map) {\n if (seen.has(value)) throw new Error('Next.js cache entry contains a circular value');\n seen.add(value);\n const entries = [...value.entries()].map(([key, item]) => [encodeBinary(key, seen), encodeBinary(item, seen)]);\n seen.delete(value);\n return { __gigadriveType: 'map', value: entries };\n }\n if (Array.isArray(value)) return value.map((item) => encodeBinary(item, seen));\n if (typeof value !== 'object' || value === null) return value;\n if (seen.has(value)) throw new Error('Next.js cache entry contains a circular value');\n seen.add(value);\n const result: Record<string, unknown> = {};\n for (const [key, item] of Object.entries(value)) result[key] = encodeBinary(item, seen);\n seen.delete(value);\n return result;\n};\n\nconst decodeBinary = (value: unknown): unknown => {\n if (Array.isArray(value)) return value.map(decodeBinary);\n if (typeof value !== 'object' || value === null) return value;\n const record = value as Record<string, unknown>;\n if (record.__gigadriveType === 'bytes' && typeof record.value === 'string')\n return Buffer.from(record.value, 'base64');\n if (record.__gigadriveType === 'map' && Array.isArray(record.value)) {\n return new Map(\n (record.value as unknown[]).map((entry) => {\n const [key, item] = entry as [unknown, unknown];\n return [decodeBinary(key), decodeBinary(item)];\n })\n );\n }\n return Object.fromEntries(Object.entries(record).map(([key, item]) => [key, decodeBinary(item)]));\n};\n\nconst getAccessToken = async (): Promise<string> => {\n if (cachedToken && cachedToken.expiresAt > Date.now() + 30_000) return cachedToken.value;\n const clientId = process.env.GIGADRIVE_CLIENT_ID;\n const clientSecret = process.env.GIGADRIVE_CLIENT_SECRET;\n if (!clientId || !clientSecret) throw new Error('Gigadrive workload credentials are unavailable');\n\n const response = await fetch(`${apiBaseUrl()}/oauth2/token`, {\n method: 'POST',\n headers: {\n authorization: `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString('base64')}`,\n 'content-type': 'application/x-www-form-urlencoded',\n },\n body: 'grant_type=client_credentials',\n });\n if (!response.ok) throw new Error(`Gigadrive workload authentication failed with status ${response.status}`);\n const token = (await response.json()) as AccessTokenResponse;\n if (!token.access_token) throw new Error('Gigadrive workload authentication returned no access token');\n cachedToken = {\n value: token.access_token,\n expiresAt: Date.now() + Math.max(60, token.expires_in ?? 300) * 1000,\n };\n return token.access_token;\n};\n\nconst request = async (pathname: string, init: RequestInit, attempt = 0): Promise<Response> => {\n const token = await getAccessToken();\n try {\n const response = await fetch(`${apiBaseUrl()}${pathname}`, {\n ...init,\n headers: {\n authorization: `Bearer ${token}`,\n 'content-type': 'application/json',\n 'x-gigadrive-deployment-id': process.env.GIGADRIVE_DEPLOYMENT_ID ?? process.env.NEBULA_DEPLOYMENT_ID ?? '',\n 'x-gigadrive-build-id': process.env.NEXT_BUILD_ID ?? '',\n ...init.headers,\n },\n });\n if (response.status === 401 && attempt === 0) {\n cachedToken = undefined;\n return request(pathname, init, attempt + 1);\n }\n if (response.status >= 500 && attempt < 2) return request(pathname, init, attempt + 1);\n return response;\n } catch (error) {\n if (attempt < 2) return request(pathname, init, attempt + 1);\n throw error;\n }\n};\n\nconst cachePath = (kind: string, key: string): string =>\n `/internal/runtime-cache/v1/entries/${encodeURIComponent(kind)}/${encodeURIComponent(key)}`;\n\nexport const readRuntimeCache = async (kind: string, key: string): Promise<unknown | undefined> => {\n try {\n const response = await request(cachePath(kind, key), { method: 'GET' });\n if (response.status === 404) return undefined;\n if (!response.ok) return undefined;\n const envelope = (await response.json()) as RuntimeCacheEnvelope;\n return decodeBinary(envelope.value);\n } catch {\n return undefined;\n }\n};\n\nexport const writeRuntimeCache = async (\n kind: string,\n key: string,\n value: unknown,\n tags: readonly string[] = []\n): Promise<void> => {\n const response = await request(cachePath(kind, key), {\n method: 'PUT',\n body: JSON.stringify({ value: encodeBinary(value), tags }),\n });\n if (!response.ok) throw new Error(`Gigadrive runtime cache write failed with status ${response.status}`);\n};\n\nexport const revalidateRuntimeCacheTags = async (\n kind: string,\n tags: readonly string[],\n durations?: { expire?: number }\n): Promise<void> => {\n const response = await request('/internal/runtime-cache/v1/tags/revalidate', {\n method: 'POST',\n body: JSON.stringify({ kind, tags, durations }),\n });\n if (!response.ok) throw new Error(`Gigadrive runtime cache revalidation failed with status ${response.status}`);\n};\n\nexport const getRuntimeCacheTagExpiration = async (kind: string, tags: readonly string[]): Promise<number> => {\n try {\n const response = await request('/internal/runtime-cache/v1/tags/expiration', {\n method: 'POST',\n body: JSON.stringify({ kind, tags }),\n });\n if (!response.ok) return 0;\n const value = (await response.json()) as { expiration?: unknown };\n return typeof value.expiration === 'number' ? value.expiration : 0;\n } catch {\n return 0;\n }\n};\n\nexport const getRuntimeCacheTagState = async (kind: string, tags: readonly string[]): Promise<RuntimeCacheTagState> => {\n try {\n const response = await request('/internal/runtime-cache/v1/tags/state', {\n method: 'POST',\n body: JSON.stringify({ kind, tags }),\n });\n if (!response.ok) return { stale: 0, expired: 0 };\n const value = (await response.json()) as { stale?: unknown; expired?: unknown };\n return {\n stale: typeof value.stale === 'number' ? value.stale : 0,\n expired: typeof value.expired === 'number' ? value.expired : 0,\n };\n } catch {\n return { stale: 0, expired: 0 };\n }\n};\n\nexport const resetRuntimeCacheClientForTests = (): void => {\n cachedToken = undefined;\n};\n","import {\n getRuntimeCacheTagState,\n readRuntimeCache,\n revalidateRuntimeCacheTags,\n writeRuntimeCache,\n} from './nextjs-runtime-cache-client';\n\n/** Header Next uses to carry an entry's cache tags (comma separated). */\nconst NEXT_CACHE_TAGS_HEADER = 'x-next-cache-tags';\n\ninterface IncrementalCacheContext {\n tags?: string[];\n softTags?: string[];\n cacheControl?: unknown;\n kind?: string;\n}\n\ninterface IncrementalCacheEntry {\n lastModified: number;\n value: unknown | null;\n tags?: string[];\n cacheControl?: unknown;\n}\n\n/** Constructor context Next passes to a custom cache handler. */\ninterface CacheHandlerConstructorContext {\n fs?: unknown;\n serverDistDir?: string;\n dev?: boolean;\n flushToDisk?: boolean;\n revalidatedTags?: string[];\n maxMemoryCacheSize?: number;\n _requestHeaders?: unknown;\n fetchCacheKeyPrefix?: string;\n}\n\ninterface FileSystemCacheLike {\n get(key: string, context: IncrementalCacheContext): Promise<IncrementalCacheEntry | null | undefined>;\n}\n\nconst isIncrementalCacheEntry = (value: unknown): value is IncrementalCacheEntry =>\n typeof value === 'object' &&\n value !== null &&\n typeof (value as Partial<IncrementalCacheEntry>).lastModified === 'number' &&\n 'value' in value;\n\n/**\n * Reads the cache tags carried by a cached value's own headers.\n *\n * Next never populates `context.tags` for APP_PAGE/APP_ROUTE/PAGES — only fetch\n * cache entries carry tags on the context. For route entries the tags live in\n * the cached value's headers under `x-next-cache-tags` as a comma-joined string,\n * so reading them here is what keeps the remote tag index (and therefore\n * `revalidateTag`) working for pages and route handlers.\n */\nconst valueTags = (value: unknown): string[] => {\n const header = (value as { headers?: Record<string, unknown> } | null)?.headers?.[NEXT_CACHE_TAGS_HEADER];\n if (typeof header !== 'string') return [];\n return header\n .split(',')\n .map((tag) => tag.trim())\n .filter(Boolean);\n};\n\n/** Every tag relevant to an entry: its own, the request's, and the value's headers. */\nconst collectTags = (\n entryTags: string[] | undefined,\n value: unknown,\n context: IncrementalCacheContext = {}\n): string[] => [\n ...new Set([...(entryTags ?? []), ...(context.tags ?? []), ...(context.softTags ?? []), ...valueTags(value)]),\n];\n\nconst isBuildPhase = (): boolean => process.env.GIGADRIVE_NEXT_BUILD === '1';\n\n/** Shared Network cache handler for ISR, server responses, fetches, and image metadata. */\nexport default class GigadriveNextCacheHandler {\n private readonly buildEntries = new Map<string, IncrementalCacheEntry>();\n /**\n * Next's own on-disk reader, pointed at the build output that ships inside the\n * deployment bundle (`writeStandaloneDirectory` recursively copies\n * `.next/server/{app,pages}`). Injecting a `cacheHandler` replaces Next's\n * FileSystemCache outright, which would otherwise strand every build-time\n * prerender and force a cold re-render of each page on first request.\n * Delegating to Next's reader keeps us off its private on-disk format.\n */\n private buildOutputCache?: Promise<FileSystemCacheLike | undefined>;\n private readonly context: CacheHandlerConstructorContext;\n\n constructor(context: CacheHandlerConstructorContext = {}) {\n this.context = context;\n }\n\n /**\n * Loads Next's on-disk reader lazily, memoized across requests. A Next release\n * that moves or renames the module degrades to \"no build-output fallback\" —\n * the remote cache still serves everything that has been regenerated.\n */\n private loadBuildOutputCache(): Promise<FileSystemCacheLike | undefined> {\n this.buildOutputCache ??= (async () => {\n if (!this.context.fs || !this.context.serverDistDir) return undefined;\n try {\n const module: unknown = await import('next/dist/server/lib/incremental-cache/file-system-cache.js');\n // Next ships this as CommonJS, so the constructor sits behind a different\n // number of `default` hops depending on whether this bundle was loaded as\n // CJS or ESM. Unwrap until a callable turns up rather than guessing.\n let candidate: unknown = module;\n for (let hop = 0; hop < 3 && candidate && typeof candidate !== 'function'; hop += 1) {\n candidate = (candidate as { default?: unknown }).default;\n }\n if (typeof candidate !== 'function') return undefined;\n const FileSystemCache = candidate as new (context: CacheHandlerConstructorContext) => FileSystemCacheLike;\n return new FileSystemCache(this.context);\n } catch {\n return undefined;\n }\n })();\n return this.buildOutputCache;\n }\n\n /**\n * Applies remote tag state to an entry.\n *\n * @returns the entry, a stale-marked copy, or `null` when a tag expired.\n */\n private async applyTagState(entry: IncrementalCacheEntry, tags: string[]): Promise<IncrementalCacheEntry | null> {\n if (tags.length === 0) return entry;\n const tagState = await getRuntimeCacheTagState('incremental', tags);\n if (tagState.expired > entry.lastModified) return null;\n return tagState.stale > entry.lastModified ? { ...entry, lastModified: 1 } : entry;\n }\n\n async get(key: string, context: IncrementalCacheContext = {}): Promise<IncrementalCacheEntry | null> {\n if (isBuildPhase()) {\n const buildEntry = this.buildEntries.get(key) ?? null;\n return isIncrementalCacheEntry(buildEntry) ? buildEntry : null;\n }\n\n const remote = await readRuntimeCache('incremental', key);\n if (isIncrementalCacheEntry(remote)) {\n return this.applyTagState(remote, collectTags(remote.tags, remote.value, context));\n }\n\n // Nothing regenerated yet: fall back to the build-time prerender shipped in\n // the bundle. Tag expiry is still resolved against the remote index — Next's\n // reader only knows its own in-process tag manifest, which is always empty\n // here, so trusting it would resurrect content after a `revalidateTag`.\n const buildOutputCache = await this.loadBuildOutputCache();\n const fromDisk = await buildOutputCache?.get(key, context).catch(() => null);\n if (!isIncrementalCacheEntry(fromDisk)) return null;\n return this.applyTagState(fromDisk, collectTags(fromDisk.tags, fromDisk.value, context));\n }\n\n async set(key: string, data: unknown, context: IncrementalCacheContext): Promise<void> {\n const tags = collectTags(undefined, data, context);\n const entry = { lastModified: Date.now(), value: data, tags, cacheControl: context.cacheControl };\n if (isBuildPhase()) {\n this.buildEntries.set(key, entry);\n return;\n }\n await writeRuntimeCache('incremental', key, entry, tags);\n }\n\n async revalidateTag(tags: string | string[], durations?: { expire?: number }): Promise<void> {\n const normalizedTags = Array.isArray(tags) ? tags : [tags];\n if (isBuildPhase()) {\n for (const [key, entry] of this.buildEntries) {\n if (entry.tags?.some((tag) => normalizedTags.includes(tag))) this.buildEntries.delete(key);\n }\n return;\n }\n await revalidateRuntimeCacheTags('incremental', normalizedTags, durations);\n }\n\n resetRequestCache(): void {}\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gigadrive/network-config",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/Gigadrive/sdk.git",
|
|
@@ -33,11 +33,6 @@
|
|
|
33
33
|
"types": "./dist/nextjs-image-loader.d.ts",
|
|
34
34
|
"require": "./dist/nextjs-image-loader.js",
|
|
35
35
|
"import": "./dist/nextjs-image-loader.mjs"
|
|
36
|
-
},
|
|
37
|
-
"./nextjs-ppr-runtime": {
|
|
38
|
-
"types": "./dist/nextjs-ppr-runtime.d.ts",
|
|
39
|
-
"require": "./dist/nextjs-ppr-runtime.js",
|
|
40
|
-
"import": "./dist/nextjs-ppr-runtime.mjs"
|
|
41
36
|
}
|
|
42
37
|
},
|
|
43
38
|
"main": "dist/index.js",
|
|
@@ -54,7 +49,7 @@
|
|
|
54
49
|
"safe-regex2": "^5.0.0",
|
|
55
50
|
"yaml": "^2.8.1",
|
|
56
51
|
"@gigadrive/build-utils": "1.0.3",
|
|
57
|
-
"@gigadrive/commons": "2.
|
|
52
|
+
"@gigadrive/commons": "2.2.0"
|
|
58
53
|
},
|
|
59
54
|
"devDependencies": {
|
|
60
55
|
"@effect/platform": "^0.96.0",
|
|
@@ -72,7 +67,7 @@
|
|
|
72
67
|
"access": "public"
|
|
73
68
|
},
|
|
74
69
|
"scripts": {
|
|
75
|
-
"build": "tsup && mkdir -p dist/v4 && cp -r ./src/v4/schema.json ./dist/v4/schema.json && cp ./src/nextjs-adapter.cjs ./dist/nextjs-adapter.cjs
|
|
70
|
+
"build": "tsup && mkdir -p dist/v4 && cp -r ./src/v4/schema.json ./dist/v4/schema.json && cp ./src/nextjs-adapter.cjs ./dist/nextjs-adapter.cjs",
|
|
76
71
|
"clean": "rm -rf dist",
|
|
77
72
|
"typecheck": "tsc --noEmit"
|
|
78
73
|
}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
// Template for the generated Edge runtime wrapper, which speaks the Web
|
|
2
|
-
// `fetch(Request) -> Response` contract expected by the edge gateway. The
|
|
3
|
-
// quoted `__GIGADRIVE_*__` tokens are replaced with JSON-encoded values and
|
|
4
|
-
// the `__GIGADRIVE_EDGE_IMPORTS__` marker line is replaced with the ordered
|
|
5
|
-
// chunk imports by `writeEntrypointWrappers` in nextjs-adapter.ts. Keep the
|
|
6
|
-
// fetch export in sync with node-middleware-web.mjs (asserted by
|
|
7
|
-
// nextjs-adapter.test.ts).
|
|
8
|
-
import { persistPprCacheEntry } from '__GIGADRIVE_PPR_RUNTIME__';
|
|
9
|
-
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
10
|
-
|
|
11
|
-
// Next's edge chunks must evaluate lazily on the first request, not at module
|
|
12
|
-
// scope: chunk evaluation can block on runtime facilities that only exist once
|
|
13
|
-
// the guest server is up, and an eager top-level await would keep the process
|
|
14
|
-
// from ever reaching its listening state (observed as a MicroVM boot loop in
|
|
15
|
-
// prod). The promise is shared, so evaluation still happens exactly once.
|
|
16
|
-
let handlerPromise;
|
|
17
|
-
const resolveHandler = () =>
|
|
18
|
-
(handlerPromise ??= (async () => {
|
|
19
|
-
globalThis.self ??= globalThis;
|
|
20
|
-
globalThis.AsyncLocalStorage ??= AsyncLocalStorage;
|
|
21
|
-
/* __GIGADRIVE_EDGE_IMPORTS__ */
|
|
22
|
-
const registry = globalThis._ENTRIES;
|
|
23
|
-
if (!registry) throw new Error('Next.js edge entry registry is unavailable');
|
|
24
|
-
const entry = await registry['__GIGADRIVE_EDGE_ENTRY_KEY__'];
|
|
25
|
-
const handler = entry?.['__GIGADRIVE_EDGE_HANDLER_EXPORT__'];
|
|
26
|
-
if (typeof handler !== 'function') throw new Error('Next.js edge handler is unavailable');
|
|
27
|
-
return handler;
|
|
28
|
-
})());
|
|
29
|
-
|
|
30
|
-
export async function fetch(request) {
|
|
31
|
-
const nextHandler = await resolveHandler();
|
|
32
|
-
const pending = [];
|
|
33
|
-
const url = new URL(request.url);
|
|
34
|
-
const response = await nextHandler(request, {
|
|
35
|
-
signal: request.signal,
|
|
36
|
-
waitUntil(promise) {
|
|
37
|
-
pending.push(Promise.resolve(promise));
|
|
38
|
-
},
|
|
39
|
-
requestMeta: {
|
|
40
|
-
hostname: url.hostname,
|
|
41
|
-
invocationTarget: request.headers.get('x-gigadrive-next-invocation-target') ?? url.pathname,
|
|
42
|
-
routeMatches: Object.fromEntries(new URLSearchParams(request.headers.get('x-now-route-matches') ?? '')),
|
|
43
|
-
relativeProjectDir: '__GIGADRIVE_RELATIVE_PROJECT_DIR__',
|
|
44
|
-
onCacheEntryV2(cacheEntry, meta) {
|
|
45
|
-
pending.push(
|
|
46
|
-
persistPprCacheEntry(
|
|
47
|
-
request.headers.get('x-gigadrive-next-cache-key') ??
|
|
48
|
-
meta.url ??
|
|
49
|
-
request.headers.get('x-matched-path') ??
|
|
50
|
-
url.pathname,
|
|
51
|
-
cacheEntry
|
|
52
|
-
)
|
|
53
|
-
);
|
|
54
|
-
return false;
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
});
|
|
58
|
-
if (!response.body) {
|
|
59
|
-
await Promise.allSettled(pending);
|
|
60
|
-
return response;
|
|
61
|
-
}
|
|
62
|
-
const reader = response.body.getReader();
|
|
63
|
-
const body = new ReadableStream({
|
|
64
|
-
async pull(controller) {
|
|
65
|
-
let result;
|
|
66
|
-
try {
|
|
67
|
-
result = await reader.read();
|
|
68
|
-
} catch (error) {
|
|
69
|
-
// A failing upstream read must still settle queued waitUntil work
|
|
70
|
-
// (e.g. PPR cache persistence) before the invocation ends.
|
|
71
|
-
await Promise.allSettled(pending);
|
|
72
|
-
throw error;
|
|
73
|
-
}
|
|
74
|
-
if (result.done) {
|
|
75
|
-
await Promise.allSettled(pending);
|
|
76
|
-
controller.close();
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
controller.enqueue(result.value);
|
|
80
|
-
},
|
|
81
|
-
async cancel(reason) {
|
|
82
|
-
try {
|
|
83
|
-
await reader.cancel(reason);
|
|
84
|
-
} finally {
|
|
85
|
-
await Promise.allSettled(pending);
|
|
86
|
-
}
|
|
87
|
-
},
|
|
88
|
-
});
|
|
89
|
-
return new Response(body, response);
|
|
90
|
-
}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
// Template for the generated Node middleware wrapper, which speaks the Web
|
|
2
|
-
// `fetch(Request) -> Response` contract expected by the edge gateway. The
|
|
3
|
-
// quoted `__GIGADRIVE_*__` tokens are replaced with JSON-encoded values by
|
|
4
|
-
// `writeEntrypointWrappers` in nextjs-adapter.ts. Keep the handler resolution
|
|
5
|
-
// block in sync with node-route.mjs and the fetch export in sync with
|
|
6
|
-
// edge-web.mjs (both are asserted by nextjs-adapter.test.ts).
|
|
7
|
-
import { persistPprCacheEntry } from '__GIGADRIVE_PPR_RUNTIME__';
|
|
8
|
-
import { fileURLToPath } from 'node:url';
|
|
9
|
-
|
|
10
|
-
// The Next server resolves configured cache handlers and build manifests
|
|
11
|
-
// relative to the process working directory, so monorepo deployments must run
|
|
12
|
-
// from the project directory rather than the repository root. The chdir runs
|
|
13
|
-
// exactly once at module evaluation — never per request — so concurrent
|
|
14
|
-
// invocations cannot race on process-global state.
|
|
15
|
-
process.chdir(fileURLToPath(new URL('__GIGADRIVE_PROJECT_DIR__', import.meta.url)));
|
|
16
|
-
|
|
17
|
-
// The Next entrypoint must be imported lazily on the first request, not with a
|
|
18
|
-
// top-level await: some route module graphs block on facilities that only
|
|
19
|
-
// exist once the guest server is listening, and eager evaluation deadlocks the
|
|
20
|
-
// boot (observed as MicroVM boot loops in prod). The promise is shared, so the
|
|
21
|
-
// module still evaluates exactly once.
|
|
22
|
-
let handlerPromise;
|
|
23
|
-
const resolveHandler = () =>
|
|
24
|
-
(handlerPromise ??= (async () => {
|
|
25
|
-
const nextEntrypoint = await import('__GIGADRIVE_ENTRYPOINT__');
|
|
26
|
-
// Turbopack builds may export the module (or its default) as a promise.
|
|
27
|
-
const loadedEntrypoint = await Promise.resolve(nextEntrypoint.default ?? nextEntrypoint);
|
|
28
|
-
const handler =
|
|
29
|
-
nextEntrypoint.handler ??
|
|
30
|
-
loadedEntrypoint?.handler ??
|
|
31
|
-
loadedEntrypoint?.default?.handler ??
|
|
32
|
-
loadedEntrypoint?.default ??
|
|
33
|
-
loadedEntrypoint;
|
|
34
|
-
if (typeof handler !== 'function') throw new Error('Next.js middleware handler is unavailable');
|
|
35
|
-
return handler;
|
|
36
|
-
})());
|
|
37
|
-
|
|
38
|
-
export async function fetch(request) {
|
|
39
|
-
const nextHandler = await resolveHandler();
|
|
40
|
-
const pending = [];
|
|
41
|
-
const url = new URL(request.url);
|
|
42
|
-
const response = await nextHandler(request, {
|
|
43
|
-
signal: request.signal,
|
|
44
|
-
waitUntil(promise) {
|
|
45
|
-
pending.push(Promise.resolve(promise));
|
|
46
|
-
},
|
|
47
|
-
requestMeta: {
|
|
48
|
-
hostname: url.hostname,
|
|
49
|
-
invocationTarget: request.headers.get('x-gigadrive-next-invocation-target') ?? url.pathname,
|
|
50
|
-
routeMatches: Object.fromEntries(new URLSearchParams(request.headers.get('x-now-route-matches') ?? '')),
|
|
51
|
-
relativeProjectDir: '__GIGADRIVE_RELATIVE_PROJECT_DIR__',
|
|
52
|
-
onCacheEntryV2(cacheEntry, meta) {
|
|
53
|
-
pending.push(
|
|
54
|
-
persistPprCacheEntry(
|
|
55
|
-
request.headers.get('x-gigadrive-next-cache-key') ??
|
|
56
|
-
meta.url ??
|
|
57
|
-
request.headers.get('x-matched-path') ??
|
|
58
|
-
url.pathname,
|
|
59
|
-
cacheEntry
|
|
60
|
-
)
|
|
61
|
-
);
|
|
62
|
-
return false;
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
});
|
|
66
|
-
if (!response.body) {
|
|
67
|
-
await Promise.allSettled(pending);
|
|
68
|
-
return response;
|
|
69
|
-
}
|
|
70
|
-
const reader = response.body.getReader();
|
|
71
|
-
const body = new ReadableStream({
|
|
72
|
-
async pull(controller) {
|
|
73
|
-
let result;
|
|
74
|
-
try {
|
|
75
|
-
result = await reader.read();
|
|
76
|
-
} catch (error) {
|
|
77
|
-
// A failing upstream read must still settle queued waitUntil work
|
|
78
|
-
// (e.g. PPR cache persistence) before the invocation ends.
|
|
79
|
-
await Promise.allSettled(pending);
|
|
80
|
-
throw error;
|
|
81
|
-
}
|
|
82
|
-
if (result.done) {
|
|
83
|
-
await Promise.allSettled(pending);
|
|
84
|
-
controller.close();
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
controller.enqueue(result.value);
|
|
88
|
-
},
|
|
89
|
-
async cancel(reason) {
|
|
90
|
-
try {
|
|
91
|
-
await reader.cancel(reason);
|
|
92
|
-
} finally {
|
|
93
|
-
await Promise.allSettled(pending);
|
|
94
|
-
}
|
|
95
|
-
},
|
|
96
|
-
});
|
|
97
|
-
return new Response(body, response);
|
|
98
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
// Template for the generated Node route-handler wrapper. The quoted
|
|
2
|
-
// `__GIGADRIVE_*__` tokens are replaced with JSON-encoded values by
|
|
3
|
-
// `writeEntrypointWrappers` in nextjs-adapter.ts before the file is written
|
|
4
|
-
// into `.gigadrive/nextjs/entrypoints/`. Keep the request contract in sync
|
|
5
|
-
// with node-middleware-web.mjs (the handler resolution block is asserted to
|
|
6
|
-
// stay identical by nextjs-adapter.test.ts).
|
|
7
|
-
import { persistPprCacheEntry, revalidateNextPath } from '__GIGADRIVE_PPR_RUNTIME__';
|
|
8
|
-
import 'next/dist/build/adapter/setup-node-env.external.js';
|
|
9
|
-
import { fileURLToPath } from 'node:url';
|
|
10
|
-
|
|
11
|
-
// The Next server resolves configured cache handlers and build manifests
|
|
12
|
-
// relative to the process working directory, so monorepo deployments must run
|
|
13
|
-
// from the project directory rather than the repository root. The chdir runs
|
|
14
|
-
// exactly once at module evaluation — never per request — so concurrent
|
|
15
|
-
// invocations cannot race on process-global state.
|
|
16
|
-
process.chdir(fileURLToPath(new URL('__GIGADRIVE_PROJECT_DIR__', import.meta.url)));
|
|
17
|
-
|
|
18
|
-
// The Next entrypoint must be imported lazily on the first request, not with a
|
|
19
|
-
// top-level await: some route module graphs block on facilities that only
|
|
20
|
-
// exist once the guest server is listening, and eager evaluation deadlocks the
|
|
21
|
-
// boot (observed as MicroVM boot loops in prod). The promise is shared, so the
|
|
22
|
-
// module still evaluates exactly once.
|
|
23
|
-
let handlerPromise;
|
|
24
|
-
const resolveHandler = () =>
|
|
25
|
-
(handlerPromise ??= (async () => {
|
|
26
|
-
const nextEntrypoint = await import('__GIGADRIVE_ENTRYPOINT__');
|
|
27
|
-
// Turbopack builds may export the module (or its default) as a promise.
|
|
28
|
-
const loadedEntrypoint = await Promise.resolve(nextEntrypoint.default ?? nextEntrypoint);
|
|
29
|
-
const handler =
|
|
30
|
-
nextEntrypoint.handler ??
|
|
31
|
-
loadedEntrypoint?.handler ??
|
|
32
|
-
loadedEntrypoint?.default?.handler ??
|
|
33
|
-
loadedEntrypoint?.default ??
|
|
34
|
-
loadedEntrypoint;
|
|
35
|
-
if (typeof handler !== 'function') throw new Error('Next.js Node handler is unavailable');
|
|
36
|
-
return handler;
|
|
37
|
-
})());
|
|
38
|
-
|
|
39
|
-
export default async function handler(req, res) {
|
|
40
|
-
const nextHandler = await resolveHandler();
|
|
41
|
-
const pending = [];
|
|
42
|
-
const hostname = Array.isArray(req.headers.host) ? req.headers.host[0] : req.headers.host;
|
|
43
|
-
await nextHandler(req, res, {
|
|
44
|
-
waitUntil(promise) {
|
|
45
|
-
pending.push(Promise.resolve(promise));
|
|
46
|
-
},
|
|
47
|
-
requestMeta: {
|
|
48
|
-
hostname,
|
|
49
|
-
invocationTarget: req.headers['x-gigadrive-next-invocation-target'] ?? req.url,
|
|
50
|
-
routeMatches: Object.fromEntries(new URLSearchParams(req.headers['x-now-route-matches'] ?? '')),
|
|
51
|
-
relativeProjectDir: '__GIGADRIVE_RELATIVE_PROJECT_DIR__',
|
|
52
|
-
revalidate(input) {
|
|
53
|
-
return revalidateNextPath({ ...input, hostname });
|
|
54
|
-
},
|
|
55
|
-
onCacheEntryV2(cacheEntry, meta) {
|
|
56
|
-
const cacheKey = req.headers['x-gigadrive-next-cache-key'];
|
|
57
|
-
const matchedPath = req.headers['x-matched-path'];
|
|
58
|
-
pending.push(
|
|
59
|
-
persistPprCacheEntry(
|
|
60
|
-
(Array.isArray(cacheKey) ? cacheKey[0] : cacheKey) ??
|
|
61
|
-
meta.url ??
|
|
62
|
-
(Array.isArray(matchedPath) ? matchedPath[0] : matchedPath) ??
|
|
63
|
-
req.url ??
|
|
64
|
-
'/',
|
|
65
|
-
cacheEntry
|
|
66
|
-
)
|
|
67
|
-
);
|
|
68
|
-
return false;
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
});
|
|
72
|
-
await Promise.allSettled(pending);
|
|
73
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
interface ResponseCacheEntry {
|
|
2
|
-
cacheControl?: unknown;
|
|
3
|
-
value?: {
|
|
4
|
-
kind?: unknown;
|
|
5
|
-
html?: unknown;
|
|
6
|
-
postponed?: unknown;
|
|
7
|
-
headers?: unknown;
|
|
8
|
-
status?: unknown;
|
|
9
|
-
} | null;
|
|
10
|
-
}
|
|
11
|
-
interface NextInternalRevalidateInput {
|
|
12
|
-
urlPath: string;
|
|
13
|
-
headers: Record<string, string>;
|
|
14
|
-
opts: {
|
|
15
|
-
unstable_onlyGenerated?: boolean;
|
|
16
|
-
};
|
|
17
|
-
hostname: string | undefined;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Persists the atomic shell/postponed pair emitted by Next's PPR response cache.
|
|
21
|
-
*
|
|
22
|
-
* @param cacheKey - The response-cache URL or pathname associated with the PPR entry.
|
|
23
|
-
* @param cacheEntry - The Next response-cache entry emitted by `onCacheEntryV2`.
|
|
24
|
-
* @returns A promise that settles after the entry is written, or immediately for unsupported entries.
|
|
25
|
-
*
|
|
26
|
-
* @example
|
|
27
|
-
* ```ts
|
|
28
|
-
* await persistPprCacheEntry('/products/one', {
|
|
29
|
-
* value: { kind: 'APP_PAGE', html: '<html>shell</html>', postponed: 'opaque-state' },
|
|
30
|
-
* });
|
|
31
|
-
* ```
|
|
32
|
-
*/
|
|
33
|
-
declare const persistPprCacheEntry: (cacheKey: string, cacheEntry: ResponseCacheEntry) => Promise<void>;
|
|
34
|
-
/**
|
|
35
|
-
* Runs Next's synchronous Pages Router on-demand revalidation protocol.
|
|
36
|
-
*
|
|
37
|
-
* The CDN path is evicted before a cache-busting request reaches the split page
|
|
38
|
-
* function. Next then regenerates the shared incremental-cache entry before this
|
|
39
|
-
* promise resolves, preserving the blocking semantics of `response.revalidate()`.
|
|
40
|
-
*
|
|
41
|
-
* @param input - The internal revalidation request emitted by Next's Pages API runtime.
|
|
42
|
-
* @returns A promise that settles after Next has regenerated the requested path.
|
|
43
|
-
*/
|
|
44
|
-
declare const revalidateNextPath: (input: NextInternalRevalidateInput) => Promise<void>;
|
|
45
|
-
|
|
46
|
-
export { persistPprCacheEntry, revalidateNextPath };
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
interface ResponseCacheEntry {
|
|
2
|
-
cacheControl?: unknown;
|
|
3
|
-
value?: {
|
|
4
|
-
kind?: unknown;
|
|
5
|
-
html?: unknown;
|
|
6
|
-
postponed?: unknown;
|
|
7
|
-
headers?: unknown;
|
|
8
|
-
status?: unknown;
|
|
9
|
-
} | null;
|
|
10
|
-
}
|
|
11
|
-
interface NextInternalRevalidateInput {
|
|
12
|
-
urlPath: string;
|
|
13
|
-
headers: Record<string, string>;
|
|
14
|
-
opts: {
|
|
15
|
-
unstable_onlyGenerated?: boolean;
|
|
16
|
-
};
|
|
17
|
-
hostname: string | undefined;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Persists the atomic shell/postponed pair emitted by Next's PPR response cache.
|
|
21
|
-
*
|
|
22
|
-
* @param cacheKey - The response-cache URL or pathname associated with the PPR entry.
|
|
23
|
-
* @param cacheEntry - The Next response-cache entry emitted by `onCacheEntryV2`.
|
|
24
|
-
* @returns A promise that settles after the entry is written, or immediately for unsupported entries.
|
|
25
|
-
*
|
|
26
|
-
* @example
|
|
27
|
-
* ```ts
|
|
28
|
-
* await persistPprCacheEntry('/products/one', {
|
|
29
|
-
* value: { kind: 'APP_PAGE', html: '<html>shell</html>', postponed: 'opaque-state' },
|
|
30
|
-
* });
|
|
31
|
-
* ```
|
|
32
|
-
*/
|
|
33
|
-
declare const persistPprCacheEntry: (cacheKey: string, cacheEntry: ResponseCacheEntry) => Promise<void>;
|
|
34
|
-
/**
|
|
35
|
-
* Runs Next's synchronous Pages Router on-demand revalidation protocol.
|
|
36
|
-
*
|
|
37
|
-
* The CDN path is evicted before a cache-busting request reaches the split page
|
|
38
|
-
* function. Next then regenerates the shared incremental-cache entry before this
|
|
39
|
-
* promise resolves, preserving the blocking semantics of `response.revalidate()`.
|
|
40
|
-
*
|
|
41
|
-
* @param input - The internal revalidation request emitted by Next's Pages API runtime.
|
|
42
|
-
* @returns A promise that settles after Next has regenerated the requested path.
|
|
43
|
-
*/
|
|
44
|
-
declare const revalidateNextPath: (input: NextInternalRevalidateInput) => Promise<void>;
|
|
45
|
-
|
|
46
|
-
export { persistPprCacheEntry, revalidateNextPath };
|