@emkodev/emroute 1.6.2 → 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 (128) hide show
  1. package/README.md +6 -6
  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 -13
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Standalone Scanning Utilities
3
+ *
4
+ * Runtime-agnostic route and widget scanning. Works with any Runtime
5
+ * that implements query() — used by tests with mock runtimes and by
6
+ * the CLI generate command.
7
+ *
8
+ * Note: BunFsRuntime has these as instance methods with lazy caching.
9
+ * These standalone functions are the same logic without caching.
10
+ */
11
+ import { filePathToPattern, getPageFileType, getRouteType, sortRoutesBySpecificity, } from "../src/route/route.matcher.js";
12
+ /** Walk directory recursively and collect files via Runtime. */
13
+ async function* walkDirectory(runtime, dir) {
14
+ const trailingDir = dir.endsWith('/') ? dir : dir + '/';
15
+ const response = await runtime.query(trailingDir);
16
+ const entries = await response.json();
17
+ for (const entry of entries) {
18
+ const path = `${trailingDir}${entry}`;
19
+ if (entry.endsWith('/')) {
20
+ yield* walkDirectory(runtime, path);
21
+ }
22
+ else {
23
+ yield path;
24
+ }
25
+ }
26
+ }
27
+ /** Generate routes manifest by scanning a directory via Runtime. */
28
+ export async function generateRoutesManifest(routesDir, runtime) {
29
+ const pageFiles = [];
30
+ const redirects = [];
31
+ const errorBoundaries = [];
32
+ const statusPages = new Map();
33
+ let errorHandler;
34
+ const allFiles = [];
35
+ for await (const file of walkDirectory(runtime, routesDir)) {
36
+ allFiles.push(file);
37
+ }
38
+ for (const filePath of allFiles) {
39
+ const relativePath = filePath.replace(`${routesDir}/`, '');
40
+ const filename = relativePath.split('/').pop() ?? '';
41
+ if (filename === 'index.error.ts' && relativePath === 'index.error.ts') {
42
+ errorHandler = {
43
+ pattern: '/',
44
+ type: 'error',
45
+ modulePath: filePath,
46
+ };
47
+ continue;
48
+ }
49
+ const cssFileType = getPageFileType(filename);
50
+ if (cssFileType === 'css') {
51
+ const pattern = filePathToPattern(relativePath);
52
+ pageFiles.push({ path: filePath, pattern, fileType: 'css' });
53
+ continue;
54
+ }
55
+ const routeType = getRouteType(filename);
56
+ if (!routeType)
57
+ continue;
58
+ const statusMatch = filename.match(/^(\d{3})\.page\.(ts|html|md)$/);
59
+ if (statusMatch) {
60
+ const statusCode = parseInt(statusMatch[1], 10);
61
+ const fileType = getPageFileType(filename);
62
+ if (fileType) {
63
+ const existing = statusPages.get(statusCode);
64
+ if (existing) {
65
+ existing.files ??= {};
66
+ existing.files[fileType] = filePath;
67
+ existing.modulePath = existing.files.ts ?? existing.files.html ?? existing.files.md ?? '';
68
+ }
69
+ else {
70
+ const files = { [fileType]: filePath };
71
+ statusPages.set(statusCode, {
72
+ pattern: `/${statusCode}`,
73
+ type: 'page',
74
+ modulePath: filePath,
75
+ statusCode,
76
+ files,
77
+ });
78
+ }
79
+ }
80
+ continue;
81
+ }
82
+ const pattern = filePathToPattern(relativePath);
83
+ if (routeType === 'error') {
84
+ const boundaryPattern = pattern.replace(/\/[^/]+$/, '') || '/';
85
+ errorBoundaries.push({ pattern: boundaryPattern, modulePath: filePath });
86
+ continue;
87
+ }
88
+ if (routeType === 'redirect') {
89
+ redirects.push({ pattern, type: 'redirect', modulePath: filePath });
90
+ continue;
91
+ }
92
+ const fileType = getPageFileType(filename);
93
+ if (fileType) {
94
+ pageFiles.push({ path: filePath, pattern, fileType });
95
+ }
96
+ }
97
+ // Group files by pattern
98
+ const groups = new Map();
99
+ for (const { path, pattern, fileType } of pageFiles) {
100
+ let group = groups.get(pattern);
101
+ if (!group) {
102
+ group = { pattern, files: {} };
103
+ const segments = pattern.split('/').filter(Boolean);
104
+ if (segments.length > 1) {
105
+ group.parent = '/' + segments.slice(0, -1).join('/');
106
+ }
107
+ groups.set(pattern, group);
108
+ }
109
+ const existing = group.files[fileType];
110
+ if (existing?.includes('/index.page.') && !path.includes('/index.page.')) {
111
+ continue;
112
+ }
113
+ group.files[fileType] = path;
114
+ }
115
+ // Detect collisions
116
+ const warnings = [];
117
+ for (const [pattern, group] of groups) {
118
+ const filePaths = Object.values(group.files).filter(Boolean);
119
+ const hasIndex = filePaths.some((p) => p?.includes('/index.page.'));
120
+ const hasFlat = filePaths.some((p) => p && !p.includes('/index.page.'));
121
+ if (hasIndex && hasFlat) {
122
+ warnings.push(`Warning: Mixed file structure for ${pattern}:\n` +
123
+ filePaths.map((p) => ` ${p}`).join('\n') +
124
+ `\n Both folder/index and flat files detected`);
125
+ }
126
+ }
127
+ // Convert groups to RouteConfig array
128
+ const routes = [];
129
+ for (const [_, group] of groups) {
130
+ const modulePath = group.files.ts ?? group.files.html ?? group.files.md ?? '';
131
+ if (!modulePath)
132
+ continue;
133
+ const route = {
134
+ pattern: group.pattern,
135
+ type: 'page',
136
+ modulePath,
137
+ files: group.files,
138
+ };
139
+ if (group.parent)
140
+ route.parent = group.parent;
141
+ routes.push(route);
142
+ }
143
+ routes.push(...redirects);
144
+ const sortedRoutes = sortRoutesBySpecificity(routes);
145
+ return {
146
+ routes: sortedRoutes,
147
+ errorBoundaries,
148
+ statusPages,
149
+ errorHandler,
150
+ warnings,
151
+ };
152
+ }
153
+ /**
154
+ * Discover widget modules and companion files by scanning a directory.
155
+ */
156
+ export async function discoverWidgets(widgetsDir, runtime, pathPrefix) {
157
+ const COMPANION_EXTENSIONS = ['html', 'md', 'css'];
158
+ const WIDGET_FILE_SUFFIX = '.widget.ts';
159
+ const entries = [];
160
+ const trailingDir = widgetsDir.endsWith('/') ? widgetsDir : widgetsDir + '/';
161
+ const response = await runtime.query(trailingDir);
162
+ const listing = await response.json();
163
+ for (const item of listing) {
164
+ if (!item.endsWith('/'))
165
+ continue;
166
+ const name = item.slice(0, -1);
167
+ const moduleFile = `${name}${WIDGET_FILE_SUFFIX}`;
168
+ const modulePath = `${trailingDir}${name}/${moduleFile}`;
169
+ if ((await runtime.query(modulePath)).status === 404)
170
+ continue;
171
+ const prefix = pathPrefix ? `${pathPrefix}/` : '';
172
+ const entry = {
173
+ name,
174
+ modulePath: `${prefix}${name}/${moduleFile}`,
175
+ tagName: `widget-${name}`,
176
+ };
177
+ const files = {};
178
+ let hasFiles = false;
179
+ for (const ext of COMPANION_EXTENSIONS) {
180
+ const companionFile = `${name}.widget.${ext}`;
181
+ const companionPath = `${trailingDir}${name}/${companionFile}`;
182
+ if ((await runtime.query(companionPath)).status !== 404) {
183
+ files[ext] = `${prefix}${name}/${companionFile}`;
184
+ hasFiles = true;
185
+ }
186
+ }
187
+ if (hasFiles)
188
+ entry.files = files;
189
+ entries.push(entry);
190
+ }
191
+ entries.sort((a, b) => a.name.localeCompare(b.name));
192
+ return entries;
193
+ }
194
+ //# sourceMappingURL=scanner.util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.util.js","sourceRoot":"","sources":["../../server/scanner.util.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,YAAY,EACZ,uBAAuB,GACxB,MAAM,+BAA+B,CAAC;AAavC,gEAAgE;AAChE,KAAK,SAAS,CAAC,CAAC,aAAa,CAAC,OAAgB,EAAE,GAAW;IACzD,MAAM,WAAW,GAAG,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC;IACxD,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAClD,MAAM,OAAO,GAAa,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IAEhD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,GAAG,WAAW,GAAG,KAAK,EAAE,CAAC;QACtC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,KAAK,CAAC,CAAC,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACtC,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,CAAC;QACb,CAAC;IACH,CAAC;AACH,CAAC;AAED,oEAAoE;AACpE,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,SAAiB,EACjB,OAAgB;IAEhB,MAAM,SAAS,GAIV,EAAE,CAAC;IACR,MAAM,SAAS,GAAkB,EAAE,CAAC;IACpC,MAAM,eAAe,GAAoB,EAAE,CAAC;IAC5C,MAAM,WAAW,GAAG,IAAI,GAAG,EAAuB,CAAC;IACnD,IAAI,YAAqC,CAAC;IAE1C,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,aAAa,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC;QAC3D,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtB,CAAC;IAED,KAAK,MAAM,QAAQ,IAAI,QAAQ,EAAE,CAAC;QAChC,MAAM,YAAY,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,SAAS,GAAG,EAAE,EAAE,CAAC,CAAC;QAC3D,MAAM,QAAQ,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;QAErD,IAAI,QAAQ,KAAK,gBAAgB,IAAI,YAAY,KAAK,gBAAgB,EAAE,CAAC;YACvE,YAAY,GAAG;gBACb,OAAO,EAAE,GAAG;gBACZ,IAAI,EAAE,OAAO;gBACb,UAAU,EAAE,QAAQ;aACrB,CAAC;YACF,SAAS;QACX,CAAC;QAED,MAAM,WAAW,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;QAC9C,IAAI,WAAW,KAAK,KAAK,EAAE,CAAC;YAC1B,MAAM,OAAO,GAAG,iBAAiB,CAAC,YAAY,CAAC,CAAC;YAChD,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;YAC7D,SAAS;QACX,CAAC;QAED,MAAM,SAAS,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;QACzC,IAAI,CAAC,SAAS;YAAE,SAAS;QAEzB,MAAM,WAAW,GAAG,QAAQ,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;QACpE,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,UAAU,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAChD,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;YAC3C,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;gBAC7C,IAAI,QAAQ,EAAE,CAAC;oBACb,QAAQ,CAAC,KAAK,KAAK,EAAE,CAAC;oBACtB,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAC;oBACpC,QAAQ,CAAC,UAAU,GAAG,QAAQ,CAAC,KAAK,CAAC,EAAE,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,IAAI,QAAQ,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;gBAC5F,CAAC;qBAAM,CAAC;oBACN,MAAM,KAAK,GAAe,EAAE,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAC;oBACnD,WAAW,CAAC,GAAG,CAAC,UAAU,EAAE;wBAC1B,OAAO,EAAE,IAAI,UAAU,EAAE;wBACzB,IAAI,EAAE,MAAM;wBACZ,UAAU,EAAE,QAAQ;wBACpB,UAAU;wBACV,KAAK;qBACN,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YACD,SAAS;QACX,CAAC;QAED,MAAM,OAAO,GAAG,iBAAiB,CAAC,YAAY,CAAC,CAAC;QAEhD,IAAI,SAAS,KAAK,OAAO,EAAE,CAAC;YAC1B,MAAM,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC;YAC/D,eAAe,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,CAAC;YACzE,SAAS;QACX,CAAC;QAED,IAAI,SAAS,KAAK,UAAU,EAAE,CAAC;YAC7B,SAAS,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,CAAC;YACpE,SAAS;QACX,CAAC;QAED,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;QAC3C,IAAI,QAAQ,EAAE,CAAC;YACb,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;QACxD,CAAC;IACH,CAAC;IAED,yBAAyB;IACzB,MAAM,MAAM,GAAG,IAAI,GAAG,EAAmE,CAAC;IAC1F,KAAK,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,SAAS,EAAE,CAAC;QACpD,IAAI,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAChC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,KAAK,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;YAC/B,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACpD,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACxB,KAAK,CAAC,MAAM,GAAG,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACvD,CAAC;YACD,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAC7B,CAAC;QACD,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACvC,IAAI,QAAQ,EAAE,QAAQ,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;YACzE,SAAS;QACX,CAAC;QACD,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC;IAC/B,CAAC;IAED,oBAAoB;IACpB,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,KAAK,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,MAAM,EAAE,CAAC;QACtC,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC7D,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC;QACpE,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC;QACxE,IAAI,QAAQ,IAAI,OAAO,EAAE,CAAC;YACxB,QAAQ,CAAC,IAAI,CACX,qCAAqC,OAAO,KAAK;gBAC/C,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;gBAC5C,kDAAkD,CACrD,CAAC;QACJ,CAAC;IACH,CAAC;IAED,sCAAsC;IACtC,MAAM,MAAM,GAAkB,EAAE,CAAC;IACjC,KAAK,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,MAAM,EAAE,CAAC;QAChC,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;QAC9E,IAAI,CAAC,UAAU;YAAE,SAAS;QAC1B,MAAM,KAAK,GAAgB;YACzB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,IAAI,EAAE,MAAM;YACZ,UAAU;YACV,KAAK,EAAE,KAAK,CAAC,KAAK;SACnB,CAAC;QACF,IAAI,KAAK,CAAC,MAAM;YAAE,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QAC9C,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrB,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC;IAC1B,MAAM,YAAY,GAAG,uBAAuB,CAAC,MAAM,CAAC,CAAC;IAErD,OAAO;QACL,MAAM,EAAE,YAAY;QACpB,eAAe;QACf,WAAW;QACX,YAAY;QACZ,QAAQ;KACT,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,UAAkB,EAClB,OAAgB,EAChB,UAAmB;IAEnB,MAAM,oBAAoB,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,CAAU,CAAC;IAC5D,MAAM,kBAAkB,GAAG,YAAY,CAAC;IACxC,MAAM,OAAO,GAA0B,EAAE,CAAC;IAE1C,MAAM,WAAW,GAAG,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,GAAG,GAAG,CAAC;IAC7E,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAClD,MAAM,OAAO,GAAa,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IAEhD,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,SAAS;QAElC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC/B,MAAM,UAAU,GAAG,GAAG,IAAI,GAAG,kBAAkB,EAAE,CAAC;QAClD,MAAM,UAAU,GAAG,GAAG,WAAW,GAAG,IAAI,IAAI,UAAU,EAAE,CAAC;QAEzD,IAAI,CAAC,MAAM,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,KAAK,GAAG;YAAE,SAAS;QAE/D,MAAM,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG,UAAU,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QAClD,MAAM,KAAK,GAAwB;YACjC,IAAI;YACJ,UAAU,EAAE,GAAG,MAAM,GAAG,IAAI,IAAI,UAAU,EAAE;YAC5C,OAAO,EAAE,UAAU,IAAI,EAAE;SAC1B,CAAC;QAEF,MAAM,KAAK,GAAiD,EAAE,CAAC;QAC/D,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,KAAK,MAAM,GAAG,IAAI,oBAAoB,EAAE,CAAC;YACvC,MAAM,aAAa,GAAG,GAAG,IAAI,WAAW,GAAG,EAAE,CAAC;YAC9C,MAAM,aAAa,GAAG,GAAG,WAAW,GAAG,IAAI,IAAI,aAAa,EAAE,CAAC;YAC/D,IAAI,CAAC,MAAM,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBACxD,KAAK,CAAC,GAAG,CAAC,GAAG,GAAG,MAAM,GAAG,IAAI,IAAI,aAAa,EAAE,CAAC;gBACjD,QAAQ,GAAG,IAAI,CAAC;YAClB,CAAC;QACH,CAAC;QAED,IAAI,QAAQ;YAAE,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC;QAClC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;IAED,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACrD,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Server API Types
3
+ *
4
+ * Interfaces for the emroute server.
5
+ * Consumers use `createEmrouteServer()` to get a server that handles
6
+ * SSR rendering, static file serving, and route matching.
7
+ */
8
+ import type { RoutesManifest } from '../src/type/route.type.ts';
9
+ import type { MarkdownRenderer } from '../src/type/markdown.type.ts';
10
+ import type { SpaMode, WidgetManifestEntry } from '../src/type/widget.type.ts';
11
+ import type { ContextProvider } from '../src/component/abstract.component.ts';
12
+ import type { BasePath } from '../src/route/route.core.ts';
13
+ import type { WidgetRegistry } from '../src/widget/widget.registry.ts';
14
+ import type { SsrHtmlRouter } from '../src/renderer/ssr/html.renderer.ts';
15
+ import type { SsrMdRouter } from '../src/renderer/ssr/md.renderer.ts';
16
+ /** Result of rendering a URL through an SSR renderer. */
17
+ export interface SsrRenderResult {
18
+ /** Rendered content (HTML or Markdown) */
19
+ content: string;
20
+ /** HTTP status code */
21
+ status: number;
22
+ /** Page title (from the leaf route's getTitle) */
23
+ title?: string;
24
+ /** Redirect target URL (for 301/302 responses) */
25
+ redirect?: string;
26
+ }
27
+ /**
28
+ * Config for `createEmrouteServer()`.
29
+ *
30
+ * The server reads manifests from the Runtime and handles SSR rendering,
31
+ * static file serving, and route matching.
32
+ */
33
+ export interface EmrouteServerConfig {
34
+ /** Pre-built manifest (alternative to reading from runtime) */
35
+ routesManifest?: RoutesManifest;
36
+ /** Pre-built widget registry (alternative to reading from runtime) */
37
+ widgets?: WidgetRegistry;
38
+ /** SPA mode — controls which routers are constructed and what gets served */
39
+ spa?: SpaMode;
40
+ /** Base paths for SSR endpoints (default: { html: '/html', md: '/md' }) */
41
+ basePath?: BasePath;
42
+ /** Page title (fallback when no route provides one) */
43
+ title?: string;
44
+ /** Markdown renderer for server-side <mark-down> expansion */
45
+ markdownRenderer?: MarkdownRenderer;
46
+ /** Enrich every ComponentContext with app-level services. */
47
+ extendContext?: ContextProvider;
48
+ }
49
+ /**
50
+ * An emroute server instance.
51
+ *
52
+ * Handles SSR rendering, static file serving, and route matching.
53
+ * Use `handleRequest(req)` to compose with your own request handling.
54
+ */
55
+ export interface EmrouteServer {
56
+ /**
57
+ * Handle an HTTP request for SSR routes and bare paths.
58
+ * Returns `null` for unmatched file requests — consumer handles 404.
59
+ */
60
+ handleRequest(req: Request): Promise<Response | null>;
61
+ /** The SSR HTML router (null in 'only' mode — no server rendering). */
62
+ readonly htmlRouter: SsrHtmlRouter | null;
63
+ /** The SSR Markdown router (null in 'only' mode). */
64
+ readonly mdRouter: SsrMdRouter | null;
65
+ /** The resolved routes manifest. */
66
+ readonly manifest: RoutesManifest;
67
+ /** Discovered widget entries. */
68
+ readonly widgetEntries: WidgetManifestEntry[];
69
+ /** The resolved HTML shell. */
70
+ readonly shell: string;
71
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Server API Types
3
+ *
4
+ * Interfaces for the emroute server.
5
+ * Consumers use `createEmrouteServer()` to get a server that handles
6
+ * SSR rendering, static file serving, and route matching.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=server-api.type.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-api.type.js","sourceRoot":"","sources":["../../server/server-api.type.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Unified Component Architecture
3
+ *
4
+ * Everything is a Component: pages and widgets.
5
+ * Components render differently based on context:
6
+ * - /md/* → Markdown (LLMs, text clients)
7
+ * - /html/* → Pre-rendered HTML (SSR)
8
+ * - SPA → Hydrated custom elements
9
+ *
10
+ * Precedence (like .ts/.html/.md):
11
+ * - renderHTML() if defined → full HTML control
12
+ * - renderMarkdown() → converted to HTML via markdown renderer
13
+ */
14
+ import type { RouteInfo } from '../type/route.type.ts';
15
+ /**
16
+ * Context passed to components during rendering.
17
+ * Extends RouteInfo (pathname, pattern, params, searchParams)
18
+ * with pre-loaded file content and an abort signal.
19
+ *
20
+ * Consumers can extend this interface via module augmentation
21
+ * to add app-level services (RPC clients, auth, feature flags, etc.).
22
+ */
23
+ /** Shape of companion file contents (html, md, css). Used by generated `.page.files.g.ts` modules. */
24
+ export type FileContents = {
25
+ html?: string;
26
+ md?: string;
27
+ css?: string;
28
+ };
29
+ export interface ComponentContext extends RouteInfo {
30
+ readonly files?: Readonly<FileContents>;
31
+ readonly signal?: AbortSignal;
32
+ /** True when this component is the leaf (matched) route, false when rendered as a layout parent. */
33
+ readonly isLeaf?: boolean;
34
+ /** Base path for SSR HTML links (e.g. '/html'). */
35
+ readonly basePath?: string;
36
+ }
37
+ /**
38
+ * Callback that enriches the base ComponentContext with app-level services.
39
+ * Registered once at router creation; called for every context construction.
40
+ *
41
+ * **1. Register** — always spread `base` to preserve routing/file/signal data:
42
+ * ```ts
43
+ * createSpaHtmlRouter(manifest, {
44
+ * extendContext: (base) => ({ ...base, rpc: myRpcClient }),
45
+ * });
46
+ * ```
47
+ *
48
+ * **2. Access** — expose custom properties to components via module augmentation:
49
+ * ```ts
50
+ * declare module '@emkodev/emroute' {
51
+ * interface ComponentContext { rpc: RpcClient; }
52
+ * }
53
+ * ```
54
+ * or per-component via the third generic:
55
+ * ```ts
56
+ * class MyPage extends PageComponent<Params, Data, AppContext> {}
57
+ * ```
58
+ */
59
+ export type ContextProvider = (base: ComponentContext) => ComponentContext;
60
+ /**
61
+ * Render context determines how components are rendered.
62
+ */
63
+ export type RenderContext = 'markdown' | 'html' | 'spa';
64
+ /**
65
+ * Abstract base class for all components.
66
+ *
67
+ * Subclasses must implement:
68
+ * - name: unique identifier for custom element tag
69
+ * - getData(): fetch/compute data
70
+ * - renderMarkdown(): render as markdown
71
+ *
72
+ * Optional override:
73
+ * - renderHTML(): custom HTML rendering (defaults to markdown→HTML conversion)
74
+ * - validateParams(): params validation
75
+ *
76
+ * @typeParam TContext — custom context shape; defaults to ComponentContext.
77
+ * Use with `extendContext` on the router to inject app-level services.
78
+ * See {@link ContextProvider} for details.
79
+ */
80
+ export declare abstract class Component<TParams = unknown, TData = unknown, TContext extends ComponentContext = ComponentContext> {
81
+ /** Type carrier for getData args — use as `this['DataArgs']` in overrides. */
82
+ readonly DataArgs: {
83
+ params: TParams;
84
+ signal?: AbortSignal;
85
+ context: TContext;
86
+ };
87
+ /** Type carrier for render args — use as `this['RenderArgs']` in overrides. */
88
+ readonly RenderArgs: {
89
+ data: TData | null;
90
+ params: TParams;
91
+ context: TContext;
92
+ };
93
+ /** Unique name in kebab-case. Used for custom element: `<widget-{name}>` */
94
+ abstract readonly name: string;
95
+ /** Host element reference, set by ComponentElement in the browser. */
96
+ element?: HTMLElement;
97
+ /** Associated file paths for pre-loaded content (html, md, css). */
98
+ readonly files?: {
99
+ html?: string;
100
+ md?: string;
101
+ css?: string;
102
+ };
103
+ /**
104
+ * When true, SSR serializes the getData() result into the element's
105
+ * light DOM so the client can access it immediately in hydrate()
106
+ * without re-fetching.
107
+ *
108
+ * Default is false — hydrate() receives `data: null`. Most widgets
109
+ * don't need this because the rendered Shadow DOM already contains
110
+ * the visual representation of the data.
111
+ *
112
+ * If you find yourself parsing the shadow DOM in hydrate() trying to
113
+ * reconstruct the original data object, set this to true instead.
114
+ * The server-fetched data will be available as `args.data` in hydrate().
115
+ */
116
+ readonly exposeSsrData?: boolean;
117
+ /**
118
+ * Fetch or compute data based on params.
119
+ * Called server-side for SSR, client-side for SPA.
120
+ *
121
+ * @example
122
+ * ```ts
123
+ * override async getData({ params, signal }: this['DataArgs']) {
124
+ * const res = await fetch(`/api/${params.id}`, { signal });
125
+ * return res.json();
126
+ * }
127
+ * ```
128
+ */
129
+ abstract getData(args: this['DataArgs']): Promise<TData | null>;
130
+ /**
131
+ * Render as markdown.
132
+ * This is the canonical content representation.
133
+ *
134
+ * @example
135
+ * ```ts
136
+ * override renderMarkdown({ data }: this['RenderArgs']) {
137
+ * return `# ${data?.title}`;
138
+ * }
139
+ * ```
140
+ */
141
+ abstract renderMarkdown(args: this['RenderArgs']): string;
142
+ /**
143
+ * Render as HTML for browser context.
144
+ *
145
+ * Default implementation converts renderMarkdown() output to HTML.
146
+ * Override for custom HTML rendering with rich styling/interactivity.
147
+ */
148
+ renderHTML(args: this['RenderArgs']): string;
149
+ /**
150
+ * Hydration hook called after SSR content is adopted or after SPA rendering.
151
+ * Use to attach event listeners to existing DOM without re-rendering.
152
+ *
153
+ * @example
154
+ * ```ts
155
+ * override hydrate({ data, params, context }: this['RenderArgs']) {
156
+ * const button = this.element?.querySelector('button');
157
+ * button?.addEventListener('click', () => this.deleteItem(data.id));
158
+ * }
159
+ * ```
160
+ */
161
+ hydrate?(args: this['RenderArgs']): void;
162
+ /**
163
+ * Cleanup hook called when the component is removed from the DOM.
164
+ * Use for clearing timers, removing event listeners, unmounting
165
+ * third-party renderers, closing connections, etc.
166
+ *
167
+ * Intentionally synchronous (called from disconnectedCallback). You can
168
+ * fire async cleanup here, but it will not be awaited.
169
+ */
170
+ destroy?(): void;
171
+ /**
172
+ * Validate params.
173
+ * @returns Error message if invalid, undefined if valid.
174
+ */
175
+ validateParams?(params: TParams): string | undefined;
176
+ /**
177
+ * Render error state.
178
+ */
179
+ renderError(args: {
180
+ error: unknown;
181
+ params: TParams;
182
+ }): string;
183
+ /**
184
+ * Render error as markdown.
185
+ */
186
+ renderMarkdownError(error: unknown): string;
187
+ }
188
+ /**
189
+ * Component manifest entry for code generation.
190
+ */
191
+ export interface ComponentManifestEntry {
192
+ name: string;
193
+ modulePath: string;
194
+ tagName: string;
195
+ type: 'page' | 'widget';
196
+ pattern?: string;
197
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Unified Component Architecture
3
+ *
4
+ * Everything is a Component: pages and widgets.
5
+ * Components render differently based on context:
6
+ * - /md/* → Markdown (LLMs, text clients)
7
+ * - /html/* → Pre-rendered HTML (SSR)
8
+ * - SPA → Hydrated custom elements
9
+ *
10
+ * Precedence (like .ts/.html/.md):
11
+ * - renderHTML() if defined → full HTML control
12
+ * - renderMarkdown() → converted to HTML via markdown renderer
13
+ */
14
+ import { escapeHtml } from "../util/html.util.js";
15
+ /**
16
+ * Abstract base class for all components.
17
+ *
18
+ * Subclasses must implement:
19
+ * - name: unique identifier for custom element tag
20
+ * - getData(): fetch/compute data
21
+ * - renderMarkdown(): render as markdown
22
+ *
23
+ * Optional override:
24
+ * - renderHTML(): custom HTML rendering (defaults to markdown→HTML conversion)
25
+ * - validateParams(): params validation
26
+ *
27
+ * @typeParam TContext — custom context shape; defaults to ComponentContext.
28
+ * Use with `extendContext` on the router to inject app-level services.
29
+ * See {@link ContextProvider} for details.
30
+ */
31
+ export class Component {
32
+ /** Host element reference, set by ComponentElement in the browser. */
33
+ element;
34
+ /** Associated file paths for pre-loaded content (html, md, css). */
35
+ files;
36
+ /**
37
+ * When true, SSR serializes the getData() result into the element's
38
+ * light DOM so the client can access it immediately in hydrate()
39
+ * without re-fetching.
40
+ *
41
+ * Default is false — hydrate() receives `data: null`. Most widgets
42
+ * don't need this because the rendered Shadow DOM already contains
43
+ * the visual representation of the data.
44
+ *
45
+ * If you find yourself parsing the shadow DOM in hydrate() trying to
46
+ * reconstruct the original data object, set this to true instead.
47
+ * The server-fetched data will be available as `args.data` in hydrate().
48
+ */
49
+ exposeSsrData;
50
+ /**
51
+ * Render as HTML for browser context.
52
+ *
53
+ * Default implementation converts renderMarkdown() output to HTML.
54
+ * Override for custom HTML rendering with rich styling/interactivity.
55
+ */
56
+ renderHTML(args) {
57
+ if (args.data === null) {
58
+ return `<div data-component="${this.name}">Loading...</div>`;
59
+ }
60
+ // Default: wrap markdown in a container
61
+ // The actual markdown→HTML conversion happens at render time
62
+ const markdown = this.renderMarkdown({
63
+ data: args.data,
64
+ params: args.params,
65
+ context: args.context,
66
+ });
67
+ return `<div data-component="${this.name}" data-markdown>${escapeHtml(markdown)}</div>`;
68
+ }
69
+ /**
70
+ * Render error state.
71
+ */
72
+ renderError(args) {
73
+ const msg = args.error instanceof Error ? args.error.message : String(args.error);
74
+ return `<div data-component="${this.name}">Error: ${escapeHtml(msg)}</div>`;
75
+ }
76
+ /**
77
+ * Render error as markdown.
78
+ */
79
+ renderMarkdownError(error) {
80
+ const msg = error instanceof Error ? error.message : String(error);
81
+ return `> **Error** (\`${this.name}\`): ${msg}`;
82
+ }
83
+ }
84
+ //# sourceMappingURL=abstract.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abstract.component.js","sourceRoot":"","sources":["../../../src/component/abstract.component.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAmDlD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,OAAgB,SAAS;IAsB7B,sEAAsE;IACtE,OAAO,CAAe;IAEtB,oEAAoE;IAC3D,KAAK,CAAgD;IAE9D;;;;;;;;;;;;OAYG;IACM,aAAa,CAAW;IA6BjC;;;;;OAKG;IACH,UAAU,CAAC,IAAwB;QACjC,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;YACvB,OAAO,wBAAwB,IAAI,CAAC,IAAI,oBAAoB,CAAC;QAC/D,CAAC;QACD,wCAAwC;QACxC,6DAA6D;QAC7D,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,CAAC;YACnC,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,OAAO,EAAE,IAAI,CAAC,OAAO;SACtB,CAAC,CAAC;QACH,OAAO,wBAAwB,IAAI,CAAC,IAAI,mBAAmB,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC;IAC1F,CAAC;IAgCD;;OAEG;IACH,WAAW,CAAC,IAAyC;QACnD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClF,OAAO,wBAAwB,IAAI,CAAC,IAAI,YAAY,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC;IAC9E,CAAC;IAED;;OAEG;IACH,mBAAmB,CAAC,KAAc;QAChC,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACnE,OAAO,kBAAkB,IAAI,CAAC,IAAI,QAAQ,GAAG,EAAE,CAAC;IAClD,CAAC;CACF"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Page Component
3
+ *
4
+ * Page component — params come from URL, context carries file content.
5
+ *
6
+ * Default implementations follow the fallback table:
7
+ * - renderHTML: html file → md via <mark-down> → <router-slot /> (non-leaf only)
8
+ * - renderMarkdown: md file → ```router-slot\n``` (non-leaf only)
9
+ * - getData: no-op (returns null)
10
+ */
11
+ import { Component, type ComponentContext } from './abstract.component.ts';
12
+ export declare class PageComponent<TParams extends Record<string, string> = Record<string, string>, TData = unknown, TContext extends ComponentContext = ComponentContext> extends Component<TParams, TData, TContext> {
13
+ readonly name: string;
14
+ /** Route pattern this page handles (optional — set by subclasses) */
15
+ readonly pattern?: string;
16
+ /**
17
+ * Fetch or compute page data. Override in subclasses.
18
+ * Default: returns null (no data needed).
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * override getData({ params, context }: this['DataArgs']) {
23
+ * return fetch(`/api/${params.id}`, { signal: context?.signal });
24
+ * }
25
+ * ```
26
+ */
27
+ getData(_args: this['DataArgs']): Promise<TData | null>;
28
+ /**
29
+ * Render page as HTML.
30
+ *
31
+ * Fallback chain:
32
+ * 1. html file content from context
33
+ * 2. md file content wrapped in `<mark-down>`
34
+ * 3. `<router-slot />` (bare slot for child routes)
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * override renderHTML({ data, params, context }: this['RenderArgs']) {
39
+ * return `<h1>${params.id}</h1><p>${context?.files?.html ?? ''}</p>`;
40
+ * }
41
+ * ```
42
+ */
43
+ renderHTML(args: this['RenderArgs']): string;
44
+ /**
45
+ * Render page as Markdown.
46
+ *
47
+ * Fallback chain:
48
+ * 1. md file content from context
49
+ * 2. `` ```router-slot\n``` `` (slot placeholder in markdown — newline required)
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * override renderMarkdown({ data, params, context }: this['RenderArgs']) {
54
+ * return `# ${params.id}\n\n${context?.files?.md ?? ''}`;
55
+ * }
56
+ * ```
57
+ */
58
+ renderMarkdown(args: this['RenderArgs']): string;
59
+ /**
60
+ * Page title. Override in subclasses.
61
+ * Default: undefined (no title).
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * override getTitle({ data, params }: this['RenderArgs']) {
66
+ * return `Project ${params.id}`;
67
+ * }
68
+ * ```
69
+ */
70
+ getTitle(_args: this['RenderArgs']): string | undefined;
71
+ }
72
+ /** Shared default instance used by renderers when no custom .page.ts exists. */
73
+ declare const _default: PageComponent<Record<string, string>, unknown, ComponentContext>;
74
+ export default _default;