@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
@@ -1,3 +1,10 @@
1
+ /**
2
+ * STX Router Directive
3
+ *
4
+ * Usage in templates:
5
+ * @stxRouter - Inject the SPA router script
6
+ * @stxRouter('main') - Specify custom container selector
7
+ */
1
8
  import type { CustomDirective } from '../types';
2
9
  /**
3
10
  * Handler for @stxRouter directive
@@ -4,5 +4,4 @@
4
4
  * Browser-side functionality for STX applications.
5
5
  */
6
6
  export * from './directive';
7
- // Re-export getRouterScript from the canonical router (packages/router)
8
7
  export { getRouterScript } from 'stx-router';
@@ -1,4 +1,62 @@
1
- export declare function shouldLogBundlerDiagnostics(environment?: { STX_DEBUG?: string }): boolean;
1
+ /**
2
+ * Client Script Bundler
3
+ *
4
+ * Uses Bun.build to resolve and bundle imports in `<script client>` blocks.
5
+ * Only activates when real user imports are detected (local files, npm packages).
6
+ * stx runtime, @stores, and @composables are marked as external — handled
7
+ * by existing auto-import and store-import transforms.
8
+ *
9
+ * @module client-script-bundler
10
+ */
11
+ import type { BuildFailureDetail } from './build-message';
12
+ export declare function shouldLogBundlerDiagnostics(environment?: {
13
+ STX_DEBUG?: string;
14
+ }): boolean;
15
+ /**
16
+ * Client-script bundle failures recorded during the current process.
17
+ *
18
+ * When a `<script client>` block fails to bundle, the bundler falls back to
19
+ * shipping the ORIGINAL, unbundled source so the page still renders. That is a
20
+ * reasonable dev-server behaviour and a terrible build behaviour: imports never
21
+ * resolve, bindings silently do nothing, and `stx build` still exited 0, so CI
22
+ * published a broken page as a success.
23
+ *
24
+ * Nothing was counting these. `ssg.failedCount` only ever saw render-time throws,
25
+ * never a bundle failure, because the fallback swallowed it and returned a
26
+ * string. The registry gives the build a way to ask, without the bundler having
27
+ * to know whether it is running under a dev server or a build. See #1884.
28
+ */
29
+ export interface BundleFailure {
30
+ filePath: string;
31
+ message: string;
32
+ /**
33
+ * The same failure with its position intact, for a dev-server overlay to
34
+ * draw a code frame from. Absent only when the thrower gave no position.
35
+ */
36
+ details?: BuildFailureDetail[];
37
+ }
38
+ /** Record a bundle failure. Called from the bundler's fallback path. */
39
+ export declare function recordBundleFailure(filePath: string, message: string, details?: BuildFailureDetail[]): void;
40
+ /** Every bundle failure since the last reset. */
41
+ export declare function getBundleFailures(): BundleFailure[];
42
+ /** Drop recorded failures — call at the start of a build, and in tests. */
43
+ export declare function clearBundleFailures(): void;
44
+ /** What to do when a client script will not bundle. */
45
+ export type BundlerFallbackMode = 'warn' | 'error';
46
+ /**
47
+ * Override the configured fallback mode, for tests and embedders.
48
+ *
49
+ * Pass `undefined` to go back to reading `strict.bundlerFallback` from config.
50
+ */
51
+ export declare function setBundlerFallbackMode(mode: BundlerFallbackMode | undefined): void;
52
+ /**
53
+ * The effective fallback mode.
54
+ *
55
+ * Read lazily rather than captured at import time: config is loaded after this
56
+ * module is first imported, so a value read at import time is always the
57
+ * default.
58
+ */
59
+ export declare function resolveBundlerFallback(): BundlerFallbackMode;
2
60
  /**
3
61
  * Check if a script has user imports that need Bun.build bundling.
4
62
  *
@@ -24,7 +82,7 @@ export declare function hasUserImports(code: string): boolean;
24
82
  */
25
83
  export declare function queueClientBundleBuild<T>(task: () => Promise<T>): Promise<T>;
26
84
  export declare function bundleClientScript(code: string, filePath: string, options?: {
27
- projectRoot?: string
28
- minify?: boolean
29
- cacheDir?: string
30
- }): Promise<string>;
85
+ projectRoot?: string;
86
+ minify?: boolean;
87
+ cacheDir?: string;
88
+ }): Promise<string>;
@@ -2,7 +2,8 @@ import path from "node:path";
2
2
  import fs from "node:fs";
3
3
  import { getPublicEnvDefine } from "./public-env.js";
4
4
  import { stateDir } from "./state-dir.js";
5
- import { formatBuildFailure } from "./build-message.js";
5
+ import { describeBuildFailure, formatBuildFailure } from "./build-message.js";
6
+ import { config } from "./config.js";
6
7
  const BUNDLE_CACHE_VERSION = 5, BUNDLE_CACHE_METADATA_VERSION = 1;
7
8
  export function shouldLogBundlerDiagnostics(environment = process.env) {
8
9
  return environment.STX_DEBUG === "true" || environment.STX_DEBUG === "1";
@@ -11,6 +12,29 @@ function logBundlerDiagnostic(...message) {
11
12
  if (shouldLogBundlerDiagnostics())
12
13
  console.log("[stx:bundler]", ...message);
13
14
  }
15
+ const _bundleFailures = [];
16
+ export function recordBundleFailure(filePath, message, details) {
17
+ _bundleFailures.push({ filePath: filePath || "<inline>", message, details });
18
+ }
19
+ export function getBundleFailures() {
20
+ return _bundleFailures.slice();
21
+ }
22
+ export function clearBundleFailures() {
23
+ _bundleFailures.length = 0;
24
+ }
25
+ let _fallbackOverride;
26
+ export function setBundlerFallbackMode(mode) {
27
+ _fallbackOverride = mode;
28
+ }
29
+ export function resolveBundlerFallback() {
30
+ if (_fallbackOverride)
31
+ return _fallbackOverride;
32
+ try {
33
+ return config.strict?.bundlerFallback ?? "warn";
34
+ } catch {
35
+ return "warn";
36
+ }
37
+ }
14
38
  const EXTERNAL_PATTERNS = [
15
39
  /^stx$/,
16
40
  /^@stacksjs\/stx$/,
@@ -268,7 +292,11 @@ ${publicAssignments}`.trim();
268
292
  }));
269
293
  return bundled;
270
294
  } catch (error) {
271
- console.warn(`[stx:bundler] error: ${formatBuildFailure(error, filePath || void 0)}`);
295
+ const details = describeBuildFailure(error, filePath || void 0), failure = formatBuildFailure(error, filePath || void 0);
296
+ console.warn(`[stx:bundler] error: ${failure}`);
297
+ recordBundleFailure(filePath || "", failure, details);
298
+ if (resolveBundlerFallback() === "error")
299
+ throw error;
272
300
  return code;
273
301
  } finally {
274
302
  try {
@@ -1,4 +1,45 @@
1
+ /**
2
+ * Client Script Processor
3
+ *
4
+ * Transforms <script client> blocks in SFC (.stx) components:
5
+ * 1. Auto-imports from 'stx' and '@stacksjs/browser' (no import statements needed)
6
+ * 2. Resolves `import { store } from '@stores'` to runtime store access
7
+ * 3. Transpiles TypeScript to JavaScript (strips type annotations, interfaces, import type)
8
+ * 4. Injects event binding code (from @click, @input, etc.) into the script scope
9
+ * 5. Auto-wraps in a scoped IIFE for isolation
10
+ *
11
+ * This enables clean component authoring:
12
+ * ```stx
13
+ * <template>
14
+ * <button @click="handleClick()">Count: {{ count() }}</button>
15
+ * </template>
16
+ *
17
+ * <script>
18
+ * // No imports needed! state, onMount, Trail, etc. are auto-imported
19
+ * const count = state(0)
20
+ *
21
+ * onMount(async () => {
22
+ * const trails = await Trail.all()
23
+ * console.log(trails)
24
+ * })
25
+ *
26
+ * function handleClick() {
27
+ * count.set(count() + 1)
28
+ * }
29
+ * </script>
30
+ * ```
31
+ *
32
+ * @module client-script
33
+ */
1
34
  import type { ParsedEvent } from './events';
35
+ export interface VendorCssImport {
36
+ /** The original specifier as written in the import — kept verbatim for the data attribute. */
37
+ source: string;
38
+ /** The resolved absolute filesystem path — used for dedupe and reads. */
39
+ resolvedPath: string;
40
+ /** The CSS file contents at build time. */
41
+ contents: string;
42
+ }
2
43
  /**
3
44
  * Strip side-effect `.css` imports from a `<script client>` body and read
4
45
  * their contents off disk.
@@ -21,7 +62,13 @@ import type { ParsedEvent } from './events';
21
62
  * call. Cross-component dedupe still relies on the caller's HTML response
22
63
  * (CSS rules collapse harmlessly at the browser level either way).
23
64
  */
24
- export declare function extractAndStripCssImports(code: string, options: { filePath?: string, projectRoot?: string }): { code: string, styles: VendorCssImport[] };
65
+ export declare function extractAndStripCssImports(code: string, options: {
66
+ filePath?: string;
67
+ projectRoot?: string;
68
+ }): {
69
+ code: string;
70
+ styles: VendorCssImport[];
71
+ };
25
72
  /**
26
73
  * Render collected vendor CSS as one `<style>` tag per resolved source,
27
74
  * each wrapped in `@layer vendor { … }` so the styles can never out-specificity
@@ -33,6 +80,33 @@ export declare function extractAndStripCssImports(code: string, options: { fileP
33
80
  * below un-layered rules, so vendor styles default to lowest precedence.
34
81
  */
35
82
  export declare function renderVendorStyleTags(styles: VendorCssImport[]): string;
83
+ /**
84
+ * Exports from 'stx' auto-imported into classic `<script>` / `<script client>`
85
+ * blocks.
86
+ *
87
+ * Derived from {@link STX_RUNTIME_GLOBALS} rather than hand-maintained (#1804).
88
+ * This was a separate 82-name literal, and it had drifted: 16 of those names
89
+ * were not on `window.stx` at all. Because the generated prologue destructures
90
+ * (`var { … } = window.stx`), a missing name is not an error — it binds
91
+ * `undefined`, and the author sees "undefined is not a function" at the call
92
+ * site with nothing naming the identifier or saying why. `provide` worked and
93
+ * `inject` did not; `onMounted` worked and `onUpdated` did not.
94
+ *
95
+ * Five of the 16 (`isDerived`, `inject`, `useSlots`, `watchMultiple`,
96
+ * `onBeforeMount`) are now implemented in the runtime and live in the shared
97
+ * list. The remaining 11 are server- or compile-time only — `h` and `Fragment`
98
+ * belong to the JSX runtime, `useMeta`/`getCurrentInstance`/`useAttrs`/
99
+ * `onErrorCaptured`/`onBeforeUpdate`/`onUpdated`/`createStore`/`createSelector`
100
+ * are module exports with no client counterpart, and `action` has no
101
+ * implementation anywhere — so they are no longer offered. A `ReferenceError`
102
+ * naming the identifier beats a silent `undefined`.
103
+ */
104
+ export declare const STX_AUTO_IMPORTS: readonly string[];
105
+ export interface BrowserCoreAutoImportResult {
106
+ code: string;
107
+ imports: string[];
108
+ models: string[];
109
+ }
36
110
  /**
37
111
  * Turn bare core browser helper usage into a real module import before the
38
112
  * client bundler runs.
@@ -55,6 +129,33 @@ export declare function injectBrowserCoreAutoImports(code: string): BrowserCoreA
55
129
  * undefined name.
56
130
  */
57
131
  export declare function stripCommentsAndLiterals(code: string): string;
132
+ export interface ClientScriptOptions {
133
+ /** Event bindings collected from template @event attributes */
134
+ eventBindings?: ParsedEvent[];
135
+ /** Whether to enable auto-imports (default: true) */
136
+ autoImports?: boolean;
137
+ /** Original script tag attributes (e.g., 'type="module"') */
138
+ attrs?: string;
139
+ /** Whether the component template contains : prefix directives */
140
+ hasColonDirectives?: boolean;
141
+ /** Template HTML content for auto-binding analysis (Phase 2: auto-detect which scripts need stx.mount()) */
142
+ templateContent?: string;
143
+ /** File path of the template (for relative import resolution in bundler) */
144
+ filePath?: string;
145
+ /** Project root directory (for @/ path resolution in bundler) */
146
+ projectRoot?: string;
147
+ /** Whether this is a production build (enables minification in bundler) */
148
+ production?: boolean;
149
+ /**
150
+ * Server → client data bridge. Top-level `<script server>` variables (and
151
+ * other template-context values) made available to `<script client>` code:
152
+ * any JSON-serializable value referenced — but not redeclared — by the client
153
+ * script is serialized into the client bundle, so reactive state can be seeded
154
+ * from build-time data (e.g. `const items = state(serverItems)`) without a
155
+ * round-trip fetch. Functions/non-serializable values are skipped.
156
+ */
157
+ serverData?: Record<string, unknown>;
158
+ }
58
159
  /**
59
160
  * Generate destructuring statements for auto-imported symbols
60
161
  * These access globals set up by the STX runtime and @stacksjs/browser
@@ -98,49 +199,3 @@ export declare function generateServerDataBridge(code: string, serverData?: Reco
98
199
  * @returns A complete `<script>...</script>` tag ready for browser injection
99
200
  */
100
201
  export declare function processClientScript(scriptContent: string, options?: ClientScriptOptions): Promise<string>;
101
- /**
102
- * Exports from 'stx' auto-imported into classic `<script>` / `<script client>`
103
- * blocks.
104
- *
105
- * Derived from {@link STX_RUNTIME_GLOBALS} rather than hand-maintained (#1804).
106
- * This was a separate 82-name literal, and it had drifted: 16 of those names
107
- * were not on `window.stx` at all. Because the generated prologue destructures
108
- * (`var { … } = window.stx`), a missing name is not an error — it binds
109
- * `undefined`, and the author sees "undefined is not a function" at the call
110
- * site with nothing naming the identifier or saying why. `provide` worked and
111
- * `inject` did not; `onMounted` worked and `onUpdated` did not.
112
- *
113
- * Five of the 16 (`isDerived`, `inject`, `useSlots`, `watchMultiple`,
114
- * `onBeforeMount`) are now implemented in the runtime and live in the shared
115
- * list. The remaining 11 are server- or compile-time only — `h` and `Fragment`
116
- * belong to the JSX runtime, `useMeta`/`getCurrentInstance`/`useAttrs`/
117
- * `onErrorCaptured`/`onBeforeUpdate`/`onUpdated`/`createStore`/`createSelector`
118
- * are module exports with no client counterpart, and `action` has no
119
- * implementation anywhere — so they are no longer offered. A `ReferenceError`
120
- * naming the identifier beats a silent `undefined`.
121
- */
122
- export declare const STX_AUTO_IMPORTS: readonly string[];
123
- export declare interface VendorCssImport {
124
- source: string
125
- resolvedPath: string
126
- contents: string
127
- }
128
- export declare interface BrowserCoreAutoImportResult {
129
- code: string
130
- imports: string[]
131
- models: string[]
132
- }
133
- // =============================================================================
134
- // Types
135
- // =============================================================================
136
- export declare interface ClientScriptOptions {
137
- eventBindings?: ParsedEvent[]
138
- autoImports?: boolean
139
- attrs?: string
140
- hasColonDirectives?: boolean
141
- templateContent?: string
142
- filePath?: string
143
- projectRoot?: string
144
- production?: boolean
145
- serverData?: Record<string, unknown>
146
- }
@@ -467,7 +467,7 @@ function transformAutoImports(code) {
467
467
  for (const symbol of STX_AUTO_IMPORTS) {
468
468
  if (existingImports.has(symbol) || locallyDeclared.has(symbol))
469
469
  continue;
470
- if (new RegExp(`\\b${symbol}\\s*\\(`, "g").test(transformedCode))
470
+ if (new RegExp(`\\b${symbol}\\s*(?:<[^>]*>)?\\s*\\(`, "g").test(transformedCode))
471
471
  usedStxImports.add(symbol);
472
472
  }
473
473
  for (const symbol of BROWSER_CORE_IMPORTS) {
@@ -522,6 +522,18 @@ function generateAutoImportGuard(stxImports, browserImports) {
522
522
  `;
523
523
  }
524
524
  export function extractBridgeData(context) {
525
+ const declared = context.__stxClientPayload;
526
+ if (declared && typeof declared === "object") {
527
+ const picked = {};
528
+ for (const key in declared) {
529
+ const value = declared[key];
530
+ if (typeof value === "function")
531
+ continue;
532
+ picked[key] = value;
533
+ }
534
+ picked.__stxDeclaredPayload = !0;
535
+ return picked;
536
+ }
525
537
  const out = {};
526
538
  for (const key in context) {
527
539
  if (key.startsWith("__") || key.startsWith("$"))
@@ -580,13 +592,13 @@ function warnAboutBridgedValue(name, json) {
580
592
  export function generateServerDataBridge(code, serverData) {
581
593
  if (!serverData)
582
594
  return "";
583
- const searchable = stripCommentsAndLiterals(code), lines = [];
595
+ const searchable = stripCommentsAndLiterals(code), declaredPayload = serverData.__stxDeclaredPayload === !0, lines = [];
584
596
  for (const [name, value] of Object.entries(serverData)) {
585
597
  if (!/^[A-Za-z_$][\w$]*$/.test(name))
586
598
  continue;
587
599
  if (name.startsWith("__") || name.startsWith("$"))
588
600
  continue;
589
- if (!referencesIdentifier(searchable, name))
601
+ if (!declaredPayload && !referencesIdentifier(searchable, name))
590
602
  continue;
591
603
  if (declaresClientIdentifier(code, name))
592
604
  continue;
@@ -0,0 +1,56 @@
1
+ /** Run the codemods over one file's source. */
2
+ export declare function codemodSource(source: string, options?: CodemodOptions): CodemodFileResult;
3
+ /** Human-readable report. */
4
+ export declare function formatCodemodFindings(findings: CodemodFinding[], wrote: boolean): string;
5
+ export declare interface CodemodFinding {
6
+ file: string
7
+ line: number
8
+ rule: CodemodRule
9
+ applied: boolean
10
+ reason?: string
11
+ snippet: string
12
+ }
13
+ export declare interface CodemodFileResult {
14
+ code: string
15
+ findings: CodemodFinding[]
16
+ }
17
+ export declare interface CodemodOptions {
18
+ rules?: CodemodRule[]
19
+ file?: string
20
+ }
21
+ /**
22
+ * Codemods for adopting stx primitives an app hand-rolled around (#1843).
23
+ *
24
+ * Two production apps built on stx reached past it into vanilla JS for things
25
+ * it already ships. The audit found the cause was discovery, not absence: one
26
+ * app called bare `confirm()` in five places and used `title=` thirteen times
27
+ * while `stxConfirm` and `x-tooltip` were both delivered and used zero times.
28
+ * A native fallback that WORKS is the hardest kind of gap to notice — nothing
29
+ * fails, so nothing prompts you to look.
30
+ *
31
+ * #1843 proposes both rewrites as "mechanical and safe". Neither is, and the
32
+ * difference matters enough that this tool reports by default and only edits
33
+ * when asked:
34
+ *
35
+ * - `confirm()` returns a boolean synchronously; `stxConfirm()` returns a
36
+ * Promise. `if (confirm(m))` rewritten to `if (stxConfirm(m))` is ALWAYS
37
+ * TRUE, because a Promise is truthy. A blind rewrite turns every "Delete
38
+ * this?" into an unconditional yes.
39
+ *
40
+ * - `title=` is announced by screen readers. `x-tooltip` sets no `role` and
41
+ * no `aria-*` — it is a visual tooltip driven by mouseover/focusin.
42
+ * Replacing one with the other removes the accessible description. So the
43
+ * tooltip rule ADDS `x-tooltip` and keeps `title`.
44
+ */
45
+ export type CodemodRule = | 'confirm'
46
+ | 'tooltip'
47
+ | 'route-params'
48
+ | 'search-params'
49
+ | 'navigate'
50
+ | 'fetch'
51
+ | 'polling'
52
+ | 'debounce'
53
+ | 'click-outside'
54
+ | 'focus'
55
+ | 'clipboard'
56
+ | 'watch';
@@ -0,0 +1,192 @@
1
+ function contextAfter(source, index, length = 240) {
2
+ return source.slice(index, index + length);
3
+ }
4
+ const REPORT_RULES = [
5
+ {
6
+ rule: "route-params",
7
+ pattern: /\blocation\s*\.\s*pathname\s*\.\s*(?:match|split)\s*\(/g,
8
+ primitive: "useRoute() / useRouteParams() / useRouteParam(name)",
9
+ why: "parsing the URL by hand re-implements the router and does not update on SPA navigation"
10
+ },
11
+ {
12
+ rule: "search-params",
13
+ pattern: /new\s+URLSearchParams\s*\(/g,
14
+ primitive: "useSearchParams()",
15
+ why: "reactive, and writes back through the router instead of history.replaceState"
16
+ },
17
+ {
18
+ rule: "navigate",
19
+ pattern: /\blocation\s*\.\s*(?:assign|replace)\s*\(|\blocation\s*\.\s*href\s*=/g,
20
+ primitive: "navigate(path) / goBack()",
21
+ why: "a full document load throws away every store and the scroll position; navigate() is an SPA transition",
22
+ reject: (source, index) => /^[^\n]*?(?:['"`]\s*)?(?:mailto:|tel:|sms:|https?:\/\/|\/\/)/.test(contextAfter(source, index, 160))
23
+ },
24
+ {
25
+ rule: "fetch",
26
+ pattern: /new\s+AbortController\s*\(/g,
27
+ primitive: "useFetch(url) / useAsyncData(key, fn)",
28
+ why: "cancellation, loading and error state are already wired; hand-rolling them is where the races live"
29
+ },
30
+ {
31
+ rule: "polling",
32
+ pattern: /setInterval\s*\(/g,
33
+ primitive: "useEventSource() / useSSE() / useWebSocket() / useChannel()",
34
+ why: "polling burns requests and lags; the push primitives are delivered",
35
+ reject: (source, index) => !/\bfetch\s*\(|\$fetch\b|useFetch\b|\.get\s*\(|axios\b/.test(contextAfter(source, index))
36
+ },
37
+ {
38
+ rule: "debounce",
39
+ pattern: /\bclearTimeout\s*\(/g,
40
+ primitive: "useDebounce(fn, ms) / useThrottle(fn, ms)",
41
+ why: "a hand-held timer handle per call site is the usual source of stale-result bugs",
42
+ requires: (source) => /setTimeout\s*\(/.test(source)
43
+ },
44
+ {
45
+ rule: "click-outside",
46
+ pattern: /document\s*\.\s*addEventListener\s*\(\s*['"](?:click|mousedown|pointerdown)['"]/g,
47
+ primitive: "useClickOutside(ref, fn)",
48
+ why: "the document-level listener plus the blur race is exactly what this composable exists to remove"
49
+ },
50
+ {
51
+ rule: "focus",
52
+ pattern: /\.\s*(?:getElementById|querySelector)\s*\([^)]*\)\s*(?:\?\.)?\s*\.?focus\s*\(/g,
53
+ primitive: "useFocus(ref) with x-ref",
54
+ why: "a query by id breaks under nested scopes and SPA navigation; a ref does not"
55
+ },
56
+ {
57
+ rule: "clipboard",
58
+ pattern: /navigator\s*\.\s*(?:clipboard|share)\b/g,
59
+ primitive: "useClipboard() / useShare()",
60
+ why: "permission state, the copied flag and the unsupported-browser fallback are already handled"
61
+ },
62
+ {
63
+ rule: "watch",
64
+ pattern: /\beffect\s*\(\s*\(\s*\)\s*=>\s*\{[^}]*\bvoid\s+\w+\s*\(\s*\)/g,
65
+ primitive: "watch(source, cb) / watchMultiple([...], cb)",
66
+ why: "the `void a(); void b()` dance is a manual dependency list; watch takes one directly"
67
+ }
68
+ ], DEFAULT_RULES = ["confirm", "tooltip", ...REPORT_RULES.map((r) => r.rule)];
69
+ function lineOf(source, index) {
70
+ let line = 1;
71
+ for (let i = 0;i < index; i++)
72
+ if (source[i] === `
73
+ `)
74
+ line++;
75
+ return line;
76
+ }
77
+ function snippetAt(source, index) {
78
+ const start = source.lastIndexOf(`
79
+ `, index) + 1, end = source.indexOf(`
80
+ `, index);
81
+ return source.slice(start, end === -1 ? source.length : end).trim().slice(0, 120);
82
+ }
83
+ function isInsideAsyncFunction(source, index) {
84
+ const before = source.slice(0, index), fnKeyword = before.lastIndexOf("function"), arrow = before.lastIndexOf("=>"), boundary = Math.max(fnKeyword, arrow);
85
+ if (boundary === -1)
86
+ return !1;
87
+ const window = source.slice(Math.max(0, boundary - 120), boundary);
88
+ return /\basync\b[^;{}]*$/.test(window);
89
+ }
90
+ function applyConfirmRule(source, file, findings) {
91
+ const pattern = /(^|[^\w$.])confirm\s*\(/g;
92
+ let out = "", last = 0, match;
93
+ while ((match = pattern.exec(source)) !== null) {
94
+ const callAt = match.index + match[1].length, line = lineOf(source, callAt), snippet = snippetAt(source, callAt);
95
+ if (/stxConfirm\s*\($/.test(source.slice(0, callAt + 8)))
96
+ continue;
97
+ const safe = isInsideAsyncFunction(source, callAt);
98
+ findings.push({
99
+ file,
100
+ line,
101
+ rule: "confirm",
102
+ applied: safe,
103
+ snippet,
104
+ reason: safe ? void 0 : "stxConfirm() returns a Promise and native confirm() is synchronous, so this needs `await` inside an async function. Rewriting it here would make the condition always true."
105
+ });
106
+ if (safe) {
107
+ out += source.slice(last, callAt) + "await stxConfirm(";
108
+ last = callAt + 8;
109
+ }
110
+ }
111
+ return out + source.slice(last);
112
+ }
113
+ function applyTooltipRule(source, file, findings) {
114
+ const pattern = /(<[a-z][\w-]*\b[^>]*?)\stitle=(["'])(.*?)\2/gis;
115
+ let out = "", last = 0, match;
116
+ while ((match = pattern.exec(source)) !== null) {
117
+ const [whole, openTag, quote, value] = match, line = lineOf(source, match.index), snippet = snippetAt(source, match.index);
118
+ if (/\bx-tooltip[=\s]/.test(openTag) || value.trim() === "") {
119
+ findings.push({
120
+ file,
121
+ line,
122
+ rule: "tooltip",
123
+ applied: !1,
124
+ snippet,
125
+ reason: /\bx-tooltip[=\s]/.test(openTag) ? "already has x-tooltip" : "empty title"
126
+ });
127
+ continue;
128
+ }
129
+ findings.push({ file, line, rule: "tooltip", applied: !0, snippet });
130
+ out += source.slice(last, match.index) + `${openTag} x-tooltip=${quote}${value}${quote} title=${quote}${value}${quote}`;
131
+ last = match.index + whole.length;
132
+ }
133
+ return out + source.slice(last);
134
+ }
135
+ function applyReportRules(source, file, rules, findings) {
136
+ for (const entry of REPORT_RULES) {
137
+ if (!rules.includes(entry.rule))
138
+ continue;
139
+ if (entry.requires && !entry.requires(source))
140
+ continue;
141
+ entry.pattern.lastIndex = 0;
142
+ let match;
143
+ while ((match = entry.pattern.exec(source)) !== null) {
144
+ if (entry.reject?.(source, match.index))
145
+ continue;
146
+ findings.push({
147
+ file,
148
+ line: lineOf(source, match.index),
149
+ rule: entry.rule,
150
+ applied: !1,
151
+ snippet: snippetAt(source, match.index),
152
+ reason: `${entry.primitive} \u2014 ${entry.why}`
153
+ });
154
+ }
155
+ }
156
+ }
157
+ export function codemodSource(source, options = {}) {
158
+ const rules = options.rules ?? DEFAULT_RULES, file = options.file ?? "<source>", findings = [];
159
+ applyReportRules(source, file, rules, findings);
160
+ let code = source;
161
+ if (rules.includes("confirm"))
162
+ code = applyConfirmRule(code, file, findings);
163
+ if (rules.includes("tooltip"))
164
+ code = applyTooltipRule(code, file, findings);
165
+ findings.sort((a, b) => a.line - b.line);
166
+ return { code, findings };
167
+ }
168
+ const REWRITABLE = new Set(["confirm", "tooltip"]);
169
+ export function formatCodemodFindings(findings, wrote) {
170
+ if (findings.length === 0)
171
+ return "No hand-rolled equivalents found.";
172
+ const applied = findings.filter((f) => f.applied), declined = findings.filter((f) => !f.applied && REWRITABLE.has(f.rule)), adopt = findings.filter((f) => !f.applied && !REWRITABLE.has(f.rule)), lines = [];
173
+ if (applied.length > 0) {
174
+ lines.push(wrote ? `Rewrote ${applied.length} site(s):` : `Would rewrite ${applied.length} site(s) (pass --fix):`);
175
+ for (const f of applied)
176
+ lines.push(` ${f.file}:${f.line} [${f.rule}] ${f.snippet}`);
177
+ }
178
+ if (declined.length > 0) {
179
+ lines.push("", `Needs a human \u2014 ${declined.length} site(s):`);
180
+ for (const f of declined)
181
+ lines.push(` ${f.file}:${f.line} [${f.rule}] ${f.snippet}
182
+ ${f.reason}`);
183
+ }
184
+ if (adopt.length > 0) {
185
+ lines.push("", `An stx primitive already covers this \u2014 ${adopt.length} site(s):`);
186
+ for (const f of adopt)
187
+ lines.push(` ${f.file}:${f.line} [${f.rule}] ${f.snippet}
188
+ use ${f.reason}`);
189
+ }
190
+ return lines.join(`
191
+ `);
192
+ }