@uniweb/runtime 0.14.2 → 0.16.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 +177 -178
- package/dist/ssr.js.map +1 -1
- package/package.json +3 -3
- package/src/components/BlockRenderer.jsx +7 -0
- package/src/default-fetcher.js +250 -394
- package/src/isolate-api.js +87 -0
- package/src/page-renderer.js +2 -2
- package/src/prefetch.js +65 -19
- package/src/prepare-props.js +1 -1
- package/src/setup.js +7 -8
- 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, substitutePlaceholders, resolveServiceUrl, 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,108 +839,80 @@ 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, fetch: fetchImpl = null } = {}) {
|
|
843
843
|
const doFetch = (input, init) => (fetchImpl || globalThis.fetch)(input, init);
|
|
844
844
|
const pathPrefix = basePath && basePath !== "/" ? basePath.replace(/\/$/, "") : "";
|
|
845
|
-
const
|
|
846
|
-
const
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
845
|
+
const doorQueues = /* @__PURE__ */ new Map();
|
|
846
|
+
const askDoor = (request, ctx) => {
|
|
847
|
+
if (typeof request.schema !== "string" || !request.schema) {
|
|
848
|
+
return Promise.resolve({
|
|
849
|
+
data: null,
|
|
850
|
+
error: `the payload stamps a records door but carries no Model ref for query "${request.query ?? request.as}" (config.queries) — the door cannot be asked`
|
|
851
|
+
});
|
|
852
|
+
}
|
|
853
|
+
return new Promise((resolve) => {
|
|
854
|
+
const url = resolveServiceUrl(request.door, pathPrefix);
|
|
855
|
+
let queue = doorQueues.get(url);
|
|
856
|
+
if (!queue) {
|
|
857
|
+
queue = [];
|
|
858
|
+
doorQueues.set(url, queue);
|
|
859
|
+
queueMicrotask(() => {
|
|
860
|
+
doorQueues.delete(url);
|
|
861
|
+
flushDoor(url, queue, doFetch);
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
queue.push({ request, ctx, resolve });
|
|
865
|
+
});
|
|
866
|
+
};
|
|
856
867
|
return {
|
|
857
868
|
/**
|
|
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.
|
|
869
|
+
* The cache identity is the request's ADDRESS — or, on a question door,
|
|
870
|
+
* the QUESTION (`deriveCacheKey` hashes every operator of an address-less
|
|
871
|
+
* request). Operators evaluated here run over a shared cached value and
|
|
872
|
+
* must NOT split the cache: two pages declaring different `where:` clauses
|
|
873
|
+
* against the same path share one entry — the file is fetched once and
|
|
874
|
+
* each page filters its own copy.
|
|
868
875
|
*/
|
|
869
876
|
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);
|
|
877
|
+
return deriveCacheKey(request);
|
|
880
878
|
},
|
|
881
879
|
async resolve(request, ctx = {}) {
|
|
882
880
|
if (!request) return { data: null };
|
|
883
|
-
|
|
881
|
+
if (request.door) return askDoor(request, ctx);
|
|
882
|
+
const { path, url, transform, body: rawBody } = request;
|
|
884
883
|
let method = (request.method || "GET").toUpperCase();
|
|
885
884
|
if (method !== "GET" && method !== "POST") {
|
|
886
885
|
console.warn(`[default-fetcher] method "${request.method}" is not supported — falling back to GET.`);
|
|
887
886
|
method = "GET";
|
|
888
887
|
}
|
|
889
888
|
let target;
|
|
890
|
-
|
|
891
|
-
if (endpoint) {
|
|
892
|
-
target = resolveServiceUrl(endpoint, pathPrefix);
|
|
893
|
-
isRemote = true;
|
|
894
|
-
} else if (path) {
|
|
889
|
+
if (path) {
|
|
895
890
|
target = pathPrefix && path.startsWith("/") && !path.startsWith("//") ? pathPrefix + path : path;
|
|
896
|
-
isRemote = false;
|
|
897
891
|
} else if (url) {
|
|
898
|
-
target =
|
|
899
|
-
isRemote = true;
|
|
892
|
+
target = url;
|
|
900
893
|
} else {
|
|
901
|
-
return { data: [], error: "No path, url or
|
|
894
|
+
return { data: [], error: "No path, url or door specified" };
|
|
902
895
|
}
|
|
903
896
|
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
897
|
if (method === "POST") {
|
|
920
898
|
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);
|
|
899
|
+
const body = rawBody !== void 0 && rawBody !== null && dc && dc.paramName ? substitutePlaceholders(rawBody, { [dc.paramName]: dc.paramValue }, { encode: false }) : rawBody;
|
|
900
|
+
if (body !== void 0 && body !== null) {
|
|
901
|
+
init.headers = { "Content-Type": "application/json" };
|
|
902
|
+
init.body = typeof body === "string" ? body : JSON.stringify(body);
|
|
928
903
|
}
|
|
929
904
|
}
|
|
930
|
-
if (Object.keys(headers).length) init.headers = headers;
|
|
931
905
|
try {
|
|
932
906
|
const response = await doFetch(target, init);
|
|
933
|
-
const
|
|
934
|
-
const effectiveEnvelope = requestEnvelope ?? (endpoint && laneEnvelope ? { ...envelope, ...laneEnvelope } : envelope);
|
|
907
|
+
const envelope = request.envelope && typeof request.envelope === "object" ? request.envelope : {};
|
|
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,109 @@ 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
|
+
let detail = null;
|
|
1003
|
+
try {
|
|
1004
|
+
const problem = safeParseJSON(await response.text());
|
|
1005
|
+
if (problem && typeof problem.detail === "string" && problem.detail) detail = problem.detail;
|
|
1006
|
+
} catch {
|
|
1007
|
+
}
|
|
1008
|
+
const error = detail ? `HTTP ${response.status}: ${detail}` : `HTTP ${response.status}: ${response.statusText}`;
|
|
1009
|
+
for (const entry of queue) entry.resolve({ data: null, error });
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
parsed = await response.json();
|
|
1013
|
+
} catch (error) {
|
|
1014
|
+
const message = error?.name === "AbortError" ? "aborted" : error?.message || String(error);
|
|
1015
|
+
for (const entry of queue) entry.resolve({ data: null, error: message });
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
const data = parsed && typeof parsed.data === "object" && parsed.data ? parsed.data : {};
|
|
1019
|
+
const errors = parsed && typeof parsed.errors === "object" && parsed.errors ? parsed.errors : {};
|
|
1020
|
+
const depths = parsed && typeof parsed.depths === "object" && parsed.depths ? parsed.depths : {};
|
|
1021
|
+
queue.forEach((entry, i) => {
|
|
1022
|
+
const key = keys[i];
|
|
1023
|
+
if (key in errors) {
|
|
1024
|
+
const e = errors[key];
|
|
1025
|
+
const detail = typeof e === "string" ? e : e?.detail || e?.message || JSON.stringify(e);
|
|
1026
|
+
const out = { data: null, error: detail };
|
|
1027
|
+
if (e && typeof e === "object" && typeof e.code === "string") out.code = e.code;
|
|
1028
|
+
entry.resolve(out);
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
if (!(key in data)) {
|
|
1032
|
+
entry.resolve({ data: null, error: `the records door answered without the key "${key}"` });
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
const depth = depths[key] === "brief" || depths[key] === "full" ? depths[key] : entry.request.depth === "brief" || entry.request.depth === "full" ? entry.request.depth : void 0;
|
|
1036
|
+
entry.resolve(depth ? { data: data[key], meta: { depth } } : { data: data[key] });
|
|
1037
|
+
});
|
|
1030
1038
|
}
|
|
1031
|
-
function
|
|
1039
|
+
function applyOperators(data, request, { dev = false } = {}) {
|
|
1032
1040
|
if (!Array.isArray(data)) return data;
|
|
1033
1041
|
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
|
-
}
|
|
1042
|
+
if (request.where) result = matchWhere(request.where, result);
|
|
1043
|
+
if (request.sort) result = applySort(result, request.sort, dev);
|
|
1044
|
+
if (typeof request.limit === "number" && request.limit > 0) result = result.slice(0, request.limit);
|
|
1043
1045
|
return result;
|
|
1044
1046
|
}
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
return
|
|
1049
|
-
})
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
if (av > bv) return desc ? -1 : 1;
|
|
1047
|
+
const warnedBadSorts = /* @__PURE__ */ new Set();
|
|
1048
|
+
function applySort(items, sortExpr, dev) {
|
|
1049
|
+
try {
|
|
1050
|
+
return sortRecords(items, sortExpr);
|
|
1051
|
+
} catch (err) {
|
|
1052
|
+
if (dev) throw err;
|
|
1053
|
+
const key = String(sortExpr);
|
|
1054
|
+
if (!warnedBadSorts.has(key)) {
|
|
1055
|
+
warnedBadSorts.add(key);
|
|
1056
|
+
console.error(`[default-fetcher] ${err.message} Records delivered unsorted.`);
|
|
1056
1057
|
}
|
|
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);
|
|
1058
|
+
return items;
|
|
1066
1059
|
}
|
|
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
1060
|
}
|
|
1083
1061
|
function getNestedValue(obj, path) {
|
|
1084
1062
|
if (!obj || !path) return obj;
|
|
@@ -1105,20 +1083,39 @@ function findPageForRoute(content, route) {
|
|
|
1105
1083
|
if (!page.isDynamic || !page.route) continue;
|
|
1106
1084
|
const compiled = routePatternToRegex(page.route);
|
|
1107
1085
|
const m = compiled?.regex ? compiled.regex.exec(route) : null;
|
|
1108
|
-
if (m)
|
|
1086
|
+
if (m) {
|
|
1087
|
+
const params = {};
|
|
1088
|
+
(compiled.paramNames || []).forEach((n, i) => {
|
|
1089
|
+
const raw = m[i + 1];
|
|
1090
|
+
params[n] = n === compiled.catchAll ? raw.split("/").map(decodeRouteValue).join("/") : decodeRouteValue(raw);
|
|
1091
|
+
});
|
|
1092
|
+
return { page, params };
|
|
1093
|
+
}
|
|
1109
1094
|
}
|
|
1110
1095
|
return { page: null, params: {} };
|
|
1111
1096
|
}
|
|
1097
|
+
function routeBinding(page, params) {
|
|
1098
|
+
const { catchAll } = routePatternToRegex(page.route);
|
|
1099
|
+
if (catchAll && params[catchAll] !== void 0) {
|
|
1100
|
+
const parts = splitPathCapture(params[catchAll]);
|
|
1101
|
+
const paramName2 = page.paramName || "slug";
|
|
1102
|
+
return { paramName: paramName2, paramValue: parts.slug, variables: { ...params, ...parts } };
|
|
1103
|
+
}
|
|
1104
|
+
const paramName = page.paramName || Object.keys(params)[0];
|
|
1105
|
+
return { paramName, paramValue: params[paramName], variables: { ...params } };
|
|
1106
|
+
}
|
|
1112
1107
|
function resolvePageFetchConfigs(content, route, { locale = null } = {}) {
|
|
1113
|
-
const { page } = findPageForRoute(content, route);
|
|
1108
|
+
const { page, params } = findPageForRoute(content, route);
|
|
1114
1109
|
if (!page) return [];
|
|
1115
1110
|
const pages = content?.pages || [];
|
|
1116
1111
|
const parent = page.parent ? pages.find((p) => p.route === page.parent) : null;
|
|
1112
|
+
const binding = page.isDynamic && Object.keys(params).length ? routeBinding(page, params) : null;
|
|
1117
1113
|
const options = {
|
|
1118
1114
|
locale,
|
|
1119
1115
|
defaultLocale: resolveDefaultLocale$1(content?.config) ?? null,
|
|
1120
1116
|
queries: content?.config?.queries ?? null,
|
|
1121
|
-
records: content?.config?.records ?? null
|
|
1117
|
+
records: content?.config?.records ?? null,
|
|
1118
|
+
variables: binding?.variables ?? null
|
|
1122
1119
|
};
|
|
1123
1120
|
const out = /* @__PURE__ */ new Map();
|
|
1124
1121
|
const add = (sources) => {
|
|
@@ -1135,6 +1132,14 @@ function resolvePageFetchConfigs(content, route, { locale = null } = {}) {
|
|
|
1135
1132
|
}
|
|
1136
1133
|
};
|
|
1137
1134
|
walk(page.sections);
|
|
1135
|
+
if (binding && binding.paramValue !== void 0 && page.parentSchema) {
|
|
1136
|
+
const listCfg = [...out.values()].find((cfg) => cfg.as === page.parentSchema && cfg.detail);
|
|
1137
|
+
const detailCfg = listCfg ? buildDetailConfig(listCfg, { paramName: binding.paramName, paramValue: String(binding.paramValue) }) : null;
|
|
1138
|
+
if (detailCfg) {
|
|
1139
|
+
const key = deriveCacheKey$1(detailCfg);
|
|
1140
|
+
if (!out.has(key)) out.set(key, detailCfg);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1138
1143
|
return [...out.values()];
|
|
1139
1144
|
}
|
|
1140
1145
|
async function executeFetchConfigs(configs, { content, fetch: fetch2 = null, dev = false, prerender = "always" } = {}) {
|
|
@@ -1143,24 +1148,18 @@ async function executeFetchConfigs(configs, { content, fetch: fetch2 = null, dev
|
|
|
1143
1148
|
}
|
|
1144
1149
|
const fetcher = createDefaultFetcher({
|
|
1145
1150
|
basePath: content?.config?.base || "",
|
|
1146
|
-
config: content?.config?.fetcher ?? {},
|
|
1147
|
-
records: content?.config?.records ?? null,
|
|
1148
1151
|
dev,
|
|
1149
1152
|
fetch: fetch2
|
|
1150
1153
|
});
|
|
1151
1154
|
const ctx = { website: null };
|
|
1152
|
-
|
|
1153
|
-
for (const config of configs || []) {
|
|
1154
|
-
if (!config) continue;
|
|
1155
|
+
return Promise.all((configs || []).filter(Boolean).map(async (config) => {
|
|
1155
1156
|
if (prerender === "author" && config.prerender === false) {
|
|
1156
|
-
|
|
1157
|
-
continue;
|
|
1157
|
+
return { config, outcome: "skipped", data: null };
|
|
1158
1158
|
}
|
|
1159
1159
|
const result = await fetcher.resolve(config, ctx);
|
|
1160
|
-
if (result?.error)
|
|
1161
|
-
|
|
1162
|
-
}
|
|
1163
|
-
return out;
|
|
1160
|
+
if (result?.error) return { config, outcome: "failed", data: null, error: result.error };
|
|
1161
|
+
return { config, outcome: "fetched", data: result?.data ?? null, ...result?.meta ? { meta: result.meta } : {} };
|
|
1162
|
+
}));
|
|
1164
1163
|
}
|
|
1165
1164
|
async function prefetchPageData({ content, route, locale = null, fetch: fetch2 = null, dev = false, prerender = "always" }) {
|
|
1166
1165
|
const configs = resolvePageFetchConfigs(content, route, { locale });
|