@emkodev/emroute 1.7.3 → 1.8.0-beta.1
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.
- package/README.md +1 -1
- package/core/component/abstract.component.ts +74 -0
- package/{src → core}/component/page.component.ts +3 -61
- package/core/component/widget.component.ts +54 -0
- package/core/pipeline/pipeline.ts +224 -0
- package/{src/renderer/ssr → core/renderer}/html.renderer.ts +19 -45
- package/{src/renderer/ssr → core/renderer}/md.renderer.ts +19 -39
- package/{src/renderer/ssr → core/renderer}/ssr.renderer.ts +41 -52
- package/{src/route → core/router}/route.resolver.ts +1 -10
- package/core/router/route.trie.ts +175 -0
- package/core/runtime/abstract.runtime.ts +47 -0
- package/core/server/emroute.server.ts +346 -0
- package/core/type/component.type.ts +39 -0
- package/core/type/element.type.ts +10 -0
- package/core/type/logger.type.ts +20 -0
- package/core/type/markdown.type.ts +8 -0
- package/core/type/route-tree.type.ts +28 -0
- package/core/type/route.type.ts +75 -0
- package/core/type/widget.type.ts +27 -0
- package/core/util/html.util.ts +50 -0
- package/{src → core}/util/md.util.ts +0 -2
- package/{src/route → core/util}/route-tree.util.ts +0 -2
- package/{src → core}/util/widget-resolve.util.ts +12 -42
- package/{src → core}/widget/widget.parser.ts +0 -21
- package/core/widget/widget.registry.ts +24 -0
- package/dist/core/component/abstract.component.d.ts +48 -0
- package/dist/core/component/abstract.component.js +42 -0
- package/dist/core/component/abstract.component.js.map +1 -0
- package/dist/core/component/page.component.d.ts +23 -0
- package/dist/core/component/page.component.js +49 -0
- package/dist/core/component/page.component.js.map +1 -0
- package/dist/core/component/widget.component.d.ts +17 -0
- package/dist/core/component/widget.component.js +37 -0
- package/dist/core/component/widget.component.js.map +1 -0
- package/dist/core/pipeline/pipeline.d.ts +61 -0
- package/dist/core/pipeline/pipeline.js +189 -0
- package/dist/core/pipeline/pipeline.js.map +1 -0
- package/dist/{src/renderer/ssr → core/renderer}/html.renderer.d.ts +8 -24
- package/dist/{src/renderer/ssr → core/renderer}/html.renderer.js +12 -33
- package/dist/core/renderer/html.renderer.js.map +1 -0
- package/dist/{src/renderer/ssr → core/renderer}/md.renderer.d.ts +6 -21
- package/dist/{src/renderer/ssr → core/renderer}/md.renderer.js +13 -30
- package/dist/core/renderer/md.renderer.js.map +1 -0
- package/dist/{src/renderer/ssr → core/renderer}/ssr.renderer.d.ts +11 -17
- package/dist/{src/renderer/ssr → core/renderer}/ssr.renderer.js +33 -35
- package/dist/core/renderer/ssr.renderer.js.map +1 -0
- package/dist/{src/route → core/router}/route.resolver.d.ts +1 -8
- package/dist/{src/route → core/router}/route.resolver.js +0 -1
- package/dist/core/router/route.resolver.js.map +1 -0
- package/dist/core/router/route.trie.d.ts +32 -0
- package/dist/core/router/route.trie.js +152 -0
- package/dist/core/router/route.trie.js.map +1 -0
- package/dist/core/runtime/abstract.runtime.d.ts +32 -0
- package/dist/core/runtime/abstract.runtime.js +26 -0
- package/dist/core/runtime/abstract.runtime.js.map +1 -0
- package/dist/core/server/emroute.server.d.ts +48 -0
- package/dist/core/server/emroute.server.js +261 -0
- package/dist/core/server/emroute.server.js.map +1 -0
- package/dist/core/server/server.type.d.ts +45 -0
- package/dist/core/server/server.type.js +11 -0
- package/dist/core/server/server.type.js.map +1 -0
- package/dist/core/type/component.type.d.ts +37 -0
- package/dist/core/type/component.type.js +7 -0
- package/dist/core/type/component.type.js.map +1 -0
- package/dist/core/type/element.type.d.ts +9 -0
- package/dist/core/type/element.type.js +5 -0
- package/dist/core/type/element.type.js.map +1 -0
- package/dist/core/type/logger.type.d.ts +14 -0
- package/dist/core/type/logger.type.js +8 -0
- package/dist/core/type/logger.type.js.map +1 -0
- package/dist/core/type/markdown.type.d.ts +7 -0
- package/dist/core/type/markdown.type.js +5 -0
- package/dist/core/type/markdown.type.js.map +1 -0
- package/dist/{src → core}/type/route-tree.type.d.ts +0 -12
- package/dist/{src → core}/type/route-tree.type.js +0 -1
- package/dist/core/type/route-tree.type.js.map +1 -0
- package/dist/core/type/route.type.d.ts +62 -0
- package/dist/core/type/route.type.js +7 -0
- package/dist/core/type/route.type.js.map +1 -0
- package/dist/core/type/widget.type.d.ts +27 -0
- package/dist/core/type/widget.type.js +5 -0
- package/dist/core/type/widget.type.js.map +1 -0
- package/dist/core/util/html.util.d.ts +14 -0
- package/dist/core/util/html.util.js +43 -0
- package/dist/core/util/html.util.js.map +1 -0
- package/dist/{src → core}/util/md.util.d.ts +0 -1
- package/dist/{src → core}/util/md.util.js +0 -2
- package/dist/core/util/md.util.js.map +1 -0
- package/dist/{src/route → core/util}/route-tree.util.js +0 -2
- package/dist/core/util/route-tree.util.js.map +1 -0
- package/dist/core/util/widget-resolve.util.d.ts +32 -0
- package/dist/{src → core}/util/widget-resolve.util.js +10 -40
- package/dist/core/util/widget-resolve.util.js.map +1 -0
- package/dist/{src → core}/widget/widget.parser.d.ts +0 -13
- package/dist/{src → core}/widget/widget.parser.js +0 -21
- package/dist/core/widget/widget.parser.js.map +1 -0
- package/dist/core/widget/widget.registry.d.ts +13 -0
- package/dist/core/widget/widget.registry.js +19 -0
- package/dist/core/widget/widget.registry.js.map +1 -0
- package/dist/emroute.js +1077 -1195
- package/dist/emroute.js.map +36 -5
- package/dist/runtime/abstract.runtime.d.ts +41 -7
- package/dist/runtime/abstract.runtime.js +394 -7
- package/dist/runtime/abstract.runtime.js.map +1 -1
- package/dist/runtime/bun/fs/bun-fs.runtime.d.ts +1 -0
- package/dist/runtime/bun/fs/bun-fs.runtime.js +15 -1
- package/dist/runtime/bun/fs/bun-fs.runtime.js.map +1 -1
- package/dist/runtime/bun/sqlite/bun-sqlite.runtime.d.ts +2 -0
- package/dist/runtime/bun/sqlite/bun-sqlite.runtime.js +8 -0
- package/dist/runtime/bun/sqlite/bun-sqlite.runtime.js.map +1 -1
- package/dist/runtime/fetch.runtime.d.ts +3 -3
- package/dist/runtime/fetch.runtime.js +3 -3
- package/dist/runtime/sitemap.generator.d.ts +1 -1
- package/dist/runtime/sitemap.generator.js +1 -1
- package/dist/runtime/sitemap.generator.js.map +1 -1
- package/dist/runtime/universal/fs/universal-fs.runtime.d.ts +1 -0
- package/dist/runtime/universal/fs/universal-fs.runtime.js +15 -1
- package/dist/runtime/universal/fs/universal-fs.runtime.js.map +1 -1
- package/dist/server/build.util.d.ts +9 -10
- package/dist/server/build.util.js +11 -31
- package/dist/server/build.util.js.map +1 -1
- package/dist/server/codegen.util.d.ts +1 -1
- package/dist/server/emroute.server.d.ts +8 -35
- package/dist/server/emroute.server.js +7 -351
- package/dist/server/emroute.server.js.map +1 -1
- package/dist/server/esbuild-manifest.plugin.js +1 -1
- package/dist/server/esbuild-manifest.plugin.js.map +1 -1
- package/dist/server/server-api.type.d.ts +3 -71
- package/dist/server/server-api.type.js +1 -8
- package/dist/server/server-api.type.js.map +1 -1
- package/dist/src/element/component.element.d.ts +4 -7
- package/dist/src/element/component.element.js +6 -19
- package/dist/src/element/component.element.js.map +1 -1
- package/dist/src/element/markdown.element.d.ts +2 -2
- package/dist/src/element/markdown.element.js +3 -2
- package/dist/src/element/markdown.element.js.map +1 -1
- package/dist/src/index.d.ts +15 -14
- package/dist/src/index.js +8 -8
- package/dist/src/index.js.map +1 -1
- package/dist/src/renderer/spa/emroute.app.d.ts +50 -0
- package/dist/src/renderer/spa/emroute.app.js +246 -0
- package/dist/src/renderer/spa/emroute.app.js.map +1 -0
- package/dist/src/renderer/spa/mod.d.ts +17 -16
- package/dist/src/renderer/spa/mod.js +9 -9
- package/dist/src/renderer/spa/mod.js.map +1 -1
- package/dist/src/renderer/spa/thin-client.d.ts +3 -3
- package/dist/src/renderer/spa/thin-client.js +7 -7
- package/dist/src/renderer/spa/thin-client.js.map +1 -1
- package/dist/src/route/route.core.d.ts +3 -3
- package/dist/src/util/html.util.d.ts +5 -22
- package/dist/src/util/html.util.js +8 -56
- package/dist/src/util/html.util.js.map +1 -1
- package/dist/src/widget/breadcrumb.widget.d.ts +2 -2
- package/dist/src/widget/breadcrumb.widget.js +2 -2
- package/dist/src/widget/breadcrumb.widget.js.map +1 -1
- package/dist/src/widget/page-title.widget.d.ts +1 -1
- package/dist/src/widget/page-title.widget.js +1 -1
- package/dist/src/widget/page-title.widget.js.map +1 -1
- package/package.json +8 -8
- package/runtime/abstract.runtime.ts +418 -10
- package/runtime/bun/fs/bun-fs.runtime.ts +15 -1
- package/runtime/bun/sqlite/bun-sqlite.runtime.ts +9 -0
- package/runtime/fetch.runtime.ts +3 -3
- package/runtime/sitemap.generator.ts +2 -2
- package/runtime/universal/fs/universal-fs.runtime.ts +15 -1
- package/server/build.util.ts +17 -43
- package/server/codegen.util.ts +1 -1
- package/server/emroute.server.ts +12 -426
- package/src/element/component.element.ts +8 -27
- package/src/element/markdown.element.ts +4 -3
- package/src/index.ts +22 -19
- package/src/renderer/spa/{thin-client.ts → emroute.app.ts} +18 -19
- package/src/renderer/spa/mod.ts +22 -22
- package/src/util/html.util.ts +16 -61
- package/src/widget/breadcrumb.widget.ts +3 -3
- package/src/widget/page-title.widget.ts +1 -1
- package/dist/src/component/abstract.component.d.ts +0 -199
- package/dist/src/component/abstract.component.js +0 -84
- package/dist/src/component/abstract.component.js.map +0 -1
- package/dist/src/component/page.component.d.ts +0 -74
- package/dist/src/component/page.component.js +0 -107
- package/dist/src/component/page.component.js.map +0 -1
- package/dist/src/component/widget.component.d.ts +0 -47
- package/dist/src/component/widget.component.js +0 -69
- package/dist/src/component/widget.component.js.map +0 -1
- package/dist/src/renderer/ssr/html.renderer.js.map +0 -1
- package/dist/src/renderer/ssr/md.renderer.js.map +0 -1
- package/dist/src/renderer/ssr/ssr.renderer.js.map +0 -1
- package/dist/src/route/route-tree.util.js.map +0 -1
- package/dist/src/route/route.matcher.d.ts +0 -86
- package/dist/src/route/route.matcher.js +0 -214
- package/dist/src/route/route.matcher.js.map +0 -1
- package/dist/src/route/route.resolver.js.map +0 -1
- package/dist/src/route/route.trie.d.ts +0 -38
- package/dist/src/route/route.trie.js +0 -206
- package/dist/src/route/route.trie.js.map +0 -1
- package/dist/src/type/element.type.d.ts +0 -19
- package/dist/src/type/element.type.js +0 -9
- package/dist/src/type/element.type.js.map +0 -1
- package/dist/src/type/logger.type.d.ts +0 -17
- package/dist/src/type/logger.type.js +0 -9
- package/dist/src/type/logger.type.js.map +0 -1
- package/dist/src/type/markdown.type.d.ts +0 -20
- package/dist/src/type/markdown.type.js +0 -2
- package/dist/src/type/markdown.type.js.map +0 -1
- package/dist/src/type/route-tree.type.js.map +0 -1
- package/dist/src/type/route.type.d.ts +0 -94
- package/dist/src/type/route.type.js +0 -8
- package/dist/src/type/route.type.js.map +0 -1
- package/dist/src/type/widget.type.d.ts +0 -55
- package/dist/src/type/widget.type.js +0 -10
- package/dist/src/type/widget.type.js.map +0 -1
- package/dist/src/util/logger.util.d.ts +0 -26
- package/dist/src/util/logger.util.js +0 -80
- package/dist/src/util/logger.util.js.map +0 -1
- package/dist/src/util/md.util.js.map +0 -1
- package/dist/src/util/widget-resolve.util.d.ts +0 -52
- package/dist/src/util/widget-resolve.util.js.map +0 -1
- package/dist/src/widget/widget.parser.js.map +0 -1
- package/dist/src/widget/widget.registry.d.ts +0 -23
- package/dist/src/widget/widget.registry.js +0 -42
- package/dist/src/widget/widget.registry.js.map +0 -1
- package/runtime/bun/esbuild-runtime-loader.plugin.ts +0 -112
- package/server/esbuild-manifest.plugin.ts +0 -209
- package/server/server-api.type.ts +0 -101
- package/src/component/abstract.component.ts +0 -231
- package/src/component/widget.component.ts +0 -85
- package/src/route/route.core.ts +0 -371
- package/src/route/route.trie.ts +0 -265
- package/src/type/element.type.ts +0 -22
- package/src/type/logger.type.ts +0 -24
- package/src/type/markdown.type.ts +0 -21
- package/src/type/route-tree.type.ts +0 -51
- package/src/type/route.type.ts +0 -124
- package/src/type/widget.type.ts +0 -65
- package/src/util/logger.util.ts +0 -83
- package/src/widget/widget.registry.ts +0 -51
- /package/dist/{src/route → core/util}/route-tree.util.d.ts +0 -0
package/src/route/route.trie.ts
DELETED
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Route Trie
|
|
3
|
-
*
|
|
4
|
-
* Segment-based trie implementing RouteResolver for O(depth) route matching.
|
|
5
|
-
*
|
|
6
|
-
* Each URL segment maps to a trie node. Nodes are tried in order:
|
|
7
|
-
* static → dynamic (:param) → wildcard (:rest*). Backtracking handles
|
|
8
|
-
* cases where a dynamic path leads to a dead end but a wildcard at an
|
|
9
|
-
* ancestor would match.
|
|
10
|
-
*
|
|
11
|
-
* Static segment matching is case-sensitive, per RFC 3986.
|
|
12
|
-
*
|
|
13
|
-
* Accepts a RouteNode tree (the JSON-serializable manifest from Runtime)
|
|
14
|
-
* and converts it to an internal trie with Map-based static children for
|
|
15
|
-
* O(1) segment lookup.
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import type { RouteNode } from '../type/route-tree.type.ts';
|
|
19
|
-
import type { RouteResolver, ResolvedRoute } from './route.resolver.ts';
|
|
20
|
-
|
|
21
|
-
/** Internal trie node with Map for O(1) static child lookup. */
|
|
22
|
-
interface TrieNode {
|
|
23
|
-
/** RouteNode when this node is a terminal route. */
|
|
24
|
-
route?: RouteNode;
|
|
25
|
-
/** Reconstructed pattern for this node (e.g. "/projects/:id"). */
|
|
26
|
-
pattern?: string;
|
|
27
|
-
/** Error boundary module path scoped to this prefix. */
|
|
28
|
-
errorBoundary?: string;
|
|
29
|
-
/** Static children keyed by exact segment. */
|
|
30
|
-
static: Map<string, TrieNode>;
|
|
31
|
-
/** Dynamic child for single-segment params (:param). */
|
|
32
|
-
dynamic?: { param: string; node: TrieNode };
|
|
33
|
-
/** Wildcard child for catch-all params (:rest*). */
|
|
34
|
-
wildcard?: { param: string; node: TrieNode };
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function createNode(): TrieNode {
|
|
38
|
-
return { static: new Map() };
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/** Try decodeURIComponent, return the original segment on malformed input. */
|
|
42
|
-
function safeDecode(segment: string): string {
|
|
43
|
-
try {
|
|
44
|
-
return decodeURIComponent(segment);
|
|
45
|
-
} catch {
|
|
46
|
-
return segment;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Split a normalized pathname into segments.
|
|
52
|
-
* Assumes leading slash, no trailing slash. Must NOT be called with '/'.
|
|
53
|
-
*/
|
|
54
|
-
function splitSegments(pathname: string): string[] {
|
|
55
|
-
return pathname.substring(1).split('/');
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Convert a RouteNode tree into a TrieNode tree.
|
|
60
|
-
* Recursively walks the RouteNode, converting Record children to Map
|
|
61
|
-
* and reconstructing URL patterns at each node.
|
|
62
|
-
*/
|
|
63
|
-
function convertNode(source: RouteNode, pattern: string): TrieNode {
|
|
64
|
-
const node = createNode();
|
|
65
|
-
|
|
66
|
-
// Terminal route (has files or redirect)
|
|
67
|
-
if (source.files || source.redirect) {
|
|
68
|
-
node.route = source;
|
|
69
|
-
node.pattern = pattern;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Error boundary
|
|
73
|
-
if (source.errorBoundary) {
|
|
74
|
-
node.errorBoundary = source.errorBoundary;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Static children
|
|
78
|
-
if (source.children) {
|
|
79
|
-
for (const [segment, child] of Object.entries(source.children)) {
|
|
80
|
-
const childPattern = pattern === '/' ? `/${segment}` : `${pattern}/${segment}`;
|
|
81
|
-
node.static.set(segment, convertNode(child, childPattern));
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Dynamic child
|
|
86
|
-
if (source.dynamic) {
|
|
87
|
-
const { param, child } = source.dynamic;
|
|
88
|
-
const childPattern = pattern === '/' ? `/:${param}` : `${pattern}/:${param}`;
|
|
89
|
-
node.dynamic = { param, node: convertNode(child, childPattern) };
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Wildcard child
|
|
93
|
-
if (source.wildcard) {
|
|
94
|
-
const { param, child } = source.wildcard;
|
|
95
|
-
const childPattern = pattern === '/' ? `/:${param}*` : `${pattern}/:${param}*`;
|
|
96
|
-
node.wildcard = { param, node: convertNode(child, childPattern) };
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return node;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Trie-based route resolver.
|
|
104
|
-
*
|
|
105
|
-
* Implements RouteResolver with O(depth) matching by walking the trie.
|
|
106
|
-
* Constructed from a RouteNode tree (produced by Runtime.scanRoutes).
|
|
107
|
-
*/
|
|
108
|
-
export class RouteTrie implements RouteResolver {
|
|
109
|
-
private readonly root: TrieNode;
|
|
110
|
-
|
|
111
|
-
constructor(tree: RouteNode) {
|
|
112
|
-
this.root = convertNode(tree, '/');
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
match(pathname: string): ResolvedRoute | undefined {
|
|
116
|
-
// Normalize: strip trailing slash (except bare '/')
|
|
117
|
-
if (pathname.length > 1 && pathname.endsWith('/')) {
|
|
118
|
-
pathname = pathname.slice(0, -1);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (!pathname.startsWith('/')) {
|
|
122
|
-
pathname = '/' + pathname;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (pathname === '/') {
|
|
126
|
-
if (this.root.route) {
|
|
127
|
-
return { node: this.root.route, pattern: '/', params: {} };
|
|
128
|
-
}
|
|
129
|
-
return undefined;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const segments = splitSegments(pathname);
|
|
133
|
-
return this.walk(this.root, segments, 0, {});
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
findErrorBoundary(pathname: string): string | undefined {
|
|
137
|
-
if (pathname.length > 1 && pathname.endsWith('/')) {
|
|
138
|
-
pathname = pathname.slice(0, -1);
|
|
139
|
-
}
|
|
140
|
-
if (!pathname.startsWith('/')) {
|
|
141
|
-
pathname = '/' + pathname;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (pathname === '/') return this.root.errorBoundary;
|
|
145
|
-
|
|
146
|
-
const segments = splitSegments(pathname);
|
|
147
|
-
return this.walkForBoundary(this.root, segments, 0, this.root.errorBoundary);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
findRoute(pattern: string): RouteNode | undefined {
|
|
151
|
-
if (pattern === '/') {
|
|
152
|
-
return this.root.route;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const segments = splitSegments(pattern);
|
|
156
|
-
let node = this.root;
|
|
157
|
-
|
|
158
|
-
for (const segment of segments) {
|
|
159
|
-
let child: TrieNode | undefined;
|
|
160
|
-
|
|
161
|
-
if (segment.startsWith(':') && segment.endsWith('*')) {
|
|
162
|
-
child = node.wildcard?.node;
|
|
163
|
-
} else if (segment.startsWith(':')) {
|
|
164
|
-
child = node.dynamic?.node;
|
|
165
|
-
} else {
|
|
166
|
-
child = node.static.get(segment);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
if (!child) return undefined;
|
|
170
|
-
node = child;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
return node.route;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// ── Private matching ──────────────────────────────────────────────────
|
|
177
|
-
|
|
178
|
-
private walk(
|
|
179
|
-
node: TrieNode,
|
|
180
|
-
segments: string[],
|
|
181
|
-
index: number,
|
|
182
|
-
params: Record<string, string>,
|
|
183
|
-
): ResolvedRoute | undefined {
|
|
184
|
-
if (index === segments.length) {
|
|
185
|
-
if (node.route) {
|
|
186
|
-
return { node: node.route, pattern: node.pattern!, params: { ...params } };
|
|
187
|
-
}
|
|
188
|
-
if (node.wildcard?.node.route) {
|
|
189
|
-
return {
|
|
190
|
-
node: node.wildcard.node.route,
|
|
191
|
-
pattern: node.wildcard.node.pattern!,
|
|
192
|
-
params: { ...params, [node.wildcard.param]: '' },
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
return undefined;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const segment = segments[index];
|
|
199
|
-
|
|
200
|
-
// 1. Try static child
|
|
201
|
-
const staticChild = node.static.get(segment);
|
|
202
|
-
if (staticChild) {
|
|
203
|
-
const result = this.walk(staticChild, segments, index + 1, params);
|
|
204
|
-
if (result) return result;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// 2. Try dynamic child (single segment)
|
|
208
|
-
if (node.dynamic) {
|
|
209
|
-
const { param, node: dynamicNode } = node.dynamic;
|
|
210
|
-
params[param] = safeDecode(segment);
|
|
211
|
-
const result = this.walk(dynamicNode, segments, index + 1, params);
|
|
212
|
-
if (result) return result;
|
|
213
|
-
delete params[param];
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// 3. Try wildcard (consumes all remaining segments)
|
|
217
|
-
if (node.wildcard?.node.route) {
|
|
218
|
-
const { param, node: wildcardNode } = node.wildcard;
|
|
219
|
-
let rest = safeDecode(segments[index]);
|
|
220
|
-
for (let i = index + 1; i < segments.length; i++) {
|
|
221
|
-
rest += '/' + safeDecode(segments[i]);
|
|
222
|
-
}
|
|
223
|
-
return {
|
|
224
|
-
node: wildcardNode.route!,
|
|
225
|
-
pattern: wildcardNode.pattern!,
|
|
226
|
-
params: { ...params, [param]: rest },
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
return undefined;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* Walk for error boundary. Follows the same priority as match
|
|
235
|
-
* (static → dynamic → wildcard) without backtracking across branches.
|
|
236
|
-
* Returns the deepest error boundary module path found along the path.
|
|
237
|
-
*/
|
|
238
|
-
private walkForBoundary(
|
|
239
|
-
node: TrieNode,
|
|
240
|
-
segments: string[],
|
|
241
|
-
index: number,
|
|
242
|
-
deepest: string | undefined,
|
|
243
|
-
): string | undefined {
|
|
244
|
-
if (index === segments.length) {
|
|
245
|
-
return node.errorBoundary ?? deepest;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
const segment = segments[index];
|
|
249
|
-
|
|
250
|
-
const staticChild = node.static.get(segment);
|
|
251
|
-
if (staticChild) {
|
|
252
|
-
return this.walkForBoundary(staticChild, segments, index + 1, staticChild.errorBoundary ?? deepest);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
if (node.dynamic) {
|
|
256
|
-
return this.walkForBoundary(node.dynamic.node, segments, index + 1, node.dynamic.node.errorBoundary ?? deepest);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
if (node.wildcard) {
|
|
260
|
-
return node.wildcard.node.errorBoundary ?? deepest;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
return deepest;
|
|
264
|
-
}
|
|
265
|
-
}
|
package/src/type/element.type.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Custom Element - Type Definitions
|
|
3
|
-
*
|
|
4
|
-
* Custom elements are plain HTMLElement subclasses auto-discovered from
|
|
5
|
-
* `elementsDir/{name}/{name}.element.ts`. They are registered in the
|
|
6
|
-
* browser via `customElements.define()` during `bootEmrouteApp()`.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Custom element manifest entry.
|
|
11
|
-
* Discovered from `elementsDir/{name}/{name}.element.ts`.
|
|
12
|
-
*/
|
|
13
|
-
export interface ElementManifestEntry {
|
|
14
|
-
/** Element name in kebab-case (must contain a hyphen per web spec) */
|
|
15
|
-
name: string;
|
|
16
|
-
|
|
17
|
-
/** Path to element module file */
|
|
18
|
-
modulePath: string;
|
|
19
|
-
|
|
20
|
-
/** Custom element tag name (same as name) */
|
|
21
|
-
tagName: string;
|
|
22
|
-
}
|
package/src/type/logger.type.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Logger Interface
|
|
3
|
-
*
|
|
4
|
-
* Minimal pluggable logger for surfacing errors from silent catch blocks.
|
|
5
|
-
* Structurally compatible with hardkore's StructuredLogger — any instance
|
|
6
|
-
* of that class satisfies this interface without an explicit dependency.
|
|
7
|
-
*
|
|
8
|
-
* Default: no-op (silent degradation). Call setLogger() at startup to wire in.
|
|
9
|
-
*/
|
|
10
|
-
export interface Logger {
|
|
11
|
-
error(msg: string, error?: Error): void;
|
|
12
|
-
warn(msg: string): void;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const noop = () => {};
|
|
16
|
-
|
|
17
|
-
/** Module-level logger. Always callable — defaults to no-op. */
|
|
18
|
-
export const logger: Logger = { error: noop, warn: noop };
|
|
19
|
-
|
|
20
|
-
/** Replace the logger implementation. Call once at startup. */
|
|
21
|
-
export function setLogger(impl: Logger): void {
|
|
22
|
-
logger.error = impl.error.bind(impl);
|
|
23
|
-
logger.warn = impl.warn.bind(impl);
|
|
24
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Markdown Renderer Interface
|
|
3
|
-
*
|
|
4
|
-
* Implement this to provide custom markdown rendering.
|
|
5
|
-
* Used by MarkdownElement (browser) and SsrHtmlRouter (server).
|
|
6
|
-
*/
|
|
7
|
-
export interface MarkdownRenderer {
|
|
8
|
-
/**
|
|
9
|
-
* Initialize the renderer (e.g., load WASM).
|
|
10
|
-
* Called once before first render.
|
|
11
|
-
*/
|
|
12
|
-
init?(): Promise<void>;
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Render markdown to HTML.
|
|
16
|
-
*
|
|
17
|
-
* **Security:** Output is assigned to `innerHTML` — the renderer must
|
|
18
|
-
* sanitize dangerous markup. See `doc/08-markdown-renderer.md`.
|
|
19
|
-
*/
|
|
20
|
-
render(markdown: string): string;
|
|
21
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Route Tree
|
|
3
|
-
*
|
|
4
|
-
* Serializable tree structure that mirrors the filesystem layout.
|
|
5
|
-
* Replaces the flat array manifest as the canonical route format.
|
|
6
|
-
*
|
|
7
|
-
* Each node corresponds to a URL segment. The tree is JSON-serializable
|
|
8
|
-
* (no Maps, no classes) so it can be written to disk, sent over the wire,
|
|
9
|
-
* or used directly as the in-memory trie for O(depth) route matching.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
/** Files associated with a route (companion files discovered alongside the page). */
|
|
13
|
-
export interface RouteFiles {
|
|
14
|
-
/** TypeScript module (e.g. "routes/about.page.ts") */
|
|
15
|
-
ts?: string;
|
|
16
|
-
/** JavaScript module — merged module with inlined companions (e.g. "routes/about.page.js") */
|
|
17
|
-
js?: string;
|
|
18
|
-
/** HTML template (e.g. "routes/about.page.html") */
|
|
19
|
-
html?: string;
|
|
20
|
-
/** Markdown content (e.g. "routes/about.page.md") */
|
|
21
|
-
md?: string;
|
|
22
|
-
/** Scoped stylesheet (e.g. "routes/about.page.css") */
|
|
23
|
-
css?: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/** A single node in the route tree. */
|
|
27
|
-
export interface RouteNode {
|
|
28
|
-
/** Route files when this node is a terminal route. */
|
|
29
|
-
files?: RouteFiles;
|
|
30
|
-
|
|
31
|
-
/** Error boundary module path scoped to this prefix (from .error.ts). */
|
|
32
|
-
errorBoundary?: string;
|
|
33
|
-
|
|
34
|
-
/** Redirect module path (from .redirect.ts). Mutually exclusive with files. */
|
|
35
|
-
redirect?: string;
|
|
36
|
-
|
|
37
|
-
/** Static children keyed by URL segment (e.g. "about", "projects"). */
|
|
38
|
-
children?: Record<string, RouteNode>;
|
|
39
|
-
|
|
40
|
-
/** Single-segment dynamic param (from [param] directories/files). */
|
|
41
|
-
dynamic?: {
|
|
42
|
-
param: string;
|
|
43
|
-
child: RouteNode;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
/** Catch-all wildcard (from directory index.page.* files). */
|
|
47
|
-
wildcard?: {
|
|
48
|
-
param: string;
|
|
49
|
-
child: RouteNode;
|
|
50
|
-
};
|
|
51
|
-
}
|
package/src/type/route.type.ts
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Router Types
|
|
3
|
-
*
|
|
4
|
-
* Native browser APIs only - no external dependencies.
|
|
5
|
-
* Follows islands architecture: pages = HTML, widgets = web components.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
/** Parameters extracted from URL patterns */
|
|
9
|
-
export type RouteParams = Readonly<Record<string, string>>;
|
|
10
|
-
|
|
11
|
-
/** Immutable route context built once per navigation, shared across the render pipeline. */
|
|
12
|
-
export interface RouteInfo {
|
|
13
|
-
/** The URL being rendered. Components read pathname, searchParams, hash from this. */
|
|
14
|
-
readonly url: URL;
|
|
15
|
-
|
|
16
|
-
/** URL parameters extracted by the trie match. */
|
|
17
|
-
readonly params: RouteParams;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/** Supported file patterns in file-based routing */
|
|
21
|
-
export type RouteFileType = 'page' | 'error' | 'redirect';
|
|
22
|
-
|
|
23
|
-
/** Redirect configuration */
|
|
24
|
-
export interface RedirectConfig {
|
|
25
|
-
to: string;
|
|
26
|
-
status: 301 | 302;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/** Available files for a route */
|
|
30
|
-
export interface RouteFiles {
|
|
31
|
-
/** TypeScript module path (.page.ts) */
|
|
32
|
-
ts?: string;
|
|
33
|
-
|
|
34
|
-
/** JavaScript module — merged module with inlined companions (.page.js) */
|
|
35
|
-
js?: string;
|
|
36
|
-
|
|
37
|
-
/** HTML template path (.page.html) */
|
|
38
|
-
html?: string;
|
|
39
|
-
|
|
40
|
-
/** Markdown content path (.page.md) */
|
|
41
|
-
md?: string;
|
|
42
|
-
|
|
43
|
-
/** CSS stylesheet path (.page.css) */
|
|
44
|
-
css?: string;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/** Route configuration for a single route */
|
|
48
|
-
export interface RouteConfig {
|
|
49
|
-
/** URLPattern pathname pattern (e.g., '/projects/:id') */
|
|
50
|
-
pattern: string;
|
|
51
|
-
|
|
52
|
-
/** Type of route file */
|
|
53
|
-
type: RouteFileType;
|
|
54
|
-
|
|
55
|
-
/** Module path for dynamic import (primary file based on precedence) */
|
|
56
|
-
modulePath: string;
|
|
57
|
-
|
|
58
|
-
/** Available files for this route */
|
|
59
|
-
files?: RouteFiles;
|
|
60
|
-
|
|
61
|
-
/** Parent route pattern for nested routes */
|
|
62
|
-
parent?: string;
|
|
63
|
-
|
|
64
|
-
/** HTTP status code (for status-specific pages like 404, 401, 403) */
|
|
65
|
-
statusCode?: number;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/** Result of matching a URL against routes */
|
|
69
|
-
export interface MatchedRoute {
|
|
70
|
-
/** The matched route configuration */
|
|
71
|
-
readonly route: RouteConfig;
|
|
72
|
-
|
|
73
|
-
/** Extracted URL parameters */
|
|
74
|
-
readonly params: RouteParams;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/** Error boundary configuration */
|
|
78
|
-
export interface ErrorBoundary {
|
|
79
|
-
/** Pattern prefix this error boundary handles */
|
|
80
|
-
pattern: string;
|
|
81
|
-
|
|
82
|
-
/** Module path for the error handler */
|
|
83
|
-
modulePath: string;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export type { RouteNode } from './route-tree.type.ts';
|
|
87
|
-
|
|
88
|
-
/** Router state for history management */
|
|
89
|
-
export interface RouterState {
|
|
90
|
-
/** Current URL pathname */
|
|
91
|
-
pathname: string;
|
|
92
|
-
|
|
93
|
-
/** Extracted route parameters */
|
|
94
|
-
params: RouteParams;
|
|
95
|
-
|
|
96
|
-
/** Scroll position to restore */
|
|
97
|
-
scrollY?: number;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/** Navigation options */
|
|
101
|
-
export interface NavigateOptions {
|
|
102
|
-
/** Replace current history entry instead of pushing */
|
|
103
|
-
replace?: boolean;
|
|
104
|
-
|
|
105
|
-
/** State to store in history */
|
|
106
|
-
state?: RouterState;
|
|
107
|
-
|
|
108
|
-
/** Hash to scroll to after navigation */
|
|
109
|
-
hash?: string;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/** Router event types */
|
|
113
|
-
export type RouterEventType = 'navigate' | 'error' | 'load';
|
|
114
|
-
|
|
115
|
-
/** Router event payload */
|
|
116
|
-
export interface RouterEvent {
|
|
117
|
-
type: RouterEventType;
|
|
118
|
-
pathname: string;
|
|
119
|
-
params: RouteParams;
|
|
120
|
-
error?: Error;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/** Router event listener */
|
|
124
|
-
export type RouterEventListener = (event: RouterEvent) => void;
|
package/src/type/widget.type.ts
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Widget System - Type Definitions
|
|
3
|
-
*
|
|
4
|
-
* Widgets are data-fetching components that work across three contexts:
|
|
5
|
-
* 1. /md/ (LLMs) - Returns markdown with JSON data
|
|
6
|
-
* 2. SSR/HTML - Pre-fetched data embedded in custom element
|
|
7
|
-
* 3. SPA/Browser - Custom element fetches and hydrates
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Parsed widget block from markdown.
|
|
12
|
-
* Represents a fenced code block with widget syntax.
|
|
13
|
-
*/
|
|
14
|
-
export interface ParsedWidgetBlock {
|
|
15
|
-
/** Full matched string including fences */
|
|
16
|
-
fullMatch: string;
|
|
17
|
-
|
|
18
|
-
/** Widget name extracted from widget:{name} */
|
|
19
|
-
widgetName: string;
|
|
20
|
-
|
|
21
|
-
/** Parsed JSON params, or null if empty/invalid */
|
|
22
|
-
params: Record<string, unknown> | null;
|
|
23
|
-
|
|
24
|
-
/** Parse error message if params JSON was invalid */
|
|
25
|
-
parseError?: string;
|
|
26
|
-
|
|
27
|
-
/** Start index in original markdown */
|
|
28
|
-
startIndex: number;
|
|
29
|
-
|
|
30
|
-
/** End index in original markdown */
|
|
31
|
-
endIndex: number;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/** Custom element tag name for widgets: `widget-{name}` */
|
|
35
|
-
export type WidgetTagName = `widget-${string}`;
|
|
36
|
-
|
|
37
|
-
/** SPA rendering mode. */
|
|
38
|
-
export type SpaMode = 'none' | 'leaf' | 'root' | 'only';
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Widget manifest entry for code generation.
|
|
42
|
-
*/
|
|
43
|
-
export interface WidgetManifestEntry {
|
|
44
|
-
/** Widget name in kebab-case */
|
|
45
|
-
name: string;
|
|
46
|
-
|
|
47
|
-
/** Path to widget module file */
|
|
48
|
-
modulePath: string;
|
|
49
|
-
|
|
50
|
-
/** Custom element tag name (widget-{name}) */
|
|
51
|
-
tagName: WidgetTagName;
|
|
52
|
-
|
|
53
|
-
/** Discovered/declared companion file paths (html, md, css) */
|
|
54
|
-
files?: { html?: string; md?: string; css?: string };
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Generated widgets manifest structure.
|
|
59
|
-
*/
|
|
60
|
-
export interface WidgetsManifest {
|
|
61
|
-
widgets: WidgetManifestEntry[];
|
|
62
|
-
|
|
63
|
-
/** Pre-bundled module loaders keyed by module path (for SPA bundles) */
|
|
64
|
-
moduleLoaders?: Record<string, () => Promise<unknown>>;
|
|
65
|
-
}
|
package/src/util/logger.util.ts
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Logger Utility
|
|
3
|
-
*
|
|
4
|
-
* Provides structured logging for emroute internals.
|
|
5
|
-
* Enable via localStorage: localStorage.setItem('emroute:debug', 'true')
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const STORAGE_KEY = 'emroute:debug';
|
|
9
|
-
const PREFIX = '[emroute]';
|
|
10
|
-
|
|
11
|
-
function isEnabled(): boolean {
|
|
12
|
-
if (typeof globalThis.localStorage === 'undefined') return false;
|
|
13
|
-
return globalThis.localStorage.getItem(STORAGE_KEY) === 'true';
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export const logger = {
|
|
17
|
-
/** Enable debug logging (persists in localStorage) */
|
|
18
|
-
enable(): void {
|
|
19
|
-
if (typeof globalThis.localStorage !== 'undefined') {
|
|
20
|
-
globalThis.localStorage.setItem(STORAGE_KEY, 'true');
|
|
21
|
-
console.log(`${PREFIX} Debug logging enabled`);
|
|
22
|
-
}
|
|
23
|
-
},
|
|
24
|
-
|
|
25
|
-
/** Disable debug logging */
|
|
26
|
-
disable(): void {
|
|
27
|
-
if (typeof globalThis.localStorage !== 'undefined') {
|
|
28
|
-
globalThis.localStorage.removeItem(STORAGE_KEY);
|
|
29
|
-
console.log(`${PREFIX} Debug logging disabled`);
|
|
30
|
-
}
|
|
31
|
-
},
|
|
32
|
-
|
|
33
|
-
/** Log general information */
|
|
34
|
-
info(category: string, message: string, data?: unknown): void {
|
|
35
|
-
if (!isEnabled()) return;
|
|
36
|
-
const prefix = `${PREFIX} [${category}]`;
|
|
37
|
-
if (data !== undefined) {
|
|
38
|
-
console.log(prefix, message, data);
|
|
39
|
-
} else {
|
|
40
|
-
console.log(prefix, message);
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
|
|
44
|
-
/** Log navigation events */
|
|
45
|
-
nav(action: string, from: string, to: string, data?: Record<string, unknown>): void {
|
|
46
|
-
if (!isEnabled()) return;
|
|
47
|
-
console.log(`${PREFIX} [nav] ${action}:`, { from, to, ...(data ?? {}) });
|
|
48
|
-
},
|
|
49
|
-
|
|
50
|
-
/** Log rendering events */
|
|
51
|
-
render(component: string, route: string, mode?: string): void {
|
|
52
|
-
if (!isEnabled()) return;
|
|
53
|
-
const modeStr = mode ? ` [mode=${mode}]` : '';
|
|
54
|
-
console.log(`${PREFIX} [render]${modeStr} ${component} → ${route}`);
|
|
55
|
-
},
|
|
56
|
-
|
|
57
|
-
/** Log link interception */
|
|
58
|
-
link(action: 'intercept' | 'passthrough', href: string, reason?: string): void {
|
|
59
|
-
if (!isEnabled()) return;
|
|
60
|
-
const reasonStr = reason ? ` (${reason})` : '';
|
|
61
|
-
console.log(`${PREFIX} [link] ${action}: ${href}${reasonStr}`);
|
|
62
|
-
},
|
|
63
|
-
|
|
64
|
-
/** Log widget lifecycle */
|
|
65
|
-
widget(event: string, name: string, data?: unknown): void {
|
|
66
|
-
if (!isEnabled()) return;
|
|
67
|
-
console.log(`${PREFIX} [widget] ${event}: ${name}`, data ?? '');
|
|
68
|
-
},
|
|
69
|
-
|
|
70
|
-
/** Log warnings (always shown, not gated by debug flag) */
|
|
71
|
-
warn(message: string): void {
|
|
72
|
-
console.warn(`${PREFIX}`, message);
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
/** Log SSR adoption */
|
|
76
|
-
ssr(action: string, route: string): void {
|
|
77
|
-
if (!isEnabled()) return;
|
|
78
|
-
console.log(`${PREFIX} [ssr] ${action}: ${route}`);
|
|
79
|
-
},
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
// Expose globally for console access
|
|
83
|
-
(globalThis as Record<string, unknown>).__emroute_logger = logger;
|