@jasonshimmy/vite-plugin-cer-app 0.28.2 → 0.29.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.
Files changed (184) hide show
  1. package/README.md +2 -0
  2. package/dist/cli/adapters/cloudflare.d.ts +2 -2
  3. package/dist/cli/adapters/cloudflare.d.ts.map +1 -1
  4. package/dist/cli/adapters/cloudflare.js +83 -159
  5. package/dist/cli/adapters/cloudflare.js.map +1 -1
  6. package/dist/cli/adapters/deployment-mode.d.ts +10 -0
  7. package/dist/cli/adapters/deployment-mode.d.ts.map +1 -0
  8. package/dist/cli/adapters/deployment-mode.js +59 -0
  9. package/dist/cli/adapters/deployment-mode.js.map +1 -0
  10. package/dist/cli/adapters/netlify.d.ts +5 -4
  11. package/dist/cli/adapters/netlify.d.ts.map +1 -1
  12. package/dist/cli/adapters/netlify.js +26 -150
  13. package/dist/cli/adapters/netlify.js.map +1 -1
  14. package/dist/cli/adapters/vercel.d.ts +2 -3
  15. package/dist/cli/adapters/vercel.d.ts.map +1 -1
  16. package/dist/cli/adapters/vercel.js +36 -111
  17. package/dist/cli/adapters/vercel.js.map +1 -1
  18. package/dist/cli/adapters/web-bridge.d.ts +3 -0
  19. package/dist/cli/adapters/web-bridge.d.ts.map +1 -0
  20. package/dist/cli/adapters/web-bridge.js +110 -0
  21. package/dist/cli/adapters/web-bridge.js.map +1 -0
  22. package/dist/cli/checks/lighthouse.d.ts +7 -1
  23. package/dist/cli/checks/lighthouse.d.ts.map +1 -1
  24. package/dist/cli/checks/lighthouse.js +49 -44
  25. package/dist/cli/checks/lighthouse.js.map +1 -1
  26. package/dist/cli/checks/links.js.map +1 -1
  27. package/dist/cli/checks/performance.d.ts +3 -0
  28. package/dist/cli/checks/performance.d.ts.map +1 -1
  29. package/dist/cli/checks/performance.js +56 -15
  30. package/dist/cli/checks/performance.js.map +1 -1
  31. package/dist/cli/checks/quality.d.ts +35 -0
  32. package/dist/cli/checks/quality.d.ts.map +1 -0
  33. package/dist/cli/checks/quality.js +72 -0
  34. package/dist/cli/checks/quality.js.map +1 -0
  35. package/dist/cli/checks/seo.d.ts +9 -0
  36. package/dist/cli/checks/seo.d.ts.map +1 -0
  37. package/dist/cli/checks/seo.js +40 -0
  38. package/dist/cli/checks/seo.js.map +1 -0
  39. package/dist/cli/commands/adapt.js.map +1 -1
  40. package/dist/cli/commands/build.d.ts.map +1 -1
  41. package/dist/cli/commands/build.js +2 -51
  42. package/dist/cli/commands/build.js.map +1 -1
  43. package/dist/cli/commands/check.d.ts.map +1 -1
  44. package/dist/cli/commands/check.js +12 -0
  45. package/dist/cli/commands/check.js.map +1 -1
  46. package/dist/cli/commands/dev.d.ts +4 -0
  47. package/dist/cli/commands/dev.d.ts.map +1 -1
  48. package/dist/cli/commands/dev.js +1 -72
  49. package/dist/cli/commands/dev.js.map +1 -1
  50. package/dist/cli/commands/generate.d.ts +1 -4
  51. package/dist/cli/commands/generate.d.ts.map +1 -1
  52. package/dist/cli/commands/generate.js +4 -73
  53. package/dist/cli/commands/generate.js.map +1 -1
  54. package/dist/cli/commands/preview-paths.d.ts +3 -0
  55. package/dist/cli/commands/preview-paths.d.ts.map +1 -0
  56. package/dist/cli/commands/preview-paths.js +8 -0
  57. package/dist/cli/commands/preview-paths.js.map +1 -0
  58. package/dist/cli/commands/preview.d.ts +11 -0
  59. package/dist/cli/commands/preview.d.ts.map +1 -1
  60. package/dist/cli/commands/preview.js +124 -458
  61. package/dist/cli/commands/preview.js.map +1 -1
  62. package/dist/cli/config.d.ts +3 -0
  63. package/dist/cli/config.d.ts.map +1 -0
  64. package/dist/cli/config.js +68 -0
  65. package/dist/cli/config.js.map +1 -0
  66. package/dist/cli/create/index.js.map +1 -1
  67. package/dist/cli/create/templates/spa/package.json.tpl +1 -1
  68. package/dist/cli/create/templates/ssg/package.json.tpl +1 -1
  69. package/dist/cli/create/templates/ssr/package.json.tpl +1 -1
  70. package/dist/plugin/build-ssg.d.ts.map +1 -1
  71. package/dist/plugin/build-ssg.js +81 -15
  72. package/dist/plugin/build-ssg.js.map +1 -1
  73. package/dist/plugin/build-ssr.d.ts.map +1 -1
  74. package/dist/plugin/build-ssr.js +3 -1
  75. package/dist/plugin/build-ssr.js.map +1 -1
  76. package/dist/plugin/content/scanner.d.ts.map +1 -1
  77. package/dist/plugin/content/scanner.js +2 -7
  78. package/dist/plugin/content/scanner.js.map +1 -1
  79. package/dist/plugin/dev-server.d.ts +8 -0
  80. package/dist/plugin/dev-server.d.ts.map +1 -1
  81. package/dist/plugin/dev-server.js.map +1 -1
  82. package/dist/plugin/dts-generator.d.ts.map +1 -1
  83. package/dist/plugin/dts-generator.js +4 -1
  84. package/dist/plugin/dts-generator.js.map +1 -1
  85. package/dist/plugin/file-scanner.d.ts +4 -0
  86. package/dist/plugin/file-scanner.d.ts.map +1 -0
  87. package/dist/plugin/file-scanner.js +59 -0
  88. package/dist/plugin/file-scanner.js.map +1 -0
  89. package/dist/plugin/html-post-process.d.ts +1 -1
  90. package/dist/plugin/html-post-process.d.ts.map +1 -1
  91. package/dist/plugin/html-post-process.js +7 -5
  92. package/dist/plugin/html-post-process.js.map +1 -1
  93. package/dist/plugin/index.d.ts +1 -4
  94. package/dist/plugin/index.d.ts.map +1 -1
  95. package/dist/plugin/index.js +26 -8
  96. package/dist/plugin/index.js.map +1 -1
  97. package/dist/plugin/path-utils.js.map +1 -1
  98. package/dist/plugin/scanner.d.ts +2 -2
  99. package/dist/plugin/scanner.d.ts.map +1 -1
  100. package/dist/plugin/scanner.js +4 -10
  101. package/dist/plugin/scanner.js.map +1 -1
  102. package/dist/plugin/server-content.d.ts +3 -0
  103. package/dist/plugin/server-content.d.ts.map +1 -0
  104. package/dist/plugin/server-content.js +25 -0
  105. package/dist/plugin/server-content.js.map +1 -0
  106. package/dist/plugin/transforms/auto-import.d.ts.map +1 -1
  107. package/dist/plugin/transforms/auto-import.js +6 -5
  108. package/dist/plugin/transforms/auto-import.js.map +1 -1
  109. package/dist/plugin/transforms/head-inject.d.ts.map +1 -1
  110. package/dist/plugin/virtual/composables.js +1 -1
  111. package/dist/plugin/virtual/composables.js.map +1 -1
  112. package/dist/plugin/virtual/content-components.d.ts +3 -1
  113. package/dist/plugin/virtual/content-components.d.ts.map +1 -1
  114. package/dist/plugin/virtual/content-components.js +105 -22
  115. package/dist/plugin/virtual/content-components.js.map +1 -1
  116. package/dist/plugin/virtual/layouts.js +1 -1
  117. package/dist/plugin/virtual/layouts.js.map +1 -1
  118. package/dist/plugin/virtual/middleware.js +1 -1
  119. package/dist/plugin/virtual/middleware.js.map +1 -1
  120. package/dist/plugin/virtual/plugins.js +1 -1
  121. package/dist/plugin/virtual/plugins.js.map +1 -1
  122. package/dist/plugin/virtual/routes.js +1 -1
  123. package/dist/plugin/virtual/routes.js.map +1 -1
  124. package/dist/plugin/virtual/server-api.js +1 -1
  125. package/dist/plugin/virtual/server-api.js.map +1 -1
  126. package/dist/plugin/virtual/server-middleware.js +1 -1
  127. package/dist/plugin/virtual/server-middleware.js.map +1 -1
  128. package/dist/runtime/app-template.d.ts.map +1 -1
  129. package/dist/runtime/app-template.js +18 -8
  130. package/dist/runtime/app-template.js.map +1 -1
  131. package/dist/runtime/composables/index.d.ts +2 -0
  132. package/dist/runtime/composables/index.d.ts.map +1 -1
  133. package/dist/runtime/composables/index.js +1 -0
  134. package/dist/runtime/composables/index.js.map +1 -1
  135. package/dist/runtime/composables/render-content.d.ts +10 -0
  136. package/dist/runtime/composables/render-content.d.ts.map +1 -0
  137. package/dist/runtime/composables/render-content.js +21 -0
  138. package/dist/runtime/composables/render-content.js.map +1 -0
  139. package/dist/runtime/composables/use-active-headings.js.map +1 -1
  140. package/dist/runtime/composables/use-auth.d.ts.map +1 -1
  141. package/dist/runtime/composables/use-auth.js +14 -6
  142. package/dist/runtime/composables/use-auth.js.map +1 -1
  143. package/dist/runtime/composables/use-content-page.d.ts +2 -0
  144. package/dist/runtime/composables/use-content-page.d.ts.map +1 -1
  145. package/dist/runtime/composables/use-content-page.js +1 -1
  146. package/dist/runtime/composables/use-content-page.js.map +1 -1
  147. package/dist/runtime/composables/use-content.d.ts.map +1 -1
  148. package/dist/runtime/composables/use-content.js.map +1 -1
  149. package/dist/runtime/composables/use-cookie.js.map +1 -1
  150. package/dist/runtime/composables/use-runtime-config.js.map +1 -1
  151. package/dist/runtime/composables/use-session.js.map +1 -1
  152. package/dist/runtime/content/client.d.ts +2 -3
  153. package/dist/runtime/content/client.d.ts.map +1 -1
  154. package/dist/runtime/content/client.js +4 -5
  155. package/dist/runtime/content/client.js.map +1 -1
  156. package/dist/runtime/entry-server-template.d.ts.map +1 -1
  157. package/dist/runtime/entry-server-template.js +45 -15
  158. package/dist/runtime/entry-server-template.js.map +1 -1
  159. package/dist/runtime/isr-handler.d.ts +4 -15
  160. package/dist/runtime/isr-handler.d.ts.map +1 -1
  161. package/dist/runtime/isr-handler.js +64 -45
  162. package/dist/runtime/isr-handler.js.map +1 -1
  163. package/dist/runtime/request-dispatcher.d.ts +31 -0
  164. package/dist/runtime/request-dispatcher.d.ts.map +1 -0
  165. package/dist/runtime/request-dispatcher.js +152 -0
  166. package/dist/runtime/request-dispatcher.js.map +1 -0
  167. package/dist/runtime/route-matching.d.ts +20 -0
  168. package/dist/runtime/route-matching.d.ts.map +1 -0
  169. package/dist/runtime/route-matching.js +45 -0
  170. package/dist/runtime/route-matching.js.map +1 -0
  171. package/dist/types/api.d.ts +3 -0
  172. package/dist/types/api.d.ts.map +1 -1
  173. package/dist/types/config.d.ts +27 -0
  174. package/dist/types/config.d.ts.map +1 -1
  175. package/dist/types/config.js.map +1 -1
  176. package/dist/types/content.d.ts +2 -0
  177. package/dist/types/content.d.ts.map +1 -1
  178. package/dist/types/page.d.ts +11 -7
  179. package/dist/types/page.d.ts.map +1 -1
  180. package/package.json +30 -16
  181. package/dist/cli/commands/preview-isr.d.ts +0 -66
  182. package/dist/cli/commands/preview-isr.d.ts.map +0 -1
  183. package/dist/cli/commands/preview-isr.js +0 -132
  184. package/dist/cli/commands/preview-isr.js.map +0 -1
@@ -9,31 +9,20 @@
9
9
  * import { handler, routes } from './dist/server/server.js'
10
10
  * app.use(createIsrHandler(routes, handler))
11
11
  *
12
- * Usage (Hono):
13
- * import { createIsrHandler } from '@jasonshimmy/vite-plugin-cer-app/isr'
14
- * import { handler, routes } from './dist/server/server.js'
15
- * app.use('*', createIsrHandler(routes, handler))
12
+ * Web Request/Response frameworks require a transport bridge; this factory
13
+ * accepts Node.js IncomingMessage/ServerResponse objects.
16
14
  */
17
15
  import type { IncomingMessage, ServerResponse } from 'node:http';
18
16
  /** A single cached SSR response stored by `createIsrHandler`. Includes the full rendered HTML, response headers, status code, and revalidation metadata. */
19
17
  export interface IsrCacheEntry {
20
18
  html: string;
21
- headers: Record<string, string>;
19
+ headers: Record<string, string | string[]>;
22
20
  statusCode: number;
23
21
  builtAt: number;
24
22
  revalidate: number;
25
23
  }
26
- /** The SSR request handler signature produced by the server entry bundle (exported as `handler`). Compatible with Express, Hono, and any Node.js HTTP server. */
24
+ /** The Node.js request handler signature produced by the server entry bundle. Web Request/Response frameworks require a bridge. */
27
25
  export type SsrHandlerFn = (req: IncomingMessage, res: ServerResponse) => unknown;
28
- /**
29
- * Wraps an SSR handler with stale-while-revalidate ISR caching.
30
- *
31
- * Routes that declare `meta.ssg.revalidate` in the `routes` array are cached
32
- * in memory. After the TTL expires the stale response is served immediately
33
- * while a fresh render runs in the background (stale-while-revalidate).
34
- *
35
- * Routes without a `revalidate` value are passed through to the handler directly.
36
- */
37
26
  export declare function createIsrHandler(routes: Array<{
38
27
  path: string;
39
28
  meta?: Record<string, unknown>;
@@ -1 +1 @@
1
- {"version":3,"file":"isr-handler.d.ts","sourceRoot":"","sources":["../../src/runtime/isr-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAEhE,4JAA4J;AAC5J,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/B,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,iKAAiK;AACjK,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,KAAK,OAAO,CAAA;AA8FjF;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,EAC/D,OAAO,EAAE,YAAY,GACpB,YAAY,CAgDd"}
1
+ {"version":3,"file":"isr-handler.d.ts","sourceRoot":"","sources":["../../src/runtime/isr-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAKhE,4JAA4J;AAC5J,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAA;IAC1C,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,mIAAmI;AACnI,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,KAAK,OAAO,CAAA;AAiGjF,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,EAC/D,OAAO,EAAE,YAAY,GACpB,YAAY,CAgEd"}
@@ -9,76 +9,71 @@
9
9
  * import { handler, routes } from './dist/server/server.js'
10
10
  * app.use(createIsrHandler(routes, handler))
11
11
  *
12
- * Usage (Hono):
13
- * import { createIsrHandler } from '@jasonshimmy/vite-plugin-cer-app/isr'
14
- * import { handler, routes } from './dist/server/server.js'
15
- * app.use('*', createIsrHandler(routes, handler))
12
+ * Web Request/Response frameworks require a transport bridge; this factory
13
+ * accepts Node.js IncomingMessage/ServerResponse objects.
16
14
  */
15
+ import { Readable } from 'node:stream';
16
+ import { EventEmitter } from 'node:events';
17
+ import { matchRoutePattern as _matchPattern, findRevalidate as _findRevalidate } from './route-matching.js';
17
18
  // ─── Internal helpers ─────────────────────────────────────────────────────────
18
- function _matchPattern(pattern, urlPath) {
19
- const norm = (s) => s.replace(/\/+$/, '') || '/';
20
- if (norm(pattern) === norm(urlPath))
21
- return true;
22
- const regexStr = '^' +
23
- norm(pattern)
24
- .replace(/[.+?^${}()|[\]\\]/g, '\\$&')
25
- .replace(/:[^/]+\*/g, '.*')
26
- .replace(/:[^/]+/g, '[^/]+') +
27
- '$';
28
- return new RegExp(regexStr).test(norm(urlPath));
29
- }
30
- function _findRevalidate(routes, urlPath) {
31
- for (const route of routes) {
32
- if (_matchPattern(route.path, urlPath)) {
33
- const ssg = route.meta?.ssg;
34
- if (typeof ssg?.revalidate === 'number')
35
- return ssg.revalidate;
36
- return null;
37
- }
38
- }
39
- return null;
40
- }
41
- async function _renderForCache(urlPath, handler, revalidate) {
19
+ async function _renderForCache(urlPath, handler, revalidate, request) {
42
20
  return new Promise((resolve) => {
21
+ const timer = setTimeout(() => resolve(null), 30_000);
22
+ timer.unref?.();
23
+ const finish = (entry) => { clearTimeout(timer); resolve(entry); };
43
24
  const chunks = [];
44
25
  const capturedHeaders = {};
45
26
  let capturedStatus = 200;
46
- const fakeRes = {
27
+ const events = new EventEmitter();
28
+ const fakeRes = Object.assign(events, {
29
+ headersSent: false,
30
+ writableEnded: false,
31
+ getHeader(name) { return capturedHeaders[name.toLowerCase()]; },
32
+ getHeaders() { return { ...capturedHeaders }; },
33
+ removeHeader(name) { delete capturedHeaders[name.toLowerCase()]; },
34
+ writeHead(status, headers) { capturedStatus = status; Object.assign(capturedHeaders, Object.fromEntries(Object.entries(headers ?? {}).map(([key, value]) => [key.toLowerCase(), value]))); this.headersSent = true; return this; },
47
35
  get statusCode() { return capturedStatus; },
48
36
  set statusCode(v) { capturedStatus = v; },
49
37
  setHeader(name, value) {
50
38
  capturedHeaders[name.toLowerCase()] = value;
51
39
  },
52
40
  write(chunk) {
41
+ this.headersSent = true;
53
42
  chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, 'utf-8'));
43
+ return true;
54
44
  },
55
45
  end(body) {
46
+ if (this.writableEnded)
47
+ return this;
48
+ this.headersSent = true;
49
+ this.writableEnded = true;
56
50
  if (body !== undefined) {
57
51
  chunks.push(Buffer.isBuffer(body) ? body : Buffer.from(String(body), 'utf-8'));
58
52
  }
59
- resolve({
53
+ finish({
60
54
  html: Buffer.concat(chunks).toString('utf-8'),
61
- headers: Object.fromEntries(Object.entries(capturedHeaders).map(([k, v]) => [k, Array.isArray(v) ? v.join(', ') : v])),
55
+ headers: { ...capturedHeaders },
62
56
  statusCode: capturedStatus,
63
57
  builtAt: Date.now(),
64
58
  revalidate,
65
59
  });
60
+ events.emit('finish');
61
+ return this;
66
62
  },
67
- };
68
- const fakeReq = {
69
- url: urlPath,
70
- method: 'GET',
71
- headers: { accept: 'text/html' },
72
- };
63
+ });
64
+ const fakeReq = Object.assign(Readable.from([]), {
65
+ url: urlPath, method: 'GET', headers: { ...request.headers },
66
+ socket: request.socket, httpVersion: request.httpVersion,
67
+ });
73
68
  try {
74
69
  const result = handler(fakeReq, fakeRes);
75
70
  if (result && typeof result.catch === 'function') {
76
71
  ;
77
- result.catch(() => resolve(null));
72
+ result.catch(() => finish(null));
78
73
  }
79
74
  }
80
75
  catch {
81
- resolve(null);
76
+ finish(null);
82
77
  }
83
78
  });
84
79
  }
@@ -100,15 +95,34 @@ function _serveFromCache(entry, res, status) {
100
95
  *
101
96
  * Routes without a `revalidate` value are passed through to the handler directly.
102
97
  */
98
+ function isCacheable(entry) {
99
+ return entry.statusCode === 200 && !entry.headers['set-cookie'] &&
100
+ !/private|no-store/i.test(String(entry.headers['cache-control'] ?? '')) &&
101
+ !String(entry.headers.vary ?? '').split(',').some((name) => name.trim() && name.trim().toLowerCase() !== 'accept-encoding');
102
+ }
103
103
  export function createIsrHandler(routes, handler) {
104
104
  const cache = new Map();
105
+ const cold = new Map();
106
+ const store = (path, entry) => {
107
+ if (!isCacheable(entry))
108
+ return;
109
+ if (!cache.has(path) && cache.size >= 1000)
110
+ cache.delete(cache.keys().next().value);
111
+ cache.set(path, entry);
112
+ };
105
113
  // True lock: stores the in-flight revalidation Promise per URL path.
106
114
  // A path present in this map means a background render is already in progress.
107
115
  const _inFlight = new Map();
108
116
  return async (req, res) => {
109
117
  const urlPath = (req.url ?? '/').split('?')[0];
110
118
  const revalidate = _findRevalidate(routes, urlPath);
111
- if (revalidate === null) {
119
+ const mode = routes.find((route) => _matchPattern(route.path, urlPath))?.meta?.render;
120
+ const middlewarePrivate = res.getHeader?.('set-cookie') ||
121
+ /private|no-store/i.test(String(res.getHeader?.('cache-control') ?? '')) ||
122
+ String(res.getHeader?.('vary') ?? '').split(',').some((name) => name.trim() && name.trim().toLowerCase() !== 'accept-encoding');
123
+ // Only anonymous, canonical GET documents can share cached HTML.
124
+ if (revalidate === null || revalidate < 0 || mode === 'server' || middlewarePrivate ||
125
+ (req.method ?? 'GET') !== 'GET' || (req.url ?? '').includes('?') || req.headers.cookie || req.headers.authorization) {
112
126
  return handler(req, res);
113
127
  }
114
128
  const cached = cache.get(urlPath);
@@ -123,9 +137,9 @@ export function createIsrHandler(routes, handler) {
123
137
  // revalidation is already in flight for this path.
124
138
  _serveFromCache(cached, res, 'STALE');
125
139
  if (!_inFlight.has(urlPath)) {
126
- const promise = _renderForCache(urlPath, handler, revalidate).then((entry) => {
140
+ const promise = _renderForCache(urlPath, handler, revalidate, req).then((entry) => {
127
141
  if (entry)
128
- cache.set(urlPath, entry);
142
+ store(urlPath, entry);
129
143
  }).catch(() => {
130
144
  // Background render failed — next request will try again.
131
145
  }).finally(() => {
@@ -136,10 +150,15 @@ export function createIsrHandler(routes, handler) {
136
150
  return;
137
151
  }
138
152
  // Cache miss — render, cache, then serve.
139
- const entry = await _renderForCache(urlPath, handler, revalidate);
153
+ // Before rendering we cannot know whether the response varies by a header.
154
+ // Coalesce only identical requests so uncached responses remain isolated.
155
+ const coldKey = JSON.stringify([urlPath, Object.entries(req.headers).sort(([a], [b]) => a.localeCompare(b))]);
156
+ if (!cold.has(coldKey))
157
+ cold.set(coldKey, _renderForCache(urlPath, handler, revalidate, req).finally(() => cold.delete(coldKey)));
158
+ const entry = await cold.get(coldKey);
140
159
  if (entry) {
141
- cache.set(urlPath, entry);
142
- _serveFromCache(entry, res, 'HIT');
160
+ store(urlPath, entry);
161
+ _serveFromCache(entry, res, isCacheable(entry) ? 'HIT' : 'BYPASS');
143
162
  }
144
163
  else {
145
164
  await handler(req, res);
@@ -1 +1 @@
1
- {"version":3,"file":"isr-handler.js","sourceRoot":"","sources":["../../src/runtime/isr-handler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"isr-handler.js","sourceRoot":"","sources":["../../src/runtime/isr-handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { IncomingMessage, ServerResponse } from 'node:http';
2
+ import { type SsrHandlerFn } from './isr-handler.js';
3
+ export interface ServerApplication {
4
+ handler: SsrHandlerFn;
5
+ spaHandler?: SsrHandlerFn;
6
+ routes?: Array<{
7
+ path: string;
8
+ meta?: Record<string, unknown>;
9
+ }>;
10
+ apiRoutes?: Array<{
11
+ path: string;
12
+ handlers: Record<string, unknown>;
13
+ }>;
14
+ runServerMiddleware?: (req: IncomingMessage, res: ServerResponse) => Promise<boolean>;
15
+ runWithRequestContext?: <T>(req: IncomingMessage, res: ServerResponse, fn: () => Promise<T>) => Promise<T>;
16
+ }
17
+ export interface DispatcherOptions {
18
+ /** Known SSG documents. The transport supplies disk or platform asset access. */
19
+ prerendered?: {
20
+ paths: readonly string[];
21
+ read: (path: string, req: IncomingMessage) => string | null | Promise<string | null>;
22
+ /** False for hybrid SSG builds that deliberately reject ungenerated static routes. */
23
+ fallback?: boolean;
24
+ notFound?: (req: IncomingMessage) => string | null | Promise<string | null>;
25
+ };
26
+ }
27
+ export declare function normalizeDocumentPath(path: string): string;
28
+ export declare function isPrivateBuildPath(path: string): boolean;
29
+ /** Common CER routing semantics; transport adapters supply Node-compatible requests/responses. */
30
+ export declare function createRequestDispatcher(app: ServerApplication, options?: DispatcherOptions): SsrHandlerFn;
31
+ //# sourceMappingURL=request-dispatcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-dispatcher.d.ts","sourceRoot":"","sources":["../../src/runtime/request-dispatcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAEhE,OAAO,EAAoB,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAEtE,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,YAAY,CAAA;IACrB,UAAU,CAAC,EAAE,YAAY,CAAA;IACzB,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAA;IAChE,SAAS,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAA;IACtE,mBAAmB,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IACrF,qBAAqB,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAA;CAC3G;AAED,MAAM,WAAW,iBAAiB;IAChC,iFAAiF;IACjF,WAAW,CAAC,EAAE;QACZ,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;QACxB,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,eAAe,KAAK,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;QACpF,sFAAsF;QACtF,QAAQ,CAAC,EAAE,OAAO,CAAA;QAClB,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;KAC5E,CAAA;CACF;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAO1D;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAExD;AAuCD,kGAAkG;AAClG,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,iBAAiB,EAAE,OAAO,GAAE,iBAAsB,GAAG,YAAY,CAwD7G"}
@@ -0,0 +1,152 @@
1
+ import { findRenderMode, findRevalidate } from './route-matching.js';
2
+ import { createIsrHandler } from './isr-handler.js';
3
+ export function normalizeDocumentPath(path) {
4
+ let decoded;
5
+ try {
6
+ decoded = decodeURIComponent(path);
7
+ }
8
+ catch {
9
+ throw Object.assign(new Error('Invalid URL encoding'), { statusCode: 400 });
10
+ }
11
+ if (!decoded.startsWith('/') || decoded.includes('\\') || decoded.includes('\0') || decoded.split('/').some((part) => part === '.' || part === '..')) {
12
+ throw Object.assign(new Error('Invalid URL path'), { statusCode: 400 });
13
+ }
14
+ return decoded.replace(/\/+$/, '') || '/';
15
+ }
16
+ export function isPrivateBuildPath(path) {
17
+ return /^\/(?:server|client|\.vite)(?:\/|$)/.test(path) || /^\/(?:ssg-manifest|cer-client-manifest|cer-startup-manifest)\.json$/.test(path);
18
+ }
19
+ function matchApiPattern(pattern, urlPath) {
20
+ const patternParts = pattern.split('/');
21
+ const urlParts = urlPath.split('/');
22
+ if (patternParts.length !== urlParts.length)
23
+ return null;
24
+ const params = {};
25
+ for (let i = 0; i < patternParts.length; i++) {
26
+ const p = patternParts[i];
27
+ const u = urlParts[i];
28
+ if (p.startsWith(':')) {
29
+ try {
30
+ params[p.slice(1)] = decodeURIComponent(u);
31
+ }
32
+ catch {
33
+ throw Object.assign(new Error('Invalid route parameter'), { statusCode: 400 });
34
+ }
35
+ }
36
+ else if (p !== u) {
37
+ return null;
38
+ }
39
+ }
40
+ return params;
41
+ }
42
+ async function parseBody(req) {
43
+ if (!['POST', 'PUT', 'PATCH', 'DELETE'].includes(req.method ?? 'GET'))
44
+ return undefined;
45
+ const chunks = [];
46
+ let size = 0;
47
+ for await (const chunk of req) {
48
+ const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
49
+ size += bytes.length;
50
+ if (size > 1024 * 1024)
51
+ throw Object.assign(new Error('Request body too large'), { statusCode: 413 });
52
+ chunks.push(bytes);
53
+ }
54
+ const bytes = Buffer.concat(chunks);
55
+ if (!bytes.length)
56
+ return undefined;
57
+ if (String(req.headers['content-type'] ?? '').includes('application/json')) {
58
+ try {
59
+ return JSON.parse(bytes.toString('utf8'));
60
+ }
61
+ catch {
62
+ throw Object.assign(new Error('Invalid JSON'), { statusCode: 400 });
63
+ }
64
+ }
65
+ return bytes;
66
+ }
67
+ /** Common CER routing semantics; transport adapters supply Node-compatible requests/responses. */
68
+ export function createRequestDispatcher(app, options = {}) {
69
+ const render = createIsrHandler(app.routes ?? [], app.handler);
70
+ const paths = new Set(options.prerendered?.paths.map(normalizeDocumentPath));
71
+ return async (req, res) => {
72
+ const dispatch = async () => {
73
+ try {
74
+ const url = new URL(req.url ?? '/', 'http://cer.local');
75
+ const path = normalizeDocumentPath(url.pathname);
76
+ if (isPrivateBuildPath(path)) {
77
+ res.statusCode = 404;
78
+ res.end('Not Found');
79
+ return;
80
+ }
81
+ if (app.runServerMiddleware && !(await app.runServerMiddleware(req, res)))
82
+ return;
83
+ if (!url.pathname.startsWith('/api/')) {
84
+ const mode = findRenderMode(app.routes ?? [], path);
85
+ if (mode === 'spa' && app.spaHandler)
86
+ await app.spaHandler(req, res);
87
+ else if (['GET', 'HEAD'].includes(req.method ?? 'GET') && mode !== 'server' && findRevalidate(app.routes ?? [], path) === null && paths.has(path)) {
88
+ const html = await options.prerendered.read(path, req);
89
+ if (html === null)
90
+ throw new Error(`Missing prerendered document: ${path}`);
91
+ res.setHeader('Content-Type', 'text/html; charset=utf-8');
92
+ if (!res.getHeader('Cache-Control'))
93
+ res.setHeader('Cache-Control', 'no-cache');
94
+ res.end(req.method === 'HEAD' ? undefined : html);
95
+ }
96
+ else if (options.prerendered?.fallback === false && mode !== 'server' && findRevalidate(app.routes ?? [], path) === null && !paths.has(path)) {
97
+ res.statusCode = 404;
98
+ res.setHeader('Content-Type', 'text/html; charset=utf-8');
99
+ const html = await options.prerendered.notFound?.(req) ?? '<!doctype html><meta name="robots" content="noindex"><title>Not found</title><h1>Not found</h1>';
100
+ res.end(req.method === 'HEAD' ? undefined : html);
101
+ }
102
+ else
103
+ await render(req, res);
104
+ return;
105
+ }
106
+ for (const route of app.apiRoutes ?? []) {
107
+ const params = matchApiPattern(route.path, url.pathname);
108
+ if (!params)
109
+ continue;
110
+ const method = (req.method ?? 'GET').toLowerCase();
111
+ const fn = route.handlers[method] ?? route.handlers[method.toUpperCase()] ?? (method === 'head' ? route.handlers.get ?? route.handlers.GET : undefined) ?? route.handlers.default;
112
+ if (typeof fn !== 'function') {
113
+ res.statusCode = 405;
114
+ res.setHeader('Allow', Object.keys(route.handlers).filter((key) => key !== 'default').map((key) => key.toUpperCase()).join(', '));
115
+ res.end('Method Not Allowed');
116
+ return;
117
+ }
118
+ Object.assign(req, { params, query: Object.fromEntries(url.searchParams), body: await parseBody(req) });
119
+ Object.assign(res, {
120
+ json(data) { res.setHeader('Content-Type', 'application/json; charset=utf-8'); res.end(JSON.stringify(data)); },
121
+ status(code) { res.statusCode = code; return res; },
122
+ });
123
+ await fn(req, res);
124
+ return;
125
+ }
126
+ res.statusCode = 404;
127
+ res.end('Not Found');
128
+ }
129
+ catch (error) {
130
+ if (res.headersSent) {
131
+ res.end();
132
+ return;
133
+ }
134
+ const code = error.statusCode;
135
+ res.statusCode = code === 400 || code === 413 ? code : 500;
136
+ res.end(res.statusCode === 500 ? 'Internal Server Error' : error.message);
137
+ }
138
+ };
139
+ try {
140
+ await (app.runWithRequestContext ? app.runWithRequestContext(req, res, dispatch) : dispatch());
141
+ }
142
+ catch {
143
+ if (!res.headersSent) {
144
+ res.statusCode = 500;
145
+ res.end('Internal Server Error');
146
+ }
147
+ else
148
+ res.end();
149
+ }
150
+ };
151
+ }
152
+ //# sourceMappingURL=request-dispatcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-dispatcher.js","sourceRoot":"","sources":["../../src/runtime/request-dispatcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ export declare function matchRoutePattern(pattern: string, urlPath: string): boolean;
2
+ /**
3
+ * Returns the per-route `render` strategy ('static' | 'server' | 'spa') for
4
+ * the route that best matches `urlPath`, or `null` when no route matches or
5
+ * none declares a render mode.
6
+ */
7
+ export declare function findRenderMode(routes: Array<{
8
+ path: string;
9
+ meta?: Record<string, unknown>;
10
+ }>, urlPath: string): 'static' | 'server' | 'spa' | null;
11
+ /**
12
+ * Looks up the `meta.ssg.revalidate` TTL (in seconds) for the route that best
13
+ * matches `urlPath`. Returns `null` when no route matches or none defines
14
+ * `revalidate`.
15
+ */
16
+ export declare function findRevalidate(routes: Array<{
17
+ path: string;
18
+ meta?: Record<string, unknown>;
19
+ }>, urlPath: string): number | null;
20
+ //# sourceMappingURL=route-matching.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-matching.d.ts","sourceRoot":"","sources":["../../src/runtime/route-matching.ts"],"names":[],"mappings":"AAAA,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAW3E;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,EAC/D,OAAO,EAAE,MAAM,GACd,QAAQ,GAAG,QAAQ,GAAG,KAAK,GAAG,IAAI,CASpC;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,EAC/D,OAAO,EAAE,MAAM,GACd,MAAM,GAAG,IAAI,CAQf"}
@@ -0,0 +1,45 @@
1
+ export function matchRoutePattern(pattern, urlPath) {
2
+ const norm = (s) => s.replace(/\/+$/, '') || '/';
3
+ if (norm(pattern) === norm(urlPath))
4
+ return true;
5
+ const regexStr = '^' + norm(pattern).split('/').map((segment, index) => {
6
+ if (/^:[^/]+\*$/.test(segment))
7
+ return index ? '(?:/.*)?' : '.*';
8
+ const prefix = index ? '/' : '';
9
+ if (segment.startsWith(':'))
10
+ return prefix + '[^/]+';
11
+ return prefix + segment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
12
+ }).join('') + '$';
13
+ return new RegExp(regexStr).test(norm(urlPath));
14
+ }
15
+ /**
16
+ * Returns the per-route `render` strategy ('static' | 'server' | 'spa') for
17
+ * the route that best matches `urlPath`, or `null` when no route matches or
18
+ * none declares a render mode.
19
+ */
20
+ export function findRenderMode(routes, urlPath) {
21
+ for (const route of routes) {
22
+ if (matchRoutePattern(route.path, urlPath)) {
23
+ const render = route.meta?.render;
24
+ if (render === 'static' || render === 'server' || render === 'spa')
25
+ return render;
26
+ return null;
27
+ }
28
+ }
29
+ return null;
30
+ }
31
+ /**
32
+ * Looks up the `meta.ssg.revalidate` TTL (in seconds) for the route that best
33
+ * matches `urlPath`. Returns `null` when no route matches or none defines
34
+ * `revalidate`.
35
+ */
36
+ export function findRevalidate(routes, urlPath) {
37
+ for (const route of routes) {
38
+ if (matchRoutePattern(route.path, urlPath)) {
39
+ const ssg = route.meta?.ssg;
40
+ return typeof ssg?.revalidate === 'number' ? ssg.revalidate : null;
41
+ }
42
+ }
43
+ return null;
44
+ }
45
+ //# sourceMappingURL=route-matching.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-matching.js","sourceRoot":"","sources":["../../src/runtime/route-matching.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,iBAAiB,CAAC,OAAe,EAAE,OAAe;IAChE,MAAM,IAAI,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,GAAG,CAAA;IAChE,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAA;IAChD,MAAM,QAAQ,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE;QACrE,IAAI,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,OAAO,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAA;QAChE,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAA;QAC/B,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,MAAM,GAAG,OAAO,CAAA;QACpD,OAAO,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAA;IAChE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,GAAG,CAAA;IAEjB,OAAO,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAA;AACjD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,MAA+D,EAC/D,OAAe;IAEf,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,iBAAiB,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC;YAC3C,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,EAAE,MAAM,CAAA;YACjC,IAAI,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,KAAK;gBAAE,OAAO,MAAM,CAAA;YACjF,OAAO,IAAI,CAAA;QACb,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,MAA+D,EAC/D,OAAe;IAEf,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,iBAAiB,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC;YAC3C,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,EAAE,GAA0C,CAAA;YAClE,OAAO,OAAO,GAAG,EAAE,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAA;QACpE,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC"}
@@ -1,6 +1,7 @@
1
1
  import type { IncomingMessage, ServerResponse } from 'node:http';
2
2
  /**
3
3
  * Augmented Node.js `IncomingMessage` passed to API route handlers.
4
+ * Web hosting adapters provide a compatibility bridge, without Node socket APIs.
4
5
  * Route params and query string are pre-parsed; `body` is parsed from JSON automatically.
5
6
  */
6
7
  export interface ApiRequest extends IncomingMessage {
@@ -10,6 +11,8 @@ export interface ApiRequest extends IncomingMessage {
10
11
  }
11
12
  /**
12
13
  * Augmented Node.js `ServerResponse` passed to API route handlers.
14
+ * Web hosting adapters implement the documented header/write/end contract;
15
+ * socket-specific Node methods require a native Node transport.
13
16
  * Adds convenience methods for JSON responses and fluent status setting.
14
17
  */
15
18
  export interface ApiResponse extends ServerResponse {
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/types/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAEhE;;;GAGG;AACH,MAAM,WAAW,UAAW,SAAQ,eAAe;IACjD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC9B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,IAAI,EAAE,OAAO,CAAA;CACd;AAED;;;GAGG;AACH,MAAM,WAAW,WAAY,SAAQ,cAAc;IACjD,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAA;IACzB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAA;CAClC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;AAEpF,6GAA6G;AAC7G,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,UAAU,CAAA;IACf,GAAG,EAAE,WAAW,CAAA;CACjB"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/types/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAEhE;;;;GAIG;AACH,MAAM,WAAW,UAAW,SAAQ,eAAe;IACjD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC9B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,IAAI,EAAE,OAAO,CAAA;CACd;AAED;;;;;GAKG;AACH,MAAM,WAAW,WAAY,SAAQ,cAAc;IACjD,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAA;IACzB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAA;CAClC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;AAEpF,6GAA6G;AAC7G,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,UAAU,CAAA;IACf,GAAG,EAAE,WAAW,CAAA;CACjB"}
@@ -4,6 +4,8 @@ import type { IncomingMessage } from 'node:http';
4
4
  export interface CerAppIntegration {
5
5
  /** Stable name used in diagnostics. */
6
6
  name: string;
7
+ /** Semantic colors contributed to build and runtime CSS. App values override these. */
8
+ customColors?: Record<string, Record<string, string>>;
7
9
  /** Resolve a rendered custom-element tag to its side-effect registration module. */
8
10
  componentResolver?: (tag: string) => string | undefined;
9
11
  /** Imports that must run once in the generated client entry (for example global CSS). */
@@ -124,7 +126,27 @@ export interface I18nConfig {
124
126
  strategy?: 'prefix' | 'prefix_except_default' | 'no_prefix';
125
127
  }
126
128
  /** Global Static Site Generation (SSG) configuration. Controls which routes are pre-rendered and at what concurrency. */
129
+ export interface SitemapPageMetadata {
130
+ /** Actual content modification date, never automatically set to build time. */
131
+ lastmod?: string;
132
+ images?: string[];
133
+ /** Exclude deliberately nonindexable pages. */
134
+ exclude?: boolean;
135
+ }
136
+ export interface NetlifyFormSchema {
137
+ name: string;
138
+ fields: string[];
139
+ honeypot?: string;
140
+ }
127
141
  export interface SsgConfig {
142
+ /** Retain intermediates for custom consumers. Auto retains hybrid/fallback/adapter builds. */
143
+ keepServer?: boolean;
144
+ /** Control entry downloading independently of hydration, per concrete generated path. */
145
+ entryPreload?: boolean | ((path: string) => boolean);
146
+ /** Metadata callback for successfully rendered sitemap entries. */
147
+ sitemap?: (path: string) => SitemapPageMetadata | Promise<SitemapPageMetadata>;
148
+ /** Optional Netlify registration artifacts, generated from one application schema. */
149
+ netlifyForms?: NetlifyFormSchema[];
128
150
  /**
129
151
  * Routes to pre-render.
130
152
  * - `'auto'` (default) — pre-render every static route discovered in `app/pages/`.
@@ -152,6 +174,9 @@ export interface SsgConfig {
152
174
  }
153
175
  /** JIT (Just-In-Time) CSS configuration for shadow-DOM style injection. */
154
176
  export interface JitCssConfig {
177
+ /** Static mode supplies scanned CSS to each shadow root without browser JIT. */
178
+ mode?: 'runtime' | 'static';
179
+ safelist?: string[];
155
180
  /** Additional glob patterns for content files scanned by the JIT CSS engine. */
156
181
  content?: string[];
157
182
  /** Enable every extended color family or list only the families to retain. Defaults to `false`. */
@@ -278,6 +303,8 @@ export interface CerAppConfig {
278
303
  autoImports?: AutoImportsConfig;
279
304
  /** UI/framework integrations composed into generated entries and component code splitting. */
280
305
  integrations?: CerAppIntegration[];
306
+ /** Options forwarded to Vite preview (including HTTPS certificates and headers). */
307
+ preview?: import('vite').PreviewOptions;
281
308
  port?: number;
282
309
  /**
283
310
  * Runtime configuration accessible via `useRuntimeConfig()`.