@askrjs/askr 0.0.43 → 0.0.44

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.
@@ -1,3 +1,4 @@
1
+ import { DocumentRenderArgs, DocumentRenderContext, DocumentRenderer } from "../common/ssr.js";
1
2
  import { DiscoveredResources, RouteConfig, RouteRenderReason, RouteRenderResult, RouteRenderStatus, SSGGenerateOptions, SSGMetadata, SSGMode, SSGOptions, SSGResult } from "./types.js";
2
3
  import { createStaticGen } from "./create-static-gen.js";
3
- export { type DiscoveredResources, type RouteConfig, type RouteRenderReason, type RouteRenderResult, type RouteRenderStatus, type SSGGenerateOptions, type SSGMetadata, type SSGMode, type SSGOptions, type SSGResult, createStaticGen };
4
+ export { type DiscoveredResources, type DocumentRenderArgs, type DocumentRenderContext, type DocumentRenderer, type RouteConfig, type RouteRenderReason, type RouteRenderResult, type RouteRenderStatus, type SSGGenerateOptions, type SSGMetadata, type SSGMode, type SSGOptions, type SSGResult, createStaticGen };
@@ -1,5 +1,8 @@
1
1
  import { interpolateRoutePath } from "./route-utils.js";
2
2
  //#region src/ssg/resolve-ssg-data.ts
3
+ function hasOwnRouteData(dataMap, path) {
4
+ return Object.prototype.hasOwnProperty.call(dataMap, path);
5
+ }
3
6
  /**
4
7
  * Resolve and validate data for SSG routes
5
8
  * Returns a map of route path -> SSRData
@@ -21,6 +24,20 @@ function resolveSsgData(routes, options = {}) {
21
24
  }
22
25
  return dataMap;
23
26
  }
27
+ function resolveSsgRouteData(dataMap, routePath, concretePath) {
28
+ if (hasOwnRouteData(dataMap, concretePath)) return {
29
+ hasData: true,
30
+ data: dataMap[concretePath]
31
+ };
32
+ if (hasOwnRouteData(dataMap, routePath)) return {
33
+ hasData: true,
34
+ data: dataMap[routePath]
35
+ };
36
+ return {
37
+ hasData: false,
38
+ data: void 0
39
+ };
40
+ }
24
41
  function isStringRecord(value) {
25
42
  if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
26
43
  for (const recordValue of Object.values(value)) if (typeof recordValue !== "string") return false;
@@ -83,6 +100,6 @@ function validateRoutes(routes) {
83
100
  }
84
101
  }
85
102
  //#endregion
86
- export { expandRoutes, resolveSsgData, validateRoutes };
103
+ export { expandRoutes, resolveSsgData, resolveSsgRouteData, validateRoutes };
87
104
 
88
105
  //# sourceMappingURL=resolve-ssg-data.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-ssg-data.js","names":[],"sources":["../../src/ssg/resolve-ssg-data.ts"],"sourcesContent":["/**\n * Data resolution for SSG\n *\n * Merges user-supplied data overrides with any auto-discovered resources.\n * In phase 1, primarily handles user-supplied data.\n */\n\nimport type { SSRData } from '../common/ssr';\nimport type { RouteConfig } from './types';\nimport { interpolateRoutePath } from './route-utils';\n\ninterface DataResolutionOptions {\n /** User-supplied data overrides per route path */\n dataOverrides?: Record<string, unknown>;\n}\n\n/**\n * Resolve and validate data for SSG routes\n * Returns a map of route path -> SSRData\n *\n * In phase 1: accepts user-supplied dataOverrides\n * In phase 2: can be extended to auto-discover resources\n */\nexport function resolveSsgData(\n routes: RouteConfig[],\n options: DataResolutionOptions = {}\n): Record<string, SSRData> {\n const { dataOverrides = {} } = options;\n const dataMap: Record<string, SSRData> = {};\n\n for (const route of routes) {\n const concretePath = interpolateRoutePath(route.path, route.params);\n const overridePath =\n concretePath in dataOverrides\n ? concretePath\n : route.path in dataOverrides\n ? route.path\n : null;\n\n // Check if user provided data for this route\n if (overridePath) {\n const data = dataOverrides[overridePath];\n if (typeof data !== 'object' || data === null || Array.isArray(data)) {\n throw new Error(\n `data for route \"${overridePath}\" must be an object, got ${typeof data}`\n );\n }\n dataMap[overridePath] = data as SSRData;\n }\n // In phase 1, routes without data are rendered with no SSR data\n // Phase 2 can add auto-discovery here\n }\n\n return dataMap;\n}\n\nfunction isStringRecord(value: unknown): value is Record<string, string> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return false;\n }\n\n for (const recordValue of Object.values(value)) {\n if (typeof recordValue !== 'string') {\n return false;\n }\n }\n\n return true;\n}\n\nfunction validateRouteShape(route: RouteConfig): void {\n if (typeof route.path !== 'string' || !route.path.startsWith('/')) {\n throw new Error(\n `route path must be a string starting with \"/\", got \"${route.path}\"`\n );\n }\n\n const handler = route.handler ?? route.component;\n if (typeof handler !== 'function') {\n throw new Error(\n `route handler must be a function for path \"${route.path}\"`\n );\n }\n\n if (route.params !== undefined && !isStringRecord(route.params)) {\n throw new Error(\n `route \"${route.path}\" params must be an object containing only string values`\n );\n }\n\n if (route.invalidationKeys !== undefined) {\n if (!Array.isArray(route.invalidationKeys)) {\n throw new Error(\n `route \"${route.path}\" invalidationKeys must be an array of strings`\n );\n }\n for (const key of route.invalidationKeys) {\n if (typeof key !== 'string' || key.length === 0) {\n throw new Error(\n `route \"${route.path}\" invalidationKeys must contain only non-empty strings`\n );\n }\n }\n }\n}\n\nfunction validateEntriesResult(\n route: RouteConfig,\n entries: unknown\n): Array<Record<string, string>> {\n if (!Array.isArray(entries)) {\n throw new Error(\n `route \"${route.path}\" entries must return an array of param maps`\n );\n }\n\n return entries.map((entry, index) => {\n if (!isStringRecord(entry)) {\n throw new Error(\n `route \"${route.path}\" entries[${index}] must be an object containing only string values`\n );\n }\n\n return entry;\n });\n}\n\nexport async function expandRoutes(\n routes: RouteConfig[]\n): Promise<RouteConfig[]> {\n if (!Array.isArray(routes)) {\n throw new Error('routes must be an array');\n }\n\n const expanded: RouteConfig[] = [];\n\n for (const route of routes) {\n validateRouteShape(route);\n\n if (route.params) {\n expanded.push({ ...route, entries: undefined });\n }\n\n if (!route.entries) {\n if (!route.params) {\n expanded.push(route);\n }\n continue;\n }\n\n const entries = validateEntriesResult(route, await route.entries());\n for (const params of entries) {\n expanded.push({ ...route, params, entries: undefined });\n }\n }\n\n return expanded;\n}\n\n/**\n * Validate routes are properly configured\n */\nexport function validateRoutes(routes: RouteConfig[]): void {\n if (!Array.isArray(routes)) {\n throw new Error('routes must be an array');\n }\n\n const seen = new Set<string>();\n\n for (const route of routes) {\n validateRouteShape(route);\n\n const key = `${route.path}::${JSON.stringify(route.params || {})}`;\n if (seen.has(key)) {\n throw new Error(\n `duplicate route entry detected for path \"${route.path}\"`\n );\n }\n seen.add(key);\n\n if (route.path.includes('{')) {\n const paramNames = Array.from(route.path.matchAll(/\\{([^}]+)\\}/g)).map(\n (m) => m[1]\n );\n if (!route.params) {\n throw new Error(\n `route \"${route.path}\" uses path parameters and requires params`\n );\n }\n for (const name of paramNames) {\n if (!(name in route.params)) {\n throw new Error(\n `route \"${route.path}\" missing required param \"${name}\"`\n );\n }\n }\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"resolve-ssg-data.js","names":[],"sources":["../../src/ssg/resolve-ssg-data.ts"],"sourcesContent":["/**\n * Data resolution for SSG\n *\n * Merges user-supplied data overrides with any auto-discovered resources.\n * In phase 1, primarily handles user-supplied data.\n */\n\nimport type { SSRData } from '../common/ssr';\nimport type { RouteConfig } from './types';\nimport { interpolateRoutePath } from './route-utils';\n\ninterface DataResolutionOptions {\n /** User-supplied data overrides per route path */\n dataOverrides?: Record<string, unknown>;\n}\n\nexport interface ResolvedSsgRouteData {\n hasData: boolean;\n data?: SSRData;\n}\n\nfunction hasOwnRouteData(\n dataMap: Record<string, SSRData>,\n path: string\n): boolean {\n return Object.prototype.hasOwnProperty.call(dataMap, path);\n}\n\n/**\n * Resolve and validate data for SSG routes\n * Returns a map of route path -> SSRData\n *\n * In phase 1: accepts user-supplied dataOverrides\n * In phase 2: can be extended to auto-discover resources\n */\nexport function resolveSsgData(\n routes: RouteConfig[],\n options: DataResolutionOptions = {}\n): Record<string, SSRData> {\n const { dataOverrides = {} } = options;\n const dataMap: Record<string, SSRData> = {};\n\n for (const route of routes) {\n const concretePath = interpolateRoutePath(route.path, route.params);\n const overridePath =\n concretePath in dataOverrides\n ? concretePath\n : route.path in dataOverrides\n ? route.path\n : null;\n\n // Check if user provided data for this route\n if (overridePath) {\n const data = dataOverrides[overridePath];\n if (typeof data !== 'object' || data === null || Array.isArray(data)) {\n throw new Error(\n `data for route \"${overridePath}\" must be an object, got ${typeof data}`\n );\n }\n dataMap[overridePath] = data as SSRData;\n }\n // In phase 1, routes without data are rendered with no SSR data\n // Phase 2 can add auto-discovery here\n }\n\n return dataMap;\n}\n\nexport function resolveSsgRouteData(\n dataMap: Record<string, SSRData>,\n routePath: string,\n concretePath: string\n): ResolvedSsgRouteData {\n if (hasOwnRouteData(dataMap, concretePath)) {\n return {\n hasData: true,\n data: dataMap[concretePath],\n };\n }\n\n if (hasOwnRouteData(dataMap, routePath)) {\n return {\n hasData: true,\n data: dataMap[routePath],\n };\n }\n\n return {\n hasData: false,\n data: undefined,\n };\n}\n\nfunction isStringRecord(value: unknown): value is Record<string, string> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return false;\n }\n\n for (const recordValue of Object.values(value)) {\n if (typeof recordValue !== 'string') {\n return false;\n }\n }\n\n return true;\n}\n\nfunction validateRouteShape(route: RouteConfig): void {\n if (typeof route.path !== 'string' || !route.path.startsWith('/')) {\n throw new Error(\n `route path must be a string starting with \"/\", got \"${route.path}\"`\n );\n }\n\n const handler = route.handler ?? route.component;\n if (typeof handler !== 'function') {\n throw new Error(\n `route handler must be a function for path \"${route.path}\"`\n );\n }\n\n if (route.params !== undefined && !isStringRecord(route.params)) {\n throw new Error(\n `route \"${route.path}\" params must be an object containing only string values`\n );\n }\n\n if (route.invalidationKeys !== undefined) {\n if (!Array.isArray(route.invalidationKeys)) {\n throw new Error(\n `route \"${route.path}\" invalidationKeys must be an array of strings`\n );\n }\n for (const key of route.invalidationKeys) {\n if (typeof key !== 'string' || key.length === 0) {\n throw new Error(\n `route \"${route.path}\" invalidationKeys must contain only non-empty strings`\n );\n }\n }\n }\n}\n\nfunction validateEntriesResult(\n route: RouteConfig,\n entries: unknown\n): Array<Record<string, string>> {\n if (!Array.isArray(entries)) {\n throw new Error(\n `route \"${route.path}\" entries must return an array of param maps`\n );\n }\n\n return entries.map((entry, index) => {\n if (!isStringRecord(entry)) {\n throw new Error(\n `route \"${route.path}\" entries[${index}] must be an object containing only string values`\n );\n }\n\n return entry;\n });\n}\n\nexport async function expandRoutes(\n routes: RouteConfig[]\n): Promise<RouteConfig[]> {\n if (!Array.isArray(routes)) {\n throw new Error('routes must be an array');\n }\n\n const expanded: RouteConfig[] = [];\n\n for (const route of routes) {\n validateRouteShape(route);\n\n if (route.params) {\n expanded.push({ ...route, entries: undefined });\n }\n\n if (!route.entries) {\n if (!route.params) {\n expanded.push(route);\n }\n continue;\n }\n\n const entries = validateEntriesResult(route, await route.entries());\n for (const params of entries) {\n expanded.push({ ...route, params, entries: undefined });\n }\n }\n\n return expanded;\n}\n\n/**\n * Validate routes are properly configured\n */\nexport function validateRoutes(routes: RouteConfig[]): void {\n if (!Array.isArray(routes)) {\n throw new Error('routes must be an array');\n }\n\n const seen = new Set<string>();\n\n for (const route of routes) {\n validateRouteShape(route);\n\n const key = `${route.path}::${JSON.stringify(route.params || {})}`;\n if (seen.has(key)) {\n throw new Error(\n `duplicate route entry detected for path \"${route.path}\"`\n );\n }\n seen.add(key);\n\n if (route.path.includes('{')) {\n const paramNames = Array.from(route.path.matchAll(/\\{([^}]+)\\}/g)).map(\n (m) => m[1]\n );\n if (!route.params) {\n throw new Error(\n `route \"${route.path}\" uses path parameters and requires params`\n );\n }\n for (const name of paramNames) {\n if (!(name in route.params)) {\n throw new Error(\n `route \"${route.path}\" missing required param \"${name}\"`\n );\n }\n }\n }\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { ComponentFunction } from "../common/component.js";
2
+ import { DocumentRenderer } from "../common/ssr.js";
2
3
  import { RouteAuthMode, RouteHandler, RoutePathParams, RoutePolicy } from "../common/router.js";
3
4
 
4
5
  //#region src/ssg/types.d.ts
@@ -59,6 +60,8 @@ interface SSGOptions<TRoutes extends readonly RouteConfig[] = RouteConfig[]> {
59
60
  seed?: number;
60
61
  /** Optional override data for resources (route-keyed) */
61
62
  dataOverrides?: Record<string, unknown>;
63
+ /** Optional document wrapper for full HTML output */
64
+ document?: DocumentRenderer;
62
65
  /** Optional concurrency limit for rendering (default: 10) */
63
66
  concurrency?: number;
64
67
  /** Preferred render parallelism. `'auto'` resolves from the host machine. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../../src/ssg/types.ts"],"mappings":";;;;KAYY,OAAA;AAAA,KAEA,iBAAA;AAAA,KAEA,iBAAA;AAAA,KAUP,iBAAA,uCAAwD,IAAA,GACzD,MAAA,mBACA,eAAA,CAAgB,IAAA;AAdpB;;;;AAA6B;AAE7B;AAFA,UAsBiB,WAAA;;EAEf,IAAA,EAAM,IAAA;EAtBqB;EAwB3B,OAAA,GAAU,YAAA;EAdU;EAgBpB,SAAA,GAAY,iBAAA;EAhB+C;EAkB3D,KAAA,GAAQ,MAAA;EAhBU;EAkBlB,SAAA;EAlBiB;EAoBjB,IAAA,GAAO,aAAA;EAtBc;EAwBrB,IAAA;EAvBE;EAyBF,UAAA;EAxBkB;EA0BlB,QAAA,YAAoB,WAAA;EA1BE;EA4BtB,MAAA,GAAS,iBAAA,CAAkB,IAAA;EApBD;EAsB1B,gBAAA;EApBM;;;;;;;;;;;;EAiCN,OAAA,SACI,KAAA,CAAM,iBAAA,CAAkB,IAAA,KACxB,OAAA,CAAQ,KAAA,CAAM,iBAAA,CAAkB,IAAA;AAAA;;UAIrB,UAAA,0BACU,WAAA,KAAgB,WAAA;EAL9B;EAQX,MAAA,EAAQ,OAAA;EA3CR;EA6CA,SAAA;EA3CA;EA6CA,IAAA;EA3CA;EA6CA,aAAA,GAAgB,MAAA;EA3ChB;EA6CA,WAAA;EA3CA;EA6CA,WAAA;AAAA;;UAIe,kBAAA;EAzCf;EA2CA,IAAA,GAAO,OAAO;EAzCd;EA2CA,WAAA;EA3C2B;EA6C3B,aAAA;EA9BA;EAgCA,SAAA;AAAA;;UAIe,iBAAA;EAlCH;EAoCZ,IAAA;EApCoC;EAsCpC,QAAA;EAtCwC;EAwCxC,IAAA;EApCyB;EAsCzB,QAAA;EArCyB;EAuCzB,cAAA;EApCQ;EAsCR,aAAA;EAhCsB;EAkCtB,MAAA,EAAQ,iBAAA;EA3CR;EA6CA,MAAA,EAAQ,iBAAiB;EA7CgB;EA+CzC,OAAA;EA5CQ;EA8CR,KAAA;AAAA;;UAIe,SAAA;EA1Cf;EA4CA,WAAA;EA1CW;EA4CX,WAAA;EAxCe;EA0Cf,UAAA;;EAEA,MAAA;EA1CA;EA4CA,aAAA;EA1CA;EA4CA,IAAA,EAAM,OAAA;EAxCN;EA0CA,OAAA;EA1CS;EA4CT,OAAA;EAxCgC;EA0ChC,OAAA;EA1ByB;EA4BzB,SAAA;EAxCA;EA0CA,eAAA;EAtCA;EAwCA,iBAAA;EApCA;EAsCA,MAAA,EAAQ,iBAAiB;AAAA;;UAIV,WAAA,SAAoB,MAAA;EApCnC;EAsCA,WAAA;EApCK;EAsCL,WAAA;EAlCe;EAoCf,UAAA;;EAEA,MAAA;EApCA;EAsCA,aAAA;EAlCA;EAoCA,IAAA,EAAM,OAAA;EAhCN;EAkCA,OAAA;EAhCM;EAkCN,OAAA;EA9BA;EAgCA,OAAA;EA5BA;EA8BA,SAAA;EA1BA;EA4BA,eAAA;EA1BQ;EA4BR,iBAAA;EA5ByB;EA8BzB,MAAA,EAAQ,KAAA;IA1BmB,eA4BzB,IAAA,UAhBI;IAkBJ,QAAA,UAUQ;IARR,QAAA,UAhCiC;IAkCjC,cAAA,UAlCuC;IAoCvC,aAAA,UAlCF;IAoCE,MAAA,EAAQ,iBAAA,EAhCV;IAkCE,MAAA,EAAQ,iBAAA,EA9BV;IAgCE,OAAA,WA9BI;IAgCJ,KAAA;EAAA;AAAA;;UAKa,mBAAA;EAAA,CACd,GAAA;IACC,KAAA;IACA,YAAA;EAAA;AAAA"}
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../src/ssg/types.ts"],"mappings":";;;;;KAaY,OAAA;AAAA,KAEA,iBAAA;AAAA,KAEA,iBAAA;AAAA,KAUP,iBAAA,uCAAwD,IAAA,GACzD,MAAA,mBACA,eAAA,CAAgB,IAAA;AAdpB;;;;AAA6B;AAE7B;AAFA,UAsBiB,WAAA;;EAEf,IAAA,EAAM,IAAA;EAtBqB;EAwB3B,OAAA,GAAU,YAAA;EAdU;EAgBpB,SAAA,GAAY,iBAAA;EAhB+C;EAkB3D,KAAA,GAAQ,MAAA;EAhBU;EAkBlB,SAAA;EAlBiB;EAoBjB,IAAA,GAAO,aAAA;EAtBc;EAwBrB,IAAA;EAvBE;EAyBF,UAAA;EAxBkB;EA0BlB,QAAA,YAAoB,WAAA;EA1BE;EA4BtB,MAAA,GAAS,iBAAA,CAAkB,IAAA;EApBD;EAsB1B,gBAAA;EApBM;;;;;;;;;;;;EAiCN,OAAA,SACI,KAAA,CAAM,iBAAA,CAAkB,IAAA,KACxB,OAAA,CAAQ,KAAA,CAAM,iBAAA,CAAkB,IAAA;AAAA;;UAIrB,UAAA,0BACU,WAAA,KAAgB,WAAA;EAL9B;EAQX,MAAA,EAAQ,OAAA;EA3CR;EA6CA,SAAA;EA3CA;EA6CA,IAAA;EA3CA;EA6CA,aAAA,GAAgB,MAAA;EA3ChB;EA6CA,QAAA,GAAW,gBAAA;EA3CX;EA6CA,WAAA;EA3CO;EA6CP,WAAA;AAAA;;UAIe,kBAAA;EAzCf;EA2CA,IAAA,GAAO,OAAO;EA3Ca;EA6C3B,WAAA;EA9BA;EAgCA,aAAA;EA/BU;EAiCV,SAAA;AAAA;;UAIe,iBAAA;EApCqB;EAsCpC,IAAA;EAtCwC;EAwCxC,QAAA;EApCyB;EAsCzB,IAAA;EArCyB;EAuCzB,QAAA;EApCQ;EAsCR,cAAA;EA9BW;EAgCX,aAAA;EAhC2B;EAkC3B,MAAA,EAAQ,iBAAA;EA7CiB;EA+CzB,MAAA,EAAQ,iBAAiB;EA5CzB;EA8CA,OAAA;EA5CA;EA8CA,KAAA;AAAA;;UAIe,SAAA;EA5CJ;EA8CX,WAAA;EA1CA;EA4CA,WAAA;EA5CW;EA8CX,UAAA;EA1CiC;EA4CjC,MAAA;EA1Cc;EA4Cd,aAAA;EA5CO;EA8CP,IAAA,EAAM,OAAA;EA1CN;EA4CA,OAAA;EA1CS;EA4CT,OAAA;EAxCe;EA0Cf,OAAA;;EAEA,SAAA;EA1CA;EA4CA,eAAA;EAxCA;EA0CA,iBAAA;EAtCA;EAwCA,MAAA,EAAQ,iBAAiB;AAAA;;UAIV,WAAA,SAAoB,MAAA;EAtC3B;EAwCR,WAAA;EApCA;EAsCA,WAAA;EAtCK;EAwCL,UAAA;EApCwB;EAsCxB,MAAA;EAZyB;EAczB,aAAA;EApCA;EAsCA,IAAA,EAAM,OAAA;EAlCN;EAoCA,OAAA;EAhCA;EAkCA,OAAA;EAhCA;EAkCA,OAAA;EA9BA;EAgCA,SAAA;EA5BA;EA8BA,eAAA;EA1BA;EA4BA,iBAAA;EA5ByB;EA8BzB,MAAA,EAAQ,KAAA;IA1BO,eA4Bb,IAAA,UA5ByB;IA8BzB,QAAA,UAQQ;IANR,QAAA,UANM;IAQN,cAAA,UAlCuC;IAoCvC,aAAA,UApCiC;IAsCjC,MAAA,EAAQ,iBAAA,EAlCV;IAoCE,MAAA,EAAQ,iBAAA,EAhCV;IAkCE,OAAA,WA9BF;IAgCE,KAAA;EAAA;AAAA;;UAKa,mBAAA;EAAA,CACd,GAAA;IACC,KAAA;IACA,YAAA;EAAA;AAAA"}
@@ -1,7 +1,6 @@
1
+ import { SSRData } from "../common/ssr.js";
1
2
  import { Route, RouteAuthOptions } from "../common/router.js";
2
3
  import { SSRDataMissingError } from "../common/ssr-errors.js";
3
- import { SSRData } from "../common/ssr.js";
4
-
5
4
  //#region src/ssr/context.d.ts
6
5
  interface RenderContext {
7
6
  url: string;
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","names":[],"sources":["../../src/ssr/context.ts"],"mappings":";;;;;UAgBiB,aAAA;EACf,GAAA;EACA,IAAA;EACA,IAAA,GAAO,OAAA;EACP,MAAA,GAAS,MAAA;EACT,MAAA,YAAkB,KAAA;EAClB,SAAA,GAAY,gBAAA;EACZ,MAAA,GAAS,WAAA;EACT,UAAA,GAAa,GAAA;EACb,aAAA,EAAe,KAAA;EAEf,UAAA;EACA,UAAA,EAAY,MAAA;AAAA;AAAA,KAIF,UAAA,GAAa,aAAa;AAAA,iBAoDtB,mBAAA,CACd,IAAA,WACA,IAAA;EACE,GAAA;EACA,IAAA,GAAO,OAAA;EACP,MAAA,GAAS,MAAA;EACT,MAAA,YAAkB,KAAA;EAClB,SAAA,GAAY,gBAAA;EACZ,MAAA,GAAS,WAAA;AAAA,IAEV,aAAA;;;;;iBAsBa,iBAAA,IAAqB,GAAA,EAAK,aAAA,EAAe,EAAA,QAAU,CAAA,GAAI,CAAA;;AAxFnD;AAIpB;;iBAuGgB,gBAAA,IAAoB,aAAa;AAAA,cASpC,aAAA,SAAa,gBAAmB;AAAA,cAChC,cAAA,SAAc,iBAAoB;AAAA,cAClC,oBAAA,SAAoB,gBAAmB;AAAA,iBAEpC,iBAAA,IAAqB,GAAA,EAAK,aAAA,EAAe,EAAA,QAAU,CAAA,GAAI,CAAA;;;;;iBASvD,mBAAA"}
1
+ {"version":3,"file":"context.d.ts","names":[],"sources":["../../src/ssr/context.ts"],"mappings":";;;;UAgBiB,aAAA;EACf,GAAA;EACA,IAAA;EACA,IAAA,GAAO,OAAA;EACP,MAAA,GAAS,MAAA;EACT,MAAA,YAAkB,KAAA;EAClB,SAAA,GAAY,gBAAA;EACZ,MAAA,GAAS,WAAA;EACT,UAAA,GAAa,GAAA;EACb,aAAA,EAAe,KAAA;EAEf,UAAA;EACA,UAAA,EAAY,MAAA;AAAA;AAAA,KAIF,UAAA,GAAa,aAAa;AAAA,iBAoDtB,mBAAA,CACd,IAAA,WACA,IAAA;EACE,GAAA;EACA,IAAA,GAAO,OAAA;EACP,MAAA,GAAS,MAAA;EACT,MAAA,YAAkB,KAAA;EAClB,SAAA,GAAY,gBAAA;EACZ,MAAA,GAAS,WAAA;AAAA,IAEV,aAAA;;;;;iBAsBa,iBAAA,IAAqB,GAAA,EAAK,aAAA,EAAe,EAAA,QAAU,CAAA,GAAI,CAAA;;AAxFnD;AAIpB;;iBAuGgB,gBAAA,IAAoB,aAAa;AAAA,cASpC,aAAA,SAAa,gBAAmB;AAAA,cAChC,cAAA,SAAc,iBAAoB;AAAA,cAClC,oBAAA,SAAoB,gBAAmB;AAAA,iBAEpC,iBAAA,IAAqB,GAAA,EAAK,aAAA,EAAe,EAAA,QAAU,CAAA,GAAI,CAAA;;;;;iBASvD,mBAAA"}
@@ -1,7 +1,7 @@
1
1
  import { JSXElement } from "../common/jsx.js";
2
+ import { DocumentRenderArgs, DocumentRenderContext, DocumentRenderer, SSRData } from "../common/ssr.js";
2
3
  import { RouteAuthOptions, RouteHandler, RouteManifest, RouteRequestResult } from "../common/router.js";
3
4
  import { SSRDataMissingError } from "../common/ssr-errors.js";
4
- import { SSRData } from "../common/ssr.js";
5
5
  import { SSRComponent, VNode } from "./types.js";
6
6
  import { renderResolvedToStringSync } from "./render-resolved.js";
7
7
 
@@ -41,21 +41,20 @@ type SSRRoute = {
41
41
  handler: RouteHandler;
42
42
  namespace?: string;
43
43
  };
44
- declare function renderToString(component: (props?: Record<string, unknown>) => VNode | JSXElement | string | number | null): string;
45
- declare function renderToString(opts: {
46
- url: string;
47
- routes: SSRRoute[];
48
- seed?: number;
49
- data?: SSRData;
50
- }): string;
51
- declare function renderToStream(opts: {
44
+ type RouteRenderOptions = {
52
45
  url: string;
53
46
  routes: SSRRoute[];
54
47
  seed?: number;
55
48
  data?: SSRData;
49
+ document?: DocumentRenderer;
50
+ };
51
+ type RouteStreamOptions = RouteRenderOptions & {
56
52
  onChunk(html: string): void;
57
53
  onComplete(): void;
58
- }): void;
54
+ };
55
+ declare function renderToString(component: (props?: Record<string, unknown>) => VNode | JSXElement | string | number | null): string;
56
+ declare function renderToString(opts: RouteRenderOptions): string;
57
+ declare function renderToStream(opts: RouteStreamOptions): void;
59
58
  //#endregion
60
- export { type SSRComponent, SSRDataMissingError, SSRRoute, type VNode, renderResolvedToStringSync, renderToStream, renderToString, renderToStringSync, resolveRequest };
59
+ export { type DocumentRenderArgs, type DocumentRenderContext, type DocumentRenderer, type SSRComponent, SSRDataMissingError, SSRRoute, type VNode, renderResolvedToStringSync, renderToStream, renderToString, renderToStringSync, resolveRequest };
61
60
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/ssr/index.ts"],"mappings":";;;;;;;;;;;;;iBAusCgB,kBAAA,CACd,SAAA,GACE,KAAA,GAAQ,MAAA,sBACL,KAAA,GAAQ,UAAA,iDACb,KAAA,GAAQ,MAAA,mBACR,OAAA;EAAY,IAAA;EAAe,IAAA,GAAO,OAAA;AAAA;AAAA,iBAgCd,cAAA,CACpB,IAAA;EAEM,GAAA;EACA,QAAA,EAAU,aAAA;EACV,MAAA,GAAS,KAAA;IACP,IAAA;IACA,OAAA,EAAS,YAAA;IACT,SAAA;EAAA;EAEF,IAAA,GAAO,gBAAA;EACP,MAAA,GAAS,WAAA;AAAA;EAGT,GAAA;EACA,QAAA,GAAW,aAAA;EACX,MAAA,EAAQ,KAAA;IACN,IAAA;IACA,OAAA,EAAS,YAAA;IACT,SAAA;EAAA;EAEF,IAAA,GAAO,gBAAA;EACP,MAAA,GAAS,WAAA;AAAA,IAEd,OAAA,CAAQ,kBAAA;AAAA,KAsCC,QAAA;EACV,IAAA;EACA,OAAA,EAAS,YAAY;EACrB,SAAA;AAAA;AAAA,iBAGc,cAAA,CACd,SAAA,GACE,KAAA,GAAQ,MAAA,sBACL,KAAA,GAAQ,UAAA;AAAA,iBAEC,cAAA,CAAe,IAAA;EAC7B,GAAA;EACA,MAAA,EAAQ,QAAA;EACR,IAAA;EACA,IAAA,GAAO,OAAO;AAAA;AAAA,iBAuBA,cAAA,CAAe,IAAA;EAC7B,GAAA;EACA,MAAA,EAAQ,QAAA;EACR,IAAA;EACA,IAAA,GAAO,OAAO;EACd,OAAA,CAAQ,IAAA;EACR,UAAA;AAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/ssr/index.ts"],"mappings":";;;;;;;;;;;;;iBAosCgB,kBAAA,CACd,SAAA,GACE,KAAA,GAAQ,MAAA,sBACL,KAAA,GAAQ,UAAA,iDACb,KAAA,GAAQ,MAAA,mBACR,OAAA;EAAY,IAAA;EAAe,IAAA,GAAO,OAAA;AAAA;AAAA,iBAgCd,cAAA,CACpB,IAAA;EAEM,GAAA;EACA,QAAA,EAAU,aAAA;EACV,MAAA,GAAS,KAAA;IACP,IAAA;IACA,OAAA,EAAS,YAAA;IACT,SAAA;EAAA;EAEF,IAAA,GAAO,gBAAA;EACP,MAAA,GAAS,WAAA;AAAA;EAGT,GAAA;EACA,QAAA,GAAW,aAAA;EACX,MAAA,EAAQ,KAAA;IACN,IAAA;IACA,OAAA,EAAS,YAAA;IACT,SAAA;EAAA;EAEF,IAAA,GAAO,gBAAA;EACP,MAAA,GAAS,WAAA;AAAA,IAEd,OAAA,CAAQ,kBAAA;AAAA,KAsCC,QAAA;EACV,IAAA;EACA,OAAA,EAAS,YAAY;EACrB,SAAA;AAAA;AAAA,KAGG,kBAAA;EACH,GAAA;EACA,MAAA,EAAQ,QAAA;EACR,IAAA;EACA,IAAA,GAAO,OAAA;EACP,QAAA,GAAW,gBAAA;AAAA;AAAA,KAGR,kBAAA,GAAqB,kBAAkB;EAC1C,OAAA,CAAQ,IAAA;EACR,UAAA;AAAA;AAAA,iBAiGc,cAAA,CACd,SAAA,GACE,KAAA,GAAQ,MAAA,sBACL,KAAA,GAAQ,UAAA;AAAA,iBAEC,cAAA,CAAe,IAAwB,EAAlB,kBAAkB;AAAA,iBAiBvC,cAAA,CAAe,IAAwB,EAAlB,kBAAkB"}
package/dist/ssr/index.js CHANGED
@@ -10,10 +10,10 @@ import { evaluateCaseState, evaluateShowState } from "../runtime/control.js";
10
10
  import { SSRDataMissingError } from "../common/ssr-errors.js";
11
11
  import { VOID_ELEMENTS, escapeText, styleObjToCss } from "./escape.js";
12
12
  import { createRenderContext, getRenderContext, throwSSRDataMissing, withRenderContext } from "./context.js";
13
- import { resolveRouteFromRoutes, resolveRouteRequest } from "../router/route.js";
13
+ import { _resolveRouteMatchFromRoutes, resolveRouteFromRoutes, resolveRouteRequest } from "../router/route.js";
14
14
  import "../jsx/index.js";
15
15
  import { DefaultPortal, disposeDefaultPortalScope } from "../foundations/structures/portal.js";
16
- import { SSR_RENDER_DATA_ATTR } from "../common/ssr.js";
16
+ import { SSR_RENDER_DATA_ATTR, renderDocument } from "../common/ssr.js";
17
17
  import { getCurrentRenderData, getNextKey, startRenderPhase, stopRenderPhase } from "./render-keys.js";
18
18
  import { installSSRBridge } from "../runtime/ssr-bridge.js";
19
19
  import { renderAttrs, renderAttrsDirect } from "./attrs.js";
@@ -65,24 +65,35 @@ function popSSRStrictPurityGuard() {
65
65
  /**
66
66
  * Synchronous rendering helpers (used for strictly synchronous SSR)
67
67
  */
68
- function renderChildSync(child, ctx) {
69
- if (typeof child === "string") return escapeText(child);
70
- if (typeof child === "number") return escapeText(String(child));
71
- if (child === null || child === void 0 || child === false) return "";
72
- if (child && typeof child === "object" && "type" in child) return renderNodeSync(child, ctx);
68
+ function renderRenderableSync(value, ctx) {
69
+ if (typeof value === "string") return escapeText(value);
70
+ if (typeof value === "number") return escapeText(String(value));
71
+ if (value === null || value === void 0 || value === false) return "";
72
+ if (Array.isArray(value)) return renderChildrenSync(value, ctx);
73
+ if (value && typeof value === "object" && "type" in value) return renderNodeSync(value, ctx);
73
74
  return "";
74
75
  }
75
- function renderChildSyncToSink(child, sink, ctx) {
76
- if (child === null || child === void 0 || child === false) return;
77
- if (typeof child === "string") {
78
- sink.write(escapeText(child));
76
+ function renderChildSync(child, ctx) {
77
+ return renderRenderableSync(child, ctx);
78
+ }
79
+ function renderRenderableSyncToSink(value, sink, ctx) {
80
+ if (value === null || value === void 0 || value === false) return;
81
+ if (typeof value === "string") {
82
+ sink.write(escapeText(value));
79
83
  return;
80
84
  }
81
- if (typeof child === "number") {
82
- sink.write(escapeText(String(child)));
85
+ if (typeof value === "number") {
86
+ sink.write(escapeText(String(value)));
83
87
  return;
84
88
  }
85
- if (child && typeof child === "object" && "type" in child) renderNodeSyncToSink(child, sink, ctx);
89
+ if (Array.isArray(value)) {
90
+ renderChildrenSyncToSink(value, sink, ctx);
91
+ return;
92
+ }
93
+ if (value && typeof value === "object" && "type" in value) renderNodeSyncToSink(value, sink, ctx);
94
+ }
95
+ function renderChildSyncToSink(child, sink, ctx) {
96
+ renderRenderableSyncToSink(child, sink, ctx);
86
97
  }
87
98
  function serializeHydrationRenderData(data) {
88
99
  if (!data || Object.keys(data).length === 0) return "";
@@ -195,19 +206,7 @@ function evaluateControlBoundaryChildren(node) {
195
206
  function renderChildrenSyncToSink(children, sink, ctx) {
196
207
  if (!children || !Array.isArray(children) || children.length === 0) return;
197
208
  if (children.length >= 32) {
198
- for (let i = 0; i < children.length; i++) {
199
- const child = children[i];
200
- if (child === null || child === void 0 || child === false) continue;
201
- if (typeof child === "string") {
202
- sink.write(escapeText(child));
203
- continue;
204
- }
205
- if (typeof child === "number") {
206
- sink.write(escapeText(String(child)));
207
- continue;
208
- }
209
- if (child && typeof child === "object" && "type" in child) renderNodeSyncToSink(child, sink, ctx);
210
- }
209
+ for (let i = 0; i < children.length; i++) renderRenderableSyncToSink(children[i], sink, ctx);
211
210
  return;
212
211
  }
213
212
  for (let i = 0; i < children.length; i++) renderChildSyncToSink(children[i], sink, ctx);
@@ -238,14 +237,10 @@ function renderNodeSync(node, ctx) {
238
237
  if (__SSR_DEBUG) try {
239
238
  logger.warn("[SSR] renderNodeSync type:", typeof type, type);
240
239
  } catch {}
241
- if (typeof type === "function") {
242
- const result = executeComponentSync(type, props, ctx);
243
- if (isPromiseLike(result)) throwSSRDataMissing();
244
- return renderNodeSync(result, ctx);
245
- }
240
+ if (typeof type === "function") return renderRenderableSync(inheritRenderableKey(node, executeComponentSync(type, props, ctx)), ctx);
246
241
  if (typeof type === "symbol") {
247
242
  if (type === Fragment) {
248
- const childrenArr = Array.isArray(node.children) ? node.children : Array.isArray(props?.children) ? props?.children : void 0;
243
+ const childrenArr = getRenderableChildren(node);
249
244
  /* istanbul ignore if - dev-only debug */
250
245
  if (__SSR_DEBUG) try {
251
246
  logger.warn("[SSR] fragment children length:", childrenArr?.length);
@@ -283,19 +278,19 @@ function renderNodeSync(node, ctx) {
283
278
  if (maybeDangerous !== void 0 && maybeDangerous !== null) {
284
279
  const { attrs, dangerousHtml } = renderAttrs(props, { returnDangerousHtml: true });
285
280
  if (dangerousHtml !== void 0) return `<${typeStr}${attrs}>${dangerousHtml}</${typeStr}>`;
286
- return `<${typeStr}${attrs}>${renderChildrenSync(node.children, ctx)}</${typeStr}>`;
281
+ return `<${typeStr}${attrs}>${renderChildrenSync(getRenderableChildren(node), ctx)}</${typeStr}>`;
287
282
  }
288
- return `<${typeStr}${renderAttrs(props)}>${renderChildrenSync(node.children, ctx)}</${typeStr}>`;
283
+ return `<${typeStr}${renderAttrs(props)}>${renderChildrenSync(getRenderableChildren(node), ctx)}</${typeStr}>`;
289
284
  }
290
285
  function renderNodeSyncToSink(node, sink, ctx) {
291
286
  const { type, props } = node;
292
287
  if (typeof type === "function") {
293
- renderNodeSyncToSink(inheritRenderableKey(node, executeComponentSync(type, props, ctx)), sink, ctx);
288
+ renderRenderableSyncToSink(inheritRenderableKey(node, executeComponentSync(type, props, ctx)), sink, ctx);
294
289
  return;
295
290
  }
296
291
  if (typeof type === "symbol") {
297
292
  if (type === Fragment) {
298
- renderChildrenSyncToSink(Array.isArray(node.children) ? node.children : Array.isArray(props?.children) ? props?.children : void 0, sink, ctx);
293
+ renderChildrenSyncToSink(getRenderableChildren(node), sink, ctx);
299
294
  return;
300
295
  }
301
296
  if (type === __CONTROL_BOUNDARY__) {
@@ -344,16 +339,11 @@ function renderNodeSyncToSink(node, sink, ctx) {
344
339
  renderAttrsDirect(props, sink);
345
340
  sink.write(">");
346
341
  if (dangerousHtml !== void 0) sink.write(dangerousHtml);
347
- else renderChildrenSyncToSink(node.children, sink, ctx);
342
+ else renderChildrenSyncToSink(getRenderableChildren(node), sink, ctx);
348
343
  sinkWrite3(sink, "</", typeStr, ">");
349
344
  return;
350
345
  }
351
- let children = node.children;
352
- if (children === void 0 && props?.children !== void 0) {
353
- const propsChildren = props.children;
354
- if (Array.isArray(propsChildren)) children = propsChildren;
355
- else if (propsChildren !== null && propsChildren !== false) children = [propsChildren];
356
- }
346
+ const children = getRenderableChildren(node);
357
347
  if (!children || Array.isArray(children) && children.length === 0) {
358
348
  sinkWrite2(sink, "<", typeStr);
359
349
  renderAttrsDirect(props, sink);
@@ -532,10 +522,11 @@ function verifyExpectedNode(node, state, ctx) {
532
522
  state.pendingText += String(node);
533
523
  return true;
534
524
  }
525
+ if (Array.isArray(node)) return verifyExpectedChildren(node, state, ctx);
535
526
  if (!node || typeof node !== "object" || !("type" in node)) return true;
536
527
  const vnode = node;
537
528
  const { type, props } = vnode;
538
- if (typeof type === "function") return verifyExpectedNode(inheritRenderableKey(vnode, executeComponentSync(type, props, ctx)), state, ctx);
529
+ if (typeof type === "function") return verifyRenderableNode(inheritRenderableKey(vnode, executeComponentSync(type, props, ctx)), state, ctx);
539
530
  if (typeof type === "symbol") {
540
531
  if (type === __CONTROL_BOUNDARY__) {
541
532
  const children = evaluateControlBoundaryChildren(vnode);
@@ -655,6 +646,64 @@ async function resolveRequest(opts) {
655
646
  params: resolved.params
656
647
  };
657
648
  }
649
+ function resolveSSRRouteRender(opts) {
650
+ const { url, routes, seed = 12345, data, document } = opts;
651
+ const routeTable = routes.map((route) => ({ ...route }));
652
+ const requestUrl = new URL(url, "http://localhost");
653
+ const matched = _resolveRouteMatchFromRoutes(requestUrl.pathname, routeTable);
654
+ if (!matched) throw new Error(`SSR: no route found for url: ${url}`);
655
+ const ctx = createRenderContext(seed, {
656
+ url,
657
+ data,
658
+ params: matched.params,
659
+ routes: routeTable
660
+ });
661
+ return {
662
+ url,
663
+ requestUrl,
664
+ route: matched.route,
665
+ params: matched.params,
666
+ seed,
667
+ data,
668
+ ctx,
669
+ document
670
+ };
671
+ }
672
+ function buildDocumentRenderArgs(resolved, appHtml) {
673
+ return {
674
+ appHtml,
675
+ context: {
676
+ mode: "ssr",
677
+ url: resolved.url,
678
+ pathname: resolved.requestUrl.pathname,
679
+ search: resolved.requestUrl.search,
680
+ hash: resolved.requestUrl.hash,
681
+ params: resolved.params,
682
+ data: resolved.data,
683
+ seed: resolved.seed,
684
+ route: {
685
+ path: resolved.route.path,
686
+ namespace: resolved.route.namespace
687
+ }
688
+ }
689
+ };
690
+ }
691
+ function renderResolvedRouteAppToSink(resolved, sink) {
692
+ const { ctx, data, route, params } = resolved;
693
+ withRenderContext(ctx, () => {
694
+ startRenderPhase(data || null);
695
+ try {
696
+ renderRenderableSyncToSink(executeComponentSync(route.handler, params, ctx), sink, ctx);
697
+ sink.write(serializeHydrationRenderData(data));
698
+ } finally {
699
+ try {
700
+ stopRenderPhase();
701
+ } finally {
702
+ disposeSSRTemporaryOwners(ctx);
703
+ }
704
+ }
705
+ });
706
+ }
658
707
  function renderToString(arg) {
659
708
  if (typeof arg === "function") return renderToStringSync(arg);
660
709
  const opts = arg;
@@ -675,29 +724,16 @@ function renderToStream(opts) {
675
724
  sink.end();
676
725
  }
677
726
  function renderToSinkInternal(opts) {
678
- const { url, routes, seed = 12345, data, sink } = opts;
679
- const routeTable = routes.map((route) => ({ ...route }));
680
- const resolved = resolveRouteFromRoutes(new URL(url, "http://localhost").pathname, routeTable);
681
- if (!resolved) throw new Error(`SSR: no route found for url: ${url}`);
682
- const ctx = createRenderContext(seed, {
683
- url,
684
- data,
685
- params: resolved.params,
686
- routes: routeTable
687
- });
688
- withRenderContext(ctx, () => {
689
- startRenderPhase(data || null);
690
- try {
691
- renderNodeSyncToSink(executeComponentSync(resolved.handler, resolved.params, ctx), sink, ctx);
692
- sink.write(serializeHydrationRenderData(data));
693
- } finally {
694
- try {
695
- stopRenderPhase();
696
- } finally {
697
- disposeSSRTemporaryOwners(ctx);
698
- }
699
- }
700
- });
727
+ const { sink, ...renderOptions } = opts;
728
+ const resolved = resolveSSRRouteRender(renderOptions);
729
+ if (!resolved.document) {
730
+ renderResolvedRouteAppToSink(resolved, sink);
731
+ return;
732
+ }
733
+ const appSink = new StringSink();
734
+ renderResolvedRouteAppToSink(resolved, appSink);
735
+ appSink.end();
736
+ sink.write(renderDocument(resolved.document, buildDocumentRenderArgs(resolved, appSink.toString()), "renderToString()/renderToStream()"));
701
737
  }
702
738
  //#endregion
703
739
  export { SSRDataMissingError, renderResolvedToStringSync, renderToStream, renderToString, renderToStringSync, resolveRequest };