@leejpsd/nextjs-cache-handler 0.3.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/AGENTS.md +33 -0
  2. package/CHANGELOG.md +43 -0
  3. package/README.md +22 -3
  4. package/bin/cli.cjs +10 -0
  5. package/dist/cache-components/index.cjs +150 -4
  6. package/dist/cache-components/index.cjs.map +1 -1
  7. package/dist/cache-components/index.d.cts +1 -1
  8. package/dist/cache-components/index.d.ts +1 -1
  9. package/dist/cache-components/index.js +150 -4
  10. package/dist/cache-components/index.js.map +1 -1
  11. package/dist/cli/index.cjs +937 -0
  12. package/dist/cli/index.cjs.map +1 -0
  13. package/dist/cli/index.d.cts +28 -0
  14. package/dist/cli/index.d.ts +28 -0
  15. package/dist/cli/index.js +927 -0
  16. package/dist/cli/index.js.map +1 -0
  17. package/dist/incremental/index.cjs +77 -3
  18. package/dist/incremental/index.cjs.map +1 -1
  19. package/dist/incremental/index.d.cts +1 -1
  20. package/dist/incremental/index.d.ts +1 -1
  21. package/dist/incremental/index.js +77 -3
  22. package/dist/incremental/index.js.map +1 -1
  23. package/dist/index.cjs +153 -5
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.d.cts +1 -1
  26. package/dist/index.d.ts +1 -1
  27. package/dist/index.js +153 -5
  28. package/dist/index.js.map +1 -1
  29. package/dist/ops/index.d.cts +1 -1
  30. package/dist/ops/index.d.ts +1 -1
  31. package/dist/otel/index.d.cts +1 -1
  32. package/dist/otel/index.d.ts +1 -1
  33. package/dist/seed/index.cjs +560 -0
  34. package/dist/seed/index.cjs.map +1 -0
  35. package/dist/seed/index.d.cts +53 -0
  36. package/dist/seed/index.d.ts +53 -0
  37. package/dist/seed/index.js +549 -0
  38. package/dist/seed/index.js.map +1 -0
  39. package/dist/shared/client/adapter-ioredis.cjs +40 -1
  40. package/dist/shared/client/adapter-ioredis.cjs.map +1 -1
  41. package/dist/shared/client/adapter-ioredis.d.cts +1 -1
  42. package/dist/shared/client/adapter-ioredis.d.ts +1 -1
  43. package/dist/shared/client/adapter-ioredis.js +40 -1
  44. package/dist/shared/client/adapter-ioredis.js.map +1 -1
  45. package/dist/shared/client/adapter-redis.cjs +34 -1
  46. package/dist/shared/client/adapter-redis.cjs.map +1 -1
  47. package/dist/shared/client/adapter-redis.d.cts +1 -1
  48. package/dist/shared/client/adapter-redis.d.ts +1 -1
  49. package/dist/shared/client/adapter-redis.js +34 -1
  50. package/dist/shared/client/adapter-redis.js.map +1 -1
  51. package/dist/{types-Bl-rkLKX.d.cts → types-C1vJ_sED.d.cts} +25 -1
  52. package/dist/{types-Bl-rkLKX.d.ts → types-C1vJ_sED.d.ts} +25 -1
  53. package/llms.txt +27 -0
  54. package/package.json +27 -2
  55. package/rules/nextjs-cache-rules.md +23 -0
  56. package/setup-instructions/setup.md +179 -0
  57. package/skills/nextjs-redis-cache/SKILL.md +182 -0
@@ -1,4 +1,4 @@
1
- import { d as MetricEventType } from '../types-Bl-rkLKX.cjs';
1
+ import { e as MetricEventType } from '../types-C1vJ_sED.cjs';
2
2
 
3
3
  /**
4
4
  * Process-local metric counters with an optional emit hook.
@@ -1,4 +1,4 @@
1
- import { d as MetricEventType } from '../types-Bl-rkLKX.js';
1
+ import { e as MetricEventType } from '../types-C1vJ_sED.js';
2
2
 
3
3
  /**
4
4
  * Process-local metric counters with an optional emit hook.
@@ -1,4 +1,4 @@
1
- import { M as MetricEmitter } from '../types-Bl-rkLKX.cjs';
1
+ import { M as MetricEmitter } from '../types-C1vJ_sED.cjs';
2
2
 
3
3
  /**
4
4
  * Built-in OpenTelemetry metric emitter.
@@ -1,4 +1,4 @@
1
- import { M as MetricEmitter } from '../types-Bl-rkLKX.js';
1
+ import { M as MetricEmitter } from '../types-C1vJ_sED.js';
2
2
 
3
3
  /**
4
4
  * Built-in OpenTelemetry metric emitter.
@@ -0,0 +1,560 @@
1
+ 'use strict';
2
+
3
+ var module$1 = require('module');
4
+ var path2 = require('path');
5
+ var fs = require('fs');
6
+ var util = require('util');
7
+ var zlib = require('zlib');
8
+
9
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
10
+
11
+ var path2__default = /*#__PURE__*/_interopDefault(path2);
12
+ var fs__default = /*#__PURE__*/_interopDefault(fs);
13
+
14
+ var __defProp = Object.defineProperty;
15
+ var __getOwnPropNames = Object.getOwnPropertyNames;
16
+ var __esm = (fn, res) => function __init() {
17
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
18
+ };
19
+ var __export = (target, all) => {
20
+ for (var name in all)
21
+ __defProp(target, name, { get: all[name], enumerable: true });
22
+ };
23
+
24
+ // node_modules/tsup/assets/cjs_shims.js
25
+ var getImportMetaUrl, importMetaUrl;
26
+ var init_cjs_shims = __esm({
27
+ "node_modules/tsup/assets/cjs_shims.js"() {
28
+ getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
29
+ importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
30
+ }
31
+ });
32
+ function nodeRequire(id) {
33
+ try {
34
+ return packageRequire(id);
35
+ } catch {
36
+ return cwdRequire(id);
37
+ }
38
+ }
39
+ var packageRequire, cwdRequire;
40
+ var init_node_require = __esm({
41
+ "src/shared/node-require.ts"() {
42
+ init_cjs_shims();
43
+ packageRequire = module$1.createRequire(importMetaUrl);
44
+ cwdRequire = module$1.createRequire(
45
+ path2__default.default.join(process.cwd(), "__resolve-anchor__.js")
46
+ );
47
+ }
48
+ });
49
+
50
+ // src/shared/client/adapter-redis.ts
51
+ var adapter_redis_exports = {};
52
+ __export(adapter_redis_exports, {
53
+ adaptRedisV5: () => adaptRedisV5,
54
+ createRedisV5Client: () => createRedisV5Client
55
+ });
56
+ function adaptRedisV5(client) {
57
+ const like = client;
58
+ like.dispose = () => {
59
+ try {
60
+ client.destroy();
61
+ } catch {
62
+ }
63
+ };
64
+ like.subscribe = async (channel, onMessage, onDown) => {
65
+ const sub = client.duplicate();
66
+ const teardown = () => {
67
+ try {
68
+ sub.destroy();
69
+ } catch {
70
+ }
71
+ };
72
+ let down = false;
73
+ const markDown = () => {
74
+ if (down) return;
75
+ down = true;
76
+ teardown();
77
+ onDown?.();
78
+ };
79
+ sub.on("error", markDown);
80
+ sub.on("end", markDown);
81
+ try {
82
+ await sub.connect();
83
+ await sub.subscribe(channel, (message) => onMessage(message));
84
+ } catch (err) {
85
+ teardown();
86
+ throw err;
87
+ }
88
+ return async () => teardown();
89
+ };
90
+ return like;
91
+ }
92
+ function createRedisV5Client(config) {
93
+ const { createClient } = nodeRequire("redis");
94
+ const useTls = config.tls ?? config.url.startsWith("rediss://");
95
+ const opts = {
96
+ url: config.url,
97
+ socket: {
98
+ connectTimeout: config.connectTimeout ?? 1500,
99
+ reconnectStrategy: false,
100
+ ...useTls ? { tls: true } : {}
101
+ }
102
+ };
103
+ if (config.password !== void 0) opts.password = config.password;
104
+ return createClient(opts);
105
+ }
106
+ var init_adapter_redis = __esm({
107
+ "src/shared/client/adapter-redis.ts"() {
108
+ init_cjs_shims();
109
+ init_node_require();
110
+ }
111
+ });
112
+
113
+ // src/shared/client/adapter-ioredis.ts
114
+ var adapter_ioredis_exports = {};
115
+ __export(adapter_ioredis_exports, {
116
+ adaptCluster: () => adaptCluster,
117
+ adaptIoredis: () => adaptIoredis,
118
+ createIoredisClient: () => createIoredisClient,
119
+ createIoredisCluster: () => createIoredisCluster,
120
+ createIoredisSentinel: () => createIoredisSentinel
121
+ });
122
+ function adapt(client, isClusterClient = false) {
123
+ return {
124
+ get isOpen() {
125
+ return client.status === "ready";
126
+ },
127
+ connect: async () => {
128
+ if (client.status !== "ready" && client.status !== "connecting") {
129
+ await client.connect();
130
+ }
131
+ },
132
+ get: (k) => client.get(k),
133
+ set: (k, v, opts) => {
134
+ const args = [];
135
+ if (opts?.EX !== void 0) args.push("EX", opts.EX);
136
+ if (opts?.NX) args.push("NX");
137
+ return client.set(k, v, ...args);
138
+ },
139
+ del: async (keys) => {
140
+ const arr = Array.isArray(keys) ? keys : [keys];
141
+ if (arr.length === 0) return 0;
142
+ return client.del(...arr);
143
+ },
144
+ sAdd: async (k, m) => {
145
+ const arr = Array.isArray(m) ? m : [m];
146
+ if (arr.length === 0) return 0;
147
+ return client.sadd(k, ...arr);
148
+ },
149
+ sMembers: (k) => client.smembers(k),
150
+ expire: async (k, s) => {
151
+ const r = await client.expire(k, s);
152
+ return r;
153
+ },
154
+ mGet: async (keys) => {
155
+ if (keys.length === 0) return [];
156
+ return client.mget(...keys);
157
+ },
158
+ eval: (script, opts) => client.eval(
159
+ script,
160
+ opts.keys.length,
161
+ ...opts.keys,
162
+ ...opts.arguments
163
+ ),
164
+ evalSha: (sha, opts) => client.evalsha(
165
+ sha,
166
+ opts.keys.length,
167
+ ...opts.keys,
168
+ ...opts.arguments
169
+ ),
170
+ scriptLoad: async (script) => await client.script("LOAD", script),
171
+ scanIterator: async function* ({ MATCH, COUNT }) {
172
+ if (isClusterClient) {
173
+ const cluster = client;
174
+ for (const node of cluster.nodes("master")) {
175
+ let cursor = "0";
176
+ do {
177
+ const [next, batch] = await node.scan(
178
+ cursor,
179
+ "MATCH",
180
+ MATCH,
181
+ "COUNT",
182
+ COUNT ?? 100
183
+ );
184
+ cursor = next;
185
+ if (batch.length > 0) yield batch;
186
+ } while (cursor !== "0");
187
+ }
188
+ } else {
189
+ const redis = client;
190
+ let cursor = "0";
191
+ do {
192
+ const [next, batch] = await redis.scan(
193
+ cursor,
194
+ "MATCH",
195
+ MATCH,
196
+ "COUNT",
197
+ COUNT ?? 100
198
+ );
199
+ cursor = next;
200
+ if (batch.length > 0) yield batch;
201
+ } while (cursor !== "0");
202
+ }
203
+ },
204
+ ping: () => client.ping(),
205
+ on: (event, listener) => {
206
+ client.on(event, listener);
207
+ return client;
208
+ },
209
+ publish: (channel, message) => client.publish(channel, message),
210
+ ...isClusterClient ? {} : {
211
+ subscribe: async (channel, onMessage, onDown) => {
212
+ const sub = client.duplicate();
213
+ let down = false;
214
+ const markDown = () => {
215
+ if (down) return;
216
+ down = true;
217
+ try {
218
+ sub.disconnect();
219
+ } catch {
220
+ }
221
+ onDown?.();
222
+ };
223
+ sub.on("error", markDown);
224
+ sub.on("end", markDown);
225
+ try {
226
+ if (sub.status !== "ready" && sub.status !== "connecting") {
227
+ await sub.connect();
228
+ }
229
+ await sub.subscribe(channel);
230
+ } catch (err) {
231
+ markDown();
232
+ throw err;
233
+ }
234
+ sub.on("message", (ch, message) => {
235
+ if (ch === channel) onMessage(message);
236
+ });
237
+ return async () => {
238
+ down = true;
239
+ sub.disconnect();
240
+ };
241
+ }
242
+ },
243
+ dispose: () => {
244
+ client.disconnect();
245
+ }
246
+ };
247
+ }
248
+ function adaptIoredis(client) {
249
+ return adapt(client, false);
250
+ }
251
+ function adaptCluster(cluster) {
252
+ return adapt(cluster, true);
253
+ }
254
+ function createIoredisClient(config) {
255
+ const IORedis = nodeRequire("ioredis");
256
+ const Ctor = IORedis.default ?? IORedis;
257
+ return new Ctor(config.url, {
258
+ password: config.password,
259
+ connectTimeout: config.connectTimeout ?? 1500,
260
+ tls: config.tls ? {} : void 0,
261
+ lazyConnect: true,
262
+ maxRetriesPerRequest: 1,
263
+ // Batch same-tick commands into one RTT (matches redis@5's default
264
+ // behavior) — makes Promise.all fan-outs a single pipeline.
265
+ enableAutoPipelining: true
266
+ });
267
+ }
268
+ function createIoredisSentinel(config) {
269
+ const IORedis = nodeRequire("ioredis");
270
+ const Ctor = IORedis.default ?? IORedis;
271
+ return new Ctor({
272
+ sentinels: config.sentinels,
273
+ name: config.name,
274
+ password: config.password,
275
+ sentinelPassword: config.sentinelPassword,
276
+ connectTimeout: config.connectTimeout ?? 1500,
277
+ tls: config.tls ? {} : void 0,
278
+ lazyConnect: true,
279
+ maxRetriesPerRequest: 1,
280
+ enableAutoPipelining: true
281
+ });
282
+ }
283
+ function createIoredisCluster(config) {
284
+ const IORedis = nodeRequire("ioredis");
285
+ const ClusterCtor = IORedis.Cluster ?? IORedis.default.Cluster;
286
+ return new ClusterCtor(config.nodes, {
287
+ redisOptions: {
288
+ password: config.password,
289
+ tls: config.tls ? {} : void 0
290
+ },
291
+ enableReadyCheck: true,
292
+ lazyConnect: true,
293
+ // Same-slot commands issued in the same tick share one pipeline.
294
+ enableAutoPipelining: true
295
+ });
296
+ }
297
+ var init_adapter_ioredis = __esm({
298
+ "src/shared/client/adapter-ioredis.ts"() {
299
+ init_cjs_shims();
300
+ init_node_require();
301
+ }
302
+ });
303
+
304
+ // src/seed/index.ts
305
+ init_cjs_shims();
306
+
307
+ // src/shared/client/index.ts
308
+ init_cjs_shims();
309
+ async function buildClient(source) {
310
+ if (typeof source === "function") {
311
+ return await source();
312
+ }
313
+ switch (source.type) {
314
+ case "redis": {
315
+ const { adaptRedisV5: adaptRedisV52, createRedisV5Client: createRedisV5Client2 } = await Promise.resolve().then(() => (init_adapter_redis(), adapter_redis_exports));
316
+ return adaptRedisV52(createRedisV5Client2(source));
317
+ }
318
+ case "ioredis": {
319
+ const { adaptIoredis: adaptIoredis2, createIoredisClient: createIoredisClient2 } = await Promise.resolve().then(() => (init_adapter_ioredis(), adapter_ioredis_exports));
320
+ return adaptIoredis2(createIoredisClient2(source));
321
+ }
322
+ case "cluster": {
323
+ const { adaptCluster: adaptCluster2, createIoredisCluster: createIoredisCluster2 } = await Promise.resolve().then(() => (init_adapter_ioredis(), adapter_ioredis_exports));
324
+ return adaptCluster2(createIoredisCluster2(source));
325
+ }
326
+ case "sentinel": {
327
+ const { adaptIoredis: adaptIoredis2, createIoredisSentinel: createIoredisSentinel2 } = await Promise.resolve().then(() => (init_adapter_ioredis(), adapter_ioredis_exports));
328
+ return adaptIoredis2(createIoredisSentinel2(source));
329
+ }
330
+ default: {
331
+ const exhaustive = source;
332
+ throw new Error(
333
+ `[next-cache] unknown client config: ${JSON.stringify(exhaustive)}`
334
+ );
335
+ }
336
+ }
337
+ }
338
+
339
+ // src/shared/compress.ts
340
+ init_cjs_shims();
341
+ var gzipAsync = util.promisify(zlib.gzip);
342
+ util.promisify(zlib.gunzip);
343
+ var brotliCompressAsync = util.promisify(zlib.brotliCompress);
344
+ util.promisify(zlib.brotliDecompress);
345
+ var GZIP_MARKER = "__ncgz__:";
346
+ var BROTLI_MARKER = "__ncbr__:";
347
+ var MIN_COMPRESS_BYTES = 1024;
348
+ var BROTLI_OPTS = {
349
+ params: { [zlib.constants.BROTLI_PARAM_QUALITY]: 5 }
350
+ };
351
+ async function compressValue(value, algo) {
352
+ if (!algo || Buffer.byteLength(value) < MIN_COMPRESS_BYTES) return value;
353
+ const input = Buffer.from(value);
354
+ if (algo === "gzip") {
355
+ return GZIP_MARKER + (await gzipAsync(input)).toString("base64");
356
+ }
357
+ return BROTLI_MARKER + (await brotliCompressAsync(input, BROTLI_OPTS)).toString("base64");
358
+ }
359
+
360
+ // src/shared/namespace.ts
361
+ init_cjs_shims();
362
+ var FALLBACK = "unversioned";
363
+ function resolveBuildNamespace(override) {
364
+ if (override !== void 0) {
365
+ return typeof override === "function" ? override() : override;
366
+ }
367
+ return process.env.DEPLOYMENT_VERSION ?? process.env.GIT_HASH ?? process.env.NEXT_DEPLOYMENT_ID ?? FALLBACK;
368
+ }
369
+ function buildKey(prefix, namespace, rest, hashTag = false) {
370
+ const ns = hashTag ? `{${namespace}}` : namespace;
371
+ return `${prefix}${ns}:${rest}`;
372
+ }
373
+
374
+ // src/incremental/serialize.ts
375
+ init_cjs_shims();
376
+ function replacer(_key, value) {
377
+ if (Buffer.isBuffer(value)) {
378
+ const env = {
379
+ __type: "Buffer",
380
+ data: value.toString("base64")
381
+ };
382
+ return env;
383
+ }
384
+ if (value instanceof Map) {
385
+ const env = {
386
+ __type: "Map",
387
+ entries: Array.from(value.entries())
388
+ };
389
+ return env;
390
+ }
391
+ return value;
392
+ }
393
+ function serializeCacheRecord(record) {
394
+ return JSON.stringify(record, replacer);
395
+ }
396
+
397
+ // src/seed/index.ts
398
+ var ONE_YEAR_SEC = 60 * 60 * 24 * 365;
399
+ var MIN_TTL_SEC = 60;
400
+ var DEFAULT_KEY_PREFIX = "next-incremental:";
401
+ function clampTtl(revalidate) {
402
+ if (revalidate === false || revalidate === void 0) return ONE_YEAR_SEC;
403
+ if (typeof revalidate === "number" && revalidate > 0) {
404
+ return Math.min(Math.max(Math.ceil(revalidate), MIN_TTL_SEC), ONE_YEAR_SEC);
405
+ }
406
+ return MIN_TTL_SEC;
407
+ }
408
+ function routeToFileBase(route) {
409
+ return route === "/" ? "/index" : route;
410
+ }
411
+ function readMeta(file) {
412
+ try {
413
+ return JSON.parse(fs__default.default.readFileSync(file, "utf8"));
414
+ } catch {
415
+ return null;
416
+ }
417
+ }
418
+ async function writeRecord(client, key, value, revalidateSec, opts) {
419
+ const record = {
420
+ lastModified: Date.now(),
421
+ value,
422
+ revalidateSec: clampTtl(revalidateSec)
423
+ };
424
+ const redisKey = buildKey(`${opts.keyPrefix}entry:`, opts.ns, key, opts.hashTag);
425
+ const payload = await compressValue(
426
+ serializeCacheRecord(record),
427
+ opts.compression
428
+ );
429
+ const result = await client.set(redisKey, payload, {
430
+ EX: clampTtl(revalidateSec),
431
+ NX: true
432
+ });
433
+ return result === null ? "skipped" : "written";
434
+ }
435
+ async function seedBuildOutput(options) {
436
+ const dir = path2__default.default.resolve(options.dir ?? ".next");
437
+ const keyPrefix = options.keyPrefix ?? DEFAULT_KEY_PREFIX;
438
+ const hashTag = options.hashTag ?? false;
439
+ const ns = resolveBuildNamespace(options.buildNamespace);
440
+ const warn = options.logger?.warn ?? (() => {
441
+ });
442
+ const summary = {
443
+ routes: 0,
444
+ pages: 0,
445
+ fetch: 0,
446
+ skippedExisting: 0,
447
+ skippedIncomplete: 0,
448
+ skippedRouteHandlers: 0,
449
+ errors: []
450
+ };
451
+ const manifestPath = path2__default.default.join(dir, "prerender-manifest.json");
452
+ if (!fs__default.default.existsSync(manifestPath)) {
453
+ throw new Error(
454
+ `[next-cache] no prerender-manifest.json in ${dir} \u2014 run \`next build\` first`
455
+ );
456
+ }
457
+ const manifest = JSON.parse(fs__default.default.readFileSync(manifestPath, "utf8"));
458
+ const client = await buildClient(options.client);
459
+ if (!client.isOpen) await client.connect();
460
+ const writeOpts = { keyPrefix, hashTag, ns, ...options.compression ? { compression: options.compression } : {} };
461
+ try {
462
+ for (const [route, info] of Object.entries(manifest.routes ?? {})) {
463
+ try {
464
+ const isPages = typeof info.dataRoute === "string" && info.dataRoute.endsWith(".json");
465
+ const base = routeToFileBase(route);
466
+ if (isPages) {
467
+ const html = path2__default.default.join(dir, "server", "pages", `${base.slice(1)}.html`);
468
+ const data = path2__default.default.join(dir, "server", "pages", `${base.slice(1)}.json`);
469
+ if (!fs__default.default.existsSync(html)) {
470
+ summary.skippedIncomplete += 1;
471
+ continue;
472
+ }
473
+ const value2 = {
474
+ kind: "PAGES",
475
+ html: fs__default.default.readFileSync(html, "utf8"),
476
+ pageData: fs__default.default.existsSync(data) ? JSON.parse(fs__default.default.readFileSync(data, "utf8")) : {},
477
+ headers: void 0,
478
+ status: void 0
479
+ };
480
+ const r2 = await writeRecord(client, route, value2, info.initialRevalidateSeconds, writeOpts);
481
+ if (r2 === "written") summary.pages += 1;
482
+ else summary.skippedExisting += 1;
483
+ continue;
484
+ }
485
+ const htmlPath = path2__default.default.join(dir, "server", "app", `${base.slice(1)}.html`);
486
+ const rscPath = path2__default.default.join(dir, "server", "app", `${base.slice(1)}.rsc`);
487
+ const metaPath = path2__default.default.join(dir, "server", "app", `${base.slice(1)}.meta`);
488
+ if (!fs__default.default.existsSync(htmlPath) || !fs__default.default.existsSync(metaPath)) {
489
+ if (fs__default.default.existsSync(path2__default.default.join(dir, "server", "app", `${base.slice(1)}.body`))) {
490
+ summary.skippedRouteHandlers += 1;
491
+ } else {
492
+ summary.skippedIncomplete += 1;
493
+ }
494
+ continue;
495
+ }
496
+ const meta = readMeta(metaPath) ?? {};
497
+ if (meta.postponed) {
498
+ summary.skippedIncomplete += 1;
499
+ continue;
500
+ }
501
+ let segmentData;
502
+ if (Array.isArray(meta.segmentPaths) && meta.segmentPaths.length > 0) {
503
+ const segDir = path2__default.default.join(dir, "server", "app", `${base.slice(1)}.segments`);
504
+ segmentData = /* @__PURE__ */ new Map();
505
+ for (const segPath of meta.segmentPaths) {
506
+ const segFile = path2__default.default.join(segDir, `${segPath.slice(1)}.segment.rsc`);
507
+ if (fs__default.default.existsSync(segFile)) {
508
+ segmentData.set(segPath, fs__default.default.readFileSync(segFile));
509
+ }
510
+ }
511
+ if (segmentData.size !== meta.segmentPaths.length) {
512
+ summary.skippedIncomplete += 1;
513
+ continue;
514
+ }
515
+ }
516
+ const value = {
517
+ kind: "APP_PAGE",
518
+ html: fs__default.default.readFileSync(htmlPath, "utf8"),
519
+ rscData: fs__default.default.existsSync(rscPath) ? fs__default.default.readFileSync(rscPath) : void 0,
520
+ headers: meta.headers,
521
+ status: meta.status
522
+ };
523
+ if (segmentData) value.segmentData = segmentData;
524
+ const r = await writeRecord(client, route, value, info.initialRevalidateSeconds, writeOpts);
525
+ if (r === "written") summary.routes += 1;
526
+ else summary.skippedExisting += 1;
527
+ } catch (err) {
528
+ summary.errors.push(`${route}: ${err.message}`);
529
+ warn("seed: route failed", { route, message: err.message });
530
+ }
531
+ }
532
+ const fetchDir = path2__default.default.join(dir, "cache", "fetch-cache");
533
+ if (fs__default.default.existsSync(fetchDir)) {
534
+ for (const file of fs__default.default.readdirSync(fetchDir)) {
535
+ if (file.startsWith(".")) continue;
536
+ try {
537
+ const raw = fs__default.default.readFileSync(path2__default.default.join(fetchDir, file), "utf8");
538
+ const value = JSON.parse(raw);
539
+ if (value.kind !== "FETCH") continue;
540
+ const r = await writeRecord(client, file, value, value.revalidate, writeOpts);
541
+ if (r === "written") summary.fetch += 1;
542
+ else summary.skippedExisting += 1;
543
+ } catch (err) {
544
+ summary.errors.push(`fetch:${file}: ${err.message}`);
545
+ }
546
+ }
547
+ }
548
+ } finally {
549
+ const c = client;
550
+ try {
551
+ (c.dispose ?? c.destroy ?? c.disconnect)?.call(c);
552
+ } catch {
553
+ }
554
+ }
555
+ return summary;
556
+ }
557
+
558
+ exports.seedBuildOutput = seedBuildOutput;
559
+ //# sourceMappingURL=index.cjs.map
560
+ //# sourceMappingURL=index.cjs.map