@uniweb/runtime 0.14.2 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ssr.js +160 -169
- package/dist/ssr.js.map +1 -1
- package/package.json +3 -3
- package/src/components/BlockRenderer.jsx +7 -0
- package/src/default-fetcher.js +234 -378
- package/src/isolate-api.js +86 -0
- package/src/prefetch.js +63 -17
- package/src/prepare-props.js +1 -1
- package/src/setup.js +7 -5
- package/src/wire-foundation.js +3 -1
package/dist/ssr.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { isRichSchema, deriveCacheKey, resolveDefaultLocale, hasDarkScheme, createUniweb,
|
|
1
|
+
import { isRichSchema, deriveCacheKey, resolveDefaultLocale, hasDarkScheme, createUniweb, resolveServiceUrl, substitutePlaceholders, matchWhere, sortRecords, sortToWire } from "@uniweb/core";
|
|
2
2
|
import React from "react";
|
|
3
3
|
import { renderToString } from "react-dom/server";
|
|
4
4
|
import { sectionDomId } from "@uniweb/core/section-id";
|
|
5
|
-
import { routePatternToRegex } from "@uniweb/core/route-match";
|
|
5
|
+
import { routePatternToRegex, decodeRouteValue, splitPathCapture } from "@uniweb/core/route-match";
|
|
6
6
|
import { DEFAULT_ICON_BASE, iconUrl } from "@uniweb/core/icon-corpus";
|
|
7
7
|
import { buildTheme, FONT_LINKS_MARKER, buildSectionOverrides } from "@uniweb/theming";
|
|
8
8
|
import "@uniweb/core/services";
|
|
9
9
|
import "@uniweb/core/tracker";
|
|
10
10
|
import { resolveFetchConfigs } from "@uniweb/core/fetch-config";
|
|
11
11
|
import { deriveCacheKey as deriveCacheKey$1 } from "@uniweb/core/datastore";
|
|
12
|
+
import { buildDetailConfig } from "@uniweb/core/detail-url";
|
|
12
13
|
import { resolveDefaultLocale as resolveDefaultLocale$1 } from "@uniweb/core/locale-config";
|
|
13
14
|
function guaranteeItemStructure(item) {
|
|
14
15
|
return {
|
|
@@ -285,7 +286,7 @@ function hydrateDataStore(website, fetchedData) {
|
|
|
285
286
|
if (!website?.dataStore || !fetchedData?.length) return;
|
|
286
287
|
for (const entry of fetchedData) {
|
|
287
288
|
if (entry.outcome && entry.outcome !== "fetched") continue;
|
|
288
|
-
website.dataStore.set(deriveCacheKey(entry.config), { data: entry.data });
|
|
289
|
+
website.dataStore.set(deriveCacheKey(entry.config), entry.meta ? { data: entry.data, meta: entry.meta } : { data: entry.data });
|
|
289
290
|
}
|
|
290
291
|
}
|
|
291
292
|
function ensureThemeCss(uniweb, foundation) {
|
|
@@ -838,48 +839,40 @@ function generate404Html({ baseHtml, website, siteContent }) {
|
|
|
838
839
|
}
|
|
839
840
|
return { html, hasNotFoundPage: !!notFoundPage };
|
|
840
841
|
}
|
|
841
|
-
|
|
842
|
-
function createDefaultFetcher({ basePath = "", config = {}, dev = false, records = null, fetch: fetchImpl = null } = {}) {
|
|
842
|
+
function createDefaultFetcher({ basePath = "", dev = false, records = null, fetch: fetchImpl = null } = {}) {
|
|
843
843
|
const doFetch = (input, init) => (fetchImpl || globalThis.fetch)(input, init);
|
|
844
844
|
const pathPrefix = basePath && basePath !== "/" ? basePath.replace(/\/$/, "") : "";
|
|
845
|
-
const baseUrl = typeof config?.baseUrl === "string" ? config.baseUrl.replace(/\/$/, "") : "";
|
|
846
|
-
const staticHeaders = buildStaticHeaders(config?.headers);
|
|
847
|
-
const supports = normalizeSupports(config?.supports);
|
|
848
|
-
const requestConfig = config?.request && typeof config.request === "object" ? config.request : {};
|
|
849
|
-
const styleName = typeof requestConfig.style === "string" ? requestConfig.style : null;
|
|
850
|
-
const style = resolveRequestStyle(styleName, { dev });
|
|
851
|
-
const rename = normalizeRename(requestConfig.rename, style, { dev });
|
|
852
|
-
const siteEnvelope = config?.envelope && typeof config.envelope === "object" ? config.envelope : null;
|
|
853
|
-
const envelope = { ...style.defaultEnvelope || {}, ...siteEnvelope || {} };
|
|
854
845
|
const stampedArrayKey = records?.envelope && typeof records.envelope === "object" && typeof records.envelope.records === "string" && records.envelope.records.length ? records.envelope.records : null;
|
|
855
846
|
const laneEnvelope = stampedArrayKey ? { list: stampedArrayKey } : null;
|
|
847
|
+
const doorQueues = /* @__PURE__ */ new Map();
|
|
848
|
+
const askDoor = (request, ctx) => new Promise((resolve) => {
|
|
849
|
+
const url = resolveServiceUrl(request.door, pathPrefix);
|
|
850
|
+
let queue = doorQueues.get(url);
|
|
851
|
+
if (!queue) {
|
|
852
|
+
queue = [];
|
|
853
|
+
doorQueues.set(url, queue);
|
|
854
|
+
queueMicrotask(() => {
|
|
855
|
+
doorQueues.delete(url);
|
|
856
|
+
flushDoor(url, queue, doFetch);
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
queue.push({ request, ctx, resolve });
|
|
860
|
+
});
|
|
856
861
|
return {
|
|
857
862
|
/**
|
|
858
|
-
*
|
|
859
|
-
* the
|
|
860
|
-
*
|
|
861
|
-
*
|
|
862
|
-
*
|
|
863
|
-
*
|
|
864
|
-
* `where:` clauses against the same path share one cache entry —
|
|
865
|
-
* the file is fetched once and each page filters its own copy. With
|
|
866
|
-
* `supports: [where]`, the same two pages fire two requests because
|
|
867
|
-
* the predicate travels in the request.
|
|
863
|
+
* The cache identity is the request's ADDRESS — or, on a question door,
|
|
864
|
+
* the QUESTION (`deriveCacheKey` hashes every operator of an address-less
|
|
865
|
+
* request). Operators evaluated here run over a shared cached value and
|
|
866
|
+
* must NOT split the cache: two pages declaring different `where:` clauses
|
|
867
|
+
* against the same path share one entry — the file is fetched once and
|
|
868
|
+
* each page filters its own copy.
|
|
868
869
|
*/
|
|
869
870
|
cacheKey(request) {
|
|
870
|
-
|
|
871
|
-
const projected = {};
|
|
872
|
-
for (const op of supports) {
|
|
873
|
-
if (!style.canPush.has(op)) continue;
|
|
874
|
-
if (request[op] !== void 0) projected[op] = request[op];
|
|
875
|
-
}
|
|
876
|
-
if (Object.keys(projected).length === 0 && style.name === "json-body") {
|
|
877
|
-
return base;
|
|
878
|
-
}
|
|
879
|
-
return base + "::style=" + style.name + "::" + JSON.stringify(projected);
|
|
871
|
+
return deriveCacheKey(request);
|
|
880
872
|
},
|
|
881
873
|
async resolve(request, ctx = {}) {
|
|
882
874
|
if (!request) return { data: null };
|
|
875
|
+
if (request.door) return askDoor(request, ctx);
|
|
883
876
|
const { path, url, endpoint, transform, body: rawBody } = request;
|
|
884
877
|
let method = (request.method || "GET").toUpperCase();
|
|
885
878
|
if (method !== "GET" && method !== "POST") {
|
|
@@ -887,59 +880,39 @@ function createDefaultFetcher({ basePath = "", config = {}, dev = false, records
|
|
|
887
880
|
method = "GET";
|
|
888
881
|
}
|
|
889
882
|
let target;
|
|
890
|
-
let isRemote;
|
|
891
883
|
if (endpoint) {
|
|
892
884
|
target = resolveServiceUrl(endpoint, pathPrefix);
|
|
893
|
-
|
|
885
|
+
if (typeof request.locale === "string" && request.locale) {
|
|
886
|
+
target += (target.includes("?") ? "&" : "?") + "locale=" + encodeURIComponent(request.locale);
|
|
887
|
+
}
|
|
894
888
|
} else if (path) {
|
|
895
889
|
target = pathPrefix && path.startsWith("/") && !path.startsWith("//") ? pathPrefix + path : path;
|
|
896
|
-
isRemote = false;
|
|
897
890
|
} else if (url) {
|
|
898
|
-
target =
|
|
899
|
-
isRemote = true;
|
|
891
|
+
target = url;
|
|
900
892
|
} else {
|
|
901
893
|
return { data: [], error: "No path, url or endpoint specified" };
|
|
902
894
|
}
|
|
903
895
|
const init = { signal: ctx.signal, method };
|
|
904
|
-
const headers = {};
|
|
905
|
-
if (isRemote && staticHeaders) Object.assign(headers, staticHeaders);
|
|
906
|
-
const pushCandidates = /* @__PURE__ */ new Set();
|
|
907
|
-
if (isRemote) {
|
|
908
|
-
for (const op of KNOWN_OPERATORS) {
|
|
909
|
-
if (supports.has(op) && style.canPush.has(op) && request[op] !== void 0 && request[op] !== null) {
|
|
910
|
-
pushCandidates.add(op);
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
const encoded = pushCandidates.size > 0 ? style.encode(request, { method, pushCandidates, rename }) : { queryParams: [], bodyMerge: null, pushed: /* @__PURE__ */ new Set() };
|
|
915
|
-
const pushedOperators = encoded.pushed;
|
|
916
|
-
if (encoded.queryParams.length > 0 && method === "GET") {
|
|
917
|
-
target = appendStyleQueryParams(target, encoded.queryParams);
|
|
918
|
-
}
|
|
919
896
|
if (method === "POST") {
|
|
920
897
|
const dc = request.dynamicContext;
|
|
921
|
-
const
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
headers["Content-Type"] = "application/json";
|
|
926
|
-
}
|
|
927
|
-
init.body = typeof finalBody === "string" ? finalBody : JSON.stringify(finalBody);
|
|
898
|
+
const body = rawBody !== void 0 && rawBody !== null && dc && dc.paramName ? substitutePlaceholders(rawBody, { [dc.paramName]: dc.paramValue }, { encode: false }) : rawBody;
|
|
899
|
+
if (body !== void 0 && body !== null) {
|
|
900
|
+
init.headers = { "Content-Type": "application/json" };
|
|
901
|
+
init.body = typeof body === "string" ? body : JSON.stringify(body);
|
|
928
902
|
}
|
|
929
903
|
}
|
|
930
|
-
if (Object.keys(headers).length) init.headers = headers;
|
|
931
904
|
try {
|
|
932
905
|
const response = await doFetch(target, init);
|
|
933
906
|
const requestEnvelope = request.envelope && typeof request.envelope === "object" ? request.envelope : null;
|
|
934
|
-
const
|
|
907
|
+
const envelope = requestEnvelope ?? (endpoint && laneEnvelope ? laneEnvelope : {});
|
|
935
908
|
if (!response.ok) {
|
|
936
909
|
let extracted;
|
|
937
|
-
if (
|
|
910
|
+
if (envelope.error) {
|
|
938
911
|
try {
|
|
939
912
|
const text = await response.text();
|
|
940
913
|
const body = safeParseJSON(text);
|
|
941
914
|
if (body !== void 0) {
|
|
942
|
-
const candidate = getNestedValue(body,
|
|
915
|
+
const candidate = getNestedValue(body, envelope.error);
|
|
943
916
|
if (typeof candidate === "string" && candidate.length) {
|
|
944
917
|
extracted = candidate;
|
|
945
918
|
}
|
|
@@ -965,12 +938,13 @@ function createDefaultFetcher({ basePath = "", config = {}, dev = false, records
|
|
|
965
938
|
}
|
|
966
939
|
}
|
|
967
940
|
const isDetailRequest = !!request.dynamicContext;
|
|
968
|
-
const effectiveTransform = transform || (isDetailRequest ?
|
|
941
|
+
const effectiveTransform = transform || (isDetailRequest ? envelope.item : envelope.list);
|
|
969
942
|
if (effectiveTransform && data !== null && data !== void 0) {
|
|
970
943
|
data = getNestedValue(data, effectiveTransform);
|
|
971
944
|
}
|
|
972
|
-
data =
|
|
973
|
-
|
|
945
|
+
data = applyOperators(data, request, { dev });
|
|
946
|
+
const depth = request.depth === "brief" || request.depth === "full" ? request.depth : void 0;
|
|
947
|
+
return depth ? { data: data ?? [], meta: { depth } } : { data: data ?? [] };
|
|
974
948
|
} catch (error) {
|
|
975
949
|
if (error?.name === "AbortError") {
|
|
976
950
|
return { data: [], error: "aborted" };
|
|
@@ -980,105 +954,100 @@ function createDefaultFetcher({ basePath = "", config = {}, dev = false, records
|
|
|
980
954
|
}
|
|
981
955
|
};
|
|
982
956
|
}
|
|
983
|
-
function
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
957
|
+
function doorQuestion(request) {
|
|
958
|
+
const q = { schema: request.schema };
|
|
959
|
+
let where = request.where && typeof request.where === "object" ? request.where : null;
|
|
960
|
+
let scope = typeof request.scope === "string" && request.scope ? request.scope : null;
|
|
961
|
+
if (where && !scope && where.path && typeof where.path === "object" && typeof where.path.under === "string" && where.path.under) {
|
|
962
|
+
const { path, ...rest } = where;
|
|
963
|
+
scope = path.under;
|
|
964
|
+
where = Object.keys(rest).length ? rest : null;
|
|
965
|
+
}
|
|
966
|
+
if (scope) q.scope = scope;
|
|
967
|
+
if (where) q.where = renameOperators(where);
|
|
968
|
+
const sort = sortToWire(request.sort);
|
|
969
|
+
if (sort) q.sort = sort;
|
|
970
|
+
if (typeof request.limit === "number" && request.limit > 0) q.limit = request.limit;
|
|
971
|
+
if (request.depth === "brief" || request.depth === "full") q.depth = request.depth;
|
|
972
|
+
return q;
|
|
997
973
|
}
|
|
998
|
-
const
|
|
999
|
-
function
|
|
1000
|
-
|
|
1001
|
-
if (!
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
else if (!warnedUnknownOperators.has(op)) {
|
|
1006
|
-
warnedUnknownOperators.add(op);
|
|
1007
|
-
console.warn(`[default-fetcher] supports: unknown operator "${op}" — ignored.`);
|
|
1008
|
-
}
|
|
974
|
+
const DOOR_OPERATOR = { nin: "not_in" };
|
|
975
|
+
function renameOperators(where) {
|
|
976
|
+
if (Array.isArray(where)) return where.map(renameOperators);
|
|
977
|
+
if (!where || typeof where !== "object") return where;
|
|
978
|
+
const out = {};
|
|
979
|
+
for (const [key, value] of Object.entries(where)) {
|
|
980
|
+
out[DOOR_OPERATOR[key] ?? key] = value && typeof value === "object" ? renameOperators(value) : value;
|
|
1009
981
|
}
|
|
1010
982
|
return out;
|
|
1011
983
|
}
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
const
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
984
|
+
async function flushDoor(url, queue, doFetch) {
|
|
985
|
+
const body = {};
|
|
986
|
+
const keys = [];
|
|
987
|
+
for (const entry of queue) {
|
|
988
|
+
const base = entry.request.as || "q";
|
|
989
|
+
let key = base;
|
|
990
|
+
for (let n = 2; key in body; n += 1) key = `${base}#${n}`;
|
|
991
|
+
keys.push(key);
|
|
992
|
+
body[key] = doorQuestion(entry.request);
|
|
993
|
+
}
|
|
994
|
+
let parsed;
|
|
995
|
+
try {
|
|
996
|
+
const response = await doFetch(url, {
|
|
997
|
+
method: "POST",
|
|
998
|
+
headers: { "Content-Type": "application/json" },
|
|
999
|
+
body: JSON.stringify(body)
|
|
1000
|
+
});
|
|
1001
|
+
if (!response.ok) {
|
|
1002
|
+
const error = `HTTP ${response.status}: ${response.statusText}`;
|
|
1003
|
+
for (const entry of queue) entry.resolve({ data: null, error });
|
|
1004
|
+
return;
|
|
1005
|
+
}
|
|
1006
|
+
parsed = await response.json();
|
|
1007
|
+
} catch (error) {
|
|
1008
|
+
const message = error?.name === "AbortError" ? "aborted" : error?.message || String(error);
|
|
1009
|
+
for (const entry of queue) entry.resolve({ data: null, error: message });
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
const data = parsed && typeof parsed.data === "object" && parsed.data ? parsed.data : {};
|
|
1013
|
+
const errors = parsed && typeof parsed.errors === "object" && parsed.errors ? parsed.errors : {};
|
|
1014
|
+
const depths = parsed && typeof parsed.depths === "object" && parsed.depths ? parsed.depths : {};
|
|
1015
|
+
queue.forEach((entry, i) => {
|
|
1016
|
+
const key = keys[i];
|
|
1017
|
+
if (key in errors) {
|
|
1018
|
+
const e = errors[key];
|
|
1019
|
+
entry.resolve({ data: null, error: typeof e === "string" ? e : e?.message || JSON.stringify(e) });
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
1022
|
+
if (!(key in data)) {
|
|
1023
|
+
entry.resolve({ data: null, error: `the records door answered without the key "${key}"` });
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1026
|
+
const depth = depths[key] === "brief" || depths[key] === "full" ? depths[key] : entry.request.depth === "brief" || entry.request.depth === "full" ? entry.request.depth : void 0;
|
|
1027
|
+
entry.resolve(depth ? { data: data[key], meta: { depth } } : { data: data[key] });
|
|
1028
|
+
});
|
|
1030
1029
|
}
|
|
1031
|
-
function
|
|
1030
|
+
function applyOperators(data, request, { dev = false } = {}) {
|
|
1032
1031
|
if (!Array.isArray(data)) return data;
|
|
1033
1032
|
let result = data;
|
|
1034
|
-
if (request.where
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
if (request.sort && !pushedOperators.has("sort")) {
|
|
1038
|
-
result = applySortFallback(result, request.sort);
|
|
1039
|
-
}
|
|
1040
|
-
if (typeof request.limit === "number" && request.limit > 0 && !pushedOperators.has("limit")) {
|
|
1041
|
-
result = result.slice(0, request.limit);
|
|
1042
|
-
}
|
|
1033
|
+
if (request.where) result = matchWhere(request.where, result);
|
|
1034
|
+
if (request.sort) result = applySort(result, request.sort, dev);
|
|
1035
|
+
if (typeof request.limit === "number" && request.limit > 0) result = result.slice(0, request.limit);
|
|
1043
1036
|
return result;
|
|
1044
1037
|
}
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
return
|
|
1049
|
-
})
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
if (av > bv) return desc ? -1 : 1;
|
|
1038
|
+
const warnedBadSorts = /* @__PURE__ */ new Set();
|
|
1039
|
+
function applySort(items, sortExpr, dev) {
|
|
1040
|
+
try {
|
|
1041
|
+
return sortRecords(items, sortExpr);
|
|
1042
|
+
} catch (err) {
|
|
1043
|
+
if (dev) throw err;
|
|
1044
|
+
const key = String(sortExpr);
|
|
1045
|
+
if (!warnedBadSorts.has(key)) {
|
|
1046
|
+
warnedBadSorts.add(key);
|
|
1047
|
+
console.error(`[default-fetcher] ${err.message} Records delivered unsorted.`);
|
|
1056
1048
|
}
|
|
1057
|
-
return
|
|
1058
|
-
});
|
|
1059
|
-
}
|
|
1060
|
-
function buildStaticHeaders(headers) {
|
|
1061
|
-
if (!headers || typeof headers !== "object" || Array.isArray(headers)) return null;
|
|
1062
|
-
const out = {};
|
|
1063
|
-
for (const [k, v] of Object.entries(headers)) {
|
|
1064
|
-
if (v === null || v === void 0) continue;
|
|
1065
|
-
out[k] = String(v);
|
|
1049
|
+
return items;
|
|
1066
1050
|
}
|
|
1067
|
-
return Object.keys(out).length ? out : null;
|
|
1068
|
-
}
|
|
1069
|
-
function hasHeader(headers, name) {
|
|
1070
|
-
const lower = name.toLowerCase();
|
|
1071
|
-
return Object.keys(headers).some((k) => k.toLowerCase() === lower);
|
|
1072
|
-
}
|
|
1073
|
-
function isAbsoluteUrl(url) {
|
|
1074
|
-
if (typeof url !== "string") return false;
|
|
1075
|
-
if (url.startsWith("//")) return true;
|
|
1076
|
-
return /^[a-z][a-z0-9+.-]*:\/\//i.test(url);
|
|
1077
|
-
}
|
|
1078
|
-
function joinUrl(baseUrl, url) {
|
|
1079
|
-
if (!baseUrl) return url;
|
|
1080
|
-
if (url.startsWith("/")) return baseUrl + url;
|
|
1081
|
-
return baseUrl + "/" + url;
|
|
1082
1051
|
}
|
|
1083
1052
|
function getNestedValue(obj, path) {
|
|
1084
1053
|
if (!obj || !path) return obj;
|
|
@@ -1105,20 +1074,39 @@ function findPageForRoute(content, route) {
|
|
|
1105
1074
|
if (!page.isDynamic || !page.route) continue;
|
|
1106
1075
|
const compiled = routePatternToRegex(page.route);
|
|
1107
1076
|
const m = compiled?.regex ? compiled.regex.exec(route) : null;
|
|
1108
|
-
if (m)
|
|
1077
|
+
if (m) {
|
|
1078
|
+
const params = {};
|
|
1079
|
+
(compiled.paramNames || []).forEach((n, i) => {
|
|
1080
|
+
const raw = m[i + 1];
|
|
1081
|
+
params[n] = n === compiled.catchAll ? raw.split("/").map(decodeRouteValue).join("/") : decodeRouteValue(raw);
|
|
1082
|
+
});
|
|
1083
|
+
return { page, params };
|
|
1084
|
+
}
|
|
1109
1085
|
}
|
|
1110
1086
|
return { page: null, params: {} };
|
|
1111
1087
|
}
|
|
1088
|
+
function routeBinding(page, params) {
|
|
1089
|
+
const { catchAll } = routePatternToRegex(page.route);
|
|
1090
|
+
if (catchAll && params[catchAll] !== void 0) {
|
|
1091
|
+
const parts = splitPathCapture(params[catchAll]);
|
|
1092
|
+
const paramName2 = page.paramName || "slug";
|
|
1093
|
+
return { paramName: paramName2, paramValue: parts.slug, variables: { ...params, ...parts } };
|
|
1094
|
+
}
|
|
1095
|
+
const paramName = page.paramName || Object.keys(params)[0];
|
|
1096
|
+
return { paramName, paramValue: params[paramName], variables: { ...params } };
|
|
1097
|
+
}
|
|
1112
1098
|
function resolvePageFetchConfigs(content, route, { locale = null } = {}) {
|
|
1113
|
-
const { page } = findPageForRoute(content, route);
|
|
1099
|
+
const { page, params } = findPageForRoute(content, route);
|
|
1114
1100
|
if (!page) return [];
|
|
1115
1101
|
const pages = content?.pages || [];
|
|
1116
1102
|
const parent = page.parent ? pages.find((p) => p.route === page.parent) : null;
|
|
1103
|
+
const binding = page.isDynamic && Object.keys(params).length ? routeBinding(page, params) : null;
|
|
1117
1104
|
const options = {
|
|
1118
1105
|
locale,
|
|
1119
1106
|
defaultLocale: resolveDefaultLocale$1(content?.config) ?? null,
|
|
1120
1107
|
queries: content?.config?.queries ?? null,
|
|
1121
|
-
records: content?.config?.records ?? null
|
|
1108
|
+
records: content?.config?.records ?? null,
|
|
1109
|
+
variables: binding?.variables ?? null
|
|
1122
1110
|
};
|
|
1123
1111
|
const out = /* @__PURE__ */ new Map();
|
|
1124
1112
|
const add = (sources) => {
|
|
@@ -1135,6 +1123,14 @@ function resolvePageFetchConfigs(content, route, { locale = null } = {}) {
|
|
|
1135
1123
|
}
|
|
1136
1124
|
};
|
|
1137
1125
|
walk(page.sections);
|
|
1126
|
+
if (binding && binding.paramValue !== void 0 && page.parentSchema) {
|
|
1127
|
+
const listCfg = [...out.values()].find((cfg) => cfg.as === page.parentSchema && cfg.detail);
|
|
1128
|
+
const detailCfg = listCfg ? buildDetailConfig(listCfg, { paramName: binding.paramName, paramValue: String(binding.paramValue) }) : null;
|
|
1129
|
+
if (detailCfg) {
|
|
1130
|
+
const key = deriveCacheKey$1(detailCfg);
|
|
1131
|
+
if (!out.has(key)) out.set(key, detailCfg);
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1138
1134
|
return [...out.values()];
|
|
1139
1135
|
}
|
|
1140
1136
|
async function executeFetchConfigs(configs, { content, fetch: fetch2 = null, dev = false, prerender = "always" } = {}) {
|
|
@@ -1143,24 +1139,19 @@ async function executeFetchConfigs(configs, { content, fetch: fetch2 = null, dev
|
|
|
1143
1139
|
}
|
|
1144
1140
|
const fetcher = createDefaultFetcher({
|
|
1145
1141
|
basePath: content?.config?.base || "",
|
|
1146
|
-
config: content?.config?.fetcher ?? {},
|
|
1147
1142
|
records: content?.config?.records ?? null,
|
|
1148
1143
|
dev,
|
|
1149
1144
|
fetch: fetch2
|
|
1150
1145
|
});
|
|
1151
1146
|
const ctx = { website: null };
|
|
1152
|
-
|
|
1153
|
-
for (const config of configs || []) {
|
|
1154
|
-
if (!config) continue;
|
|
1147
|
+
return Promise.all((configs || []).filter(Boolean).map(async (config) => {
|
|
1155
1148
|
if (prerender === "author" && config.prerender === false) {
|
|
1156
|
-
|
|
1157
|
-
continue;
|
|
1149
|
+
return { config, outcome: "skipped", data: null };
|
|
1158
1150
|
}
|
|
1159
1151
|
const result = await fetcher.resolve(config, ctx);
|
|
1160
|
-
if (result?.error)
|
|
1161
|
-
|
|
1162
|
-
}
|
|
1163
|
-
return out;
|
|
1152
|
+
if (result?.error) return { config, outcome: "failed", data: null, error: result.error };
|
|
1153
|
+
return { config, outcome: "fetched", data: result?.data ?? null, ...result?.meta ? { meta: result.meta } : {} };
|
|
1154
|
+
}));
|
|
1164
1155
|
}
|
|
1165
1156
|
async function prefetchPageData({ content, route, locale = null, fetch: fetch2 = null, dev = false, prerender = "always" }) {
|
|
1166
1157
|
const configs = resolvePageFetchConfigs(content, route, { locale });
|