@stacksjs/stx 0.2.155 → 0.2.156

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 (240) hide show
  1. package/dist/a11y.d.ts +87 -46
  2. package/dist/animation.d.ts +96 -42
  3. package/dist/appearance-bootstrap.d.ts +49 -15
  4. package/dist/asset-roots.d.ts +32 -0
  5. package/dist/asset-roots.js +7 -0
  6. package/dist/assets.d.ts +9 -7
  7. package/dist/async-components.d.ts +78 -56
  8. package/dist/auth.d.ts +3 -0
  9. package/dist/build-externalize.d.ts +40 -0
  10. package/dist/build-externalize.js +63 -0
  11. package/dist/build-id.d.ts +22 -22
  12. package/dist/build-message.d.ts +73 -2
  13. package/dist/build-message.js +47 -8
  14. package/dist/builtins/attrs.d.ts +14 -0
  15. package/dist/builtins/drawer.d.ts +22 -0
  16. package/dist/builtins/icon.d.ts +16 -2
  17. package/dist/builtins/index.d.ts +5 -15
  18. package/dist/builtins/modal.d.ts +24 -0
  19. package/dist/builtins/safe-image.d.ts +42 -0
  20. package/dist/builtins/stx-image.d.ts +27 -0
  21. package/dist/builtins/stx-link.d.ts +9 -0
  22. package/dist/builtins/stx-loading-indicator.d.ts +11 -0
  23. package/dist/builtins/suspense.d.ts +28 -0
  24. package/dist/builtins/toast.d.ts +17 -0
  25. package/dist/builtins/transition-group.d.ts +25 -0
  26. package/dist/caching.d.ts +12 -15
  27. package/dist/cli.js +1798 -908
  28. package/dist/client/directive.d.ts +7 -0
  29. package/dist/client/index.d.ts +0 -1
  30. package/dist/client-script-bundler.d.ts +63 -5
  31. package/dist/client-script-bundler.js +30 -2
  32. package/dist/client-script.d.ts +102 -47
  33. package/dist/client-script.js +15 -3
  34. package/dist/codemod.d.ts +56 -0
  35. package/dist/codemod.js +192 -0
  36. package/dist/color-mode-boot.d.ts +83 -65
  37. package/dist/component-client-factories.d.ts +1 -6
  38. package/dist/component-processing.d.ts +58 -49
  39. package/dist/component-processing.js +1 -1
  40. package/dist/component-registry.d.ts +94 -31
  41. package/dist/component-renderer.d.ts +18 -0
  42. package/dist/composable-loader.d.ts +24 -0
  43. package/dist/composable-loader.js +12 -9
  44. package/dist/composables/index.d.ts +7 -0
  45. package/dist/composables/index.js +1 -0
  46. package/dist/composables/use-fetch.js +30 -2
  47. package/dist/composables/use-form.d.ts +91 -0
  48. package/dist/composables/use-form.js +112 -0
  49. package/dist/composables.d.ts +64 -60
  50. package/dist/computed.d.ts +86 -92
  51. package/dist/conditionals.d.ts +24 -8
  52. package/dist/conditionals.js +16 -0
  53. package/dist/config.d.ts +34 -21
  54. package/dist/config.js +35 -14
  55. package/dist/craft.js +1476 -768
  56. package/dist/crosswind-config.d.ts +58 -0
  57. package/dist/crosswind-config.js +40 -0
  58. package/dist/csp.d.ts +107 -41
  59. package/dist/defer.d.ts +24 -0
  60. package/dist/dev-server/crosswind.d.ts +35 -26
  61. package/dist/dev-server/crosswind.js +2 -18
  62. package/dist/dev-server/serve-app.js +4 -3
  63. package/dist/dev-server/terminal-colors.d.ts +32 -32
  64. package/dist/directive-api.d.ts +86 -43
  65. package/dist/document-shell.d.ts +54 -41
  66. package/dist/dynamic-components.d.ts +9 -0
  67. package/dist/error-boundaries.d.ts +47 -41
  68. package/dist/error-handling.d.ts +1 -1
  69. package/dist/errors/codes.d.ts +51 -43
  70. package/dist/errors/formatter.d.ts +58 -38
  71. package/dist/errors/index.d.ts +5 -61
  72. package/dist/errors/logger.d.ts +114 -40
  73. package/dist/errors/sanitizer.d.ts +53 -23
  74. package/dist/errors/types.d.ts +44 -31
  75. package/dist/events.d.ts +86 -25
  76. package/dist/expressions.d.ts +50 -42
  77. package/dist/forms-validation.d.ts +138 -123
  78. package/dist/forms-validation.js +1 -90
  79. package/dist/forms.d.ts +120 -66
  80. package/dist/forms.js +31 -11
  81. package/dist/fragment-container.d.ts +57 -0
  82. package/dist/fragment-container.js +109 -0
  83. package/dist/framework-composables.d.ts +32 -0
  84. package/dist/head.d.ts +156 -114
  85. package/dist/heatmap.d.ts +152 -72
  86. package/dist/html-masking.d.ts +40 -34
  87. package/dist/hydration-runtime.d.ts +20 -24
  88. package/dist/i18n.d.ts +93 -32
  89. package/dist/image-optimization/auto-sibling.d.ts +32 -4
  90. package/dist/includes.d.ts +69 -7
  91. package/dist/index.d.ts +66 -0
  92. package/dist/index.js +23 -0
  93. package/dist/internal-markdown.d.ts +12 -8
  94. package/dist/island-chunking.d.ts +30 -13
  95. package/dist/keep-alive.d.ts +72 -52
  96. package/dist/layout-types.d.ts +45 -0
  97. package/dist/layout-types.js +56 -0
  98. package/dist/lazy-module.d.ts +10 -0
  99. package/dist/loops.d.ts +66 -0
  100. package/dist/loops.js +47 -6
  101. package/dist/markdown.d.ts +0 -1
  102. package/dist/media/image/component.d.ts +25 -9
  103. package/dist/media/image/directive.d.ts +15 -1
  104. package/dist/media/image/placeholder.d.ts +20 -9
  105. package/dist/media/image/processor/cache.d.ts +40 -18
  106. package/dist/media/image/processor/index.d.ts +1 -10
  107. package/dist/media/image/processor/optimizer.d.ts +12 -1
  108. package/dist/media/image/processor/responsive.d.ts +16 -6
  109. package/dist/media/image/srcset.d.ts +47 -50
  110. package/dist/media/shared/cache.d.ts +51 -31
  111. package/dist/media/shared/hash.d.ts +7 -0
  112. package/dist/media/types.d.ts +1235 -754
  113. package/dist/media/video/directive.d.ts +15 -1
  114. package/dist/media/video/index.d.ts +39 -17
  115. package/dist/media/video/processor/cache.d.ts +42 -19
  116. package/dist/media/video/processor/index.d.ts +1 -18
  117. package/dist/media/video/processor/streaming.d.ts +17 -10
  118. package/dist/media/video/processor/thumbnail.d.ts +21 -15
  119. package/dist/media/video/processor/transcoder.d.ts +11 -1
  120. package/dist/method-spoofing.d.ts +7 -0
  121. package/dist/middleware.d.ts +60 -0
  122. package/dist/owned-routes.d.ts +42 -0
  123. package/dist/owned-routes.js +26 -0
  124. package/dist/page-meta.d.ts +38 -0
  125. package/dist/page-meta.js +16 -0
  126. package/dist/parser/directive-parser.d.ts +57 -45
  127. package/dist/parser/expression-parser.d.ts +33 -35
  128. package/dist/parser/index.d.ts +4 -39
  129. package/dist/parser/tokenizer.d.ts +61 -49
  130. package/dist/partial-hydration.d.ts +82 -50
  131. package/dist/performance-utils.d.ts +217 -124
  132. package/dist/placeholder.d.ts +18 -9
  133. package/dist/plugin-system.d.ts +264 -115
  134. package/dist/process.d.ts +11 -6
  135. package/dist/process.js +21 -4
  136. package/dist/production-builder.js +7 -0
  137. package/dist/prop-sentinels.d.ts +11 -11
  138. package/dist/public-env.d.ts +14 -0
  139. package/dist/pwa/directives.d.ts +12 -3
  140. package/dist/pwa/inject.d.ts +10 -0
  141. package/dist/pwa/precache.d.ts +22 -16
  142. package/dist/pwa/service-worker.d.ts +12 -0
  143. package/dist/pwa.js +1318 -610
  144. package/dist/reactive-bindings.d.ts +34 -11
  145. package/dist/reactive.d.ts +58 -30
  146. package/dist/reactive.js +56 -31
  147. package/dist/render.js +1 -0
  148. package/dist/route-middleware.d.ts +2 -35
  149. package/dist/route-types.d.ts +134 -0
  150. package/dist/route-types.js +24 -0
  151. package/dist/router.d.ts +5 -7
  152. package/dist/routes.d.ts +1 -3
  153. package/dist/runtime-globals.d.ts +147 -24
  154. package/dist/runtime-globals.js +16 -1
  155. package/dist/runtime-injection.d.ts +8 -0
  156. package/dist/runtime-injection.js +33 -24
  157. package/dist/script-emit.d.ts +30 -0
  158. package/dist/script-json.d.ts +36 -0
  159. package/dist/script-json.js +6 -0
  160. package/dist/script-validation.d.ts +21 -4
  161. package/dist/seo.d.ts +118 -47
  162. package/dist/server-bindings.d.ts +21 -0
  163. package/dist/sfc-template.d.ts +8 -8
  164. package/dist/signal-processing.d.ts +35 -15
  165. package/dist/signals-api.d.ts +117 -58
  166. package/dist/signals-api.js +7 -4
  167. package/dist/signals.d.ts +24 -17
  168. package/dist/signals.js +765 -60
  169. package/dist/site-builder/build.js +8 -0
  170. package/dist/site-url.d.ts +46 -0
  171. package/dist/site-url.js +70 -0
  172. package/dist/slots.d.ts +86 -20
  173. package/dist/source-signature.d.ts +45 -0
  174. package/dist/source-signature.js +20 -0
  175. package/dist/ssg.d.ts +285 -142
  176. package/dist/ssg.js +1268 -556
  177. package/dist/state-dir.d.ts +4 -4
  178. package/dist/state-management.d.ts +0 -12
  179. package/dist/state-management.js +8 -25
  180. package/dist/store-imports.d.ts +23 -0
  181. package/dist/store-imports.js +3 -0
  182. package/dist/store-loader.d.ts +10 -0
  183. package/dist/store-loader.js +10 -9
  184. package/dist/story/analytics.d.ts +138 -60
  185. package/dist/story/bookmarks.d.ts +87 -34
  186. package/dist/story/cli.d.ts +123 -69
  187. package/dist/story/collect/scanner.d.ts +4 -0
  188. package/dist/story/collect/tree.d.ts +8 -1
  189. package/dist/story/commands/build.d.ts +11 -6
  190. package/dist/story/commands/dev.d.ts +15 -8
  191. package/dist/story/commands/preview.d.ts +13 -7
  192. package/dist/story/config-watcher.d.ts +20 -13
  193. package/dist/story/config.d.ts +11 -8
  194. package/dist/story/context.d.ts +14 -8
  195. package/dist/story/hmr.d.ts +24 -26
  196. package/dist/story/hot-swap.d.ts +34 -23
  197. package/dist/story/keyboard-shortcuts.d.ts +23 -18
  198. package/dist/story/output.d.ts +46 -44
  199. package/dist/story/performance.d.ts +107 -45
  200. package/dist/story/presets.d.ts +107 -41
  201. package/dist/story/renderer.d.ts +41 -27
  202. package/dist/story/search.d.ts +23 -12
  203. package/dist/story/server.d.ts +21 -11
  204. package/dist/story/setup.d.ts +23 -15
  205. package/dist/story/testing.d.ts +65 -40
  206. package/dist/story/types.d.ts +262 -160
  207. package/dist/streaming.d.ts +119 -57
  208. package/dist/stx-virtual-ts.d.ts +165 -0
  209. package/dist/stx-virtual-ts.js +372 -0
  210. package/dist/stx.d.ts +78 -1
  211. package/dist/style-scoping.d.ts +17 -4
  212. package/dist/suspense.d.ts +67 -52
  213. package/dist/teleport.d.ts +22 -0
  214. package/dist/template-tag-balance.d.ts +22 -6
  215. package/dist/transitions.d.ts +66 -55
  216. package/dist/typecheck.d.ts +77 -0
  217. package/dist/typecheck.js +156 -0
  218. package/dist/types/component-types.d.ts +187 -122
  219. package/dist/types/config-types.d.ts +758 -297
  220. package/dist/types/context-types.d.ts +46 -24
  221. package/dist/types/csp-types.d.ts +70 -66
  222. package/dist/types/directive-types.d.ts +211 -155
  223. package/dist/types/index.d.ts +6 -109
  224. package/dist/types/pwa-types.d.ts +273 -148
  225. package/dist/types.d.ts +0 -1
  226. package/dist/typescript-templates.d.ts +11 -0
  227. package/dist/unresolved-identifiers.d.ts +24 -22
  228. package/dist/unresolved-identifiers.js +2 -0
  229. package/dist/utils.d.ts +24 -21
  230. package/dist/validator.d.ts +28 -26
  231. package/dist/variable-extractor.d.ts +48 -13
  232. package/dist/variable-extractor.js +27 -4
  233. package/dist/view-composers.d.ts +80 -81
  234. package/dist/virtual-scrolling.d.ts +101 -81
  235. package/dist/vue-template.d.ts +23 -0
  236. package/dist/web-components/css-scoping.d.ts +21 -11
  237. package/dist/web-components.d.ts +81 -58
  238. package/dist/x-element.d.ts +0 -1
  239. package/package.json +5 -5
  240. package/stx.d.ts +78 -1
@@ -1,57 +1,3 @@
1
- /**
2
- * Build the synchronous pre-paint `<script>`.
3
- *
4
- * Every storage access is wrapped: a sandboxed origin or disabled storage must
5
- * degrade to the default mode, never throw. A throw here is unusually costly
6
- * because the script sits above the application shell — an uncaught error would
7
- * block the rest of the document.
8
- */
9
- export declare function generateColorModeBootScript(options?: ColorModeBootConfig): string;
10
- /**
11
- * Insert the boot script as the first thing in `<head>`.
12
- *
13
- * Placement matters twice over: the script must run before the browser paints,
14
- * and a stylesheet `<link>` above it would block its execution (CSS blocks JS),
15
- * delaying the very thing it exists to make early.
16
- *
17
- * `<meta charset>` is stepped over when it leads the head, so the encoding
18
- * declaration keeps its place at the top of the document.
19
- *
20
- * Idempotent — a document that already carries the marker is returned
21
- * unchanged, so this is safe on paths that run it more than once.
22
- */
23
- export declare function injectColorModeBootScript(html: string, options?: ColorModeBootConfig): string;
24
- /**
25
- * Re-assert the ordering of the two head tags whose position is load-bearing:
26
- * `<meta charset>` first, then the color-mode boot script, then everything else.
27
- *
28
- * Run this LAST, after every other head injection. Ordering the injectors
29
- * against each other would hold only until the next one is added; re-asserting
30
- * the result once at the end holds regardless of what ran before.
31
- */
32
- export declare function normalizeCriticalHeadOrder(html: string): string;
33
- /**
34
- * Move an already-injected boot script back to the top of `<head>`.
35
- *
36
- * `injectColorModeBootScript` puts it first, but later stages of the pipeline
37
- * then insert *ahead* of it and push it down (#1803): the signals runtime is
38
- * relocated before the first `<script>` on the page — which is this one — and
39
- * the store and composable bundles anchor themselves to the runtime tag. The
40
- * boot script ended up fifth, behind all three.
41
- *
42
- * That is not merely a paint-timing problem. The bundles that overtook it read
43
- * `window.__STX_COLOR_MODE__`, which this script publishes, so a `useColorMode()`
44
- * inside a store saw `undefined` and silently fell back to the default storage
45
- * key and a null attribute — writing the user's preference somewhere the boot
46
- * script will never read it back, and never applying the configured attribute.
47
- *
48
- * Run this last, after every other head injection. Ordering the injectors
49
- * against each other would work only until the next one is added; re-asserting
50
- * the position once at the end holds regardless of what ran before.
51
- *
52
- * A no-op when the document has no boot script or it is already in place.
53
- */
54
- export declare function hoistColorModeBootScript(html: string): string;
55
1
  /**
56
2
  * Pre-paint color-mode bootstrap (stacksjs/stx#1794).
57
3
  *
@@ -103,22 +49,94 @@ export declare function hoistColorModeBootScript(html: string): string;
103
49
  * @module color-mode-boot
104
50
  */
105
51
  /** Marker attribute — makes injection idempotent and findable by tests. */
106
- export declare const COLOR_MODE_BOOT_MARKER: 'data-stx-color-mode-boot';
52
+ export declare const COLOR_MODE_BOOT_MARKER = "data-stx-color-mode-boot";
107
53
  /** The global the boot script publishes its resolved config on. */
108
- export declare const COLOR_MODE_BOOT_GLOBAL: '__STX_COLOR_MODE__';
54
+ export declare const COLOR_MODE_BOOT_GLOBAL = "__STX_COLOR_MODE__";
55
+ export type ColorModeBootMode = 'light' | 'dark' | 'auto' | 'system';
56
+ /** Spelling written to storage for "follow the system". */
57
+ export type ColorModeBootAutoValue = 'auto' | 'system';
109
58
  /**
110
59
  * Pre-paint options. A strict subset of `ColorModeOptions` — only the fields
111
60
  * that affect what lands on the root element before paint. `disableTransitions`
112
61
  * is deliberately absent: nothing has painted yet, so there is no transition to
113
62
  * suppress, and forcing a reflow in the critical path would only cost time.
114
63
  */
115
- export declare interface ColorModeBootConfig {
116
- storageKey?: string
117
- initialMode?: ColorModeBootMode
118
- darkClass?: string | null
119
- attribute?: string | null
120
- autoValue?: ColorModeBootAutoValue
64
+ export interface ColorModeBootConfig {
65
+ /** localStorage key holding the preference. @default 'stx-color-mode' */
66
+ storageKey?: string;
67
+ /**
68
+ * Mode used when nothing valid is stored. @default 'auto'
69
+ * `'system'` is accepted as an alias for `'auto'`.
70
+ */
71
+ initialMode?: ColorModeBootMode;
72
+ /**
73
+ * Class applied to `<html>` when dark. @default 'dark'
74
+ * Pass `null` to opt out — the attribute alone then carries the mode.
75
+ */
76
+ darkClass?: string | null;
77
+ /**
78
+ * Attribute set to the resolved mode on `<html>`, e.g. `'data-theme'`.
79
+ * Applied *alongside* `darkClass`, mirroring `useColorMode` (#1788).
80
+ */
81
+ attribute?: string | null;
82
+ /**
83
+ * Spelling `useColorMode` writes back for "follow the system". Published to
84
+ * the client so the composable doesn't convert an app's existing `'system'`
85
+ * key to `'auto'`. @default whatever is stored, else 'auto'
86
+ */
87
+ autoValue?: ColorModeBootAutoValue;
121
88
  }
122
- export type ColorModeBootMode = 'light' | 'dark' | 'auto' | 'system';
123
- /** Spelling written to storage for "follow the system". */
124
- export type ColorModeBootAutoValue = 'auto' | 'system';
89
+ /**
90
+ * Build the synchronous pre-paint `<script>`.
91
+ *
92
+ * Every storage access is wrapped: a sandboxed origin or disabled storage must
93
+ * degrade to the default mode, never throw. A throw here is unusually costly
94
+ * because the script sits above the application shell — an uncaught error would
95
+ * block the rest of the document.
96
+ */
97
+ export declare function generateColorModeBootScript(options?: ColorModeBootConfig): string;
98
+ /**
99
+ * Insert the boot script as the first thing in `<head>`.
100
+ *
101
+ * Placement matters twice over: the script must run before the browser paints,
102
+ * and a stylesheet `<link>` above it would block its execution (CSS blocks JS),
103
+ * delaying the very thing it exists to make early.
104
+ *
105
+ * `<meta charset>` is stepped over when it leads the head, so the encoding
106
+ * declaration keeps its place at the top of the document.
107
+ *
108
+ * Idempotent — a document that already carries the marker is returned
109
+ * unchanged, so this is safe on paths that run it more than once.
110
+ */
111
+ export declare function injectColorModeBootScript(html: string, options?: ColorModeBootConfig): string;
112
+ /**
113
+ * Re-assert the ordering of the two head tags whose position is load-bearing:
114
+ * `<meta charset>` first, then the color-mode boot script, then everything else.
115
+ *
116
+ * Run this LAST, after every other head injection. Ordering the injectors
117
+ * against each other would hold only until the next one is added; re-asserting
118
+ * the result once at the end holds regardless of what ran before.
119
+ */
120
+ export declare function normalizeCriticalHeadOrder(html: string): string;
121
+ /**
122
+ * Move an already-injected boot script back to the top of `<head>`.
123
+ *
124
+ * `injectColorModeBootScript` puts it first, but later stages of the pipeline
125
+ * then insert *ahead* of it and push it down (#1803): the signals runtime is
126
+ * relocated before the first `<script>` on the page — which is this one — and
127
+ * the store and composable bundles anchor themselves to the runtime tag. The
128
+ * boot script ended up fifth, behind all three.
129
+ *
130
+ * That is not merely a paint-timing problem. The bundles that overtook it read
131
+ * `window.__STX_COLOR_MODE__`, which this script publishes, so a `useColorMode()`
132
+ * inside a store saw `undefined` and silently fell back to the default storage
133
+ * key and a null attribute — writing the user's preference somewhere the boot
134
+ * script will never read it back, and never applying the configured attribute.
135
+ *
136
+ * Run this last, after every other head injection. Ordering the injectors
137
+ * against each other would work only until the next one is added; re-asserting
138
+ * the position once at the end holds regardless of what ran before.
139
+ *
140
+ * A no-op when the document has no boot script or it is already in place.
141
+ */
142
+ export declare function hoistColorModeBootScript(html: string): string;
@@ -1,9 +1,4 @@
1
+ export declare const COMPONENT_CLIENT_FACTORIES_CONTEXT_KEY = "__stx_component_client_factories";
1
2
  export declare function initializeComponentClientFactories(context: Record<string, unknown>): void;
2
3
  export declare function registerComponentClientFactory(context: Record<string, unknown>, factoryBody: string): string | null;
3
4
  export declare function injectComponentClientFactories(html: string, context: Record<string, unknown>): string;
4
- export declare const COMPONENT_CLIENT_FACTORIES_CONTEXT_KEY: '__stx_component_client_factories';
5
- declare interface ComponentClientFactory {
6
- body: string
7
- instances: number
8
- }
9
- declare type ComponentClientFactoryRegistry = Map<string, ComponentClientFactory>;
@@ -1,4 +1,10 @@
1
- import { restoreStashedScripts, stashScriptElements } from './html-masking';
1
+ /**
2
+ * Component processing functions extracted from process.ts.
3
+ *
4
+ * Handles component tag parsing, attribute parsing, built-in STX components,
5
+ * @import directives, @component directives, and custom element resolution
6
+ * (kebab-case and PascalCase).
7
+ */
2
8
  import type { StxOptions } from './types';
3
9
  /**
4
10
  * Convert PascalCase to kebab-case with proper handling of consecutive uppercase letters.
@@ -16,6 +22,54 @@ import type { StxOptions } from './types';
16
22
  * 3. Convert to lowercase
17
23
  */
18
24
  export declare function pascalToKebab(str: string): string;
25
+ import { restoreStashedScripts, stashScriptElements } from './html-masking';
26
+ export { restoreStashedScripts, stashScriptElements };
27
+ /**
28
+ * Parse HTML/JSX-like attributes from a string.
29
+ *
30
+ * Properly handles:
31
+ * - Simple attributes: `name="value"`
32
+ * - Boolean attributes: `disabled`
33
+ * - Dynamic values: `prop="{{ expr }}"` (evaluated at SSR)
34
+ * - Attributes with `=` in values: `url="https://example.com?a=1&b=2"`
35
+ * - Single and double quoted values
36
+ * - Escaped quotes within values
37
+ *
38
+ * ## Dynamic Data Syntax
39
+ *
40
+ * Use `{{ }}` for all dynamic data interpolation:
41
+ * ```html
42
+ * <Component title="{{ pageTitle }}" count="{{ items.length }}" />
43
+ * <div class="{{ isActive ? 'active' : '' }}">{{ content }}</div>
44
+ * ```
45
+ *
46
+ * ## Event Syntax (Alpine-style)
47
+ *
48
+ * ```html
49
+ * <button @click="handleClick()">Click me</button>
50
+ * <form @submit.prevent="handleSubmit()">...</form>
51
+ * <input @keydown.enter="send()" @keydown.escape="cancel()">
52
+ * ```
53
+ *
54
+ * @param attributesStr - The attributes portion of a tag (between tag name and closing)
55
+ * @returns Array of parsed attributes with name and value
56
+ */
57
+ export interface ParsedAttribute {
58
+ name: string;
59
+ value: string | true;
60
+ }
61
+ /**
62
+ * Component tag match result
63
+ */
64
+ export interface ComponentTagMatch {
65
+ fullMatch: string;
66
+ tagName: string;
67
+ attributes: string;
68
+ content: string;
69
+ startIndex: number;
70
+ endIndex: number;
71
+ isSelfClosing: boolean;
72
+ }
19
73
  /**
20
74
  * Find component tags in HTML, properly handling quoted strings
21
75
  * This solves the issue where `>` inside attribute values would incorrectly end the tag
@@ -54,7 +108,9 @@ export declare function findComponentTags(html: string, tagPattern: RegExp, skip
54
108
  * live markup.
55
109
  */
56
110
  export declare function decodeAttributeEntities(value: string): string;
57
- export declare function decodeStxProp(attrValue: string, options?: { debug?: boolean }): unknown;
111
+ export declare function decodeStxProp(attrValue: string, options?: {
112
+ debug?: boolean;
113
+ }): unknown;
58
114
  /**
59
115
  * Parse multiline attributes from a component tag
60
116
  * Handles HTML content inside attribute values correctly
@@ -89,50 +145,3 @@ export declare function processComponentDirectives(template: string, context: Re
89
145
  * Uses a proper parser that handles HTML content inside attribute values
90
146
  */
91
147
  export declare function processCustomElements(template: string, context: Record<string, any>, filePath: string, componentsDir: string, options: StxOptions, dependencies: Set<string>): Promise<string>;
92
- /**
93
- * Parse HTML/JSX-like attributes from a string.
94
- *
95
- * Properly handles:
96
- * - Simple attributes: `name="value"`
97
- * - Boolean attributes: `disabled`
98
- * - Dynamic values: `prop="{{ expr }}"` (evaluated at SSR)
99
- * - Attributes with `=` in values: `url="https://example.com?a=1&b=2"`
100
- * - Single and double quoted values
101
- * - Escaped quotes within values
102
- *
103
- * ## Dynamic Data Syntax
104
- *
105
- * Use `{{ }}` for all dynamic data interpolation:
106
- * ```html
107
- * <Component title="{{ pageTitle }}" count="{{ items.length }}" />
108
- * <div class="{{ isActive ? 'active' : '' }}">{{ content }}</div>
109
- * ```
110
- *
111
- * ## Event Syntax (Alpine-style)
112
- *
113
- * ```html
114
- * <button @click="handleClick()">Click me</button>
115
- * <form @submit.prevent="handleSubmit()">...</form>
116
- * <input @keydown.enter="send()" @keydown.escape="cancel()">
117
- * ```
118
- *
119
- * @param attributesStr - The attributes portion of a tag (between tag name and closing)
120
- * @returns Array of parsed attributes with name and value
121
- */
122
- export declare interface ParsedAttribute {
123
- name: string
124
- value: string | true
125
- }
126
- /**
127
- * Component tag match result
128
- */
129
- export declare interface ComponentTagMatch {
130
- fullMatch: string
131
- tagName: string
132
- attributes: string
133
- content: string
134
- startIndex: number
135
- endIndex: number
136
- isSelfClosing: boolean
137
- }
138
- export { restoreStashedScripts, stashScriptElements };
@@ -14,7 +14,7 @@ export function findComponentTags(html, tagPattern, skipTags) {
14
14
  const tagStart = html.indexOf("<", pos);
15
15
  if (tagStart === -1)
16
16
  break;
17
- const tagNameMatch = html.slice(tagStart + 1).match(new RegExp(`^(${tagPattern.source})`));
17
+ const tagNameMatch = html.slice(tagStart + 1).match(new RegExp(`^(${tagPattern.source})(?![\\w.:-])`));
18
18
  if (!tagNameMatch) {
19
19
  pos = tagStart + 1;
20
20
  continue;
@@ -1,45 +1,80 @@
1
- import type { StxOptions } from './types';
2
1
  /**
3
- * Convert a kebab-case string to PascalCase.
2
+ * Component Registry
4
3
  *
5
- * @example kebabToPascal("stx-link") // "StxLink"
6
- */
7
- export declare function kebabToPascal(str: string): string;
8
- /**
9
- * Convert a PascalCase string to kebab-case.
4
+ * Handles resolution of both builtin and file-based STX components.
5
+ * Builtin components (e.g., StxLink, StxImage) are registered in-memory
6
+ * and render directly to HTML without requiring a .stx file on disk.
7
+ * File-based components are resolved through a multi-strategy search
8
+ * that mirrors the existing resolution logic in utils.ts.
10
9
  *
11
- * @example pascalToKebab("StxLink") // "stx-link"
10
+ * @module component-registry
12
11
  */
13
- export declare function pascalToKebab(str: string): string;
14
- /** Singleton registry instance shared across the framework */
15
- export declare const registry: ComponentRegistry;
12
+ import type { StxOptions } from './types';
16
13
  /**
17
14
  * Context passed to builtin component render functions.
18
15
  */
19
- export declare interface RenderContext {
20
- context: Record<string, any>
21
- filePath: string
22
- options: StxOptions
23
- dependencies: Set<string>
16
+ export interface RenderContext {
17
+ /** Template variable context */
18
+ context: Record<string, any>;
19
+ /** Path of the template invoking this component */
20
+ filePath: string;
21
+ /** Compiler options */
22
+ options: StxOptions;
23
+ /** Dependency set for cache invalidation tracking */
24
+ dependencies: Set<string>;
24
25
  }
25
26
  /**
26
27
  * Props resolved from component attributes, categorized by binding type.
27
28
  */
28
- export declare interface ResolvedProps {
29
- static: Record<string, string | boolean>
30
- staticNames: Record<string, string>
31
- serverDynamic: Record<string, unknown>
32
- clientReactive: Record<string, string>
33
- events: Record<string, string>
29
+ export interface ResolvedProps {
30
+ /** Static attribute values — e.g. title="Hello", disabled */
31
+ static: Record<string, string | boolean>;
32
+ /**
33
+ * Original attribute spelling for any `static` key that was camelized,
34
+ * keyed by the camelized name — e.g. `{ activeClass: 'active-class' }`.
35
+ *
36
+ * A static attribute is two things at once, and they disagree about naming.
37
+ * To a `.stx` component it is a prop, and `active-class` has to arrive as
38
+ * `$props.activeClass` because a kebab key cannot be destructured. To a
39
+ * builtin that forwards unconsumed attributes onto real markup it is an HTML
40
+ * attribute, and must go out spelled the way the author wrote it. Keeping the
41
+ * original here lets both be true; without it, forwarding emitted
42
+ * `activeClass="…"` and the browser saw an unknown attribute.
43
+ *
44
+ * Only present for names that actually changed, so `key in staticNames` is a
45
+ * meaningful test.
46
+ */
47
+ staticNames: Record<string, string>;
48
+ /** Server-side dynamic bindings — e.g. :count="items.length" (evaluated at compile time) */
49
+ serverDynamic: Record<string, unknown>;
50
+ /** Client-side reactive bindings — expression string preserved for signals */
51
+ clientReactive: Record<string, string>;
52
+ /** Event handler bindings — e.g. @click="handler()" */
53
+ events: Record<string, string>;
34
54
  }
35
55
  /**
36
56
  * Definition for a builtin component that renders without a .stx file.
37
57
  */
38
- export declare interface BuiltinComponentDef {
39
- name: string
40
- aliases: string[]
41
- render(props: ResolvedProps, slotContent: string, ctx: RenderContext): string
58
+ export interface BuiltinComponentDef {
59
+ /** Canonical component name (e.g. "StxLink") */
60
+ name: string;
61
+ /** Alternative names that resolve to this component (e.g. ["stx-link"]) */
62
+ aliases: string[];
63
+ /** Render the component to an HTML string */
64
+ render(props: ResolvedProps, slotContent: string, ctx: RenderContext): string;
42
65
  }
66
+ /**
67
+ * Convert a kebab-case string to PascalCase.
68
+ *
69
+ * @example kebabToPascal("stx-link") // "StxLink"
70
+ */
71
+ export declare function kebabToPascal(str: string): string;
72
+ /**
73
+ * Convert a PascalCase string to kebab-case.
74
+ *
75
+ * @example pascalToKebab("StxLink") // "stx-link"
76
+ */
77
+ export declare function pascalToKebab(str: string): string;
43
78
  /**
44
79
  * Central registry for STX components.
45
80
  *
@@ -47,9 +82,37 @@ export declare interface BuiltinComponentDef {
47
82
  * file-based component resolution with multi-directory search.
48
83
  */
49
84
  export declare class ComponentRegistry {
50
- builtins: Map<string, BuiltinComponentDef>;
51
- registerBuiltin(def: BuiltinComponentDef): void;
52
- isBuiltin(name: string): boolean;
53
- getBuiltin(name: string): BuiltinComponentDef | null;
54
- resolveFile(name: string, fromFile: string, options: StxOptions, parentContext?: Record<string, any>): Promise<string | null>;
85
+ /** Map of lowercase name/alias → builtin definition */
86
+ builtins: Map<string, BuiltinComponentDef>;
87
+ /**
88
+ * Register a builtin component under its canonical name and all aliases.
89
+ */
90
+ registerBuiltin(def: BuiltinComponentDef): void;
91
+ /**
92
+ * Check whether a component name matches a registered builtin (case-insensitive).
93
+ */
94
+ isBuiltin(name: string): boolean;
95
+ /**
96
+ * Retrieve a builtin component definition by name (case-insensitive).
97
+ * Returns null if no matching builtin is found.
98
+ */
99
+ getBuiltin(name: string): BuiltinComponentDef | null;
100
+ /**
101
+ * Resolve a file-based component to an absolute file path.
102
+ *
103
+ * Resolution order:
104
+ * 1. Check `context.__importedComponents` Map (explicit @import directives)
105
+ * 2. Generate name variants: [baseName.stx, PascalCase.stx, kebab-case.stx]
106
+ * 3. Search directories in priority order:
107
+ * - Page-relative components/
108
+ * - Global componentsDir
109
+ * - Parent-relative components/
110
+ * - Project root src/components and components/
111
+ * - Built-in STX components directory
112
+ * 4. Search subdirectories one level deep within each directory
113
+ * 5. Return null if not found
114
+ */
115
+ resolveFile(name: string, fromFile: string, options: StxOptions, parentContext?: Record<string, any>): Promise<string | null>;
55
116
  }
117
+ /** Singleton registry instance shared across the framework */
118
+ export declare const registry: ComponentRegistry;
@@ -1,3 +1,21 @@
1
+ /**
2
+ * Unified Component Renderer
3
+ *
4
+ * Replaces all existing component processing with a single `processComponents()`
5
+ * entry point. Handles:
6
+ *
7
+ * 1. Built-in components (StxLink, StxImage, StxLoadingIndicator) — resolved
8
+ * via the component registry and rendered directly to HTML.
9
+ * 2. File-based user components — resolved from the filesystem and rendered
10
+ * via `renderComponentWithSlot()`.
11
+ * 3. `@component` directives — `@component('name', { props })...@endcomponent`.
12
+ * 4. `@import` directives — `@import('ComponentName', './path/to/component')`.
13
+ *
14
+ * Props are categorized into static, serverDynamic, clientReactive, and events
15
+ * through `parseComponentProps()` before being passed to the appropriate renderer.
16
+ *
17
+ * @module component-renderer
18
+ */
1
19
  import type { StxOptions } from './types';
2
20
  /**
3
21
  * Process ES `import { A, B } from '<spec>'` statements inside `<script>` tags
@@ -1,3 +1,27 @@
1
+ /**
2
+ * Composable Auto-Loader
3
+ *
4
+ * Discovers .ts files in `composablesDir`, bundles them into a single script,
5
+ * and exposes every exported binding on `window.__composables`. That object is
6
+ * what `import { useThing } from '@composables'` compiles to — store-imports.ts
7
+ * rewrites the import into `const { useThing } = window.__composables`, a plain
8
+ * destructure in the page's own scope, which is exactly where template
9
+ * directives can see it.
10
+ *
11
+ * Before this loader existed the rewrite had nothing feeding it: `@composables`
12
+ * imports compiled to a destructure of `undefined`, so every binding that
13
+ * referenced a composable silently did nothing — `@click` never fired, `@show`
14
+ * never flipped, and there was no console error or build warning to notice.
15
+ * Sharing client logic between pages was effectively impossible, so the same
16
+ * block got copy-pasted into page after page. See #1780.
17
+ *
18
+ * Mirrors store-loader.ts, with one deliberate difference: stores are
19
+ * side-effectful (running `defineStore(...)` registers them globally, so
20
+ * nothing needs to be exported), whereas composables are *values* — their
21
+ * exported names have to be collected and attached to `window.__composables`.
22
+ *
23
+ * @module composable-loader
24
+ */
1
25
  /**
2
26
  * Discover composable files and bundle them into a single script that
3
27
  * populates `window.__composables`.
@@ -1,6 +1,8 @@
1
1
  import path from "node:path";
2
2
  import { loadStxConfig } from "./config.js";
3
3
  import { getPublicEnvDefine } from "./public-env.js";
4
+ import { readSigned, sourceSignature, writeSigned } from "./source-signature.js";
5
+ import { stripModuleImports } from "./store-imports.js";
4
6
  import { STX_RUNTIME_GLOBALS } from "./runtime-globals.js";
5
7
  import { transformStoreImports } from "./store-imports.js";
6
8
  const _cachedComposableScripts = new Map, _cachedDirs = new Map, DEFAULT_DIR_CANDIDATES = ["composables", "functions"];
@@ -15,7 +17,8 @@ async function resolveComposablesDir(composablesDir) {
15
17
  if (cachedDir !== void 0)
16
18
  return cachedDir;
17
19
  const resolved = await probeDefaultDirs();
18
- _cachedDirs.set(cacheKey, resolved);
20
+ if (resolved !== null)
21
+ _cachedDirs.set(cacheKey, resolved);
19
22
  return resolved;
20
23
  }
21
24
  async function probeDefaultDirs() {
@@ -37,9 +40,6 @@ export async function getComposableScript(composablesDir) {
37
40
  const resolvedDir = await resolveComposablesDir(composablesDir);
38
41
  if (!resolvedDir)
39
42
  return null;
40
- const cached = _cachedComposableScripts.get(resolvedDir);
41
- if (cached !== void 0)
42
- return cached || null;
43
43
  const composableFiles = [];
44
44
  try {
45
45
  const glob = new Bun.Glob("**/*.ts");
@@ -49,11 +49,14 @@ export async function getComposableScript(composablesDir) {
49
49
  composableFiles.push(file);
50
50
  }
51
51
  } catch {
52
- _cachedComposableScripts.set(resolvedDir, "");
52
+ writeSigned(_cachedComposableScripts, resolvedDir, "", "");
53
53
  return null;
54
54
  }
55
+ const signature = sourceSignature(composableFiles), cached = readSigned(_cachedComposableScripts, resolvedDir, signature);
56
+ if (cached !== void 0)
57
+ return cached || null;
55
58
  if (composableFiles.length === 0) {
56
- _cachedComposableScripts.set(resolvedDir, "");
59
+ writeSigned(_cachedComposableScripts, resolvedDir, signature, "");
57
60
  return null;
58
61
  }
59
62
  composableFiles.sort();
@@ -71,7 +74,7 @@ export async function getComposableScript(composablesDir) {
71
74
  exportedNames.add(name);
72
75
  } catch {}
73
76
  code = transformStoreImports(code);
74
- code = code.replace(/^import\s+.*from\s+['"][^'"]+['"]\s*;?\s*$/gm, "");
77
+ code = stripModuleImports(code);
75
78
  code = code.replace(/^export\s+(default\s+)?/gm, "");
76
79
  code = transpiler.transformSync(code);
77
80
  chunks.push(`// Composable: ${composableName}
@@ -80,7 +83,7 @@ ${code.trim()}`);
80
83
  console.warn(`[stx] Failed to load composable ${file}:`, e);
81
84
  }
82
85
  if (chunks.length === 0 || exportedNames.size === 0) {
83
- _cachedComposableScripts.set(resolvedDir, "");
86
+ writeSigned(_cachedComposableScripts, resolvedDir, signature, "");
84
87
  return null;
85
88
  }
86
89
  const assignments = [...exportedNames].map((name) => ` try { __c[${JSON.stringify(name)}] = ${name}; } catch (e) {}`).join(`
@@ -108,7 +111,7 @@ ${assignments}
108
111
  window[__n] = __c[__n];
109
112
  }
110
113
  })();`;
111
- _cachedComposableScripts.set(resolvedDir, code);
114
+ writeSigned(_cachedComposableScripts, resolvedDir, signature, code);
112
115
  return code;
113
116
  }
114
117
  export function clearComposableCache() {
@@ -408,3 +408,10 @@ export {
408
408
  type ColorModeRef,
409
409
  type DarkRef,
410
410
  } from './use-color-mode';
411
+ // `defineForm` was reachable from a `<script client>` block (#1846) and listed
412
+ // in SERVER_ONLY_COMPOSABLES, but never re-exported here. That list is checked
413
+ // against this barrel — "exactly the composable exports the runtime does not
414
+ // provide" — so an entry with no export behind it failed the guard, while
415
+ // `framework-composables.ts` needs the entry to know to bundle it. One missing
416
+ // re-export, two contradictory test suites. See #1849.
417
+ export { defineForm, useForm } from './use-form';
@@ -219,3 +219,4 @@ export {
219
219
  useColorMode,
220
220
  useDark
221
221
  } from "./use-color-mode.js";
222
+ export { defineForm, useForm } from "./use-form.js";
@@ -78,8 +78,36 @@ export function useFetch(url, options = {}) {
78
78
  clearTimeout(timeoutId);
79
79
  status.set(response.status);
80
80
  statusText.set(response.statusText);
81
- if (!response.ok)
82
- throw Error(`HTTP ${response.status}: ${response.statusText}`);
81
+ if (!response.ok) {
82
+ let body = null;
83
+ try {
84
+ const text = await response.text();
85
+ if (text)
86
+ try {
87
+ body = JSON.parse(text);
88
+ } catch {
89
+ body = text;
90
+ }
91
+ } catch {
92
+ body = null;
93
+ }
94
+ let detail = "";
95
+ if (body && typeof body === "object")
96
+ detail = String(body.message ?? body.error ?? "");
97
+ else if (typeof body === "string")
98
+ detail = body.slice(0, 200);
99
+ let message = `HTTP ${response.status}`;
100
+ if (response.statusText)
101
+ message += `: ${response.statusText}`;
102
+ if (detail)
103
+ message += ` - ${detail}`;
104
+ const httpError = Error(message);
105
+ httpError.status = response.status;
106
+ httpError.statusText = response.statusText;
107
+ httpError.data = body;
108
+ httpError.response = response;
109
+ throw httpError;
110
+ }
83
111
  let payload = await parseResponse(response);
84
112
  if (transform)
85
113
  payload = transform(payload);