@ecopages/core 0.2.0-beta.27 → 0.2.0-beta.29

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 (103) hide show
  1. package/README.md +1 -1
  2. package/package.json +42 -26
  3. package/src/adapters/abstract/application-adapter.d.ts +22 -4
  4. package/src/adapters/abstract/application-adapter.js +27 -4
  5. package/src/adapters/abstract/server-adapter.d.ts +10 -0
  6. package/src/adapters/bun/create-app.d.ts +1 -0
  7. package/src/adapters/bun/create-app.js +11 -4
  8. package/src/adapters/bun/server-adapter.js +1 -0
  9. package/src/adapters/node/create-app.d.ts +1 -0
  10. package/src/adapters/node/create-app.js +10 -3
  11. package/src/adapters/node/server-adapter.js +1 -0
  12. package/src/adapters/shared/fs-server-response-factory.d.ts +5 -0
  13. package/src/adapters/shared/fs-server-response-factory.js +20 -0
  14. package/src/adapters/shared/fs-server-response-matcher.d.ts +18 -0
  15. package/src/adapters/shared/fs-server-response-matcher.js +75 -15
  16. package/src/config/config-builder.d.ts +12 -2
  17. package/src/config/config-builder.js +41 -2
  18. package/src/config/constants.d.ts +1 -0
  19. package/src/config/constants.js +2 -1
  20. package/src/eco/eco.js +2 -2
  21. package/src/eco/eco.utils.d.ts +1 -1
  22. package/src/eco/eco.utils.js +1 -1
  23. package/src/hmr/client/hmr-runtime.js +1 -1
  24. package/src/hmr/hmr-file-change-prep.d.ts +1 -1
  25. package/src/hmr/hmr-file-change-prep.js +1 -1
  26. package/src/plugins/processor.d.ts +8 -0
  27. package/src/route-renderer/GRAPH.md +11 -11
  28. package/src/route-renderer/README.md +11 -6
  29. package/src/route-renderer/orchestration/{document-shell-render.service.d.ts → document-shell/document-shell-render.service.d.ts} +19 -2
  30. package/src/route-renderer/orchestration/{document-shell-render.service.js → document-shell/document-shell-render.service.js} +28 -0
  31. package/src/route-renderer/orchestration/{layout-shell-props.service.d.ts → document-shell/layout-shell-props.service.d.ts} +1 -1
  32. package/src/route-renderer/orchestration/{component-render-context.d.ts → foreign-child/component-render-context.d.ts} +1 -1
  33. package/src/route-renderer/orchestration/{component-render-context.js → foreign-child/component-render-context.js} +1 -1
  34. package/src/route-renderer/orchestration/{render-output.utils.d.ts → foreign-child/foreign-child-output.utils.d.ts} +1 -7
  35. package/src/route-renderer/orchestration/{render-output.utils.js → foreign-child/foreign-child-output.utils.js} +1 -26
  36. package/src/route-renderer/orchestration/{foreign-subtree-execution.service.d.ts → foreign-child/foreign-subtree-execution.service.d.ts} +5 -5
  37. package/src/route-renderer/orchestration/{foreign-subtree-execution.service.js → foreign-child/foreign-subtree-execution.service.js} +2 -2
  38. package/src/route-renderer/orchestration/foreign-child/owning-renderer-resolution.d.ts +19 -0
  39. package/src/route-renderer/orchestration/foreign-child/owning-renderer-resolution.js +35 -0
  40. package/src/route-renderer/orchestration/integration-renderer.d.ts +4 -46
  41. package/src/route-renderer/orchestration/integration-renderer.js +53 -157
  42. package/src/route-renderer/orchestration/integration-renderer.test-fixtures.d.ts +75 -0
  43. package/src/route-renderer/orchestration/integration-renderer.test-fixtures.js +177 -0
  44. package/src/route-renderer/orchestration/{component-graph-collectors.d.ts → ownership-graph/component-graph-collectors.d.ts} +4 -4
  45. package/src/route-renderer/orchestration/{component-graph-collectors.js → ownership-graph/component-graph-collectors.js} +1 -1
  46. package/src/route-renderer/orchestration/{component-graph.d.ts → ownership-graph/component-graph.d.ts} +1 -3
  47. package/src/route-renderer/orchestration/{component-graph.js → ownership-graph/component-graph.js} +0 -2
  48. package/src/route-renderer/orchestration/{ownership-validation.service.d.ts → ownership-graph/ownership-validation.service.d.ts} +2 -2
  49. package/src/route-renderer/orchestration/{ownership-validation.service.js → ownership-graph/ownership-validation.service.js} +3 -4
  50. package/src/route-renderer/orchestration/{global-injector-assets.service.d.ts → page-browser-graph/global-injector-assets.service.d.ts} +3 -3
  51. package/src/route-renderer/orchestration/{global-injector-assets.service.js → page-browser-graph/global-injector-assets.service.js} +2 -2
  52. package/src/route-renderer/orchestration/{page-browser-graph-contribution.loader.d.ts → page-browser-graph/page-browser-graph-contribution.loader.d.ts} +1 -1
  53. package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-merge.utils.d.ts +12 -0
  54. package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-merge.utils.js +29 -0
  55. package/src/route-renderer/orchestration/{page-browser-graph-session.d.ts → page-browser-graph/page-browser-graph-session.d.ts} +3 -3
  56. package/src/route-renderer/orchestration/{page-browser-graph-session.js → page-browser-graph/page-browser-graph-session.js} +1 -1
  57. package/src/route-renderer/orchestration/{page-browser-graph.service.d.ts → page-browser-graph/page-browser-graph.service.d.ts} +3 -3
  58. package/src/route-renderer/orchestration/{page-browser-graph.service.js → page-browser-graph/page-browser-graph.service.js} +3 -3
  59. package/src/route-renderer/orchestration/{processed-asset-dedupe.d.ts → page-browser-graph/processed-asset-dedupe.d.ts} +1 -1
  60. package/src/route-renderer/orchestration/{integration-route-render-adapter.d.ts → route-pipeline/integration-route-render-adapter.d.ts} +3 -3
  61. package/src/route-renderer/orchestration/{integration-route-render-adapter.js → route-pipeline/integration-route-render-adapter.js} +1 -1
  62. package/src/route-renderer/orchestration/route-pipeline/marker-artifact.utils.d.ts +6 -0
  63. package/src/route-renderer/orchestration/route-pipeline/marker-artifact.utils.js +27 -0
  64. package/src/route-renderer/orchestration/route-pipeline/robots-meta.contribution.d.ts +15 -0
  65. package/src/route-renderer/orchestration/route-pipeline/robots-meta.contribution.js +36 -0
  66. package/src/route-renderer/orchestration/{route-html-finalization.service.d.ts → route-pipeline/route-html-finalization.service.d.ts} +6 -2
  67. package/src/route-renderer/orchestration/{route-html-finalization.service.js → route-pipeline/route-html-finalization.service.js} +5 -2
  68. package/src/route-renderer/orchestration/{route-prepared-options.builder.d.ts → route-pipeline/route-prepared-options.builder.d.ts} +3 -3
  69. package/src/route-renderer/orchestration/{route-prepared-options.builder.js → route-pipeline/route-prepared-options.builder.js} +8 -4
  70. package/src/route-renderer/orchestration/{route-prepared-options.utils.js → route-pipeline/route-prepared-options.utils.js} +1 -1
  71. package/src/route-renderer/orchestration/{route-render-orchestrator.d.ts → route-pipeline/route-render-orchestrator.d.ts} +6 -6
  72. package/src/route-renderer/orchestration/{route-render-orchestrator.js → route-pipeline/route-render-orchestrator.js} +8 -5
  73. package/src/route-renderer/orchestration/string-markup-renderer.js +1 -1
  74. package/src/services/html/html-transformer.service.js +1 -1
  75. package/src/services/invalidation/development-invalidation.service.d.ts +5 -0
  76. package/src/services/invalidation/development-invalidation.service.js +8 -9
  77. package/src/static-site-generator/README.md +9 -1
  78. package/src/static-site-generator/production-page-browser-graph-prebuild.js +1 -1
  79. package/src/static-site-generator/sitemap-routes.d.ts +14 -0
  80. package/src/static-site-generator/sitemap-routes.js +31 -0
  81. package/src/static-site-generator/sitemap.d.ts +15 -0
  82. package/src/static-site-generator/sitemap.js +33 -0
  83. package/src/static-site-generator/static-export-context.d.ts +9 -1
  84. package/src/static-site-generator/static-site-generator.d.ts +27 -6
  85. package/src/static-site-generator/static-site-generator.js +200 -50
  86. package/src/types/internal-types.d.ts +9 -2
  87. package/src/types/public-types.d.ts +78 -2
  88. package/src/utils/ecopages-route-info.d.ts +26 -0
  89. package/src/utils/ecopages-route-info.js +26 -0
  90. package/src/utils/html-escaping.d.ts +7 -0
  91. package/src/utils/html-escaping.js +5 -1
  92. package/src/utils/path-pattern.d.ts +23 -0
  93. package/src/utils/path-pattern.js +27 -0
  94. package/src/watchers/project-watcher.d.ts +3 -3
  95. package/src/watchers/project-watcher.js +4 -4
  96. package/src/route-renderer/orchestration/declared-ownership-graph.d.ts +0 -1
  97. package/src/route-renderer/orchestration/declared-ownership-graph.js +0 -8
  98. package/src/route-renderer/orchestration/template-serialization.d.ts +0 -38
  99. package/src/route-renderer/orchestration/template-serialization.js +0 -45
  100. /package/src/route-renderer/orchestration/{layout-shell-props.service.js → document-shell/layout-shell-props.service.js} +0 -0
  101. /package/src/route-renderer/orchestration/{page-browser-graph-contribution.loader.js → page-browser-graph/page-browser-graph-contribution.loader.js} +0 -0
  102. /package/src/route-renderer/orchestration/{processed-asset-dedupe.js → page-browser-graph/processed-asset-dedupe.js} +0 -0
  103. /package/src/route-renderer/orchestration/{route-prepared-options.utils.d.ts → route-pipeline/route-prepared-options.utils.d.ts} +0 -0
package/README.md CHANGED
@@ -267,7 +267,7 @@ Use these entrypoints when implementing integrations, processors, or source tran
267
267
  - `@ecopages/core/plugins/integration-plugin`
268
268
  - `@ecopages/core/plugins/processor`
269
269
  - `@ecopages/core/plugins/source-transform`
270
- - `@ecopages/core/route-renderer/integration-renderer`
270
+ - `@ecopages/core/route-renderer/orchestration/integration-renderer`
271
271
  - `@ecopages/core/services/asset-processing-service`
272
272
  - `@ecopages/core/hmr/hmr-strategy`
273
273
  - `@ecopages/core/integrations/ghtml`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ecopages/core",
3
- "version": "0.2.0-beta.27",
3
+ "version": "0.2.0-beta.29",
4
4
  "description": "Core package for Ecopages",
5
5
  "keywords": [
6
6
  "ecopages",
@@ -17,7 +17,7 @@
17
17
  "directory": "packages/core"
18
18
  },
19
19
  "dependencies": {
20
- "@ecopages/file-system": "0.2.0-beta.27",
20
+ "@ecopages/file-system": "0.2.0-beta.29",
21
21
  "@ecopages/logger": "^0.2.3",
22
22
  "@ecopages/scripts-injector": "^0.1.5",
23
23
  "@oxc-project/runtime": "0.134.0",
@@ -44,11 +44,11 @@
44
44
  "types": "./src/adapters/create-app.d.ts",
45
45
  "default": "./src/adapters/create-app.js"
46
46
  },
47
- "./route-renderer/integration-renderer": {
47
+ "./route-renderer/orchestration/integration-renderer": {
48
48
  "types": "./src/route-renderer/orchestration/integration-renderer.d.ts",
49
49
  "default": "./src/route-renderer/orchestration/integration-renderer.js"
50
50
  },
51
- "./route-renderer/string-markup-renderer": {
51
+ "./route-renderer/orchestration/string-markup-renderer": {
52
52
  "types": "./src/route-renderer/orchestration/string-markup-renderer.d.ts",
53
53
  "default": "./src/route-renderer/orchestration/string-markup-renderer.js"
54
54
  },
@@ -56,18 +56,26 @@
56
56
  "types": "./src/route-renderer/route-renderer.d.ts",
57
57
  "default": "./src/route-renderer/route-renderer.js"
58
58
  },
59
- "./route-renderer/component-render-context": {
60
- "import": "./src/route-renderer/orchestration/component-render-context.js",
61
- "types": "./src/route-renderer/orchestration/component-render-context.d.ts",
62
- "default": "./src/route-renderer/orchestration/component-render-context.js"
59
+ "./route-renderer/orchestration/foreign-child/component-render-context": {
60
+ "import": "./src/route-renderer/orchestration/foreign-child/component-render-context.js",
61
+ "types": "./src/route-renderer/orchestration/foreign-child/component-render-context.d.ts",
62
+ "default": "./src/route-renderer/orchestration/foreign-child/component-render-context.js"
63
63
  },
64
- "./route-renderer/orchestration/foreign-subtree-execution.service": {
65
- "types": "./src/route-renderer/orchestration/foreign-subtree-execution.service.d.ts",
66
- "default": "./src/route-renderer/orchestration/foreign-subtree-execution.service.js"
64
+ "./route-renderer/orchestration/foreign-child/foreign-subtree-execution.service": {
65
+ "types": "./src/route-renderer/orchestration/foreign-child/foreign-subtree-execution.service.d.ts",
66
+ "default": "./src/route-renderer/orchestration/foreign-child/foreign-subtree-execution.service.js"
67
67
  },
68
- "./route-renderer/orchestration/document-shell-render.service": {
69
- "types": "./src/route-renderer/orchestration/document-shell-render.service.d.ts",
70
- "default": "./src/route-renderer/orchestration/document-shell-render.service.js"
68
+ "./route-renderer/orchestration/document-shell/document-shell-render.service": {
69
+ "types": "./src/route-renderer/orchestration/document-shell/document-shell-render.service.d.ts",
70
+ "default": "./src/route-renderer/orchestration/document-shell/document-shell-render.service.js"
71
+ },
72
+ "./route-renderer/orchestration/page-browser-graph/page-browser-graph-merge.utils": {
73
+ "types": "./src/route-renderer/orchestration/page-browser-graph/page-browser-graph-merge.utils.d.ts",
74
+ "default": "./src/route-renderer/orchestration/page-browser-graph/page-browser-graph-merge.utils.js"
75
+ },
76
+ "./route-renderer/orchestration/foreign-child/owning-renderer-resolution": {
77
+ "types": "./src/route-renderer/orchestration/foreign-child/owning-renderer-resolution.d.ts",
78
+ "default": "./src/route-renderer/orchestration/foreign-child/owning-renderer-resolution.js"
71
79
  },
72
80
  "./router/navigation-coordinator": {
73
81
  "types": "./src/router/client/navigation-coordinator.d.ts",
@@ -269,11 +277,11 @@
269
277
  "types": "./src/adapters/create-app.d.ts",
270
278
  "default": "./src/adapters/create-app.js"
271
279
  },
272
- "./route-renderer/integration-renderer.ts": {
280
+ "./route-renderer/orchestration/integration-renderer.ts": {
273
281
  "types": "./src/route-renderer/orchestration/integration-renderer.d.ts",
274
282
  "default": "./src/route-renderer/orchestration/integration-renderer.js"
275
283
  },
276
- "./route-renderer/string-markup-renderer.ts": {
284
+ "./route-renderer/orchestration/string-markup-renderer.ts": {
277
285
  "types": "./src/route-renderer/orchestration/string-markup-renderer.d.ts",
278
286
  "default": "./src/route-renderer/orchestration/string-markup-renderer.js"
279
287
  },
@@ -281,18 +289,26 @@
281
289
  "types": "./src/route-renderer/route-renderer.d.ts",
282
290
  "default": "./src/route-renderer/route-renderer.js"
283
291
  },
284
- "./route-renderer/component-render-context.ts": {
285
- "import": "./src/route-renderer/orchestration/component-render-context.js",
286
- "types": "./src/route-renderer/orchestration/component-render-context.d.ts",
287
- "default": "./src/route-renderer/orchestration/component-render-context.js"
292
+ "./route-renderer/orchestration/foreign-child/component-render-context.ts": {
293
+ "import": "./src/route-renderer/orchestration/foreign-child/component-render-context.js",
294
+ "types": "./src/route-renderer/orchestration/foreign-child/component-render-context.d.ts",
295
+ "default": "./src/route-renderer/orchestration/foreign-child/component-render-context.js"
296
+ },
297
+ "./route-renderer/orchestration/foreign-child/foreign-subtree-execution.service.ts": {
298
+ "types": "./src/route-renderer/orchestration/foreign-child/foreign-subtree-execution.service.d.ts",
299
+ "default": "./src/route-renderer/orchestration/foreign-child/foreign-subtree-execution.service.js"
300
+ },
301
+ "./route-renderer/orchestration/document-shell/document-shell-render.service.ts": {
302
+ "types": "./src/route-renderer/orchestration/document-shell/document-shell-render.service.d.ts",
303
+ "default": "./src/route-renderer/orchestration/document-shell/document-shell-render.service.js"
288
304
  },
289
- "./route-renderer/orchestration/foreign-subtree-execution.service.ts": {
290
- "types": "./src/route-renderer/orchestration/foreign-subtree-execution.service.d.ts",
291
- "default": "./src/route-renderer/orchestration/foreign-subtree-execution.service.js"
305
+ "./route-renderer/orchestration/page-browser-graph/page-browser-graph-merge.utils.ts": {
306
+ "types": "./src/route-renderer/orchestration/page-browser-graph/page-browser-graph-merge.utils.d.ts",
307
+ "default": "./src/route-renderer/orchestration/page-browser-graph/page-browser-graph-merge.utils.js"
292
308
  },
293
- "./route-renderer/orchestration/document-shell-render.service.ts": {
294
- "types": "./src/route-renderer/orchestration/document-shell-render.service.d.ts",
295
- "default": "./src/route-renderer/orchestration/document-shell-render.service.js"
309
+ "./route-renderer/orchestration/foreign-child/owning-renderer-resolution.ts": {
310
+ "types": "./src/route-renderer/orchestration/foreign-child/owning-renderer-resolution.d.ts",
311
+ "default": "./src/route-renderer/orchestration/foreign-child/owning-renderer-resolution.js"
296
312
  },
297
313
  "./router/navigation-coordinator.ts": {
298
314
  "types": "./src/router/client/navigation-coordinator.d.ts",
@@ -8,7 +8,7 @@
8
8
  */
9
9
  import type { SourceModuleLoader } from '../../services/module-loading/module-loading-types.js';
10
10
  import type { EcoPagesAppConfig } from '../../types/internal-types.js';
11
- import type { ApiHandler, ApiHandlerContext, ErrorHandler, Middleware, RouteOptions, StaticRoute, ViewLoader } from '../../types/public-types.js';
11
+ import type { ApiHandler, ApiHandlerContext, ErrorHandler, Middleware, RouteOptions, StaticRoute, ViewLoader, EcopagesRouteInfo } from '../../types/public-types.js';
12
12
  import type { EcopagesWebSocketHandler } from '../../types/public-types.js';
13
13
  import { type EcopagesRuntimeLabel } from '../../dev/runtime-server-started-message.js';
14
14
  import { type ReturnParseCliArgs } from '../../utils/parse-cli-args.js';
@@ -52,6 +52,15 @@ export interface ApplicationRuntimeOptions {
52
52
  }
53
53
  export interface AppStartInfo {
54
54
  origin: string;
55
+ /**
56
+ * Static-generation routes available when the runtime becomes ready.
57
+ *
58
+ * @remarks
59
+ * Empty when the server adapter has no route registry yet, or when route
60
+ * resolution fails (failures never block startup). Route resolution completes
61
+ * before the `onAppStart` callback runs.
62
+ */
63
+ routes: EcopagesRouteInfo[];
55
64
  }
56
65
  export type OnAppStartCallback = (info: AppStartInfo) => void;
57
66
  /** @deprecated Use {@link AppStartInfo}. */
@@ -82,7 +91,7 @@ export interface ApplicationAdapter<T = any> extends AsyncDisposable {
82
91
  /** Boot the server. Pass a callback to run when the runtime is ready (optional). */
83
92
  start(onAppStart?: OnAppStartCallback): Promise<T | void>;
84
93
  /** Invoked by embedded hosts once the app can take traffic. */
85
- handleListening(origin: string): void;
94
+ handleListening(origin: string): void | Promise<void>;
86
95
  stop(force?: boolean): Promise<void>;
87
96
  }
88
97
  /**
@@ -272,8 +281,17 @@ export declare abstract class AbstractApplicationAdapter<TOptions extends Applic
272
281
  /**
273
282
  * Invoked by embedded hosts (for example Vite) once the app can take traffic.
274
283
  */
275
- handleListening(origin: string): void;
276
- protected notifyListening(origin: string): void;
284
+ handleListening(origin: string): Promise<void>;
285
+ protected notifyListening(origin: string): Promise<void>;
286
+ /**
287
+ * Resolves routes exposed on {@link AppStartInfo}.
288
+ *
289
+ * @remarks
290
+ * Default is empty. Runtime adapters override this to read from the
291
+ * initialized server route registry.
292
+ */
293
+ protected resolveAppRoutes(): Promise<EcopagesRouteInfo[]>;
294
+ private invokeAppStartCallback;
277
295
  /**
278
296
  * Stops the application server and releases runtime resources.
279
297
  *
@@ -218,16 +218,39 @@ class AbstractApplicationAdapter {
218
218
  /**
219
219
  * Invoked by embedded hosts (for example Vite) once the app can take traffic.
220
220
  */
221
- handleListening(origin) {
222
- this.notifyListening(origin);
221
+ async handleListening(origin) {
222
+ await this.notifyListening(origin);
223
223
  }
224
- notifyListening(origin) {
224
+ async notifyListening(origin) {
225
225
  const normalizedOrigin = origin.replace(/\/$/, "");
226
226
  startupTrace.markServerListening();
227
227
  if (!this.onAppStartCallback) {
228
228
  this.logServerStarted(normalizedOrigin);
229
+ return;
230
+ }
231
+ await this.invokeAppStartCallback(normalizedOrigin);
232
+ }
233
+ /**
234
+ * Resolves routes exposed on {@link AppStartInfo}.
235
+ *
236
+ * @remarks
237
+ * Default is empty. Runtime adapters override this to read from the
238
+ * initialized server route registry.
239
+ */
240
+ async resolveAppRoutes() {
241
+ return [];
242
+ }
243
+ async invokeAppStartCallback(origin) {
244
+ let routes = [];
245
+ try {
246
+ routes = await this.resolveAppRoutes();
247
+ } catch (error) {
248
+ appLogger.debug(
249
+ "Failed to resolve app start routes; continuing with empty routes",
250
+ error instanceof Error ? error.message : String(error)
251
+ );
229
252
  }
230
- this.onAppStartCallback?.({ origin: normalizedOrigin });
253
+ this.onAppStartCallback?.({ origin, routes });
231
254
  }
232
255
  /**
233
256
  * Stops the application server and releases runtime resources.
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import type { EcoPagesAppConfig } from '../../types/internal-types.js';
10
10
  import type { ApiHandler } from '../../types/public-types.js';
11
+ import type { StaticGenerationRoute } from '../../router/server/route-registry.js';
11
12
  /**
12
13
  * Configuration options for all server adapters
13
14
  */
@@ -35,6 +36,15 @@ export interface ServerAdapterResult {
35
36
  }) => Promise<string | undefined>;
36
37
  servePreviewOnly: () => Promise<string | undefined>;
37
38
  dispose(): Promise<void>;
39
+ /**
40
+ * Lists static-generation routes for app-start consumers.
41
+ *
42
+ * @remarks
43
+ * Optional so lightweight adapter stubs in tests need not implement routing.
44
+ */
45
+ listStaticGenerationRoutes?: (input: {
46
+ runtimeOrigin: string;
47
+ }) => Promise<readonly StaticGenerationRoute[]>;
38
48
  }
39
49
  /**
40
50
  * Abstract base class for server adapters across different runtimes
@@ -55,6 +55,7 @@ export declare class BunEcopagesApp<WebSocketData = undefined> extends SharedApp
55
55
  */
56
56
  protected bootServer(): Promise<Server<WebSocketData> | void>;
57
57
  stop(force?: boolean): Promise<void>;
58
+ protected resolveAppRoutes(): Promise<import("../../index.browser.js").EcopagesRouteInfo[]>;
58
59
  }
59
60
  /**
60
61
  * Factory function to create a Bun application
@@ -5,6 +5,7 @@ import { startupTrace } from "../../diagnostics/startup-trace.js";
5
5
  import { createBunServerAdapter } from "./server-adapter.js";
6
6
  import { BunRuntimeHost } from "./runtime-host.js";
7
7
  import { hostOwnsDevClient } from "../../dev/dev-client-ownership.js";
8
+ import { resolveAppStartRoutes } from "../../utils/ecopages-route-info.js";
8
9
  class BunEcopagesApp extends SharedApplicationAdapter {
9
10
  serverAdapter;
10
11
  server = null;
@@ -83,7 +84,7 @@ class BunEcopagesApp extends SharedApplicationAdapter {
83
84
  if (preview && serveOnly) {
84
85
  const previewOrigin2 = await this.serverAdapter.servePreviewOnly();
85
86
  if (previewOrigin2) {
86
- this.notifyListening(previewOrigin2);
87
+ await this.notifyListening(previewOrigin2);
87
88
  }
88
89
  return;
89
90
  }
@@ -92,7 +93,7 @@ class BunEcopagesApp extends SharedApplicationAdapter {
92
93
  const previewOrigin2 = await this.serverAdapter.buildStatic({ preview, force });
93
94
  appLogger.debugTimeEnd("Building static pages");
94
95
  if (preview && previewOrigin2) {
95
- this.notifyListening(previewOrigin2);
96
+ await this.notifyListening(previewOrigin2);
96
97
  }
97
98
  if (build) {
98
99
  process.exit(0);
@@ -131,10 +132,10 @@ class BunEcopagesApp extends SharedApplicationAdapter {
131
132
  process.exit(0);
132
133
  }
133
134
  } else {
134
- this.notifyListening(runtimeOrigin);
135
+ await this.notifyListening(runtimeOrigin);
135
136
  }
136
137
  if (preview && previewOrigin) {
137
- this.notifyListening(previewOrigin);
138
+ await this.notifyListening(previewOrigin);
138
139
  }
139
140
  return this.server ?? void 0;
140
141
  }
@@ -152,6 +153,12 @@ class BunEcopagesApp extends SharedApplicationAdapter {
152
153
  }
153
154
  this.stopped = true;
154
155
  }
156
+ async resolveAppRoutes() {
157
+ return resolveAppStartRoutes({
158
+ listStaticGenerationRoutes: this.serverAdapter?.listStaticGenerationRoutes,
159
+ runtimeOrigin: this.appConfig.baseUrl
160
+ });
161
+ }
155
162
  }
156
163
  async function createApp(options) {
157
164
  return new BunEcopagesApp(options, {
@@ -539,6 +539,7 @@ class BunServerAdapter extends SharedServerAdapter {
539
539
  completeInitialization: this.completeInitialization.bind(this),
540
540
  handleRequest: this.handleRequest.bind(this),
541
541
  attachUserWebSocketUpgrades: this.attachUserWebSocketUpgrades.bind(this),
542
+ listStaticGenerationRoutes: (input) => this.router.listStaticGenerationRoutes(input),
542
543
  dispose: this.dispose.bind(this)
543
544
  };
544
545
  }
@@ -17,6 +17,7 @@ export declare class NodeEcopagesApp extends SharedApplicationAdapter<EcopagesAp
17
17
  });
18
18
  protected createServerAdapter(params: Parameters<typeof createNodeServerAdapter>[0]): Promise<NodeServerAdapterResult>;
19
19
  stop(force?: boolean): Promise<void>;
20
+ protected resolveAppRoutes(): Promise<import("../../index.browser.js").EcopagesRouteInfo[]>;
20
21
  protected initializeServerAdapter(): Promise<NodeServerAdapterResult>;
21
22
  protected bootServer(): Promise<NodeServerInstance | void>;
22
23
  fetch(request: Request): Promise<Response>;
@@ -6,6 +6,7 @@ import { NodeHttpRequestBridge } from "./http-request-bridge.js";
6
6
  import { NodeRuntimeHost } from "./runtime-host.js";
7
7
  import { hostOwnsDevClient } from "../../dev/dev-client-ownership.js";
8
8
  import { startupTrace } from "../../diagnostics/startup-trace.js";
9
+ import { resolveAppStartRoutes } from "../../utils/ecopages-route-info.js";
9
10
  class NodeEcopagesApp extends SharedApplicationAdapter {
10
11
  serverAdapter;
11
12
  server = null;
@@ -33,6 +34,12 @@ class NodeEcopagesApp extends SharedApplicationAdapter {
33
34
  }
34
35
  this.stopped = true;
35
36
  }
37
+ async resolveAppRoutes() {
38
+ return resolveAppStartRoutes({
39
+ listStaticGenerationRoutes: this.serverAdapter?.listStaticGenerationRoutes,
40
+ runtimeOrigin: this.appConfig.baseUrl
41
+ });
42
+ }
36
43
  async initializeServerAdapter() {
37
44
  const binding = resolveRuntimeBinding({
38
45
  cliArgs: this.cliArgs,
@@ -71,7 +78,7 @@ class NodeEcopagesApp extends SharedApplicationAdapter {
71
78
  if (preview && serveOnly) {
72
79
  const previewOrigin = await this.serverAdapter.servePreviewOnly();
73
80
  if (previewOrigin) {
74
- this.notifyListening(previewOrigin);
81
+ await this.notifyListening(previewOrigin);
75
82
  }
76
83
  return;
77
84
  }
@@ -80,7 +87,7 @@ class NodeEcopagesApp extends SharedApplicationAdapter {
80
87
  const previewOrigin = await this.serverAdapter.buildStatic({ preview, force });
81
88
  appLogger.debugTimeEnd("Building static pages");
82
89
  if (preview && previewOrigin) {
83
- this.notifyListening(previewOrigin);
90
+ await this.notifyListening(previewOrigin);
84
91
  }
85
92
  if (build) {
86
93
  process.exit(0);
@@ -97,7 +104,7 @@ class NodeEcopagesApp extends SharedApplicationAdapter {
97
104
  });
98
105
  this.runtimeOrigin = this.runtimeHost.getOrigin(this.server, serveOptions);
99
106
  await this.serverAdapter.completeInitialization(this.server);
100
- this.notifyListening(this.runtimeOrigin);
107
+ await this.notifyListening(this.runtimeOrigin);
101
108
  return this.server;
102
109
  }
103
110
  async fetch(request) {
@@ -198,6 +198,7 @@ class NodeServerAdapter extends SharedServerAdapter {
198
198
  completeInitialization: this.completeInitialization.bind(this),
199
199
  handleRequest: this.handleRequest.bind(this),
200
200
  attachUserWebSocketUpgrades: this.attachUserWebSocketUpgrades.bind(this),
201
+ listStaticGenerationRoutes: (input) => this.router.listStaticGenerationRoutes(input),
201
202
  dispose: this.dispose.bind(this)
202
203
  };
203
204
  }
@@ -16,6 +16,11 @@ export declare class FileSystemServerResponseFactory {
16
16
  * Wraps already-rendered HTML in a 404 response envelope.
17
17
  */
18
18
  createHtmlNotFoundResponse(body: RouteRendererBody): Promise<Response>;
19
+ createDefaultServerErrorResponse(): Promise<Response>;
20
+ /**
21
+ * Wraps already-rendered HTML in a 500 response envelope.
22
+ */
23
+ createHtmlServerErrorResponse(body: RouteRendererBody): Promise<Response>;
19
24
  /**
20
25
  * Reads a static file response, returning `null` when the file is missing.
21
26
  */
@@ -38,6 +38,26 @@ class FileSystemServerResponseFactory {
38
38
  }
39
39
  });
40
40
  }
41
+ async createDefaultServerErrorResponse() {
42
+ return new Response(STATUS_MESSAGE[500], {
43
+ status: 500,
44
+ headers: {
45
+ "Content-Type": "text/plain; charset=utf-8"
46
+ }
47
+ });
48
+ }
49
+ /**
50
+ * Wraps already-rendered HTML in a 500 response envelope.
51
+ */
52
+ async createHtmlServerErrorResponse(body) {
53
+ return await this.createResponseWithBody(body, {
54
+ status: 500,
55
+ statusText: STATUS_MESSAGE[500],
56
+ headers: {
57
+ "Content-Type": "text/html"
58
+ }
59
+ });
60
+ }
41
61
  /**
42
62
  * Reads a static file response, returning `null` when the file is missing.
43
63
  */
@@ -55,7 +55,25 @@ export declare class FileSystemResponseMatcher {
55
55
  * when the page template cannot be resolved.
56
56
  */
57
57
  private renderCustomNotFoundResponse;
58
+ /**
59
+ * Renders the app-owned custom 500 page, falling back to the default text 500
60
+ * when the page template cannot be resolved.
61
+ */
62
+ private renderCustomServerErrorResponse;
63
+ /**
64
+ * Builds development-only error details for the custom 500 page.
65
+ * @remarks Production omits these fields so stacks are not serialized into HTML.
66
+ */
67
+ private buildServerErrorPageProps;
68
+ private renderCustomErrorPageResponse;
58
69
  private renderCustomNotFoundResponseOrServerError;
70
+ /**
71
+ * Logs the original render failure, then tries the custom 500 page once.
72
+ * @remarks Any failure while rendering the custom 500 page falls back to the
73
+ * default plain-text response and never re-enters the custom page path.
74
+ * In development the thrown error's `message` and `stack` are passed into the
75
+ * custom 500 page props.
76
+ */
59
77
  private createInternalServerErrorResponse;
60
78
  private createExecutionPlan;
61
79
  /**
@@ -100,9 +100,9 @@ class FileSystemResponseMatcher {
100
100
  return error;
101
101
  }
102
102
  if (error instanceof LocalsAccessError) {
103
- return this.createInternalServerErrorResponse(error.message, match.requestedPathname, error);
103
+ return await this.createInternalServerErrorResponse(error.message, match.requestedPathname, error);
104
104
  }
105
- return this.createInternalServerErrorResponse(
105
+ return await this.createInternalServerErrorResponse(
106
106
  error instanceof Error ? error.message : "Internal Server Error",
107
107
  match.requestedPathname,
108
108
  error
@@ -114,21 +114,66 @@ class FileSystemResponseMatcher {
114
114
  * when the page template cannot be resolved.
115
115
  */
116
116
  async renderCustomNotFoundResponse() {
117
- const error404TemplatePath = this.appConfig.absolutePaths.error404TemplatePath;
117
+ return this.renderCustomErrorPageResponse({
118
+ templatePath: this.appConfig.absolutePaths.error404TemplatePath,
119
+ label: "404",
120
+ createDefaultResponse: () => this.fileSystemResponseFactory.createDefaultNotFoundResponse(),
121
+ createHtmlResponse: (body) => this.fileSystemResponseFactory.createHtmlNotFoundResponse(body)
122
+ });
123
+ }
124
+ /**
125
+ * Renders the app-owned custom 500 page, falling back to the default text 500
126
+ * when the page template cannot be resolved.
127
+ */
128
+ async renderCustomServerErrorResponse(error) {
129
+ return this.renderCustomErrorPageResponse({
130
+ templatePath: this.appConfig.absolutePaths.error500TemplatePath,
131
+ label: "500",
132
+ props: this.buildServerErrorPageProps(error),
133
+ createDefaultResponse: () => this.fileSystemResponseFactory.createDefaultServerErrorResponse(),
134
+ createHtmlResponse: (body) => this.fileSystemResponseFactory.createHtmlServerErrorResponse(body)
135
+ });
136
+ }
137
+ /**
138
+ * Builds development-only error details for the custom 500 page.
139
+ * @remarks Production omits these fields so stacks are not serialized into HTML.
140
+ */
141
+ buildServerErrorPageProps(error) {
142
+ if (!isDevelopmentRuntime()) {
143
+ return void 0;
144
+ }
145
+ if (error instanceof Error) {
146
+ return {
147
+ message: error.message,
148
+ stack: error.stack
149
+ };
150
+ }
151
+ return {
152
+ message: String(error)
153
+ };
154
+ }
155
+ async renderCustomErrorPageResponse({
156
+ templatePath,
157
+ label,
158
+ props,
159
+ createDefaultResponse,
160
+ createHtmlResponse
161
+ }) {
118
162
  let routeRenderer;
119
163
  try {
120
- routeRenderer = this.routeRendererFactory.getPageRenderer(error404TemplatePath);
164
+ routeRenderer = this.routeRendererFactory.getPageRenderer(templatePath);
121
165
  } catch {
122
166
  appLogger.debug(
123
- "Custom 404 template not found, falling back to default 404 response",
124
- error404TemplatePath
167
+ `Custom ${label} template not found, falling back to default ${label} response`,
168
+ templatePath
125
169
  );
126
- return this.fileSystemResponseFactory.createDefaultNotFoundResponse();
170
+ return createDefaultResponse();
127
171
  }
128
172
  const result = await routeRenderer.execute({
129
- file: error404TemplatePath
173
+ file: templatePath,
174
+ props
130
175
  });
131
- return this.fileSystemResponseFactory.createHtmlNotFoundResponse(result.body);
176
+ return createHtmlResponse(result.body);
132
177
  }
133
178
  async renderCustomNotFoundResponseOrServerError(pathname) {
134
179
  try {
@@ -137,23 +182,38 @@ class FileSystemResponseMatcher {
137
182
  if (error instanceof Response) {
138
183
  return error;
139
184
  }
140
- return this.createInternalServerErrorResponse(
185
+ return await this.createInternalServerErrorResponse(
141
186
  error instanceof Error ? error.message : "Internal Server Error",
142
187
  pathname,
143
188
  error
144
189
  );
145
190
  }
146
191
  }
147
- createInternalServerErrorResponse(message, pathname, error) {
192
+ /**
193
+ * Logs the original render failure, then tries the custom 500 page once.
194
+ * @remarks Any failure while rendering the custom 500 page falls back to the
195
+ * default plain-text response and never re-enters the custom page path.
196
+ * In development the thrown error's `message` and `stack` are passed into the
197
+ * custom 500 page props.
198
+ */
199
+ async createInternalServerErrorResponse(message, pathname, error) {
148
200
  if (isDevelopmentRuntime() || appLogger.isDebugEnabled()) {
149
201
  appLogger.error(`[FileSystemResponseMatcher] ${message} at ${pathname}`, error);
150
202
  } else {
151
203
  appLogger.error(`[FileSystemResponseMatcher] Render error at ${pathname}`, error);
152
204
  }
153
- return new Response("Internal Server Error", {
154
- status: 500,
155
- headers: { "Content-Type": "text/plain; charset=utf-8" }
156
- });
205
+ try {
206
+ return await this.renderCustomServerErrorResponse(error);
207
+ } catch (serverErrorPageError) {
208
+ if (serverErrorPageError instanceof Response) {
209
+ return serverErrorPageError;
210
+ }
211
+ appLogger.error(
212
+ `[FileSystemResponseMatcher] Custom 500 template failed at ${pathname}`,
213
+ serverErrorPageError
214
+ );
215
+ return this.fileSystemResponseFactory.createDefaultServerErrorResponse();
216
+ }
157
217
  }
158
218
  async createExecutionPlan(match, request) {
159
219
  const cacheKey = this.pageRequestCacheCoordinator.buildCacheKey({
@@ -8,7 +8,7 @@ import type { EcoPagesAppConfig, RobotsPreference } from '../types/internal-type
8
8
  import type { AnyIntegrationPlugin } from '../plugins/integration-plugin.js';
9
9
  import type { Processor } from '../plugins/processor.js';
10
10
  import type { EcoSourceTransform } from '../plugins/source-transform.js';
11
- import type { PageMetadataProps } from '../types/public-types.js';
11
+ import type { PageMetadataProps, SitemapConfig } from '../types/public-types.js';
12
12
  import type { CacheConfig } from '../services/cache/cache.types.js';
13
13
  export declare const CONFIG_BUILDER_ERRORS: {
14
14
  readonly DUPLICATE_INTEGRATION_NAMES: "Integrations names must be unique";
@@ -16,7 +16,7 @@ export declare const CONFIG_BUILDER_ERRORS: {
16
16
  readonly MIXED_JSX_ENGINES: "Both kitajs and react integrations are enabled. Use per-file JSX import source/pragma consistently (e.g. `/** @jsxImportSource react */` for React files and `/** @jsxImportSource @kitajs/html */` for Kita files).";
17
17
  readonly duplicateProcessorName: (name: string) => string;
18
18
  readonly duplicateLoaderName: (name: string) => string;
19
- readonly duplicateSemanticTemplate: (kind: "html" | "404", matches: string[]) => string;
19
+ readonly duplicateSemanticTemplate: (kind: "html" | "404" | "500", matches: string[]) => string;
20
20
  readonly incompatibleRuntimeCapability: (kind: "integration" | "processor", name: string, runtime: RuntimeKind, reason: string) => string;
21
21
  readonly unsupportedRuntimeVersion: (kind: "integration" | "processor", name: string, runtime: RuntimeKind, current: string, min: string) => string;
22
22
  readonly invalidRuntimeVersion: (kind: "integration" | "processor", name: string, version: string) => string;
@@ -138,6 +138,16 @@ export declare class ConfigBuilder {
138
138
  setRobotsTxt(robotsTxt: {
139
139
  preferences: RobotsPreference;
140
140
  }): this;
141
+ /**
142
+ * Sets automatic sitemap.xml generation during static export.
143
+ *
144
+ * @remarks
145
+ * Merged over `{ enabled: false, fileName: 'sitemap.xml', extraUrls: [], exclude: [] }`.
146
+ * Existing apps stay unchanged until `enabled: true` is set. Page-level
147
+ * `metadata.robots.index: false` and metadata resolution failures are handled
148
+ * during static generation (not by this config merge).
149
+ */
150
+ setSitemap(sitemap: SitemapConfig): this;
141
151
  /**
142
152
  * Sets the integration plugins to use.
143
153
  * These plugins provide additional functionality to the application.