@farm.js/core 0.1.0-beta.3 → 0.1.0-beta.4
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/agent-runtime.d.mts +2 -1
- package/dist/agent-runtime.d.ts +2 -1
- package/dist/api/route-manager.cjs +313 -10
- package/dist/api/route-manager.cjs.map +1 -1
- package/dist/api/route-manager.d.mts +3 -2
- package/dist/api/route-manager.d.ts +3 -2
- package/dist/api/route-manager.mjs +6 -5
- package/dist/api/runtime.cjs +313 -10
- package/dist/api/runtime.cjs.map +1 -1
- package/dist/api/runtime.d.mts +2 -1
- package/dist/api/runtime.d.ts +2 -1
- package/dist/api/runtime.mjs +4 -3
- package/dist/api.cjs +642 -57
- package/dist/api.cjs.map +1 -1
- package/dist/api.d.mts +3 -2
- package/dist/api.d.ts +3 -2
- package/dist/api.mjs +29 -9
- package/dist/{app-DOKxo-LH.d.ts → app-BjN-da13.d.ts} +38 -3
- package/dist/{app-BB3Iu3xh.d.mts → app-CakAr8Be.d.mts} +38 -3
- package/dist/app-markdown.d.mts +2 -1
- package/dist/app-markdown.d.ts +2 -1
- package/dist/cache.cjs +395 -12
- package/dist/cache.cjs.map +1 -1
- package/dist/cache.d.mts +100 -6
- package/dist/cache.d.ts +100 -6
- package/dist/cache.mjs +8 -2
- package/dist/{chunk-XUBZ7JI4.mjs → chunk-2CUKEMVR.mjs} +187 -16
- package/dist/chunk-2CUKEMVR.mjs.map +1 -0
- package/dist/{chunk-5QH4IKY3.mjs → chunk-3U3ZUQAQ.mjs} +2 -2
- package/dist/{chunk-6WXJ54TC.mjs → chunk-65A7QIKI.mjs} +5 -5
- package/dist/{chunk-6WXJ54TC.mjs.map → chunk-65A7QIKI.mjs.map} +1 -1
- package/dist/{chunk-OHC6VCDI.mjs → chunk-AJWRO5WE.mjs} +521 -42
- package/dist/chunk-AJWRO5WE.mjs.map +1 -0
- package/dist/chunk-ALCLXXTY.mjs +187 -0
- package/dist/chunk-ALCLXXTY.mjs.map +1 -0
- package/dist/{chunk-XJSHJN4M.mjs → chunk-AOBXKNS5.mjs} +593 -49
- package/dist/chunk-AOBXKNS5.mjs.map +1 -0
- package/dist/{chunk-CSU4KJJF.mjs → chunk-C2ROET2V.mjs} +2 -2
- package/dist/{chunk-BIOIOWXZ.mjs → chunk-HNIQFZKB.mjs} +2 -2
- package/dist/{chunk-G5I34UA6.mjs → chunk-JDEOUK5V.mjs} +4 -4
- package/dist/{chunk-T543TMFY.mjs → chunk-NM3CRAAL.mjs} +3 -2
- package/dist/chunk-NM3CRAAL.mjs.map +1 -0
- package/dist/{chunk-LWMGIEJA.mjs → chunk-PFE6GMNE.mjs} +47 -37
- package/dist/chunk-PFE6GMNE.mjs.map +1 -0
- package/dist/{chunk-6VS6CKAV.mjs → chunk-QM746KUO.mjs} +8 -1
- package/dist/chunk-QM746KUO.mjs.map +1 -0
- package/dist/{chunk-SHRJG6OG.mjs → chunk-RBRLLZL5.mjs} +18 -3
- package/dist/chunk-RBRLLZL5.mjs.map +1 -0
- package/dist/{chunk-MH6FKW2I.mjs → chunk-SADGIRS2.mjs} +3 -3
- package/dist/{chunk-NRRFGX45.mjs → chunk-TQAEEJB4.mjs} +3 -3
- package/dist/{chunk-45LKHSHJ.mjs → chunk-UB5UOSHB.mjs} +24 -12
- package/dist/chunk-UB5UOSHB.mjs.map +1 -0
- package/dist/chunk-V3XURYJO.mjs +756 -0
- package/dist/chunk-V3XURYJO.mjs.map +1 -0
- package/dist/{chunk-5FW6PQ3J.mjs → chunk-VVKWFZAH.mjs} +41 -16
- package/dist/chunk-VVKWFZAH.mjs.map +1 -0
- package/dist/{chunk-HZYXGV4K.mjs → chunk-VY2UYN5R.mjs} +24 -4
- package/dist/chunk-VY2UYN5R.mjs.map +1 -0
- package/dist/{chunk-C33RALN7.mjs → chunk-W2CRQFF6.mjs} +79 -15
- package/dist/chunk-W2CRQFF6.mjs.map +1 -0
- package/dist/{chunk-JNYVN7YP.mjs → chunk-W4UIVMVI.mjs} +2 -2
- package/dist/{chunk-JNYVN7YP.mjs.map → chunk-W4UIVMVI.mjs.map} +1 -1
- package/dist/{chunk-LG3YINQN.mjs → chunk-Z5CQMP54.mjs} +3 -3
- package/dist/{client-DiViK9yK.d.mts → client-BT1mFQas.d.mts} +23 -4
- package/dist/{client-DpEBJbts.d.ts → client-Nn0q4eO4.d.ts} +23 -4
- package/dist/{client-plugins-CTnsZz_A.d.ts → client-plugins-Cw4qUGsw.d.ts} +6 -0
- package/dist/{client-plugins-Cnr0D3kk.d.mts → client-plugins-DQQ1adXM.d.mts} +6 -0
- package/dist/client-plugins.d.mts +2 -1
- package/dist/client-plugins.d.ts +2 -1
- package/dist/client.cjs +665 -82
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.mts +70 -4
- package/dist/client.d.ts +70 -4
- package/dist/client.mjs +10 -7
- package/dist/config.d.mts +2 -1
- package/dist/config.d.ts +2 -1
- package/dist/{devtools-HE4FTUDN.mjs → devtools-LVC3VB5P.mjs} +4 -4
- package/dist/docs.cjs +11 -1
- package/dist/docs.cjs.map +1 -1
- package/dist/docs.mjs +4 -4
- package/dist/index.cjs +2112 -197
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +12 -11
- package/dist/index.d.ts +12 -11
- package/dist/index.mjs +49 -21
- package/dist/index.mjs.map +1 -1
- package/dist/integrations.d.mts +2 -1
- package/dist/integrations.d.ts +2 -1
- package/dist/internal/build-runtime.cjs +1455 -90
- package/dist/internal/build-runtime.cjs.map +1 -1
- package/dist/internal/build-runtime.d.mts +4 -3
- package/dist/internal/build-runtime.d.ts +4 -3
- package/dist/internal/build-runtime.mjs +15 -14
- package/dist/internal/config-runtime.cjs +12 -1
- package/dist/internal/config-runtime.cjs.map +1 -1
- package/dist/internal/config-runtime.d.mts +3 -2
- package/dist/internal/config-runtime.d.ts +3 -2
- package/dist/internal/config-runtime.mjs +3 -3
- package/dist/internal/production-runtime.cjs +282 -6
- package/dist/internal/production-runtime.cjs.map +1 -1
- package/dist/internal/production-runtime.d.mts +4 -4
- package/dist/internal/production-runtime.d.ts +4 -4
- package/dist/internal/production-runtime.mjs +4 -2
- package/dist/{metadata-eoVmHoyv.d.ts → metadata-BAPg_Nmo.d.ts} +1 -1
- package/dist/{metadata-Dp8AdvsB.d.mts → metadata-_6i9oFG3.d.mts} +1 -1
- package/dist/middleware.d.mts +4 -3
- package/dist/middleware.d.ts +4 -3
- package/dist/navigation.cjs +40 -15
- package/dist/navigation.cjs.map +1 -1
- package/dist/navigation.mjs +2 -2
- package/dist/plugin.cjs +913 -53
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.mts +2 -1
- package/dist/plugin.d.ts +2 -1
- package/dist/plugin.mjs +11 -10
- package/dist/{production-runtime-BqTnUTai.d.ts → production-runtime-BoJDc5mc.d.ts} +1 -1
- package/dist/{production-runtime-DtDdG9fc.d.mts → production-runtime-DPfgMV9Y.d.mts} +1 -1
- package/dist/routes.cjs +276 -6
- package/dist/routes.cjs.map +1 -1
- package/dist/routes.d.mts +1 -1
- package/dist/routes.d.ts +1 -1
- package/dist/routes.mjs +3 -3
- package/dist/{runtime-C1B7pSGT.d.mts → runtime-9nQjVM7X.d.mts} +8 -9
- package/dist/{runtime-CdPHt0bv.d.ts → runtime-DgeS2skB.d.ts} +8 -9
- package/dist/server-action-security.cjs +28 -10
- package/dist/server-action-security.cjs.map +1 -1
- package/dist/server-action-security.d.mts +2 -1
- package/dist/server-action-security.d.ts +2 -1
- package/dist/server-action-security.mjs +2 -2
- package/dist/server-fn-client.d.mts +1 -0
- package/dist/server-fn-client.d.ts +1 -0
- package/dist/server-fn.cjs +785 -3
- package/dist/server-fn.cjs.map +1 -1
- package/dist/server-fn.d.mts +10 -1
- package/dist/server-fn.d.ts +10 -1
- package/dist/server-fn.mjs +6 -3
- package/dist/server-plugins.d.mts +3 -2
- package/dist/server-plugins.d.ts +3 -2
- package/dist/server-query-client.cjs +278 -7
- package/dist/server-query-client.cjs.map +1 -1
- package/dist/server-query-client.mjs +4 -4
- package/dist/server-query.cjs +381 -10
- package/dist/server-query.cjs.map +1 -1
- package/dist/server-query.mjs +5 -5
- package/dist/server.cjs +1086 -76
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.mts +8 -7
- package/dist/server.d.ts +8 -7
- package/dist/server.mjs +31 -16
- package/dist/server.mjs.map +1 -1
- package/dist/testing.cjs +341 -20
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.mts +3 -2
- package/dist/testing.d.ts +3 -2
- package/dist/testing.mjs +7 -6
- package/dist/testing.mjs.map +1 -1
- package/dist/transport-BmbGTXA5.d.mts +49 -0
- package/dist/transport-BmbGTXA5.d.ts +49 -0
- package/dist/{utils-8USa5BiY.d.mts → utils-CHXlMDkg.d.mts} +1 -1
- package/dist/{utils-DKJb6K6G.d.ts → utils-Drsyz1Vj.d.ts} +1 -1
- package/dist/version.cjs +1 -1
- package/dist/version.cjs.map +1 -1
- package/dist/version.mjs +1 -1
- package/dist/vite.cjs +914 -54
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.d.mts +2 -1
- package/dist/vite.d.ts +2 -1
- package/dist/vite.mjs +11 -10
- package/package.json +1 -1
- package/types/api.d.ts +44 -0
- package/types/client.d.ts +85 -4
- package/dist/chunk-45LKHSHJ.mjs.map +0 -1
- package/dist/chunk-5FW6PQ3J.mjs.map +0 -1
- package/dist/chunk-6VS6CKAV.mjs.map +0 -1
- package/dist/chunk-C33RALN7.mjs.map +0 -1
- package/dist/chunk-GZWT6VI5.mjs +0 -382
- package/dist/chunk-GZWT6VI5.mjs.map +0 -1
- package/dist/chunk-HZYXGV4K.mjs.map +0 -1
- package/dist/chunk-LWMGIEJA.mjs.map +0 -1
- package/dist/chunk-OHC6VCDI.mjs.map +0 -1
- package/dist/chunk-SHRJG6OG.mjs.map +0 -1
- package/dist/chunk-T543TMFY.mjs.map +0 -1
- package/dist/chunk-XJSHJN4M.mjs.map +0 -1
- package/dist/chunk-XUBZ7JI4.mjs.map +0 -1
- /package/dist/{chunk-5QH4IKY3.mjs.map → chunk-3U3ZUQAQ.mjs.map} +0 -0
- /package/dist/{chunk-CSU4KJJF.mjs.map → chunk-C2ROET2V.mjs.map} +0 -0
- /package/dist/{chunk-BIOIOWXZ.mjs.map → chunk-HNIQFZKB.mjs.map} +0 -0
- /package/dist/{chunk-G5I34UA6.mjs.map → chunk-JDEOUK5V.mjs.map} +0 -0
- /package/dist/{chunk-MH6FKW2I.mjs.map → chunk-SADGIRS2.mjs.map} +0 -0
- /package/dist/{chunk-NRRFGX45.mjs.map → chunk-TQAEEJB4.mjs.map} +0 -0
- /package/dist/{chunk-LG3YINQN.mjs.map → chunk-Z5CQMP54.mjs.map} +0 -0
- /package/dist/{devtools-HE4FTUDN.mjs.map → devtools-LVC3VB5P.mjs.map} +0 -0
|
@@ -4225,7 +4225,8 @@ var init_route_rules = __esm({
|
|
|
4225
4225
|
// src/cache-invalidation.ts
|
|
4226
4226
|
function getFarmCacheInvalidationState() {
|
|
4227
4227
|
return globalState[FARM_CACHE_INVALIDATION_STATE] ?? (globalState[FARM_CACHE_INVALIDATION_STATE] = {
|
|
4228
|
-
listeners: /* @__PURE__ */ new Set()
|
|
4228
|
+
listeners: /* @__PURE__ */ new Set(),
|
|
4229
|
+
taskListeners: /* @__PURE__ */ new Set()
|
|
4229
4230
|
});
|
|
4230
4231
|
}
|
|
4231
4232
|
function encodeFarmCacheInvalidations(keys) {
|
|
@@ -4251,6 +4252,11 @@ function subscribeFarmCacheInvalidation(listener) {
|
|
|
4251
4252
|
state.listeners.add(listener);
|
|
4252
4253
|
return () => state.listeners.delete(listener);
|
|
4253
4254
|
}
|
|
4255
|
+
function subscribeFarmCacheTask(listener) {
|
|
4256
|
+
const state = getFarmCacheInvalidationState();
|
|
4257
|
+
state.taskListeners.add(listener);
|
|
4258
|
+
return () => state.taskListeners.delete(listener);
|
|
4259
|
+
}
|
|
4254
4260
|
var FARM_CACHE_INVALIDATION_HEADER, FARM_CACHE_INVALIDATION_STATE, globalState;
|
|
4255
4261
|
var init_cache_invalidation = __esm({
|
|
4256
4262
|
"src/cache-invalidation.ts"() {
|
|
@@ -4262,6 +4268,7 @@ var init_cache_invalidation = __esm({
|
|
|
4262
4268
|
__name(encodeFarmCacheInvalidations, "encodeFarmCacheInvalidations");
|
|
4263
4269
|
__name(decodeFarmCacheInvalidations, "decodeFarmCacheInvalidations");
|
|
4264
4270
|
__name(subscribeFarmCacheInvalidation, "subscribeFarmCacheInvalidation");
|
|
4271
|
+
__name(subscribeFarmCacheTask, "subscribeFarmCacheTask");
|
|
4265
4272
|
}
|
|
4266
4273
|
});
|
|
4267
4274
|
|
|
@@ -4365,6 +4372,9 @@ var init_server_action_security = __esm({
|
|
|
4365
4372
|
subscribeFarmCacheInvalidation((key) => {
|
|
4366
4373
|
getServerActionExecutionContext()?.invalidations.add(key);
|
|
4367
4374
|
});
|
|
4375
|
+
subscribeFarmCacheTask((task) => {
|
|
4376
|
+
getServerActionExecutionContext()?.cacheTasks.add(task);
|
|
4377
|
+
});
|
|
4368
4378
|
__name(getServerActionStorage, "getServerActionStorage");
|
|
4369
4379
|
__name(parseBodySizeLimit, "parseBodySizeLimit");
|
|
4370
4380
|
__name(normalizeAllowedOriginPattern, "normalizeAllowedOriginPattern");
|
|
@@ -5522,6 +5532,7 @@ async function resolveConfig(userConfig, mode) {
|
|
|
5522
5532
|
observability: userConfig.observability ?? false,
|
|
5523
5533
|
devtools: resolveFarmDevtoolsConfig(userConfig.devtools, mode),
|
|
5524
5534
|
storage: userConfig.storage || {},
|
|
5535
|
+
cache: userConfig.cache || {},
|
|
5525
5536
|
suppressLintOnLink: userConfig.suppressLintOnLink ?? false,
|
|
5526
5537
|
experimental: {
|
|
5527
5538
|
serverComponents: false,
|
|
@@ -6316,6 +6327,9 @@ var init_bridge = __esm({
|
|
|
6316
6327
|
function getFarmDataCache() {
|
|
6317
6328
|
return sharedFarmDataCache;
|
|
6318
6329
|
}
|
|
6330
|
+
function configureFarmCache(config) {
|
|
6331
|
+
sharedFarmDataCache.configure(config);
|
|
6332
|
+
}
|
|
6319
6333
|
function createPathCacheTag(routePath) {
|
|
6320
6334
|
return `path:${normalizeRevalidatePath(routePath)}`;
|
|
6321
6335
|
}
|
|
@@ -6369,6 +6383,46 @@ function normalizeCacheTag(tag) {
|
|
|
6369
6383
|
}
|
|
6370
6384
|
return normalized;
|
|
6371
6385
|
}
|
|
6386
|
+
function normalizeCacheNamespace(namespace) {
|
|
6387
|
+
if (typeof namespace !== "string") {
|
|
6388
|
+
throw new TypeError("Cache namespace must be a string.");
|
|
6389
|
+
}
|
|
6390
|
+
const normalized = namespace.trim().replace(/:+$/g, "");
|
|
6391
|
+
if (!normalized) {
|
|
6392
|
+
throw new Error("Cache namespace cannot be empty.");
|
|
6393
|
+
}
|
|
6394
|
+
return normalized;
|
|
6395
|
+
}
|
|
6396
|
+
function normalizeAdapterVersion(value) {
|
|
6397
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
|
|
6398
|
+
}
|
|
6399
|
+
function normalizePositiveDuration(value, fallback, name) {
|
|
6400
|
+
if (value === void 0) return fallback;
|
|
6401
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
6402
|
+
throw new TypeError(`${name} must be a positive number of milliseconds.`);
|
|
6403
|
+
}
|
|
6404
|
+
return Math.floor(value);
|
|
6405
|
+
}
|
|
6406
|
+
function delay(ms) {
|
|
6407
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
6408
|
+
}
|
|
6409
|
+
function normalizeCacheOptionsTags(options) {
|
|
6410
|
+
const tags = /* @__PURE__ */ new Set();
|
|
6411
|
+
for (const tag of options.tags ?? []) {
|
|
6412
|
+
tags.add(normalizeCacheTag(tag));
|
|
6413
|
+
}
|
|
6414
|
+
for (const routePath of options.paths ?? []) {
|
|
6415
|
+
tags.add(createPathCacheTag(routePath));
|
|
6416
|
+
}
|
|
6417
|
+
return tags;
|
|
6418
|
+
}
|
|
6419
|
+
function assertFarmCacheEntry(entry, expectedKey) {
|
|
6420
|
+
if (!entry || typeof entry !== "object" || entry.key !== expectedKey || !Array.isArray(entry.tags) || typeof entry.createdAt !== "number") {
|
|
6421
|
+
throw new TypeError(
|
|
6422
|
+
`Cache adapter returned an invalid entry for ${JSON.stringify(expectedKey)}.`
|
|
6423
|
+
);
|
|
6424
|
+
}
|
|
6425
|
+
}
|
|
6372
6426
|
function getFunctionCacheIdentity(fn) {
|
|
6373
6427
|
if (fn.name) {
|
|
6374
6428
|
return `name:${fn.name}`;
|
|
@@ -6429,11 +6483,51 @@ var init_cache = __esm({
|
|
|
6429
6483
|
init_bridge();
|
|
6430
6484
|
init_cache_invalidation();
|
|
6431
6485
|
_FarmDataCache = class _FarmDataCache {
|
|
6432
|
-
constructor() {
|
|
6486
|
+
constructor(config = {}) {
|
|
6433
6487
|
this.entries = /* @__PURE__ */ new Map();
|
|
6434
6488
|
this.inflight = /* @__PURE__ */ new Map();
|
|
6435
6489
|
this.invalidatedTagVersions = /* @__PURE__ */ new Map();
|
|
6436
6490
|
this.version = 0;
|
|
6491
|
+
this.namespace = "farm";
|
|
6492
|
+
this.local = true;
|
|
6493
|
+
this.lease = {
|
|
6494
|
+
enabled: true,
|
|
6495
|
+
ttlMs: 1e4,
|
|
6496
|
+
waitTimeoutMs: 1e4,
|
|
6497
|
+
pollIntervalMs: 25
|
|
6498
|
+
};
|
|
6499
|
+
this.configure(config);
|
|
6500
|
+
}
|
|
6501
|
+
configure(config = {}) {
|
|
6502
|
+
this.adapter = config.adapter;
|
|
6503
|
+
this.namespace = normalizeCacheNamespace(config.namespace || "farm");
|
|
6504
|
+
this.local = !config.adapter;
|
|
6505
|
+
this.lease = config.lease === false ? { ...this.lease, enabled: false } : {
|
|
6506
|
+
enabled: true,
|
|
6507
|
+
ttlMs: normalizePositiveDuration(config.lease?.ttlMs, 1e4, "cache.lease.ttlMs"),
|
|
6508
|
+
waitTimeoutMs: normalizePositiveDuration(
|
|
6509
|
+
config.lease?.waitTimeoutMs,
|
|
6510
|
+
1e4,
|
|
6511
|
+
"cache.lease.waitTimeoutMs"
|
|
6512
|
+
),
|
|
6513
|
+
pollIntervalMs: normalizePositiveDuration(
|
|
6514
|
+
config.lease?.pollIntervalMs,
|
|
6515
|
+
25,
|
|
6516
|
+
"cache.lease.pollIntervalMs"
|
|
6517
|
+
)
|
|
6518
|
+
};
|
|
6519
|
+
if (!this.local) {
|
|
6520
|
+
this.entries.clear();
|
|
6521
|
+
this.inflight.clear();
|
|
6522
|
+
this.invalidatedTagVersions.clear();
|
|
6523
|
+
this.version = 0;
|
|
6524
|
+
}
|
|
6525
|
+
}
|
|
6526
|
+
get adapterName() {
|
|
6527
|
+
return this.adapter?.name || (this.adapter ? "custom" : "memory");
|
|
6528
|
+
}
|
|
6529
|
+
get hasAdapter() {
|
|
6530
|
+
return this.adapter !== void 0;
|
|
6437
6531
|
}
|
|
6438
6532
|
get size() {
|
|
6439
6533
|
return this.entries.size;
|
|
@@ -6469,6 +6563,45 @@ var init_cache = __esm({
|
|
|
6469
6563
|
});
|
|
6470
6564
|
return this.toPublicEntry(entry);
|
|
6471
6565
|
}
|
|
6566
|
+
async getEntryAsync(key, options = {}) {
|
|
6567
|
+
if (this.local) {
|
|
6568
|
+
const localEntry = this.getEntry(key, options);
|
|
6569
|
+
if (localEntry) return localEntry;
|
|
6570
|
+
}
|
|
6571
|
+
if (!this.adapter) {
|
|
6572
|
+
return this.local ? void 0 : this.getEntry(key, options);
|
|
6573
|
+
}
|
|
6574
|
+
const entry = await this.adapter.get(this.createAdapterKey(key));
|
|
6575
|
+
if (!entry) {
|
|
6576
|
+
emitFarmEvent({ type: "cache.miss", key });
|
|
6577
|
+
return void 0;
|
|
6578
|
+
}
|
|
6579
|
+
assertFarmCacheEntry(entry, key);
|
|
6580
|
+
const stale = await this.isAdapterEntryStale(entry, options.now);
|
|
6581
|
+
if (stale) {
|
|
6582
|
+
emitFarmEvent({
|
|
6583
|
+
type: "cache.stale",
|
|
6584
|
+
key,
|
|
6585
|
+
tags: [...entry.tags],
|
|
6586
|
+
revalidate: entry.revalidate
|
|
6587
|
+
});
|
|
6588
|
+
if (!options.allowStale) {
|
|
6589
|
+
emitFarmEvent({ type: "cache.miss", key, reason: "stale" });
|
|
6590
|
+
return void 0;
|
|
6591
|
+
}
|
|
6592
|
+
}
|
|
6593
|
+
emitFarmEvent({
|
|
6594
|
+
type: "cache.hit",
|
|
6595
|
+
key,
|
|
6596
|
+
tags: [...entry.tags],
|
|
6597
|
+
revalidate: entry.revalidate,
|
|
6598
|
+
stale
|
|
6599
|
+
});
|
|
6600
|
+
if (this.local && !stale) {
|
|
6601
|
+
this.hydrateLocalEntry(entry);
|
|
6602
|
+
}
|
|
6603
|
+
return { ...entry, key };
|
|
6604
|
+
}
|
|
6472
6605
|
set(key, value, options = {}) {
|
|
6473
6606
|
const tags = /* @__PURE__ */ new Set();
|
|
6474
6607
|
for (const tag of options.tags ?? []) {
|
|
@@ -6481,6 +6614,7 @@ var init_cache = __esm({
|
|
|
6481
6614
|
key,
|
|
6482
6615
|
value,
|
|
6483
6616
|
tags,
|
|
6617
|
+
tagVersions: void 0,
|
|
6484
6618
|
createdAt: options.createdAt ?? Date.now(),
|
|
6485
6619
|
createdVersion: ++this.version,
|
|
6486
6620
|
revalidate: normalizeRevalidate(options.revalidate)
|
|
@@ -6494,11 +6628,45 @@ var init_cache = __esm({
|
|
|
6494
6628
|
});
|
|
6495
6629
|
return this.toPublicEntry(entry);
|
|
6496
6630
|
}
|
|
6631
|
+
async setAsync(key, value, options = {}, tagVersions) {
|
|
6632
|
+
const tags = normalizeCacheOptionsTags(options);
|
|
6633
|
+
const capturedVersions = tagVersions ?? await this.getAdapterTagVersions(Array.from(tags.values()));
|
|
6634
|
+
const entry = {
|
|
6635
|
+
key,
|
|
6636
|
+
value,
|
|
6637
|
+
tags: Array.from(tags),
|
|
6638
|
+
tagVersions: capturedVersions,
|
|
6639
|
+
createdAt: options.createdAt ?? Date.now(),
|
|
6640
|
+
createdVersion: ++this.version,
|
|
6641
|
+
revalidate: normalizeRevalidate(options.revalidate)
|
|
6642
|
+
};
|
|
6643
|
+
if (this.local) {
|
|
6644
|
+
this.hydrateLocalEntry(entry);
|
|
6645
|
+
}
|
|
6646
|
+
if (this.adapter) {
|
|
6647
|
+
await this.adapter.set(this.createAdapterKey(key), entry);
|
|
6648
|
+
}
|
|
6649
|
+
emitFarmEvent({
|
|
6650
|
+
type: "cache.set",
|
|
6651
|
+
key,
|
|
6652
|
+
tags: [...entry.tags],
|
|
6653
|
+
revalidate: entry.revalidate
|
|
6654
|
+
});
|
|
6655
|
+
return entry;
|
|
6656
|
+
}
|
|
6497
6657
|
delete(key) {
|
|
6498
6658
|
const deleted = this.entries.delete(key);
|
|
6499
6659
|
emitFarmEvent({ type: "cache.delete", key, deleted });
|
|
6500
6660
|
return deleted;
|
|
6501
6661
|
}
|
|
6662
|
+
async deleteAsync(key) {
|
|
6663
|
+
const deleted = this.entries.delete(key);
|
|
6664
|
+
if (this.adapter) {
|
|
6665
|
+
await this.adapter.delete(this.createAdapterKey(key));
|
|
6666
|
+
}
|
|
6667
|
+
emitFarmEvent({ type: "cache.delete", key, deleted: this.adapter ? true : deleted });
|
|
6668
|
+
return this.adapter ? true : deleted;
|
|
6669
|
+
}
|
|
6502
6670
|
clear() {
|
|
6503
6671
|
const count = this.entries.size;
|
|
6504
6672
|
this.entries.clear();
|
|
@@ -6507,6 +6675,10 @@ var init_cache = __esm({
|
|
|
6507
6675
|
this.version = 0;
|
|
6508
6676
|
emitFarmEvent({ type: "cache.clear", count });
|
|
6509
6677
|
}
|
|
6678
|
+
async clearAsync() {
|
|
6679
|
+
this.clear();
|
|
6680
|
+
await this.adapter?.clear?.();
|
|
6681
|
+
}
|
|
6510
6682
|
isStale(entry, now = Date.now()) {
|
|
6511
6683
|
if (typeof entry.revalidate === "number" && entry.revalidate > 0 && now - entry.createdAt >= entry.revalidate * 1e3) {
|
|
6512
6684
|
return true;
|
|
@@ -6519,6 +6691,12 @@ var init_cache = __esm({
|
|
|
6519
6691
|
}
|
|
6520
6692
|
return false;
|
|
6521
6693
|
}
|
|
6694
|
+
async isStaleAsync(entry, now = Date.now()) {
|
|
6695
|
+
if (this.adapter) {
|
|
6696
|
+
return this.isAdapterEntryStale(entry, now);
|
|
6697
|
+
}
|
|
6698
|
+
return this.isStale(entry, now);
|
|
6699
|
+
}
|
|
6522
6700
|
revalidateTag(tag, options = {}) {
|
|
6523
6701
|
const normalized = normalizeCacheTag(tag);
|
|
6524
6702
|
const count = this.invalidateTag(normalized);
|
|
@@ -6527,6 +6705,15 @@ var init_cache = __esm({
|
|
|
6527
6705
|
);
|
|
6528
6706
|
return count;
|
|
6529
6707
|
}
|
|
6708
|
+
async revalidateTagAsync(tag, options = {}) {
|
|
6709
|
+
const normalized = normalizeCacheTag(tag);
|
|
6710
|
+
const count = this.invalidateTag(normalized);
|
|
6711
|
+
await this.adapter?.invalidateTags?.([this.createAdapterTag(normalized)]);
|
|
6712
|
+
emitFarmEvent(
|
|
6713
|
+
options.source === "updateTag" ? { type: "cache.updateTag", tag: normalized, count } : { type: "cache.revalidateTag", tag: normalized, profile: options.profile, count }
|
|
6714
|
+
);
|
|
6715
|
+
return count;
|
|
6716
|
+
}
|
|
6530
6717
|
revalidatePath(routePath) {
|
|
6531
6718
|
const normalizedPath = normalizeRevalidatePath(routePath);
|
|
6532
6719
|
const pathTag = createPathCacheTag(normalizedPath);
|
|
@@ -6543,8 +6730,25 @@ var init_cache = __esm({
|
|
|
6543
6730
|
}
|
|
6544
6731
|
return count;
|
|
6545
6732
|
}
|
|
6733
|
+
async revalidatePathAsync(routePath) {
|
|
6734
|
+
const normalizedPath = normalizeRevalidatePath(routePath);
|
|
6735
|
+
const pathTag = createPathCacheTag(normalizedPath);
|
|
6736
|
+
const pprCount = this.countEntriesForTags([pathTag, "ppr"]);
|
|
6737
|
+
const count = this.invalidateTag(pathTag);
|
|
6738
|
+
await this.adapter?.invalidateTags?.([this.createAdapterTag(pathTag)]);
|
|
6739
|
+
emitFarmEvent({ type: "cache.revalidatePath", path: normalizedPath, count });
|
|
6740
|
+
if (pprCount > 0) {
|
|
6741
|
+
emitFarmEvent({
|
|
6742
|
+
type: "ppr.shell.invalidated",
|
|
6743
|
+
route: normalizedPath,
|
|
6744
|
+
reason: "revalidatePath",
|
|
6745
|
+
count: pprCount
|
|
6746
|
+
});
|
|
6747
|
+
}
|
|
6748
|
+
return count;
|
|
6749
|
+
}
|
|
6546
6750
|
async getOrSet(key, producer, options = {}) {
|
|
6547
|
-
const cached = this.
|
|
6751
|
+
const cached = await this.getEntryAsync(key);
|
|
6548
6752
|
if (cached) {
|
|
6549
6753
|
return cached.value;
|
|
6550
6754
|
}
|
|
@@ -6553,10 +6757,8 @@ var init_cache = __esm({
|
|
|
6553
6757
|
emitFarmEvent({ type: "cache.dedupe", key });
|
|
6554
6758
|
return inflight;
|
|
6555
6759
|
}
|
|
6556
|
-
const
|
|
6557
|
-
|
|
6558
|
-
return value;
|
|
6559
|
-
}).catch((error) => {
|
|
6760
|
+
const tags = Array.from(normalizeCacheOptionsTags(options));
|
|
6761
|
+
const promise = this.fillCacheEntry(key, producer, options, tags).catch((error) => {
|
|
6560
6762
|
emitFarmEvent({ type: "cache.error", key, operation: "set", error });
|
|
6561
6763
|
throw error;
|
|
6562
6764
|
}).finally(() => {
|
|
@@ -6565,6 +6767,40 @@ var init_cache = __esm({
|
|
|
6565
6767
|
this.inflight.set(key, promise);
|
|
6566
6768
|
return promise;
|
|
6567
6769
|
}
|
|
6770
|
+
async fillCacheEntry(key, producer, options, tags) {
|
|
6771
|
+
const leaseKey = `${this.namespace}:lease:${key}`;
|
|
6772
|
+
let leaseToken;
|
|
6773
|
+
if (this.adapter?.acquireLease && this.adapter.releaseLease && this.lease.enabled) {
|
|
6774
|
+
leaseToken = await this.adapter.acquireLease(leaseKey, this.lease.ttlMs);
|
|
6775
|
+
if (!leaseToken) {
|
|
6776
|
+
const shared = await this.waitForAdapterEntry(key);
|
|
6777
|
+
if (shared) {
|
|
6778
|
+
emitFarmEvent({ type: "cache.dedupe", key });
|
|
6779
|
+
return shared.value;
|
|
6780
|
+
}
|
|
6781
|
+
leaseToken = await this.adapter.acquireLease(leaseKey, this.lease.ttlMs);
|
|
6782
|
+
}
|
|
6783
|
+
}
|
|
6784
|
+
try {
|
|
6785
|
+
const initialTagVersions = await this.getAdapterTagVersions(tags);
|
|
6786
|
+
const value = await producer();
|
|
6787
|
+
await this.setAsync(key, value, options, initialTagVersions);
|
|
6788
|
+
return value;
|
|
6789
|
+
} finally {
|
|
6790
|
+
if (leaseToken && this.adapter?.releaseLease) {
|
|
6791
|
+
await this.adapter.releaseLease(leaseKey, leaseToken);
|
|
6792
|
+
}
|
|
6793
|
+
}
|
|
6794
|
+
}
|
|
6795
|
+
async waitForAdapterEntry(key) {
|
|
6796
|
+
const deadline = Date.now() + this.lease.waitTimeoutMs;
|
|
6797
|
+
while (Date.now() < deadline) {
|
|
6798
|
+
await delay(this.lease.pollIntervalMs);
|
|
6799
|
+
const entry = await this.getEntryAsync(key);
|
|
6800
|
+
if (entry) return entry;
|
|
6801
|
+
}
|
|
6802
|
+
return void 0;
|
|
6803
|
+
}
|
|
6568
6804
|
countEntriesForTag(tag) {
|
|
6569
6805
|
let count = 0;
|
|
6570
6806
|
for (const entry of this.entries.values()) {
|
|
@@ -6592,11 +6828,53 @@ var init_cache = __esm({
|
|
|
6592
6828
|
key: entry.key,
|
|
6593
6829
|
value: entry.value,
|
|
6594
6830
|
tags: Array.from(entry.tags),
|
|
6831
|
+
tagVersions: entry.tagVersions,
|
|
6595
6832
|
createdAt: entry.createdAt,
|
|
6596
6833
|
createdVersion: entry.createdVersion,
|
|
6597
6834
|
revalidate: entry.revalidate
|
|
6598
6835
|
};
|
|
6599
6836
|
}
|
|
6837
|
+
hydrateLocalEntry(entry) {
|
|
6838
|
+
this.entries.set(entry.key, {
|
|
6839
|
+
key: entry.key,
|
|
6840
|
+
value: entry.value,
|
|
6841
|
+
tags: new Set(entry.tags.map(normalizeCacheTag)),
|
|
6842
|
+
tagVersions: entry.tagVersions,
|
|
6843
|
+
createdAt: entry.createdAt,
|
|
6844
|
+
createdVersion: entry.createdVersion ?? ++this.version,
|
|
6845
|
+
revalidate: normalizeRevalidate(entry.revalidate)
|
|
6846
|
+
});
|
|
6847
|
+
}
|
|
6848
|
+
createAdapterKey(key) {
|
|
6849
|
+
return `${this.namespace}:entry:${key}`;
|
|
6850
|
+
}
|
|
6851
|
+
createAdapterTag(tag) {
|
|
6852
|
+
return `${this.namespace}:tag:${tag}`;
|
|
6853
|
+
}
|
|
6854
|
+
async getAdapterTagVersions(tags) {
|
|
6855
|
+
if (!this.adapter?.getTagVersions || tags.length === 0) return {};
|
|
6856
|
+
const normalized = tags.map(normalizeCacheTag);
|
|
6857
|
+
const physicalTags = normalized.map((tag) => this.createAdapterTag(tag));
|
|
6858
|
+
const versions = await this.adapter.getTagVersions(physicalTags);
|
|
6859
|
+
return Object.fromEntries(
|
|
6860
|
+
normalized.map((tag, index) => [
|
|
6861
|
+
tag,
|
|
6862
|
+
normalizeAdapterVersion(versions[physicalTags[index]])
|
|
6863
|
+
])
|
|
6864
|
+
);
|
|
6865
|
+
}
|
|
6866
|
+
async isAdapterEntryStale(entry, now = Date.now()) {
|
|
6867
|
+
if (typeof entry.revalidate === "number" && entry.revalidate > 0 && now - entry.createdAt >= entry.revalidate * 1e3) {
|
|
6868
|
+
return true;
|
|
6869
|
+
}
|
|
6870
|
+
const currentVersions = await this.getAdapterTagVersions(entry.tags);
|
|
6871
|
+
for (const tag of entry.tags) {
|
|
6872
|
+
if (normalizeAdapterVersion(currentVersions[tag]) > normalizeAdapterVersion(entry.tagVersions?.[tag])) {
|
|
6873
|
+
return true;
|
|
6874
|
+
}
|
|
6875
|
+
}
|
|
6876
|
+
return false;
|
|
6877
|
+
}
|
|
6600
6878
|
};
|
|
6601
6879
|
__name(_FarmDataCache, "FarmDataCache");
|
|
6602
6880
|
FarmDataCache = _FarmDataCache;
|
|
@@ -6606,6 +6884,7 @@ var init_cache = __esm({
|
|
|
6606
6884
|
functionIds = /* @__PURE__ */ new WeakMap();
|
|
6607
6885
|
nextFunctionId = 0;
|
|
6608
6886
|
__name(getFarmDataCache, "getFarmDataCache");
|
|
6887
|
+
__name(configureFarmCache, "configureFarmCache");
|
|
6609
6888
|
__name(createPathCacheTag, "createPathCacheTag");
|
|
6610
6889
|
__name(createRouteDataCacheTag, "createRouteDataCacheTag");
|
|
6611
6890
|
__name(createRouteDataCacheKey, "createRouteDataCacheKey");
|
|
@@ -6613,6 +6892,12 @@ var init_cache = __esm({
|
|
|
6613
6892
|
__name(createFarmCacheKey, "createFarmCacheKey");
|
|
6614
6893
|
__name(normalizeRevalidate, "normalizeRevalidate");
|
|
6615
6894
|
__name(normalizeCacheTag, "normalizeCacheTag");
|
|
6895
|
+
__name(normalizeCacheNamespace, "normalizeCacheNamespace");
|
|
6896
|
+
__name(normalizeAdapterVersion, "normalizeAdapterVersion");
|
|
6897
|
+
__name(normalizePositiveDuration, "normalizePositiveDuration");
|
|
6898
|
+
__name(delay, "delay");
|
|
6899
|
+
__name(normalizeCacheOptionsTags, "normalizeCacheOptionsTags");
|
|
6900
|
+
__name(assertFarmCacheEntry, "assertFarmCacheEntry");
|
|
6616
6901
|
__name(getFunctionCacheIdentity, "getFunctionCacheIdentity");
|
|
6617
6902
|
__name(stableSerialize, "stableSerialize");
|
|
6618
6903
|
}
|
|
@@ -7734,6 +8019,83 @@ var init_routing = __esm({
|
|
|
7734
8019
|
}
|
|
7735
8020
|
});
|
|
7736
8021
|
|
|
8022
|
+
// src/routing/route-slots.ts
|
|
8023
|
+
function parseRouteSlotFile(filePath) {
|
|
8024
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
8025
|
+
const parts = normalized.split("/").filter(Boolean);
|
|
8026
|
+
const fileName = parts.at(-1) ?? "";
|
|
8027
|
+
const baseName = fileName.replace(/\.(tsx?|jsx?)$/, "");
|
|
8028
|
+
if (baseName !== "page" && baseName !== "default") return null;
|
|
8029
|
+
const slotIndex = parts.findIndex((part) => SLOT_SEGMENT.test(part));
|
|
8030
|
+
if (slotIndex < 0) return null;
|
|
8031
|
+
if (parts.slice(slotIndex + 1, -1).some((part) => SLOT_SEGMENT.test(part))) {
|
|
8032
|
+
throw new Error(`Nested route slots are not supported in "${filePath}"`);
|
|
8033
|
+
}
|
|
8034
|
+
const slotMatch = parts[slotIndex].match(SLOT_SEGMENT);
|
|
8035
|
+
const name = slotMatch?.[1];
|
|
8036
|
+
if (!name) return null;
|
|
8037
|
+
const ownerParts = parts.slice(0, slotIndex);
|
|
8038
|
+
const contentParts = parts.slice(slotIndex + 1, -1);
|
|
8039
|
+
const marker = parseInterceptionMarker(contentParts[0]);
|
|
8040
|
+
let targetBase = ownerParts;
|
|
8041
|
+
let targetParts = contentParts;
|
|
8042
|
+
if (marker) {
|
|
8043
|
+
targetBase = marker.root === true ? [] : ownerParts.slice(0, Math.max(0, ownerParts.length - marker.up));
|
|
8044
|
+
targetParts = [...marker.remainder ? [marker.remainder] : [], ...contentParts.slice(1)];
|
|
8045
|
+
}
|
|
8046
|
+
const ownerFile = [...ownerParts, "layout.tsx"].join("/");
|
|
8047
|
+
const routeFile = [
|
|
8048
|
+
...baseName === "default" ? ownerParts : targetBase,
|
|
8049
|
+
...baseName === "default" ? [] : targetParts,
|
|
8050
|
+
"page.tsx"
|
|
8051
|
+
].join("/");
|
|
8052
|
+
return {
|
|
8053
|
+
name,
|
|
8054
|
+
ownerRoute: parseRoutePath(ownerFile || "layout.tsx"),
|
|
8055
|
+
route: parseRoutePath(routeFile || "page.tsx"),
|
|
8056
|
+
interception: marker !== null,
|
|
8057
|
+
fallback: baseName === "default"
|
|
8058
|
+
};
|
|
8059
|
+
}
|
|
8060
|
+
function createRouteSlotContainerId(name, ownerPattern) {
|
|
8061
|
+
const owner = ownerPattern === "/" ? "root" : ownerPattern.replace(/^\//, "").replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
8062
|
+
return `__farm_slot_${name}_${owner || "root"}__`;
|
|
8063
|
+
}
|
|
8064
|
+
function parseInterceptionMarker(segment) {
|
|
8065
|
+
if (!segment) return null;
|
|
8066
|
+
if (segment.startsWith("(...)")) {
|
|
8067
|
+
return {
|
|
8068
|
+
up: 0,
|
|
8069
|
+
root: true,
|
|
8070
|
+
remainder: segment.slice(5)
|
|
8071
|
+
};
|
|
8072
|
+
}
|
|
8073
|
+
if (segment.startsWith("(.)")) {
|
|
8074
|
+
return {
|
|
8075
|
+
up: 0,
|
|
8076
|
+
remainder: segment.slice(3)
|
|
8077
|
+
};
|
|
8078
|
+
}
|
|
8079
|
+
let remainder = segment;
|
|
8080
|
+
let up = 0;
|
|
8081
|
+
while (remainder.startsWith("(..)")) {
|
|
8082
|
+
up += 1;
|
|
8083
|
+
remainder = remainder.slice(4);
|
|
8084
|
+
}
|
|
8085
|
+
return up > 0 ? { up, remainder } : null;
|
|
8086
|
+
}
|
|
8087
|
+
var SLOT_SEGMENT;
|
|
8088
|
+
var init_route_slots = __esm({
|
|
8089
|
+
"src/routing/route-slots.ts"() {
|
|
8090
|
+
"use strict";
|
|
8091
|
+
init_utils();
|
|
8092
|
+
SLOT_SEGMENT = /^@([A-Za-z][A-Za-z0-9_-]*)$/;
|
|
8093
|
+
__name(parseRouteSlotFile, "parseRouteSlotFile");
|
|
8094
|
+
__name(createRouteSlotContainerId, "createRouteSlotContainerId");
|
|
8095
|
+
__name(parseInterceptionMarker, "parseInterceptionMarker");
|
|
8096
|
+
}
|
|
8097
|
+
});
|
|
8098
|
+
|
|
7737
8099
|
// src/routing/route-manager.ts
|
|
7738
8100
|
function routeSpecificity(entry) {
|
|
7739
8101
|
return entry.route.segments.reduce((score, segment) => {
|
|
@@ -7805,11 +8167,13 @@ var init_route_manager = __esm({
|
|
|
7805
8167
|
init_static_metadata_image();
|
|
7806
8168
|
init_route_runtime();
|
|
7807
8169
|
init_routing();
|
|
8170
|
+
init_route_slots();
|
|
7808
8171
|
__name(routeSpecificity, "routeSpecificity");
|
|
7809
8172
|
_RouteManager = class _RouteManager {
|
|
7810
8173
|
constructor(config, viteServer) {
|
|
7811
8174
|
this.routes = /* @__PURE__ */ new Map();
|
|
7812
8175
|
this.layouts = /* @__PURE__ */ new Map();
|
|
8176
|
+
this.routeSlots = /* @__PURE__ */ new Map();
|
|
7813
8177
|
this.loadings = /* @__PURE__ */ new Map();
|
|
7814
8178
|
this.errors = /* @__PURE__ */ new Map();
|
|
7815
8179
|
this.metadataImages = /* @__PURE__ */ new Map();
|
|
@@ -7825,6 +8189,7 @@ var init_route_manager = __esm({
|
|
|
7825
8189
|
async discoverRoutes() {
|
|
7826
8190
|
this.routes.clear();
|
|
7827
8191
|
this.layouts.clear();
|
|
8192
|
+
this.routeSlots.clear();
|
|
7828
8193
|
this.loadings.clear();
|
|
7829
8194
|
this.errors.clear();
|
|
7830
8195
|
this.metadataImages.clear();
|
|
@@ -7835,6 +8200,13 @@ var init_route_manager = __esm({
|
|
|
7835
8200
|
await this.discoverFileRoutes(source);
|
|
7836
8201
|
await this.discoverProgrammaticRoutes(source);
|
|
7837
8202
|
}
|
|
8203
|
+
for (const slot of this.routeSlots.values()) {
|
|
8204
|
+
if (slot.interception && !this.routes.has(slot.pattern)) {
|
|
8205
|
+
throw new Error(
|
|
8206
|
+
`Intercepting route slot "${slot.name}" targets "${slot.pattern}", but no canonical page exists for that URL`
|
|
8207
|
+
);
|
|
8208
|
+
}
|
|
8209
|
+
}
|
|
7838
8210
|
this.routes = new Map(
|
|
7839
8211
|
Array.from(this.routes.entries()).sort(
|
|
7840
8212
|
([, left], [, right]) => routeSpecificity(right) - routeSpecificity(left)
|
|
@@ -7850,7 +8222,7 @@ var init_route_manager = __esm({
|
|
|
7850
8222
|
/**
|
|
7851
8223
|
* Find matching route for a given URL path
|
|
7852
8224
|
*/
|
|
7853
|
-
matchRoute(pathname) {
|
|
8225
|
+
matchRoute(pathname, options = {}) {
|
|
7854
8226
|
const routePathname = this.toRoutePathname(pathname);
|
|
7855
8227
|
const normalizedPath = routePathname === "/" ? "/" : routePathname.replace(/\/$/, "");
|
|
7856
8228
|
let matchedRoute = null;
|
|
@@ -7864,10 +8236,12 @@ var init_route_manager = __esm({
|
|
|
7864
8236
|
}
|
|
7865
8237
|
}
|
|
7866
8238
|
const layouts = this.findMatchingLayouts(normalizedPath);
|
|
8239
|
+
const slots = this.findMatchingRouteSlots(normalizedPath, options.interceptFrom);
|
|
7867
8240
|
return {
|
|
7868
8241
|
route: matchedRoute,
|
|
7869
8242
|
params,
|
|
7870
|
-
layouts
|
|
8243
|
+
layouts,
|
|
8244
|
+
slots
|
|
7871
8245
|
};
|
|
7872
8246
|
}
|
|
7873
8247
|
/**
|
|
@@ -7882,6 +8256,9 @@ var init_route_manager = __esm({
|
|
|
7882
8256
|
getLayouts() {
|
|
7883
8257
|
return new Map(this.layouts);
|
|
7884
8258
|
}
|
|
8259
|
+
getRouteSlots() {
|
|
8260
|
+
return new Map(this.routeSlots);
|
|
8261
|
+
}
|
|
7885
8262
|
/** Resolve route rules, inherited layouts, and the page export in precedence order. */
|
|
7886
8263
|
async resolveRouteRuntimeConfig(pattern) {
|
|
7887
8264
|
const routeEntry = this.routes.get(pattern);
|
|
@@ -8040,7 +8417,22 @@ var init_route_manager = __esm({
|
|
|
8040
8417
|
pattern: entry.pattern,
|
|
8041
8418
|
modulePath: toUrlPath(entry.modulePath)
|
|
8042
8419
|
}));
|
|
8043
|
-
|
|
8420
|
+
const slots = Array.from(this.routeSlots.values()).map((entry) => {
|
|
8421
|
+
const metadata = getClientModuleMetadata(entry.modulePath, projectRoot);
|
|
8422
|
+
return {
|
|
8423
|
+
name: entry.name,
|
|
8424
|
+
ownerPattern: entry.ownerPattern,
|
|
8425
|
+
pattern: entry.pattern,
|
|
8426
|
+
modulePath: toUrlPath(entry.modulePath),
|
|
8427
|
+
containerId: entry.containerId,
|
|
8428
|
+
interception: entry.interception,
|
|
8429
|
+
fallback: entry.fallback,
|
|
8430
|
+
shouldHydrate: metadata.shouldHydrate,
|
|
8431
|
+
isClientComponent: metadata.isClientComponent,
|
|
8432
|
+
segments: entry.route.segments
|
|
8433
|
+
};
|
|
8434
|
+
});
|
|
8435
|
+
return { routes, layouts, slots };
|
|
8044
8436
|
}
|
|
8045
8437
|
/**
|
|
8046
8438
|
* Load a route module dynamically
|
|
@@ -8117,6 +8509,7 @@ var init_route_manager = __esm({
|
|
|
8117
8509
|
async discoverFileRoutes(source) {
|
|
8118
8510
|
const appDir = resolveAppPath(source.root, source.srcDir, "app");
|
|
8119
8511
|
const pageFiles = await safeGlobFiles("**/page.{ts,tsx,js,jsx,md,mdx}", appDir);
|
|
8512
|
+
const slotDefaultFiles = await safeGlobFiles("**/@*/**/default.{ts,tsx,js,jsx}", appDir);
|
|
8120
8513
|
const layoutFiles = await safeGlobFiles("**/layout.{ts,tsx,js,jsx}", appDir);
|
|
8121
8514
|
const loadingFiles = await safeGlobFiles("**/loading.{ts,tsx,js,jsx}", appDir);
|
|
8122
8515
|
const errorFiles = await safeGlobFiles("**/error.{ts,tsx,js,jsx}", appDir);
|
|
@@ -8125,7 +8518,7 @@ var init_route_manager = __esm({
|
|
|
8125
8518
|
appDir
|
|
8126
8519
|
);
|
|
8127
8520
|
for (const [kind, files, target] of [
|
|
8128
|
-
["page", pageFiles, this.routes],
|
|
8521
|
+
["page", pageFiles.filter((file) => parseRouteSlotFile(file) === null), this.routes],
|
|
8129
8522
|
["layout", layoutFiles, this.layouts],
|
|
8130
8523
|
["loading", loadingFiles, this.loadings],
|
|
8131
8524
|
["error", errorFiles, this.errors]
|
|
@@ -8149,6 +8542,32 @@ var init_route_manager = __esm({
|
|
|
8149
8542
|
});
|
|
8150
8543
|
}
|
|
8151
8544
|
}
|
|
8545
|
+
for (const file of [...pageFiles, ...slotDefaultFiles]) {
|
|
8546
|
+
const slot = parseRouteSlotFile(file);
|
|
8547
|
+
if (!slot) continue;
|
|
8548
|
+
const modulePath = import_path6.default.join(appDir, file);
|
|
8549
|
+
const pattern = this.createRoutePattern(slot.route);
|
|
8550
|
+
const ownerPattern = this.createRoutePattern(slot.ownerRoute);
|
|
8551
|
+
const key = `${ownerPattern}:${slot.name}:${slot.interception ? "intercept" : "slot"}:${slot.fallback ? "default" : pattern}`;
|
|
8552
|
+
const existing = this.routeSlots.get(key);
|
|
8553
|
+
if (existing?.sourceRoot === source.root) {
|
|
8554
|
+
throw new Error(
|
|
8555
|
+
`Duplicate route slot "${slot.name}" for "${pattern}". Found both ${existing.modulePath} and ${modulePath}.`
|
|
8556
|
+
);
|
|
8557
|
+
}
|
|
8558
|
+
this.routeSlots.set(key, {
|
|
8559
|
+
route: slot.route,
|
|
8560
|
+
modulePath,
|
|
8561
|
+
pattern,
|
|
8562
|
+
source: "file",
|
|
8563
|
+
sourceRoot: source.root,
|
|
8564
|
+
name: slot.name,
|
|
8565
|
+
ownerPattern,
|
|
8566
|
+
interception: slot.interception,
|
|
8567
|
+
fallback: slot.fallback,
|
|
8568
|
+
containerId: createRouteSlotContainerId(slot.name, ownerPattern)
|
|
8569
|
+
});
|
|
8570
|
+
}
|
|
8152
8571
|
for (const file of metadataImageFiles) {
|
|
8153
8572
|
const fileBase = import_path6.default.basename(file, import_path6.default.extname(file));
|
|
8154
8573
|
const kind = fileBase === "twitter-image" ? "twitter" : "opengraph";
|
|
@@ -8274,6 +8693,71 @@ var init_route_manager = __esm({
|
|
|
8274
8693
|
}
|
|
8275
8694
|
return matchingLayouts;
|
|
8276
8695
|
}
|
|
8696
|
+
findMatchingRouteSlots(pathname, interceptFrom) {
|
|
8697
|
+
const groups = /* @__PURE__ */ new Map();
|
|
8698
|
+
for (const entry of this.routeSlots.values()) {
|
|
8699
|
+
if (!this.matchesRoutePrefix(pathname, entry.ownerPattern)) continue;
|
|
8700
|
+
const key = `${entry.ownerPattern}:${entry.name}`;
|
|
8701
|
+
const entries = groups.get(key) ?? [];
|
|
8702
|
+
entries.push(entry);
|
|
8703
|
+
groups.set(key, entries);
|
|
8704
|
+
}
|
|
8705
|
+
const normalizedFrom = interceptFrom ? this.toRoutePathname(new URL(interceptFrom, "http://farm.local").pathname) : void 0;
|
|
8706
|
+
const matches = [];
|
|
8707
|
+
for (const entries of groups.values()) {
|
|
8708
|
+
const candidates = entries.filter((entry) => !entry.fallback).filter(
|
|
8709
|
+
(entry) => !entry.interception || normalizedFrom !== void 0 && this.matchesRoutePrefix(normalizedFrom, entry.ownerPattern)
|
|
8710
|
+
).map((entry) => ({
|
|
8711
|
+
entry,
|
|
8712
|
+
match: matchRoute(pathname, entry.route.segments)
|
|
8713
|
+
})).filter((candidate) => candidate.match.matches).sort((left, right) => {
|
|
8714
|
+
if (left.entry.interception !== right.entry.interception) {
|
|
8715
|
+
return left.entry.interception ? -1 : 1;
|
|
8716
|
+
}
|
|
8717
|
+
return routeSpecificity(right.entry) - routeSpecificity(left.entry);
|
|
8718
|
+
});
|
|
8719
|
+
const selected = candidates[0];
|
|
8720
|
+
if (selected) {
|
|
8721
|
+
matches.push({
|
|
8722
|
+
name: selected.entry.name,
|
|
8723
|
+
ownerPattern: selected.entry.ownerPattern,
|
|
8724
|
+
containerId: selected.entry.containerId,
|
|
8725
|
+
interception: selected.entry.interception,
|
|
8726
|
+
fallback: false,
|
|
8727
|
+
route: selected.entry,
|
|
8728
|
+
params: selected.match.params
|
|
8729
|
+
});
|
|
8730
|
+
continue;
|
|
8731
|
+
}
|
|
8732
|
+
const fallback = entries.find((entry) => entry.fallback);
|
|
8733
|
+
if (fallback) {
|
|
8734
|
+
matches.push({
|
|
8735
|
+
name: fallback.name,
|
|
8736
|
+
ownerPattern: fallback.ownerPattern,
|
|
8737
|
+
containerId: fallback.containerId,
|
|
8738
|
+
interception: false,
|
|
8739
|
+
fallback: true,
|
|
8740
|
+
route: fallback,
|
|
8741
|
+
params: {}
|
|
8742
|
+
});
|
|
8743
|
+
}
|
|
8744
|
+
}
|
|
8745
|
+
return matches.sort((left, right) => {
|
|
8746
|
+
const ownerDifference = left.route.route.segments.length - right.route.route.segments.length;
|
|
8747
|
+
return ownerDifference || left.name.localeCompare(right.name);
|
|
8748
|
+
});
|
|
8749
|
+
}
|
|
8750
|
+
matchesRoutePrefix(pathname, pattern) {
|
|
8751
|
+
if (pattern === "/") return true;
|
|
8752
|
+
const patternSegments = parseRoutePath(`${pattern}/page.tsx`).segments;
|
|
8753
|
+
const pathSegments = pathname.split("/").filter(Boolean);
|
|
8754
|
+
if (patternSegments.length > pathSegments.length) return false;
|
|
8755
|
+
for (let index = 0; index < patternSegments.length; index++) {
|
|
8756
|
+
const segment = patternSegments[index];
|
|
8757
|
+
if (!segment.isDynamic && segment.segment !== pathSegments[index]) return false;
|
|
8758
|
+
}
|
|
8759
|
+
return true;
|
|
8760
|
+
}
|
|
8277
8761
|
findNearestBoundary(pathname, boundaries) {
|
|
8278
8762
|
const normalizedPath = pathname === "/" ? "/" : pathname.replace(/\/$/, "");
|
|
8279
8763
|
const pathSegments = normalizedPath.split("/").filter(Boolean);
|
|
@@ -9868,7 +10352,9 @@ var init_renderer = __esm({
|
|
|
9868
10352
|
async runWithRequestContext(request, fn) {
|
|
9869
10353
|
return _runWithCurrentRequest(
|
|
9870
10354
|
request,
|
|
9871
|
-
() => this.i18nRuntime?.config.enabled ? _runWithFarmI18nRequest(this.i18nRuntime, request, fn, {
|
|
10355
|
+
() => this.i18nRuntime?.config.enabled ? _runWithFarmI18nRequest(this.i18nRuntime, request, fn, {
|
|
10356
|
+
redirect: false
|
|
10357
|
+
}) : fn()
|
|
9872
10358
|
);
|
|
9873
10359
|
}
|
|
9874
10360
|
async resolveRouteContext(input) {
|
|
@@ -9891,11 +10377,11 @@ var init_renderer = __esm({
|
|
|
9891
10377
|
/**
|
|
9892
10378
|
* Check if a path should be served from SSG cache
|
|
9893
10379
|
*/
|
|
9894
|
-
shouldServeSSG(pathname) {
|
|
10380
|
+
async shouldServeSSG(pathname) {
|
|
9895
10381
|
const ssgPage = matchSSGPage(pathname, this.ssgManifest);
|
|
9896
10382
|
if (!ssgPage) return null;
|
|
9897
|
-
const cached = this.getCachedSSGPage(pathname);
|
|
9898
|
-
if (cached && this.dataCache.
|
|
10383
|
+
const cached = await this.getCachedSSGPage(pathname);
|
|
10384
|
+
if (cached && await this.dataCache.isStaleAsync(cached)) {
|
|
9899
10385
|
this.regenerateSSGPage(ssgPage);
|
|
9900
10386
|
}
|
|
9901
10387
|
return ssgPage;
|
|
@@ -9907,12 +10393,12 @@ var init_renderer = __esm({
|
|
|
9907
10393
|
return createFarmCacheKey(["ppr", normalizeRevalidatePath(pathname), search]);
|
|
9908
10394
|
}
|
|
9909
10395
|
getCachedSSGPage(urlPath) {
|
|
9910
|
-
return this.dataCache.
|
|
10396
|
+
return this.dataCache.getEntryAsync(this.getSSGCacheKey(urlPath), {
|
|
9911
10397
|
allowStale: true
|
|
9912
10398
|
});
|
|
9913
10399
|
}
|
|
9914
|
-
cacheSSGPage(page2, html, options) {
|
|
9915
|
-
this.dataCache.
|
|
10400
|
+
async cacheSSGPage(page2, html, options) {
|
|
10401
|
+
await this.dataCache.setAsync(
|
|
9916
10402
|
this.getSSGCacheKey(page2.urlPath),
|
|
9917
10403
|
{ html, document: options.document },
|
|
9918
10404
|
{
|
|
@@ -9924,11 +10410,11 @@ var init_renderer = __esm({
|
|
|
9924
10410
|
);
|
|
9925
10411
|
}
|
|
9926
10412
|
getCachedPPRShell(pathname, search) {
|
|
9927
|
-
return this.dataCache.
|
|
10413
|
+
return this.dataCache.getEntryAsync(this.getPPRCacheKey(pathname, search));
|
|
9928
10414
|
}
|
|
9929
|
-
cachePPRShell(options, html) {
|
|
10415
|
+
async cachePPRShell(options, html) {
|
|
9930
10416
|
const key = this.getPPRCacheKey(options.pathname, options.search);
|
|
9931
|
-
this.dataCache.
|
|
10417
|
+
await this.dataCache.setAsync(
|
|
9932
10418
|
key,
|
|
9933
10419
|
{ html },
|
|
9934
10420
|
{
|
|
@@ -10026,7 +10512,7 @@ var init_renderer = __esm({
|
|
|
10026
10512
|
}
|
|
10027
10513
|
const { renderToString } = await import("react-dom/server");
|
|
10028
10514
|
const html = renderToString(await this.wrapWithIntegrationProviders(pageElement));
|
|
10029
|
-
this.cacheSSGPage(page2, html, { document: false });
|
|
10515
|
+
await this.cacheSSGPage(page2, html, { document: false });
|
|
10030
10516
|
logger.info(`ISR: Regenerated ${page2.urlPath}`);
|
|
10031
10517
|
} catch (error) {
|
|
10032
10518
|
logger.error(`ISR regeneration failed for ${page2.urlPath}: ${error}`);
|
|
@@ -10040,7 +10526,7 @@ var init_renderer = __esm({
|
|
|
10040
10526
|
* Serve a pre-rendered SSG page
|
|
10041
10527
|
*/
|
|
10042
10528
|
async serveSSGPage(req, res, page2) {
|
|
10043
|
-
const cached = this.getCachedSSGPage(page2.urlPath);
|
|
10529
|
+
const cached = await this.getCachedSSGPage(page2.urlPath);
|
|
10044
10530
|
if (cached) {
|
|
10045
10531
|
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
10046
10532
|
res.setHeader("X-Farm-SSG", "cached");
|
|
@@ -10058,9 +10544,12 @@ var init_renderer = __esm({
|
|
|
10058
10544
|
if (fs2.existsSync(htmlPath)) {
|
|
10059
10545
|
const stat = fs2.statSync(htmlPath);
|
|
10060
10546
|
const html = fs2.readFileSync(htmlPath, "utf-8");
|
|
10061
|
-
this.cacheSSGPage(page2, html, {
|
|
10062
|
-
|
|
10063
|
-
|
|
10547
|
+
await this.cacheSSGPage(page2, html, {
|
|
10548
|
+
document: true,
|
|
10549
|
+
createdAt: stat.mtimeMs
|
|
10550
|
+
});
|
|
10551
|
+
const fileCacheEntry = await this.getCachedSSGPage(page2.urlPath);
|
|
10552
|
+
if (fileCacheEntry && await this.dataCache.isStaleAsync(fileCacheEntry)) {
|
|
10064
10553
|
this.regenerateSSGPage(page2);
|
|
10065
10554
|
}
|
|
10066
10555
|
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
@@ -10106,6 +10595,7 @@ var init_renderer = __esm({
|
|
|
10106
10595
|
let pathname = "/";
|
|
10107
10596
|
let params = {};
|
|
10108
10597
|
let layouts = [];
|
|
10598
|
+
let routeSlots = [];
|
|
10109
10599
|
let searchParamsObject = {};
|
|
10110
10600
|
let middlewareMap = /* @__PURE__ */ new Map();
|
|
10111
10601
|
let middlewareContext = /* @__PURE__ */ new Map();
|
|
@@ -10138,7 +10628,7 @@ var init_renderer = __esm({
|
|
|
10138
10628
|
}
|
|
10139
10629
|
this.applyDeploymentHeaders(req, res);
|
|
10140
10630
|
if (process.env.NODE_ENV === "production") {
|
|
10141
|
-
const ssgPage = this.shouldServeSSG(pathname);
|
|
10631
|
+
const ssgPage = await this.shouldServeSSG(pathname);
|
|
10142
10632
|
if (ssgPage) {
|
|
10143
10633
|
const served = await this.serveSSGPage(req, res, ssgPage);
|
|
10144
10634
|
if (served) {
|
|
@@ -10151,6 +10641,7 @@ var init_renderer = __esm({
|
|
|
10151
10641
|
const route = match.route;
|
|
10152
10642
|
params = match.params;
|
|
10153
10643
|
layouts = match.layouts;
|
|
10644
|
+
routeSlots = match.slots ?? [];
|
|
10154
10645
|
if (!route) {
|
|
10155
10646
|
emitFarmEvent({ type: "route.notFound", pathname });
|
|
10156
10647
|
await this.render404(req, res);
|
|
@@ -10167,7 +10658,9 @@ var init_renderer = __esm({
|
|
|
10167
10658
|
errorBoundaryEntry = this.routeManager.getMatchingError(pathname);
|
|
10168
10659
|
middlewareMap = toMiddlewareMap2(req.__FARM_MIDDLEWARE_DATA__);
|
|
10169
10660
|
middlewareContext = toMiddlewareMap2(req.__FARM_MIDDLEWARE_CONTEXT__);
|
|
10170
|
-
pluginExposedContext = getRequestContextSnapshot(req, {
|
|
10661
|
+
pluginExposedContext = getRequestContextSnapshot(req, {
|
|
10662
|
+
exposedOnly: true
|
|
10663
|
+
});
|
|
10171
10664
|
const currentRequest = createWebRequestFromFarmRequest(req);
|
|
10172
10665
|
const routeContext = await this.resolveRouteContext({
|
|
10173
10666
|
request: currentRequest,
|
|
@@ -10231,8 +10724,16 @@ var init_renderer = __esm({
|
|
|
10231
10724
|
revalidate: renderingConfig.revalidate
|
|
10232
10725
|
} : void 0;
|
|
10233
10726
|
if (renderingConfig.ppr && pprBypassReason) {
|
|
10234
|
-
emitFarmEvent({
|
|
10235
|
-
|
|
10727
|
+
emitFarmEvent({
|
|
10728
|
+
type: "ppr.shell.bypass",
|
|
10729
|
+
route: pathname,
|
|
10730
|
+
reason: pprBypassReason
|
|
10731
|
+
});
|
|
10732
|
+
emitFarmEvent({
|
|
10733
|
+
type: "cache.bypass",
|
|
10734
|
+
route: pathname,
|
|
10735
|
+
reason: pprBypassReason
|
|
10736
|
+
});
|
|
10236
10737
|
if (pprBypassReason === "refresh") {
|
|
10237
10738
|
pprRefreshRoute = pathname;
|
|
10238
10739
|
emitFarmEvent({ type: "ppr.refresh.start", route: pathname });
|
|
@@ -10240,14 +10741,22 @@ var init_renderer = __esm({
|
|
|
10240
10741
|
}
|
|
10241
10742
|
if (pprShellOptions) {
|
|
10242
10743
|
const pprCacheKey = this.getPPRCacheKey(pathname, url.search);
|
|
10243
|
-
const cachedPPRShell = this.getCachedPPRShell(pathname, url.search);
|
|
10744
|
+
const cachedPPRShell = await this.getCachedPPRShell(pathname, url.search);
|
|
10244
10745
|
if (cachedPPRShell) {
|
|
10245
|
-
emitFarmEvent({
|
|
10746
|
+
emitFarmEvent({
|
|
10747
|
+
type: "ppr.shell.hit",
|
|
10748
|
+
route: pathname,
|
|
10749
|
+
key: pprCacheKey
|
|
10750
|
+
});
|
|
10246
10751
|
this.serveCachedPPRShell(res, cachedPPRShell.value, renderingConfig.revalidate);
|
|
10247
10752
|
completeRender(res.statusCode || 200, pathname);
|
|
10248
10753
|
return;
|
|
10249
10754
|
}
|
|
10250
|
-
emitFarmEvent({
|
|
10755
|
+
emitFarmEvent({
|
|
10756
|
+
type: "ppr.shell.miss",
|
|
10757
|
+
route: pathname,
|
|
10758
|
+
key: pprCacheKey
|
|
10759
|
+
});
|
|
10251
10760
|
}
|
|
10252
10761
|
let LoadingFallbackComponent = null;
|
|
10253
10762
|
if (loadingBoundaryEntry) {
|
|
@@ -10271,14 +10780,77 @@ var init_renderer = __esm({
|
|
|
10271
10780
|
);
|
|
10272
10781
|
const moduleMetadata = routeManifestEntry || getClientModuleMetadata(route.modulePath, this.config.root);
|
|
10273
10782
|
const isClientComponent = moduleMetadata.isClientComponent;
|
|
10783
|
+
const renderedRouteSlots = await Promise.all(
|
|
10784
|
+
routeSlots.map(async (slot) => {
|
|
10785
|
+
const slotModule = await this.routeManager.loadRouteModule(slot.route.modulePath);
|
|
10786
|
+
if (!slotModule.default) {
|
|
10787
|
+
throw new Error(
|
|
10788
|
+
`Route slot "${slot.name}" module ${slot.route.modulePath} does not export a default component`
|
|
10789
|
+
);
|
|
10790
|
+
}
|
|
10791
|
+
const slotContext = await this.resolveRouteContext({
|
|
10792
|
+
request: currentRequest,
|
|
10793
|
+
rawRequest: req,
|
|
10794
|
+
params: slot.params,
|
|
10795
|
+
search: searchParamsObject,
|
|
10796
|
+
path: pathname
|
|
10797
|
+
});
|
|
10798
|
+
const rawSlotProps = withFarmRouteContext(
|
|
10799
|
+
{
|
|
10800
|
+
params: slot.params,
|
|
10801
|
+
searchParams: Promise.resolve(searchParamsObject),
|
|
10802
|
+
path: pathname,
|
|
10803
|
+
middleware: middlewareMap.size > 0 ? { data: middlewareMap } : void 0,
|
|
10804
|
+
context: pluginExposedContext.size > 0 ? { data: pluginExposedContext } : void 0
|
|
10805
|
+
},
|
|
10806
|
+
slotContext
|
|
10807
|
+
);
|
|
10808
|
+
const slotProps = await parseRouteModuleProps(slotModule, {
|
|
10809
|
+
props: rawSlotProps,
|
|
10810
|
+
search: searchParamsObject,
|
|
10811
|
+
routePath: slot.route.pattern
|
|
10812
|
+
});
|
|
10813
|
+
const metadata = getClientModuleMetadata(slot.route.modulePath, this.config.root);
|
|
10814
|
+
return {
|
|
10815
|
+
...slot,
|
|
10816
|
+
module: slotModule,
|
|
10817
|
+
props: slotProps,
|
|
10818
|
+
isClientComponent: metadata.isClientComponent,
|
|
10819
|
+
shouldHydrate: metadata.shouldHydrate
|
|
10820
|
+
};
|
|
10821
|
+
})
|
|
10822
|
+
);
|
|
10274
10823
|
const shouldHydrate = moduleMetadata.shouldHydrate;
|
|
10824
|
+
const hasHydratableRouteSlots = renderedRouteSlots.some(
|
|
10825
|
+
(slot) => slot.isClientComponent || slot.shouldHydrate
|
|
10826
|
+
);
|
|
10275
10827
|
req.__FARM_PAGE_PATH__ = route.modulePath;
|
|
10276
10828
|
req.__FARM_ROUTE__ = pathname;
|
|
10277
10829
|
req.__FARM_IS_CLIENT_COMPONENT__ = isClientComponent;
|
|
10278
10830
|
req.__FARM_SHOULD_HYDRATE__ = shouldHydrate;
|
|
10831
|
+
req.__FARM_HAS_HYDRATABLE_ROUTE_SLOTS__ = hasHydratableRouteSlots;
|
|
10279
10832
|
req.__FARM_LOADING_MODULE_PATH__ = loadingBoundaryEntry?.modulePath ? loadingBoundaryEntry.modulePath.substring(
|
|
10280
10833
|
loadingBoundaryEntry.modulePath.indexOf("/src/app/")
|
|
10281
10834
|
) : null;
|
|
10835
|
+
req.__FARM_ROUTE_SLOTS__ = renderedRouteSlots.map((slot) => ({
|
|
10836
|
+
name: slot.name,
|
|
10837
|
+
ownerPattern: slot.ownerPattern,
|
|
10838
|
+
containerId: slot.containerId,
|
|
10839
|
+
interception: slot.interception,
|
|
10840
|
+
fallback: slot.fallback,
|
|
10841
|
+
modulePath: slot.route.modulePath,
|
|
10842
|
+
isClientComponent: slot.isClientComponent,
|
|
10843
|
+
shouldHydrate: slot.shouldHydrate,
|
|
10844
|
+
props: {
|
|
10845
|
+
params: slot.props.params,
|
|
10846
|
+
search: slot.props.search,
|
|
10847
|
+
searchParams: slot.props.search,
|
|
10848
|
+
..."data" in slot.props ? { data: slot.props.data } : {},
|
|
10849
|
+
...slot.props.__farmCanonicalPath ? { __farmCanonicalPath: slot.props.__farmCanonicalPath } : {},
|
|
10850
|
+
...slot.props.__farmRoutePropsResolved ? { __farmRoutePropsResolved: true } : {},
|
|
10851
|
+
path: pathname
|
|
10852
|
+
}
|
|
10853
|
+
}));
|
|
10282
10854
|
req.__FARM_PROPS__ = {
|
|
10283
10855
|
params: pageProps.params,
|
|
10284
10856
|
search: pageProps.search,
|
|
@@ -10338,12 +10910,32 @@ var init_renderer = __esm({
|
|
|
10338
10910
|
let wrappedElement = pageElement;
|
|
10339
10911
|
for (let i = layoutModules.length - 1; i >= 0; i--) {
|
|
10340
10912
|
const layoutModule = layoutModules[i];
|
|
10913
|
+
const layoutEntry = layouts[i];
|
|
10341
10914
|
const LayoutComponent = layoutModule.default;
|
|
10915
|
+
const slotProps = {};
|
|
10916
|
+
for (const slot of renderedRouteSlots) {
|
|
10917
|
+
if (slot.ownerPattern !== layoutEntry.pattern) continue;
|
|
10918
|
+
let slotElement = import_react4.default.createElement(
|
|
10919
|
+
slot.module.default,
|
|
10920
|
+
slot.props
|
|
10921
|
+
);
|
|
10922
|
+
slotElement = import_react4.default.createElement(
|
|
10923
|
+
"div",
|
|
10924
|
+
{
|
|
10925
|
+
id: slot.containerId,
|
|
10926
|
+
"data-farm-route-slot": slot.name,
|
|
10927
|
+
"data-farm-slot-owner": slot.ownerPattern
|
|
10928
|
+
},
|
|
10929
|
+
slotElement
|
|
10930
|
+
);
|
|
10931
|
+
slotProps[slot.name] = slotElement;
|
|
10932
|
+
}
|
|
10342
10933
|
wrappedElement = import_react4.default.createElement(
|
|
10343
10934
|
LayoutComponent,
|
|
10344
10935
|
{
|
|
10345
10936
|
children: wrappedElement,
|
|
10346
|
-
params
|
|
10937
|
+
params,
|
|
10938
|
+
...slotProps
|
|
10347
10939
|
}
|
|
10348
10940
|
);
|
|
10349
10941
|
}
|
|
@@ -10380,7 +10972,10 @@ var init_renderer = __esm({
|
|
|
10380
10972
|
routeManifest,
|
|
10381
10973
|
captureStaticShell: Boolean(pprShellOptions),
|
|
10382
10974
|
observabilityRoute: pathname,
|
|
10383
|
-
onSuspenseHoleDetected: pprShellOptions ? () => emitFarmEvent({
|
|
10975
|
+
onSuspenseHoleDetected: pprShellOptions ? () => emitFarmEvent({
|
|
10976
|
+
type: "ppr.suspense.holeDetected",
|
|
10977
|
+
route: pathname
|
|
10978
|
+
}) : void 0,
|
|
10384
10979
|
onComplete: pprShellOptions && req.method !== "HEAD" ? (html) => this.cachePPRShell(pprShellOptions, html) : void 0
|
|
10385
10980
|
}
|
|
10386
10981
|
);
|
|
@@ -10428,7 +11023,11 @@ var init_renderer = __esm({
|
|
|
10428
11023
|
}
|
|
10429
11024
|
emitFarmEvent({ type: "render.error", route: pathname, error });
|
|
10430
11025
|
if (pprRefreshRoute) {
|
|
10431
|
-
emitFarmEvent({
|
|
11026
|
+
emitFarmEvent({
|
|
11027
|
+
type: "ppr.refresh.error",
|
|
11028
|
+
route: pprRefreshRoute,
|
|
11029
|
+
error
|
|
11030
|
+
});
|
|
10432
11031
|
}
|
|
10433
11032
|
logger.error(`Error rendering page: ${error}`);
|
|
10434
11033
|
if (res.headersSent || res.writableEnded) {
|
|
@@ -10481,7 +11080,9 @@ var init_renderer = __esm({
|
|
|
10481
11080
|
if (typeof layoutModule.generateMetadata === "function") {
|
|
10482
11081
|
metadata = mergeMetadata(
|
|
10483
11082
|
metadata,
|
|
10484
|
-
await layoutModule.generateMetadata({
|
|
11083
|
+
await layoutModule.generateMetadata({
|
|
11084
|
+
params: options.pageProps.params
|
|
11085
|
+
})
|
|
10485
11086
|
);
|
|
10486
11087
|
}
|
|
10487
11088
|
}
|
|
@@ -10712,7 +11313,13 @@ var init_renderer = __esm({
|
|
|
10712
11313
|
clientEntry: "/@farm/client.js",
|
|
10713
11314
|
routes: {},
|
|
10714
11315
|
layouts: {},
|
|
10715
|
-
|
|
11316
|
+
slots: [],
|
|
11317
|
+
sharedAssets: [
|
|
11318
|
+
{
|
|
11319
|
+
tag: "link",
|
|
11320
|
+
attrs: { rel: "stylesheet", href: "/src/app/globals.css" }
|
|
11321
|
+
}
|
|
11322
|
+
]
|
|
10716
11323
|
};
|
|
10717
11324
|
for (const routeEntry of manifest.routes) {
|
|
10718
11325
|
clientManifest.routes[routeEntry.pattern] = {
|
|
@@ -10734,9 +11341,26 @@ var init_renderer = __esm({
|
|
|
10734
11341
|
assets: []
|
|
10735
11342
|
};
|
|
10736
11343
|
}
|
|
10737
|
-
const
|
|
11344
|
+
for (const slotEntry of manifest.slots ?? []) {
|
|
11345
|
+
clientManifest.slots.push({
|
|
11346
|
+
...slotEntry,
|
|
11347
|
+
preloads: [slotEntry.modulePath],
|
|
11348
|
+
assets: []
|
|
11349
|
+
});
|
|
11350
|
+
}
|
|
11351
|
+
const routeSlotPayload = (req.__FARM_ROUTE_SLOTS__ || []).map(
|
|
11352
|
+
(slot) => ({
|
|
11353
|
+
...slot,
|
|
11354
|
+
modulePath: typeof slot.modulePath === "string" && slot.modulePath.startsWith(this.config.root) ? slot.modulePath.slice(this.config.root.length) : slot.modulePath
|
|
11355
|
+
})
|
|
11356
|
+
);
|
|
11357
|
+
const deferredProps = prepareDeferredData({
|
|
11358
|
+
page: req.__FARM_PROPS__ || {},
|
|
11359
|
+
slots: routeSlotPayload
|
|
11360
|
+
});
|
|
10738
11361
|
const propsScript = `<script>
|
|
10739
|
-
window.__FARM_PROPS__ = ${serializeInlineValue(deferredProps.data)};
|
|
11362
|
+
window.__FARM_PROPS__ = ${serializeInlineValue(deferredProps.data.page)};
|
|
11363
|
+
window.__FARM_ROUTE_SLOTS__ = ${serializeInlineValue(deferredProps.data.slots)};
|
|
10740
11364
|
window.__FARM_DEPLOYMENT_ID__ = ${serializeInlineValue(deploymentId)};
|
|
10741
11365
|
window.__FARM_PATH__ = ${JSON.stringify(req.__FARM_ROUTE__ || req.url || "/")};
|
|
10742
11366
|
window.__FARM_IS_CLIENT__ = ${JSON.stringify(isClientComponent)};
|
|
@@ -10749,7 +11373,7 @@ window.__FARM_MANIFEST__ = ${JSON.stringify(clientManifest)};
|
|
|
10749
11373
|
window.__FARM_INTEGRATION_API_MANIFEST__ = ${JSON.stringify(getRegisteredIntegrationAPIManifest())};
|
|
10750
11374
|
${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue(getFarmI18nClientSnapshot())};` : ""}
|
|
10751
11375
|
</script>`;
|
|
10752
|
-
const hydrationClickQueueScript = isClientComponent || req.__FARM_SHOULD_HYDRATE__ === true ? createPreHydrationClickQueueScript() : "";
|
|
11376
|
+
const hydrationClickQueueScript = isClientComponent || req.__FARM_SHOULD_HYDRATE__ === true || req.__FARM_HAS_HYDRATABLE_ROUTE_SLOTS__ === true ? createPreHydrationClickQueueScript() : "";
|
|
10753
11377
|
const {
|
|
10754
11378
|
title,
|
|
10755
11379
|
tags: metaTags,
|
|
@@ -10860,7 +11484,11 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue(g
|
|
|
10860
11484
|
didError = true;
|
|
10861
11485
|
if (!isFarmRedirectError(error) && !isFarmNotFoundError(error)) {
|
|
10862
11486
|
logger.error(`SSR shell error: ${error}`);
|
|
10863
|
-
emitFarmEvent({
|
|
11487
|
+
emitFarmEvent({
|
|
11488
|
+
type: "render.error",
|
|
11489
|
+
route: observabilityRoute,
|
|
11490
|
+
error
|
|
11491
|
+
});
|
|
10864
11492
|
}
|
|
10865
11493
|
if (clearMiddlewareData) {
|
|
10866
11494
|
clearMiddlewareData();
|
|
@@ -10871,7 +11499,11 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue(g
|
|
|
10871
11499
|
didError = true;
|
|
10872
11500
|
if (!isFarmRedirectError(error) && !isFarmNotFoundError(error)) {
|
|
10873
11501
|
logger.error(`SSR streaming error: ${error}`);
|
|
10874
|
-
emitFarmEvent({
|
|
11502
|
+
emitFarmEvent({
|
|
11503
|
+
type: "render.error",
|
|
11504
|
+
route: observabilityRoute,
|
|
11505
|
+
error
|
|
11506
|
+
});
|
|
10875
11507
|
}
|
|
10876
11508
|
}
|
|
10877
11509
|
});
|
|
@@ -11053,6 +11685,7 @@ var init_app = __esm({
|
|
|
11053
11685
|
init_devtools_config();
|
|
11054
11686
|
init_config();
|
|
11055
11687
|
init_server2();
|
|
11688
|
+
init_cache();
|
|
11056
11689
|
defaultDocsConfig = {
|
|
11057
11690
|
enabled: false,
|
|
11058
11691
|
entry: "/docs",
|
|
@@ -11074,6 +11707,7 @@ var init_app = __esm({
|
|
|
11074
11707
|
logger.info("Initializing Farm.js application...");
|
|
11075
11708
|
}
|
|
11076
11709
|
await initStorage(this.config.storage);
|
|
11710
|
+
await configureFarmCache(this.config.cache);
|
|
11077
11711
|
await this.i18nRuntime.initialize();
|
|
11078
11712
|
await this.verifyAppStructure();
|
|
11079
11713
|
await this.routeManager.discoverRoutes();
|
|
@@ -11105,6 +11739,7 @@ var init_app = __esm({
|
|
|
11105
11739
|
preset: config.preset ?? "node-server",
|
|
11106
11740
|
deploy: config.deploy || {},
|
|
11107
11741
|
storage: config.storage || {},
|
|
11742
|
+
cache: config.cache || {},
|
|
11108
11743
|
integrations: config.integrations || {},
|
|
11109
11744
|
plugins: config.plugins || [],
|
|
11110
11745
|
migrations: config.migrations || { commands: [] },
|
|
@@ -11178,6 +11813,13 @@ var init_app = __esm({
|
|
|
11178
11813
|
}
|
|
11179
11814
|
});
|
|
11180
11815
|
|
|
11816
|
+
// src/api/transport.ts
|
|
11817
|
+
var init_transport = __esm({
|
|
11818
|
+
"src/api/transport.ts"() {
|
|
11819
|
+
"use strict";
|
|
11820
|
+
}
|
|
11821
|
+
});
|
|
11822
|
+
|
|
11181
11823
|
// src/api/endpoint.ts
|
|
11182
11824
|
function isEndpointFailure(value) {
|
|
11183
11825
|
return value instanceof EndpointFailure;
|
|
@@ -11188,6 +11830,7 @@ var init_endpoint = __esm({
|
|
|
11188
11830
|
"use strict";
|
|
11189
11831
|
import_better_call = require("better-call");
|
|
11190
11832
|
init_cache();
|
|
11833
|
+
init_transport();
|
|
11191
11834
|
_EndpointFailure = class _EndpointFailure extends Error {
|
|
11192
11835
|
constructor(code, data, options) {
|
|
11193
11836
|
super(options.message);
|
|
@@ -11247,7 +11890,7 @@ async function invokeAPIRouteEndpointInContext(endpoint, request, params) {
|
|
|
11247
11890
|
});
|
|
11248
11891
|
let body = void 0;
|
|
11249
11892
|
if (request.method.toUpperCase() !== "GET" && request.method.toUpperCase() !== "HEAD") {
|
|
11250
|
-
body = await
|
|
11893
|
+
body = await readRequestBody2(request);
|
|
11251
11894
|
}
|
|
11252
11895
|
const headers = Object.fromEntries(request.headers.entries());
|
|
11253
11896
|
const types = endpoint.__types || {};
|
|
@@ -11345,16 +11988,46 @@ function isFarmContextHandler(endpoint) {
|
|
|
11345
11988
|
const firstParameter = (arrowMatch?.[1] || arrowMatch?.[2] || functionMatch?.[1] || "").split(",")[0].trim();
|
|
11346
11989
|
return firstParameter === "ctx" || firstParameter === "context" || firstParameter.startsWith("{");
|
|
11347
11990
|
}
|
|
11348
|
-
async function
|
|
11991
|
+
async function readRequestBody2(request) {
|
|
11992
|
+
const contentType = request.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase();
|
|
11349
11993
|
try {
|
|
11994
|
+
if (contentType === "multipart/form-data") {
|
|
11995
|
+
return formDataToObject(await request.clone().formData());
|
|
11996
|
+
}
|
|
11350
11997
|
const text2 = await request.clone().text();
|
|
11351
|
-
if (text2)
|
|
11998
|
+
if (!text2) return void 0;
|
|
11999
|
+
if (contentType === "application/x-www-form-urlencoded") {
|
|
12000
|
+
return searchParamsToObject2(new URLSearchParams(text2));
|
|
12001
|
+
}
|
|
12002
|
+
if (contentType === "application/json" || contentType?.endsWith("+json")) {
|
|
11352
12003
|
return JSON.parse(text2);
|
|
11353
12004
|
}
|
|
12005
|
+
return JSON.parse(text2);
|
|
11354
12006
|
} catch {
|
|
11355
12007
|
}
|
|
11356
12008
|
return void 0;
|
|
11357
12009
|
}
|
|
12010
|
+
function formDataToObject(formData) {
|
|
12011
|
+
return entriesToObject(formData.entries());
|
|
12012
|
+
}
|
|
12013
|
+
function searchParamsToObject2(searchParams) {
|
|
12014
|
+
return entriesToObject(searchParams.entries());
|
|
12015
|
+
}
|
|
12016
|
+
function entriesToObject(entries) {
|
|
12017
|
+
const output = /* @__PURE__ */ Object.create(null);
|
|
12018
|
+
for (const [key, value] of entries) {
|
|
12019
|
+
if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
|
|
12020
|
+
const current = output[key];
|
|
12021
|
+
if (current === void 0) {
|
|
12022
|
+
output[key] = value;
|
|
12023
|
+
} else if (Array.isArray(current)) {
|
|
12024
|
+
current.push(value);
|
|
12025
|
+
} else {
|
|
12026
|
+
output[key] = [current, value];
|
|
12027
|
+
}
|
|
12028
|
+
}
|
|
12029
|
+
return output;
|
|
12030
|
+
}
|
|
11358
12031
|
function validateInput(schema, value, error) {
|
|
11359
12032
|
if (!schema || typeof schema.parse !== "function") {
|
|
11360
12033
|
return value;
|
|
@@ -11453,7 +12126,10 @@ var init_runtime2 = __esm({
|
|
|
11453
12126
|
__name(attachEndpointInvalidations, "attachEndpointInvalidations");
|
|
11454
12127
|
__name(createEndpointFailureResponse, "createEndpointFailureResponse");
|
|
11455
12128
|
__name(isFarmContextHandler, "isFarmContextHandler");
|
|
11456
|
-
__name(
|
|
12129
|
+
__name(readRequestBody2, "readRequestBody");
|
|
12130
|
+
__name(formDataToObject, "formDataToObject");
|
|
12131
|
+
__name(searchParamsToObject2, "searchParamsToObject");
|
|
12132
|
+
__name(entriesToObject, "entriesToObject");
|
|
11457
12133
|
__name(validateInput, "validateInput");
|
|
11458
12134
|
__name(matchRoutePath, "matchRoutePath");
|
|
11459
12135
|
__name(getPathSegments, "getPathSegments");
|
|
@@ -13810,6 +14486,7 @@ async function generateRouteTypes(options) {
|
|
|
13810
14486
|
absolute: false
|
|
13811
14487
|
});
|
|
13812
14488
|
for (const file of pageFiles) {
|
|
14489
|
+
if (parseRouteSlotFile(file)) continue;
|
|
13813
14490
|
const route = parseRoutePath(file);
|
|
13814
14491
|
if (route.type === "page") {
|
|
13815
14492
|
const pattern = createRoutePattern(route);
|
|
@@ -13875,6 +14552,7 @@ var init_generate_route_types = __esm({
|
|
|
13875
14552
|
path17 = __toESM(require("path"));
|
|
13876
14553
|
fs5 = __toESM(require("fs"));
|
|
13877
14554
|
init_utils();
|
|
14555
|
+
init_route_slots();
|
|
13878
14556
|
init_routes_server();
|
|
13879
14557
|
init_write_file_if_changed();
|
|
13880
14558
|
__name(routeSegmentsToTsTypeLiteral, "routeSegmentsToTsTypeLiteral");
|
|
@@ -14243,7 +14921,7 @@ var init_type_artifacts = __esm({
|
|
|
14243
14921
|
var version;
|
|
14244
14922
|
var init_package = __esm({
|
|
14245
14923
|
"package.json"() {
|
|
14246
|
-
version = "0.1.0-beta.
|
|
14924
|
+
version = "0.1.0-beta.4";
|
|
14247
14925
|
}
|
|
14248
14926
|
});
|
|
14249
14927
|
|
|
@@ -21213,7 +21891,7 @@ function farmPlugin(options = {}, initialPluginManager) {
|
|
|
21213
21891
|
}, "isProgrammaticRouteSourceFile");
|
|
21214
21892
|
const isAppRuntimeFile = /* @__PURE__ */ __name((file) => {
|
|
21215
21893
|
const normalized = file.replace(/\\/g, "/");
|
|
21216
|
-
return appDirSlugs.some((appDir) => normalized.startsWith(`${appDir}/`)) && /\/(?:page|layout|loading|error|middleware|route)\.(?:ts|tsx|js|jsx|md|mdx)$|\/(?:opengraph-image|twitter-image)(?:\.(?:ts|tsx|js|jsx|png|jpg|jpeg|gif|webp)|\.alt\.txt)$/.test(
|
|
21894
|
+
return appDirSlugs.some((appDir) => normalized.startsWith(`${appDir}/`)) && /\/(?:page|default|layout|loading|error|middleware|route)\.(?:ts|tsx|js|jsx|md|mdx)$|\/(?:opengraph-image|twitter-image)(?:\.(?:ts|tsx|js|jsx|png|jpg|jpeg|gif|webp)|\.alt\.txt)$/.test(
|
|
21217
21895
|
normalized
|
|
21218
21896
|
);
|
|
21219
21897
|
}, "isAppRuntimeFile");
|
|
@@ -21904,7 +22582,10 @@ function farmPlugin(options = {}, initialPluginManager) {
|
|
|
21904
22582
|
method: req.method || "GET"
|
|
21905
22583
|
});
|
|
21906
22584
|
}
|
|
21907
|
-
const
|
|
22585
|
+
const interceptFromHeader = request.headers.get("x-farm-intercept-from") || void 0;
|
|
22586
|
+
const match = routeManager3.matchRoute(targetRequestUrl.pathname, {
|
|
22587
|
+
interceptFrom: interceptFromHeader
|
|
22588
|
+
});
|
|
21908
22589
|
if (pm) {
|
|
21909
22590
|
await pm.runHookParallel("afterRouteMatch", {
|
|
21910
22591
|
pathname: targetPath,
|
|
@@ -21920,7 +22601,7 @@ function farmPlugin(options = {}, initialPluginManager) {
|
|
|
21920
22601
|
res.end(JSON.stringify({ error: "Route not found" }));
|
|
21921
22602
|
return;
|
|
21922
22603
|
}
|
|
21923
|
-
const { route, params, layouts } = match;
|
|
22604
|
+
const { route, params, layouts, slots } = match;
|
|
21924
22605
|
if (!route) {
|
|
21925
22606
|
res.statusCode = 404;
|
|
21926
22607
|
res.setHeader("Content-Type", "application/json");
|
|
@@ -21975,6 +22656,54 @@ function farmPlugin(options = {}, initialPluginManager) {
|
|
|
21975
22656
|
search: searchParams,
|
|
21976
22657
|
routePath: route.pattern
|
|
21977
22658
|
});
|
|
22659
|
+
const routeSlots = await Promise.all(
|
|
22660
|
+
slots.map(async (slot) => {
|
|
22661
|
+
const slotModule = await routeManager3.loadRouteModule(slot.route.modulePath);
|
|
22662
|
+
const slotMetadata = getClientModuleMetadata(
|
|
22663
|
+
slot.route.modulePath,
|
|
22664
|
+
server.config.root
|
|
22665
|
+
);
|
|
22666
|
+
const slotContext = await resolveFarmRouteContext(farmApp.getConfig(), {
|
|
22667
|
+
request,
|
|
22668
|
+
params: slot.params,
|
|
22669
|
+
search: searchParams,
|
|
22670
|
+
path: targetUrl.pathname
|
|
22671
|
+
});
|
|
22672
|
+
const slotProps = await parseRouteModuleProps2(slotModule, {
|
|
22673
|
+
props: withFarmRouteContext(
|
|
22674
|
+
{
|
|
22675
|
+
params: slot.params,
|
|
22676
|
+
searchParams: Promise.resolve(searchParams),
|
|
22677
|
+
path: targetUrl.pathname
|
|
22678
|
+
},
|
|
22679
|
+
slotContext
|
|
22680
|
+
),
|
|
22681
|
+
search: searchParams,
|
|
22682
|
+
routePath: slot.route.pattern
|
|
22683
|
+
});
|
|
22684
|
+
return {
|
|
22685
|
+
name: slot.name,
|
|
22686
|
+
ownerPattern: slot.ownerPattern,
|
|
22687
|
+
containerId: slot.containerId,
|
|
22688
|
+
interception: slot.interception,
|
|
22689
|
+
fallback: slot.fallback,
|
|
22690
|
+
modulePath: slot.route.modulePath,
|
|
22691
|
+
isClientComponent: slotMetadata.isClientComponent,
|
|
22692
|
+
shouldHydrate: slotMetadata.shouldHydrate,
|
|
22693
|
+
props: {
|
|
22694
|
+
params: slotProps.params,
|
|
22695
|
+
search: slotProps.search,
|
|
22696
|
+
searchParams: slotProps.search,
|
|
22697
|
+
..."data" in slotProps ? { data: slotProps.data } : {},
|
|
22698
|
+
...slotProps.__farmCanonicalPath ? {
|
|
22699
|
+
__farmCanonicalPath: slotProps.__farmCanonicalPath
|
|
22700
|
+
} : {},
|
|
22701
|
+
...slotProps.__farmRoutePropsResolved ? { __farmRoutePropsResolved: true } : {},
|
|
22702
|
+
path: targetUrl.pathname
|
|
22703
|
+
}
|
|
22704
|
+
};
|
|
22705
|
+
})
|
|
22706
|
+
);
|
|
21978
22707
|
const projectRoot = server.config.root;
|
|
21979
22708
|
const toUrlPath = /* @__PURE__ */ __name((absolutePath) => {
|
|
21980
22709
|
if (absolutePath.startsWith(projectRoot)) {
|
|
@@ -21995,13 +22724,21 @@ function farmPlugin(options = {}, initialPluginManager) {
|
|
|
21995
22724
|
},
|
|
21996
22725
|
canonicalPath: routeProps.__farmCanonicalPath,
|
|
21997
22726
|
modulePath: toUrlPath(route.modulePath),
|
|
21998
|
-
isClientComponent,
|
|
21999
|
-
shouldHydrate,
|
|
22727
|
+
isClientComponent: routeSlots.length > 0 ? false : isClientComponent,
|
|
22728
|
+
shouldHydrate: shouldHydrate || routeSlots.some((slot) => slot.isClientComponent || slot.shouldHydrate),
|
|
22000
22729
|
metadata: {
|
|
22001
22730
|
title: mergedMetadata.title,
|
|
22002
22731
|
description: mergedMetadata.description
|
|
22003
22732
|
},
|
|
22004
22733
|
layoutModules: layouts.map((l) => toUrlPath(l.modulePath)),
|
|
22734
|
+
routeSlots: routeSlots.map((slot) => ({
|
|
22735
|
+
...slot,
|
|
22736
|
+
modulePath: toUrlPath(slot.modulePath)
|
|
22737
|
+
})),
|
|
22738
|
+
interception: routeSlots.some((slot) => slot.interception) ? {
|
|
22739
|
+
from: interceptFromHeader,
|
|
22740
|
+
slots: routeSlots.filter((slot) => slot.interception).map((slot) => slot.name)
|
|
22741
|
+
} : void 0,
|
|
22005
22742
|
i18n: getFarmI18nClientSnapshot()
|
|
22006
22743
|
};
|
|
22007
22744
|
await sendWebResponse(
|
|
@@ -22337,6 +23074,7 @@ function farmPlugin(options = {}, initialPluginManager) {
|
|
|
22337
23074
|
export const getManifest = () => ({
|
|
22338
23075
|
routes: {},
|
|
22339
23076
|
layouts: {},
|
|
23077
|
+
slots: [],
|
|
22340
23078
|
clientEntry: "/@farm/client.js",
|
|
22341
23079
|
sharedAssets: []
|
|
22342
23080
|
});
|
|
@@ -22347,6 +23085,7 @@ export const getManifest = () => ({
|
|
|
22347
23085
|
clientEntry: "/@farm/client.js",
|
|
22348
23086
|
routes: {},
|
|
22349
23087
|
layouts: {},
|
|
23088
|
+
slots: [],
|
|
22350
23089
|
sharedAssets: [
|
|
22351
23090
|
{
|
|
22352
23091
|
tag: "link",
|
|
@@ -22376,6 +23115,13 @@ export const getManifest = () => ({
|
|
|
22376
23115
|
assets: []
|
|
22377
23116
|
};
|
|
22378
23117
|
}
|
|
23118
|
+
for (const slot of manifest.slots) {
|
|
23119
|
+
fullManifest.slots.push({
|
|
23120
|
+
...slot,
|
|
23121
|
+
preloads: [slot.modulePath],
|
|
23122
|
+
assets: []
|
|
23123
|
+
});
|
|
23124
|
+
}
|
|
22379
23125
|
return `
|
|
22380
23126
|
// Auto-generated manifest for SPA navigation (TanStack Start pattern)
|
|
22381
23127
|
// This manifest is inlined in the server bundle - no file on disk
|
|
@@ -23012,7 +23758,7 @@ window.__FARM_WRAP_PROVIDERS__ = wrapWithIntegrationProviders;
|
|
|
23012
23758
|
|
|
23013
23759
|
// Get manifest from window (inlined by server in HTML)
|
|
23014
23760
|
// Fallback to empty manifest if not available yet
|
|
23015
|
-
const getManifest = () => window.__FARM_MANIFEST__ || { routes: {}, layouts: {}, clientEntry: '', sharedAssets: [] };
|
|
23761
|
+
const getManifest = () => window.__FARM_MANIFEST__ || { routes: {}, layouts: {}, slots: [], clientEntry: '', sharedAssets: [] };
|
|
23016
23762
|
|
|
23017
23763
|
// ====== CLIENT-SIDE ROUTE MATCHING ======
|
|
23018
23764
|
// Matches URL to route using manifest (no server request!)
|
|
@@ -23351,6 +24097,9 @@ let LayoutComponent = null;
|
|
|
23351
24097
|
|
|
23352
24098
|
// Track if we've taken over rendering from SSR
|
|
23353
24099
|
let hasClientTakenOver = false;
|
|
24100
|
+
const routeSlotRoots = new Map();
|
|
24101
|
+
const routeSlotDefinitions = new Map();
|
|
24102
|
+
let activeRouteInterception = null;
|
|
23354
24103
|
|
|
23355
24104
|
function normalizeServerProps(rawProps) {
|
|
23356
24105
|
const props = rawProps && typeof rawProps === 'object' ? { ...rawProps } : {};
|
|
@@ -23363,6 +24112,123 @@ function normalizeServerProps(rawProps) {
|
|
|
23363
24112
|
return props;
|
|
23364
24113
|
}
|
|
23365
24114
|
|
|
24115
|
+
function getRouteSlotKey(slot) {
|
|
24116
|
+
return slot.ownerPattern + ':' + slot.name;
|
|
24117
|
+
}
|
|
24118
|
+
|
|
24119
|
+
async function renderRouteSlot(slot, mode = 'render') {
|
|
24120
|
+
const container = document.getElementById(slot.containerId);
|
|
24121
|
+
if (!container || !slot.modulePath) return false;
|
|
24122
|
+
if (mode === 'intercept' && !slot.isClientComponent && !slot.shouldHydrate) {
|
|
24123
|
+
return false;
|
|
24124
|
+
}
|
|
24125
|
+
|
|
24126
|
+
let slotModule = pageModuleCache.get(slot.modulePath);
|
|
24127
|
+
if (!slotModule) {
|
|
24128
|
+
slotModule = await import(/* @vite-ignore */ slot.modulePath);
|
|
24129
|
+
pageModuleCache.set(slot.modulePath, slotModule);
|
|
24130
|
+
}
|
|
24131
|
+
const SlotComponent = slotModule?.default;
|
|
24132
|
+
if (!SlotComponent) return false;
|
|
24133
|
+
|
|
24134
|
+
const props = reviveDeferredData(
|
|
24135
|
+
normalizeServerProps(slot.props || {}),
|
|
24136
|
+
window.__FARM_DEFERRED_DATA__ || {},
|
|
24137
|
+
);
|
|
24138
|
+
const element = wrapWithIntegrationProviders(
|
|
24139
|
+
React.createElement(SlotComponent, props),
|
|
24140
|
+
);
|
|
24141
|
+
const key = getRouteSlotKey(slot);
|
|
24142
|
+
const existingRoot = routeSlotRoots.get(key);
|
|
24143
|
+
|
|
24144
|
+
if (mode === 'hydrate') {
|
|
24145
|
+
if (existingRoot) return true;
|
|
24146
|
+
const root = hydrateRoot(container, element);
|
|
24147
|
+
routeSlotRoots.set(key, root);
|
|
24148
|
+
} else {
|
|
24149
|
+
if (existingRoot) {
|
|
24150
|
+
try { existingRoot.unmount(); } catch (error) {}
|
|
24151
|
+
}
|
|
24152
|
+
const root = createRoot(container);
|
|
24153
|
+
root.render(element);
|
|
24154
|
+
routeSlotRoots.set(key, root);
|
|
24155
|
+
}
|
|
24156
|
+
|
|
24157
|
+
routeSlotDefinitions.set(key, slot);
|
|
24158
|
+
return true;
|
|
24159
|
+
}
|
|
24160
|
+
|
|
24161
|
+
async function hydrateInitialRouteSlots() {
|
|
24162
|
+
const slots = Array.isArray(window.__FARM_ROUTE_SLOTS__)
|
|
24163
|
+
? window.__FARM_ROUTE_SLOTS__
|
|
24164
|
+
: [];
|
|
24165
|
+
let hydrated = false;
|
|
24166
|
+
|
|
24167
|
+
for (const slot of slots) {
|
|
24168
|
+
routeSlotDefinitions.set(getRouteSlotKey(slot), slot);
|
|
24169
|
+
if (!slot.isClientComponent && !slot.shouldHydrate) continue;
|
|
24170
|
+
try {
|
|
24171
|
+
hydrated = (await renderRouteSlot(slot, 'hydrate')) || hydrated;
|
|
24172
|
+
} catch (error) {
|
|
24173
|
+
console.warn('[Farm.js] Could not hydrate route slot:', slot.name, error);
|
|
24174
|
+
}
|
|
24175
|
+
}
|
|
24176
|
+
|
|
24177
|
+
return hydrated;
|
|
24178
|
+
}
|
|
24179
|
+
|
|
24180
|
+
async function renderRouteInterception(pageData) {
|
|
24181
|
+
const slots = Array.isArray(pageData.routeSlots)
|
|
24182
|
+
? pageData.routeSlots.filter((slot) => slot.interception)
|
|
24183
|
+
: [];
|
|
24184
|
+
if (!pageData.interception || slots.length === 0) return false;
|
|
24185
|
+
|
|
24186
|
+
const previous = new Map();
|
|
24187
|
+
for (const slot of slots) {
|
|
24188
|
+
const key = getRouteSlotKey(slot);
|
|
24189
|
+
previous.set(key, routeSlotDefinitions.get(key) || null);
|
|
24190
|
+
if (!(await renderRouteSlot(slot, 'intercept'))) {
|
|
24191
|
+
return false;
|
|
24192
|
+
}
|
|
24193
|
+
}
|
|
24194
|
+
|
|
24195
|
+
activeRouteInterception = {
|
|
24196
|
+
from: pageData.interception.from,
|
|
24197
|
+
slots,
|
|
24198
|
+
previous,
|
|
24199
|
+
};
|
|
24200
|
+
return true;
|
|
24201
|
+
}
|
|
24202
|
+
|
|
24203
|
+
async function clearRouteInterception(destination) {
|
|
24204
|
+
if (!activeRouteInterception) return false;
|
|
24205
|
+
const active = activeRouteInterception;
|
|
24206
|
+
activeRouteInterception = null;
|
|
24207
|
+
|
|
24208
|
+
for (const slot of active.slots) {
|
|
24209
|
+
const key = getRouteSlotKey(slot);
|
|
24210
|
+
const root = routeSlotRoots.get(key);
|
|
24211
|
+
if (root) {
|
|
24212
|
+
try { root.unmount(); } catch (error) {}
|
|
24213
|
+
routeSlotRoots.delete(key);
|
|
24214
|
+
}
|
|
24215
|
+
|
|
24216
|
+
const previous = active.previous.get(key);
|
|
24217
|
+
if (previous && (previous.isClientComponent || previous.shouldHydrate)) {
|
|
24218
|
+
await renderRouteSlot(previous, 'render');
|
|
24219
|
+
} else {
|
|
24220
|
+
const container = document.getElementById(slot.containerId);
|
|
24221
|
+
if (container) container.replaceChildren();
|
|
24222
|
+
if (previous) routeSlotDefinitions.set(key, previous);
|
|
24223
|
+
else routeSlotDefinitions.delete(key);
|
|
24224
|
+
}
|
|
24225
|
+
}
|
|
24226
|
+
|
|
24227
|
+
if (!active.from) return false;
|
|
24228
|
+
const background = new URL(active.from, window.location.origin);
|
|
24229
|
+
return destination === background.pathname + background.search;
|
|
24230
|
+
}
|
|
24231
|
+
|
|
23366
24232
|
function applyCanonicalPathFromProps(props) {
|
|
23367
24233
|
const canonicalPath = props && typeof props.__farmCanonicalPath === 'string'
|
|
23368
24234
|
? props.__farmCanonicalPath
|
|
@@ -23597,6 +24463,17 @@ async function tryHydrateImportedPage(
|
|
|
23597
24463
|
async function renderPage(pageData) {
|
|
23598
24464
|
const container = document.getElementById('root');
|
|
23599
24465
|
if (!container) return;
|
|
24466
|
+
const destination = window.location.pathname + window.location.search;
|
|
24467
|
+
|
|
24468
|
+
if (pageData.interception) {
|
|
24469
|
+
if (await renderRouteInterception(pageData)) return;
|
|
24470
|
+
window.location.assign(destination);
|
|
24471
|
+
return;
|
|
24472
|
+
}
|
|
24473
|
+
|
|
24474
|
+
if (await clearRouteInterception(destination)) {
|
|
24475
|
+
return;
|
|
24476
|
+
}
|
|
23600
24477
|
|
|
23601
24478
|
const route = {
|
|
23602
24479
|
modulePath: pageData.modulePath,
|
|
@@ -23825,7 +24702,9 @@ async function hydrate() {
|
|
|
23825
24702
|
window.__FARM_SHOULD_HYDRATE__ === true ||
|
|
23826
24703
|
isClientComponent ||
|
|
23827
24704
|
await moduleLooksClient(modulePath);
|
|
24705
|
+
const hydratedSlots = await hydrateInitialRouteSlots();
|
|
23828
24706
|
if (!shouldHydrate) {
|
|
24707
|
+
if (hydratedSlots) replayPreHydrationClicks();
|
|
23829
24708
|
console.log('[Farm.js] Server component - SPA router ready')
|
|
23830
24709
|
return
|
|
23831
24710
|
}
|
|
@@ -26338,6 +27217,18 @@ async function buildUniversal(config, routeManager, apiRouteManager, serverRende
|
|
|
26338
27217
|
...isFarmMarkdownPageFile(entry.modulePath) ? { source: (0, import_fs5.readFileSync)(entry.modulePath, "utf8") } : {}
|
|
26339
27218
|
});
|
|
26340
27219
|
}
|
|
27220
|
+
const routeSlots = Array.from(
|
|
27221
|
+
routeManager.getRouteSlots().values(),
|
|
27222
|
+
(entry) => ({
|
|
27223
|
+
name: entry.name,
|
|
27224
|
+
ownerPattern: entry.ownerPattern,
|
|
27225
|
+
pattern: entry.pattern,
|
|
27226
|
+
modulePath: entry.modulePath,
|
|
27227
|
+
containerId: entry.containerId,
|
|
27228
|
+
interception: entry.interception,
|
|
27229
|
+
fallback: entry.fallback
|
|
27230
|
+
})
|
|
27231
|
+
);
|
|
26341
27232
|
logger.info(`\u{1F4CB} Found ${pageRoutes.length} page routes`);
|
|
26342
27233
|
const fs8 = await import("fs/promises");
|
|
26343
27234
|
const layoutRoutes = [];
|
|
@@ -26381,7 +27272,16 @@ async function buildUniversal(config, routeManager, apiRouteManager, serverRende
|
|
|
26381
27272
|
logger.info("\u{1F4E6} Building client and SSR bundles in parallel...");
|
|
26382
27273
|
const [clientBuildResult, ssrBuildResult] = await Promise.allSettled([
|
|
26383
27274
|
// Client build (to disk)
|
|
26384
|
-
buildClient(
|
|
27275
|
+
buildClient(
|
|
27276
|
+
productionVite,
|
|
27277
|
+
config,
|
|
27278
|
+
root,
|
|
27279
|
+
srcDir,
|
|
27280
|
+
clientOutputDir,
|
|
27281
|
+
pageRoutes,
|
|
27282
|
+
layoutRoutes,
|
|
27283
|
+
routeSlots
|
|
27284
|
+
),
|
|
26385
27285
|
// SSR build (in memory)
|
|
26386
27286
|
buildSSRInMemory(
|
|
26387
27287
|
productionVite,
|
|
@@ -26392,7 +27292,8 @@ async function buildUniversal(config, routeManager, apiRouteManager, serverRende
|
|
|
26392
27292
|
serverRenderer,
|
|
26393
27293
|
preset,
|
|
26394
27294
|
pageRoutes,
|
|
26395
|
-
layoutRoutes
|
|
27295
|
+
layoutRoutes,
|
|
27296
|
+
routeSlots
|
|
26396
27297
|
)
|
|
26397
27298
|
]);
|
|
26398
27299
|
const [routeRuntimeManifestResult] = await routeRuntimeManifestResultPromise;
|
|
@@ -26412,7 +27313,8 @@ async function buildUniversal(config, routeManager, apiRouteManager, serverRende
|
|
|
26412
27313
|
srcDir,
|
|
26413
27314
|
clientOutputDir,
|
|
26414
27315
|
pageRoutes,
|
|
26415
|
-
layoutRoutes
|
|
27316
|
+
layoutRoutes,
|
|
27317
|
+
routeSlots
|
|
26416
27318
|
);
|
|
26417
27319
|
}
|
|
26418
27320
|
const ssrResult = ssrBuildResult.value;
|
|
@@ -26515,7 +27417,7 @@ async function validateClientBuildOutput(root, srcDir, outputDir) {
|
|
|
26515
27417
|
}
|
|
26516
27418
|
}
|
|
26517
27419
|
__name(validateClientBuildOutput, "validateClientBuildOutput");
|
|
26518
|
-
async function buildClient(productionVite, config, root, srcDir, outputDir, pageRoutes, layoutRoutes = []) {
|
|
27420
|
+
async function buildClient(productionVite, config, root, srcDir, outputDir, pageRoutes, layoutRoutes = [], routeSlots = []) {
|
|
26519
27421
|
const viteBuild = productionVite.build;
|
|
26520
27422
|
const { farmPlugin: farmPlugin2 } = await Promise.resolve().then(() => (init_vite(), vite_exports));
|
|
26521
27423
|
const { PluginManager: PluginManager2 } = await Promise.resolve().then(() => (init_plugin(), plugin_exports));
|
|
@@ -26542,8 +27444,16 @@ async function buildClient(productionVite, config, root, srcDir, outputDir, page
|
|
|
26542
27444
|
logger.warn(`\u26A0\uFE0F Could not inspect route file ${route.modulePath}: ${error}`);
|
|
26543
27445
|
}
|
|
26544
27446
|
}
|
|
27447
|
+
const clientRouteSlots = routeSlots.filter((slot) => {
|
|
27448
|
+
try {
|
|
27449
|
+
return getClientModuleMetadata(slot.modulePath, root).shouldHydrate;
|
|
27450
|
+
} catch (error) {
|
|
27451
|
+
logger.warn(`\u26A0\uFE0F Could not inspect route slot ${slot.modulePath}: ${error}`);
|
|
27452
|
+
return false;
|
|
27453
|
+
}
|
|
27454
|
+
});
|
|
26545
27455
|
logger.info(
|
|
26546
|
-
`\u{1F4F1} Total hydratable routes detected: ${clientPages.length}
|
|
27456
|
+
`\u{1F4F1} Total hydratable routes detected: ${clientPages.length} pages and ${clientRouteSlots.length} slots`
|
|
26547
27457
|
);
|
|
26548
27458
|
const clientEntryRoot = import_path12.default.join(root, ".farm", "tmp");
|
|
26549
27459
|
await fs8.mkdir(clientEntryRoot, { recursive: true });
|
|
@@ -26552,6 +27462,7 @@ async function buildClient(productionVite, config, root, srcDir, outputDir, page
|
|
|
26552
27462
|
const clientHydrationCode = generateClientHydrationEntry(
|
|
26553
27463
|
clientPages,
|
|
26554
27464
|
layoutRoutes,
|
|
27465
|
+
clientRouteSlots,
|
|
26555
27466
|
root,
|
|
26556
27467
|
srcDir,
|
|
26557
27468
|
isFarmDocsSearchEnabled(config.docs),
|
|
@@ -26884,7 +27795,7 @@ function generateUniversalRouterStateProperties() {
|
|
|
26884
27795
|
`.trim();
|
|
26885
27796
|
}
|
|
26886
27797
|
__name(generateUniversalRouterStateProperties, "generateUniversalRouterStateProperties");
|
|
26887
|
-
function generateClientHydrationEntry(clientPages, layoutRoutes, root, srcDir, docsSearchEnabled, docsSearchModuleId, i18nConfig = {
|
|
27798
|
+
function generateClientHydrationEntry(clientPages, layoutRoutes, clientRouteSlots, root, srcDir, docsSearchEnabled, docsSearchModuleId, i18nConfig = {
|
|
26888
27799
|
enabled: false,
|
|
26889
27800
|
locales: ["en"],
|
|
26890
27801
|
defaultLocale: "en",
|
|
@@ -26952,7 +27863,7 @@ function isFarmLocaleDocumentChange() {
|
|
|
26952
27863
|
return false;
|
|
26953
27864
|
}
|
|
26954
27865
|
`;
|
|
26955
|
-
if (clientPages.length === 0) {
|
|
27866
|
+
if (clientPages.length === 0 && clientRouteSlots.length === 0) {
|
|
26956
27867
|
return `
|
|
26957
27868
|
// Farm.js Client Runtime (no client components)
|
|
26958
27869
|
${cssImport}
|
|
@@ -27202,11 +28113,25 @@ document.addEventListener("click", function(e) {
|
|
|
27202
28113
|
}
|
|
27203
28114
|
const imports = [];
|
|
27204
28115
|
const routeEntries = [];
|
|
28116
|
+
const routeSlotEntries = [];
|
|
27205
28117
|
clientPages.forEach((page2, index) => {
|
|
27206
28118
|
const importPath = toImportPath(page2.modulePath);
|
|
27207
28119
|
imports.push(`import Page${index} from "${importPath}";`);
|
|
27208
28120
|
routeEntries.push(` { pattern: ${JSON.stringify(page2.pattern)}, Component: Page${index} }`);
|
|
27209
28121
|
});
|
|
28122
|
+
clientRouteSlots.forEach((slot, index) => {
|
|
28123
|
+
const importPath = toImportPath(slot.modulePath);
|
|
28124
|
+
imports.push(`import RouteSlot${index} from "${importPath}";`);
|
|
28125
|
+
routeSlotEntries.push(` {
|
|
28126
|
+
name: ${JSON.stringify(slot.name)},
|
|
28127
|
+
ownerPattern: ${JSON.stringify(slot.ownerPattern)},
|
|
28128
|
+
pattern: ${JSON.stringify(slot.pattern)},
|
|
28129
|
+
containerId: ${JSON.stringify(slot.containerId)},
|
|
28130
|
+
interception: ${JSON.stringify(slot.interception)},
|
|
28131
|
+
fallback: ${JSON.stringify(slot.fallback)},
|
|
28132
|
+
Component: RouteSlot${index},
|
|
28133
|
+
}`);
|
|
28134
|
+
});
|
|
27210
28135
|
return `
|
|
27211
28136
|
// Farm.js Client Runtime - SPA with Hydration
|
|
27212
28137
|
${cssImport}
|
|
@@ -27228,6 +28153,10 @@ const clientRoutes = [
|
|
|
27228
28153
|
${routeEntries.join(",\n")}
|
|
27229
28154
|
];
|
|
27230
28155
|
|
|
28156
|
+
const clientRouteSlots = [
|
|
28157
|
+
${routeSlotEntries.join(",\n")}
|
|
28158
|
+
];
|
|
28159
|
+
|
|
27231
28160
|
// Layout routes for wrapping client components
|
|
27232
28161
|
const layoutRoutes = [
|
|
27233
28162
|
${layoutRegistrations.join(",\n")}
|
|
@@ -27282,6 +28211,146 @@ function matchRoute(pathname) {
|
|
|
27282
28211
|
return null;
|
|
27283
28212
|
}
|
|
27284
28213
|
|
|
28214
|
+
function matchesRoutePrefix(pathname, pattern) {
|
|
28215
|
+
if (pattern === "/") return true;
|
|
28216
|
+
const pathSegments = getFarmRoutePathname(pathname).split("/").filter(Boolean);
|
|
28217
|
+
const patternSegments = pattern.split("/").filter(Boolean);
|
|
28218
|
+
if (patternSegments.length > pathSegments.length) return false;
|
|
28219
|
+
const candidate = "/" + pathSegments.slice(0, patternSegments.length).join("/");
|
|
28220
|
+
return matchFarmRoute(pattern, candidate) !== null;
|
|
28221
|
+
}
|
|
28222
|
+
|
|
28223
|
+
function matchInterceptedRouteSlot(pathname, from) {
|
|
28224
|
+
for (const slot of clientRouteSlots) {
|
|
28225
|
+
if (!slot.interception || !matchesRoutePrefix(from, slot.ownerPattern)) continue;
|
|
28226
|
+
const params = matchFarmRoute(slot.pattern, getFarmRoutePathname(pathname));
|
|
28227
|
+
if (params !== null) return { slot: slot, params: params };
|
|
28228
|
+
}
|
|
28229
|
+
return null;
|
|
28230
|
+
}
|
|
28231
|
+
|
|
28232
|
+
function routeSlotKey(slot) {
|
|
28233
|
+
return slot.ownerPattern + ":" + slot.name;
|
|
28234
|
+
}
|
|
28235
|
+
|
|
28236
|
+
function findClientRouteSlot(slot) {
|
|
28237
|
+
return clientRouteSlots.find(function(candidate) {
|
|
28238
|
+
return candidate.name === slot.name &&
|
|
28239
|
+
candidate.ownerPattern === slot.ownerPattern &&
|
|
28240
|
+
candidate.pattern === slot.pattern &&
|
|
28241
|
+
candidate.interception === slot.interception &&
|
|
28242
|
+
candidate.fallback === slot.fallback;
|
|
28243
|
+
});
|
|
28244
|
+
}
|
|
28245
|
+
|
|
28246
|
+
function readRouteSlotPayload(doc) {
|
|
28247
|
+
const script = doc.getElementById("__farm_route_slots_data__");
|
|
28248
|
+
if (!script?.textContent) return [];
|
|
28249
|
+
try {
|
|
28250
|
+
const parsed = JSON.parse(script.textContent);
|
|
28251
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
28252
|
+
} catch {
|
|
28253
|
+
return [];
|
|
28254
|
+
}
|
|
28255
|
+
}
|
|
28256
|
+
|
|
28257
|
+
const routeSlotRoots = new Map();
|
|
28258
|
+
let activeRouteInterception = null;
|
|
28259
|
+
|
|
28260
|
+
function resetRouteSlotRoots() {
|
|
28261
|
+
for (const root of routeSlotRoots.values()) {
|
|
28262
|
+
try { root.unmount(); } catch {}
|
|
28263
|
+
}
|
|
28264
|
+
routeSlotRoots.clear();
|
|
28265
|
+
activeRouteInterception = null;
|
|
28266
|
+
}
|
|
28267
|
+
|
|
28268
|
+
function renderClientRouteSlot(slot, registration, mode) {
|
|
28269
|
+
const container = document.getElementById(slot.containerId);
|
|
28270
|
+
if (!container || !registration?.Component) return false;
|
|
28271
|
+
const key = routeSlotKey(slot);
|
|
28272
|
+
const existingRoot = routeSlotRoots.get(key);
|
|
28273
|
+
const props = slot.props && typeof slot.props === "object" ? slot.props : {};
|
|
28274
|
+
const element = React.createElement(registration.Component, props);
|
|
28275
|
+
|
|
28276
|
+
if (mode === "hydrate") {
|
|
28277
|
+
if (existingRoot) return true;
|
|
28278
|
+
const root = hydrateRoot(container, element);
|
|
28279
|
+
routeSlotRoots.set(key, root);
|
|
28280
|
+
return true;
|
|
28281
|
+
}
|
|
28282
|
+
|
|
28283
|
+
if (existingRoot) {
|
|
28284
|
+
try { existingRoot.unmount(); } catch {}
|
|
28285
|
+
routeSlotRoots.delete(key);
|
|
28286
|
+
} else {
|
|
28287
|
+
container.replaceChildren();
|
|
28288
|
+
}
|
|
28289
|
+
const root = createRoot(container);
|
|
28290
|
+
root.render(element);
|
|
28291
|
+
routeSlotRoots.set(key, root);
|
|
28292
|
+
return true;
|
|
28293
|
+
}
|
|
28294
|
+
|
|
28295
|
+
function hydrateInitialRouteSlots() {
|
|
28296
|
+
const slots = Array.isArray(window.__FARM_ROUTE_SLOTS__)
|
|
28297
|
+
? window.__FARM_ROUTE_SLOTS__
|
|
28298
|
+
: [];
|
|
28299
|
+
for (const slot of slots) {
|
|
28300
|
+
const registration = findClientRouteSlot(slot);
|
|
28301
|
+
if (!registration) continue;
|
|
28302
|
+
try {
|
|
28303
|
+
renderClientRouteSlot(slot, registration, "hydrate");
|
|
28304
|
+
} catch (error) {
|
|
28305
|
+
console.warn("[Farm.js] Could not hydrate route slot:", slot.name, error);
|
|
28306
|
+
}
|
|
28307
|
+
}
|
|
28308
|
+
}
|
|
28309
|
+
|
|
28310
|
+
function renderRouteInterception(slot, registration, from) {
|
|
28311
|
+
const container = document.getElementById(slot.containerId);
|
|
28312
|
+
if (!container) return false;
|
|
28313
|
+
const key = routeSlotKey(slot);
|
|
28314
|
+
const previousSlot = (window.__FARM_ROUTE_SLOTS__ || []).find(function(candidate) {
|
|
28315
|
+
return routeSlotKey(candidate) === key;
|
|
28316
|
+
}) || null;
|
|
28317
|
+
const previousHtml = container.innerHTML;
|
|
28318
|
+
|
|
28319
|
+
if (!renderClientRouteSlot(slot, registration, "render")) return false;
|
|
28320
|
+
activeRouteInterception = {
|
|
28321
|
+
from: from,
|
|
28322
|
+
key: key,
|
|
28323
|
+
slot: slot,
|
|
28324
|
+
previousSlot: previousSlot,
|
|
28325
|
+
previousHtml: previousHtml,
|
|
28326
|
+
};
|
|
28327
|
+
return true;
|
|
28328
|
+
}
|
|
28329
|
+
|
|
28330
|
+
function clearRouteInterception(destination) {
|
|
28331
|
+
if (!activeRouteInterception) return false;
|
|
28332
|
+
const active = activeRouteInterception;
|
|
28333
|
+
activeRouteInterception = null;
|
|
28334
|
+
const root = routeSlotRoots.get(active.key);
|
|
28335
|
+
if (root) {
|
|
28336
|
+
try { root.unmount(); } catch {}
|
|
28337
|
+
routeSlotRoots.delete(active.key);
|
|
28338
|
+
}
|
|
28339
|
+
const container = document.getElementById(active.slot.containerId);
|
|
28340
|
+
if (container) {
|
|
28341
|
+
container.innerHTML = active.previousHtml;
|
|
28342
|
+
if (active.previousSlot) {
|
|
28343
|
+
const registration = findClientRouteSlot(active.previousSlot);
|
|
28344
|
+
if (registration) {
|
|
28345
|
+
try {
|
|
28346
|
+
renderClientRouteSlot(active.previousSlot, registration, "hydrate");
|
|
28347
|
+
} catch {}
|
|
28348
|
+
}
|
|
28349
|
+
}
|
|
28350
|
+
}
|
|
28351
|
+
return destination === active.from;
|
|
28352
|
+
}
|
|
28353
|
+
|
|
27285
28354
|
// State
|
|
27286
28355
|
let reactRoot = null;
|
|
27287
28356
|
let currentPathname = null;
|
|
@@ -27300,12 +28369,17 @@ function resetReactRoot() {
|
|
|
27300
28369
|
async function hydrate() {
|
|
27301
28370
|
const pathname = window.location.pathname;
|
|
27302
28371
|
const matched = matchRoute(pathname);
|
|
27303
|
-
|
|
28372
|
+
hydrateInitialRouteSlots();
|
|
28373
|
+
|
|
27304
28374
|
if (!matched) {
|
|
27305
28375
|
return;
|
|
27306
28376
|
}
|
|
27307
28377
|
|
|
27308
|
-
const
|
|
28378
|
+
const hasRouteSlots =
|
|
28379
|
+
Array.isArray(window.__FARM_ROUTE_SLOTS__) && window.__FARM_ROUTE_SLOTS__.length > 0;
|
|
28380
|
+
const container =
|
|
28381
|
+
(hasRouteSlots ? document.getElementById("__farm_page__") : null) ||
|
|
28382
|
+
document.getElementById("root");
|
|
27309
28383
|
if (!container) {
|
|
27310
28384
|
console.error("[Farm.js] No root element found");
|
|
27311
28385
|
return;
|
|
@@ -27319,7 +28393,9 @@ async function hydrate() {
|
|
|
27319
28393
|
|
|
27320
28394
|
// Create page element and wrap with layouts
|
|
27321
28395
|
const pageElement = React.createElement(Component, props);
|
|
27322
|
-
const wrappedElement =
|
|
28396
|
+
const wrappedElement = hasRouteSlots
|
|
28397
|
+
? pageElement
|
|
28398
|
+
: wrapWithLayouts(pageElement, pathname, params);
|
|
27323
28399
|
|
|
27324
28400
|
const shouldHydrate = !isHydrated && Boolean(container.innerHTML.trim());
|
|
27325
28401
|
const hydrationSession = await farmClientRuntime.beginHydration({
|
|
@@ -27384,6 +28460,57 @@ ${generateUniversalRouterStateProperties()}
|
|
|
27384
28460
|
: undefined,
|
|
27385
28461
|
});
|
|
27386
28462
|
|
|
28463
|
+
if (activeRouteInterception && clearRouteInterception(to)) {
|
|
28464
|
+
await farmClientRuntime.markNavigationLoaded(clientNavigation, {
|
|
28465
|
+
routeSlots: "restored",
|
|
28466
|
+
});
|
|
28467
|
+
await farmClientRuntime.resolveNavigation(clientNavigation);
|
|
28468
|
+
void farmClientRuntime.scheduleNavigationRendered(clientNavigation);
|
|
28469
|
+
this.currentPath = to;
|
|
28470
|
+
this.finishNavigation();
|
|
28471
|
+
return;
|
|
28472
|
+
}
|
|
28473
|
+
if (activeRouteInterception) {
|
|
28474
|
+
clearRouteInterception(to);
|
|
28475
|
+
}
|
|
28476
|
+
|
|
28477
|
+
const intercepted = matchInterceptedRouteSlot(pathname, from);
|
|
28478
|
+
if (intercepted) {
|
|
28479
|
+
const html = await this.fetchPage(to, from);
|
|
28480
|
+
const doc = new DOMParser().parseFromString(html, "text/html");
|
|
28481
|
+
const slotPayload = readRouteSlotPayload(doc);
|
|
28482
|
+
const selectedSlot = slotPayload.find(function(slot) {
|
|
28483
|
+
return slot.interception &&
|
|
28484
|
+
slot.name === intercepted.slot.name &&
|
|
28485
|
+
slot.ownerPattern === intercepted.slot.ownerPattern;
|
|
28486
|
+
});
|
|
28487
|
+
|
|
28488
|
+
if (
|
|
28489
|
+
selectedSlot &&
|
|
28490
|
+
renderRouteInterception(selectedSlot, intercepted.slot, from)
|
|
28491
|
+
) {
|
|
28492
|
+
await farmClientRuntime.markNavigationLoaded(clientNavigation, {
|
|
28493
|
+
route: intercepted.slot.pattern,
|
|
28494
|
+
params: intercepted.params,
|
|
28495
|
+
});
|
|
28496
|
+
const historyState = createHistoryState(
|
|
28497
|
+
to,
|
|
28498
|
+
options.state,
|
|
28499
|
+
window.history.state,
|
|
28500
|
+
);
|
|
28501
|
+
if (action === "replace") {
|
|
28502
|
+
window.history.replaceState(historyState, "", url);
|
|
28503
|
+
} else if (action !== "pop") {
|
|
28504
|
+
window.history.pushState(historyState, "", url);
|
|
28505
|
+
}
|
|
28506
|
+
await farmClientRuntime.resolveNavigation(clientNavigation);
|
|
28507
|
+
void farmClientRuntime.scheduleNavigationRendered(clientNavigation);
|
|
28508
|
+
this.currentPath = to;
|
|
28509
|
+
this.finishNavigation();
|
|
28510
|
+
return;
|
|
28511
|
+
}
|
|
28512
|
+
}
|
|
28513
|
+
|
|
27387
28514
|
if (matched) {
|
|
27388
28515
|
const Component = matched.route.Component;
|
|
27389
28516
|
const params = matched.params;
|
|
@@ -27460,21 +28587,28 @@ ${generateUniversalRouterStateProperties()}
|
|
|
27460
28587
|
}
|
|
27461
28588
|
},
|
|
27462
28589
|
|
|
27463
|
-
fetchPage: async function(url) {
|
|
27464
|
-
const
|
|
28590
|
+
fetchPage: async function(url, interceptFrom) {
|
|
28591
|
+
const cacheKey = interceptFrom ? url + "\\nintercept:" + interceptFrom : url;
|
|
28592
|
+
const cached = this.prefetchCache.get(cacheKey);
|
|
27465
28593
|
if (cached) return cached;
|
|
27466
28594
|
|
|
27467
28595
|
const response = await fetch(url, {
|
|
27468
|
-
headers: {
|
|
28596
|
+
headers: {
|
|
28597
|
+
"Accept": "text/html",
|
|
28598
|
+
...(interceptFrom ? { "X-Farm-Intercept-From": interceptFrom } : {}),
|
|
28599
|
+
}
|
|
27469
28600
|
});
|
|
27470
28601
|
if (!response.ok) throw new Error("Failed to fetch page");
|
|
27471
|
-
|
|
28602
|
+
const html = await response.text();
|
|
28603
|
+
this.prefetchCache.set(cacheKey, html);
|
|
28604
|
+
return html;
|
|
27472
28605
|
},
|
|
27473
28606
|
|
|
27474
28607
|
swapContent: function(html, targetPath) {
|
|
27475
28608
|
const parser = new DOMParser();
|
|
27476
28609
|
const doc = parser.parseFromString(html, "text/html");
|
|
27477
28610
|
if (isFarmLocaleDocumentChange(doc)) return false;
|
|
28611
|
+
const nextRouteSlots = readRouteSlotPayload(doc);
|
|
27478
28612
|
|
|
27479
28613
|
// Update title
|
|
27480
28614
|
const newTitle = doc.querySelector("title");
|
|
@@ -27499,7 +28633,10 @@ ${generateUniversalRouterStateProperties()}
|
|
|
27499
28633
|
const currentRoot = document.getElementById("root");
|
|
27500
28634
|
if (!newRoot || !currentRoot) return this.swapDocument(doc);
|
|
27501
28635
|
resetReactRoot();
|
|
28636
|
+
resetRouteSlotRoots();
|
|
27502
28637
|
currentRoot.innerHTML = newRoot.innerHTML;
|
|
28638
|
+
window.__FARM_ROUTE_SLOTS__ = nextRouteSlots;
|
|
28639
|
+
hydrateInitialRouteSlots();
|
|
27503
28640
|
|
|
27504
28641
|
// Check if new page has a client component
|
|
27505
28642
|
const targetUrl = new URL(targetPath || window.location.href, window.location.origin);
|
|
@@ -27511,13 +28648,15 @@ ${generateUniversalRouterStateProperties()}
|
|
|
27511
28648
|
const params = matched.params;
|
|
27512
28649
|
const searchParams = Object.fromEntries(targetUrl.searchParams);
|
|
27513
28650
|
const props = { params: params, searchParams: Promise.resolve(searchParams) };
|
|
27514
|
-
|
|
27515
|
-
if (!reactRoot) {
|
|
27516
|
-
reactRoot = createRoot(currentRoot);
|
|
27517
|
-
}
|
|
27518
28651
|
const pageElement = React.createElement(Component, props);
|
|
27519
|
-
const
|
|
27520
|
-
|
|
28652
|
+
const hasRouteSlots = nextRouteSlots.length > 0;
|
|
28653
|
+
const pageContainer =
|
|
28654
|
+
(hasRouteSlots ? document.getElementById("__farm_page__") : null) || currentRoot;
|
|
28655
|
+
const wrappedElement = hasRouteSlots
|
|
28656
|
+
? pageElement
|
|
28657
|
+
: wrapWithLayouts(pageElement, newPathname, params);
|
|
28658
|
+
reactRoot = hydrateRoot(pageContainer, wrappedElement);
|
|
28659
|
+
isHydrated = true;
|
|
27521
28660
|
}
|
|
27522
28661
|
return true;
|
|
27523
28662
|
},
|
|
@@ -27526,6 +28665,7 @@ ${generateUniversalRouterStateProperties()}
|
|
|
27526
28665
|
if (!doc.documentElement || !doc.body) return false;
|
|
27527
28666
|
|
|
27528
28667
|
resetReactRoot();
|
|
28668
|
+
resetRouteSlotRoots();
|
|
27529
28669
|
|
|
27530
28670
|
Array.from(document.documentElement.attributes).forEach(function(attr) {
|
|
27531
28671
|
if (!doc.documentElement.hasAttribute(attr.name)) {
|
|
@@ -27559,10 +28699,12 @@ ${generateUniversalRouterStateProperties()}
|
|
|
27559
28699
|
if (url.origin !== window.location.origin) return;
|
|
27560
28700
|
|
|
27561
28701
|
const pathname = url.pathname + url.search;
|
|
27562
|
-
|
|
28702
|
+
const interceptFrom = this.currentPath;
|
|
28703
|
+
const cacheKey = pathname + "\\nintercept:" + interceptFrom;
|
|
28704
|
+
if (this.prefetchCache.has(cacheKey)) return;
|
|
27563
28705
|
|
|
27564
|
-
this.fetchPage(pathname)
|
|
27565
|
-
.then(function(html) { spaRouter.prefetchCache.set(
|
|
28706
|
+
this.fetchPage(pathname, interceptFrom)
|
|
28707
|
+
.then(function(html) { spaRouter.prefetchCache.set(cacheKey, html); })
|
|
27566
28708
|
.catch(function() {});
|
|
27567
28709
|
},
|
|
27568
28710
|
|
|
@@ -27653,7 +28795,7 @@ if (isFarmDocsSearchPage()) {
|
|
|
27653
28795
|
`.trim();
|
|
27654
28796
|
}
|
|
27655
28797
|
__name(generateClientHydrationEntry, "generateClientHydrationEntry");
|
|
27656
|
-
async function buildSSRInMemory(productionVite, config, root, routeManager, apiRouteManager, serverRenderer, preset, collectedPageRoutes, collectedLayoutRoutes) {
|
|
28798
|
+
async function buildSSRInMemory(productionVite, config, root, routeManager, apiRouteManager, serverRenderer, preset, collectedPageRoutes, collectedLayoutRoutes, collectedRouteSlots) {
|
|
27657
28799
|
const viteBuild = productionVite.build;
|
|
27658
28800
|
const { farmPlugin: farmPlugin2 } = await Promise.resolve().then(() => (init_vite(), vite_exports));
|
|
27659
28801
|
const { PluginManager: PluginManager2 } = await Promise.resolve().then(() => (init_plugin(), plugin_exports));
|
|
@@ -27686,6 +28828,7 @@ async function buildSSRInMemory(productionVite, config, root, routeManager, apiR
|
|
|
27686
28828
|
let ssrEntryFile;
|
|
27687
28829
|
const pageRoutes = collectedPageRoutes.map((route) => ({ ...route }));
|
|
27688
28830
|
const layoutRoutes = collectedLayoutRoutes.map((route) => ({ ...route }));
|
|
28831
|
+
const routeSlots = collectedRouteSlots.map((slot) => ({ ...slot }));
|
|
27689
28832
|
const metadataImageRoutes = [];
|
|
27690
28833
|
for (const entry of routeManager.getMetadataImages().values()) {
|
|
27691
28834
|
if (entry.sourceType === "static") {
|
|
@@ -27786,6 +28929,7 @@ async function buildSSRInMemory(productionVite, config, root, routeManager, apiR
|
|
|
27786
28929
|
apiRoutes,
|
|
27787
28930
|
pageRoutes,
|
|
27788
28931
|
layoutRoutes,
|
|
28932
|
+
routeSlots,
|
|
27789
28933
|
errorRoutes,
|
|
27790
28934
|
metadataImageRoutes,
|
|
27791
28935
|
middlewareRoutes,
|
|
@@ -27956,10 +29100,14 @@ async function buildSSRInMemory(productionVite, config, root, routeManager, apiR
|
|
|
27956
29100
|
}
|
|
27957
29101
|
}
|
|
27958
29102
|
}
|
|
27959
|
-
return {
|
|
29103
|
+
return {
|
|
29104
|
+
bundle: ssrBundle,
|
|
29105
|
+
entryFile: ssrEntryFile,
|
|
29106
|
+
configuredHeaderRoutes
|
|
29107
|
+
};
|
|
27960
29108
|
}
|
|
27961
29109
|
__name(buildSSRInMemory, "buildSSRInMemory");
|
|
27962
|
-
function generateVirtualEntryCode(apiRoutes, pageRoutes, layoutRoutes, errorRoutes, metadataImageRoutes, middlewareRoutes, redirectRoutes, configuredRewriteRoutes, configuredHeaderRoutes, notFoundPath, config, configModulePath, hasServerRuntimeIntegrations, hasRuntimeIntegrationConfig, hasConfiguredRuntimePlugins, preset, i18nCatalogs) {
|
|
29110
|
+
function generateVirtualEntryCode(apiRoutes, pageRoutes, layoutRoutes, routeSlots, errorRoutes, metadataImageRoutes, middlewareRoutes, redirectRoutes, configuredRewriteRoutes, configuredHeaderRoutes, notFoundPath, config, configModulePath, hasServerRuntimeIntegrations, hasRuntimeIntegrationConfig, hasConfiguredRuntimePlugins, preset, i18nCatalogs) {
|
|
27963
29111
|
const hasPluginRuntime = hasRuntimeIntegrationConfig || hasConfiguredRuntimePlugins;
|
|
27964
29112
|
const hasMiddlewareRuntime = middlewareRoutes.length > 0 || hasFarmMiddlewareConfig(config.middleware);
|
|
27965
29113
|
const farmDocsBaseConfig = config.docs?.enabled ? { ...config.docs, config: void 0 } : void 0;
|
|
@@ -28003,10 +29151,12 @@ function generateVirtualEntryCode(apiRoutes, pageRoutes, layoutRoutes, errorRout
|
|
|
28003
29151
|
return;
|
|
28004
29152
|
}
|
|
28005
29153
|
pageImports.push(`import * as ${varName} from "${route.modulePath}";`);
|
|
29154
|
+
const shouldHydrate = getClientModuleMetadata(route.modulePath, config.root).shouldHydrate;
|
|
28006
29155
|
pageRegistrations.push(`
|
|
28007
29156
|
{
|
|
28008
29157
|
pattern: ${JSON.stringify(route.pattern)},
|
|
28009
29158
|
module: ${varName},
|
|
29159
|
+
shouldHydrate: ${JSON.stringify(shouldHydrate)},
|
|
28010
29160
|
}`);
|
|
28011
29161
|
});
|
|
28012
29162
|
const layoutImports = [];
|
|
@@ -28020,6 +29170,22 @@ function generateVirtualEntryCode(apiRoutes, pageRoutes, layoutRoutes, errorRout
|
|
|
28020
29170
|
module: ${varName},
|
|
28021
29171
|
}`);
|
|
28022
29172
|
});
|
|
29173
|
+
const routeSlotImports = [];
|
|
29174
|
+
const routeSlotRegistrations = [];
|
|
29175
|
+
routeSlots.forEach((slot, index) => {
|
|
29176
|
+
const varName = `routeSlot${index}`;
|
|
29177
|
+
routeSlotImports.push(`import * as ${varName} from "${slot.modulePath}";`);
|
|
29178
|
+
routeSlotRegistrations.push(`
|
|
29179
|
+
{
|
|
29180
|
+
name: ${JSON.stringify(slot.name)},
|
|
29181
|
+
ownerPattern: ${JSON.stringify(slot.ownerPattern)},
|
|
29182
|
+
pattern: ${JSON.stringify(slot.pattern)},
|
|
29183
|
+
containerId: ${JSON.stringify(slot.containerId)},
|
|
29184
|
+
interception: ${JSON.stringify(slot.interception)},
|
|
29185
|
+
fallback: ${JSON.stringify(slot.fallback)},
|
|
29186
|
+
module: ${varName},
|
|
29187
|
+
}`);
|
|
29188
|
+
});
|
|
28023
29189
|
const errorImports = [];
|
|
28024
29190
|
const errorRegistrations = [];
|
|
28025
29191
|
errorRoutes.forEach((errorRoute, index) => {
|
|
@@ -28070,6 +29236,7 @@ function generateVirtualEntryCode(apiRoutes, pageRoutes, layoutRoutes, errorRout
|
|
|
28070
29236
|
_runWithMiddlewareData,
|
|
28071
29237
|
addMetadataImageReference,
|
|
28072
29238
|
applyProductionMiddlewareHeaders,
|
|
29239
|
+
configureFarmCache,
|
|
28073
29240
|
configureFarmObservability,
|
|
28074
29241
|
createFarmCacheKey,
|
|
28075
29242
|
createFarmLocaleCookie,
|
|
@@ -28172,6 +29339,7 @@ async function handleAPIRequest(request) {
|
|
|
28172
29339
|
${apiImports.join("\n")}
|
|
28173
29340
|
${pageImports.join("\n")}
|
|
28174
29341
|
${layoutImports.join("\n")}
|
|
29342
|
+
${routeSlotImports.join("\n")}
|
|
28175
29343
|
${errorImports.join("\n")}
|
|
28176
29344
|
${metadataImageImports.join("\n")}
|
|
28177
29345
|
${middlewareImports.join("\n")}
|
|
@@ -28249,6 +29417,7 @@ const farmObservabilityConfig = farmUserConfig?.observability ?? ${JSON.stringif
|
|
|
28249
29417
|
config.observability ?? false
|
|
28250
29418
|
)};
|
|
28251
29419
|
configureFarmObservability(farmObservabilityConfig);
|
|
29420
|
+
configureFarmCache(farmResolvedRuntimeConfig.cache);
|
|
28252
29421
|
const farmI18nConfig = ${JSON.stringify(config.i18n)};
|
|
28253
29422
|
const farmI18nRuntime = ${config.i18n.enabled ? `createFarmI18nRuntime(farmI18nConfig, ${JSON.stringify(i18nCatalogs)})` : "null"};
|
|
28254
29423
|
${config.i18n.enabled ? "_setDefaultFarmI18nRuntime(farmI18nRuntime);" : ""}
|
|
@@ -28306,6 +29475,9 @@ for (const route of pageRoutes) {
|
|
|
28306
29475
|
const layoutRoutes = [${layoutRegistrations.join(",")}
|
|
28307
29476
|
];
|
|
28308
29477
|
|
|
29478
|
+
const routeSlots = [${routeSlotRegistrations.join(",")}
|
|
29479
|
+
];
|
|
29480
|
+
|
|
28309
29481
|
// Route-level error boundaries bundled at build time.
|
|
28310
29482
|
const errorRoutes = [${errorRegistrations.join(",")}
|
|
28311
29483
|
];
|
|
@@ -28340,18 +29512,21 @@ function serializeFarmInlineValue(value) {
|
|
|
28340
29512
|
.replace(/\\u2029/g, "\\\\u2029");
|
|
28341
29513
|
}
|
|
28342
29514
|
|
|
28343
|
-
function renderFarmClientBootstrapScript(canonicalPath) {
|
|
29515
|
+
function renderFarmClientBootstrapScript(canonicalPath, selectedRouteSlots) {
|
|
28344
29516
|
const integrationManifest = ${hasRuntimeIntegrationConfig ? "getRegisteredIntegrationAPIManifest()" : "{}"};
|
|
29517
|
+
const serializedRouteSlots = serializeFarmInlineValue(selectedRouteSlots || []);
|
|
28345
29518
|
let source = 'window.__FARM_INTEGRATION_API_MANIFEST__=' +
|
|
28346
29519
|
serializeFarmInlineValue(integrationManifest) +
|
|
28347
|
-
';';
|
|
29520
|
+
';window.__FARM_ROUTE_SLOTS__=' + serializedRouteSlots + ';';
|
|
28348
29521
|
if (typeof canonicalPath === "string" && canonicalPath.startsWith("/")) {
|
|
28349
29522
|
const serializedPath = serializeFarmInlineValue(canonicalPath);
|
|
28350
29523
|
source += 'if(location.pathname+location.search!==' + serializedPath + '){' +
|
|
28351
29524
|
'history.replaceState(Object.assign({},history.state||{},{path:' + serializedPath + '}),"",' +
|
|
28352
29525
|
serializedPath + ');}';
|
|
28353
29526
|
}
|
|
28354
|
-
return '<script>' +
|
|
29527
|
+
return '<script id="__farm_route_slots_data__" type="application/json">' +
|
|
29528
|
+
serializedRouteSlots +
|
|
29529
|
+
'</script><script>' + source + '</script>';
|
|
28355
29530
|
}
|
|
28356
29531
|
|
|
28357
29532
|
function escapeFarmHtmlAttribute(value) {
|
|
@@ -29101,6 +30276,79 @@ function matchPageRoute(pathname) {
|
|
|
29101
30276
|
return null;
|
|
29102
30277
|
}
|
|
29103
30278
|
|
|
30279
|
+
function matchesRoutePrefix(pathname, pattern) {
|
|
30280
|
+
if (pattern === "/") return true;
|
|
30281
|
+
const pathSegments = normalizeRuntimePath(pathname).split("/").filter(Boolean);
|
|
30282
|
+
const patternSegments = normalizeRuntimePath(pattern).split("/").filter(Boolean);
|
|
30283
|
+
if (patternSegments.length > pathSegments.length) return false;
|
|
30284
|
+
const candidate = "/" + pathSegments.slice(0, patternSegments.length).join("/");
|
|
30285
|
+
return matchRuntimePathPattern(pattern, candidate) !== null;
|
|
30286
|
+
}
|
|
30287
|
+
|
|
30288
|
+
function routeSlotSpecificity(slot) {
|
|
30289
|
+
return slot.pattern.split("/").reduce(function(score, segment) {
|
|
30290
|
+
if (!segment) return score;
|
|
30291
|
+
if (segment.startsWith("[[...")) return score + 1;
|
|
30292
|
+
if (segment.startsWith("[...")) return score + 2;
|
|
30293
|
+
if (segment.startsWith("[")) return score + 3;
|
|
30294
|
+
return score + 10;
|
|
30295
|
+
}, 0);
|
|
30296
|
+
}
|
|
30297
|
+
|
|
30298
|
+
function matchRouteSlots(pathname, interceptFrom) {
|
|
30299
|
+
const groups = new Map();
|
|
30300
|
+
for (const slot of routeSlots) {
|
|
30301
|
+
if (!matchesRoutePrefix(pathname, slot.ownerPattern)) continue;
|
|
30302
|
+
const key = slot.ownerPattern + ":" + slot.name;
|
|
30303
|
+
const entries = groups.get(key) || [];
|
|
30304
|
+
entries.push(slot);
|
|
30305
|
+
groups.set(key, entries);
|
|
30306
|
+
}
|
|
30307
|
+
|
|
30308
|
+
const normalizedFrom =
|
|
30309
|
+
typeof interceptFrom === "string" && interceptFrom.startsWith("/")
|
|
30310
|
+
? new URL(interceptFrom, "http://farm.local").pathname
|
|
30311
|
+
: null;
|
|
30312
|
+
const matches = [];
|
|
30313
|
+
|
|
30314
|
+
for (const entries of groups.values()) {
|
|
30315
|
+
const candidates = entries
|
|
30316
|
+
.filter(function(entry) { return !entry.fallback; })
|
|
30317
|
+
.filter(function(entry) {
|
|
30318
|
+
return !entry.interception ||
|
|
30319
|
+
(normalizedFrom && matchesRoutePrefix(normalizedFrom, entry.ownerPattern));
|
|
30320
|
+
})
|
|
30321
|
+
.map(function(entry) {
|
|
30322
|
+
return {
|
|
30323
|
+
entry: entry,
|
|
30324
|
+
params: matchRuntimePathPattern(entry.pattern, pathname),
|
|
30325
|
+
};
|
|
30326
|
+
})
|
|
30327
|
+
.filter(function(candidate) { return candidate.params !== null; })
|
|
30328
|
+
.sort(function(left, right) {
|
|
30329
|
+
if (left.entry.interception !== right.entry.interception) {
|
|
30330
|
+
return left.entry.interception ? -1 : 1;
|
|
30331
|
+
}
|
|
30332
|
+
return routeSlotSpecificity(right.entry) - routeSlotSpecificity(left.entry);
|
|
30333
|
+
});
|
|
30334
|
+
|
|
30335
|
+
if (candidates[0]) {
|
|
30336
|
+
matches.push({ ...candidates[0].entry, params: candidates[0].params });
|
|
30337
|
+
continue;
|
|
30338
|
+
}
|
|
30339
|
+
|
|
30340
|
+
const fallback = entries.find(function(entry) { return entry.fallback; });
|
|
30341
|
+
if (fallback) matches.push({ ...fallback, params: {} });
|
|
30342
|
+
}
|
|
30343
|
+
|
|
30344
|
+
return matches.sort(function(left, right) {
|
|
30345
|
+
const ownerDepth =
|
|
30346
|
+
left.ownerPattern.split("/").filter(Boolean).length -
|
|
30347
|
+
right.ownerPattern.split("/").filter(Boolean).length;
|
|
30348
|
+
return ownerDepth || left.name.localeCompare(right.name);
|
|
30349
|
+
});
|
|
30350
|
+
}
|
|
30351
|
+
|
|
29104
30352
|
function hasLocalRequestRoute(request, routePathname) {
|
|
29105
30353
|
const pathname = new URL(request.url).pathname;
|
|
29106
30354
|
if (matchLocalAPIRequest(request) || matchLocalIntegrationRequest(request)) {
|
|
@@ -29251,6 +30499,7 @@ function getPPRShellBypassReason(request, middlewareData, middlewareContext) {
|
|
|
29251
30499
|
if (method !== "GET" && method !== "HEAD") return "method";
|
|
29252
30500
|
if (request.headers.get("cookie")) return "cookie";
|
|
29253
30501
|
if (request.headers.get("authorization")) return "authorization";
|
|
30502
|
+
if (request.headers.get("x-farm-intercept-from")) return "route-interception";
|
|
29254
30503
|
if (request.headers.get("x-farm-ppr-refresh")) return "refresh";
|
|
29255
30504
|
if (hasConfiguredRouteContext) return "route-context";
|
|
29256
30505
|
if (middlewareData?.size) return "middleware-data";
|
|
@@ -29304,8 +30553,8 @@ function getRouteSharedCacheControl(routeModule, pprCanCache) {
|
|
|
29304
30553
|
return "public, s-maxage=" + revalidate + ", stale-while-revalidate=300";
|
|
29305
30554
|
}
|
|
29306
30555
|
|
|
29307
|
-
function getCachedPPRShell(cacheKey) {
|
|
29308
|
-
const entry = pprShellCache.
|
|
30556
|
+
async function getCachedPPRShell(cacheKey) {
|
|
30557
|
+
const entry = await pprShellCache.getEntryAsync(cacheKey);
|
|
29309
30558
|
if (!entry) {
|
|
29310
30559
|
return null;
|
|
29311
30560
|
}
|
|
@@ -29520,7 +30769,7 @@ async function handleFarmRequestInContext(
|
|
|
29520
30769
|
}
|
|
29521
30770
|
}
|
|
29522
30771
|
if (pprCacheKey) {
|
|
29523
|
-
const cachedPPRShell = getCachedPPRShell(pprCacheKey);
|
|
30772
|
+
const cachedPPRShell = await getCachedPPRShell(pprCacheKey);
|
|
29524
30773
|
if (cachedPPRShell) {
|
|
29525
30774
|
emitFarmEvent({ type: "ppr.shell.hit", route: pathname, key: pprCacheKey });
|
|
29526
30775
|
emitFarmEvent({
|
|
@@ -29548,6 +30797,10 @@ async function handleFarmRequestInContext(
|
|
|
29548
30797
|
|
|
29549
30798
|
// Get applicable layouts for this page
|
|
29550
30799
|
const applicableLayouts = getApplicableLayouts(routePathname);
|
|
30800
|
+
const selectedRouteSlots = matchRouteSlots(
|
|
30801
|
+
routePathname,
|
|
30802
|
+
request.headers.get("x-farm-intercept-from"),
|
|
30803
|
+
);
|
|
29551
30804
|
|
|
29552
30805
|
if (PageComponent) {
|
|
29553
30806
|
// Parse search params - make it a resolved Promise for async components
|
|
@@ -29635,6 +30888,82 @@ async function handleFarmRequestInContext(
|
|
|
29635
30888
|
throw routeStateError;
|
|
29636
30889
|
}
|
|
29637
30890
|
}
|
|
30891
|
+
|
|
30892
|
+
const renderedRouteSlots = await Promise.all(
|
|
30893
|
+
selectedRouteSlots.map(async function(slot) {
|
|
30894
|
+
const slotContext = hasConfiguredRouteContext
|
|
30895
|
+
? await resolveFarmRouteContext(farmResolvedRuntimeConfig, {
|
|
30896
|
+
request,
|
|
30897
|
+
params: slot.params,
|
|
30898
|
+
search: searchParamsObj,
|
|
30899
|
+
path: pathname,
|
|
30900
|
+
})
|
|
30901
|
+
: undefined;
|
|
30902
|
+
const rawSlotProps = withFarmRouteContext(
|
|
30903
|
+
{
|
|
30904
|
+
params: slot.params,
|
|
30905
|
+
searchParams: Promise.resolve(searchParamsObj),
|
|
30906
|
+
path: pathname,
|
|
30907
|
+
...(middlewareData?.size ? { middleware: { data: middlewareData } } : {}),
|
|
30908
|
+
},
|
|
30909
|
+
slotContext,
|
|
30910
|
+
);
|
|
30911
|
+
if (slot.module.__farmResolveRouteProps && slotContext !== undefined) {
|
|
30912
|
+
Object.defineProperty(rawSlotProps, "context", {
|
|
30913
|
+
value: slotContext,
|
|
30914
|
+
enumerable: true,
|
|
30915
|
+
configurable: true,
|
|
30916
|
+
});
|
|
30917
|
+
}
|
|
30918
|
+
|
|
30919
|
+
const slotSchemas = slot.module.__farmRouteParsesProps
|
|
30920
|
+
? {}
|
|
30921
|
+
: slot.module.__farmRouteSchemas || {};
|
|
30922
|
+
let slotProps = slot.module.__farmResolveRouteProps
|
|
30923
|
+
? await slot.module.__farmResolveRouteProps(rawSlotProps)
|
|
30924
|
+
: {
|
|
30925
|
+
...rawSlotProps,
|
|
30926
|
+
params: slotSchemas.params?.parse
|
|
30927
|
+
? slotSchemas.params.parse(slot.params)
|
|
30928
|
+
: slot.params,
|
|
30929
|
+
search: slotSchemas.search?.parse
|
|
30930
|
+
? slotSchemas.search.parse(searchParamsObj)
|
|
30931
|
+
: searchParamsObj,
|
|
30932
|
+
searchParams: Promise.resolve(
|
|
30933
|
+
slotSchemas.search?.parse
|
|
30934
|
+
? slotSchemas.search.parse(searchParamsObj)
|
|
30935
|
+
: searchParamsObj,
|
|
30936
|
+
),
|
|
30937
|
+
};
|
|
30938
|
+
if (
|
|
30939
|
+
slot.module.__farmResolveRouteProps &&
|
|
30940
|
+
slotContext !== undefined &&
|
|
30941
|
+
slotProps.context === slotContext
|
|
30942
|
+
) {
|
|
30943
|
+
const { context: _slotContext, ...renderSlotProps } = slotProps;
|
|
30944
|
+
slotProps = renderSlotProps;
|
|
30945
|
+
}
|
|
30946
|
+
|
|
30947
|
+
return { ...slot, props: slotProps };
|
|
30948
|
+
}),
|
|
30949
|
+
);
|
|
30950
|
+
const routeSlotPayload = renderedRouteSlots.map(function(slot) {
|
|
30951
|
+
return {
|
|
30952
|
+
name: slot.name,
|
|
30953
|
+
ownerPattern: slot.ownerPattern,
|
|
30954
|
+
pattern: slot.pattern,
|
|
30955
|
+
containerId: slot.containerId,
|
|
30956
|
+
interception: slot.interception,
|
|
30957
|
+
fallback: slot.fallback,
|
|
30958
|
+
props: {
|
|
30959
|
+
params: slot.props.params,
|
|
30960
|
+
search: slot.props.search || searchParamsObj,
|
|
30961
|
+
searchParams: slot.props.search || searchParamsObj,
|
|
30962
|
+
...("data" in slot.props ? { data: slot.props.data } : {}),
|
|
30963
|
+
path: pathname,
|
|
30964
|
+
},
|
|
30965
|
+
};
|
|
30966
|
+
});
|
|
29638
30967
|
|
|
29639
30968
|
const renderPageElement = async () => {
|
|
29640
30969
|
// First, render the page content
|
|
@@ -29662,6 +30991,14 @@ async function handleFarmRequestInContext(
|
|
|
29662
30991
|
pageElement = React.createElement(PageComponent, pageProps);
|
|
29663
30992
|
}
|
|
29664
30993
|
|
|
30994
|
+
if (route.shouldHydrate && renderedRouteSlots.length > 0) {
|
|
30995
|
+
pageElement = React.createElement(
|
|
30996
|
+
"div",
|
|
30997
|
+
{ id: "__farm_page__", "data-farm-client": "true" },
|
|
30998
|
+
pageElement,
|
|
30999
|
+
);
|
|
31000
|
+
}
|
|
31001
|
+
|
|
29665
31002
|
// Wrap with layouts (from innermost to outermost)
|
|
29666
31003
|
// Layouts are sorted by depth (root first), so we process in reverse
|
|
29667
31004
|
let wrappedElement = pageElement;
|
|
@@ -29669,9 +31006,23 @@ async function handleFarmRequestInContext(
|
|
|
29669
31006
|
const layout = applicableLayouts[i];
|
|
29670
31007
|
const LayoutComponent = layout.module.default;
|
|
29671
31008
|
if (LayoutComponent) {
|
|
31009
|
+
const slotProps = {};
|
|
31010
|
+
for (const slot of renderedRouteSlots) {
|
|
31011
|
+
if (slot.ownerPattern !== layout.pattern) continue;
|
|
31012
|
+
slotProps[slot.name] = React.createElement(
|
|
31013
|
+
"div",
|
|
31014
|
+
{
|
|
31015
|
+
id: slot.containerId,
|
|
31016
|
+
"data-farm-route-slot": slot.name,
|
|
31017
|
+
"data-farm-slot-owner": slot.ownerPattern,
|
|
31018
|
+
},
|
|
31019
|
+
React.createElement(slot.module.default, slot.props),
|
|
31020
|
+
);
|
|
31021
|
+
}
|
|
29672
31022
|
wrappedElement = React.createElement(LayoutComponent, {
|
|
29673
31023
|
children: wrappedElement,
|
|
29674
31024
|
params,
|
|
31025
|
+
...slotProps,
|
|
29675
31026
|
});
|
|
29676
31027
|
}
|
|
29677
31028
|
}
|
|
@@ -29723,6 +31074,7 @@ async function handleFarmRequestInContext(
|
|
|
29723
31074
|
);
|
|
29724
31075
|
const hasRequestScopedRender = Boolean(
|
|
29725
31076
|
pageStatus >= 400 ||
|
|
31077
|
+
request.headers.get("x-farm-intercept-from") ||
|
|
29726
31078
|
hasPrivateRequestHeaders ||
|
|
29727
31079
|
hasConfiguredRouteContext ||
|
|
29728
31080
|
middlewareData?.size ||
|
|
@@ -29762,7 +31114,10 @@ async function handleFarmRequestInContext(
|
|
|
29762
31114
|
' <link rel="modulepreload" href="/farm-client.js">\\n' +
|
|
29763
31115
|
'</head>\\n<body>\\n <div id="root">';
|
|
29764
31116
|
const streamSuffix = '</div>\\n' +
|
|
29765
|
-
' ' + renderFarmClientBootstrapScript(
|
|
31117
|
+
' ' + renderFarmClientBootstrapScript(
|
|
31118
|
+
pageProps.__farmCanonicalPath,
|
|
31119
|
+
routeSlotPayload
|
|
31120
|
+
) + '\\n' +
|
|
29766
31121
|
' <script type="module" src="/farm-client.js"></script>\\n' +
|
|
29767
31122
|
'</body>\\n</html>';
|
|
29768
31123
|
const streamedDocument = createFarmDocumentStream(
|
|
@@ -29819,7 +31174,10 @@ async function handleFarmRequestInContext(
|
|
|
29819
31174
|
// Inject client script before closing body tag
|
|
29820
31175
|
.replace(
|
|
29821
31176
|
/<\\/body>/i,
|
|
29822
|
-
' ' + renderFarmClientBootstrapScript(
|
|
31177
|
+
' ' + renderFarmClientBootstrapScript(
|
|
31178
|
+
pageProps.__farmCanonicalPath,
|
|
31179
|
+
routeSlotPayload
|
|
31180
|
+
) + '\\n' +
|
|
29823
31181
|
' <script type="module" src="/farm-client.js"></script>\\n</body>',
|
|
29824
31182
|
);
|
|
29825
31183
|
|
|
@@ -29840,7 +31198,7 @@ async function handleFarmRequestInContext(
|
|
|
29840
31198
|
</head>
|
|
29841
31199
|
<body>
|
|
29842
31200
|
<div id="root">\${html}</div>
|
|
29843
|
-
\${renderFarmClientBootstrapScript(pageProps.__farmCanonicalPath)}
|
|
31201
|
+
\${renderFarmClientBootstrapScript(pageProps.__farmCanonicalPath, routeSlotPayload)}
|
|
29844
31202
|
<script type="module" src="/farm-client.js"></script>
|
|
29845
31203
|
</body>
|
|
29846
31204
|
</html>\`;
|
|
@@ -29848,7 +31206,7 @@ async function handleFarmRequestInContext(
|
|
|
29848
31206
|
fullHtml = applyFarmI18nDocument(fullHtml, pathname, farmI18nSnapshot);
|
|
29849
31207
|
|
|
29850
31208
|
if (pageStatus === 200 && pprCacheKey && request.method.toUpperCase() !== "HEAD") {
|
|
29851
|
-
pprShellCache.
|
|
31209
|
+
await pprShellCache.setAsync(
|
|
29852
31210
|
pprCacheKey,
|
|
29853
31211
|
{ html: fullHtml },
|
|
29854
31212
|
{
|
|
@@ -31041,6 +32399,13 @@ function mergeFarmViteConfig(farmConfig, userConfig = {}) {
|
|
|
31041
32399
|
...farmConfig.server,
|
|
31042
32400
|
...userConfig.server
|
|
31043
32401
|
},
|
|
32402
|
+
resolve: {
|
|
32403
|
+
...farmConfig.resolve,
|
|
32404
|
+
...userConfig.resolve,
|
|
32405
|
+
dedupe: Array.from(
|
|
32406
|
+
/* @__PURE__ */ new Set([...farmConfig.resolve?.dedupe || [], ...userConfig.resolve?.dedupe || []])
|
|
32407
|
+
)
|
|
32408
|
+
},
|
|
31044
32409
|
optimizeDeps: {
|
|
31045
32410
|
...farmConfig.optimizeDeps,
|
|
31046
32411
|
...userConfig.optimizeDeps,
|