@askrjs/askr 0.0.9 → 0.0.10

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 (49) hide show
  1. package/dist/chunk-37RC6ZT3.js +1 -0
  2. package/dist/chunk-4RTKQ7SC.js +1 -0
  3. package/dist/chunk-534P7OMI.js +2 -0
  4. package/dist/chunk-62D2TNHX.js +1 -0
  5. package/dist/chunk-CI6AOGB5.js +1 -0
  6. package/dist/chunk-D2JSJKCW.js +1 -0
  7. package/dist/chunk-DPHQ4JD6.js +1 -0
  8. package/dist/chunk-FFSNBDHN.js +1 -0
  9. package/dist/chunk-JJDSOK6C.js +1 -0
  10. package/dist/chunk-LOSY3JCR.js +1 -0
  11. package/dist/chunk-OQMK7H2R.js +1 -0
  12. package/dist/chunk-Q7GRUZHR.js +1 -0
  13. package/dist/chunk-XKFPM4SY.js +2 -0
  14. package/dist/chunk-Y3Q3LOFT.js +1 -0
  15. package/dist/chunk-YRY4OLQF.js +1 -0
  16. package/dist/{component-DHAn9JxU.d.ts → component-mtwBWhUr.d.ts} +1 -1
  17. package/dist/for/index.d.ts +61 -0
  18. package/dist/for/index.js +1 -0
  19. package/dist/foundations/core.d.ts +449 -0
  20. package/dist/foundations/core.js +1 -0
  21. package/dist/foundations/index.d.ts +4 -722
  22. package/dist/foundations/index.js +1 -1274
  23. package/dist/foundations/structures.d.ts +223 -0
  24. package/dist/foundations/structures.js +1 -0
  25. package/dist/fx/index.js +1 -636
  26. package/dist/index.d.ts +2 -2
  27. package/dist/index.js +1 -3955
  28. package/dist/jsx-dev-runtime.js +1 -17
  29. package/dist/jsx-runtime.js +1 -23
  30. package/dist/logger-MPMIVXAP.js +1 -0
  31. package/dist/main-EPE35NMW.js +65 -0
  32. package/dist/navigate-6CC6IXXF.js +1 -0
  33. package/dist/resources/index.d.ts +1 -1
  34. package/dist/resources/index.js +1 -792
  35. package/dist/route-4QZ3RYN5.js +1 -0
  36. package/dist/router/index.d.ts +5 -0
  37. package/dist/router/index.js +1 -3194
  38. package/dist/ssr/index.js +1 -3935
  39. package/dist/vite/index.js +1 -2306
  40. package/package.json +19 -5
  41. package/dist/foundations/index.js.map +0 -1
  42. package/dist/fx/index.js.map +0 -1
  43. package/dist/index.js.map +0 -1
  44. package/dist/jsx-dev-runtime.js.map +0 -1
  45. package/dist/jsx-runtime.js.map +0 -1
  46. package/dist/resources/index.js.map +0 -1
  47. package/dist/router/index.js.map +0 -1
  48. package/dist/ssr/index.js.map +0 -1
  49. package/dist/vite/index.js.map +0 -1
@@ -0,0 +1,449 @@
1
+ import { S as State } from '../component-mtwBWhUr.js';
2
+ import '../jsx-CSWf4VFg.js';
3
+
4
+ /**
5
+ * composeHandlers
6
+ *
7
+ * Compose two event handlers into one. The first handler runs, and unless it
8
+ * calls `event.preventDefault()` (or sets `defaultPrevented`), the second
9
+ * handler runs. This prevents accidental clobbering of child handlers when
10
+ * injecting props.
11
+ *
12
+ * POLICY DECISIONS (LOCKED):
13
+ *
14
+ * 1. Execution Order
15
+ * First handler runs before second (injected before base).
16
+ * This allows injected handlers to prevent default behavior.
17
+ *
18
+ * 2. Default Prevention Check
19
+ * By default, checks `defaultPrevented` on first argument.
20
+ * Can be disabled via options.checkDefaultPrevented = false.
21
+ *
22
+ * 3. Undefined Handler Support
23
+ * Undefined handlers are skipped (no-op). This simplifies usage
24
+ * where handlers are optional.
25
+ *
26
+ * 4. Type Safety
27
+ * Args are readonly to prevent mutation. Return type matches input.
28
+ */
29
+ interface ComposeHandlersOptions {
30
+ /**
31
+ * When true (default), do not run the second handler if the first prevented default.
32
+ * When false, always run both handlers.
33
+ */
34
+ checkDefaultPrevented?: boolean;
35
+ }
36
+ declare function composeHandlers<A extends readonly unknown[]>(first?: (...args: A) => void, second?: (...args: A) => void, options?: ComposeHandlersOptions): (...args: A) => void;
37
+
38
+ declare function mergeProps<TBase extends object, TInjected extends object>(base: TBase, injected: TInjected): TInjected & TBase;
39
+
40
+ /**
41
+ * Tiny aria helpers
42
+ */
43
+ declare function ariaDisabled(disabled?: boolean): {
44
+ 'aria-disabled'?: 'true';
45
+ };
46
+ declare function ariaExpanded(expanded?: boolean): {
47
+ 'aria-expanded'?: 'true' | 'false';
48
+ };
49
+ declare function ariaSelected(selected?: boolean): {
50
+ 'aria-selected'?: 'true' | 'false';
51
+ };
52
+
53
+ /**
54
+ * Ref composition utilities
55
+ *
56
+ * POLICY DECISIONS (LOCKED):
57
+ *
58
+ * 1. Ref Types Supported
59
+ * - Callback refs: (value: T | null) => void
60
+ * - Object refs: { current: T | null }
61
+ * - null/undefined (no-op)
62
+ *
63
+ * 2. Write Failure Handling
64
+ * setRef catches write failures (readonly refs) and ignores them.
65
+ * This is intentional — refs may be readonly in some contexts.
66
+ *
67
+ * 3. Composition Order
68
+ * composeRefs applies refs in array order (left to right).
69
+ * All refs are called even if one fails.
70
+ */
71
+ type Ref<T> = ((value: T | null) => void) | {
72
+ current: T | null;
73
+ } | null | undefined;
74
+ declare function setRef<T>(ref: Ref<T>, value: T | null): void;
75
+ declare function composeRefs<T>(...refs: Array<Ref<T>>): (value: T | null) => void;
76
+
77
+ interface FormatIdOptions {
78
+ /** Defaults to 'askr' */
79
+ prefix?: string;
80
+ /** Stable, caller-provided identity */
81
+ id: string | number;
82
+ }
83
+ /**
84
+ * formatId
85
+ *
86
+ * Formats a stable ID from a caller-provided identity.
87
+ * - Pure and deterministic (no time/randomness/global counters)
88
+ * - SSR-safe
89
+ *
90
+ * POLICY DECISIONS (LOCKED):
91
+ *
92
+ * 1. No Auto-Generation
93
+ * Caller must provide the `id`. No random/sequential generation.
94
+ * This ensures determinism and SSR safety.
95
+ *
96
+ * 2. Format Convention
97
+ * IDs are formatted as `{prefix}-{id}`.
98
+ * Default prefix is "askr".
99
+ *
100
+ * 3. Type Coercion
101
+ * Numbers are coerced to strings via String().
102
+ * This is deterministic and consistent.
103
+ */
104
+ declare function formatId(options: FormatIdOptions): string;
105
+
106
+ interface DefaultPreventable {
107
+ defaultPrevented?: boolean;
108
+ preventDefault?: () => void;
109
+ }
110
+ interface PropagationStoppable {
111
+ stopPropagation?: () => void;
112
+ }
113
+ interface KeyboardLikeEvent extends DefaultPreventable, PropagationStoppable {
114
+ key: string;
115
+ }
116
+ interface PointerLikeEvent extends DefaultPreventable, PropagationStoppable {
117
+ target?: unknown;
118
+ }
119
+
120
+ /**
121
+ * pressable
122
+ *
123
+ * Interaction helper that produces VNode props for 'press' semantics.
124
+ * - Pure and deterministic: no DOM construction or mutation here
125
+ * - The runtime owns event attachment and scheduling
126
+ * - This helper returns plain props (handlers) intended to be attached by the runtime
127
+ *
128
+ * Behaviour:
129
+ * - For native buttons: only an `onClick` prop is provided (no ARIA or keyboard shims)
130
+ * - For non-button elements: add `role="button"` and `tabIndex` and keyboard handlers
131
+ * - Activation: `Enter` activates on keydown, `Space` activates on keyup (matches native button)
132
+ * - Disabled: handlers short-circuit and `aria-disabled` is set for all hosts
133
+ *
134
+ * POLICY DECISIONS (LOCKED):
135
+ *
136
+ * 1. Activation Timing (Platform Parity)
137
+ * - Enter fires on keydown (immediate response)
138
+ * - Space fires on keyup (allows cancel by moving focus, matches native)
139
+ * - Space keydown prevents scroll (matches native button behavior)
140
+ *
141
+ * 2. Disabled Enforcement Strategy
142
+ * - Native buttons: Use HTML `disabled` attribute (platform-enforced non-interactivity)
143
+ * AND `aria-disabled` (consistent a11y signaling)
144
+ * - Non-native: Use `tabIndex=-1` (removes from tab order)
145
+ * AND `aria-disabled` (signals disabled state to AT)
146
+ * - Click handler short-circuits as defense-in-depth (prevents leaked focus issues)
147
+ *
148
+ * 3. Key Repeat Behavior
149
+ * - Held Enter/Space will fire onPress repeatedly (matches native button)
150
+ * - No debouncing or repeat prevention (platform parity)
151
+ */
152
+ interface PressableOptions {
153
+ disabled?: boolean;
154
+ onPress?: (e: PressEvent) => void;
155
+ /**
156
+ * Whether the host is a native button. Defaults to false.
157
+ */
158
+ isNativeButton?: boolean;
159
+ }
160
+
161
+ type PressEvent = DefaultPreventable & PropagationStoppable;
162
+ interface PressableResult {
163
+ onClick: (e: PressEvent) => void;
164
+ disabled?: true;
165
+ role?: 'button';
166
+ tabIndex?: number;
167
+ onKeyDown?: (e: KeyboardLikeEvent) => void;
168
+ onKeyUp?: (e: KeyboardLikeEvent) => void;
169
+ 'aria-disabled'?: 'true';
170
+ }
171
+ declare function pressable({ disabled, onPress, isNativeButton, }: PressableOptions): PressableResult;
172
+
173
+ /**
174
+ * dismissable
175
+ *
176
+ * THE dismissal primitive. Handles Escape key and outside interactions.
177
+ *
178
+ * INVARIANTS:
179
+ * 1. Returns props that compose via mergeProps (no factories)
180
+ * 2. Disabled state respected exactly once, here
181
+ * 3. No side effects - pure props generation
182
+ * 4. Outside detection requires explicit node reference
183
+ * 5. This is the ONLY dismissal primitive - do not create alternatives
184
+ *
185
+ * DESIGN:
186
+ * - Returns standard event handler props (onKeyDown, onPointerDownCapture)
187
+ * - Composable via mergeProps with other foundations
188
+ * - Caller provides node reference for outside detection
189
+ * - Single onDismiss callback for all dismiss triggers
190
+ *
191
+ * PIT OF SUCCESS:
192
+ * ✓ Can't accidentally bypass (only way to get dismiss behavior)
193
+ * ✓ Can't duplicate (disabled checked once)
194
+ * ✓ Composes via mergeProps (standard props)
195
+ * ✓ Wrong usage is hard (no factories to misuse)
196
+ *
197
+ * USAGE:
198
+ * const props = dismissable({
199
+ * node: elementRef,
200
+ * disabled: false,
201
+ * onDismiss: () => close()
202
+ * });
203
+ *
204
+ * <div ref={elementRef} {...props}>Content</div>
205
+ *
206
+ * MISUSE EXAMPLE (PREVENTED):
207
+ * ❌ Can't forget to check disabled - checked inside dismissable
208
+ * ❌ Can't create custom escape handler - this is the only one
209
+ * ❌ Can't bypass via direct event listeners - mergeProps composes correctly
210
+ */
211
+ interface DismissableOptions {
212
+ /**
213
+ * Reference to the element for outside click detection
214
+ */
215
+ node?: Node | null;
216
+ /**
217
+ * Whether dismiss is disabled
218
+ */
219
+ disabled?: boolean;
220
+ /**
221
+ * Called when dismiss is triggered (Escape or outside click)
222
+ */
223
+ onDismiss?: (trigger: 'escape' | 'outside') => void;
224
+ }
225
+
226
+ declare function dismissable({ node, disabled, onDismiss }: DismissableOptions): {
227
+ onKeyDown: (e: KeyboardLikeEvent) => void;
228
+ onPointerDownCapture: (e: PointerLikeEvent) => void;
229
+ };
230
+
231
+ /**
232
+ * focusable
233
+ *
234
+ * Normalize focus-related props for hosts.
235
+ * - No DOM manipulation here; returns props that the runtime may attach.
236
+ */
237
+ interface FocusableOptions {
238
+ disabled?: boolean;
239
+ tabIndex?: number | undefined;
240
+ }
241
+ interface FocusableResult {
242
+ tabIndex?: number;
243
+ 'aria-disabled'?: 'true';
244
+ }
245
+ declare function focusable({ disabled, tabIndex, }: FocusableOptions): FocusableResult;
246
+
247
+ /**
248
+ * hoverable
249
+ *
250
+ * Produces props for pointer enter/leave handling. Pure and deterministic.
251
+ */
252
+ interface HoverableOptions {
253
+ disabled?: boolean;
254
+ onEnter?: (e: HoverEvent) => void;
255
+ onLeave?: (e: HoverEvent) => void;
256
+ }
257
+
258
+ type HoverEvent = DefaultPreventable & PropagationStoppable;
259
+ interface HoverableResult {
260
+ onPointerEnter?: (e: HoverEvent) => void;
261
+ onPointerLeave?: (e: HoverEvent) => void;
262
+ }
263
+ declare function hoverable({ disabled, onEnter, onLeave, }: HoverableOptions): HoverableResult;
264
+
265
+ /**
266
+ * rovingFocus
267
+ *
268
+ * Single tab stop navigation with arrow-key control.
269
+ *
270
+ * INVARIANTS:
271
+ * 1. Only one item in the group is reachable via Tab (single tab stop)
272
+ * 2. Arrow keys move focus within the group
273
+ * 3. Orientation determines which arrow keys are active
274
+ * 4. Looping is opt-in
275
+ * 5. Disabled items are skipped
276
+ * 6. Returns props objects, never factories (composes via mergeProps)
277
+ *
278
+ * DESIGN:
279
+ * - Container gets onKeyDown for arrow navigation
280
+ * - Each item gets tabIndex based on current selection
281
+ * - Navigation logic is pure - caller controls focus application
282
+ * - Disabled check happens per-item via predicate
283
+ *
284
+ * PIT OF SUCCESS:
285
+ * ✓ Can't accidentally break tab order (tabIndex assigned correctly)
286
+ * ✓ Can't duplicate navigation logic (single source)
287
+ * ✓ Composes via mergeProps (all standard props)
288
+ * ✓ Type-safe - invalid indices caught at call site
289
+ *
290
+ * USAGE:
291
+ * const nav = rovingFocus({
292
+ * currentIndex: 0,
293
+ * itemCount: 3,
294
+ * orientation: 'horizontal',
295
+ * onNavigate: setIndex
296
+ * });
297
+ *
298
+ * <div {...nav.container}>
299
+ * <button {...nav.item(0)}>First</button>
300
+ * <button {...nav.item(1)}>Second</button>
301
+ * </div>
302
+ *
303
+ * MISUSE EXAMPLE (PREVENTED):
304
+ * ❌ Can't forget to set tabIndex - returned in item props
305
+ * ❌ Can't create conflicting arrow handlers - mergeProps composes
306
+ * ❌ Can't skip disabled items incorrectly - logic is internal
307
+ */
308
+
309
+ type Orientation = 'horizontal' | 'vertical' | 'both';
310
+ interface RovingFocusOptions {
311
+ /**
312
+ * Current focused index
313
+ */
314
+ currentIndex: number;
315
+ /**
316
+ * Total number of items
317
+ */
318
+ itemCount: number;
319
+ /**
320
+ * Navigation orientation
321
+ * - horizontal: ArrowLeft/ArrowRight
322
+ * - vertical: ArrowUp/ArrowDown
323
+ * - both: all arrow keys
324
+ */
325
+ orientation?: Orientation;
326
+ /**
327
+ * Whether to loop when reaching the end
328
+ */
329
+ loop?: boolean;
330
+ /**
331
+ * Callback when navigation occurs
332
+ */
333
+ onNavigate?: (index: number) => void;
334
+ /**
335
+ * Optional disabled state check per index
336
+ */
337
+ isDisabled?: (index: number) => boolean;
338
+ }
339
+ interface RovingFocusResult {
340
+ /**
341
+ * Props for the container element (composes via mergeProps)
342
+ */
343
+ container: {
344
+ onKeyDown: (e: KeyboardLikeEvent) => void;
345
+ };
346
+ /**
347
+ * Generate props for an item at the given index (composes via mergeProps)
348
+ */
349
+ item: (index: number) => {
350
+ tabIndex: number;
351
+ 'data-roving-index': number;
352
+ };
353
+ }
354
+ declare function rovingFocus(options: RovingFocusOptions): RovingFocusResult;
355
+
356
+ interface InteractionPolicyInput {
357
+ /** Whether the host element is a native interactive element (button, a, etc) */
358
+ isNative: boolean;
359
+ /** Disabled state - checked ONLY here, never in components */
360
+ disabled: boolean;
361
+ /** User-provided press handler - semantic action, not DOM event */
362
+ onPress?: (e: Event) => void;
363
+ /** Optional ref to compose */
364
+ ref?: Ref<unknown>;
365
+ }
366
+ /**
367
+ * THE interaction policy. Components MUST use this, NEVER implement
368
+ * interaction logic directly.
369
+ */
370
+ declare function applyInteractionPolicy({ isNative, disabled, onPress, ref, }: InteractionPolicyInput): {
371
+ disabled: true | undefined;
372
+ onClick: (e: Event) => void;
373
+ ref: Ref<unknown>;
374
+ } | {
375
+ 'aria-disabled': true | undefined;
376
+ tabIndex: number;
377
+ ref: Ref<unknown>;
378
+ onClick: (e: DefaultPreventable & PropagationStoppable) => void;
379
+ disabled?: true;
380
+ role?: "button";
381
+ onKeyDown?: (e: KeyboardLikeEvent) => void;
382
+ onKeyUp?: (e: KeyboardLikeEvent) => void;
383
+ };
384
+ /**
385
+ * Merge rule for Slot / asChild
386
+ *
387
+ * Precedence:
388
+ * policy → user → child
389
+ *
390
+ * Event handlers are composed (policy first).
391
+ * Refs are always composed.
392
+ * Policy props MUST take precedence to enforce invariants.
393
+ */
394
+ declare function mergeInteractionProps(childProps: Record<string, unknown>, policyProps: Record<string, unknown>, userProps?: Record<string, unknown>): Record<string, unknown>;
395
+
396
+ /**
397
+ * controllable
398
+ *
399
+ * Small utilities for controlled vs uncontrolled components. These helpers are
400
+ * intentionally minimal and do not manage state themselves; they help component
401
+ * implementations make correct decisions about when to call `onChange` vs
402
+ * update internal state.
403
+ *
404
+ * POLICY DECISIONS (LOCKED):
405
+ *
406
+ * 1. Controlled Detection
407
+ * A value is "controlled" if it is not `undefined`.
408
+ * This matches React conventions and is SSR-safe.
409
+ *
410
+ * 2. onChange Timing
411
+ * - Controlled mode: onChange called immediately, no internal update
412
+ * - Uncontrolled mode: internal state updated first, then onChange called
413
+ * This ensures onChange sees the new value in both modes.
414
+ *
415
+ * 3. Value Equality
416
+ * controllableState uses Object.is() to prevent unnecessary onChange calls.
417
+ * This is intentional — strict equality, no deep comparison.
418
+ */
419
+
420
+ declare function isControlled<T>(value: T | undefined): value is T;
421
+ declare function resolveControllable<T>(value: T | undefined, defaultValue: T): {
422
+ value: T;
423
+ isControlled: boolean;
424
+ };
425
+ declare function makeControllable<T>(options: {
426
+ value: T | undefined;
427
+ defaultValue: T;
428
+ onChange?: (next: T) => void;
429
+ setInternal?: (next: T) => void;
430
+ }): {
431
+ set: (next: T) => void;
432
+ isControlled: boolean;
433
+ };
434
+ type ControllableState<T> = State<T> & {
435
+ isControlled: boolean;
436
+ };
437
+ /**
438
+ * controllableState
439
+ *
440
+ * Hook-like primitive that mirrors `state()` semantics while supporting
441
+ * controlled/uncontrolled behavior.
442
+ */
443
+ declare function controllableState<T>(options: {
444
+ value: T | undefined;
445
+ defaultValue: T;
446
+ onChange?: (next: T) => void;
447
+ }): ControllableState<T>;
448
+
449
+ export { type ComposeHandlersOptions, type ControllableState, type DismissableOptions, type FocusableOptions, type FocusableResult, type FormatIdOptions, type HoverableOptions, type HoverableResult, type InteractionPolicyInput, type Orientation, type PressableOptions, type PressableResult, type Ref, type RovingFocusOptions, type RovingFocusResult, applyInteractionPolicy, ariaDisabled, ariaExpanded, ariaSelected, composeHandlers, composeRefs, controllableState, dismissable, focusable, formatId, hoverable, isControlled, makeControllable, mergeInteractionProps, mergeProps, pressable, resolveControllable, rovingFocus, setRef };
@@ -0,0 +1 @@
1
+ export{i as controllableState,b as dismissable,c as focusable,a as formatId,d as hoverable,f as isControlled,h as makeControllable,g as resolveControllable,e as rovingFocus}from'../chunk-Q7GRUZHR.js';import'../chunk-LOSY3JCR.js';export{i as applyInteractionPolicy,c as ariaDisabled,d as ariaExpanded,e as ariaSelected,a as composeHandlers,g as composeRefs,j as mergeInteractionProps,b as mergeProps,h as pressable,f as setRef}from'../chunk-DPHQ4JD6.js';import'../chunk-534P7OMI.js';import'../chunk-JJDSOK6C.js';import'../chunk-OQMK7H2R.js';import'../chunk-37RC6ZT3.js';import'../chunk-62D2TNHX.js';