@stacksjs/stx 0.2.155 → 0.2.157

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 (241) 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.d.ts +0 -14
  148. package/dist/render.js +17 -0
  149. package/dist/route-middleware.d.ts +2 -35
  150. package/dist/route-types.d.ts +134 -0
  151. package/dist/route-types.js +24 -0
  152. package/dist/router.d.ts +5 -7
  153. package/dist/routes.d.ts +1 -3
  154. package/dist/runtime-globals.d.ts +147 -24
  155. package/dist/runtime-globals.js +16 -1
  156. package/dist/runtime-injection.d.ts +8 -0
  157. package/dist/runtime-injection.js +33 -24
  158. package/dist/script-emit.d.ts +30 -0
  159. package/dist/script-json.d.ts +36 -0
  160. package/dist/script-json.js +6 -0
  161. package/dist/script-validation.d.ts +21 -4
  162. package/dist/seo.d.ts +118 -47
  163. package/dist/server-bindings.d.ts +21 -0
  164. package/dist/sfc-template.d.ts +8 -8
  165. package/dist/signal-processing.d.ts +35 -15
  166. package/dist/signals-api.d.ts +117 -58
  167. package/dist/signals-api.js +7 -4
  168. package/dist/signals.d.ts +24 -17
  169. package/dist/signals.js +765 -60
  170. package/dist/site-builder/build.js +8 -0
  171. package/dist/site-url.d.ts +46 -0
  172. package/dist/site-url.js +70 -0
  173. package/dist/slots.d.ts +86 -20
  174. package/dist/source-signature.d.ts +45 -0
  175. package/dist/source-signature.js +20 -0
  176. package/dist/ssg.d.ts +285 -142
  177. package/dist/ssg.js +1268 -556
  178. package/dist/state-dir.d.ts +4 -4
  179. package/dist/state-management.d.ts +0 -12
  180. package/dist/state-management.js +8 -25
  181. package/dist/store-imports.d.ts +23 -0
  182. package/dist/store-imports.js +3 -0
  183. package/dist/store-loader.d.ts +10 -0
  184. package/dist/store-loader.js +10 -9
  185. package/dist/story/analytics.d.ts +138 -60
  186. package/dist/story/bookmarks.d.ts +87 -34
  187. package/dist/story/cli.d.ts +123 -69
  188. package/dist/story/collect/scanner.d.ts +4 -0
  189. package/dist/story/collect/tree.d.ts +8 -1
  190. package/dist/story/commands/build.d.ts +11 -6
  191. package/dist/story/commands/dev.d.ts +15 -8
  192. package/dist/story/commands/preview.d.ts +13 -7
  193. package/dist/story/config-watcher.d.ts +20 -13
  194. package/dist/story/config.d.ts +11 -8
  195. package/dist/story/context.d.ts +14 -8
  196. package/dist/story/hmr.d.ts +24 -26
  197. package/dist/story/hot-swap.d.ts +34 -23
  198. package/dist/story/keyboard-shortcuts.d.ts +23 -18
  199. package/dist/story/output.d.ts +46 -44
  200. package/dist/story/performance.d.ts +107 -45
  201. package/dist/story/presets.d.ts +107 -41
  202. package/dist/story/renderer.d.ts +41 -27
  203. package/dist/story/search.d.ts +23 -12
  204. package/dist/story/server.d.ts +21 -11
  205. package/dist/story/setup.d.ts +23 -15
  206. package/dist/story/testing.d.ts +65 -40
  207. package/dist/story/types.d.ts +262 -160
  208. package/dist/streaming.d.ts +119 -57
  209. package/dist/stx-virtual-ts.d.ts +165 -0
  210. package/dist/stx-virtual-ts.js +372 -0
  211. package/dist/stx.d.ts +78 -1
  212. package/dist/style-scoping.d.ts +17 -4
  213. package/dist/suspense.d.ts +67 -52
  214. package/dist/teleport.d.ts +22 -0
  215. package/dist/template-tag-balance.d.ts +22 -6
  216. package/dist/transitions.d.ts +66 -55
  217. package/dist/typecheck.d.ts +77 -0
  218. package/dist/typecheck.js +156 -0
  219. package/dist/types/component-types.d.ts +187 -122
  220. package/dist/types/config-types.d.ts +758 -297
  221. package/dist/types/context-types.d.ts +46 -24
  222. package/dist/types/csp-types.d.ts +70 -66
  223. package/dist/types/directive-types.d.ts +211 -155
  224. package/dist/types/index.d.ts +6 -109
  225. package/dist/types/pwa-types.d.ts +273 -148
  226. package/dist/types.d.ts +0 -1
  227. package/dist/typescript-templates.d.ts +11 -0
  228. package/dist/unresolved-identifiers.d.ts +24 -22
  229. package/dist/unresolved-identifiers.js +2 -0
  230. package/dist/utils.d.ts +24 -21
  231. package/dist/validator.d.ts +28 -26
  232. package/dist/variable-extractor.d.ts +48 -13
  233. package/dist/variable-extractor.js +27 -4
  234. package/dist/view-composers.d.ts +80 -81
  235. package/dist/virtual-scrolling.d.ts +101 -81
  236. package/dist/vue-template.d.ts +23 -0
  237. package/dist/web-components/css-scoping.d.ts +21 -11
  238. package/dist/web-components.d.ts +81 -58
  239. package/dist/x-element.d.ts +0 -1
  240. package/package.json +5 -5
  241. package/stx.d.ts +78 -1
package/dist/index.d.ts CHANGED
@@ -44,6 +44,38 @@ export {
44
44
  useStorage,
45
45
  type UseStorageOptions,
46
46
  } from './composables/use-storage';
47
+ // View composers were unreachable: `runComposers` is wired into the render
48
+ // pipeline (process.ts:1206) and runs on every render, but the two functions that
49
+ // PUT anything in the registry were never exported, so the registry was always
50
+ // empty and the whole layer was dead weight. The module's own docblock tells you
51
+ // to `import { composer } from 'stx'`, which threw. The existing test suite
52
+ // imported from './view-composers' directly, so nothing exercised the public
53
+ // surface and the gap stayed invisible. See #1860.
54
+ export {
55
+ clearComposers,
56
+ composer,
57
+ composerPattern,
58
+ runComposers,
59
+ type ViewComposerCallback,
60
+ } from './view-composers';
61
+ // useForm and the `v` validator builder shipped unreachable: the module's
62
+ // docblock says `import { useForm, v } from 'stx'` (forms-validation.ts:9),
63
+ // and that import threw. process.ts reached the module lazily for directive
64
+ // processing only, so the form primitive itself had no way in. See #1856.
65
+ //
66
+ // `defineForm` is the deprecated pre-#1843 spelling, exported alongside so an
67
+ // app does not have to rename in the same release it upgrades in.
68
+ export {
69
+ defineForm,
70
+ useForm,
71
+ v,
72
+ Validator,
73
+ type ValidatorFn,
74
+ type AsyncValidatorFn,
75
+ type ValidatorRule,
76
+ type FieldState,
77
+ type FormState,
78
+ } from './forms-validation';
47
79
  export { formatSize, getTotalSize } from './deploy/index';
48
80
  export { type ImageRenderResult, type ImageVariant, type ProcessedImage, clearImageCache, getFallbackVariant, getMimeType, groupVariantsByFormat, optimizeImage, processImage } from './image-optimization/index';
49
81
  export { type AnalysisResult } from './analyzer';
@@ -96,6 +128,16 @@ export * from './defer';
96
128
  export * from './deploy/index';
97
129
  export * from './dev-server';
98
130
  export * from './document-shell';
131
+ export * from './fragment-container';
132
+ // DevTools entry points. Exported selectively rather than with a wildcard:
133
+ // devtools.ts also exports registerStore, which would collide with
134
+ // state-management's function of the same name. Their own docblock says
135
+ // `import { enableDevTools } from 'stx'`, and that import threw — so devtools
136
+ // could not be turned on from the package entry at all. See #1873.
137
+ export { disableDevTools, enableDevTools } from './devtools';
138
+ // composables.ts:161 documents `import { onUpdate } from 'stx'`; it was
139
+ // reachable only through the stx/composables subpath.
140
+ export { onUpdate } from './composables';
99
141
  export * from './directive-api';
100
142
  export * from './docs';
101
143
  export * from './edge-runtime';
@@ -137,6 +179,10 @@ export * from './suspense';
137
179
  export * from './teleport';
138
180
  export * from './transitions';
139
181
  export * from './type-checker';
182
+ // Type-checking the TypeScript inside .stx script blocks, and the CLI behind
183
+ // `stx typecheck` (#1852). Distinct from ./type-checker, which is a bespoke
184
+ // checker for template EXPRESSIONS.
185
+ export * from './typecheck';
140
186
  export * from './web-components';
141
187
  export * from './virtual-scrolling';
142
188
  export * from './partial-hydration';
@@ -178,6 +224,26 @@ export { definePlugin, pluginManager } from './plugin-system';
178
224
  // These modules are exported first to establish their types
179
225
  export * from './render';
180
226
  export { extractClassNames, generateCrosswindCSS, getCrosswindServeAsset, injectCrosswindCSS } from './dev-server/crosswind';
227
+ // One crosswind merge shared by every render path (#1867)
228
+ export { deepMergeThemes, mergeCrosswindConfig, type MergedCrosswindConfig } from './crosswind-config';
229
+ // Dev-mode invalidation for the store/composable bundles (#1877)
230
+ export { clearStoreCache, getStoreScript } from './store-loader';
231
+ export { clearComposableCache } from './composable-loader';
232
+ // Generated union of layouts on disk (#1879)
233
+ export { discoverLayoutNames, generateLayoutTypes, LAYOUT_TYPES_FILE, renderLayoutTypes } from './layout-types';
234
+ // Typed route paths (#1887). Without this export the generated augmentation is
235
+ // dangling again, for exactly the reason `stx/routes` was.
236
+ export {
237
+ $path,
238
+ type CheckHref,
239
+ type KnownRoutes,
240
+ type RouteHref,
241
+ type RouteParams,
242
+ type RoutePath,
243
+ type RoutePattern,
244
+ type RoutePatternUsedAsHref,
245
+ type UnknownRoute,
246
+ } from './route-types';
181
247
  export { runDoctor, formatDoctorReport, findPantryPackage } from './doctor';
182
248
  export {
183
249
  applyTranslations,
package/dist/index.js CHANGED
@@ -6,6 +6,18 @@ export {
6
6
  useSessionStorage,
7
7
  useStorage
8
8
  } from "./composables/use-storage.js";
9
+ export {
10
+ clearComposers,
11
+ composer,
12
+ composerPattern,
13
+ runComposers
14
+ } from "./view-composers.js";
15
+ export {
16
+ defineForm,
17
+ useForm,
18
+ v,
19
+ Validator
20
+ } from "./forms-validation.js";
9
21
  export { formatSize, getTotalSize } from "./deploy/index.js";
10
22
  export { clearImageCache, getFallbackVariant, getMimeType, groupVariantsByFormat, optimizeImage, processImage } from "./image-optimization/index.js";
11
23
  export { fileExists } from "./utils.js";
@@ -44,6 +56,9 @@ export * from "./defer.js";
44
56
  export * from "./deploy/index.js";
45
57
  export * from "./dev-server.js";
46
58
  export * from "./document-shell.js";
59
+ export * from "./fragment-container.js";
60
+ export { disableDevTools, enableDevTools } from "./devtools.js";
61
+ export { onUpdate } from "./composables.js";
47
62
  export * from "./directive-api.js";
48
63
  export * from "./docs.js";
49
64
  export * from "./edge-runtime.js";
@@ -84,6 +99,7 @@ export * from "./suspense.js";
84
99
  export * from "./teleport.js";
85
100
  export * from "./transitions.js";
86
101
  export * from "./type-checker.js";
102
+ export * from "./typecheck.js";
87
103
  export * from "./web-components.js";
88
104
  export * from "./virtual-scrolling.js";
89
105
  export * from "./partial-hydration.js";
@@ -119,6 +135,13 @@ export * from "./dynamic-components.js";
119
135
  export { definePlugin, pluginManager } from "./plugin-system.js";
120
136
  export * from "./render.js";
121
137
  export { extractClassNames, generateCrosswindCSS, getCrosswindServeAsset, injectCrosswindCSS } from "./dev-server/crosswind.js";
138
+ export { deepMergeThemes, mergeCrosswindConfig } from "./crosswind-config.js";
139
+ export { clearStoreCache, getStoreScript } from "./store-loader.js";
140
+ export { clearComposableCache } from "./composable-loader.js";
141
+ export { discoverLayoutNames, generateLayoutTypes, LAYOUT_TYPES_FILE, renderLayoutTypes } from "./layout-types.js";
142
+ export {
143
+ $path
144
+ } from "./route-types.js";
122
145
  export { runDoctor, formatDoctorReport, findPantryPackage } from "./doctor.js";
123
146
  export {
124
147
  applyTranslations,
@@ -1,3 +1,15 @@
1
+ /**
2
+ * Internal markdown utilities to replace ts-md dependency
3
+ * Provides frontmatter parsing and markdown to HTML conversion
4
+ */
5
+ export interface FrontmatterResult {
6
+ data: Record<string, unknown>;
7
+ content: string;
8
+ }
9
+ export interface MarkdownOptions {
10
+ gfm?: boolean;
11
+ breaks?: boolean;
12
+ }
1
13
  /**
2
14
  * Parse YAML frontmatter from markdown content
3
15
  * Frontmatter is delimited by --- at the start of the file
@@ -8,11 +20,3 @@ export declare function parseFrontmatter(content: string): FrontmatterResult;
8
20
  * Simple implementation that handles common markdown patterns
9
21
  */
10
22
  export declare function parseMarkdown(content: string, options?: MarkdownOptions): string;
11
- export declare interface FrontmatterResult {
12
- data: Record<string, unknown>
13
- content: string
14
- }
15
- export declare interface MarkdownOptions {
16
- gfm?: boolean
17
- breaks?: boolean
18
- }
@@ -1,3 +1,33 @@
1
+ /** One extracted island chunk: a content hash and the IIFE it holds. */
2
+ export interface IslandChunk {
3
+ /** Content hash of `code` — the chunk's filename stem (`/_stx/islands/<hash>.js`). */
4
+ hash: string;
5
+ /** The island setup IIFE (trimmed) to write to the chunk file. */
6
+ code: string;
7
+ }
8
+ export interface ExtractIslandChunksResult {
9
+ /** HTML with each inline island IIFE replaced by a `data-stx-src` reference. */
10
+ html: string;
11
+ /** The unique chunks to write (deduped by content hash within the page). */
12
+ chunks: IslandChunk[];
13
+ }
14
+ export interface ExtractIslandChunksOptions {
15
+ /**
16
+ * Stamp `data-stx-integrity="sha384-…"` (Subresource Integrity) on each
17
+ * chunked tag so the runtime can pin the chunk against tampering and a strict
18
+ * CSP can require SRI. Default `false` — only enable when chunks are served
19
+ * byte-for-byte unchanged (no CDN/proxy transform), else the hash won't match
20
+ * and the island silently won't hydrate.
21
+ */
22
+ integrity?: boolean;
23
+ /**
24
+ * Minify each chunk's JS (whitespace only) before hashing/writing, so the
25
+ * lazily-loaded bytes aren't padded with source whitespace. Default `false`;
26
+ * the SSG passes its `minify` flag. Hashing happens over the minified bytes,
27
+ * so the content hash + SRI always match what's served.
28
+ */
29
+ minify?: boolean;
30
+ }
1
31
  /**
2
32
  * Lift inline island IIFEs into content-hashed chunks. Pure: no filesystem
3
33
  * access — the caller writes `result.chunks` and `result.html`.
@@ -16,16 +46,3 @@ export declare function extractIslandChunks(html: string, options?: ExtractIslan
16
46
  * not used within N seconds" console warning for below-the-fold islands.
17
47
  */
18
48
  export declare function injectIslandChunkPrefetch(html: string): string;
19
- /** One extracted island chunk: a content hash and the IIFE it holds. */
20
- export declare interface IslandChunk {
21
- hash: string
22
- code: string
23
- }
24
- export declare interface ExtractIslandChunksResult {
25
- html: string
26
- chunks: IslandChunk[]
27
- }
28
- export declare interface ExtractIslandChunksOptions {
29
- integrity?: boolean
30
- minify?: boolean
31
- }
@@ -1,3 +1,66 @@
1
+ /**
2
+ * STX Keep-Alive
3
+ *
4
+ * Caches component instances to preserve state when toggling visibility.
5
+ * Similar to Vue's <KeepAlive> component.
6
+ *
7
+ * @module keep-alive
8
+ *
9
+ * @example
10
+ * ```html
11
+ * @keepAlive
12
+ * @if (currentTab === 'settings')
13
+ * <SettingsPanel />
14
+ * @elseif (currentTab === 'profile')
15
+ * <ProfilePanel />
16
+ * @endif
17
+ * @endkeepAlive
18
+ * ```
19
+ *
20
+ * With options:
21
+ * ```html
22
+ * @keepAlive(max: 10, include: 'Settings,Profile', exclude: 'Debug')
23
+ * <component :is="currentComponent" />
24
+ * @endkeepAlive
25
+ * ```
26
+ */
27
+ export interface KeepAliveOptions {
28
+ /** Maximum number of component instances to cache */
29
+ max?: number;
30
+ /** Comma-separated list of component names to include */
31
+ include?: string;
32
+ /** Comma-separated list of component names to exclude */
33
+ exclude?: string;
34
+ /** Custom cache key */
35
+ id?: string;
36
+ }
37
+ export interface CachedComponent {
38
+ /** Component identifier */
39
+ key: string;
40
+ /** Cached HTML content */
41
+ html: string;
42
+ /** Cached state data */
43
+ state: Record<string, unknown>;
44
+ /** Timestamp when cached */
45
+ cachedAt: number;
46
+ /** Scroll positions */
47
+ scrollPositions: Map<string, {
48
+ top: number;
49
+ left: number;
50
+ }>;
51
+ }
52
+ export interface KeepAliveCache {
53
+ /** Unique cache ID */
54
+ id: string;
55
+ /** Cached components (LRU order) */
56
+ cache: Map<string, CachedComponent>;
57
+ /** Maximum cache size */
58
+ max: number;
59
+ /** Include filter */
60
+ include: string[] | null;
61
+ /** Exclude filter */
62
+ exclude: string[] | null;
63
+ }
1
64
  /**
2
65
  * Process @keepAlive directives in templates.
3
66
  *
@@ -30,59 +93,16 @@ export declare function generateKeepAliveRuntime(): string;
30
93
  * Hook called when a component is activated from cache.
31
94
  * Use in client-side scripts.
32
95
  */
33
- export declare function onActivated(callback: (detail: { key: string; keepAliveId: string; cachedAt: number }) => void): void;
96
+ export declare function onActivated(callback: (detail: {
97
+ key: string;
98
+ keepAliveId: string;
99
+ cachedAt: number;
100
+ }) => void): void;
34
101
  /**
35
102
  * Hook called when a component is deactivated and cached.
36
103
  * Use in client-side scripts.
37
104
  */
38
- export declare function onDeactivated(callback: (detail: { key: string; keepAliveId: string }) => void): void;
39
- /* eslint-disable prefer-const, style/max-statements-per-line, no-super-linear-backtracking, regexp/no-unused-capturing-group */
40
- /**
41
- * STX Keep-Alive
42
- *
43
- * Caches component instances to preserve state when toggling visibility.
44
- * Similar to Vue's <KeepAlive> component.
45
- *
46
- * @module keep-alive
47
- *
48
- * @example
49
- * ```html
50
- * @keepAlive
51
- * @if (currentTab === 'settings')
52
- * <SettingsPanel />
53
- * @elseif (currentTab === 'profile')
54
- * <ProfilePanel />
55
- * @endif
56
- * @endkeepAlive
57
- * ```
58
- *
59
- * With options:
60
- * ```html
61
- * @keepAlive(max: 10, include: 'Settings,Profile', exclude: 'Debug')
62
- * <component :is="currentComponent" />
63
- * @endkeepAlive
64
- * ```
65
- */
66
- // =============================================================================
67
- // Types
68
- // =============================================================================
69
- export declare interface KeepAliveOptions {
70
- max?: number
71
- include?: string
72
- exclude?: string
73
- id?: string
74
- }
75
- export declare interface CachedComponent {
76
- key: string
77
- html: string
78
- state: Record<string, unknown>
79
- cachedAt: number
80
- scrollPositions: Map<string, { top: number; left: number }>
81
- }
82
- export declare interface KeepAliveCache {
83
- id: string
84
- cache: Map<string, CachedComponent>
85
- max: number
86
- include: string[] | null
87
- exclude: string[] | null
88
- }
105
+ export declare function onDeactivated(callback: (detail: {
106
+ key: string;
107
+ keepAliveId: string;
108
+ }) => void): void;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Generate a union of the layouts that exist on disk, so naming one wrong is a
3
+ * type error rather than an unlayouted page (stacksjs/stx#1879 ask 3).
4
+ *
5
+ * `definePageMeta({ layout: 'app' })` now selects a layout, which makes the
6
+ * name a load-bearing string — and a typo produces exactly the same render as a
7
+ * correct name pointing at a missing file. Nuxt types `layout` as a union of
8
+ * the files on disk for this reason; the failure is caught before the build,
9
+ * not by reading the console afterwards.
10
+ *
11
+ * ## Why this augments the real package, not a synthetic specifier
12
+ *
13
+ * The augmentation targets `@stacksjs/stx`, where `KnownLayouts` is really
14
+ * declared and really referenced by `PageMeta['layout']`.
15
+ *
16
+ * That mattered because the route-type generator used to emit
17
+ * `declare module "stx/routes"` — a specifier nothing in the source tree
18
+ * declares or imports, which TypeScript reads as an ambient module declaration
19
+ * rather than an augmentation. It parsed, shipped, and constrained nothing: the
20
+ * same "typed and never read" shape #1879 is about, one level up. Routes now
21
+ * use this same target (#1887); the reasoning below is what kept this file from
22
+ * repeating it.
23
+ *
24
+ * @module layout-types
25
+ */
26
+ /** The name of the generated declaration file, inside the state directory. */
27
+ export declare const LAYOUT_TYPES_FILE = "layout-types.d.ts";
28
+ /**
29
+ * Layout names in `layoutsDir`, without the `.stx` extension.
30
+ *
31
+ * Nested layouts keep their path (`admin/settings`), matching what the layout
32
+ * resolver accepts. `_layout.stx` files are excluded: they are applied by
33
+ * directory position, never named.
34
+ */
35
+ export declare function discoverLayoutNames(layoutsDir: string): string[];
36
+ /** The declaration text for a set of layout names. */
37
+ export declare function renderLayoutTypes(names: readonly string[]): string;
38
+ /**
39
+ * Write the declaration for `layoutsDir` into `outputDir`.
40
+ *
41
+ * Best-effort and idempotent: an unwritable state directory is not a build
42
+ * failure, and an unchanged file is left alone so watchers do not see a write
43
+ * on every render.
44
+ */
45
+ export declare function generateLayoutTypes(layoutsDir: string | undefined, outputDir: string): string[];
@@ -0,0 +1,56 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ export const LAYOUT_TYPES_FILE = "layout-types.d.ts";
4
+ export function discoverLayoutNames(layoutsDir) {
5
+ const names = [], walk = (dir, prefix) => {
6
+ let entries;
7
+ try {
8
+ entries = fs.readdirSync(dir, { withFileTypes: !0 });
9
+ } catch {
10
+ return;
11
+ }
12
+ for (const entry of entries) {
13
+ if (entry.name.startsWith("."))
14
+ continue;
15
+ const full = path.join(dir, entry.name);
16
+ if (entry.isDirectory())
17
+ walk(full, prefix ? `${prefix}/${entry.name}` : entry.name);
18
+ else if (entry.isFile() && entry.name.endsWith(".stx") && entry.name !== "_layout.stx") {
19
+ const base = entry.name.slice(0, -4);
20
+ names.push(prefix ? `${prefix}/${base}` : base);
21
+ }
22
+ }
23
+ };
24
+ walk(layoutsDir, "");
25
+ return names.sort();
26
+ }
27
+ export function renderLayoutTypes(names) {
28
+ const lines = [
29
+ "// Auto-generated by stx \u2014 do not edit.",
30
+ "//",
31
+ "// Makes `definePageMeta({ layout })` reject a name with no layout behind it.",
32
+ "// Include this file in your tsconfig (it lives in the stx state directory,",
33
+ "// `.stx/` by default) for the check to apply.",
34
+ ""
35
+ ];
36
+ if (names.length === 0) {
37
+ lines.push("// No layouts found \u2014 `layout` stays typed as `string`.", "export {}", "");
38
+ return lines.join(`
39
+ `);
40
+ }
41
+ lines.push("declare module '@stacksjs/stx' {", " interface KnownLayouts {", ...names.map((name) => ` '${name}': true`), " }", "}", "", "export {}", "");
42
+ return lines.join(`
43
+ `);
44
+ }
45
+ export function generateLayoutTypes(layoutsDir, outputDir) {
46
+ if (!layoutsDir)
47
+ return [];
48
+ const abs = path.isAbsolute(layoutsDir) ? layoutsDir : path.resolve(process.cwd(), layoutsDir), names = discoverLayoutNames(abs), contents = renderLayoutTypes(names), target = path.join(outputDir, LAYOUT_TYPES_FILE);
49
+ try {
50
+ if (fs.existsSync(target) && fs.readFileSync(target, "utf8") === contents)
51
+ return names;
52
+ fs.mkdirSync(outputDir, { recursive: !0 });
53
+ fs.writeFileSync(target, contents);
54
+ } catch {}
55
+ return names;
56
+ }
@@ -1 +1,11 @@
1
+ /**
2
+ * Deduplicate cold dynamic imports across concurrent template renders.
3
+ *
4
+ * Bun normally caches evaluated modules, but separate first-use import()
5
+ * calls can still enter its TypeScript compilation path concurrently. A dev
6
+ * server prewarming several routes exposed that race as partial module builds
7
+ * such as "Unexpected end of file" from forms-validation.ts. Keeping the
8
+ * pending promise makes first compilation single-flight while preserving lazy
9
+ * loading and the circular-dependency boundaries that require import().
10
+ */
1
11
  export declare function importOnce<T>(key: string, importer: () => Promise<T>): Promise<T>;
package/dist/loops.d.ts CHANGED
@@ -1,3 +1,46 @@
1
+ /**
2
+ * Module for processing loop directives (@foreach, @for, @while, @forelse)
3
+ *
4
+ * Supports loop control directives:
5
+ * - `@break` - Exit the loop immediately
6
+ * - `@break(condition)` - Exit if condition is true
7
+ * - `@continue` - Skip to next iteration
8
+ * - `@continue(condition)` - Skip if condition is true
9
+ *
10
+ * @foreach syntax variants:
11
+ * - `@foreach(items as item)` - Basic iteration
12
+ * - `@foreach(items as index => item)` - With index variable
13
+ *
14
+ * @example Basic iteration
15
+ * ```html
16
+ * @foreach(items as item)
17
+ * {{ item.name }}
18
+ * @endforeach
19
+ * ```
20
+ *
21
+ * @example With index variable
22
+ * ```html
23
+ * @foreach(items as index => item)
24
+ * {{ index }}: {{ item.name }}
25
+ * @if(index === 0)
26
+ * (first item)
27
+ * @endif
28
+ * @endforeach
29
+ * ```
30
+ *
31
+ * @example With loop control
32
+ * ```html
33
+ * @foreach(items as item)
34
+ * @if(item.skip)
35
+ * @continue
36
+ * @endif
37
+ * @if(item.stop)
38
+ * @break
39
+ * @endif
40
+ * {{ item.name }}
41
+ * @endforeach
42
+ * ```
43
+ */
1
44
  import type { StxOptions } from './types';
2
45
  /**
3
46
  * Process loops (@foreach, @for, @while, @forelse)
@@ -22,4 +65,27 @@ import type { StxOptions } from './types';
22
65
  * @param filePath - Path to template file for error messages
23
66
  * @param options - Optional stx configuration
24
67
  */
68
+ /**
69
+ * Split `@foreach(...)` / `@forelse(...)` params into the collection and the
70
+ * item binding.
71
+ *
72
+ * Two spellings are documented and only one was implemented. Blade's
73
+ * `items as item` worked; the JS-natural `item in items` — which
74
+ * docs/SCRIPT_VARIABLES.md shows, and which matches the `:for="item in items"`
75
+ * client directive — did not, and the failure was silent. An unrecognised
76
+ * binding was skipped with `continue`, so the directive text survived into the
77
+ * built page and every `{{ }}` inside it resolved against a variable that never
78
+ * existed. The reported page shipped `data-variant-row="{{ p.id }}"` to the
79
+ * browser: a literal no selector matches and no binding resolves
80
+ * (stacksjs/stx#1842).
81
+ *
82
+ * The operands are the other way round between the two forms, which is the
83
+ * whole reason this needs saying once rather than at each call site. Vue's
84
+ * parenthesised `(item, index)` maps onto the comma form the item parser
85
+ * already understands.
86
+ */
87
+ export declare function parseLoopBinding(params: string): {
88
+ arrayExpr: string;
89
+ itemVar: string;
90
+ } | null;
25
91
  export declare function processLoops(template: string, context: Record<string, any>, filePath: string, options?: StxOptions): string;
package/dist/loops.js CHANGED
@@ -4,6 +4,33 @@ import { processExpressions } from "./expressions.js";
4
4
  import { findDirectiveBlocks, findMatchingDelimiter } from "./parser/index.js";
5
5
  import { createSafeFunction, isExpressionSafe, isForExpressionSafe, sanitizeExpression, safeEvaluate, safeEvaluateObject } from "./safe-evaluator.js";
6
6
  const DEFAULT_MAX_WHILE_ITERATIONS = 1000, DEFAULT_USE_ALT_LOOP_VARIABLE = !1;
7
+ function preEvaluateIncludeData(content, ctx) {
8
+ const pattern = /@(include|partial)\s*\(\s*(['"])([^'"]+)\2\s*,\s*/g;
9
+ let result = "", lastIndex = 0, match;
10
+ while ((match = pattern.exec(content)) !== null) {
11
+ const argsStart = match.index + match[0].length, openParen = match.index + match[0].indexOf("("), close = findMatchingDelimiter(content, "(", ")", openParen);
12
+ if (close === -1)
13
+ continue;
14
+ const varsString = content.slice(argsStart, close).trim();
15
+ if (!varsString)
16
+ continue;
17
+ if (!Object.keys(ctx).some((name) => new RegExp(`(?:^|[^\\w$.])${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![\\w$])`).test(varsString)))
18
+ continue;
19
+ let frozen;
20
+ try {
21
+ const evaluated = safeEvaluateObject(varsString, ctx);
22
+ if (!evaluated || typeof evaluated !== "object")
23
+ continue;
24
+ frozen = JSON.stringify(evaluated);
25
+ } catch {
26
+ continue;
27
+ }
28
+ result += content.slice(lastIndex, argsStart) + frozen;
29
+ lastIndex = close;
30
+ pattern.lastIndex = close;
31
+ }
32
+ return lastIndex === 0 ? content : result + content.slice(lastIndex);
33
+ }
7
34
  function preEvaluateComponentProps(content, ctx) {
8
35
  const componentPat = /@component\s*\(/g;
9
36
  let result = content, match;
@@ -222,10 +249,10 @@ function findOutermostForelse(template) {
222
249
  }
223
250
  if (closeParen === -1)
224
251
  continue;
225
- const params = template.substring(openParen + 1, closeParen), asIndex = params.indexOf(" as ");
226
- if (asIndex === -1)
252
+ const params = template.substring(openParen + 1, closeParen), binding = parseLoopBinding(params);
253
+ if (!binding)
227
254
  continue;
228
- const arrayExpr = params.substring(0, asIndex).trim(), itemVar = params.substring(asIndex + 4).trim(), contentStart = closeParen + 1;
255
+ const { arrayExpr, itemVar } = binding, contentStart = closeParen + 1;
229
256
  let depth = 1, searchPos = contentStart;
230
257
  const nestedForelseRegex = /@forelse\s*\(/g, endForelseRegex = /@endforelse/g;
231
258
  let endForelsePos = -1;
@@ -310,6 +337,18 @@ function processForelse(template, context) {
310
337
  }
311
338
  return output;
312
339
  }
340
+ export function parseLoopBinding(params) {
341
+ const asIndex = params.indexOf(" as ");
342
+ if (asIndex !== -1) {
343
+ const arrayExpr = params.slice(0, asIndex).trim(), itemVar = params.slice(asIndex + 4).trim();
344
+ return arrayExpr && itemVar ? { arrayExpr, itemVar } : null;
345
+ }
346
+ const inMatch = /^\s*(.+?)\s+in\s+([\s\S]+)$/.exec(params);
347
+ if (!inMatch)
348
+ return null;
349
+ const itemVar = inMatch[1].trim().replace(/^\(([\s\S]*)\)$/, "$1").trim(), arrayExpr = inMatch[2].trim();
350
+ return itemVar && arrayExpr ? { arrayExpr, itemVar } : null;
351
+ }
313
352
  export function processLoops(template, context, filePath, options) {
314
353
  let output = template;
315
354
  output = processForelse(output, context);
@@ -333,10 +372,10 @@ export function processLoops(template, context, filePath, options) {
333
372
  }
334
373
  if (closeParen === -1)
335
374
  continue;
336
- const params = template.substring(openParen + 1, closeParen), asIndex = params.indexOf(" as ");
337
- if (asIndex === -1)
375
+ const params = template.substring(openParen + 1, closeParen), binding = parseLoopBinding(params);
376
+ if (!binding)
338
377
  continue;
339
- const start = foreachStart, arrayExpr = params.substring(0, asIndex), itemVar = params.substring(asIndex + 4), contentStart = closeParen + 1;
378
+ const start = foreachStart, { arrayExpr, itemVar } = binding, contentStart = closeParen + 1;
340
379
  let depth = 1, searchPos = contentStart;
341
380
  const nestedForeachRegex = /@foreach\s*\(/g, endforeachRegex = /@endforeach/g;
342
381
  while (depth > 0 && searchPos < template.length) {
@@ -387,6 +426,7 @@ export function processLoops(template, context, filePath, options) {
387
426
  array = Object.entries(array);
388
427
  isObjectEntries = !0;
389
428
  } else {
429
+ console.warn(`[stx] @foreach(${trimmedArrayExpr}) in ${filePath}: ${trimmedArrayExpr} is not iterable server-side, so the loop rendered nothing. ` + "Values a server loop reads must come from <script server> or the page context \u2014 " + "a plain <script> runs in the browser.");
390
430
  const errorMsg = inlineError("Foreach", `Error in @foreach: ${trimmedArrayExpr} is not iterable`, ErrorCodes.TYPE_ERROR);
391
431
  result = result.substring(0, start) + errorMsg + result.substring(end);
392
432
  continue;
@@ -468,6 +508,7 @@ export function processLoops(template, context, filePath, options) {
468
508
  processedContent = processExpressions(processedContent, itemContext, filePath);
469
509
  processedContent = processPropBindings(processedContent, itemContext);
470
510
  processedContent = preEvaluateComponentProps(processedContent, itemContext);
511
+ processedContent = preEvaluateIncludeData(processedContent, itemContext);
471
512
  loopResult += processedContent;
472
513
  }
473
514
  result = result.substring(0, start) + loopResult + result.substring(end);
@@ -2,7 +2,6 @@
2
2
  * Process markdown directives in templates
3
3
  * Converts markdown content to HTML
4
4
  */
5
- // eslint-disable-next-line pickier/no-unused-vars
6
5
  export declare function processMarkdownDirectives(template: string, context: Record<string, any>, filePath?: string): Promise<string>;
7
6
  /**
8
7
  * Custom directive handler for markdown