@stacksjs/stx 0.2.10 → 0.2.12

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 (129) hide show
  1. package/README.md +16 -11
  2. package/dist/a11y.d.ts +2 -1
  3. package/dist/analyzer.d.ts +4 -0
  4. package/dist/app-shell.d.ts +72 -0
  5. package/dist/app.d.ts +1 -0
  6. package/dist/broadcasting.d.ts +77 -0
  7. package/dist/build-assets.d.ts +32 -0
  8. package/dist/build-mode-detector.d.ts +26 -0
  9. package/dist/build.d.ts +31 -0
  10. package/dist/builtins/icon.d.ts +7 -0
  11. package/dist/builtins/index.d.ts +21 -0
  12. package/dist/builtins/stx-image.d.ts +2 -0
  13. package/dist/builtins/stx-link.d.ts +2 -0
  14. package/dist/builtins/stx-loading-indicator.d.ts +2 -0
  15. package/dist/bundle-analyzer/index.d.ts +2 -0
  16. package/dist/bundle-analyzer.js +20 -20
  17. package/dist/caching.d.ts +27 -0
  18. package/dist/cli.js +3942 -2608
  19. package/dist/client-helpers.d.ts +7 -3
  20. package/dist/client-script-bundler.d.ts +27 -0
  21. package/dist/client-script.d.ts +5 -1
  22. package/dist/client.d.ts +1 -0
  23. package/dist/component-hmr.d.ts +1 -0
  24. package/dist/component-processing.d.ts +107 -0
  25. package/dist/component-registry.d.ts +54 -0
  26. package/dist/component-renderer.d.ts +18 -0
  27. package/dist/components/StxImage.stx +12 -0
  28. package/dist/components/StxLink.stx +18 -8
  29. package/dist/composables/index.d.ts +7 -0
  30. package/dist/composables/use-broadcast-channel.d.ts +1 -0
  31. package/dist/composables/use-channel.d.ts +67 -0
  32. package/dist/composables/use-intersection-observer.d.ts +2 -0
  33. package/dist/composables/use-keyboard.d.ts +1 -0
  34. package/dist/composables/use-mouse.d.ts +1 -0
  35. package/dist/composables/use-router.d.ts +4 -0
  36. package/dist/composables/use-websocket.d.ts +1 -0
  37. package/dist/composables.d.ts +2 -0
  38. package/dist/composition-api.d.ts +1 -0
  39. package/dist/computed.d.ts +1 -0
  40. package/dist/config.d.ts +12 -1
  41. package/dist/craft-bridge.d.ts +6 -3
  42. package/dist/craft-components.d.ts +1 -0
  43. package/dist/craft.js +39 -33
  44. package/dist/deploy/index.d.ts +1 -0
  45. package/dist/dev-server/crosswind.d.ts +2 -5
  46. package/dist/dev-server/native-window.d.ts +19 -1
  47. package/dist/dev-server/serve-app.d.ts +15 -0
  48. package/dist/dev-server/serve-file.d.ts +3 -0
  49. package/dist/dev-server/serve-markdown.d.ts +3 -0
  50. package/dist/dev-server/serve-multi.d.ts +5 -0
  51. package/dist/dev-server/types.d.ts +2 -0
  52. package/dist/dev-server.d.ts +6 -40
  53. package/dist/devtools.d.ts +1 -0
  54. package/dist/directive-api.d.ts +1 -1
  55. package/dist/document-shell.d.ts +33 -0
  56. package/dist/error-boundaries.d.ts +1 -0
  57. package/dist/events.d.ts +1 -0
  58. package/dist/expressions.d.ts +38 -0
  59. package/dist/forms-validation.d.ts +2 -0
  60. package/dist/head.d.ts +1 -0
  61. package/dist/hot-reload.d.ts +1 -0
  62. package/dist/hydration-runtime.d.ts +1 -0
  63. package/dist/hydration.d.ts +1 -0
  64. package/dist/i18n.d.ts +3 -1
  65. package/dist/image-optimization/build-plugin.d.ts +1 -0
  66. package/dist/index.d.ts +22 -1
  67. package/dist/index.js +5571 -4182
  68. package/dist/inline-assets.d.ts +28 -0
  69. package/dist/internal-markdown.d.ts +0 -4
  70. package/dist/jsx-runtime.d.ts +1 -0
  71. package/dist/keep-alive.d.ts +1 -0
  72. package/dist/manifest.d.ts +41 -0
  73. package/dist/markdown.d.ts +1 -0
  74. package/dist/media/client/blur-up.d.ts +1 -0
  75. package/dist/media/client/lazy-load.d.ts +1 -0
  76. package/dist/media/client/upload-handler.d.ts +1 -0
  77. package/dist/media/shared/cache.d.ts +2 -0
  78. package/dist/media/video/index.d.ts +1 -0
  79. package/dist/media/video/processor/thumbnail.d.ts +1 -0
  80. package/dist/misc-directives.d.ts +49 -0
  81. package/dist/parser/directive-parser.d.ts +13 -0
  82. package/dist/performance-utils.d.ts +2 -0
  83. package/dist/placeholder.d.ts +45 -0
  84. package/dist/plugin-system.d.ts +32 -2
  85. package/dist/process.d.ts +11 -1
  86. package/dist/production-builder.d.ts +42 -0
  87. package/dist/production-server.d.ts +19 -0
  88. package/dist/props.d.ts +1 -0
  89. package/dist/pwa.js +3883 -2679
  90. package/dist/reactive.d.ts +2 -16
  91. package/dist/reactivity.d.ts +1 -0
  92. package/dist/render.d.ts +2 -1
  93. package/dist/router.d.ts +1 -1
  94. package/dist/runtime-injection.d.ts +20 -0
  95. package/dist/runtime.d.ts +2 -2
  96. package/dist/script-classifier.d.ts +35 -0
  97. package/dist/script-validation.d.ts +16 -0
  98. package/dist/server-bindings.d.ts +7 -0
  99. package/dist/server-components.d.ts +4 -0
  100. package/dist/signal-processing.d.ts +109 -0
  101. package/dist/signals-api.d.ts +309 -0
  102. package/dist/signals.d.ts +2 -309
  103. package/dist/spa-shell.d.ts +1 -0
  104. package/dist/ssg.js +3862 -2655
  105. package/dist/ssr.d.ts +1 -0
  106. package/dist/state-management.d.ts +27 -0
  107. package/dist/store-imports.d.ts +24 -0
  108. package/dist/store-loader.d.ts +16 -0
  109. package/dist/story/bun-test.d.ts +1 -0
  110. package/dist/story/compiled-output.d.ts +1 -0
  111. package/dist/story/interactions.d.ts +1 -0
  112. package/dist/story/renderer.d.ts +2 -0
  113. package/dist/story/setup.d.ts +1 -0
  114. package/dist/story/ui/code-panel.d.ts +1 -0
  115. package/dist/stx.d.ts +437 -0
  116. package/dist/style-scoping.d.ts +23 -0
  117. package/dist/template-compiler.d.ts +31 -0
  118. package/dist/template-hydrator.d.ts +18 -0
  119. package/dist/transitions.d.ts +1 -0
  120. package/dist/types/config-types.d.ts +62 -0
  121. package/dist/validator.d.ts +1 -0
  122. package/dist/variable-extractor.d.ts +1 -1
  123. package/dist/view-composers.d.ts +1 -0
  124. package/dist/virtual-scrolling.d.ts +1 -0
  125. package/dist/visual-testing.js +2 -2
  126. package/dist/web-components.d.ts +1 -0
  127. package/dist/x-element.d.ts +1 -0
  128. package/package.json +19 -7
  129. package/stx.d.ts +437 -0
@@ -216,10 +216,43 @@ export declare interface StrictModeConfig {
216
216
  failOnViolation?: boolean
217
217
  allowPatterns?: string[]
218
218
  }
219
+ /**
220
+ * Real-time broadcasting configuration.
221
+ *
222
+ * When enabled, stx starts a ts-broadcasting WebSocket server
223
+ * alongside the dev server so pages can receive push events.
224
+ */
225
+ export declare interface BroadcastingConfig {
226
+ enabled?: boolean
227
+ driver?: 'bun' | 'reverb' | 'pusher' | 'ably' | 'log' | 'null'
228
+ host?: string
229
+ port?: number
230
+ scheme?: 'ws' | 'wss'
231
+ verbose?: boolean
232
+ }
233
+ /**
234
+ * App head configuration — auto-generates the document shell.
235
+ * No .stx file needs to write <!DOCTYPE>, <html>, <head>, or <body>.
236
+ */
237
+ export declare interface AppHeadConfig {
238
+ title?: string
239
+ lang?: string
240
+ meta?: Array<Record<string, string>>
241
+ link?: Array<Record<string, string>>
242
+ script?: Array<{ src?: string, content?: string, [key: string]: any }>
243
+ headRaw?: string
244
+ bodyClass?: string
245
+ bodyAttrs?: Record<string, string>
246
+ }
219
247
  /**
220
248
  * stx configuration options
221
249
  */
222
250
  export declare interface StxConfig {
251
+ app?: {
252
+ head?: AppHeadConfig
253
+ }
254
+ root?: string
255
+ pagesDir?: string
223
256
  templatesDir?: string
224
257
  defaultTitle?: string
225
258
  defaultDescription?: string
@@ -232,7 +265,13 @@ export declare interface StxConfig {
232
265
  partialsDir: string
233
266
  componentsDir: string
234
267
  layoutsDir?: string
268
+ storesDir?: string
235
269
  defaultLayout?: string
270
+ plugins?: Array<string | [string, Record<string, any>]>
271
+ publicDir?: string
272
+ envFile?: string | boolean
273
+ envPrefix?: string
274
+ css?: string | { config?: string, content?: string[], preflight?: boolean, minify?: boolean }
236
275
  debug: boolean
237
276
  cachePath: string
238
277
  customDirectives?: CustomDirective[]
@@ -261,6 +300,29 @@ export declare interface StxConfig {
261
300
  build?: Partial<BuildConfig>
262
301
  media?: Partial<MediaConfig>
263
302
  strict?: boolean | StrictModeConfig
303
+ buildMode?: 'compile' | 'serve'
304
+ autoShell?: boolean
305
+ outputDir?: string
306
+ shell?: string | false
307
+ broadcasting?: Partial<BroadcastingConfig>
308
+ apiRoutes?: Record<string, (request: Request) => Response | Promise<Response>>
309
+ router?: {
310
+ enabled?: boolean
311
+ container?: string
312
+ linkSelector?: string
313
+ viewTransitions?: boolean
314
+ viewTransitionDuration?: number
315
+ viewTransitionEasing?: string
316
+ scrollToTop?: boolean
317
+ prefetch?: boolean
318
+ cache?: boolean
319
+ cacheTTL?: number
320
+ skipSelectors?: string
321
+ viewTransitionCSS?: Record<string, string>
322
+ progressColor?: string
323
+ progressHeight?: string
324
+ }
325
+ apiRouter?: { handleRequest: (request: Request) => Response | Promise<Response> }
264
326
  }
265
327
  /**
266
328
  * SSG Build configuration for static site generation
@@ -36,6 +36,7 @@ export declare function getPositionInfo(template: string, position: number): { l
36
36
  * @param params - Parameters passed to the directive
37
37
  * @returns Validation errors, if any
38
38
  */
39
+ // eslint-disable-next-line pickier/no-unused-vars
39
40
  export declare function validateDirective(directiveName: string, content: string, params: string[]): TemplateValidationError[];
40
41
  /**
41
42
  * Check if a template has any directives
@@ -19,7 +19,7 @@ export declare function extractVariables(scriptContent: string, context: Record<
19
19
  * @param scriptContent - ES module style script content
20
20
  * @returns CommonJS compatible script
21
21
  */
22
- export declare function convertToCommonJS(scriptContent: string): string;
22
+ export declare function convertToCommonJS(scriptContent: string, filePath?: string): string;
23
23
  /**
24
24
  * Extract script content from an HTML template
25
25
  *
@@ -168,4 +168,5 @@ export declare function clearComposers(): void;
168
168
  * }
169
169
  * ```
170
170
  */
171
+ // eslint-disable-next-line pickier/no-unused-vars
171
172
  export type ViewComposerCallback = (context: Record<string, any>, filePath: string) => void | Promise<void>;
@@ -28,6 +28,7 @@ export declare function processInfiniteListDirectives(template: string, context?
28
28
  * Process @virtualGrid directives for grid virtualization
29
29
  */
30
30
  export declare function processVirtualGridDirectives(template: string, context?: Record<string, unknown>, _filePath?: string): string;
31
+ /* eslint-disable prefer-const, style/max-statements-per-line, no-super-linear-backtracking, regexp/no-unused-capturing-group */
31
32
  /**
32
33
  * Virtual Scrolling Module
33
34
  *
@@ -1,6 +1,6 @@
1
1
  // @bun
2
- import B from"fs";import $ from"path";import k from"crypto";var V=[{name:"mobile",width:375,height:667,isMobile:!0,hasTouch:!0},{name:"tablet",width:768,height:1024,isMobile:!0,hasTouch:!0},{name:"desktop",width:1280,height:800,isMobile:!1,hasTouch:!1},{name:"wide",width:1920,height:1080,isMobile:!1,hasTouch:!1}];function J(N={}){let Y={snapshotDir:N.snapshotDir||"__snapshots__",screenshotDir:N.screenshotDir||"__screenshots__",updateSnapshots:N.updateSnapshots||process.env.UPDATE_SNAPSHOTS==="true",threshold:N.threshold??0.01,viewports:N.viewports||V,generateDiff:N.generateDiff??!0,diffDir:N.diffDir||"__diffs__",compare:N.compare||b,ci:N.ci??process.env.CI==="true",retries:N.retries??(N.ci?3:1)};return{async snapshot(M,X){let q=$.join(Y.snapshotDir,`${X}.html`),G=C(M),Q=K(G);if(B.existsSync(q)){let W=await Bun.file(q).text(),Z=K(C(W));if(Q===Z)return{passed:!0,snapshotPath:q,message:"Snapshot matches"};if(Y.updateSnapshots)return await B.promises.mkdir($.dirname(q),{recursive:!0}),await Bun.write(q,G),{passed:!0,snapshotPath:q,message:"Snapshot updated"};let F=$.join(Y.diffDir,`${X}.actual.html`),U=$.join(Y.diffDir,`${X}.diff.html`);return await B.promises.mkdir(Y.diffDir,{recursive:!0}),await Bun.write(F,G),await Bun.write(U,I(W,G)),{passed:!1,snapshotPath:q,actualPath:F,diffPath:U,message:"Snapshot does not match"}}return await B.promises.mkdir($.dirname(q),{recursive:!0}),await Bun.write(q,G),{passed:!0,snapshotPath:q,message:"New snapshot created"}},async snapshotJson(M,X){let q=$.join(Y.snapshotDir,`${X}.json`),G=JSON.stringify(M,null,2);if(B.existsSync(q)){let Q=await Bun.file(q).text();if(G===Q)return{passed:!0,snapshotPath:q};if(Y.updateSnapshots)return await Bun.write(q,G),{passed:!0,snapshotPath:q,message:"Snapshot updated"};return{passed:!1,snapshotPath:q,message:`JSON snapshot mismatch:
3
- ${z(Q,G)}`}}return await B.promises.mkdir($.dirname(q),{recursive:!0}),await Bun.write(q,G),{passed:!0,snapshotPath:q,message:"New snapshot created"}},matchInlineSnapshot(M,X){let q=C(M);if(X===void 0)return{passed:!0,expected:q};let G=C(X);return{passed:q===G,expected:q}},getConfig(){return Y}}}function D(N={}){let Y=J(N),M=Y.getConfig();return{async compareScreenshots(X,q,G="desktop"){let Q=$.join(M.screenshotDir,G,`${X}.png`),W=$.join(M.diffDir,G,`${X}.actual.png`),Z=$.join(M.diffDir,G,`${X}.diff.png`);if(B.existsSync(Q)){let F=await Bun.file(Q).arrayBuffer(),U=Buffer.from(F),_=await M.compare(q,U);if(_.match||_.diffPercentage<=M.threshold)return{viewport:G,passed:!0,baselinePath:Q};if(M.updateSnapshots)return await B.promises.mkdir($.dirname(Q),{recursive:!0}),await Bun.write(Q,q),{viewport:G,passed:!0,baselinePath:Q};if(await B.promises.mkdir($.dirname(W),{recursive:!0}),await Bun.write(W,q),_.diffImage&&M.generateDiff)await Bun.write(Z,_.diffImage);return{viewport:G,passed:!1,baselinePath:Q,actualPath:W,diffPath:M.generateDiff?Z:void 0,diffPercentage:_.diffPercentage}}return await B.promises.mkdir($.dirname(Q),{recursive:!0}),await Bun.write(Q,q),{viewport:G,passed:!0,baselinePath:Q}},async testViewports(X,q,G=M.viewports){let Q=[];for(let W of G){let Z=null;for(let F=0;F<M.retries;F++)try{let U=await q(W),_=await this.compareScreenshots(X,U,W.name);Q.push(_),Z=null;break}catch(U){if(Z=U,F<M.retries-1)await new Promise((_)=>setTimeout(_,100*(F+1)))}if(Z)Q.push({viewport:W.name,passed:!1,baselinePath:"",diffPercentage:100})}return Q},getSnapshotTester(){return Y}}}function L(N={}){let M=D(N).getSnapshotTester(),X=new Map;return{addStory(q){X.set(`${q.component}/${q.name}`,q)},addStories(q){for(let G of q)this.addStory(G)},getStories(){return Array.from(X.values())},getStoriesForComponent(q){return Array.from(X.values()).filter((G)=>G.component===q)},async testStory(q){let G=X.get(q);if(!G)return{name:q,passed:!1,duration:0,snapshots:[],screenshots:[],errors:[Error(`Story not found: ${q}`)]};let Q=Date.now(),W={name:q,passed:!0,duration:0,snapshots:[],screenshots:[],errors:[]};try{let Z=await G.render(),F=await M.snapshot(Z,q.replace(/\//g,"-"));if(W.snapshots.push(F),!F.passed)W.passed=!1}catch(Z){W.passed=!1,W.errors.push(Z)}return W.duration=Date.now()-Q,W},async testAllStories(){let q=[];for(let G of X.keys()){let Q=await this.testStory(G);q.push(Q)}return q},async generateStoryIndex(){let q={};for(let G of X.values()){if(!q[G.component])q[G.component]=[];q[G.component].push({name:G.name,args:G.args})}return JSON.stringify(q,null,2)}}}async function S(N,Y={}){let M={outputDir:Y.outputDir||"visual-test-report",format:Y.format||"html",title:Y.title||"Visual Test Report",includeScreenshots:Y.includeScreenshots??!0};await B.promises.mkdir(M.outputDir,{recursive:!0});let X=N.length,q=N.filter((F)=>F.passed).length,G=X-q,Q=N.reduce((F,U)=>F+U.duration,0);if(M.format==="json"){let F={title:M.title,summary:{total:X,passed:q,failed:G,duration:Q},results:N,generatedAt:new Date().toISOString()},U=$.join(M.outputDir,"report.json");return await Bun.write(U,JSON.stringify(F,null,2)),U}if(M.format==="markdown"){let F=`# ${M.title}
2
+ import B from"fs";import $ from"path";import k from"crypto";var V=[{name:"mobile",width:375,height:667,isMobile:!0,hasTouch:!0},{name:"tablet",width:768,height:1024,isMobile:!0,hasTouch:!0},{name:"desktop",width:1280,height:800,isMobile:!1,hasTouch:!1},{name:"wide",width:1920,height:1080,isMobile:!1,hasTouch:!1}];function J(N={}){let Y={snapshotDir:N.snapshotDir||"__snapshots__",screenshotDir:N.screenshotDir||"__screenshots__",updateSnapshots:N.updateSnapshots||process.env.UPDATE_SNAPSHOTS==="true",threshold:N.threshold??0.01,viewports:N.viewports||V,generateDiff:N.generateDiff??!0,diffDir:N.diffDir||"__diffs__",compare:N.compare||b,ci:N.ci??process.env.CI==="true",retries:N.retries??(N.ci?3:1)};return{async snapshot(M,X){let q=$.join(Y.snapshotDir,`${X}.html`),G=C(M),Q=K(G);if(await Bun.file(q).exists()){let W=await Bun.file(q).text(),Z=K(C(W));if(Q===Z)return{passed:!0,snapshotPath:q,message:"Snapshot matches"};if(Y.updateSnapshots)return await B.promises.mkdir($.dirname(q),{recursive:!0}),await Bun.write(q,G),{passed:!0,snapshotPath:q,message:"Snapshot updated"};let F=$.join(Y.diffDir,`${X}.actual.html`),U=$.join(Y.diffDir,`${X}.diff.html`);return await B.promises.mkdir(Y.diffDir,{recursive:!0}),await Bun.write(F,G),await Bun.write(U,I(W,G)),{passed:!1,snapshotPath:q,actualPath:F,diffPath:U,message:"Snapshot does not match"}}return await B.promises.mkdir($.dirname(q),{recursive:!0}),await Bun.write(q,G),{passed:!0,snapshotPath:q,message:"New snapshot created"}},async snapshotJson(M,X){let q=$.join(Y.snapshotDir,`${X}.json`),G=JSON.stringify(M,null,2);if(await Bun.file(q).exists()){let Q=await Bun.file(q).text();if(G===Q)return{passed:!0,snapshotPath:q};if(Y.updateSnapshots)return await Bun.write(q,G),{passed:!0,snapshotPath:q,message:"Snapshot updated"};return{passed:!1,snapshotPath:q,message:`JSON snapshot mismatch:
3
+ ${z(Q,G)}`}}return await B.promises.mkdir($.dirname(q),{recursive:!0}),await Bun.write(q,G),{passed:!0,snapshotPath:q,message:"New snapshot created"}},matchInlineSnapshot(M,X){let q=C(M);if(X===void 0)return{passed:!0,expected:q};let G=C(X);return{passed:q===G,expected:q}},getConfig(){return Y}}}function D(N={}){let Y=J(N),M=Y.getConfig();return{async compareScreenshots(X,q,G="desktop"){let Q=$.join(M.screenshotDir,G,`${X}.png`),W=$.join(M.diffDir,G,`${X}.actual.png`),Z=$.join(M.diffDir,G,`${X}.diff.png`);if(await Bun.file(Q).exists()){let F=await Bun.file(Q).arrayBuffer(),U=Buffer.from(F),_=await M.compare(q,U);if(_.match||_.diffPercentage<=M.threshold)return{viewport:G,passed:!0,baselinePath:Q};if(M.updateSnapshots)return await B.promises.mkdir($.dirname(Q),{recursive:!0}),await Bun.write(Q,q),{viewport:G,passed:!0,baselinePath:Q};if(await B.promises.mkdir($.dirname(W),{recursive:!0}),await Bun.write(W,q),_.diffImage&&M.generateDiff)await Bun.write(Z,_.diffImage);return{viewport:G,passed:!1,baselinePath:Q,actualPath:W,diffPath:M.generateDiff?Z:void 0,diffPercentage:_.diffPercentage}}return await B.promises.mkdir($.dirname(Q),{recursive:!0}),await Bun.write(Q,q),{viewport:G,passed:!0,baselinePath:Q}},async testViewports(X,q,G=M.viewports){let Q=[];for(let W of G){let Z=null;for(let F=0;F<M.retries;F++)try{let U=await q(W),_=await this.compareScreenshots(X,U,W.name);Q.push(_),Z=null;break}catch(U){if(Z=U,F<M.retries-1)await new Promise((_)=>setTimeout(_,100*(F+1)))}if(Z)Q.push({viewport:W.name,passed:!1,baselinePath:"",diffPercentage:100})}return Q},getSnapshotTester(){return Y}}}function L(N={}){let M=D(N).getSnapshotTester(),X=new Map;return{addStory(q){X.set(`${q.component}/${q.name}`,q)},addStories(q){for(let G of q)this.addStory(G)},getStories(){return Array.from(X.values())},getStoriesForComponent(q){return Array.from(X.values()).filter((G)=>G.component===q)},async testStory(q){let G=X.get(q);if(!G)return{name:q,passed:!1,duration:0,snapshots:[],screenshots:[],errors:[Error(`Story not found: ${q}`)]};let Q=Date.now(),W={name:q,passed:!0,duration:0,snapshots:[],screenshots:[],errors:[]};try{let Z=await G.render(),F=await M.snapshot(Z,q.replace(/\//g,"-"));if(W.snapshots.push(F),!F.passed)W.passed=!1}catch(Z){W.passed=!1,W.errors.push(Z)}return W.duration=Date.now()-Q,W},async testAllStories(){let q=[];for(let G of X.keys()){let Q=await this.testStory(G);q.push(Q)}return q},async generateStoryIndex(){let q={};for(let G of X.values()){if(!q[G.component])q[G.component]=[];q[G.component].push({name:G.name,args:G.args})}return JSON.stringify(q,null,2)}}}async function S(N,Y={}){let M={outputDir:Y.outputDir||"visual-test-report",format:Y.format||"html",title:Y.title||"Visual Test Report",includeScreenshots:Y.includeScreenshots??!0};await B.promises.mkdir(M.outputDir,{recursive:!0});let X=N.length,q=N.filter((F)=>F.passed).length,G=X-q,Q=N.reduce((F,U)=>F+U.duration,0);if(M.format==="json"){let F={title:M.title,summary:{total:X,passed:q,failed:G,duration:Q},results:N,generatedAt:new Date().toISOString()},U=$.join(M.outputDir,"report.json");return await Bun.write(U,JSON.stringify(F,null,2)),U}if(M.format==="markdown"){let F=`# ${M.title}
4
4
 
5
5
  `;F+=`## Summary
6
6
 
@@ -170,6 +170,7 @@ export declare function generateSFCWebComponent(parsed: ParsedSFCComponent): str
170
170
  * </style>
171
171
  * ```
172
172
  */
173
+ // eslint-disable-next-line pickier/no-unused-vars
173
174
  export declare function webComponentDirectiveHandler(content: string, params: string[], context: Record<string, any>, filePath: string): Promise<string>;
174
175
  /**
175
176
  * Property definition for reactive web components
@@ -1,3 +1,4 @@
1
+ /* eslint-disable prefer-const, style/max-statements-per-line, no-super-linear-backtracking, regexp/no-unused-capturing-group */
1
2
  /**
2
3
  * x-element: Lightweight two-way binding for STX
3
4
  *
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stacksjs/stx",
3
3
  "type": "module",
4
- "version": "0.2.10",
4
+ "version": "0.2.12",
5
5
  "description": "A performant UI Framework. Powered by Bun.",
6
6
  "author": "Chris Breuer <chris@stacksjs.org>",
7
7
  "license": "MIT",
@@ -60,6 +60,16 @@
60
60
  "types": "./dist/bundle-analyzer.d.ts",
61
61
  "import": "./dist/bundle-analyzer.js"
62
62
  },
63
+ "./composables": {
64
+ "bun": "./dist/composables/index.js",
65
+ "types": "./dist/composables/index.d.ts",
66
+ "import": "./dist/composables/index.js"
67
+ },
68
+ "./composables/*": {
69
+ "bun": "./dist/composables/*.js",
70
+ "types": "./dist/composables/*.d.ts",
71
+ "import": "./dist/composables/*.js"
72
+ },
63
73
  "./*": {
64
74
  "import": "./dist/*"
65
75
  }
@@ -72,7 +82,8 @@
72
82
  "files": [
73
83
  "LICENSE.md",
74
84
  "README.md",
75
- "dist"
85
+ "dist",
86
+ "stx.d.ts"
76
87
  ],
77
88
  "scripts": {
78
89
  "build": "bun run build:css && bun --bun build.ts",
@@ -103,14 +114,15 @@
103
114
  "typecheck": "bun tsc --noEmit"
104
115
  },
105
116
  "dependencies": {
106
- "@cwcss/crosswind": "^0.1.6",
117
+ "@cwcss/crosswind": "^0.2.0",
107
118
  "@stacksjs/clapp": "^0.2.0",
108
- "@stacksjs/desktop": "0.2.10",
109
- "@stacksjs/sanitizer": "0.2.10",
119
+ "@stacksjs/desktop": "0.2.12",
120
+ "@stacksjs/sanitizer": "0.2.12",
110
121
  "@stacksjs/ts-craft": "^0.0.2",
111
- "bun-plugin-stx": "0.2.10",
122
+ "bun-plugin-stx": "0.2.12",
112
123
  "bunfig": "^0.15.6",
113
- "stx-router": "0.2.10",
124
+ "stx-router": "0.2.12",
125
+ "ts-broadcasting": "^0.0.4",
114
126
  "ts-syntax-highlighter": "^0.2.1"
115
127
  }
116
128
  }
package/stx.d.ts ADDED
@@ -0,0 +1,437 @@
1
+ /**
2
+ * STX Ambient Type Declarations
3
+ *
4
+ * This file is shipped with the @stacksjs/stx package and referenced from
5
+ * the package's main types entry. When stx is installed, TypeScript picks
6
+ * these declarations up automatically — apps do NOT need to write their own
7
+ * stx.d.ts workaround for runtime globals.
8
+ *
9
+ * What's declared here:
10
+ * 1. Module declarations for `*.stx` and `*.md` imports
11
+ * 2. Runtime globals injected by the stx signals runtime into <script client>
12
+ * blocks (signals, lifecycle, stores, composables, head, routing, etc.)
13
+ * 3. The `window.stx` registry interface
14
+ *
15
+ * Source of truth for the auto-imported global list:
16
+ * packages/stx/src/client-script.ts → STX_AUTO_IMPORTS
17
+ * packages/stx/src/signals.ts → window.stx = { ... }
18
+ */
19
+
20
+ // ============================================================================
21
+ // Module declarations
22
+ // ============================================================================
23
+
24
+ // Allow importing .stx files
25
+ declare module '*.stx'
26
+
27
+ // Allow importing .md files with frontmatter
28
+ declare module '*.md' {
29
+ const content: string
30
+ const data: Record<string, any>
31
+ export default content
32
+ export { data }
33
+ }
34
+
35
+ // ============================================================================
36
+ // Signal types
37
+ // ============================================================================
38
+
39
+ interface StxSignal<T> {
40
+ (): T
41
+ set: (value: T) => void
42
+ update: (fn: (current: T) => T) => void
43
+ subscribe: (cb: (value: T) => void) => () => void
44
+ readonly _isSignal: true
45
+ }
46
+
47
+ interface StxDerivedSignal<T> {
48
+ (): T
49
+ readonly _isSignal: true
50
+ }
51
+
52
+ interface StxRef<T> {
53
+ value: T
54
+ }
55
+
56
+ type StxCleanup = () => void
57
+
58
+ // ============================================================================
59
+ // Signals (modern reactivity)
60
+ // ============================================================================
61
+
62
+ declare function state<T>(initial: T): StxSignal<T>
63
+ declare function derived<T>(compute: () => T): StxDerivedSignal<T>
64
+ declare function effect(fn: () => void | StxCleanup): StxCleanup
65
+ declare function batch(fn: () => void): void
66
+ declare function untrack<T>(value: T | StxSignal<T> | StxDerivedSignal<T>): T
67
+ declare function peek<T>(fn: () => T): T
68
+ declare function isSignal(value: unknown): value is StxSignal<unknown>
69
+ declare function isDerived(value: unknown): value is StxDerivedSignal<unknown>
70
+
71
+ // ============================================================================
72
+ // Lifecycle
73
+ // ============================================================================
74
+
75
+ declare function onMount(fn: () => void | StxCleanup): void
76
+ declare function onDestroy(fn: () => void): void
77
+ declare function onBeforeMount(fn: () => void): void
78
+ declare function onMounted(fn: () => void | StxCleanup): void
79
+ declare function onBeforeUpdate(fn: () => void): void
80
+ declare function onUpdated(fn: () => void): void
81
+ declare function onBeforeUnmount(fn: () => void): void
82
+ declare function onUnmounted(fn: () => void): void
83
+ declare function onErrorCaptured(fn: (error: unknown, instance: any, info: string) => boolean | void): void
84
+
85
+ // ============================================================================
86
+ // Template refs
87
+ // ============================================================================
88
+
89
+ declare function useRef<T = HTMLElement>(name: string): { current: T | null }
90
+
91
+ // ============================================================================
92
+ // Routing / navigation
93
+ // ============================================================================
94
+
95
+ declare function navigate(url: string, options?: { replace?: boolean }): void
96
+ declare function goBack(): void
97
+ declare function goForward(): void
98
+ declare function useRoute(): {
99
+ path: string
100
+ params: Record<string, string>
101
+ query: Record<string, string>
102
+ hash: string
103
+ }
104
+ declare function setRouteParams(params: Record<string, string>): void
105
+ declare function useSearchParams(): {
106
+ get: (key: string) => string | null
107
+ set: (key: string, value: string) => void
108
+ delete: (key: string) => void
109
+ has: (key: string) => boolean
110
+ }
111
+
112
+ // ============================================================================
113
+ // Data fetching
114
+ // ============================================================================
115
+
116
+ interface StxQueryResult<T> {
117
+ data: StxSignal<T | null>
118
+ loading: StxSignal<boolean>
119
+ error: StxSignal<Error | null>
120
+ refetch: () => Promise<void>
121
+ }
122
+
123
+ interface StxMutationResult<T> {
124
+ data: StxSignal<T | null>
125
+ loading: StxSignal<boolean>
126
+ error: StxSignal<Error | null>
127
+ mutate: (body?: any) => Promise<T>
128
+ }
129
+
130
+ declare function useFetch<T = any>(url: string, options?: any): StxQueryResult<T>
131
+ declare function useQuery<T = any>(url: string, options?: any): StxQueryResult<T>
132
+ declare function useMutation<T = any>(url: string, options?: any): StxMutationResult<T>
133
+
134
+ // ============================================================================
135
+ // DOM utilities
136
+ // ============================================================================
137
+
138
+ declare function useEventListener<K extends keyof WindowEventMap>(
139
+ target: Window | Document | HTMLElement | string | null,
140
+ event: K,
141
+ handler: (event: WindowEventMap[K]) => void,
142
+ options?: boolean | AddEventListenerOptions,
143
+ ): StxCleanup
144
+ declare function useEventListener(
145
+ target: Window | Document | HTMLElement | string | null,
146
+ event: string,
147
+ handler: (event: Event) => void,
148
+ options?: boolean | AddEventListenerOptions,
149
+ ): StxCleanup
150
+ declare function useMeta(meta: Record<string, string>): void
151
+ declare function useClickOutside(target: any, handler: (event: MouseEvent) => void): StxCleanup
152
+ declare function useFocus(target: any): { focused: StxSignal<boolean>, focus: () => void, blur: () => void }
153
+
154
+ // ============================================================================
155
+ // Timers / scheduling
156
+ // ============================================================================
157
+
158
+ declare function useDebounce<T extends (...args: any[]) => any>(fn: T, delay: number): T
159
+ declare function useDebouncedValue<T>(value: StxSignal<T>, delay: number): StxSignal<T>
160
+ declare function useThrottle<T extends (...args: any[]) => any>(fn: T, delay: number): T
161
+ declare function useInterval(fn: () => void, ms: number, options?: { immediate?: boolean }): {
162
+ start: () => void
163
+ stop: () => void
164
+ isActive: StxSignal<boolean>
165
+ }
166
+ declare function useTimeout(fn: () => void, ms: number): {
167
+ start: () => void
168
+ stop: () => void
169
+ isPending: StxSignal<boolean>
170
+ }
171
+ declare function nextTick(fn?: () => void): Promise<void>
172
+
173
+ // ============================================================================
174
+ // State utilities
175
+ // ============================================================================
176
+
177
+ declare function useToggle(initial?: boolean): [StxSignal<boolean>, (value?: boolean) => void]
178
+ declare function useCounter(initial?: number, options?: { min?: number, max?: number }): {
179
+ count: StxSignal<number>
180
+ inc: (n?: number) => void
181
+ dec: (n?: number) => void
182
+ set: (n: number) => void
183
+ reset: () => void
184
+ }
185
+ declare function useAsync<T>(fn: () => Promise<T>): {
186
+ data: StxSignal<T | null>
187
+ loading: StxSignal<boolean>
188
+ error: StxSignal<Error | null>
189
+ execute: () => Promise<T>
190
+ }
191
+
192
+ // ============================================================================
193
+ // Storage
194
+ // ============================================================================
195
+
196
+ declare function useLocalStorage<T>(key: string, defaultValue: T): StxSignal<T>
197
+ declare function useSessionStorage<T>(key: string, defaultValue: T): StxSignal<T>
198
+
199
+ // ============================================================================
200
+ // WebSocket
201
+ // ============================================================================
202
+
203
+ declare function useWebSocket(url: string, options?: {
204
+ immediate?: boolean
205
+ autoReconnect?: boolean | { retries?: number, delay?: number }
206
+ onMessage?: (event: MessageEvent) => void
207
+ onConnected?: () => void
208
+ onDisconnected?: () => void
209
+ onError?: (event: Event) => void
210
+ }): {
211
+ status: StxSignal<'CONNECTING' | 'OPEN' | 'CLOSING' | 'CLOSED'>
212
+ data: StxSignal<any>
213
+ send: (data: string | ArrayBuffer | Blob) => void
214
+ open: () => void
215
+ close: () => void
216
+ ws: WebSocket | null
217
+ }
218
+
219
+ // ============================================================================
220
+ // Color mode
221
+ // ============================================================================
222
+
223
+ declare function useColorMode(): {
224
+ mode: StxSignal<'light' | 'dark' | 'auto'>
225
+ setMode: (mode: 'light' | 'dark' | 'auto') => void
226
+ }
227
+ declare function useDark(): StxSignal<boolean>
228
+
229
+ // ============================================================================
230
+ // Head / SEO
231
+ // ============================================================================
232
+
233
+ interface StxHeadConfig {
234
+ title?: string
235
+ titleTemplate?: string | ((title?: string) => string)
236
+ meta?: Array<Record<string, string>>
237
+ link?: Array<Record<string, string>>
238
+ script?: Array<Record<string, string> & { children?: string }>
239
+ style?: Array<Record<string, string> & { children?: string }>
240
+ htmlAttrs?: Record<string, string>
241
+ bodyAttrs?: Record<string, string>
242
+ }
243
+
244
+ interface StxSeoMetaConfig {
245
+ title?: string
246
+ description?: string
247
+ keywords?: string
248
+ author?: string
249
+ ogTitle?: string
250
+ ogDescription?: string
251
+ ogImage?: string
252
+ ogUrl?: string
253
+ ogType?: string
254
+ twitterCard?: 'summary' | 'summary_large_image' | 'app' | 'player'
255
+ twitterTitle?: string
256
+ twitterDescription?: string
257
+ twitterImage?: string
258
+ robots?: string
259
+ canonical?: string
260
+ }
261
+
262
+ declare function useHead(config: StxHeadConfig): void
263
+ declare function useSeoMeta(config: StxSeoMetaConfig): void
264
+
265
+ // ============================================================================
266
+ // Vue-style reactivity (alternative API)
267
+ // ============================================================================
268
+
269
+ declare function ref<T>(value: T): StxRef<T>
270
+ declare function reactive<T extends object>(target: T): T
271
+ declare function computed<T>(getter: () => T): StxRef<T>
272
+ declare function watch<T>(
273
+ source: (() => T) | StxRef<T> | StxSignal<T>,
274
+ callback: (value: T, oldValue: T) => void,
275
+ options?: { immediate?: boolean, deep?: boolean },
276
+ ): StxCleanup
277
+ declare function watchEffect(fn: () => void): StxCleanup
278
+ declare function watchMultiple(
279
+ sources: Array<() => any>,
280
+ callback: (values: any[], oldValues: any[]) => void,
281
+ options?: { immediate?: boolean },
282
+ ): StxCleanup
283
+
284
+ // ============================================================================
285
+ // Component definition / composition API
286
+ // ============================================================================
287
+
288
+ declare function defineProps<T extends Record<string, any> = Record<string, any>>(definitions?: any): T
289
+ declare function withDefaults<T extends Record<string, any>>(props: T, defaults: Partial<T>): T
290
+ declare function defineEmits<T extends string = string>(): (event: T, payload?: any) => void
291
+ declare function defineExpose<T extends Record<string, any>>(exposed: T): void
292
+ declare function provide<T>(key: string | symbol, value: T): void
293
+ declare function inject<T>(key: string | symbol, defaultValue?: T): T | undefined
294
+ declare function getCurrentInstance(): any
295
+ declare function useSlots(): Record<string, any>
296
+ declare function useAttrs(): Record<string, any>
297
+ declare function $computed<T>(getter: () => T): StxRef<T>
298
+ declare function $watch<T>(
299
+ source: (() => T) | StxRef<T> | StxSignal<T>,
300
+ callback: (value: T, oldValue: T) => void,
301
+ options?: { immediate?: boolean, deep?: boolean },
302
+ ): StxCleanup
303
+
304
+ // ============================================================================
305
+ // Stores (Pinia-inspired, signals-based)
306
+ // ============================================================================
307
+
308
+ interface StxStorePersistOptions {
309
+ pick?: string[]
310
+ storage?: 'localStorage' | 'sessionStorage'
311
+ key?: string
312
+ }
313
+
314
+ interface StxStoreOptions {
315
+ persist?: boolean | StxStorePersistOptions
316
+ }
317
+
318
+ declare function defineStore<T>(
319
+ id: string,
320
+ setup: () => T,
321
+ options?: StxStoreOptions,
322
+ ): () => T
323
+ declare function defineStore<S extends Record<string, any>, G extends Record<string, any>, A extends Record<string, any>>(
324
+ id: string,
325
+ options: {
326
+ state?: () => S
327
+ getters?: G
328
+ actions?: A
329
+ persist?: boolean | StxStorePersistOptions
330
+ },
331
+ ): () => S & G & A
332
+ declare function useStore<T = any>(id: string): T
333
+ declare function createStore<T>(setup: () => T, options?: StxStoreOptions): () => T
334
+ declare function action<T extends (...args: any[]) => any>(fn: T): T
335
+ declare function createSelector<T, R>(selector: (state: T) => R): (state: T) => R
336
+
337
+ // ============================================================================
338
+ // JSX runtime (Vue/React style)
339
+ // ============================================================================
340
+
341
+ declare function h(tag: string | Function, props?: Record<string, any> | null, ...children: any[]): any
342
+ declare const Fragment: unique symbol
343
+
344
+ // ============================================================================
345
+ // window.stx registry
346
+ // ============================================================================
347
+
348
+ interface StxRuntimeRegistry {
349
+ // Signals
350
+ state: typeof state
351
+ derived: typeof derived
352
+ effect: typeof effect
353
+ batch: typeof batch
354
+ untrack: typeof untrack
355
+ peek: typeof peek
356
+ isSignal: typeof isSignal
357
+
358
+ // Lifecycle
359
+ onMount: typeof onMount
360
+ onDestroy: typeof onDestroy
361
+
362
+ // Composables
363
+ useFetch: typeof useFetch
364
+ useRef: typeof useRef
365
+ useQuery: typeof useQuery
366
+ useMutation: typeof useMutation
367
+
368
+ // Routing
369
+ navigate: typeof navigate
370
+ goBack: typeof goBack
371
+ goForward: typeof goForward
372
+ useRoute: typeof useRoute
373
+ setRouteParams: typeof setRouteParams
374
+ useSearchParams: typeof useSearchParams
375
+
376
+ // Composition
377
+ provide: typeof provide
378
+ defineProps: typeof defineProps
379
+ withDefaults: typeof withDefaults
380
+ defineEmits: typeof defineEmits
381
+ defineExpose: typeof defineExpose
382
+
383
+ // Vue compat
384
+ ref: typeof ref
385
+ reactive: typeof reactive
386
+ computed: typeof computed
387
+ watch: typeof watch
388
+ watchEffect: typeof watchEffect
389
+ $computed: typeof $computed
390
+ $watch: typeof $watch
391
+
392
+ // Utilities
393
+ useDebounce: typeof useDebounce
394
+ useDebouncedValue: typeof useDebouncedValue
395
+ useThrottle: typeof useThrottle
396
+ useInterval: typeof useInterval
397
+ useTimeout: typeof useTimeout
398
+ useToggle: typeof useToggle
399
+ useCounter: typeof useCounter
400
+ useClickOutside: typeof useClickOutside
401
+ useFocus: typeof useFocus
402
+ useAsync: typeof useAsync
403
+ useLocalStorage: typeof useLocalStorage
404
+ useEventListener: typeof useEventListener
405
+ useWebSocket: typeof useWebSocket
406
+ useColorMode: typeof useColorMode
407
+ useDark: typeof useDark
408
+ useHead: typeof useHead
409
+ useSeoMeta: typeof useSeoMeta
410
+
411
+ // Stores
412
+ defineStore: typeof defineStore
413
+ useStore: typeof useStore
414
+
415
+ // Mount API
416
+ mount: (setupFn: () => any) => void
417
+ mountEl: (selector: string, setupFn: () => any) => void
418
+
419
+ // Helpers (template helpers — populated by app)
420
+ helpers: Record<string, any>
421
+
422
+ // Internals (escape hatch)
423
+ [key: string]: any
424
+ }
425
+
426
+ // Bare global so existing code that references `stx` (without `window.`) still works
427
+ declare const stx: StxRuntimeRegistry
428
+
429
+ // Augment Window so `window.stx` is typed
430
+ interface Window {
431
+ stx: StxRuntimeRegistry
432
+ __STX_STORES__?: Record<string, any>
433
+ __STX_CURRENT_PROPS__?: Record<string, any>
434
+ __STX_CURRENT_ELEMENT__?: HTMLElement | null
435
+ __stxRouter?: boolean
436
+ __stxRouterConfig?: Record<string, any>
437
+ }