@stacksjs/stx-vscode 0.2.357 → 0.2.358

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.
@@ -0,0 +1,965 @@
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
+ value: T
42
+ set: (value: T) => void
43
+ update: (fn: (current: T) => T) => void
44
+ subscribe: (cb: (value: T) => void) => () => void
45
+ readonly _isSignal: true
46
+ }
47
+
48
+ interface StxDerivedSignal<T> {
49
+ (): T
50
+ readonly value: T
51
+ // Derived signals carry `_isDerived: true` (distinguishes them from state
52
+ // signals, which carry `_isSignal: true`). The two branches are exposed
53
+ // separately as `isSignal()` / `isDerived()`. Surfaced by the dual-impl
54
+ // parity test — both implementations agree on this contract.
55
+ readonly _isDerived: true
56
+ }
57
+
58
+ interface StxRef<T> {
59
+ value: T
60
+ }
61
+
62
+ type StxCleanup = () => void
63
+
64
+ // ============================================================================
65
+ // Signals (modern reactivity)
66
+ // ============================================================================
67
+
68
+ declare function state<T>(_initial: T): StxSignal<T>
69
+ declare function derived<T>(_compute: () => T): StxDerivedSignal<T>
70
+ declare function effect(_fn: () => void | StxCleanup): StxCleanup
71
+ declare function batch(_fn: () => void): void
72
+ declare function untrack<T>(_value: T | StxSignal<T> | StxDerivedSignal<T>): T
73
+ declare function peek<T>(_fn: () => T): T
74
+ declare function isSignal(_value: unknown): value is StxSignal<unknown>
75
+ declare function isDerived(_value: unknown): value is StxDerivedSignal<unknown>
76
+
77
+ // ============================================================================
78
+ // Lifecycle
79
+ // ============================================================================
80
+
81
+ /**
82
+ * A lifecycle hook may be `async`.
83
+ *
84
+ * `onMount(async () => { rows.set(await load()) })` is the documented way to
85
+ * fetch a component's initial data - it is the second JSDoc example on both
86
+ * `onMount` implementations - and the runtime has always awaited nothing and
87
+ * simply let the promise run. These declarations left the promise out of the
88
+ * return union, so every async `onMount` in a real app was a type error
89
+ * against a call that works.
90
+ *
91
+ * Only a synchronous return can be a cleanup function: the runtime registers
92
+ * it before the next hook runs. So a promise resolves to `void`.
93
+ */
94
+ declare function onMount(_fn: () => void | StxCleanup | Promise<void>): void
95
+ declare function onDestroy(_fn: () => void | Promise<void>): void
96
+ declare function onBeforeMount(_fn: () => void | Promise<void>): void
97
+ declare function onMounted(_fn: () => void | StxCleanup | Promise<void>): void
98
+ declare function onBeforeUnmount(_fn: () => void | Promise<void>): void
99
+ declare function onUnmounted(_fn: () => void | Promise<void>): void
100
+ // onBeforeUpdate / onUpdated / onErrorCaptured are NOT declared: they exist in
101
+ // reactivity.ts and composition-api.ts but have no client-runtime counterpart,
102
+ // so declaring them promised a global that resolved to undefined (#1804).
103
+
104
+ // ============================================================================
105
+ // Template refs
106
+ // ============================================================================
107
+
108
+ /** Stable per-component id, unique across instances. */
109
+ declare function useId(_prefix?: string): string
110
+ /**
111
+ * Signal tracking a parent-driven `:prop="signal()"` attribute on the
112
+ * component's root element. One-way: parent to child.
113
+ */
114
+ declare function useReactiveProp<T = unknown>(
115
+ _name: string,
116
+ _defaultValue?: T,
117
+ _options?: { parse?: (_value: string) => T },
118
+ ): StxSignal<T>
119
+ interface StxModelOptions<T> {
120
+ default?: T
121
+ parse?: (value: string) => T
122
+ get?: (value: T) => T
123
+ set?: (value: T, modifiers: Readonly<Record<string, boolean>>) => T
124
+ }
125
+ interface StxModelSignal<T> extends StxSignal<T> {
126
+ readonly modifiers: Readonly<Record<string, boolean>>
127
+ input(value: T): void
128
+ change(value: T): void
129
+ }
130
+ declare function useModel<T>(options: StxModelOptions<T> & { default: T }): StxModelSignal<T>
131
+ declare function useModel<T>(name: string, options: StxModelOptions<T> & { default: T }): StxModelSignal<T>
132
+ declare function useModel<T = unknown>(name?: string, options?: StxModelOptions<T>): StxModelSignal<T | undefined>
133
+ declare function useModel<T = unknown>(options?: StxModelOptions<T>): StxModelSignal<T | undefined>
134
+ /**
135
+ * A template ref. Read it however the surrounding code reads things: `ref()`
136
+ * like every other stx accessor, or `ref.current` / `ref.value` for the
137
+ * Vue- and React-shaped call sites.
138
+ */
139
+ interface StxTemplateRef<T> {
140
+ (): T | null
141
+ readonly current: T | null
142
+ readonly value: T | null
143
+ }
144
+ declare function useRef<T = HTMLElement>(_name: string): StxTemplateRef<T>
145
+
146
+ // ============================================================================
147
+ // Routing / navigation
148
+ // ============================================================================
149
+
150
+ interface StxNavigateOptions {
151
+ /** Replace the current history entry instead of pushing a new one. */
152
+ replace?: boolean
153
+ /** Bypass the SPA router and perform a full document load. */
154
+ reload?: boolean
155
+ }
156
+
157
+ /**
158
+ * Navigate via the SPA router.
159
+ *
160
+ * The second argument is deliberately NOT typed to also accept a boolean. The
161
+ * legacy positional `forceReload` boolean still works at runtime — it was the
162
+ * shape that actually shipped — but keeping it out of the type means nothing
163
+ * that type-checks today changes meaning (#1807).
164
+ */
165
+ declare function navigate<T extends string>(
166
+ _url: T & import('@stacksjs/stx').CheckHref<T>,
167
+ _options?: StxNavigateOptions,
168
+ ): void
169
+ declare function goBack(): void
170
+ declare function goForward(): void
171
+
172
+ /**
173
+ * Re-run the current route against the server and swap the result, without a
174
+ * document load. Use after a mutation that changed server-rendered content.
175
+ */
176
+ declare function refresh(): Promise<boolean>
177
+
178
+ /**
179
+ * Expire one cached route so the next visit re-fetches it. Defaults to the
180
+ * current path.
181
+ */
182
+ declare function invalidateRoute(_url?: string): void
183
+ declare function useRoute(): {
184
+ path: string
185
+ params: Record<string, string>
186
+ query: Record<string, string>
187
+ hash: string
188
+ }
189
+ declare function setRouteParams(_params: Record<string, string>): void
190
+
191
+ /**
192
+ * Reactive access to the current route's dynamic segments.
193
+ *
194
+ * Published on `window.stx` by 5daaba1f9b but never added to the auto-imported
195
+ * list, so it was reachable only as `window.stx.useRouteParams()` — exactly the
196
+ * authoring-surface gap in #1846.
197
+ */
198
+ declare function useRouteParams(): StxSignal<Record<string, string>>
199
+ /** One dynamic segment, as a signal. */
200
+ declare function useRouteParam(_name: string, _defaultValue?: string): StxSignal<string>
201
+ /** How a search-param mutation is written to session history (#1825). */
202
+ interface StxSearchParamsCommitOptions {
203
+ /** Replace the current history entry instead of pushing a new one. */
204
+ replace?: boolean
205
+ }
206
+
207
+ /**
208
+ * Reactive access to the URL query string.
209
+ *
210
+ * The declaration was wrong in both directions (#1806): it promised `delete`
211
+ * and `has`, which did not exist at runtime, and omitted `setAll` and `data`,
212
+ * which did. `delete`/`has` are now implemented rather than dropped; `get`
213
+ * returns `undefined` for a missing key, not `null`, which is what the runtime
214
+ * and the docs have always said.
215
+ */
216
+ declare function useSearchParams(): {
217
+ /** The backing signal — read it to make a binding reactive to the query. */
218
+ data: StxSignal<Record<string, string>>
219
+ get: (_key: string) => string | undefined
220
+ has: (_key: string) => boolean
221
+ /** Set a param. Pushes a history entry unless `{ replace: true }` is passed. */
222
+ set: (_key: string, _value: string, _options?: StxSearchParamsCommitOptions) => void
223
+ /**
224
+ * Remove a param. Pushes a history entry unless `{ replace: true }` is passed.
225
+ *
226
+ * CONSUMING a one-shot param — an OAuth callback result, `?checkout=success`,
227
+ * a flash token — needs `{ replace: true }`. Under the default push, the URL
228
+ * that still carries the param becomes the previous history entry, so Back
229
+ * replays the callback and re-runs whatever consuming it triggered (#1825).
230
+ */
231
+ delete: (_key: string, _options?: StxSearchParamsCommitOptions) => void
232
+ /** Set several params. Pushes a history entry unless `{ replace: true }` is passed. */
233
+ setAll: (_values: Record<string, string>, _options?: StxSearchParamsCommitOptions) => void
234
+ }
235
+
236
+ // ============================================================================
237
+ // Data fetching
238
+ // ============================================================================
239
+
240
+ /** How one run of a request should behave. */
241
+ interface StxRefetchOptions {
242
+ /**
243
+ * Refresh without touching `loading` or clearing the current error.
244
+ *
245
+ * A poll refreshes data that is already on screen, so driving the first-load
246
+ * state from it puts a spinner over a populated view on every tick — the
247
+ * reason a polling view could not use these primitives at all (#1929). Bind
248
+ * `isFetching` for a subtle in-flight indicator instead.
249
+ */
250
+ background?: boolean
251
+ }
252
+
253
+ interface StxFetchResult<T> {
254
+ data: StxSignal<T | null>
255
+ /** True while a first-load request is in flight. A background one leaves it alone. */
256
+ loading: StxSignal<boolean>
257
+ /** True while ANY request is in flight, background ones included. */
258
+ isFetching: StxSignal<boolean>
259
+ error: StxSignal<Error | null>
260
+ refetch: (_options?: StxRefetchOptions) => Promise<void>
261
+ }
262
+
263
+ /*
264
+ * `useQuery` returns more than `useFetch` does, and declaring one shape for
265
+ * both hid it: `isStale` and `invalidate` existed at runtime and were invisible
266
+ * to the type, so reading either was an error on a value that was really there
267
+ * (#1929). Extending rather than widening the shared interface, because the
268
+ * inverse mistake is worse — declaring `invalidate` on `useFetch` would
269
+ * type-check a call to something that is undefined at runtime.
270
+ */
271
+ interface StxQueryResult<T> extends StxFetchResult<T> {
272
+ /** True when cached data is on screen while a revalidation runs. */
273
+ isStale: StxSignal<boolean>
274
+ /** Drop this key from the cache and refetch. */
275
+ invalidate: (_options?: StxRefetchOptions) => Promise<void>
276
+ }
277
+
278
+ interface StxMutationResult<T> {
279
+ data: StxSignal<T | null>
280
+ loading: StxSignal<boolean>
281
+ error: StxSignal<Error | null>
282
+ mutate: (body?: unknown) => Promise<T>
283
+ }
284
+
285
+ declare function useFetch<T = any>(_url: string, _options?: any): StxFetchResult<T>
286
+ /** Server-only, keyed JSON data exposed to this render's client hydration. */
287
+ declare function useServerData<T>(_key: string, _loader: () => T | Promise<T>): Promise<T>
288
+ /** Clear a hydration snapshot without changing existing signals. */
289
+ declare function clearServerData(_key?: string): void
290
+ declare function useQuery<T = any>(_url: string, _options?: any): StxQueryResult<T>
291
+ declare function useMutation<T = any>(_url: string, _options?: any): StxMutationResult<T>
292
+
293
+ /** Context handed to a request interceptor. Mutate it; the return is ignored. */
294
+ interface StxFetchRequestContext {
295
+ /** Which primitive made the call: 'useFetch' | 'useQuery' | 'useMutation'. */
296
+ source: string
297
+ /** Reassignable, so a hook can prefix a baseURL. */
298
+ url: string
299
+ /** `headers` is normalised to an object before the hook runs. */
300
+ options: RequestInit & { headers: Record<string, string> }
301
+ }
302
+
303
+ /** Context handed to a response interceptor. */
304
+ interface StxFetchResponseContext extends StxFetchRequestContext {
305
+ response: Response
306
+ }
307
+
308
+ /**
309
+ * Context handed to an error interceptor. `response` is the non-ok Response, or
310
+ * null when the fetch itself threw (a network failure); `error` is set only in
311
+ * the latter case.
312
+ */
313
+ interface StxFetchErrorContext extends StxFetchRequestContext {
314
+ response: Response | null
315
+ error: Error | null
316
+ }
317
+
318
+ /**
319
+ * Install request/response interceptors for every stx data primitive.
320
+ *
321
+ * All three funnel through one internal fetch, so a single pair of hooks
322
+ * carries an auth header or observes a 401 across `useFetch`, `useQuery` and
323
+ * `useMutation`. Calling this again REPLACES the hooks rather than adding to
324
+ * them, so a re-evaluated module cannot install the same interceptor twice.
325
+ *
326
+ * `onResponseError` fires for a non-ok response and for a thrown fetch. Return
327
+ * a Response to replace the failed one — the shape a 401 -> refresh -> retry
328
+ * takes: refresh the token, re-issue the request with a plain `fetch()` (not
329
+ * this data layer, so it does not re-enter the hook), and return that Response.
330
+ * Return nothing to let the original error stand.
331
+ */
332
+ declare function configureFetch(_config: {
333
+ onRequest?: (_ctx: StxFetchRequestContext) => void | Promise<void>
334
+ onResponse?: (_ctx: StxFetchResponseContext) => void | Promise<void>
335
+ onResponseError?: (_ctx: StxFetchErrorContext) => void | Response | Promise<void | Response>
336
+ /**
337
+ * Double-submit CSRF. By default every same-origin POST/PUT/PATCH/DELETE
338
+ * repeats the `X-CSRF-Token` cookie in an `X-CSRF-Token` header, which is
339
+ * what a Stacks server checks. Rename either side, or pass `false` to stop.
340
+ * Unlike the hooks, this persists across calls that do not mention it.
341
+ */
342
+ csrf?: false | {
343
+ cookie?: string
344
+ header?: string
345
+ /**
346
+ * A same-origin GET that sets the CSRF cookie, requested once before an
347
+ * unsafe request finds no cookie. Needed on a page served from a CDN
348
+ * cache, which cannot carry Set-Cookie. Any safe Stacks route seeds it.
349
+ */
350
+ prime?: string
351
+ }
352
+ }): void
353
+
354
+ declare function useOptimistic<T = any, A = any>(
355
+ _base: StxSignal<T> | (() => T) | T,
356
+ _reducer: (_current: T, _action: A) => T,
357
+ ): [StxSignal<T>, (_action: A, _settleWhen?: PromiseLike<unknown>) => () => void]
358
+
359
+ // ============================================================================
360
+ // DOM utilities
361
+ // ============================================================================
362
+
363
+ /**
364
+ * Options the runtime reads, including the target it binds to.
365
+ *
366
+ * The target travels HERE rather than in a leading parameter, because that is
367
+ * what the implementation does: `var target = (options && options.target) || window`.
368
+ */
369
+ interface StxEventListenerOptions {
370
+ /** Element, selector, or document/window. Defaults to `window`. */
371
+ target?: Window | Document | HTMLElement | string | null
372
+ capture?: boolean
373
+ passive?: boolean
374
+ once?: boolean
375
+ }
376
+
377
+ /*
378
+ * Event first, then handler, then options (stacksjs/stx#1923).
379
+ *
380
+ * This was declared target-first while the runtime has always been
381
+ * event-first, so the two disagreed in the worst possible direction: `_target`
382
+ * accepted `string`, so the CORRECT call got as far as reading 'keydown' as a
383
+ * selector before failing on the handler, and an author who trusted the type
384
+ * and added a target wrote `useEventListener(window, 'keydown', fn)` — which
385
+ * binds an event named "window" with the string 'keydown' as its handler, and
386
+ * silently attaches nothing.
387
+ *
388
+ * `browser-composables.ts` had it right all along; the package shipped two
389
+ * declarations of one name with different arities and the registry got the
390
+ * wrong one. Same shape as the `reactive` split: one name, two conventions, and
391
+ * no way for the call site to tell which it has.
392
+ */
393
+ declare function useEventListener<K extends keyof WindowEventMap>(
394
+ _event: K,
395
+ _handler: (event: WindowEventMap[K]) => void,
396
+ _options?: StxEventListenerOptions,
397
+ ): StxCleanup
398
+ declare function useEventListener(
399
+ _event: string,
400
+ _handler: (event: Event) => void,
401
+ _options?: StxEventListenerOptions,
402
+ ): StxCleanup
403
+ declare function useScrollLock(
404
+ _target?: HTMLElement | null | { current?: HTMLElement | null, value?: HTMLElement | null } | (() => HTMLElement | null | undefined),
405
+ ): StxSignal<boolean>
406
+ declare function useClickOutside(_target: HTMLElement | StxRef<HTMLElement | null> | string | null, _handler: (event: MouseEvent) => void): StxCleanup
407
+ declare function useFocus(_target: HTMLElement | StxRef<HTMLElement | null> | string | null): { focused: StxSignal<boolean>, focus: () => void, blur: () => void }
408
+
409
+ // ============================================================================
410
+ // Timers / scheduling
411
+ // ============================================================================
412
+
413
+ declare function useDebounce<T extends (..._args: any[]) => any>(_fn: T, _delay: number): T
414
+ declare function useDebouncedValue<T>(_value: StxSignal<T>, _delay: number): StxSignal<T>
415
+ declare function useThrottle<T extends (..._args: any[]) => any>(_fn: T, _delay: number): T
416
+ interface StxIntervalOptions {
417
+ /** Tick once on start/resume instead of waiting out the first interval. */
418
+ immediate?: boolean
419
+ /** Gate ticks. `false`, or a function returning false, skips them entirely. */
420
+ enabled?: boolean | (() => boolean)
421
+ /** Skip ticks while `document.hidden`. */
422
+ whileVisible?: boolean
423
+ }
424
+
425
+ interface StxIntervalControls {
426
+ /**
427
+ * Ticks elapsed. A plain number — read it, do not call it. It is not a
428
+ * signal and an `effect` cannot track it; use `subscribe` to react.
429
+ */
430
+ readonly counter: number
431
+ pause: () => void
432
+ resume: () => void
433
+ reset: () => void
434
+ /** Runs on each tick with the new count. Returns an unsubscribe. */
435
+ subscribe: (_fn: (_count: number) => void) => () => void
436
+ }
437
+
438
+ /**
439
+ * Interval with controls. Both call forms work:
440
+ * `useInterval(1000)` for the counter, `useInterval(fn, 1000)` to run something.
441
+ *
442
+ * There is deliberately no `start`/`stop`/`isActive` here. A declaration of that
443
+ * shape shipped for some time and nothing implemented it, so calls against it
444
+ * typechecked clean and threw `poll.start is not a function` on mount (#1941).
445
+ * `resume`/`pause` are the real names.
446
+ */
447
+ declare function useInterval(_interval?: number, _options?: StxIntervalOptions): StxIntervalControls
448
+ declare function useInterval(_fn: (_count: number) => void, _ms?: number, _options?: StxIntervalOptions): StxIntervalControls
449
+ declare function useInterval(_fn: (_count: number) => void, _options?: StxIntervalOptions): StxIntervalControls
450
+ declare function useTimeout(_fn: () => void, _ms?: number): {
451
+ /** Plain boolean, not a signal. Use `subscribe` to react to it. */
452
+ readonly isPending: boolean
453
+ start: () => void
454
+ stop: () => void
455
+ subscribe: (_fn: (_pending: boolean) => void) => () => void
456
+ }
457
+ declare function nextTick(_fn?: () => void): Promise<void>
458
+
459
+ // ============================================================================
460
+ // State utilities
461
+ // ============================================================================
462
+
463
+ declare function useToggle(_initial?: boolean): [StxSignal<boolean>, (value?: boolean) => void]
464
+ declare function useCounter(_initial?: number, _options?: { min?: number, max?: number }): {
465
+ count: StxSignal<number>
466
+ inc: (n?: number) => void
467
+ dec: (n?: number) => void
468
+ set: (n: number) => void
469
+ reset: () => void
470
+ }
471
+ declare function useAsync<T>(_fn: () => Promise<T>): {
472
+ data: StxSignal<T | null>
473
+ loading: StxSignal<boolean>
474
+ error: StxSignal<Error | null>
475
+ execute: () => Promise<T>
476
+ }
477
+
478
+ // ============================================================================
479
+ // Storage
480
+ // ============================================================================
481
+
482
+ declare function useLocalStorage<T>(_key: string, _defaultValue: T): StxSignal<T>
483
+ declare function useSessionStorage<T>(_key: string, _defaultValue: T): StxSignal<T>
484
+
485
+ /**
486
+ * Options accepted by the auto-imported `useCookie` global.
487
+ *
488
+ * This describes the CLIENT RUNTIME's cookie composable, which is what a bare
489
+ * `useCookie(...)` in a `<script client>` block resolves to. The module export
490
+ * `@stacksjs/stx/composables` accepts a slightly wider `expires` (Date, epoch ms
491
+ * or parseable string); the runtime calls `.toUTCString()` on it, so only a Date
492
+ * works there. See #1710.
493
+ */
494
+ interface StxCookieOptions {
495
+ /** Max age in seconds. */
496
+ maxAge?: number
497
+ /** Expiration date. */
498
+ expires?: Date
499
+ /** Cookie path. Defaults to '/'. */
500
+ path?: string
501
+ /** Cookie domain. */
502
+ domain?: string
503
+ /** Secure flag. Defaults to true when location.protocol is https:. */
504
+ secure?: boolean
505
+ /** SameSite attribute. Defaults to 'Lax'. */
506
+ sameSite?: 'Strict' | 'Lax' | 'None' | 'strict' | 'lax' | 'none'
507
+ /** Value returned when the cookie is absent. Defaults to ''. */
508
+ defaultValue?: string
509
+ /** Custom encoder. Defaults to encodeURIComponent. */
510
+ encode?: (_value: string) => string
511
+ /** Custom decoder. Defaults to decodeURIComponent. */
512
+ decode?: (_value: string) => string
513
+ }
514
+
515
+ /**
516
+ * Reactive binding to a single cookie (#1808).
517
+ *
518
+ * Returns a signal, like `useLocalStorage` — writes serialise straight to
519
+ * `document.cookie`, and `.set('')` deletes by emitting `max-age=0`.
520
+ */
521
+ declare function useCookie(_name: string, _options?: StxCookieOptions): StxSignal<string>
522
+
523
+ // ============================================================================
524
+ // WebSocket
525
+ // ============================================================================
526
+
527
+ declare function useWebSocket(_url: string, _options?: {
528
+ immediate?: boolean
529
+ autoReconnect?: boolean | { retries?: number, delay?: number }
530
+ onMessage?: (event: MessageEvent) => void
531
+ onConnected?: () => void
532
+ onDisconnected?: () => void
533
+ onError?: (event: Event) => void
534
+ }): {
535
+ status: StxSignal<'CONNECTING' | 'OPEN' | 'CLOSING' | 'CLOSED'>
536
+ data: StxSignal<unknown>
537
+ send: (data: string | ArrayBuffer | Blob) => void
538
+ open: () => void
539
+ close: () => void
540
+ ws: WebSocket | null
541
+ }
542
+
543
+ // ============================================================================
544
+ // Color mode
545
+ // ============================================================================
546
+
547
+ interface StxColorModeOptions {
548
+ /** localStorage key holding the preference. @default 'stx-color-mode' */
549
+ storageKey?: string
550
+ /** Mode used when nothing valid is stored. @default 'auto' */
551
+ initialMode?: 'light' | 'dark' | 'auto' | 'system'
552
+ /**
553
+ * Class applied to `<html>` when dark. @default 'dark'
554
+ * Applied alongside `attribute`; pass `null` to opt out of the class.
555
+ */
556
+ darkClass?: string | null
557
+ /** Attribute set to the resolved mode on `<html>`, e.g. 'data-theme'. */
558
+ attribute?: string | null
559
+ /**
560
+ * Spelling written to storage for "follow the system".
561
+ * @default whatever is already stored, else 'auto'
562
+ */
563
+ autoValue?: 'auto' | 'system'
564
+ /** Suppress CSS transitions across a mode switch. @default true */
565
+ disableTransitions?: boolean
566
+ }
567
+
568
+ /**
569
+ * Omitting the options picks up whatever `app.colorMode` in `stx.config.ts`
570
+ * published via the pre-paint boot script (stacksjs/stx#1794), so the storage
571
+ * key and attribute don't have to be repeated at the call site.
572
+ */
573
+ declare function useColorMode(_options?: StxColorModeOptions): {
574
+ /** Current resolved mode. */
575
+ readonly mode: 'light' | 'dark'
576
+ /** User preference, which may be 'auto'. */
577
+ readonly preference: 'light' | 'dark' | 'auto'
578
+ readonly isDark: boolean
579
+ set: (mode: 'light' | 'dark' | 'auto' | 'system') => void
580
+ toggle: () => void
581
+ subscribe: (fn: (mode: 'light' | 'dark', preference: 'light' | 'dark' | 'auto') => void) => () => void
582
+ }
583
+ declare function useDark(_options?: StxColorModeOptions): StxSignal<boolean> & {
584
+ readonly isDark: boolean
585
+ toggle: () => void
586
+ }
587
+ declare function useMediaQuery(query: string): StxSignal<boolean> & {
588
+ readonly matches: boolean
589
+ readonly value: boolean
590
+ }
591
+ declare function usePreferredDark(): ReturnType<typeof useMediaQuery>
592
+ declare function usePreferredLight(): ReturnType<typeof useMediaQuery>
593
+ declare function usePreferredReducedMotion(): ReturnType<typeof useMediaQuery>
594
+ declare function usePreferredContrast(): ReturnType<typeof useMediaQuery>
595
+ /** `document.visibilityState`, kept current; `'visible'` or `'hidden'`. */
596
+ declare function useDocumentVisibility(): StxSignal<DocumentVisibilityState> & {
597
+ readonly value: DocumentVisibilityState
598
+ }
599
+
600
+ // ============================================================================
601
+ // Head / SEO
602
+ // ============================================================================
603
+
604
+ interface StxHeadConfig {
605
+ title?: string
606
+ titleTemplate?: string | ((title?: string) => string)
607
+ meta?: Array<Record<string, string>>
608
+ link?: Array<Record<string, string>>
609
+ script?: Array<Record<string, string> & { children?: string }>
610
+ style?: Array<Record<string, string> & { children?: string }>
611
+ htmlAttrs?: Record<string, string>
612
+ bodyAttrs?: Record<string, string>
613
+ }
614
+
615
+ interface StxSeoMetaConfig {
616
+ title?: string
617
+ description?: string
618
+ keywords?: string
619
+ author?: string
620
+ ogTitle?: string
621
+ ogDescription?: string
622
+ ogImage?: string
623
+ ogUrl?: string
624
+ ogType?: string
625
+ twitterCard?: 'summary' | 'summary_large_image' | 'app' | 'player'
626
+ twitterTitle?: string
627
+ twitterDescription?: string
628
+ twitterImage?: string
629
+ robots?: string
630
+ canonical?: string
631
+ }
632
+
633
+ declare function useHead(_config: StxHeadConfig): void
634
+ declare function useSeoMeta(_config: StxSeoMetaConfig): void
635
+ /** No-op on the client; the real one runs at SSR/SSG time. */
636
+ declare function definePageMeta(_meta: Record<string, unknown>): void
637
+
638
+ // ============================================================================
639
+ // Vue-style reactivity (alternative API)
640
+ // ============================================================================
641
+
642
+ declare function ref<T>(_value: T): StxRef<T>
643
+ declare function reactive<T extends object>(_target: T): T
644
+ declare function computed<T>(_getter: () => T): StxRef<T>
645
+ declare function watch<T>(
646
+ _source: (() => T) | StxRef<T> | StxSignal<T>,
647
+ _callback: (value: T, oldValue: T) => void,
648
+ _options?: { immediate?: boolean, deep?: boolean },
649
+ ): StxCleanup
650
+ declare function watchEffect(_fn: () => void): StxCleanup
651
+ declare function watchMultiple(
652
+ _sources: Array<() => unknown>,
653
+ _callback: (values: unknown[], oldValues: unknown[]) => void,
654
+ _options?: { immediate?: boolean },
655
+ ): StxCleanup
656
+
657
+ // ============================================================================
658
+ // Component definition / composition API
659
+ // ============================================================================
660
+
661
+ declare function defineProps<T extends Record<string, any> = Record<string, any>>(_definitions?: any): T
662
+ declare function withDefaults<T extends Record<string, any>>(_props: T, _defaults: Partial<T>): T
663
+ /**
664
+ * Two forms, matching the runtime.
665
+ *
666
+ * The union form names the events and leaves the payload open. The map form
667
+ * gives each event a payload tuple, so `emit('view')` with a missing record
668
+ * and `emit('view', record, extra)` with a spare one are both errors.
669
+ *
670
+ * This used to be the union form alone, with a single optional `_payload`.
671
+ * That made the documented map form - the one `props.ts` and the composition
672
+ * API both implement, and the one the dashboard tables are written in - a
673
+ * constraint violation, and made a third argument an arity error even though
674
+ * the runtime forwards every argument it is given.
675
+ *
676
+ * @example
677
+ * const emit = defineEmits<'change' | 'close'>()
678
+ * @example
679
+ * const emit = defineEmits<{ view: [record: Row], remove: [id: number] }>()
680
+ */
681
+ declare function defineEmits<T extends string = string>(): (_event: T, ..._args: unknown[]) => void
682
+ declare function defineEmits<T extends Record<string, unknown[]>>(): <K extends keyof T & string>(_event: K, ..._args: T[K]) => void
683
+ declare function defineExpose<T extends Record<string, any>>(_exposed: T): void
684
+ declare function defineSlots<T extends Record<string, (..._args: any[]) => any> = Record<string, (..._args: any[]) => any>>(): T
685
+ declare function provide<T>(_key: string | symbol, _value: T): void
686
+ declare function inject<T>(_key: string | symbol, _defaultValue?: T): T | undefined
687
+ declare function useSlots(): Record<string, any>
688
+ // getCurrentInstance / useAttrs are NOT declared — composition-api.ts exports
689
+ // them for server use; no client-runtime counterpart exists (#1804).
690
+ declare function $computed<T>(_getter: () => T): StxRef<T>
691
+ declare function $watch<T>(
692
+ _source: (() => T) | StxRef<T> | StxSignal<T>,
693
+ _callback: (value: T, oldValue: T) => void,
694
+ _options?: { immediate?: boolean, deep?: boolean },
695
+ ): StxCleanup
696
+
697
+ // ============================================================================
698
+ // Toast notifications
699
+ // ============================================================================
700
+
701
+ /*
702
+ * Every option `addToast` reads (#1932).
703
+ *
704
+ * `title`, `id` and `dark` shipped in the runtime for #1913 and none of them
705
+ * reached here, so the feature built to unblock a migration could not be called
706
+ * from a typechecked file — `stx typecheck` rejected the exact snippet that
707
+ * issue was closed with.
708
+ *
709
+ * `test/signals/toast-declaration-drift.test.ts` now reads the option names out
710
+ * of the runtime and fails if this list falls behind again. Adding an option
711
+ * here without the runtime reading it fails too: a documented option that does
712
+ * nothing is the quieter half of the same problem.
713
+ */
714
+ interface StxToastOptions {
715
+ /** Auto-dismiss duration in ms. 0 = persistent. Default: 3000 */
716
+ duration?: number
717
+ /** Heading rendered above the message, in bold. */
718
+ title?: string
719
+ /**
720
+ * Semantic key. A second toast with the same id REPLACES the first in place
721
+ * rather than stacking, and `dismiss(id)` ends it — so a persistent
722
+ * "Publishing…" toast and the call that clears it need not thread the numeric
723
+ * handle between them.
724
+ */
725
+ id?: string | number
726
+ /**
727
+ * Force the dark or light palette. Omitted, it follows the document — the
728
+ * `dark` class, then `data-theme` / `data-color-mode`, then the OS setting.
729
+ */
730
+ dark?: boolean
731
+ }
732
+
733
+ interface StxToast {
734
+ /** Show a success toast (green) */
735
+ success: (message: string, options?: StxToastOptions) => number
736
+ /** Show an error toast (red) */
737
+ error: (message: string, options?: StxToastOptions) => number
738
+ /** Show an info toast (blue) */
739
+ info: (message: string, options?: StxToastOptions) => number
740
+ /** Show a warning toast (yellow) */
741
+ warning: (message: string, options?: StxToastOptions) => number
742
+ /**
743
+ * Dismiss a toast, or all of them if nothing is given.
744
+ *
745
+ * Takes the semantic `id` the toast was created with, or the numeric handle
746
+ * these methods return. The runtime branches on which it got, so a caller does
747
+ * not have to know which kind it is holding — this used to accept `number`
748
+ * only, which made replace-by-id unreachable from a typechecked call site.
749
+ */
750
+ dismiss: (id?: string | number) => void
751
+ }
752
+
753
+ declare const toast: StxToast
754
+
755
+ // ============================================================================
756
+ // Modal system
757
+ // ============================================================================
758
+
759
+ interface StxModal {
760
+ /** Open a modal by its id */
761
+ open: (id: string) => void
762
+ /** Close a modal by its id */
763
+ close: (id: string) => void
764
+ /** Toggle a modal by its id */
765
+ toggle: (id: string) => void
766
+ }
767
+
768
+ declare const modal: StxModal
769
+
770
+ // ============================================================================
771
+ // Drawer system
772
+ // ============================================================================
773
+
774
+ interface StxDrawer {
775
+ /** Open a drawer by its id */
776
+ open: (id: string) => void
777
+ /** Close a drawer by its id */
778
+ close: (id: string) => void
779
+ /** Toggle a drawer by its id */
780
+ toggle: (id: string) => void
781
+ }
782
+
783
+ declare const drawer: StxDrawer
784
+
785
+ // ============================================================================
786
+ // Alert & Confirm dialogs
787
+ // ============================================================================
788
+
789
+ interface StxDialogOptions {
790
+ /** Dialog title displayed above the message */
791
+ title?: string
792
+ /** Icon type: 'info' | 'warning' | 'error' | 'success' | 'question' */
793
+ type?: 'info' | 'warning' | 'error' | 'success' | 'question'
794
+ /** Text for the confirm/OK button (default: 'OK') */
795
+ confirmText?: string
796
+ /** Text for the cancel button — confirm only (default: 'Cancel') */
797
+ cancelText?: string
798
+ }
799
+
800
+ /** Styled replacement for window.alert(). Returns a Promise that resolves when dismissed. */
801
+ declare function stxAlert(_message: string, _options?: StxDialogOptions): Promise<void>
802
+
803
+ /** Styled replacement for window.confirm(). Returns a Promise<boolean>. */
804
+ declare function stxConfirm(_message: string, _options?: StxDialogOptions): Promise<boolean>
805
+
806
+ // ============================================================================
807
+ // Stores (Pinia-inspired, signals-based)
808
+ // ============================================================================
809
+
810
+ interface StxStorePersistOptions {
811
+ pick?: string[]
812
+ storage?: 'localStorage' | 'sessionStorage'
813
+ key?: string
814
+ }
815
+
816
+ interface StxStoreOptions {
817
+ persist?: boolean | StxStorePersistOptions
818
+ }
819
+
820
+ declare function defineStore<T>(
821
+ _id: string,
822
+ _setup: () => T,
823
+ _options?: StxStoreOptions,
824
+ ): () => T
825
+ declare function defineStore<S extends Record<string, any>, G extends Record<string, any>, A extends Record<string, any>>(
826
+ _id: string,
827
+ _options: {
828
+ state?: () => S
829
+ getters?: G
830
+ actions?: A
831
+ persist?: boolean | StxStorePersistOptions
832
+ },
833
+ ): () => S & G & A
834
+ declare function useStore<T = any>(_id: string): T
835
+ declare function registerStoresClient(_stores: Record<string, unknown>): void
836
+ // createStore / createSelector are exported by state-management.ts for server
837
+ // use and `action` has no implementation anywhere; none reaches window.stx, so
838
+ // none is declared as an ambient global (#1804).
839
+
840
+ // ============================================================================
841
+ // JSX runtime (Vue/React style)
842
+ // ============================================================================
843
+
844
+ // h / Fragment belong to the JSX runtime (jsx-runtime.ts) and are resolved by
845
+ // the transform, not destructured off window.stx — so they are not ambient
846
+ // globals (#1804). Import them explicitly if you call them directly.
847
+
848
+ // ============================================================================
849
+ // window.stx registry
850
+ // ============================================================================
851
+
852
+ interface StxRuntimeRegistry {
853
+ // Signals
854
+ state: typeof state
855
+ derived: typeof derived
856
+ effect: typeof effect
857
+ batch: typeof batch
858
+ untrack: typeof untrack
859
+ peek: typeof peek
860
+ isSignal: typeof isSignal
861
+
862
+ // Lifecycle
863
+ onMount: typeof onMount
864
+ onDestroy: typeof onDestroy
865
+
866
+ // Composables
867
+ useFetch: typeof useFetch
868
+ clearServerData: typeof clearServerData
869
+ useRef: typeof useRef
870
+ useQuery: typeof useQuery
871
+ useMutation: typeof useMutation
872
+ configureFetch: typeof configureFetch
873
+ useOptimistic: typeof useOptimistic
874
+
875
+ // Routing
876
+ navigate: typeof navigate
877
+ goBack: typeof goBack
878
+ goForward: typeof goForward
879
+ refresh: typeof refresh
880
+ invalidateRoute: typeof invalidateRoute
881
+ useRoute: typeof useRoute
882
+ setRouteParams: typeof setRouteParams
883
+ useRouteParams: typeof useRouteParams
884
+ useRouteParam: typeof useRouteParam
885
+ useSearchParams: typeof useSearchParams
886
+
887
+ // Composition
888
+ provide: typeof provide
889
+ defineProps: typeof defineProps
890
+ withDefaults: typeof withDefaults
891
+ defineEmits: typeof defineEmits
892
+ defineExpose: typeof defineExpose
893
+ defineSlots: typeof defineSlots
894
+
895
+ // Vue compat
896
+ ref: typeof ref
897
+ reactive: typeof reactive
898
+ computed: typeof computed
899
+ watch: typeof watch
900
+ watchEffect: typeof watchEffect
901
+ $computed: typeof $computed
902
+ $watch: typeof $watch
903
+
904
+ // Utilities
905
+ useDebounce: typeof useDebounce
906
+ useDebouncedValue: typeof useDebouncedValue
907
+ useThrottle: typeof useThrottle
908
+ useInterval: typeof useInterval
909
+ useTimeout: typeof useTimeout
910
+ useToggle: typeof useToggle
911
+ useCounter: typeof useCounter
912
+ useClickOutside: typeof useClickOutside
913
+ useFocus: typeof useFocus
914
+ useAsync: typeof useAsync
915
+ useModel: typeof useModel
916
+ useLocalStorage: typeof useLocalStorage
917
+ useSessionStorage: typeof useSessionStorage
918
+ useEventListener: typeof useEventListener
919
+ useScrollLock: typeof useScrollLock
920
+ useWebSocket: typeof useWebSocket
921
+ useColorMode: typeof useColorMode
922
+ useDark: typeof useDark
923
+ useHead: typeof useHead
924
+ useSeoMeta: typeof useSeoMeta
925
+
926
+ // Stores
927
+ defineStore: typeof defineStore
928
+ useStore: typeof useStore
929
+
930
+ // Toast
931
+ toast: StxToast
932
+
933
+ // Modal
934
+ modal: StxModal
935
+
936
+ // Drawer
937
+ drawer: StxDrawer
938
+
939
+ // Dialogs
940
+ alert: typeof stxAlert
941
+ confirm: typeof stxConfirm
942
+
943
+ // Mount API
944
+ mount: (setupFn: () => any) => void
945
+ mountEl: (selector: string, setupFn: () => any) => void
946
+
947
+ // Helpers (template helpers — populated by app)
948
+ helpers: Record<string, any>
949
+
950
+ // Internals (escape hatch)
951
+ [key: string]: any
952
+ }
953
+
954
+ // Bare global so existing code that references `stx` (without `window.`) still works
955
+ declare const stx: StxRuntimeRegistry
956
+
957
+ // Augment Window so `window.stx` is typed
958
+ interface Window {
959
+ stx: StxRuntimeRegistry
960
+ __STX_STORES__?: Record<string, any>
961
+ __STX_CURRENT_PROPS__?: Record<string, any>
962
+ __STX_CURRENT_ELEMENT__?: HTMLElement | null
963
+ __stxRouter?: boolean
964
+ __stxRouterConfig?: Record<string, any>
965
+ }