@gigadrive/network-config 5.5.0 → 5.5.2
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 +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -1
- package/dist/nextjs-adapter.js +11 -0
- package/dist/nextjs-adapter.js.map +1 -1
- package/dist/nextjs-adapter.mjs +11 -0
- package/dist/nextjs-adapter.mjs.map +1 -1
- package/dist/nextjs-cache-components-handler.js +11 -1
- package/dist/nextjs-cache-components-handler.js.map +1 -1
- package/dist/nextjs-cache-components-handler.mjs +11 -1
- package/dist/nextjs-cache-components-handler.mjs.map +1 -1
- package/dist/nextjs-cache-handler.js +23 -4
- package/dist/nextjs-cache-handler.js.map +1 -1
- package/dist/nextjs-cache-handler.mjs +23 -4
- package/dist/nextjs-cache-handler.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/nextjs-cache-components-handler.ts","../src/nextjs-runtime-cache-client.ts"],"sourcesContent":["import {\n getRuntimeCacheTagState,\n readRuntimeCache,\n revalidateRuntimeCacheTags,\n writeRuntimeCache,\n} from './nextjs-runtime-cache-client';\n\ninterface CacheComponentEntry {\n value: ReadableStream<Uint8Array>;\n tags: string[];\n stale: number;\n timestamp: number;\n expire: number;\n revalidate: number;\n}\n\ninterface SerializedCacheComponentEntry extends Omit<CacheComponentEntry, 'value'> {\n value: Uint8Array;\n}\n\nconst isSerializedCacheComponentEntry = (value: unknown): value is SerializedCacheComponentEntry => {\n if (typeof value !== 'object' || value === null) return false;\n const entry = value as Partial<SerializedCacheComponentEntry>;\n return (\n entry.value instanceof Uint8Array &&\n Array.isArray(entry.tags) &&\n entry.tags.every((tag) => typeof tag === 'string') &&\n typeof entry.stale === 'number' &&\n typeof entry.timestamp === 'number' &&\n typeof entry.expire === 'number' &&\n typeof entry.revalidate === 'number'\n );\n};\n\nconst pendingWrites = new Map<string, Promise<void>>();\nconst buildEntries = new Map<string, SerializedCacheComponentEntry>();\n\nconst streamToBytes = async (stream: ReadableStream<Uint8Array>): Promise<Uint8Array> => {\n const chunks: Uint8Array[] = [];\n let size = 0;\n const reader = stream.getReader();\n while (true) {\n const result = await reader.read();\n if (result.done) break;\n chunks.push(result.value);\n size += result.value.byteLength;\n }\n const bytes = new Uint8Array(size);\n let offset = 0;\n for (const chunk of chunks) {\n bytes.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return bytes;\n};\n\n/** Shared Network cache handler for Next.js Cache Components and `use cache`. */\nconst cacheHandler = {\n async get(cacheKey: string, softTags: string[]): Promise<CacheComponentEntry | undefined> {\n await pendingWrites.get(cacheKey);\n const value =\n process.env.GIGADRIVE_NEXT_BUILD === '1'\n ? buildEntries.get(cacheKey)\n : await readRuntimeCache('component', cacheKey);\n if (!isSerializedCacheComponentEntry(value)) return undefined;\n const tagState =\n process.env.GIGADRIVE_NEXT_BUILD === '1'\n ? { stale: 0, expired: 0 }\n : await getRuntimeCacheTagState('component', [...new Set([...value.tags, ...softTags])]);\n if (tagState.expired > value.timestamp) return undefined;\n return {\n ...value,\n ...(tagState.stale > value.timestamp ? { revalidate: -1 } : {}),\n value: new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(value.value);\n controller.close();\n },\n }),\n };\n },\n\n async set(cacheKey: string, pendingEntry: Promise<CacheComponentEntry>): Promise<void> {\n const write = (async () => {\n const entry = await pendingEntry;\n const value = await streamToBytes(entry.value);\n const serializedEntry = { ...entry, value };\n if (process.env.GIGADRIVE_NEXT_BUILD === '1') {\n buildEntries.set(cacheKey, serializedEntry);\n } else {\n await writeRuntimeCache('component', cacheKey, serializedEntry, entry.tags);\n }\n })();\n pendingWrites.set(cacheKey, write);\n try {\n await write;\n } finally {\n if (pendingWrites.get(cacheKey) === write) pendingWrites.delete(cacheKey);\n }\n },\n\n async refreshTags(): Promise<void> {},\n\n getExpiration(): Promise<number> {\n return Promise.resolve(Infinity);\n },\n\n async updateTags(tags: string[], durations?: { expire?: number }): Promise<void> {\n if (process.env.GIGADRIVE_NEXT_BUILD === '1') {\n for (const [key, entry] of buildEntries) {\n if (entry.tags.some((tag) => tags.includes(tag))) buildEntries.delete(key);\n }\n return;\n }\n return revalidateRuntimeCacheTags('component', tags, durations);\n },\n};\n\nexport default cacheHandler;\n","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\n/**\n * Re-establishes the client's network state after a microVM resume.\n *\n * The guest runtime delivers SIGUSR2 when the instance is restored from a\n * suspend snapshot. At that moment every kept-alive TCP connection from before\n * the freeze is dead, and the cached OAuth token may have expired while the\n * clock was stopped. Dropping the token and dialing one throwaway read\n * re-opens TCP+TLS and refreshes the token concurrently with the guest's own\n * wake-up work, so the first real cache read after a resume does not\n * serialize behind reconnection.\n *\n * Registered only when a Gigadrive deployment marker is present in the\n * environment, so local `next dev`/`next build` never gains a signal listener.\n */\nconst registerResumeRewarm = (): void => {\n // Next exposes this marker before evaluating an Edge route bundle. Bail out\n // before touching `process`: its Edge compatibility proxy throws when the\n // Node-only signal API is read, including through Reflect.\n if (Reflect.has(globalThis, 'EdgeRuntime')) return;\n\n // The deployment id is intentionally available during `next build`, but the\n // shared cache client is also bundled into Edge routes at that point. Keep\n // every Node-only signal reference behind the build-phase guard so Turbopack\n // can eliminate this branch before its Edge runtime validation runs.\n if (process.env.GIGADRIVE_NEXT_BUILD === '1') return;\n\n const onGigadrive =\n process.env.GIGADRIVE_DEPLOYMENT_ID ?? process.env.NEBULA_DEPLOYMENT_ID ?? process.env.GIGADRIVE_CLIENT_ID;\n if (!onGigadrive) return;\n\n // Resolve the optional Node signal API reflectively so non-Node runtimes can\n // load the shared client while Node deployments retain resume rewarming.\n const runtimeProcess = Reflect.get(globalThis, 'process') as NodeJS.Process | undefined;\n const addSignalListener = runtimeProcess && Reflect.get(runtimeProcess, 'on');\n if (typeof addSignalListener !== 'function') return;\n\n Reflect.apply(addSignalListener, runtimeProcess, [\n 'SIGUSR2',\n () => {\n cachedToken = undefined;\n if (!process.env.GIGADRIVE_CLIENT_ID || !process.env.GIGADRIVE_CLIENT_SECRET) return;\n void readRuntimeCache('warmup', '__resume__');\n },\n ]);\n};\nregisterResumeRewarm();\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/nextjs-cache-components-handler.ts","../src/nextjs-cache-clock.ts","../src/nextjs-runtime-cache-client.ts"],"sourcesContent":["import { nextClockLagMs } from './nextjs-cache-clock';\nimport {\n getRuntimeCacheTagState,\n readRuntimeCache,\n revalidateRuntimeCacheTags,\n writeRuntimeCache,\n} from './nextjs-runtime-cache-client';\n\ninterface CacheComponentEntry {\n value: ReadableStream<Uint8Array>;\n tags: string[];\n stale: number;\n timestamp: number;\n expire: number;\n revalidate: number;\n}\n\ninterface SerializedCacheComponentEntry extends Omit<CacheComponentEntry, 'value'> {\n value: Uint8Array;\n}\n\nconst isSerializedCacheComponentEntry = (value: unknown): value is SerializedCacheComponentEntry => {\n if (typeof value !== 'object' || value === null) return false;\n const entry = value as Partial<SerializedCacheComponentEntry>;\n return (\n entry.value instanceof Uint8Array &&\n Array.isArray(entry.tags) &&\n entry.tags.every((tag) => typeof tag === 'string') &&\n typeof entry.stale === 'number' &&\n typeof entry.timestamp === 'number' &&\n typeof entry.expire === 'number' &&\n typeof entry.revalidate === 'number'\n );\n};\n\nconst pendingWrites = new Map<string, Promise<void>>();\nconst buildEntries = new Map<string, SerializedCacheComponentEntry>();\n\nconst streamToBytes = async (stream: ReadableStream<Uint8Array>): Promise<Uint8Array> => {\n const chunks: Uint8Array[] = [];\n let size = 0;\n const reader = stream.getReader();\n while (true) {\n const result = await reader.read();\n if (result.done) break;\n chunks.push(result.value);\n size += result.value.byteLength;\n }\n const bytes = new Uint8Array(size);\n let offset = 0;\n for (const chunk of chunks) {\n bytes.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return bytes;\n};\n\n/** Shared Network cache handler for Next.js Cache Components and `use cache`. */\nconst cacheHandler = {\n async get(cacheKey: string, softTags: string[]): Promise<CacheComponentEntry | undefined> {\n await pendingWrites.get(cacheKey);\n const value =\n process.env.GIGADRIVE_NEXT_BUILD === '1'\n ? buildEntries.get(cacheKey)\n : await readRuntimeCache('component', cacheKey);\n if (!isSerializedCacheComponentEntry(value)) return undefined;\n const tagState =\n process.env.GIGADRIVE_NEXT_BUILD === '1'\n ? { stale: 0, expired: 0 }\n : await getRuntimeCacheTagState('component', [...new Set([...value.tags, ...softTags])]);\n if (tagState.expired > value.timestamp) return undefined;\n return {\n ...value,\n // Build entries were stamped by Next itself, so only stored ones need moving.\n timestamp: process.env.GIGADRIVE_NEXT_BUILD === '1' ? value.timestamp : value.timestamp - nextClockLagMs(),\n ...(tagState.stale > value.timestamp ? { revalidate: -1 } : {}),\n value: new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(value.value);\n controller.close();\n },\n }),\n };\n },\n\n async set(cacheKey: string, pendingEntry: Promise<CacheComponentEntry>): Promise<void> {\n const write = (async () => {\n const entry = await pendingEntry;\n const value = await streamToBytes(entry.value);\n const serializedEntry = { ...entry, value };\n if (process.env.GIGADRIVE_NEXT_BUILD === '1') {\n buildEntries.set(cacheKey, serializedEntry);\n } else {\n const stored = { ...serializedEntry, timestamp: entry.timestamp + nextClockLagMs() };\n await writeRuntimeCache('component', cacheKey, stored, entry.tags);\n }\n })();\n pendingWrites.set(cacheKey, write);\n try {\n await write;\n } finally {\n if (pendingWrites.get(cacheKey) === write) pendingWrites.delete(cacheKey);\n }\n },\n\n async refreshTags(): Promise<void> {},\n\n getExpiration(): Promise<number> {\n return Promise.resolve(Infinity);\n },\n\n async updateTags(tags: string[], durations?: { expire?: number }): Promise<void> {\n if (process.env.GIGADRIVE_NEXT_BUILD === '1') {\n for (const [key, entry] of buildEntries) {\n if (entry.tags.some((tag) => tags.includes(tag))) buildEntries.delete(key);\n }\n return;\n }\n return revalidateRuntimeCacheTags('component', tags, durations);\n },\n};\n\nexport default cacheHandler;\n","/** Differences below this are ordinary timer noise, not a clock that fell behind. */\nconst NEGLIGIBLE_LAG_MS = 1_000;\n\n/**\n * How far Next's clock trails the wall clock.\n *\n * Next measures cache age with `performance.timeOrigin + performance.now()`,\n * which is built on the monotonic clock. A process resumed from a snapshot keeps\n * its monotonic clock from before the pause, while the wall clock is corrected,\n * so Next's clock falls behind by every moment spent paused. An entry stamped\n * with the wall clock then looks newer than it is and never goes stale.\n *\n * Stored timestamps use the wall clock, because other processes read them.\n * Anything handed to Next is shifted onto its clock by this amount.\n */\nexport const nextClockLagMs = (): number => {\n const lag = Date.now() - (performance.timeOrigin + performance.now());\n return Math.abs(lag) < NEGLIGIBLE_LAG_MS ? 0 : lag;\n};\n","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\n/**\n * Re-establishes the client's network state after a microVM resume.\n *\n * The guest runtime delivers SIGUSR2 when the instance is restored from a\n * suspend snapshot. At that moment every kept-alive TCP connection from before\n * the freeze is dead, and the cached OAuth token may have expired while the\n * clock was stopped. Dropping the token and dialing one throwaway read\n * re-opens TCP+TLS and refreshes the token concurrently with the guest's own\n * wake-up work, so the first real cache read after a resume does not\n * serialize behind reconnection.\n *\n * Registered only when a Gigadrive deployment marker is present in the\n * environment, so local `next dev`/`next build` never gains a signal listener.\n */\nconst registerResumeRewarm = (): void => {\n // Next exposes this marker before evaluating an Edge route bundle. Bail out\n // before touching `process`: its Edge compatibility proxy throws when the\n // Node-only signal API is read, including through Reflect.\n if (Reflect.has(globalThis, 'EdgeRuntime')) return;\n\n // The deployment id is intentionally available during `next build`, but the\n // shared cache client is also bundled into Edge routes at that point. Keep\n // every Node-only signal reference behind the build-phase guard so Turbopack\n // can eliminate this branch before its Edge runtime validation runs.\n if (process.env.GIGADRIVE_NEXT_BUILD === '1') return;\n\n const onGigadrive =\n process.env.GIGADRIVE_DEPLOYMENT_ID ?? process.env.NEBULA_DEPLOYMENT_ID ?? process.env.GIGADRIVE_CLIENT_ID;\n if (!onGigadrive) return;\n\n // Resolve the optional Node signal API reflectively so non-Node runtimes can\n // load the shared client while Node deployments retain resume rewarming.\n const runtimeProcess = Reflect.get(globalThis, 'process') as NodeJS.Process | undefined;\n const addSignalListener = runtimeProcess && Reflect.get(runtimeProcess, 'on');\n if (typeof addSignalListener !== 'function') return;\n\n Reflect.apply(addSignalListener, runtimeProcess, [\n 'SIGUSR2',\n () => {\n cachedToken = undefined;\n if (!process.env.GIGADRIVE_CLIENT_ID || !process.env.GIGADRIVE_CLIENT_SECRET) return;\n void readRuntimeCache('warmup', '__resume__');\n },\n ]);\n};\nregisterResumeRewarm();\n"],"mappings":"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","names":[]}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
// src/nextjs-cache-clock.ts
|
|
2
|
+
var NEGLIGIBLE_LAG_MS = 1e3;
|
|
3
|
+
var nextClockLagMs = () => {
|
|
4
|
+
const lag = Date.now() - (performance.timeOrigin + performance.now());
|
|
5
|
+
return Math.abs(lag) < NEGLIGIBLE_LAG_MS ? 0 : lag;
|
|
6
|
+
};
|
|
7
|
+
|
|
1
8
|
// src/nextjs-runtime-cache-client.ts
|
|
2
9
|
var cachedToken;
|
|
3
10
|
var apiBaseUrl = () => (process.env.GIGADRIVE_API_URL ?? "https://api.gigadrive.network").replace(/\/$/, "");
|
|
@@ -182,6 +189,8 @@ var cacheHandler = {
|
|
|
182
189
|
if (tagState.expired > value.timestamp) return void 0;
|
|
183
190
|
return {
|
|
184
191
|
...value,
|
|
192
|
+
// Build entries were stamped by Next itself, so only stored ones need moving.
|
|
193
|
+
timestamp: process.env.GIGADRIVE_NEXT_BUILD === "1" ? value.timestamp : value.timestamp - nextClockLagMs(),
|
|
185
194
|
...tagState.stale > value.timestamp ? { revalidate: -1 } : {},
|
|
186
195
|
value: new ReadableStream({
|
|
187
196
|
start(controller) {
|
|
@@ -199,7 +208,8 @@ var cacheHandler = {
|
|
|
199
208
|
if (process.env.GIGADRIVE_NEXT_BUILD === "1") {
|
|
200
209
|
buildEntries.set(cacheKey, serializedEntry);
|
|
201
210
|
} else {
|
|
202
|
-
|
|
211
|
+
const stored = { ...serializedEntry, timestamp: entry.timestamp + nextClockLagMs() };
|
|
212
|
+
await writeRuntimeCache("component", cacheKey, stored, entry.tags);
|
|
203
213
|
}
|
|
204
214
|
})();
|
|
205
215
|
pendingWrites.set(cacheKey, write);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/nextjs-runtime-cache-client.ts","../src/nextjs-cache-components-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\n/**\n * Re-establishes the client's network state after a microVM resume.\n *\n * The guest runtime delivers SIGUSR2 when the instance is restored from a\n * suspend snapshot. At that moment every kept-alive TCP connection from before\n * the freeze is dead, and the cached OAuth token may have expired while the\n * clock was stopped. Dropping the token and dialing one throwaway read\n * re-opens TCP+TLS and refreshes the token concurrently with the guest's own\n * wake-up work, so the first real cache read after a resume does not\n * serialize behind reconnection.\n *\n * Registered only when a Gigadrive deployment marker is present in the\n * environment, so local `next dev`/`next build` never gains a signal listener.\n */\nconst registerResumeRewarm = (): void => {\n // Next exposes this marker before evaluating an Edge route bundle. Bail out\n // before touching `process`: its Edge compatibility proxy throws when the\n // Node-only signal API is read, including through Reflect.\n if (Reflect.has(globalThis, 'EdgeRuntime')) return;\n\n // The deployment id is intentionally available during `next build`, but the\n // shared cache client is also bundled into Edge routes at that point. Keep\n // every Node-only signal reference behind the build-phase guard so Turbopack\n // can eliminate this branch before its Edge runtime validation runs.\n if (process.env.GIGADRIVE_NEXT_BUILD === '1') return;\n\n const onGigadrive =\n process.env.GIGADRIVE_DEPLOYMENT_ID ?? process.env.NEBULA_DEPLOYMENT_ID ?? process.env.GIGADRIVE_CLIENT_ID;\n if (!onGigadrive) return;\n\n // Resolve the optional Node signal API reflectively so non-Node runtimes can\n // load the shared client while Node deployments retain resume rewarming.\n const runtimeProcess = Reflect.get(globalThis, 'process') as NodeJS.Process | undefined;\n const addSignalListener = runtimeProcess && Reflect.get(runtimeProcess, 'on');\n if (typeof addSignalListener !== 'function') return;\n\n Reflect.apply(addSignalListener, runtimeProcess, [\n 'SIGUSR2',\n () => {\n cachedToken = undefined;\n if (!process.env.GIGADRIVE_CLIENT_ID || !process.env.GIGADRIVE_CLIENT_SECRET) return;\n void readRuntimeCache('warmup', '__resume__');\n },\n ]);\n};\nregisterResumeRewarm();\n","import {\n getRuntimeCacheTagState,\n readRuntimeCache,\n revalidateRuntimeCacheTags,\n writeRuntimeCache,\n} from './nextjs-runtime-cache-client';\n\ninterface CacheComponentEntry {\n value: ReadableStream<Uint8Array>;\n tags: string[];\n stale: number;\n timestamp: number;\n expire: number;\n revalidate: number;\n}\n\ninterface SerializedCacheComponentEntry extends Omit<CacheComponentEntry, 'value'> {\n value: Uint8Array;\n}\n\nconst isSerializedCacheComponentEntry = (value: unknown): value is SerializedCacheComponentEntry => {\n if (typeof value !== 'object' || value === null) return false;\n const entry = value as Partial<SerializedCacheComponentEntry>;\n return (\n entry.value instanceof Uint8Array &&\n Array.isArray(entry.tags) &&\n entry.tags.every((tag) => typeof tag === 'string') &&\n typeof entry.stale === 'number' &&\n typeof entry.timestamp === 'number' &&\n typeof entry.expire === 'number' &&\n typeof entry.revalidate === 'number'\n );\n};\n\nconst pendingWrites = new Map<string, Promise<void>>();\nconst buildEntries = new Map<string, SerializedCacheComponentEntry>();\n\nconst streamToBytes = async (stream: ReadableStream<Uint8Array>): Promise<Uint8Array> => {\n const chunks: Uint8Array[] = [];\n let size = 0;\n const reader = stream.getReader();\n while (true) {\n const result = await reader.read();\n if (result.done) break;\n chunks.push(result.value);\n size += result.value.byteLength;\n }\n const bytes = new Uint8Array(size);\n let offset = 0;\n for (const chunk of chunks) {\n bytes.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return bytes;\n};\n\n/** Shared Network cache handler for Next.js Cache Components and `use cache`. */\nconst cacheHandler = {\n async get(cacheKey: string, softTags: string[]): Promise<CacheComponentEntry | undefined> {\n await pendingWrites.get(cacheKey);\n const value =\n process.env.GIGADRIVE_NEXT_BUILD === '1'\n ? buildEntries.get(cacheKey)\n : await readRuntimeCache('component', cacheKey);\n if (!isSerializedCacheComponentEntry(value)) return undefined;\n const tagState =\n process.env.GIGADRIVE_NEXT_BUILD === '1'\n ? { stale: 0, expired: 0 }\n : await getRuntimeCacheTagState('component', [...new Set([...value.tags, ...softTags])]);\n if (tagState.expired > value.timestamp) return undefined;\n return {\n ...value,\n ...(tagState.stale > value.timestamp ? { revalidate: -1 } : {}),\n value: new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(value.value);\n controller.close();\n },\n }),\n };\n },\n\n async set(cacheKey: string, pendingEntry: Promise<CacheComponentEntry>): Promise<void> {\n const write = (async () => {\n const entry = await pendingEntry;\n const value = await streamToBytes(entry.value);\n const serializedEntry = { ...entry, value };\n if (process.env.GIGADRIVE_NEXT_BUILD === '1') {\n buildEntries.set(cacheKey, serializedEntry);\n } else {\n await writeRuntimeCache('component', cacheKey, serializedEntry, entry.tags);\n }\n })();\n pendingWrites.set(cacheKey, write);\n try {\n await write;\n } finally {\n if (pendingWrites.get(cacheKey) === write) pendingWrites.delete(cacheKey);\n }\n },\n\n async refreshTags(): Promise<void> {},\n\n getExpiration(): Promise<number> {\n return Promise.resolve(Infinity);\n },\n\n async updateTags(tags: string[], durations?: { expire?: number }): Promise<void> {\n if (process.env.GIGADRIVE_NEXT_BUILD === '1') {\n for (const [key, entry] of buildEntries) {\n if (entry.tags.some((tag) => tags.includes(tag))) buildEntries.delete(key);\n }\n return;\n }\n return revalidateRuntimeCacheTags('component', tags, durations);\n },\n};\n\nexport default cacheHandler;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/nextjs-cache-clock.ts","../src/nextjs-runtime-cache-client.ts","../src/nextjs-cache-components-handler.ts"],"sourcesContent":["/** Differences below this are ordinary timer noise, not a clock that fell behind. */\nconst NEGLIGIBLE_LAG_MS = 1_000;\n\n/**\n * How far Next's clock trails the wall clock.\n *\n * Next measures cache age with `performance.timeOrigin + performance.now()`,\n * which is built on the monotonic clock. A process resumed from a snapshot keeps\n * its monotonic clock from before the pause, while the wall clock is corrected,\n * so Next's clock falls behind by every moment spent paused. An entry stamped\n * with the wall clock then looks newer than it is and never goes stale.\n *\n * Stored timestamps use the wall clock, because other processes read them.\n * Anything handed to Next is shifted onto its clock by this amount.\n */\nexport const nextClockLagMs = (): number => {\n const lag = Date.now() - (performance.timeOrigin + performance.now());\n return Math.abs(lag) < NEGLIGIBLE_LAG_MS ? 0 : lag;\n};\n","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\n/**\n * Re-establishes the client's network state after a microVM resume.\n *\n * The guest runtime delivers SIGUSR2 when the instance is restored from a\n * suspend snapshot. At that moment every kept-alive TCP connection from before\n * the freeze is dead, and the cached OAuth token may have expired while the\n * clock was stopped. Dropping the token and dialing one throwaway read\n * re-opens TCP+TLS and refreshes the token concurrently with the guest's own\n * wake-up work, so the first real cache read after a resume does not\n * serialize behind reconnection.\n *\n * Registered only when a Gigadrive deployment marker is present in the\n * environment, so local `next dev`/`next build` never gains a signal listener.\n */\nconst registerResumeRewarm = (): void => {\n // Next exposes this marker before evaluating an Edge route bundle. Bail out\n // before touching `process`: its Edge compatibility proxy throws when the\n // Node-only signal API is read, including through Reflect.\n if (Reflect.has(globalThis, 'EdgeRuntime')) return;\n\n // The deployment id is intentionally available during `next build`, but the\n // shared cache client is also bundled into Edge routes at that point. Keep\n // every Node-only signal reference behind the build-phase guard so Turbopack\n // can eliminate this branch before its Edge runtime validation runs.\n if (process.env.GIGADRIVE_NEXT_BUILD === '1') return;\n\n const onGigadrive =\n process.env.GIGADRIVE_DEPLOYMENT_ID ?? process.env.NEBULA_DEPLOYMENT_ID ?? process.env.GIGADRIVE_CLIENT_ID;\n if (!onGigadrive) return;\n\n // Resolve the optional Node signal API reflectively so non-Node runtimes can\n // load the shared client while Node deployments retain resume rewarming.\n const runtimeProcess = Reflect.get(globalThis, 'process') as NodeJS.Process | undefined;\n const addSignalListener = runtimeProcess && Reflect.get(runtimeProcess, 'on');\n if (typeof addSignalListener !== 'function') return;\n\n Reflect.apply(addSignalListener, runtimeProcess, [\n 'SIGUSR2',\n () => {\n cachedToken = undefined;\n if (!process.env.GIGADRIVE_CLIENT_ID || !process.env.GIGADRIVE_CLIENT_SECRET) return;\n void readRuntimeCache('warmup', '__resume__');\n },\n ]);\n};\nregisterResumeRewarm();\n","import { nextClockLagMs } from './nextjs-cache-clock';\nimport {\n getRuntimeCacheTagState,\n readRuntimeCache,\n revalidateRuntimeCacheTags,\n writeRuntimeCache,\n} from './nextjs-runtime-cache-client';\n\ninterface CacheComponentEntry {\n value: ReadableStream<Uint8Array>;\n tags: string[];\n stale: number;\n timestamp: number;\n expire: number;\n revalidate: number;\n}\n\ninterface SerializedCacheComponentEntry extends Omit<CacheComponentEntry, 'value'> {\n value: Uint8Array;\n}\n\nconst isSerializedCacheComponentEntry = (value: unknown): value is SerializedCacheComponentEntry => {\n if (typeof value !== 'object' || value === null) return false;\n const entry = value as Partial<SerializedCacheComponentEntry>;\n return (\n entry.value instanceof Uint8Array &&\n Array.isArray(entry.tags) &&\n entry.tags.every((tag) => typeof tag === 'string') &&\n typeof entry.stale === 'number' &&\n typeof entry.timestamp === 'number' &&\n typeof entry.expire === 'number' &&\n typeof entry.revalidate === 'number'\n );\n};\n\nconst pendingWrites = new Map<string, Promise<void>>();\nconst buildEntries = new Map<string, SerializedCacheComponentEntry>();\n\nconst streamToBytes = async (stream: ReadableStream<Uint8Array>): Promise<Uint8Array> => {\n const chunks: Uint8Array[] = [];\n let size = 0;\n const reader = stream.getReader();\n while (true) {\n const result = await reader.read();\n if (result.done) break;\n chunks.push(result.value);\n size += result.value.byteLength;\n }\n const bytes = new Uint8Array(size);\n let offset = 0;\n for (const chunk of chunks) {\n bytes.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return bytes;\n};\n\n/** Shared Network cache handler for Next.js Cache Components and `use cache`. */\nconst cacheHandler = {\n async get(cacheKey: string, softTags: string[]): Promise<CacheComponentEntry | undefined> {\n await pendingWrites.get(cacheKey);\n const value =\n process.env.GIGADRIVE_NEXT_BUILD === '1'\n ? buildEntries.get(cacheKey)\n : await readRuntimeCache('component', cacheKey);\n if (!isSerializedCacheComponentEntry(value)) return undefined;\n const tagState =\n process.env.GIGADRIVE_NEXT_BUILD === '1'\n ? { stale: 0, expired: 0 }\n : await getRuntimeCacheTagState('component', [...new Set([...value.tags, ...softTags])]);\n if (tagState.expired > value.timestamp) return undefined;\n return {\n ...value,\n // Build entries were stamped by Next itself, so only stored ones need moving.\n timestamp: process.env.GIGADRIVE_NEXT_BUILD === '1' ? value.timestamp : value.timestamp - nextClockLagMs(),\n ...(tagState.stale > value.timestamp ? { revalidate: -1 } : {}),\n value: new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(value.value);\n controller.close();\n },\n }),\n };\n },\n\n async set(cacheKey: string, pendingEntry: Promise<CacheComponentEntry>): Promise<void> {\n const write = (async () => {\n const entry = await pendingEntry;\n const value = await streamToBytes(entry.value);\n const serializedEntry = { ...entry, value };\n if (process.env.GIGADRIVE_NEXT_BUILD === '1') {\n buildEntries.set(cacheKey, serializedEntry);\n } else {\n const stored = { ...serializedEntry, timestamp: entry.timestamp + nextClockLagMs() };\n await writeRuntimeCache('component', cacheKey, stored, entry.tags);\n }\n })();\n pendingWrites.set(cacheKey, write);\n try {\n await write;\n } finally {\n if (pendingWrites.get(cacheKey) === write) pendingWrites.delete(cacheKey);\n }\n },\n\n async refreshTags(): Promise<void> {},\n\n getExpiration(): Promise<number> {\n return Promise.resolve(Infinity);\n },\n\n async updateTags(tags: string[], durations?: { expire?: number }): Promise<void> {\n if (process.env.GIGADRIVE_NEXT_BUILD === '1') {\n for (const [key, entry] of buildEntries) {\n if (entry.tags.some((tag) => tags.includes(tag))) buildEntries.delete(key);\n }\n return;\n }\n return revalidateRuntimeCacheTags('component', tags, durations);\n },\n};\n\nexport default cacheHandler;\n"],"mappings":"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","names":[]}
|
|
@@ -35,6 +35,13 @@ __export(nextjs_cache_handler_exports, {
|
|
|
35
35
|
});
|
|
36
36
|
module.exports = __toCommonJS(nextjs_cache_handler_exports);
|
|
37
37
|
|
|
38
|
+
// src/nextjs-cache-clock.ts
|
|
39
|
+
var NEGLIGIBLE_LAG_MS = 1e3;
|
|
40
|
+
var nextClockLagMs = () => {
|
|
41
|
+
const lag = Date.now() - (performance.timeOrigin + performance.now());
|
|
42
|
+
return Math.abs(lag) < NEGLIGIBLE_LAG_MS ? 0 : lag;
|
|
43
|
+
};
|
|
44
|
+
|
|
38
45
|
// src/nextjs-runtime-cache-client.ts
|
|
39
46
|
var cachedToken;
|
|
40
47
|
var apiBaseUrl = () => (process.env.GIGADRIVE_API_URL ?? "https://api.gigadrive.network").replace(/\/$/, "");
|
|
@@ -192,10 +199,17 @@ var valueTags = (value) => {
|
|
|
192
199
|
if (typeof header !== "string") return [];
|
|
193
200
|
return header.split(",").map((tag) => tag.trim()).filter(Boolean);
|
|
194
201
|
};
|
|
202
|
+
var FORCED_STALE_LAST_MODIFIED = 1;
|
|
203
|
+
var onNextClock = (entry) => {
|
|
204
|
+
const lag = nextClockLagMs();
|
|
205
|
+
return entry && lag !== 0 && entry.lastModified > FORCED_STALE_LAST_MODIFIED ? { ...entry, lastModified: entry.lastModified - lag } : entry;
|
|
206
|
+
};
|
|
195
207
|
var collectTags = (entryTags, value, context = {}) => [
|
|
196
208
|
.../* @__PURE__ */ new Set([...entryTags ?? [], ...context.tags ?? [], ...context.softTags ?? [], ...valueTags(value)])
|
|
197
209
|
];
|
|
198
210
|
var isBuildPhase = () => process.env.GIGADRIVE_NEXT_BUILD === "1";
|
|
211
|
+
var BUILD_OUTPUT_AGE_MS = 30 * 24 * 60 * 6e4;
|
|
212
|
+
var withBuildOutputAge = (entry, context) => entry.lastModified === 0 && context.kind !== "FETCH" ? { ...entry, lastModified: Date.now() - BUILD_OUTPUT_AGE_MS } : entry;
|
|
199
213
|
var LOCAL_ENTRY_TTL_MS = 5 * 6e4;
|
|
200
214
|
var LOCAL_CACHE_MAX_ENTRIES = 256;
|
|
201
215
|
var LOCAL_CACHE_MAX_TOTAL_BYTES = 64 * 1024 * 1024;
|
|
@@ -296,7 +310,7 @@ var GigadriveNextCacheHandler = class {
|
|
|
296
310
|
if (tags.length === 0) return entry;
|
|
297
311
|
const tagState = await getRuntimeCacheTagState("incremental", tags);
|
|
298
312
|
if (tagState.expired > entry.lastModified) return null;
|
|
299
|
-
return tagState.stale > entry.lastModified ? { ...entry, lastModified:
|
|
313
|
+
return tagState.stale > entry.lastModified ? { ...entry, lastModified: FORCED_STALE_LAST_MODIFIED } : entry;
|
|
300
314
|
}
|
|
301
315
|
async get(key, context = {}) {
|
|
302
316
|
if (isBuildPhase()) {
|
|
@@ -305,17 +319,22 @@ var GigadriveNextCacheHandler = class {
|
|
|
305
319
|
}
|
|
306
320
|
const local = localEntries.get(key);
|
|
307
321
|
if (local) {
|
|
308
|
-
return this.applyTagState(local, collectTags(local.tags, local.value, context));
|
|
322
|
+
return onNextClock(await this.applyTagState(local, collectTags(local.tags, local.value, context)));
|
|
309
323
|
}
|
|
310
324
|
const remote = await readRuntimeCache("incremental", key);
|
|
311
325
|
if (isIncrementalCacheEntry(remote)) {
|
|
312
326
|
localEntries.set(key, remote);
|
|
313
|
-
return this.applyTagState(remote, collectTags(remote.tags, remote.value, context));
|
|
327
|
+
return onNextClock(await this.applyTagState(remote, collectTags(remote.tags, remote.value, context)));
|
|
314
328
|
}
|
|
315
329
|
const buildOutputCache = await this.loadBuildOutputCache();
|
|
316
330
|
const fromDisk = await buildOutputCache?.get(key, context).catch(() => null);
|
|
317
331
|
if (!isIncrementalCacheEntry(fromDisk)) return null;
|
|
318
|
-
return
|
|
332
|
+
return onNextClock(
|
|
333
|
+
await this.applyTagState(
|
|
334
|
+
withBuildOutputAge(fromDisk, context),
|
|
335
|
+
collectTags(fromDisk.tags, fromDisk.value, context)
|
|
336
|
+
)
|
|
337
|
+
);
|
|
319
338
|
}
|
|
320
339
|
async set(key, data, context) {
|
|
321
340
|
const tags = collectTags(void 0, data, context);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/nextjs-cache-handler.ts","../src/nextjs-runtime-cache-client.ts"],"sourcesContent":["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/**\n * How long a locally cached entry may serve before the remote copy is re-read.\n * Bounds staleness when another instance rewrites the entry; tag-driven\n * invalidation is unaffected because tag state is checked remotely on every\n * read regardless of where the entry bytes came from.\n */\nconst LOCAL_ENTRY_TTL_MS = 5 * 60_000;\nconst LOCAL_CACHE_MAX_ENTRIES = 256;\nconst LOCAL_CACHE_MAX_TOTAL_BYTES = 64 * 1024 * 1024;\nconst LOCAL_CACHE_MAX_ENTRY_BYTES = 8 * 1024 * 1024;\n\ninterface LocalCacheSlot {\n entry: IncrementalCacheEntry;\n cachedAt: number;\n bytes: number;\n}\n\nconst approximateEntryBytes = (entry: IncrementalCacheEntry): number => {\n try {\n return JSON.stringify(entry)?.length ?? LOCAL_CACHE_MAX_ENTRY_BYTES + 1;\n } catch {\n // Circular or otherwise unserializable values are never cached locally.\n return LOCAL_CACHE_MAX_ENTRY_BYTES + 1;\n }\n};\n\n/**\n * Byte-bounded in-process LRU in front of the remote entry store.\n *\n * The remote store is an HTTP service backed by object storage, so every miss\n * costs a network round trip — and when that store degrades, page renders\n * degrade with it (a production incident showed second-level blob latency\n * turning every render of a cached page into a multi-second response).\n * Serving entry bytes locally removes that dependency from the hot path while\n * the per-read remote tag-state check keeps `revalidateTag` authoritative.\n */\nclass LocalEntryCache {\n private readonly slots = new Map<string, LocalCacheSlot>();\n private totalBytes = 0;\n\n get(key: string): IncrementalCacheEntry | undefined {\n const slot = this.slots.get(key);\n if (!slot) return undefined;\n if (Date.now() - slot.cachedAt > LOCAL_ENTRY_TTL_MS) {\n this.delete(key);\n return undefined;\n }\n // Refresh recency for LRU eviction.\n this.slots.delete(key);\n this.slots.set(key, slot);\n return slot.entry;\n }\n\n set(key: string, entry: IncrementalCacheEntry): void {\n const bytes = approximateEntryBytes(entry);\n if (bytes > LOCAL_CACHE_MAX_ENTRY_BYTES) {\n this.delete(key);\n return;\n }\n this.delete(key);\n this.slots.set(key, { entry, cachedAt: Date.now(), bytes });\n this.totalBytes += bytes;\n while (this.slots.size > LOCAL_CACHE_MAX_ENTRIES || this.totalBytes > LOCAL_CACHE_MAX_TOTAL_BYTES) {\n const oldest = this.slots.keys().next().value;\n if (oldest === undefined) break;\n this.delete(oldest);\n }\n }\n\n delete(key: string): void {\n const slot = this.slots.get(key);\n if (!slot) return;\n this.slots.delete(key);\n this.totalBytes -= slot.bytes;\n }\n\n clear(): void {\n this.slots.clear();\n this.totalBytes = 0;\n }\n}\n\nconst localEntries = new LocalEntryCache();\n\n/** Test-only: the local entry cache is process-wide, so suites must isolate it. */\nexport const clearLocalEntryCacheForTesting = (): void => localEntries.clear();\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 local = localEntries.get(key);\n if (local) {\n return this.applyTagState(local, collectTags(local.tags, local.value, context));\n }\n\n const remote = await readRuntimeCache('incremental', key);\n if (isIncrementalCacheEntry(remote)) {\n localEntries.set(key, 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 localEntries.set(key, entry);\n try {\n await writeRuntimeCache('incremental', key, entry, tags);\n } catch (error) {\n // The local copy would outlive a failed durable write and mask it from\n // this instance for the whole TTL; drop it so reads retry remotely.\n localEntries.delete(key);\n throw error;\n }\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","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\n/**\n * Re-establishes the client's network state after a microVM resume.\n *\n * The guest runtime delivers SIGUSR2 when the instance is restored from a\n * suspend snapshot. At that moment every kept-alive TCP connection from before\n * the freeze is dead, and the cached OAuth token may have expired while the\n * clock was stopped. Dropping the token and dialing one throwaway read\n * re-opens TCP+TLS and refreshes the token concurrently with the guest's own\n * wake-up work, so the first real cache read after a resume does not\n * serialize behind reconnection.\n *\n * Registered only when a Gigadrive deployment marker is present in the\n * environment, so local `next dev`/`next build` never gains a signal listener.\n */\nconst registerResumeRewarm = (): void => {\n // Next exposes this marker before evaluating an Edge route bundle. Bail out\n // before touching `process`: its Edge compatibility proxy throws when the\n // Node-only signal API is read, including through Reflect.\n if (Reflect.has(globalThis, 'EdgeRuntime')) return;\n\n // The deployment id is intentionally available during `next build`, but the\n // shared cache client is also bundled into Edge routes at that point. Keep\n // every Node-only signal reference behind the build-phase guard so Turbopack\n // can eliminate this branch before its Edge runtime validation runs.\n if (process.env.GIGADRIVE_NEXT_BUILD === '1') return;\n\n const onGigadrive =\n process.env.GIGADRIVE_DEPLOYMENT_ID ?? process.env.NEBULA_DEPLOYMENT_ID ?? process.env.GIGADRIVE_CLIENT_ID;\n if (!onGigadrive) return;\n\n // Resolve the optional Node signal API reflectively so non-Node runtimes can\n // load the shared client while Node deployments retain resume rewarming.\n const runtimeProcess = Reflect.get(globalThis, 'process') as NodeJS.Process | undefined;\n const addSignalListener = runtimeProcess && Reflect.get(runtimeProcess, 'on');\n if (typeof addSignalListener !== 'function') return;\n\n Reflect.apply(addSignalListener, runtimeProcess, [\n 'SIGUSR2',\n () => {\n cachedToken = undefined;\n if (!process.env.GIGADRIVE_CLIENT_ID || !process.env.GIGADRIVE_CLIENT_SECRET) return;\n void readRuntimeCache('warmup', '__resume__');\n },\n ]);\n};\nregisterResumeRewarm();\n"],"mappings":"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","names":["module"]}
|
|
1
|
+
{"version":3,"sources":["../src/nextjs-cache-handler.ts","../src/nextjs-cache-clock.ts","../src/nextjs-runtime-cache-client.ts"],"sourcesContent":["import { nextClockLagMs } from './nextjs-cache-clock';\nimport {\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/** The `lastModified` of an entry forced stale, which must stay as it is for Next. */\nconst FORCED_STALE_LAST_MODIFIED = 1;\n\n/** Moves a wall-clock entry onto the clock Next measures its age with. */\nconst onNextClock = (entry: IncrementalCacheEntry | null): IncrementalCacheEntry | null => {\n const lag = nextClockLagMs();\n return entry && lag !== 0 && entry.lastModified > FORCED_STALE_LAST_MODIFIED\n ? { ...entry, lastModified: entry.lastModified - lag }\n : entry;\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/**\n * Age reported for a build-time prerender whose file has no timestamp.\n *\n * Reproducible deployment images zero every mtime, and Next reads a\n * `lastModified` of 0 as \"written just now\", so the page would stay fresh\n * forever and never regenerate. The real age is unknown here, because build\n * metadata carries neither the time nor the page's cache lifetime. This value\n * is past most revalidate windows, which starts a background regeneration, and\n * short of the default one-year `expire`, which would block the response on a\n * render instead.\n */\nconst BUILD_OUTPUT_AGE_MS = 30 * 24 * 60 * 60_000;\n\n/** Gives page output a usable age. Fetch entries already read 0 as stale. */\nconst withBuildOutputAge = (entry: IncrementalCacheEntry, context: IncrementalCacheContext): IncrementalCacheEntry =>\n entry.lastModified === 0 && context.kind !== 'FETCH'\n ? { ...entry, lastModified: Date.now() - BUILD_OUTPUT_AGE_MS }\n : entry;\n\n/**\n * How long a locally cached entry may serve before the remote copy is re-read.\n * Bounds staleness when another instance rewrites the entry; tag-driven\n * invalidation is unaffected because tag state is checked remotely on every\n * read regardless of where the entry bytes came from.\n */\nconst LOCAL_ENTRY_TTL_MS = 5 * 60_000;\nconst LOCAL_CACHE_MAX_ENTRIES = 256;\nconst LOCAL_CACHE_MAX_TOTAL_BYTES = 64 * 1024 * 1024;\nconst LOCAL_CACHE_MAX_ENTRY_BYTES = 8 * 1024 * 1024;\n\ninterface LocalCacheSlot {\n entry: IncrementalCacheEntry;\n cachedAt: number;\n bytes: number;\n}\n\nconst approximateEntryBytes = (entry: IncrementalCacheEntry): number => {\n try {\n return JSON.stringify(entry)?.length ?? LOCAL_CACHE_MAX_ENTRY_BYTES + 1;\n } catch {\n // Circular or otherwise unserializable values are never cached locally.\n return LOCAL_CACHE_MAX_ENTRY_BYTES + 1;\n }\n};\n\n/**\n * Byte-bounded in-process LRU in front of the remote entry store.\n *\n * The remote store is an HTTP service backed by object storage, so every miss\n * costs a network round trip — and when that store degrades, page renders\n * degrade with it (a production incident showed second-level blob latency\n * turning every render of a cached page into a multi-second response).\n * Serving entry bytes locally removes that dependency from the hot path while\n * the per-read remote tag-state check keeps `revalidateTag` authoritative.\n */\nclass LocalEntryCache {\n private readonly slots = new Map<string, LocalCacheSlot>();\n private totalBytes = 0;\n\n get(key: string): IncrementalCacheEntry | undefined {\n const slot = this.slots.get(key);\n if (!slot) return undefined;\n if (Date.now() - slot.cachedAt > LOCAL_ENTRY_TTL_MS) {\n this.delete(key);\n return undefined;\n }\n // Refresh recency for LRU eviction.\n this.slots.delete(key);\n this.slots.set(key, slot);\n return slot.entry;\n }\n\n set(key: string, entry: IncrementalCacheEntry): void {\n const bytes = approximateEntryBytes(entry);\n if (bytes > LOCAL_CACHE_MAX_ENTRY_BYTES) {\n this.delete(key);\n return;\n }\n this.delete(key);\n this.slots.set(key, { entry, cachedAt: Date.now(), bytes });\n this.totalBytes += bytes;\n while (this.slots.size > LOCAL_CACHE_MAX_ENTRIES || this.totalBytes > LOCAL_CACHE_MAX_TOTAL_BYTES) {\n const oldest = this.slots.keys().next().value;\n if (oldest === undefined) break;\n this.delete(oldest);\n }\n }\n\n delete(key: string): void {\n const slot = this.slots.get(key);\n if (!slot) return;\n this.slots.delete(key);\n this.totalBytes -= slot.bytes;\n }\n\n clear(): void {\n this.slots.clear();\n this.totalBytes = 0;\n }\n}\n\nconst localEntries = new LocalEntryCache();\n\n/** Test-only: the local entry cache is process-wide, so suites must isolate it. */\nexport const clearLocalEntryCacheForTesting = (): void => localEntries.clear();\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: FORCED_STALE_LAST_MODIFIED } : 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 local = localEntries.get(key);\n if (local) {\n return onNextClock(await this.applyTagState(local, collectTags(local.tags, local.value, context)));\n }\n\n const remote = await readRuntimeCache('incremental', key);\n if (isIncrementalCacheEntry(remote)) {\n localEntries.set(key, remote);\n return onNextClock(await 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 onNextClock(\n await this.applyTagState(\n withBuildOutputAge(fromDisk, context),\n collectTags(fromDisk.tags, fromDisk.value, context)\n )\n );\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 localEntries.set(key, entry);\n try {\n await writeRuntimeCache('incremental', key, entry, tags);\n } catch (error) {\n // The local copy would outlive a failed durable write and mask it from\n // this instance for the whole TTL; drop it so reads retry remotely.\n localEntries.delete(key);\n throw error;\n }\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","/** Differences below this are ordinary timer noise, not a clock that fell behind. */\nconst NEGLIGIBLE_LAG_MS = 1_000;\n\n/**\n * How far Next's clock trails the wall clock.\n *\n * Next measures cache age with `performance.timeOrigin + performance.now()`,\n * which is built on the monotonic clock. A process resumed from a snapshot keeps\n * its monotonic clock from before the pause, while the wall clock is corrected,\n * so Next's clock falls behind by every moment spent paused. An entry stamped\n * with the wall clock then looks newer than it is and never goes stale.\n *\n * Stored timestamps use the wall clock, because other processes read them.\n * Anything handed to Next is shifted onto its clock by this amount.\n */\nexport const nextClockLagMs = (): number => {\n const lag = Date.now() - (performance.timeOrigin + performance.now());\n return Math.abs(lag) < NEGLIGIBLE_LAG_MS ? 0 : lag;\n};\n","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\n/**\n * Re-establishes the client's network state after a microVM resume.\n *\n * The guest runtime delivers SIGUSR2 when the instance is restored from a\n * suspend snapshot. At that moment every kept-alive TCP connection from before\n * the freeze is dead, and the cached OAuth token may have expired while the\n * clock was stopped. Dropping the token and dialing one throwaway read\n * re-opens TCP+TLS and refreshes the token concurrently with the guest's own\n * wake-up work, so the first real cache read after a resume does not\n * serialize behind reconnection.\n *\n * Registered only when a Gigadrive deployment marker is present in the\n * environment, so local `next dev`/`next build` never gains a signal listener.\n */\nconst registerResumeRewarm = (): void => {\n // Next exposes this marker before evaluating an Edge route bundle. Bail out\n // before touching `process`: its Edge compatibility proxy throws when the\n // Node-only signal API is read, including through Reflect.\n if (Reflect.has(globalThis, 'EdgeRuntime')) return;\n\n // The deployment id is intentionally available during `next build`, but the\n // shared cache client is also bundled into Edge routes at that point. Keep\n // every Node-only signal reference behind the build-phase guard so Turbopack\n // can eliminate this branch before its Edge runtime validation runs.\n if (process.env.GIGADRIVE_NEXT_BUILD === '1') return;\n\n const onGigadrive =\n process.env.GIGADRIVE_DEPLOYMENT_ID ?? process.env.NEBULA_DEPLOYMENT_ID ?? process.env.GIGADRIVE_CLIENT_ID;\n if (!onGigadrive) return;\n\n // Resolve the optional Node signal API reflectively so non-Node runtimes can\n // load the shared client while Node deployments retain resume rewarming.\n const runtimeProcess = Reflect.get(globalThis, 'process') as NodeJS.Process | undefined;\n const addSignalListener = runtimeProcess && Reflect.get(runtimeProcess, 'on');\n if (typeof addSignalListener !== 'function') return;\n\n Reflect.apply(addSignalListener, runtimeProcess, [\n 'SIGUSR2',\n () => {\n cachedToken = undefined;\n if (!process.env.GIGADRIVE_CLIENT_ID || !process.env.GIGADRIVE_CLIENT_SECRET) return;\n void readRuntimeCache('warmup', '__resume__');\n },\n ]);\n};\nregisterResumeRewarm();\n"],"mappings":"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","names":["module"]}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
// src/nextjs-cache-clock.ts
|
|
2
|
+
var NEGLIGIBLE_LAG_MS = 1e3;
|
|
3
|
+
var nextClockLagMs = () => {
|
|
4
|
+
const lag = Date.now() - (performance.timeOrigin + performance.now());
|
|
5
|
+
return Math.abs(lag) < NEGLIGIBLE_LAG_MS ? 0 : lag;
|
|
6
|
+
};
|
|
7
|
+
|
|
1
8
|
// src/nextjs-runtime-cache-client.ts
|
|
2
9
|
var cachedToken;
|
|
3
10
|
var apiBaseUrl = () => (process.env.GIGADRIVE_API_URL ?? "https://api.gigadrive.network").replace(/\/$/, "");
|
|
@@ -155,10 +162,17 @@ var valueTags = (value) => {
|
|
|
155
162
|
if (typeof header !== "string") return [];
|
|
156
163
|
return header.split(",").map((tag) => tag.trim()).filter(Boolean);
|
|
157
164
|
};
|
|
165
|
+
var FORCED_STALE_LAST_MODIFIED = 1;
|
|
166
|
+
var onNextClock = (entry) => {
|
|
167
|
+
const lag = nextClockLagMs();
|
|
168
|
+
return entry && lag !== 0 && entry.lastModified > FORCED_STALE_LAST_MODIFIED ? { ...entry, lastModified: entry.lastModified - lag } : entry;
|
|
169
|
+
};
|
|
158
170
|
var collectTags = (entryTags, value, context = {}) => [
|
|
159
171
|
.../* @__PURE__ */ new Set([...entryTags ?? [], ...context.tags ?? [], ...context.softTags ?? [], ...valueTags(value)])
|
|
160
172
|
];
|
|
161
173
|
var isBuildPhase = () => process.env.GIGADRIVE_NEXT_BUILD === "1";
|
|
174
|
+
var BUILD_OUTPUT_AGE_MS = 30 * 24 * 60 * 6e4;
|
|
175
|
+
var withBuildOutputAge = (entry, context) => entry.lastModified === 0 && context.kind !== "FETCH" ? { ...entry, lastModified: Date.now() - BUILD_OUTPUT_AGE_MS } : entry;
|
|
162
176
|
var LOCAL_ENTRY_TTL_MS = 5 * 6e4;
|
|
163
177
|
var LOCAL_CACHE_MAX_ENTRIES = 256;
|
|
164
178
|
var LOCAL_CACHE_MAX_TOTAL_BYTES = 64 * 1024 * 1024;
|
|
@@ -259,7 +273,7 @@ var GigadriveNextCacheHandler = class {
|
|
|
259
273
|
if (tags.length === 0) return entry;
|
|
260
274
|
const tagState = await getRuntimeCacheTagState("incremental", tags);
|
|
261
275
|
if (tagState.expired > entry.lastModified) return null;
|
|
262
|
-
return tagState.stale > entry.lastModified ? { ...entry, lastModified:
|
|
276
|
+
return tagState.stale > entry.lastModified ? { ...entry, lastModified: FORCED_STALE_LAST_MODIFIED } : entry;
|
|
263
277
|
}
|
|
264
278
|
async get(key, context = {}) {
|
|
265
279
|
if (isBuildPhase()) {
|
|
@@ -268,17 +282,22 @@ var GigadriveNextCacheHandler = class {
|
|
|
268
282
|
}
|
|
269
283
|
const local = localEntries.get(key);
|
|
270
284
|
if (local) {
|
|
271
|
-
return this.applyTagState(local, collectTags(local.tags, local.value, context));
|
|
285
|
+
return onNextClock(await this.applyTagState(local, collectTags(local.tags, local.value, context)));
|
|
272
286
|
}
|
|
273
287
|
const remote = await readRuntimeCache("incremental", key);
|
|
274
288
|
if (isIncrementalCacheEntry(remote)) {
|
|
275
289
|
localEntries.set(key, remote);
|
|
276
|
-
return this.applyTagState(remote, collectTags(remote.tags, remote.value, context));
|
|
290
|
+
return onNextClock(await this.applyTagState(remote, collectTags(remote.tags, remote.value, context)));
|
|
277
291
|
}
|
|
278
292
|
const buildOutputCache = await this.loadBuildOutputCache();
|
|
279
293
|
const fromDisk = await buildOutputCache?.get(key, context).catch(() => null);
|
|
280
294
|
if (!isIncrementalCacheEntry(fromDisk)) return null;
|
|
281
|
-
return
|
|
295
|
+
return onNextClock(
|
|
296
|
+
await this.applyTagState(
|
|
297
|
+
withBuildOutputAge(fromDisk, context),
|
|
298
|
+
collectTags(fromDisk.tags, fromDisk.value, context)
|
|
299
|
+
)
|
|
300
|
+
);
|
|
282
301
|
}
|
|
283
302
|
async set(key, data, context) {
|
|
284
303
|
const tags = collectTags(void 0, data, context);
|