@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,107 @@
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 } from "./abstract.component.js";
12
+ import { escapeHtml } from "../util/html.util.js";
13
+ export class PageComponent extends Component {
14
+ name = 'page';
15
+ /** Route pattern this page handles (optional — set by subclasses) */
16
+ pattern;
17
+ /**
18
+ * Fetch or compute page data. Override in subclasses.
19
+ * Default: returns null (no data needed).
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * override getData({ params, context }: this['DataArgs']) {
24
+ * return fetch(`/api/${params.id}`, { signal: context?.signal });
25
+ * }
26
+ * ```
27
+ */
28
+ getData(_args) {
29
+ return Promise.resolve(null);
30
+ }
31
+ /**
32
+ * Render page as HTML.
33
+ *
34
+ * Fallback chain:
35
+ * 1. html file content from context
36
+ * 2. md file content wrapped in `<mark-down>`
37
+ * 3. `<router-slot />` (bare slot for child routes)
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * override renderHTML({ data, params, context }: this['RenderArgs']) {
42
+ * return `<h1>${params.id}</h1><p>${context?.files?.html ?? ''}</p>`;
43
+ * }
44
+ * ```
45
+ */
46
+ renderHTML(args) {
47
+ const files = args.context.files;
48
+ const style = files?.css ? `<style>${files.css}</style>\n` : '';
49
+ if (files?.html) {
50
+ let html = style + files.html;
51
+ if (files.md && html.includes('<mark-down></mark-down>')) {
52
+ html = html.replace('<mark-down></mark-down>', `<mark-down>${escapeHtml(files.md)}</mark-down>`);
53
+ }
54
+ return html;
55
+ }
56
+ if (files?.md) {
57
+ // HOTFIX: skip external <router-slot> when markdown already defines one
58
+ // via ```router-slot fenced block. Without this, SPA mode produces two
59
+ // slots with the same pattern — one inside <mark-down> (from the fenced
60
+ // block) and one outside (from this suffix). SSR strips empty duplicates
61
+ // via stripSlots, but SPA has no equivalent cleanup.
62
+ // See: issues/pending/spa-duplicate-router-slot.issue.md
63
+ const hasSlot = files.md.includes('```router-slot');
64
+ const slot = args.context.isLeaf || hasSlot ? '' : '\n<router-slot></router-slot>';
65
+ return `${style}<mark-down>${escapeHtml(files.md)}</mark-down>${slot}`;
66
+ }
67
+ return args.context.isLeaf ? '' : '<router-slot></router-slot>';
68
+ }
69
+ /**
70
+ * Render page as Markdown.
71
+ *
72
+ * Fallback chain:
73
+ * 1. md file content from context
74
+ * 2. `` ```router-slot\n``` `` (slot placeholder in markdown — newline required)
75
+ *
76
+ * @example
77
+ * ```ts
78
+ * override renderMarkdown({ data, params, context }: this['RenderArgs']) {
79
+ * return `# ${params.id}\n\n${context?.files?.md ?? ''}`;
80
+ * }
81
+ * ```
82
+ */
83
+ renderMarkdown(args) {
84
+ const files = args.context.files;
85
+ if (files?.md) {
86
+ return files.md;
87
+ }
88
+ return args.context.isLeaf ? '' : '```router-slot\n```';
89
+ }
90
+ /**
91
+ * Page title. Override in subclasses.
92
+ * Default: undefined (no title).
93
+ *
94
+ * @example
95
+ * ```ts
96
+ * override getTitle({ data, params }: this['RenderArgs']) {
97
+ * return `Project ${params.id}`;
98
+ * }
99
+ * ```
100
+ */
101
+ getTitle(_args) {
102
+ return undefined;
103
+ }
104
+ }
105
+ /** Shared default instance used by renderers when no custom .page.ts exists. */
106
+ export default new PageComponent();
107
+ //# sourceMappingURL=page.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page.component.js","sourceRoot":"","sources":["../../../src/component/page.component.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,SAAS,EAAyB,MAAM,yBAAyB,CAAC;AAC3E,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAElD,MAAM,OAAO,aAIX,SAAQ,SAAmC;IACzB,IAAI,GAAW,MAAM,CAAC;IAExC,qEAAqE;IAC5D,OAAO,CAAU;IAE1B;;;;;;;;;;OAUG;IACM,OAAO,CACd,KAAuB;QAEvB,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACM,UAAU,CACjB,IAAwB;QAExB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;QACjC,MAAM,KAAK,GAAG,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,UAAU,KAAK,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;QAEhE,IAAI,KAAK,EAAE,IAAI,EAAE,CAAC;YAChB,IAAI,IAAI,GAAG,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC;YAC9B,IAAI,KAAK,CAAC,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,yBAAyB,CAAC,EAAE,CAAC;gBACzD,IAAI,GAAG,IAAI,CAAC,OAAO,CACjB,yBAAyB,EACzB,cAAc,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC,cAAc,CACjD,CAAC;YACJ,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,KAAK,EAAE,EAAE,EAAE,CAAC;YACd,wEAAwE;YACxE,uEAAuE;YACvE,wEAAwE;YACxE,yEAAyE;YACzE,qDAAqD;YACrD,yDAAyD;YACzD,MAAM,OAAO,GAAG,KAAK,CAAC,EAAE,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC;YACpD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,+BAA+B,CAAC;YACnF,OAAO,GAAG,KAAK,cAAc,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC,eAAe,IAAI,EAAE,CAAC;QACzE,CAAC;QAED,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,6BAA6B,CAAC;IAClE,CAAC;IAED;;;;;;;;;;;;;OAaG;IACM,cAAc,CACrB,IAAwB;QAExB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;QAEjC,IAAI,KAAK,EAAE,EAAE,EAAE,CAAC;YACd,OAAO,KAAK,CAAC,EAAE,CAAC;QAClB,CAAC;QAED,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,qBAAqB,CAAC;IAC1D,CAAC;IAED;;;;;;;;;;OAUG;IACH,QAAQ,CACN,KAAyB;QAEzB,OAAO,SAAS,CAAC;IACnB,CAAC;CACF;AAED,gFAAgF;AAChF,eAAe,IAAI,aAAa,EAAE,CAAC"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * WidgetComponent — embeddable unit within page content.
3
+ *
4
+ * Everything reusable that is not a page is a Widget.
5
+ * Widgets render across all contexts (HTML, Markdown, SPA) and are
6
+ * resolved by name via WidgetRegistry.
7
+ *
8
+ * Pages live in the routes manifest. Widgets live in the registry.
9
+ *
10
+ * Default rendering fallback chains (parallel to PageComponent):
11
+ * - renderHTML: html file → md file in <mark-down> → base Component default
12
+ * - renderMarkdown: md file → ''
13
+ */
14
+ import { Component, type ComponentContext } from './abstract.component.ts';
15
+ export declare abstract class WidgetComponent<TParams = unknown, TData = unknown, TContext extends ComponentContext = ComponentContext> extends Component<TParams, TData, TContext> {
16
+ /**
17
+ * Render widget as HTML.
18
+ *
19
+ * Fallback chain:
20
+ * 1. html file content from context
21
+ * 2. md file content wrapped in `<mark-down>`
22
+ * 3. base Component default (markdown→HTML conversion)
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * override renderHTML({ data, params }: this['RenderArgs']) {
27
+ * return `<span>${params.coin}: $${data?.price}</span>`;
28
+ * }
29
+ * ```
30
+ */
31
+ renderHTML(args: this['RenderArgs']): string;
32
+ /**
33
+ * Render widget as Markdown.
34
+ *
35
+ * Fallback chain:
36
+ * 1. md file content from context
37
+ * 2. empty string
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * override renderMarkdown({ data, params }: this['RenderArgs']) {
42
+ * return `**${params.coin}**: $${data?.price}`;
43
+ * }
44
+ * ```
45
+ */
46
+ renderMarkdown(args: this['RenderArgs']): string;
47
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * WidgetComponent — embeddable unit within page content.
3
+ *
4
+ * Everything reusable that is not a page is a Widget.
5
+ * Widgets render across all contexts (HTML, Markdown, SPA) and are
6
+ * resolved by name via WidgetRegistry.
7
+ *
8
+ * Pages live in the routes manifest. Widgets live in the registry.
9
+ *
10
+ * Default rendering fallback chains (parallel to PageComponent):
11
+ * - renderHTML: html file → md file in <mark-down> → base Component default
12
+ * - renderMarkdown: md file → ''
13
+ */
14
+ import { Component } from "./abstract.component.js";
15
+ import { escapeHtml, scopeWidgetCss } from "../util/html.util.js";
16
+ export class WidgetComponent extends Component {
17
+ /**
18
+ * Render widget as HTML.
19
+ *
20
+ * Fallback chain:
21
+ * 1. html file content from context
22
+ * 2. md file content wrapped in `<mark-down>`
23
+ * 3. base Component default (markdown→HTML conversion)
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * override renderHTML({ data, params }: this['RenderArgs']) {
28
+ * return `<span>${params.coin}: $${data?.price}</span>`;
29
+ * }
30
+ * ```
31
+ */
32
+ renderHTML(args) {
33
+ const files = args.context.files;
34
+ // @scope needed for SSR Light DOM output; redundant but harmless in SPA Shadow DOM
35
+ const style = files?.css ? `<style>${scopeWidgetCss(files.css, this.name)}</style>\n` : '';
36
+ if (files?.html) {
37
+ return style + files.html;
38
+ }
39
+ if (files?.md) {
40
+ return `${style}<mark-down>${escapeHtml(files.md)}</mark-down>`;
41
+ }
42
+ if (style) {
43
+ return style + super.renderHTML(args);
44
+ }
45
+ return super.renderHTML(args);
46
+ }
47
+ /**
48
+ * Render widget as Markdown.
49
+ *
50
+ * Fallback chain:
51
+ * 1. md file content from context
52
+ * 2. empty string
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * override renderMarkdown({ data, params }: this['RenderArgs']) {
57
+ * return `**${params.coin}**: $${data?.price}`;
58
+ * }
59
+ * ```
60
+ */
61
+ renderMarkdown(args) {
62
+ const files = args.context.files;
63
+ if (files?.md) {
64
+ return files.md;
65
+ }
66
+ return '';
67
+ }
68
+ }
69
+ //# sourceMappingURL=widget.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"widget.component.js","sourceRoot":"","sources":["../../../src/component/widget.component.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,SAAS,EAAyB,MAAM,yBAAyB,CAAC;AAC3E,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAElE,MAAM,OAAgB,eAIpB,SAAQ,SAAmC;IAC3C;;;;;;;;;;;;;;OAcG;IACM,UAAU,CACjB,IAAwB;QAExB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;QACjC,mFAAmF;QACnF,MAAM,KAAK,GAAG,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,UAAU,cAAc,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;QAE3F,IAAI,KAAK,EAAE,IAAI,EAAE,CAAC;YAChB,OAAO,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC;QAC5B,CAAC;QAED,IAAI,KAAK,EAAE,EAAE,EAAE,CAAC;YACd,OAAO,GAAG,KAAK,cAAc,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC,cAAc,CAAC;QAClE,CAAC;QAED,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,KAAK,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACxC,CAAC;QAED,OAAO,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAED;;;;;;;;;;;;;OAaG;IACM,cAAc,CACrB,IAAwB;QAExB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;QAEjC,IAAI,KAAK,EAAE,EAAE,EAAE,CAAC;YACd,OAAO,KAAK,CAAC,EAAE,CAAC;QAClB,CAAC;QAED,OAAO,EAAE,CAAC;IACZ,CAAC;CACF"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Widget Element - Browser Custom Element
3
+ *
4
+ * Renders Widget instances in the browser as `widget-{name}` elements.
5
+ * Handles:
6
+ * - SSR hydration (ssr attribute)
7
+ * - Client-side data fetching with AbortSignal
8
+ * - Companion file loading (html, md, css) with caching
9
+ * - Loading/error states
10
+ */
11
+ import type { Component, ContextProvider } from '../component/abstract.component.ts';
12
+ import { HTMLElementBase } from '../util/html.util.ts';
13
+ type WidgetFiles = {
14
+ html?: string;
15
+ md?: string;
16
+ css?: string;
17
+ };
18
+ /**
19
+ * Custom element that renders a Component in the browser.
20
+ */
21
+ export declare class ComponentElement<TParams, TData> extends HTMLElementBase {
22
+ /** Shared file content cache — deduplicates fetches across all widget instances. */
23
+ private static fileCache;
24
+ /** App-level context provider set once during router initialization. */
25
+ private static extendContext;
26
+ /** Register (or clear) the context provider that enriches every widget's ComponentContext. */
27
+ static setContextProvider(provider: ContextProvider | undefined): void;
28
+ private component;
29
+ private effectiveFiles?;
30
+ private params;
31
+ private data;
32
+ private context;
33
+ private state;
34
+ private errorMessage;
35
+ private deferred;
36
+ private abortController;
37
+ private intersectionObserver;
38
+ /** Promise that resolves with fetched data (available after loadData starts) */
39
+ dataPromise: Promise<TData | null> | null;
40
+ constructor(component: Component<TParams, TData>, files?: WidgetFiles);
41
+ /**
42
+ * Register a widget as a custom element: `widget-{name}`.
43
+ * Creates a fresh widget instance per DOM element (per-element state).
44
+ * Optional `files` parameter provides discovered file paths without mutating
45
+ * the component instance.
46
+ */
47
+ static register<TP, TD>(component: Component<TP, TD>, files?: WidgetFiles): void;
48
+ /**
49
+ * Register a widget class (not instance) as a custom element: `widget-{name}`.
50
+ * Used for manifest-based registration where classes are loaded dynamically.
51
+ */
52
+ static registerClass<TP, TD>(WidgetClass: new () => Component<TP, TD>, name: string, files?: WidgetFiles): void;
53
+ /**
54
+ * Promise that resolves when component is ready (data loaded and rendered).
55
+ * Used by router to wait for async components.
56
+ */
57
+ get ready(): Promise<void>;
58
+ connectedCallback(): Promise<void>;
59
+ disconnectedCallback(): void;
60
+ /**
61
+ * Reload component data. Aborts any in-flight request first.
62
+ */
63
+ reload(): Promise<void>;
64
+ /**
65
+ * Fetch a single file by path, with caching.
66
+ * Absolute URLs (http/https) pass through; relative paths get '/' prefix.
67
+ */
68
+ private static loadFile;
69
+ /**
70
+ * Load all companion files for this widget instance.
71
+ * Uses effectiveFiles (from registration) falling back to component.files.
72
+ */
73
+ private loadFiles;
74
+ private loadData;
75
+ private setError;
76
+ private signalReady;
77
+ private render;
78
+ }
79
+ export {};
@@ -0,0 +1,293 @@
1
+ /**
2
+ * Widget Element - Browser Custom Element
3
+ *
4
+ * Renders Widget instances in the browser as `widget-{name}` elements.
5
+ * Handles:
6
+ * - SSR hydration (ssr attribute)
7
+ * - Client-side data fetching with AbortSignal
8
+ * - Companion file loading (html, md, css) with caching
9
+ * - Loading/error states
10
+ */
11
+ import { HTMLElementBase, LAZY_ATTR, SSR_ATTR } from "../util/html.util.js";
12
+ /**
13
+ * Custom element that renders a Component in the browser.
14
+ */
15
+ export class ComponentElement extends HTMLElementBase {
16
+ /** Shared file content cache — deduplicates fetches across all widget instances. */
17
+ static fileCache = new Map();
18
+ /** App-level context provider set once during router initialization. */
19
+ static extendContext;
20
+ /** Register (or clear) the context provider that enriches every widget's ComponentContext. */
21
+ static setContextProvider(provider) {
22
+ ComponentElement.extendContext = provider;
23
+ }
24
+ component;
25
+ effectiveFiles;
26
+ params = null;
27
+ data = null;
28
+ context;
29
+ state = 'idle';
30
+ errorMessage = '';
31
+ deferred = null;
32
+ abortController = null;
33
+ intersectionObserver = null;
34
+ /** Promise that resolves with fetched data (available after loadData starts) */
35
+ dataPromise = null;
36
+ constructor(component, files) {
37
+ super();
38
+ this.component = component;
39
+ this.effectiveFiles = files;
40
+ // Attach shadow root if not already present (Declarative Shadow DOM creates it from <template shadowrootmode="open">)
41
+ // This enables progressive enhancement: SSR with DSD works without JS, then hydrates when JS loads
42
+ if (!this.shadowRoot) {
43
+ this.attachShadow({ mode: 'open' });
44
+ }
45
+ }
46
+ /**
47
+ * Register a widget as a custom element: `widget-{name}`.
48
+ * Creates a fresh widget instance per DOM element (per-element state).
49
+ * Optional `files` parameter provides discovered file paths without mutating
50
+ * the component instance.
51
+ */
52
+ static register(component, files) {
53
+ const tagName = `widget-${component.name}`;
54
+ if (!globalThis.customElements || customElements.get(tagName)) {
55
+ return;
56
+ }
57
+ const WidgetClass = component.constructor;
58
+ const BoundElement = class extends ComponentElement {
59
+ constructor() {
60
+ super(new WidgetClass(), files);
61
+ }
62
+ };
63
+ customElements.define(tagName, BoundElement);
64
+ }
65
+ /**
66
+ * Register a widget class (not instance) as a custom element: `widget-{name}`.
67
+ * Used for manifest-based registration where classes are loaded dynamically.
68
+ */
69
+ static registerClass(WidgetClass, name, files) {
70
+ const tagName = `widget-${name}`;
71
+ if (!globalThis.customElements || customElements.get(tagName)) {
72
+ return;
73
+ }
74
+ const BoundElement = class extends ComponentElement {
75
+ constructor() {
76
+ super(new WidgetClass(), files);
77
+ }
78
+ };
79
+ customElements.define(tagName, BoundElement);
80
+ }
81
+ /**
82
+ * Promise that resolves when component is ready (data loaded and rendered).
83
+ * Used by router to wait for async components.
84
+ */
85
+ get ready() {
86
+ if (this.state === 'ready') {
87
+ return Promise.resolve();
88
+ }
89
+ this.deferred ??= Promise.withResolvers();
90
+ return this.deferred.promise;
91
+ }
92
+ async connectedCallback() {
93
+ this.component.element = this;
94
+ this.style.contentVisibility = 'auto';
95
+ this.abortController = new AbortController();
96
+ const signal = this.abortController.signal;
97
+ // Parse params from element attributes
98
+ const params = {};
99
+ for (const attr of this.attributes) {
100
+ if (attr.name === SSR_ATTR || attr.name === LAZY_ATTR)
101
+ continue;
102
+ const key = attr.name.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
103
+ try {
104
+ params[key] = JSON.parse(attr.value);
105
+ }
106
+ catch {
107
+ params[key] = attr.value;
108
+ }
109
+ }
110
+ this.params = params;
111
+ // Validate params
112
+ if (this.component.validateParams && this.params !== null) {
113
+ const error = this.component.validateParams(this.params);
114
+ if (error) {
115
+ this.setError(error);
116
+ return;
117
+ }
118
+ }
119
+ // Load companion files (html, md, css) if declared
120
+ const files = await this.loadFiles();
121
+ if (signal.aborted)
122
+ return;
123
+ const base = {
124
+ pathname: globalThis.location?.pathname ?? '/',
125
+ pattern: '',
126
+ params: {},
127
+ searchParams: new URLSearchParams(globalThis.location?.search ?? ''),
128
+ files: (files.html || files.md || files.css) ? files : undefined,
129
+ };
130
+ this.context = ComponentElement.extendContext ? ComponentElement.extendContext(base) : base;
131
+ // Hydrate from SSR: adopt content from Declarative Shadow DOM
132
+ if (this.hasAttribute(SSR_ATTR)) {
133
+ this.removeAttribute(SSR_ATTR);
134
+ // Read SSR data from light DOM (JSON text placed alongside shadow root)
135
+ const lightText = this.textContent?.trim();
136
+ if (lightText) {
137
+ try {
138
+ this.data = JSON.parse(lightText);
139
+ }
140
+ catch {
141
+ // Not valid JSON — proceed with data: null
142
+ }
143
+ }
144
+ // Clear light DOM content (JSON text)
145
+ this.textContent = '';
146
+ this.state = 'ready';
147
+ // Call hydrate() hook to attach event listeners
148
+ if (this.component.hydrate) {
149
+ const args = { data: this.data, params: this.params, context: this.context };
150
+ queueMicrotask(() => {
151
+ this.component.hydrate(args);
152
+ });
153
+ }
154
+ this.signalReady();
155
+ return;
156
+ }
157
+ // Lazy: defer loadData until element is visible
158
+ if (this.hasAttribute(LAZY_ATTR)) {
159
+ this.intersectionObserver = new IntersectionObserver(([entry]) => {
160
+ if (entry.isIntersecting) {
161
+ this.intersectionObserver?.disconnect();
162
+ this.intersectionObserver = null;
163
+ this.loadData();
164
+ }
165
+ });
166
+ this.intersectionObserver.observe(this);
167
+ return;
168
+ }
169
+ await this.loadData();
170
+ }
171
+ disconnectedCallback() {
172
+ this.component.destroy?.();
173
+ this.component.element = undefined;
174
+ this.intersectionObserver?.disconnect();
175
+ this.intersectionObserver = null;
176
+ this.abortController?.abort();
177
+ this.abortController = null;
178
+ this.state = 'idle';
179
+ this.data = null;
180
+ this.context = undefined;
181
+ this.dataPromise = null;
182
+ this.errorMessage = '';
183
+ this.signalReady();
184
+ this.deferred = null;
185
+ }
186
+ /**
187
+ * Reload component data. Aborts any in-flight request first.
188
+ */
189
+ async reload() {
190
+ if (this.params === null)
191
+ return;
192
+ // Abort previous and create fresh controller
193
+ this.abortController?.abort();
194
+ this.abortController = new AbortController();
195
+ await this.loadData();
196
+ }
197
+ /**
198
+ * Fetch a single file by path, with caching.
199
+ * Absolute URLs (http/https) pass through; relative paths get '/' prefix.
200
+ */
201
+ static loadFile(path) {
202
+ const cached = ComponentElement.fileCache.get(path);
203
+ if (cached)
204
+ return cached;
205
+ const url = path.startsWith('http://') || path.startsWith('https://')
206
+ ? path
207
+ : (path.startsWith('/') ? path : '/' + path);
208
+ const promise = fetch(url).then((res) => res.ok ? res.text() : undefined, () => undefined);
209
+ ComponentElement.fileCache.set(path, promise);
210
+ return promise;
211
+ }
212
+ /**
213
+ * Load all companion files for this widget instance.
214
+ * Uses effectiveFiles (from registration) falling back to component.files.
215
+ */
216
+ async loadFiles() {
217
+ const filePaths = this.effectiveFiles ?? this.component.files;
218
+ if (!filePaths)
219
+ return {};
220
+ const [html, md, css] = await Promise.all([
221
+ filePaths.html ? ComponentElement.loadFile(filePaths.html) : undefined,
222
+ filePaths.md ? ComponentElement.loadFile(filePaths.md) : undefined,
223
+ filePaths.css ? ComponentElement.loadFile(filePaths.css) : undefined,
224
+ ]);
225
+ return { html, md, css };
226
+ }
227
+ async loadData() {
228
+ if (this.params === null)
229
+ return;
230
+ const signal = this.abortController?.signal;
231
+ this.state = 'loading';
232
+ this.render();
233
+ try {
234
+ const promise = this.component.getData({
235
+ params: this.params,
236
+ signal,
237
+ context: this.context,
238
+ });
239
+ this.dataPromise = promise;
240
+ this.data = await promise;
241
+ // Check abort after await — don't touch DOM if disconnected
242
+ if (signal?.aborted)
243
+ return;
244
+ this.state = 'ready';
245
+ }
246
+ catch (e) {
247
+ if (e instanceof DOMException && e.name === 'AbortError')
248
+ return;
249
+ if (signal?.aborted)
250
+ return;
251
+ this.setError(e instanceof Error ? e.message : String(e));
252
+ return;
253
+ }
254
+ this.render();
255
+ this.signalReady();
256
+ }
257
+ setError(message) {
258
+ this.state = 'error';
259
+ this.errorMessage = message;
260
+ this.render();
261
+ this.signalReady(); // Ready even on error (completed loading)
262
+ }
263
+ signalReady() {
264
+ this.deferred?.resolve();
265
+ this.deferred = null;
266
+ }
267
+ render() {
268
+ if (this.params === null) {
269
+ this.shadowRoot.setHTMLUnsafe('');
270
+ return;
271
+ }
272
+ if (this.state === 'error') {
273
+ this.shadowRoot.setHTMLUnsafe(this.component.renderError({
274
+ error: new Error(this.errorMessage),
275
+ params: this.params,
276
+ }));
277
+ return;
278
+ }
279
+ this.shadowRoot.setHTMLUnsafe(this.component.renderHTML({
280
+ data: this.state === 'ready' ? this.data : null,
281
+ params: this.params,
282
+ context: this.context,
283
+ }));
284
+ // Call hydrate() after rendering to attach event listeners
285
+ if (this.state === 'ready' && this.component.hydrate) {
286
+ const args = { data: this.data, params: this.params, context: this.context };
287
+ queueMicrotask(() => {
288
+ this.component.hydrate(args);
289
+ });
290
+ }
291
+ }
292
+ }
293
+ //# sourceMappingURL=component.element.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"component.element.js","sourceRoot":"","sources":["../../../src/element/component.element.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Markdown Element — <mark-down> custom element.
3
+ *
4
+ * Renders markdown content with pluggable renderer.
5
+ * Supports:
6
+ * - Inline content: <mark-down># Title</mark-down>
7
+ * - Source attribute: <mark-down src="/path/to.md"></mark-down>
8
+ */
9
+ import { HTMLElementBase } from '../util/html.util.ts';
10
+ import type { MarkdownRenderer } from '../type/markdown.type.ts';
11
+ export declare class MarkdownElement extends HTMLElementBase {
12
+ private static renderer;
13
+ private static rendererInitPromise;
14
+ private abortController;
15
+ /**
16
+ * Set the markdown renderer.
17
+ * Must be called before any <mark-down> elements are connected.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * import { createEmkoRenderer } from './emko.renderer.ts';
22
+ * MarkdownElement.setRenderer(await createEmkoRenderer());
23
+ * ```
24
+ */
25
+ static setRenderer(renderer: MarkdownRenderer): void;
26
+ /**
27
+ * Get the current renderer, waiting for init if needed.
28
+ */
29
+ private static getRenderer;
30
+ connectedCallback(): Promise<void>;
31
+ disconnectedCallback(): void;
32
+ private loadContent;
33
+ private loadFromSrc;
34
+ private renderContent;
35
+ private showError;
36
+ }