@uniweb/runtime 0.15.0 → 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 CHANGED
@@ -1,4 +1,4 @@
1
- import { isRichSchema, deriveCacheKey, resolveDefaultLocale, hasDarkScheme, createUniweb, resolveServiceUrl, substitutePlaceholders, matchWhere, sortRecords, sortToWire } from "@uniweb/core";
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";
@@ -839,25 +839,31 @@ function generate404Html({ baseHtml, website, siteContent }) {
839
839
  }
840
840
  return { html, hasNotFoundPage: !!notFoundPage };
841
841
  }
842
- function createDefaultFetcher({ basePath = "", 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 stampedArrayKey = records?.envelope && typeof records.envelope === "object" && typeof records.envelope.records === "string" && records.envelope.records.length ? records.envelope.records : null;
846
- const laneEnvelope = stampedArrayKey ? { list: stampedArrayKey } : null;
847
845
  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);
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`
857
851
  });
858
852
  }
859
- queue.push({ request, ctx, resolve });
860
- });
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
+ };
861
867
  return {
862
868
  /**
863
869
  * The cache identity is the request's ADDRESS — or, on a question door,
@@ -873,24 +879,19 @@ function createDefaultFetcher({ basePath = "", dev = false, records = null, fetc
873
879
  async resolve(request, ctx = {}) {
874
880
  if (!request) return { data: null };
875
881
  if (request.door) return askDoor(request, ctx);
876
- const { path, url, endpoint, transform, body: rawBody } = request;
882
+ const { path, url, transform, body: rawBody } = request;
877
883
  let method = (request.method || "GET").toUpperCase();
878
884
  if (method !== "GET" && method !== "POST") {
879
885
  console.warn(`[default-fetcher] method "${request.method}" is not supported — falling back to GET.`);
880
886
  method = "GET";
881
887
  }
882
888
  let target;
883
- if (endpoint) {
884
- target = resolveServiceUrl(endpoint, pathPrefix);
885
- if (typeof request.locale === "string" && request.locale) {
886
- target += (target.includes("?") ? "&" : "?") + "locale=" + encodeURIComponent(request.locale);
887
- }
888
- } else if (path) {
889
+ if (path) {
889
890
  target = pathPrefix && path.startsWith("/") && !path.startsWith("//") ? pathPrefix + path : path;
890
891
  } else if (url) {
891
892
  target = url;
892
893
  } else {
893
- return { data: [], error: "No path, url or endpoint specified" };
894
+ return { data: [], error: "No path, url or door specified" };
894
895
  }
895
896
  const init = { signal: ctx.signal, method };
896
897
  if (method === "POST") {
@@ -903,8 +904,7 @@ function createDefaultFetcher({ basePath = "", dev = false, records = null, fetc
903
904
  }
904
905
  try {
905
906
  const response = await doFetch(target, init);
906
- const requestEnvelope = request.envelope && typeof request.envelope === "object" ? request.envelope : null;
907
- const envelope = requestEnvelope ?? (endpoint && laneEnvelope ? laneEnvelope : {});
907
+ const envelope = request.envelope && typeof request.envelope === "object" ? request.envelope : {};
908
908
  if (!response.ok) {
909
909
  let extracted;
910
910
  if (envelope.error) {
@@ -999,7 +999,13 @@ async function flushDoor(url, queue, doFetch) {
999
999
  body: JSON.stringify(body)
1000
1000
  });
1001
1001
  if (!response.ok) {
1002
- const error = `HTTP ${response.status}: ${response.statusText}`;
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}`;
1003
1009
  for (const entry of queue) entry.resolve({ data: null, error });
1004
1010
  return;
1005
1011
  }
@@ -1016,7 +1022,10 @@ async function flushDoor(url, queue, doFetch) {
1016
1022
  const key = keys[i];
1017
1023
  if (key in errors) {
1018
1024
  const e = errors[key];
1019
- entry.resolve({ data: null, error: typeof e === "string" ? e : e?.message || JSON.stringify(e) });
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);
1020
1029
  return;
1021
1030
  }
1022
1031
  if (!(key in data)) {
@@ -1139,7 +1148,6 @@ async function executeFetchConfigs(configs, { content, fetch: fetch2 = null, dev
1139
1148
  }
1140
1149
  const fetcher = createDefaultFetcher({
1141
1150
  basePath: content?.config?.base || "",
1142
- records: content?.config?.records ?? null,
1143
1151
  dev,
1144
1152
  fetch: fetch2
1145
1153
  });