@uniweb/runtime 0.24.0 → 0.25.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 CHANGED
@@ -1,4 +1,4 @@
1
- import { isRichSchema, deriveCacheKey, resolveDefaultLocale, hasDarkScheme, createUniweb, substitutePlaceholders, resolveServiceUrl, applyScope, matchWhere, sortRecords, sortToWire } from "@uniweb/core";
1
+ import { isRichSchema, deriveCacheKey, resolveDefaultLocale, hasDarkScheme, createUniweb, substitutePlaceholders, resolveServiceUrl, evaluateQuery, 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";
@@ -9,7 +9,7 @@ import { DEFAULT_ICON_BASE, iconUrl } from "@uniweb/core/icon-corpus";
9
9
  import { buildTheme, FONT_LINKS_MARKER, buildSectionOverrides } from "@uniweb/theming";
10
10
  import "@uniweb/core/services";
11
11
  import "@uniweb/core/tracker";
12
- import { pageRouteQuery, siteReaches, resolveFetchConfigs, currentOf, othersView, routeSelection } from "@uniweb/core/fetch-config";
12
+ import { pageRouteQuery, siteReaches, fetchEntries, resolveFetchConfigs, currentFor, othersView, routeSelection } from "@uniweb/core/fetch-config";
13
13
  import { deriveCacheKey as deriveCacheKey$1 } from "@uniweb/core/datastore";
14
14
  import { buildDetailConfig } from "@uniweb/core/detail-url";
15
15
  import { resolveDefaultLocale as resolveDefaultLocale$1 } from "@uniweb/core/locale-config";
@@ -171,19 +171,30 @@ function applyDefaults(params, defaults) {
171
171
  ...params || {}
172
172
  };
173
173
  }
174
- function mergeEntityData(block, entityData) {
175
- if (!entityData) return;
176
- const current = block.parsedContent.data || {};
177
- let changed = false;
178
- const merged = { ...current };
179
- for (const key of Object.keys(entityData)) {
180
- if (merged[key] === void 0) {
181
- merged[key] = entityData[key];
182
- changed = true;
183
- }
184
- }
185
- if (changed) {
186
- block.parsedContent.data = merged;
174
+ function assembleData(block, meta, entityData) {
175
+ const declared = block.website?.declaredKeys ? block.website.declaredKeys(meta) : [];
176
+ const held = block.heldData || {};
177
+ const data = {};
178
+ for (const [key] of declared) {
179
+ if (held[key] !== void 0) data[key] = held[key];
180
+ else if (entityData && entityData[key] !== void 0) data[key] = entityData[key];
181
+ else data[key] = null;
182
+ }
183
+ warnUndeclaredBlocks(block, declared);
184
+ block.parsedContent.data = data;
185
+ }
186
+ const warnedBlocks = /* @__PURE__ */ new Set();
187
+ function warnUndeclaredBlocks(block, declared) {
188
+ if (!block.website?.entityStore?.dev) return;
189
+ const keys = new Set(declared.map(([key]) => key));
190
+ for (const item of block.parsedContent?.sequence || []) {
191
+ if (item?.type !== "dataBlock" || typeof item.tag !== "string" || keys.has(item.tag)) continue;
192
+ const memo = `${block.type}::${item.tag}`;
193
+ if (warnedBlocks.has(memo)) continue;
194
+ warnedBlocks.add(memo);
195
+ console.warn(
196
+ `[uniweb] ${block.type}: the \`${item.tag}\` data block is not in content.data — ${block.type} does not declare \`${item.tag}\` in its meta.js \`data:\`. It stays in content.sequence.`
197
+ );
187
198
  }
188
199
  }
189
200
  function runDataHandler(block) {
@@ -227,7 +238,8 @@ function runPropsHandler(content, params, block) {
227
238
  return null;
228
239
  }
229
240
  function prepareProps(block, meta, entityData = null) {
230
- mergeEntityData(block, entityData);
241
+ assembleData(block, meta, entityData);
242
+ block.website?.entityStore?.linkOwnRecords?.(block);
231
243
  runDataHandler(block);
232
244
  runContentHandler(block);
233
245
  const defaults = meta?.defaults || {};
@@ -937,14 +949,32 @@ function toQuestion(request) {
937
949
  const scope = typeof request.scope === "string" && request.scope ? request.scope : null;
938
950
  if (scope) q.scope = scope;
939
951
  if (where) q.where = where;
940
- if (request.match && typeof request.match === "object") q.match = request.match;
941
- const sort = sortToWire(request.sort);
952
+ const sort = wireSort(request.sort);
942
953
  if (sort) q.sort = sort;
943
954
  if (typeof request.limit === "number" && request.limit > 0) q.limit = request.limit;
944
955
  if (request.whole === true) q.whole = true;
945
- if (typeof request.cursor === "string" && request.cursor) q.cursor = request.cursor;
956
+ const narrow = toNarrow(request.narrow);
957
+ if (narrow) q.narrow = narrow;
946
958
  return q;
947
959
  }
960
+ function toNarrow(narrow) {
961
+ if (!narrow || typeof narrow !== "object") return null;
962
+ const n = {};
963
+ if (narrow.where && typeof narrow.where === "object") n.where = narrow.where;
964
+ if (narrow.match && typeof narrow.match === "object") n.match = narrow.match;
965
+ const sort = wireSort(narrow.sort);
966
+ if (sort) n.sort = sort;
967
+ if (typeof narrow.cursor === "string" && narrow.cursor) n.cursor = narrow.cursor;
968
+ if (typeof narrow.limit === "number" && narrow.limit > 0) n.limit = narrow.limit;
969
+ return Object.keys(n).length > 0 ? n : null;
970
+ }
971
+ function wireSort(sort) {
972
+ try {
973
+ return sortToWire(sort);
974
+ } catch {
975
+ return typeof sort === "string" && sort.trim() ? sort : null;
976
+ }
977
+ }
948
978
  const DEFAULT_MAX_PAGES = 20;
949
979
  async function flushAsked(url, queue, doFetch) {
950
980
  const pending = /* @__PURE__ */ new Map();
@@ -1034,7 +1064,7 @@ async function flushAsked(url, queue, doFetch) {
1034
1064
  await Promise.all([...pending].map(([entry, state]) => {
1035
1065
  const next = {
1036
1066
  ...entry,
1037
- request: { ...entry.request, cursor: state.cursor },
1067
+ request: { ...entry.request, narrow: { ...entry.request.narrow || {}, cursor: state.cursor } },
1038
1068
  collected: state.collected,
1039
1069
  page: state.page
1040
1070
  };
@@ -1072,13 +1102,10 @@ function sortLocale(request, ctx) {
1072
1102
  return typeof active === "string" && active ? active : null;
1073
1103
  }
1074
1104
  function applyOperators(data, request, { dev = false, locale = null } = {}) {
1075
- if (!Array.isArray(data)) return data;
1076
- let result = data;
1077
- if (typeof request.scope === "string" && request.scope) result = applyScope(result, request.scope);
1078
- if (request.where) result = matchWhere(request.where, result);
1079
- if (request.sort) result = applySort(result, request.sort, dev, locale);
1080
- if (typeof request.limit === "number" && request.limit > 0) result = result.slice(0, request.limit);
1081
- return result;
1105
+ return evaluateQuery(data, request, {
1106
+ locale,
1107
+ sort: (items, sortExpr, options) => applySort(items, sortExpr, dev, options.locale)
1108
+ });
1082
1109
  }
1083
1110
  const warnedBadSorts = /* @__PURE__ */ new Set();
1084
1111
  function applySort(items, sortExpr, dev, locale) {
@@ -1152,7 +1179,6 @@ function resolvePageFetchConfigs(content, route, { locale = null } = {}) {
1152
1179
  sectionsOf: (p) => p.sections,
1153
1180
  site: siteFetch
1154
1181
  }) : null;
1155
- const routeKey = chosen?.key ?? null;
1156
1182
  const routeSource = chosen?.nested ? chosen.config : null;
1157
1183
  const out = /* @__PURE__ */ new Map();
1158
1184
  const put = (cfg) => {
@@ -1160,8 +1186,9 @@ function resolvePageFetchConfigs(content, route, { locale = null } = {}) {
1160
1186
  if (!out.has(key)) out.set(key, cfg);
1161
1187
  };
1162
1188
  const add = (sources) => {
1163
- for (const cfg of resolveFetchConfigs(sources, options).values()) {
1164
- const current = routeKey && cfg.as === routeKey ? currentOf(cfg) : null;
1189
+ const resolved = sources.flatMap((source) => fetchEntries(source).map((entry) => resolveFetchConfigs([entry], options).get(entry.as)));
1190
+ for (const cfg of resolved.filter(Boolean)) {
1191
+ const current = currentFor(cfg, chosen);
1165
1192
  if (current === "exclude") {
1166
1193
  put(othersView(cfg));
1167
1194
  continue;
@@ -1174,7 +1201,7 @@ function resolvePageFetchConfigs(content, route, { locale = null } = {}) {
1174
1201
  const detailCfg = buildDetailConfig(cfg, { paramName: binding.paramName, paramValue: String(binding.paramValue) });
1175
1202
  if (detailCfg) put(detailCfg);
1176
1203
  }
1177
- put(cfg.ask ? cfg : routeSelection(cfg));
1204
+ if (!cfg.ask) put(routeSelection(cfg));
1178
1205
  }
1179
1206
  };
1180
1207
  const siteForPage = siteReaches(parent) ? siteFetch : null;
@@ -1265,8 +1292,7 @@ async function collectSiteRecords(content, { locale, fetch: fetch2, signal, only
1265
1292
  const meta = {};
1266
1293
  await Promise.all([...configs].map(async ([name, cfg]) => {
1267
1294
  if (!cfg.ask) return;
1268
- const { limit, ...population } = cfg;
1269
- const asked = { ...population, whole, exhaustive: true };
1295
+ const asked = { ...cfg, whole, exhaustive: true };
1270
1296
  if (typeof maxPages === "number" && maxPages > 0) asked.maxPages = maxPages;
1271
1297
  const result = await fetcher.resolve(asked, { signal });
1272
1298
  if (result?.error) errors[name] = result.error;