@uniweb/runtime 0.17.1 → 0.18.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
@@ -940,8 +940,8 @@ function createDefaultFetcher({ basePath = "", dev = false, fetch: fetchImpl = n
940
940
  data = getNestedValue(data, effectiveTransform);
941
941
  }
942
942
  data = applyOperators(data, request, { dev });
943
- const depth = request.depth === "brief" || request.depth === "full" ? request.depth : void 0;
944
- return depth ? { data: data ?? [], meta: { depth } } : { data: data ?? [] };
943
+ const whole = typeof request.whole === "boolean" ? request.whole : void 0;
944
+ return whole === void 0 ? { data: data ?? [] } : { data: data ?? [], meta: { whole } };
945
945
  } catch (error) {
946
946
  if (error?.name === "AbortError") {
947
947
  return { data: [], error: "aborted" };
@@ -965,7 +965,7 @@ function toQuestion(request) {
965
965
  const sort = sortToWire(request.sort);
966
966
  if (sort) q.sort = sort;
967
967
  if (typeof request.limit === "number" && request.limit > 0) q.limit = request.limit;
968
- if (request.depth === "brief" || request.depth === "full") q.depth = request.depth;
968
+ if (request.whole === true) q.whole = true;
969
969
  if (typeof request.cursor === "string" && request.cursor) q.cursor = request.cursor;
970
970
  return q;
971
971
  }
@@ -979,7 +979,7 @@ function renameOperators(where) {
979
979
  }
980
980
  return out;
981
981
  }
982
- const MAX_PAGES = 50;
982
+ const DEFAULT_MAX_PAGES = 20;
983
983
  async function flushAsked(url, queue, doFetch) {
984
984
  const pending = /* @__PURE__ */ new Map();
985
985
  const body = {};
@@ -1012,20 +1012,24 @@ async function flushAsked(url, queue, doFetch) {
1012
1012
  parsed = await response.json();
1013
1013
  } catch (error) {
1014
1014
  const message = error?.name === "AbortError" ? "aborted" : error?.message || String(error);
1015
- for (const entry of queue) entry.resolve({ data: null, error: message });
1015
+ for (const entry of queue) {
1016
+ const held = Array.isArray(entry.collected) && entry.collected.length ? entry.collected : null;
1017
+ entry.resolve(held ? { data: held, error: message, meta: withMeta({ partial: true, pages: entry.page }) } : { data: null, error: message });
1018
+ }
1016
1019
  return;
1017
1020
  }
1018
1021
  const data = parsed && typeof parsed.data === "object" && parsed.data ? parsed.data : {};
1019
1022
  const errors = parsed && typeof parsed.errors === "object" && parsed.errors ? parsed.errors : {};
1020
- const depths = parsed && typeof parsed.depths === "object" && parsed.depths ? parsed.depths : {};
1023
+ const wholes = parsed && typeof parsed.whole === "object" && parsed.whole ? parsed.whole : {};
1021
1024
  const cursors = parsed && typeof parsed.cursors === "object" && parsed.cursors ? parsed.cursors : {};
1022
1025
  const limits = parsed && typeof parsed.limits === "object" && parsed.limits ? parsed.limits : {};
1023
1026
  queue.forEach((entry, i) => {
1024
1027
  const key = keys[i];
1028
+ const held = Array.isArray(entry.collected) && entry.collected.length ? entry.collected : null;
1025
1029
  if (key in errors) {
1026
1030
  const e = errors[key];
1027
1031
  const detail = typeof e === "string" ? e : e?.detail || e?.message || JSON.stringify(e);
1028
- const out = { data: null, error: detail };
1032
+ const out = held ? { data: held, error: detail, meta: withMeta({ partial: true, pages: entry.page }) } : { data: null, error: detail };
1029
1033
  if (e && typeof e === "object" && typeof e.code === "string") out.code = e.code;
1030
1034
  entry.resolve(out);
1031
1035
  return;
@@ -1034,28 +1038,39 @@ async function flushAsked(url, queue, doFetch) {
1034
1038
  entry.resolve({ data: null, error: `the records service answered without the key "${key}"` });
1035
1039
  return;
1036
1040
  }
1037
- const depth = depths[key] === "brief" || depths[key] === "full" ? depths[key] : entry.request.depth === "brief" || entry.request.depth === "full" ? entry.request.depth : void 0;
1041
+ const whole = typeof wholes[key] === "boolean" ? wholes[key] : typeof entry.request.whole === "boolean" ? entry.request.whole : void 0;
1038
1042
  const cursor = typeof cursors[key] === "string" && cursors[key] ? cursors[key] : null;
1039
1043
  const bound = typeof limits[key] === "number" ? limits[key] : void 0;
1040
1044
  const rows = Array.isArray(data[key]) ? data[key] : data[key];
1041
1045
  if (cursor && entry.request.exhaustive && Array.isArray(rows)) {
1042
1046
  const acc = entry.collected ? entry.collected.concat(rows) : rows.slice();
1043
1047
  const page = (entry.page || 1) + 1;
1044
- if (page <= MAX_PAGES) {
1045
- pending.set(entry, { cursor, collected: acc, page, depth, bound });
1048
+ const cap = typeof entry.request.maxPages === "number" && entry.request.maxPages > 0 ? entry.request.maxPages : DEFAULT_MAX_PAGES;
1049
+ if (page <= cap) {
1050
+ pending.set(entry, { cursor, collected: acc, page, whole, bound });
1046
1051
  return;
1047
1052
  }
1048
- entry.resolve({ data: acc, meta: withMeta({ depth, bound, truncated: true, pages: MAX_PAGES }) });
1053
+ entry.resolve({ data: acc, meta: withMeta({ whole, bound, partial: true, pages: cap }) });
1049
1054
  return;
1050
1055
  }
1051
1056
  const collected = entry.collected ? entry.collected.concat(Array.isArray(rows) ? rows : []) : rows;
1052
1057
  const meta = withMeta({
1053
- depth,
1058
+ whole,
1054
1059
  bound,
1055
- // ⭐ A cursor IS the truncation signal, and it is the only one for a query
1056
- // that declared no `limit`: `limits` is reported only when an author's own
1057
- // limit was clamped (the records contract §5).
1058
- truncated: cursor ? true : void 0,
1060
+ // ⭐ ONE FLAG, MEANING **NOT THE WHOLE POPULATION** asked for by name, so
1061
+ // a caller has one boolean to branch on rather than three signals to
1062
+ // combine. It is set in every case that means it:
1063
+ // · a cursor came back and this caller does not page (a page render);
1064
+ // · a cursor came back and the caller's `maxPages` stopped the walk;
1065
+ // · the walk failed or aborted with pages already in hand;
1066
+ // · the service reported it BOUNDED the answer and offered no cursor.
1067
+ // ⚠️ The last is why `limits` is read at all: a cursor is the signal for a
1068
+ // query that declared no `limit`, and `limits` is the signal for one whose
1069
+ // author limit was clamped (the records contract §5). Neither alone covers
1070
+ // both. ⛔ Named `truncated` when it shipped in 0.17.0 this morning; renamed
1071
+ // the same day, before any consumer adopted it, because "truncated" says
1072
+ // something was cut and this also means "there is more you did not ask for".
1073
+ partial: cursor || bound !== void 0 ? true : void 0,
1059
1074
  pages: entry.page && entry.page > 1 ? entry.page : void 0
1060
1075
  });
1061
1076
  entry.resolve(meta ? { data: collected, meta } : { data: collected });
@@ -1239,7 +1254,7 @@ async function prefetchAndHydrate({ website, content, route, locale = null, fetc
1239
1254
  hydrateDataStore(website, fetched);
1240
1255
  return fetched;
1241
1256
  }
1242
- async function collectSiteRecords(content, { locale, fetch: fetch2, signal, only = null } = {}) {
1257
+ async function collectSiteRecords(content, { locale, fetch: fetch2, signal, only = null, whole = false, maxPages } = {}) {
1243
1258
  const config = content?.config;
1244
1259
  const services = config?.services ?? null;
1245
1260
  const queries = config?.queries ?? null;
@@ -1257,12 +1272,13 @@ async function collectSiteRecords(content, { locale, fetch: fetch2, signal, only
1257
1272
  const meta = {};
1258
1273
  await Promise.all([...configs].map(async ([name, cfg]) => {
1259
1274
  if (!cfg.ask) return;
1260
- const result = await fetcher.resolve({ ...cfg, depth: "brief", exhaustive: true }, { signal });
1261
- if (result?.error) {
1262
- errors[name] = result.error;
1263
- return;
1264
- }
1265
- records[name] = Array.isArray(result?.data) ? result.data : [];
1275
+ const { limit, ...population } = cfg;
1276
+ const asked = { ...population, whole, exhaustive: true };
1277
+ if (typeof maxPages === "number" && maxPages > 0) asked.maxPages = maxPages;
1278
+ const result = await fetcher.resolve(asked, { signal });
1279
+ if (result?.error) errors[name] = result.error;
1280
+ if (Array.isArray(result?.data)) records[name] = result.data;
1281
+ else if (!result?.error) records[name] = [];
1266
1282
  if (result?.meta) meta[name] = result.meta;
1267
1283
  }));
1268
1284
  return { records, errors: Object.keys(errors).length ? errors : null, meta };