@emkodev/emroute 1.6.1 → 1.6.3

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 (129) hide show
  1. package/README.md +19 -17
  2. package/dist/runtime/abstract.runtime.d.ts +94 -0
  3. package/dist/runtime/abstract.runtime.js +339 -0
  4. package/dist/runtime/abstract.runtime.js.map +1 -0
  5. package/dist/runtime/bun/esbuild-runtime-loader.plugin.d.ts +25 -0
  6. package/dist/runtime/bun/esbuild-runtime-loader.plugin.js +72 -0
  7. package/dist/runtime/bun/esbuild-runtime-loader.plugin.js.map +1 -0
  8. package/dist/runtime/bun/fs/bun-fs.runtime.d.ts +21 -0
  9. package/dist/runtime/bun/fs/bun-fs.runtime.js +205 -0
  10. package/dist/runtime/bun/fs/bun-fs.runtime.js.map +1 -0
  11. package/dist/runtime/bun/sqlite/bun-sqlite.runtime.d.ts +27 -0
  12. package/dist/runtime/bun/sqlite/bun-sqlite.runtime.js +234 -0
  13. package/dist/runtime/bun/sqlite/bun-sqlite.runtime.js.map +1 -0
  14. package/dist/runtime/sitemap.generator.d.ts +58 -0
  15. package/dist/runtime/sitemap.generator.js +107 -0
  16. package/dist/runtime/sitemap.generator.js.map +1 -0
  17. package/dist/runtime/universal/fs/universal-fs.runtime.d.ts +29 -0
  18. package/dist/runtime/universal/fs/universal-fs.runtime.js +213 -0
  19. package/dist/runtime/universal/fs/universal-fs.runtime.js.map +1 -0
  20. package/dist/server/codegen.util.d.ts +16 -0
  21. package/dist/server/codegen.util.js +46 -0
  22. package/dist/server/codegen.util.js.map +1 -0
  23. package/dist/server/emroute.server.d.ts +37 -0
  24. package/dist/server/emroute.server.js +314 -0
  25. package/dist/server/emroute.server.js.map +1 -0
  26. package/dist/server/esbuild-manifest.plugin.d.ts +26 -0
  27. package/dist/server/esbuild-manifest.plugin.js +187 -0
  28. package/dist/server/esbuild-manifest.plugin.js.map +1 -0
  29. package/dist/server/scanner.util.d.ts +22 -0
  30. package/dist/server/scanner.util.js +194 -0
  31. package/dist/server/scanner.util.js.map +1 -0
  32. package/dist/server/server-api.type.d.ts +71 -0
  33. package/dist/server/server-api.type.js +9 -0
  34. package/dist/server/server-api.type.js.map +1 -0
  35. package/dist/src/component/abstract.component.d.ts +197 -0
  36. package/dist/src/component/abstract.component.js +84 -0
  37. package/dist/src/component/abstract.component.js.map +1 -0
  38. package/dist/src/component/page.component.d.ts +74 -0
  39. package/dist/src/component/page.component.js +107 -0
  40. package/dist/src/component/page.component.js.map +1 -0
  41. package/dist/src/component/widget.component.d.ts +47 -0
  42. package/dist/src/component/widget.component.js +69 -0
  43. package/dist/src/component/widget.component.js.map +1 -0
  44. package/dist/src/element/component.element.d.ts +79 -0
  45. package/dist/src/element/component.element.js +293 -0
  46. package/dist/src/element/component.element.js.map +1 -0
  47. package/dist/src/element/markdown.element.d.ts +36 -0
  48. package/dist/src/element/markdown.element.js +93 -0
  49. package/dist/src/element/markdown.element.js.map +1 -0
  50. package/dist/src/element/slot.element.d.ts +30 -0
  51. package/dist/src/element/slot.element.js +31 -0
  52. package/dist/src/element/slot.element.js.map +1 -0
  53. package/dist/src/index.d.ts +23 -0
  54. package/dist/src/index.js +24 -0
  55. package/dist/src/index.js.map +1 -0
  56. package/dist/src/overlay/mod.d.ts +9 -0
  57. package/dist/src/overlay/mod.js +9 -0
  58. package/dist/src/overlay/mod.js.map +1 -0
  59. package/dist/src/overlay/overlay.css.d.ts +8 -0
  60. package/dist/src/overlay/overlay.css.js +170 -0
  61. package/dist/src/overlay/overlay.css.js.map +1 -0
  62. package/dist/src/overlay/overlay.service.d.ts +14 -0
  63. package/dist/src/overlay/overlay.service.js +307 -0
  64. package/dist/src/overlay/overlay.service.js.map +1 -0
  65. package/dist/src/overlay/overlay.type.d.ts +33 -0
  66. package/dist/src/overlay/overlay.type.js +11 -0
  67. package/dist/src/overlay/overlay.type.js.map +1 -0
  68. package/dist/src/renderer/spa/base.renderer.d.ts +39 -0
  69. package/dist/src/renderer/spa/base.renderer.js +149 -0
  70. package/dist/src/renderer/spa/base.renderer.js.map +1 -0
  71. package/dist/src/renderer/spa/hash.renderer.d.ts +78 -0
  72. package/dist/src/renderer/spa/hash.renderer.js +162 -0
  73. package/dist/src/renderer/spa/hash.renderer.js.map +1 -0
  74. package/dist/src/renderer/spa/html.renderer.d.ts +81 -0
  75. package/dist/src/renderer/spa/html.renderer.js +304 -0
  76. package/dist/src/renderer/spa/html.renderer.js.map +1 -0
  77. package/dist/src/renderer/spa/mod.d.ts +30 -0
  78. package/dist/src/renderer/spa/mod.js +35 -0
  79. package/dist/src/renderer/spa/mod.js.map +1 -0
  80. package/dist/src/renderer/ssr/html.renderer.d.ts +49 -0
  81. package/dist/src/renderer/ssr/html.renderer.js +108 -0
  82. package/dist/src/renderer/ssr/html.renderer.js.map +1 -0
  83. package/dist/src/renderer/ssr/md.renderer.d.ts +40 -0
  84. package/dist/src/renderer/ssr/md.renderer.js +100 -0
  85. package/dist/src/renderer/ssr/md.renderer.js.map +1 -0
  86. package/dist/src/renderer/ssr/ssr.renderer.d.ts +74 -0
  87. package/dist/src/renderer/ssr/ssr.renderer.js +185 -0
  88. package/dist/src/renderer/ssr/ssr.renderer.js.map +1 -0
  89. package/dist/src/route/route.core.d.ts +129 -0
  90. package/dist/src/route/route.core.js +255 -0
  91. package/dist/src/route/route.core.js.map +1 -0
  92. package/dist/src/route/route.matcher.d.ts +86 -0
  93. package/dist/src/route/route.matcher.js +214 -0
  94. package/dist/src/route/route.matcher.js.map +1 -0
  95. package/dist/src/type/logger.type.d.ts +17 -0
  96. package/dist/src/type/logger.type.js +9 -0
  97. package/dist/src/type/logger.type.js.map +1 -0
  98. package/dist/src/type/markdown.type.d.ts +20 -0
  99. package/dist/src/type/markdown.type.js +2 -0
  100. package/dist/src/type/markdown.type.js.map +1 -0
  101. package/dist/src/type/route.type.d.ts +112 -0
  102. package/dist/src/type/route.type.js +8 -0
  103. package/dist/src/type/route.type.js.map +1 -0
  104. package/dist/src/type/widget.type.d.ts +55 -0
  105. package/dist/src/type/widget.type.js +10 -0
  106. package/dist/src/type/widget.type.js.map +1 -0
  107. package/dist/src/util/html.util.d.ts +29 -0
  108. package/dist/src/util/html.util.js +158 -0
  109. package/dist/src/util/html.util.js.map +1 -0
  110. package/dist/src/util/logger.util.d.ts +26 -0
  111. package/dist/src/util/logger.util.js +80 -0
  112. package/dist/src/util/logger.util.js.map +1 -0
  113. package/dist/src/util/widget-resolve.util.d.ts +52 -0
  114. package/dist/src/util/widget-resolve.util.js +149 -0
  115. package/dist/src/util/widget-resolve.util.js.map +1 -0
  116. package/dist/src/widget/breadcrumb.widget.d.ts +48 -0
  117. package/dist/src/widget/breadcrumb.widget.js +72 -0
  118. package/dist/src/widget/breadcrumb.widget.js.map +1 -0
  119. package/dist/src/widget/page-title.widget.d.ts +33 -0
  120. package/dist/src/widget/page-title.widget.js +33 -0
  121. package/dist/src/widget/page-title.widget.js.map +1 -0
  122. package/dist/src/widget/widget.parser.d.ts +26 -0
  123. package/dist/src/widget/widget.parser.js +76 -0
  124. package/dist/src/widget/widget.parser.js.map +1 -0
  125. package/dist/src/widget/widget.registry.d.ts +23 -0
  126. package/dist/src/widget/widget.registry.js +42 -0
  127. package/dist/src/widget/widget.registry.js.map +1 -0
  128. package/package.json +72 -12
  129. package/runtime/universal/fs/universal-fs.runtime.ts +253 -0
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  </p>
4
4
 
5
5
  <p align="center">
6
- File-based router with triple rendering. Zero dependencies.
6
+ File-based (but storage-agnostic) router with triple rendering. Zero dependencies.
7
7
  </p>
8
8
 
9
9
  ---
@@ -22,11 +22,11 @@ GET /md/projects/42 → plain Markdown
22
22
  ## Install
23
23
 
24
24
  ```bash
25
- bun add @emkodev/emroute
25
+ npm add @emkodev/emroute # or bun add, pnpm add, yarn add
26
26
  ```
27
27
 
28
- > emroute ships TypeScript source. Your toolchain must handle `.ts` imports
29
- > (Bun, tsx, esbuild, etc.).
28
+ Works on **Node**, **Bun**, and **Deno**. Node uses compiled JS; Bun and Deno
29
+ use TypeScript source directly.
30
30
 
31
31
  For markdown rendering, add [@emkodev/emkoma](doc/08c-setup-emkoma.md) (built
32
32
  for emroute) or bring your own — [marked](doc/08a-setup-marked.md) and
@@ -116,35 +116,37 @@ export default new ProjectPage();
116
116
  - **Sitemap generation** — opt-in `sitemap.xml` from the routes manifest with support for dynamic route enumerators
117
117
  - **Dev server** — zero-config: auto-generates `main.ts`, `index.html`, and route/widget manifests. File watcher with hot reload and bundle serving
118
118
 
119
- ## Why Bun?
119
+ ## Runtimes
120
120
 
121
- emroute 1.5.x shipped on JSR (Deno's registry). Starting with 1.6.0, emroute
122
- publishes to npm and targets Bun as the primary runtime.
121
+ emroute ships two filesystem runtimes:
122
+
123
+ - **`UniversalFsRuntime`** — uses only `node:` APIs and esbuild. Works on Node,
124
+ Deno, and Bun. The default choice for getting started.
125
+ - **`BunFsRuntime`** — uses Bun-native APIs (`Bun.file()`, `Bun.write()`,
126
+ `Bun.Transpiler`) for better I/O performance in production on Bun.
123
127
 
124
- **TL;DR:** JSR's design freezes the entire module graph at publish time. This
125
- breaks dynamic `import()` of consumer dependencies, peer dependency
126
- deduplication, and runtime resolution of package entry points for bundling — all
127
- things a framework with plugin architecture needs. The npm/`node_modules` model
128
- handles them with zero friction.
128
+ emroute ships TypeScript source. Your runtime must handle `.ts` imports natively
129
+ (Bun, Deno) or via a loader (`tsx`, `node --experimental-strip-types`).
129
130
 
130
- Full analysis with documentation and issue references:
131
+ emroute 1.5.x shipped on JSR (Deno's registry). Starting with 1.6.0, emroute
132
+ publishes to npm. Full analysis:
131
133
  [ADR-0017 — Move to Bun ecosystem](doc/architecture/ADR-0017-move-to-bun-ecosystem.md).
132
134
 
133
135
  ## Getting Started
134
136
 
135
- See [Setup](doc/01-setup.md) and [First Route](doc/02-first-route.md).
137
+ Pick your runtime: [Bun](doc/01a-setup-bun.md) | [Node](doc/01b-setup-node.md) | [Deno](doc/01c-setup-deno.md)
136
138
 
137
139
  ## Documentation
138
140
 
139
- - [Setup](doc/01-setup.md) install and create a server
140
- - [First route](doc/02-first-route.md) — create your first page
141
+ - Setup — [Bun](doc/01a-setup-bun.md), [Node](doc/01b-setup-node.md), [Deno](doc/01c-setup-deno.md)
142
+ - [First route](doc/02-first-route.md) — route files and rendering modes
141
143
  - [Pages](doc/03-pages.md) — page components, companion files, data fetching
142
144
  - [Routing](doc/04-routing.md) — dynamic segments, catch-all, redirects
143
145
  - [Nesting](doc/05-nesting.md) — layouts, slots, passthrough pages, tips and tricks
144
146
  - [Widgets](doc/06-widgets.md) — interactive islands with data lifecycle
145
147
  - [Server](doc/07-server.md) — `createEmrouteServer`, composition, static files
146
148
  - [Markdown renderers](doc/08-markdown-renderer.md) — pluggable parser interface and setup
147
- - [Runtime](doc/09-runtime.md) — abstract runtime, BunFsRuntime, BunSqliteRuntime
149
+ - [Runtime](doc/09-runtime.md) — abstract runtime, UniversalFsRuntime, BunFsRuntime, BunSqliteRuntime
148
150
  - [SPA modes](doc/10-spa-mode.md) — none, leaf, root, only
149
151
  - [Error handling](doc/11-error-handling.md) — widget errors, boundaries, status pages
150
152
  - [Shadow DOM](doc/12-shadow-dom.md) — unified architecture, SSR hydration
@@ -0,0 +1,94 @@
1
+ import type { RoutesManifest } from '../src/type/route.type.ts';
2
+ import type { WidgetManifestEntry } from '../src/type/widget.type.ts';
3
+ export declare const CONTENT_TYPES: Map<string, string>;
4
+ export type FetchParams = Parameters<typeof fetch>;
5
+ export type FetchReturn = ReturnType<typeof fetch>;
6
+ export declare const DEFAULT_ROUTES_DIR = "/routes";
7
+ export declare const DEFAULT_WIDGETS_DIR = "/widgets";
8
+ export declare const ROUTES_MANIFEST_PATH = "/routes.manifest.json";
9
+ export declare const WIDGETS_MANIFEST_PATH = "/widgets.manifest.json";
10
+ export declare const EMROUTE_EXTERNALS: readonly ["@emkodev/emroute/spa", "@emkodev/emroute/overlay", "@emkodev/emroute"];
11
+ export interface RuntimeConfig {
12
+ routesDir?: string;
13
+ widgetsDir?: string;
14
+ /** SPA mode. When 'none', bundling is skipped entirely. */
15
+ spa?: 'none' | 'leaf' | 'root' | 'only';
16
+ /** Consumer's SPA entry point (e.g. '/main.ts'). Skips app bundle when absent. */
17
+ entryPoint?: string;
18
+ bundlePaths?: {
19
+ emroute: string;
20
+ app: string;
21
+ widgets?: string;
22
+ };
23
+ }
24
+ /**
25
+ * Abstract resource provider. Speaks Request/Response (ADR-1).
26
+ *
27
+ * Three access patterns:
28
+ * - `handle()` — raw passthrough, server forwards browser requests as-is.
29
+ * - `query()` — read. Returns Response, or string when `{ as: "text" }`.
30
+ * - `command()` — write (PUT by default, override with `{ method }` in options).
31
+ *
32
+ * Includes manifest resolution: when `query(ROUTES_MANIFEST_PATH)` or
33
+ * `query(WIDGETS_MANIFEST_PATH)` returns 404, the runtime scans the
34
+ * configured directories and caches the result.
35
+ */
36
+ export declare abstract class Runtime {
37
+ readonly config: RuntimeConfig;
38
+ constructor(config?: RuntimeConfig);
39
+ /** Concrete runtimes implement this. Accepts the same args as `fetch()`. */
40
+ abstract handle(resource: FetchParams[0], init?: FetchParams[1]): FetchReturn;
41
+ /**
42
+ * Read with `{ as: "text" }` — skip metadata, return contents only.
43
+ * Semantically equivalent to `Accept: text/plain`; `as` exists for type safety.
44
+ */
45
+ abstract query(resource: FetchParams[0], options: FetchParams[1] & {
46
+ as: 'text';
47
+ }): Promise<string>;
48
+ /** Read — returns full Response with headers, status, body. */
49
+ abstract query(resource: FetchParams[0], options?: FetchParams[1]): FetchReturn;
50
+ /** Write. Defaults to PUT; pass `{ method: "DELETE" }` etc. to override. */
51
+ command(resource: FetchParams[0], options?: FetchParams[1]): FetchReturn;
52
+ /**
53
+ * Dynamically import a module from this runtime's storage.
54
+ * Used by the server for SSR imports of `.page.ts` and `.widget.ts` files.
55
+ */
56
+ loadModule(_path: string): Promise<unknown>;
57
+ static transpile(_ts: string): Promise<string>;
58
+ /**
59
+ * Build client bundles. Called by the server after manifests are written.
60
+ * No-op by default — override in runtimes that support bundling.
61
+ */
62
+ bundle(): Promise<void>;
63
+ /**
64
+ * Generate an HTML shell (`index.html`) if one doesn't already exist.
65
+ * Writes through `this.command()` so it works for any runtime.
66
+ */
67
+ protected writeShell(paths: {
68
+ emroute: string;
69
+ app: string;
70
+ widgets?: string;
71
+ }): Promise<void>;
72
+ static compress(_data: Uint8Array, _encoding: 'br' | 'gzip'): Promise<Uint8Array>;
73
+ /** Stop the bundler subprocess if running. No-op by default. */
74
+ static stopBundler(): Promise<void>;
75
+ private routesManifestCache;
76
+ private widgetsManifestCache;
77
+ /** Clear cached manifests so the next query triggers a fresh scan. */
78
+ invalidateManifests(): void;
79
+ /**
80
+ * Resolve the routes manifest. Called when the concrete runtime returns
81
+ * 404 for ROUTES_MANIFEST_PATH. Scans `config.routesDir` (or default).
82
+ */
83
+ resolveRoutesManifest(): Promise<Response>;
84
+ /**
85
+ * Resolve the widgets manifest. Called when the concrete runtime returns
86
+ * 404 for WIDGETS_MANIFEST_PATH. Scans `config.widgetsDir` (or default).
87
+ */
88
+ resolveWidgetsManifest(): Promise<Response>;
89
+ protected walkDirectory(dir: string): AsyncGenerator<string>;
90
+ protected scanRoutes(routesDir: string): Promise<RoutesManifest & {
91
+ warnings: string[];
92
+ }>;
93
+ protected scanWidgets(widgetsDir: string, pathPrefix?: string): Promise<WidgetManifestEntry[]>;
94
+ }
@@ -0,0 +1,339 @@
1
+ import { filePathToPattern, getPageFileType, getRouteType, sortRoutesBySpecificity, } from "../src/route/route.matcher.js";
2
+ export const CONTENT_TYPES = new Map([
3
+ ['.html', 'text/html; charset=utf-8'],
4
+ ['.css', 'text/css; charset=utf-8'],
5
+ ['.js', 'application/javascript; charset=utf-8'],
6
+ ['.mjs', 'application/javascript; charset=utf-8'],
7
+ ['.ts', 'text/typescript; charset=utf-8'],
8
+ ['.json', 'application/json; charset=utf-8'],
9
+ ['.md', 'text/plain; charset=utf-8'],
10
+ ['.txt', 'text/plain; charset=utf-8'],
11
+ ['.wasm', 'application/wasm'],
12
+ ['.map', 'application/json; charset=utf-8'],
13
+ ['.png', 'image/png'],
14
+ ['.jpg', 'image/jpeg'],
15
+ ['.jpeg', 'image/jpeg'],
16
+ ['.gif', 'image/gif'],
17
+ ['.svg', 'image/svg+xml'],
18
+ ['.ico', 'image/x-icon'],
19
+ ['.webp', 'image/webp'],
20
+ ['.avif', 'image/avif'],
21
+ ['.woff', 'font/woff'],
22
+ ['.woff2', 'font/woff2'],
23
+ ['.ttf', 'font/ttf'],
24
+ ]);
25
+ export const DEFAULT_ROUTES_DIR = '/routes';
26
+ export const DEFAULT_WIDGETS_DIR = '/widgets';
27
+ export const ROUTES_MANIFEST_PATH = '/routes.manifest.json';
28
+ export const WIDGETS_MANIFEST_PATH = '/widgets.manifest.json';
29
+ export const EMROUTE_EXTERNALS = [
30
+ '@emkodev/emroute/spa',
31
+ '@emkodev/emroute/overlay',
32
+ '@emkodev/emroute',
33
+ ];
34
+ /**
35
+ * Abstract resource provider. Speaks Request/Response (ADR-1).
36
+ *
37
+ * Three access patterns:
38
+ * - `handle()` — raw passthrough, server forwards browser requests as-is.
39
+ * - `query()` — read. Returns Response, or string when `{ as: "text" }`.
40
+ * - `command()` — write (PUT by default, override with `{ method }` in options).
41
+ *
42
+ * Includes manifest resolution: when `query(ROUTES_MANIFEST_PATH)` or
43
+ * `query(WIDGETS_MANIFEST_PATH)` returns 404, the runtime scans the
44
+ * configured directories and caches the result.
45
+ */
46
+ export class Runtime {
47
+ config;
48
+ constructor(config = {}) {
49
+ this.config = config;
50
+ this.config = config;
51
+ }
52
+ /** Write. Defaults to PUT; pass `{ method: "DELETE" }` etc. to override. */
53
+ command(resource, options) {
54
+ return this.handle(resource, { method: 'PUT', ...options });
55
+ }
56
+ /**
57
+ * Dynamically import a module from this runtime's storage.
58
+ * Used by the server for SSR imports of `.page.ts` and `.widget.ts` files.
59
+ */
60
+ loadModule(_path) {
61
+ throw new Error(`loadModule not implemented for ${this.constructor.name}`);
62
+ }
63
+ static transpile(_ts) {
64
+ throw new Error('Not implemented');
65
+ }
66
+ /**
67
+ * Build client bundles. Called by the server after manifests are written.
68
+ * No-op by default — override in runtimes that support bundling.
69
+ */
70
+ bundle() {
71
+ return Promise.resolve();
72
+ }
73
+ /**
74
+ * Generate an HTML shell (`index.html`) if one doesn't already exist.
75
+ * Writes through `this.command()` so it works for any runtime.
76
+ */
77
+ async writeShell(paths) {
78
+ if ((await this.query('/index.html')).status !== 404)
79
+ return;
80
+ const imports = Object.fromEntries(EMROUTE_EXTERNALS.map((pkg) => [pkg, paths.emroute]));
81
+ const importMap = JSON.stringify({ imports }, null, 2);
82
+ const scripts = [
83
+ `<script type="importmap">\n${importMap}\n </script>`,
84
+ ];
85
+ if (this.config.entryPoint) {
86
+ scripts.push(`<script type="module" src="${paths.app}"></script>`);
87
+ }
88
+ const html = `<!DOCTYPE html>
89
+ <html>
90
+ <head>
91
+ <meta charset="utf-8">
92
+ <meta name="viewport" content="width=device-width, initial-scale=1">
93
+ <title>emroute</title>
94
+ <style>@view-transition { navigation: auto; } router-slot { display: contents; }</style>
95
+ </head>
96
+ <body>
97
+ <router-slot></router-slot>
98
+ ${scripts.join('\n ')}
99
+ </body>
100
+ </html>`;
101
+ await this.command('/index.html', { body: html });
102
+ }
103
+ static compress(_data, _encoding) {
104
+ throw new Error('Not implemented');
105
+ }
106
+ /** Stop the bundler subprocess if running. No-op by default. */
107
+ static stopBundler() {
108
+ return Promise.resolve();
109
+ }
110
+ // ── Manifest resolution ─────────────────────────────────────────────
111
+ routesManifestCache = null;
112
+ widgetsManifestCache = null;
113
+ /** Clear cached manifests so the next query triggers a fresh scan. */
114
+ invalidateManifests() {
115
+ this.routesManifestCache = null;
116
+ this.widgetsManifestCache = null;
117
+ }
118
+ /**
119
+ * Resolve the routes manifest. Called when the concrete runtime returns
120
+ * 404 for ROUTES_MANIFEST_PATH. Scans `config.routesDir` (or default).
121
+ */
122
+ async resolveRoutesManifest() {
123
+ if (this.routesManifestCache)
124
+ return this.routesManifestCache.clone();
125
+ const routesDir = this.config.routesDir ?? DEFAULT_ROUTES_DIR;
126
+ // Check if directory exists by querying it
127
+ const dirResponse = await this.query(routesDir + '/');
128
+ if (dirResponse.status === 404) {
129
+ return new Response('Not Found', { status: 404 });
130
+ }
131
+ const { warnings, ...manifest } = await this.scanRoutes(routesDir);
132
+ for (const w of warnings)
133
+ console.warn(w);
134
+ const json = {
135
+ routes: manifest.routes,
136
+ errorBoundaries: manifest.errorBoundaries,
137
+ statusPages: [...manifest.statusPages.entries()],
138
+ errorHandler: manifest.errorHandler,
139
+ };
140
+ this.routesManifestCache = Response.json(json);
141
+ return this.routesManifestCache.clone();
142
+ }
143
+ /**
144
+ * Resolve the widgets manifest. Called when the concrete runtime returns
145
+ * 404 for WIDGETS_MANIFEST_PATH. Scans `config.widgetsDir` (or default).
146
+ */
147
+ async resolveWidgetsManifest() {
148
+ if (this.widgetsManifestCache)
149
+ return this.widgetsManifestCache.clone();
150
+ const widgetsDir = this.config.widgetsDir ?? DEFAULT_WIDGETS_DIR;
151
+ const dirResponse = await this.query(widgetsDir + '/');
152
+ if (dirResponse.status === 404) {
153
+ return new Response('Not Found', { status: 404 });
154
+ }
155
+ const entries = await this.scanWidgets(widgetsDir, widgetsDir.replace(/^\//, ''));
156
+ this.widgetsManifestCache = Response.json(entries);
157
+ return this.widgetsManifestCache.clone();
158
+ }
159
+ // ── Scanning ──────────────────────────────────────────────────────────
160
+ async *walkDirectory(dir) {
161
+ const trailingDir = dir.endsWith('/') ? dir : dir + '/';
162
+ const response = await this.query(trailingDir);
163
+ const entries = await response.json();
164
+ for (const entry of entries) {
165
+ const path = `${trailingDir}${entry}`;
166
+ if (entry.endsWith('/')) {
167
+ yield* this.walkDirectory(path);
168
+ }
169
+ else {
170
+ yield path;
171
+ }
172
+ }
173
+ }
174
+ async scanRoutes(routesDir) {
175
+ const pageFiles = [];
176
+ const redirects = [];
177
+ const errorBoundaries = [];
178
+ const statusPages = new Map();
179
+ let errorHandler;
180
+ const allFiles = [];
181
+ for await (const file of this.walkDirectory(routesDir)) {
182
+ allFiles.push(file);
183
+ }
184
+ for (const filePath of allFiles) {
185
+ const relativePath = filePath.replace(`${routesDir}/`, '');
186
+ const filename = relativePath.split('/').pop() ?? '';
187
+ if (filename === 'index.error.ts' && relativePath === 'index.error.ts') {
188
+ errorHandler = {
189
+ pattern: '/',
190
+ type: 'error',
191
+ modulePath: filePath,
192
+ };
193
+ continue;
194
+ }
195
+ const cssFileType = getPageFileType(filename);
196
+ if (cssFileType === 'css') {
197
+ const pattern = filePathToPattern(relativePath);
198
+ pageFiles.push({ path: filePath, pattern, fileType: 'css' });
199
+ continue;
200
+ }
201
+ const routeType = getRouteType(filename);
202
+ if (!routeType)
203
+ continue;
204
+ const statusMatch = filename.match(/^(\d{3})\.page\.(ts|html|md)$/);
205
+ if (statusMatch) {
206
+ const statusCode = parseInt(statusMatch[1], 10);
207
+ const fileType = getPageFileType(filename);
208
+ if (fileType) {
209
+ const existing = statusPages.get(statusCode);
210
+ if (existing) {
211
+ existing.files ??= {};
212
+ existing.files[fileType] = filePath;
213
+ existing.modulePath = existing.files.ts ?? existing.files.html ?? existing.files.md ??
214
+ '';
215
+ }
216
+ else {
217
+ const files = { [fileType]: filePath };
218
+ statusPages.set(statusCode, {
219
+ pattern: `/${statusCode}`,
220
+ type: 'page',
221
+ modulePath: filePath,
222
+ statusCode,
223
+ files,
224
+ });
225
+ }
226
+ }
227
+ continue;
228
+ }
229
+ const pattern = filePathToPattern(relativePath);
230
+ if (routeType === 'error') {
231
+ const boundaryPattern = pattern.replace(/\/[^/]+$/, '') || '/';
232
+ errorBoundaries.push({ pattern: boundaryPattern, modulePath: filePath });
233
+ continue;
234
+ }
235
+ if (routeType === 'redirect') {
236
+ redirects.push({ pattern, type: 'redirect', modulePath: filePath });
237
+ continue;
238
+ }
239
+ const fileType = getPageFileType(filename);
240
+ if (fileType) {
241
+ pageFiles.push({ path: filePath, pattern, fileType });
242
+ }
243
+ }
244
+ // Group files by pattern
245
+ const groups = new Map();
246
+ for (const { path, pattern, fileType } of pageFiles) {
247
+ let group = groups.get(pattern);
248
+ if (!group) {
249
+ group = { pattern, files: {} };
250
+ const segments = pattern.split('/').filter(Boolean);
251
+ if (segments.length > 1) {
252
+ group.parent = '/' + segments.slice(0, -1).join('/');
253
+ }
254
+ groups.set(pattern, group);
255
+ }
256
+ const existing = group.files[fileType];
257
+ if (existing?.includes('/index.page.') && !path.includes('/index.page.')) {
258
+ continue;
259
+ }
260
+ group.files[fileType] = path;
261
+ }
262
+ // Detect collisions
263
+ const warnings = [];
264
+ for (const [pattern, group] of groups) {
265
+ const filePaths = Object.values(group.files).filter(Boolean);
266
+ const hasIndex = filePaths.some((p) => p?.includes('/index.page.'));
267
+ const hasFlat = filePaths.some((p) => p && !p.includes('/index.page.'));
268
+ if (hasIndex && hasFlat) {
269
+ warnings.push(`Warning: Mixed file structure for ${pattern}:\n` +
270
+ filePaths.map((p) => ` ${p}`).join('\n') +
271
+ `\n Both folder/index and flat files detected`);
272
+ }
273
+ }
274
+ // Convert groups to RouteConfig array
275
+ const routes = [];
276
+ for (const [_, group] of groups) {
277
+ const modulePath = group.files.ts ?? group.files.html ?? group.files.md ?? '';
278
+ if (!modulePath)
279
+ continue;
280
+ const route = {
281
+ pattern: group.pattern,
282
+ type: 'page',
283
+ modulePath,
284
+ files: group.files,
285
+ };
286
+ if (group.parent)
287
+ route.parent = group.parent;
288
+ routes.push(route);
289
+ }
290
+ routes.push(...redirects);
291
+ const sortedRoutes = sortRoutesBySpecificity(routes);
292
+ return {
293
+ routes: sortedRoutes,
294
+ errorBoundaries,
295
+ statusPages,
296
+ errorHandler,
297
+ warnings,
298
+ };
299
+ }
300
+ async scanWidgets(widgetsDir, pathPrefix) {
301
+ const COMPANION_EXTENSIONS = ['html', 'md', 'css'];
302
+ const WIDGET_FILE_SUFFIX = '.widget.ts';
303
+ const entries = [];
304
+ const trailingDir = widgetsDir.endsWith('/') ? widgetsDir : widgetsDir + '/';
305
+ const response = await this.query(trailingDir);
306
+ const listing = await response.json();
307
+ for (const item of listing) {
308
+ if (!item.endsWith('/'))
309
+ continue;
310
+ const name = item.slice(0, -1);
311
+ const moduleFile = `${name}${WIDGET_FILE_SUFFIX}`;
312
+ const modulePath = `${trailingDir}${name}/${moduleFile}`;
313
+ if ((await this.query(modulePath)).status === 404)
314
+ continue;
315
+ const prefix = pathPrefix ? `${pathPrefix}/` : '';
316
+ const entry = {
317
+ name,
318
+ modulePath: `${prefix}${name}/${moduleFile}`,
319
+ tagName: `widget-${name}`,
320
+ };
321
+ const files = {};
322
+ let hasFiles = false;
323
+ for (const ext of COMPANION_EXTENSIONS) {
324
+ const companionFile = `${name}.widget.${ext}`;
325
+ const companionPath = `${trailingDir}${name}/${companionFile}`;
326
+ if ((await this.query(companionPath)).status !== 404) {
327
+ files[ext] = `${prefix}${name}/${companionFile}`;
328
+ hasFiles = true;
329
+ }
330
+ }
331
+ if (hasFiles)
332
+ entry.files = files;
333
+ entries.push(entry);
334
+ }
335
+ entries.sort((a, b) => a.name.localeCompare(b.name));
336
+ return entries;
337
+ }
338
+ }
339
+ //# sourceMappingURL=abstract.runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abstract.runtime.js","sourceRoot":"","sources":["../../runtime/abstract.runtime.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * esbuild Runtime Loader Plugin
3
+ *
4
+ * Intercepts file resolution so esbuild reads source files through the
5
+ * runtime's `query()` method instead of the filesystem. This allows
6
+ * bundling to work with any Runtime implementation (filesystem, SQLite,
7
+ * in-memory, etc.).
8
+ *
9
+ * The plugin intercepts `.ts` and `.js` imports that resolve under the
10
+ * virtual root and loads their contents from the runtime.
11
+ */
12
+ import type { Runtime } from '../../runtime/abstract.runtime.ts';
13
+ interface RuntimeLoaderOptions {
14
+ runtime: Runtime;
15
+ /**
16
+ * The filesystem root that esbuild would normally resolve paths against.
17
+ * Paths starting with this prefix are stripped to produce runtime paths
18
+ * (e.g. `/app/root/routes/index.page.ts` → `/routes/index.page.ts`).
19
+ * For runtimes without a filesystem root, pass an empty string.
20
+ */
21
+ root: string;
22
+ }
23
+ type EsbuildPlugin = any;
24
+ export declare function createRuntimeLoaderPlugin(options: RuntimeLoaderOptions): EsbuildPlugin;
25
+ export {};
@@ -0,0 +1,72 @@
1
+ /**
2
+ * esbuild Runtime Loader Plugin
3
+ *
4
+ * Intercepts file resolution so esbuild reads source files through the
5
+ * runtime's `query()` method instead of the filesystem. This allows
6
+ * bundling to work with any Runtime implementation (filesystem, SQLite,
7
+ * in-memory, etc.).
8
+ *
9
+ * The plugin intercepts `.ts` and `.js` imports that resolve under the
10
+ * virtual root and loads their contents from the runtime.
11
+ */
12
+ export function createRuntimeLoaderPlugin(options) {
13
+ const { runtime, root } = options;
14
+ return {
15
+ name: 'emroute-runtime-loader',
16
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
+ setup(build) {
18
+ // Intercept .ts and .js file resolution — redirect to 'runtime' namespace
19
+ // Only intercepts files that resolve under the runtime root.
20
+ build.onResolve({ filter: /\.[tj]s$/ },
21
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
22
+ (args) => {
23
+ // Skip bare specifiers (node_modules, packages)
24
+ if (!args.path.startsWith('.') && !args.path.startsWith('/'))
25
+ return undefined;
26
+ // Skip if already in a custom namespace (except 'runtime' for nested imports)
27
+ // Entry points have namespace '' (empty string)
28
+ if (args.namespace !== 'file' && args.namespace !== 'runtime' && args.namespace !== '')
29
+ return undefined;
30
+ let absPath;
31
+ if (args.path.startsWith('/')) {
32
+ absPath = args.path;
33
+ }
34
+ else if (args.resolveDir) {
35
+ absPath = args.resolveDir + '/' + args.path;
36
+ }
37
+ else {
38
+ return undefined;
39
+ }
40
+ // Normalize ../ and ./ segments
41
+ const parts = absPath.split('/');
42
+ const normalized = [];
43
+ for (const part of parts) {
44
+ if (part === '..')
45
+ normalized.pop();
46
+ else if (part !== '.' && part !== '')
47
+ normalized.push(part);
48
+ }
49
+ absPath = '/' + normalized.join('/');
50
+ // Only intercept files under the runtime root
51
+ if (root && !absPath.startsWith(root + '/'))
52
+ return undefined;
53
+ return { path: absPath, namespace: 'runtime' };
54
+ });
55
+ // Load file contents from the runtime
56
+ build.onLoad({ filter: /.*/, namespace: 'runtime' },
57
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
58
+ async (args) => {
59
+ // Strip root prefix to get runtime path (e.g. /app/root/routes/x.ts → /routes/x.ts)
60
+ const runtimePath = root && args.path.startsWith(root)
61
+ ? args.path.slice(root.length)
62
+ : args.path;
63
+ const contents = await runtime.query(runtimePath, { as: 'text' });
64
+ const ext = args.path.slice(args.path.lastIndexOf('.') + 1);
65
+ const loader = ext === 'ts' ? 'ts' : 'js';
66
+ const resolveDir = args.path.slice(0, args.path.lastIndexOf('/'));
67
+ return { contents, loader, resolveDir };
68
+ });
69
+ },
70
+ };
71
+ }
72
+ //# sourceMappingURL=esbuild-runtime-loader.plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"esbuild-runtime-loader.plugin.js","sourceRoot":"","sources":["../../../runtime/bun/esbuild-runtime-loader.plugin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { type FetchParams, type FetchReturn, Runtime, type RuntimeConfig } from '../../abstract.runtime.ts';
2
+ export declare class BunFsRuntime extends Runtime {
3
+ private readonly root;
4
+ constructor(root: string, config?: RuntimeConfig);
5
+ handle(resource: FetchParams[0], init?: FetchParams[1]): FetchReturn;
6
+ query(resource: FetchParams[0], options: FetchParams[1] & {
7
+ as: 'text';
8
+ }): Promise<string>;
9
+ query(resource: FetchParams[0], options?: FetchParams[1]): FetchReturn;
10
+ private parsePath;
11
+ private parse;
12
+ private read;
13
+ private list;
14
+ private write;
15
+ loadModule(path: string): Promise<unknown>;
16
+ bundle(): Promise<void>;
17
+ private static _esbuild;
18
+ private static esbuild;
19
+ static transpile(source: string): Promise<string>;
20
+ static stopBundler(): Promise<void>;
21
+ }