@adukiorg/anza 0.2.0 → 0.2.3

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 (83) hide show
  1. package/CHANGELOG.md +90 -4
  2. package/README.md +97 -133
  3. package/bin/anza/anza-linux-arm64 +0 -0
  4. package/bin/anza/anza-linux-x64 +0 -0
  5. package/bin/anza/anza-macos-arm64 +0 -0
  6. package/bin/anza/anza-macos-x64 +0 -0
  7. package/bin/anza/anza-windows-x64.exe +0 -0
  8. package/bin/anza/find.js +35 -0
  9. package/bin/anza/index.js +34 -0
  10. package/bin/anza/launch.js +19 -0
  11. package/bin/common/index.js +7 -0
  12. package/bin/common/logs.js +62 -0
  13. package/bin/create/copy.js +18 -0
  14. package/bin/create/index.js +45 -0
  15. package/bin/create/run.js +210 -0
  16. package/bin/create/write.js +19 -0
  17. package/importmap.json +4 -0
  18. package/package.json +16 -10
  19. package/src/core/offline/{usage.md → notes/usage.md} +11 -1
  20. package/src/core/router/boot.js +82 -0
  21. package/src/core/router/cascade.js +76 -0
  22. package/src/core/router/container.js +63 -72
  23. package/src/core/router/graph.js +144 -0
  24. package/src/core/router/index.js +12 -2
  25. package/src/core/router/intercept.js +26 -7
  26. package/src/core/router/lca.js +58 -0
  27. package/src/core/router/match.js +49 -36
  28. package/src/core/router/notes/audit-old.md +887 -0
  29. package/src/core/router/notes/audti.md +773 -0
  30. package/src/core/router/notes/tasks.md +473 -0
  31. package/src/core/router/{usage.md → notes/usage.md} +57 -35
  32. package/src/core/router/sync/tab.js +6 -4
  33. package/src/core/router/transitions.js +35 -8
  34. package/src/core/router/trie.js +130 -0
  35. package/src/core/security/{usage.md → notes/usage.md} +1 -2
  36. package/src/core/storage/{usage.md → notes/usage.md} +6 -6
  37. package/src/core/theme/index.js +78 -0
  38. package/src/core/ui/define/index.js +2 -1
  39. package/src/core/ui/define/orchestrator.js +10 -4
  40. package/src/core/ui/defs/dock.js +134 -0
  41. package/src/core/ui/defs/index.js +20 -0
  42. package/src/core/ui/defs/page.js +89 -0
  43. package/src/core/ui/defs/part.js +28 -0
  44. package/src/core/ui/defs/spec.js +96 -0
  45. package/src/core/ui/defs/view.js +23 -0
  46. package/src/core/ui/index.js +16 -3
  47. package/src/core/ui/notes/definations.md +979 -0
  48. package/src/tokens/index.css +1 -0
  49. package/src/tokens/semantic/contrast.css +18 -0
  50. package/src/tokens/semantic/transitions.css +32 -0
  51. package/types/core/platform/index.d.ts +39 -10
  52. package/types/core/router/index.d.ts +9 -0
  53. package/types/core/theme/index.d.ts +18 -0
  54. package/types/core/ui/index.d.ts +11 -0
  55. package/types/index.d.ts +1 -0
  56. package/bin/anza.js +0 -63
  57. package/bin/create.js +0 -150
  58. package/src/core/api/plan.md +0 -209
  59. package/src/core/events/missing.md +0 -103
  60. package/src/core/events/plan.md +0 -177
  61. package/src/core/offline/missing.md +0 -89
  62. package/src/core/offline/plan.md +0 -143
  63. package/src/core/platform/missing.md +0 -119
  64. package/src/core/platform/platform.d.ts +0 -88
  65. package/src/core/router/missing.md +0 -716
  66. package/src/core/router/outlet.js +0 -139
  67. package/src/core/router/plan.md +0 -370
  68. package/src/core/security/missing.md +0 -97
  69. package/src/core/state/missing.md +0 -165
  70. package/src/core/storage/missing.md +0 -165
  71. package/src/core/storage/plan.md +0 -69
  72. package/src/core/ui/implementation.md +0 -170
  73. package/src/core/ui/plan.md +0 -510
  74. package/src/core/ui/ui.types.md +0 -890
  75. /package/src/core/animations/{usage.md → notes/usage.md} +0 -0
  76. /package/src/core/api/{usage.md → notes/usage.md} +0 -0
  77. /package/src/core/events/{usage.md → notes/usage.md} +0 -0
  78. /package/src/core/platform/{usage.md → notes/usage.md} +0 -0
  79. /package/src/core/state/{usage.md → notes/usage.md} +0 -0
  80. /package/src/core/ui/{usage.md → notes/usage.md} +0 -0
  81. /package/src/core/ui/{watch.md → notes/watch.md} +0 -0
  82. /package/src/core/workers/{plan.md → notes/plan.md} +0 -0
  83. /package/src/core/workers/{usage.md → notes/usage.md} +0 -0
@@ -1,890 +0,0 @@
1
- # Native UI Type System Plan
2
-
3
- This document specifies the TypeScript declaration surface for `@adukiorg/anza/ui`. The goal is strict IDE enforcement for component authors using plain JavaScript with JSDoc or TypeScript consumers importing the package declarations.
4
-
5
- The current runtime source of truth is:
6
-
7
- - `src/core/ui/index.js`
8
- - `src/core/ui/base.js`
9
- - `src/core/ui/schedule.js`
10
- - `src/core/ui/template.js`
11
- - `src/core/ui/transitions.js`
12
- - `src/core/ui/observe.js`
13
- - `src/core/ui/define/define.js`
14
- - `src/core/ui/define/element.js`
15
- - `src/core/ui/define/container.js`
16
- - `src/core/ui/define/proxy.js`
17
-
18
- The implemented declaration file is:
19
-
20
- - `types/core/ui/index.d.ts`
21
-
22
- Optional generated component-specific files:
23
-
24
- - `index.tags.d.ts`
25
- - generated `dist/types/**/*.d.ts`
26
-
27
- ## 1. Goals
28
-
29
- - Make `ui.element(...)` and `ui.container(...)` strongly typed.
30
- - Infer component property types from `props`.
31
- - Type `mount`, `update`, and `unmount` lifecycle contexts.
32
- - Type injected `refs`, `tags`, `on`, and `watch`.
33
- - Type `ElementInternals` availability for form-associated elements.
34
- - Type scheduler, observer, template, and transition utilities according to the actual runtime.
35
- - Type generated refs from `.tags.json` or optional `index.tags.d.ts`.
36
- - Catch misspelled prop names, invalid update value types, invalid ref names where possible, and incorrect handler signatures.
37
- - Preserve ergonomic JavaScript usage through JSDoc-compatible exported types.
38
-
39
- ## 2. Non-Goals
40
-
41
- - No full CSS selector parser in TypeScript. Selector strings remain `string`.
42
- - No compile-time verification that a selector exists in the HTML template unless generated template typings are imported.
43
- - No static guarantee that an arbitrary runtime `tags.one(selector)` is non-null.
44
- - No framework JSX runtime. JSX custom element typing can be added separately.
45
- - No type-level HTML parser inside declarations.
46
-
47
- ## 3. Public Exports
48
-
49
- `@adukiorg/anza/ui` must export:
50
-
51
- ```typescript
52
- export class BaseElement extends HTMLElement {}
53
-
54
- export function define<TagName extends string>(
55
- tagName: TagName,
56
- elementClass: CustomElementConstructor
57
- ): void;
58
-
59
- export function element<
60
- TagName extends string,
61
- Props extends PropsDefinition = {},
62
- Refs extends RefsMap = RefsMap,
63
- Form extends boolean = false
64
- >(
65
- tagName: TagName,
66
- spec: ElementSpec<Props, Refs, Form>,
67
- base?: string | URL
68
- ): void;
69
-
70
- export function container<
71
- TagName extends string,
72
- Props extends PropsDefinition = {},
73
- Refs extends RefsMap = RefsMap
74
- >(
75
- tagName: TagName,
76
- spec: ContainerSpec<Props, Refs>,
77
- base?: string | URL
78
- ): void;
79
-
80
- export function schedule<T>(
81
- fn: () => T | Promise<T>,
82
- priority?: TaskPriority
83
- ): Promise<T>;
84
-
85
- export function scheduleFrame<T>(fn: () => T): Promise<T>;
86
-
87
- export function yieldTask(): Promise<void>;
88
-
89
- export function transition<T>(fn: () => T): Promise<ViewTransitionLike<T>>;
90
-
91
- export function template(
92
- strings: TemplateStringsArray,
93
- ...values: unknown[]
94
- ): DocumentFragment;
95
-
96
- export const observe: ObserveApi;
97
-
98
- export const ui: UiApi;
99
- ```
100
-
101
- ## 4. Primitive Type Aliases
102
-
103
- ```typescript
104
- export type TaskPriority = 'user-blocking' | 'user-visible' | 'background';
105
-
106
- export type Constructor<T = HTMLElement> = new (...args: any[]) => T;
107
-
108
- export type MaybePromise<T> = T | Promise<T>;
109
-
110
- export type Disposer = () => void;
111
-
112
- export type RefsMap = Record<string, Element>;
113
-
114
- export type ElementRefs<T> = {
115
- [K in keyof T]: Element;
116
- };
117
-
118
- export type RefElement = Element;
119
-
120
- export type SignalOrOptions<T extends object = {}> =
121
- | AbortSignal
122
- | (T & { signal?: AbortSignal; once?: boolean });
123
- ```
124
-
125
- Rationale:
126
-
127
- - `Disposer` is returned by `on.*` and `watch.*`.
128
- - `SignalOrOptions` lets the runtime support both `ctrl.signal` and `{ signal, once }`.
129
- - `RefsMap` defaults to permissive `Record<string, Element>`, but generated template typings can replace it with exact refs.
130
-
131
- ## 5. Prop Definitions
132
-
133
- Runtime props use constructors in config objects:
134
-
135
- ```javascript
136
- props: {
137
- disabled: { type: Boolean, default: false, state: true },
138
- count: { type: Number, default: 0 },
139
- label: { type: String, default: 'Untitled' }
140
- }
141
- ```
142
-
143
- Declaration:
144
-
145
- ```typescript
146
- export type PropConstructor = BooleanConstructor | NumberConstructor | StringConstructor;
147
-
148
- export interface PropConfig<T extends PropConstructor = PropConstructor> {
149
- type: T;
150
- default?: PropValueFromConstructor<T>;
151
- state?: boolean;
152
- reflect?: boolean;
153
- }
154
-
155
- export type AnyPropConfig =
156
- | PropConfig<BooleanConstructor>
157
- | PropConfig<NumberConstructor>
158
- | PropConfig<StringConstructor>;
159
-
160
- export type PropsDefinition = Record<string, AnyPropConfig>;
161
-
162
- export type PropValueFromConstructor<T> =
163
- T extends BooleanConstructor ? boolean :
164
- T extends NumberConstructor ? number :
165
- T extends StringConstructor ? string :
166
- never;
167
-
168
- export type InferProps<Props extends PropsDefinition> = {
169
- [K in keyof Props]: PropValueFromConstructor<Props[K]['type']>;
170
- };
171
- ```
172
-
173
- Strictness requirements:
174
-
175
- - `default` must match `type`.
176
- - `update({ name, val, prev })` must narrow by prop key.
177
- - `el` in lifecycle methods must expose declared props.
178
-
179
- Example expected IDE behavior:
180
-
181
- ```typescript
182
- ui.element('ui-count', {
183
- props: {
184
- count: { type: Number, default: 0 },
185
- open: { type: Boolean, default: false }
186
- },
187
- update(ctx) {
188
- if (ctx.name === 'count') {
189
- ctx.val.toFixed(); // ok, number
190
- ctx.val.trim(); // error
191
- }
192
- }
193
- });
194
- ```
195
-
196
- ## 6. Component Host Type
197
-
198
- The lifecycle `el` should be the custom element host plus inferred props.
199
-
200
- ```typescript
201
- export type ComponentHost<Props extends PropsDefinition> =
202
- HTMLElement & InferProps<Props>;
203
- ```
204
-
205
- For form-associated elements:
206
-
207
- ```typescript
208
- export type InternalsFor<Form extends boolean> =
209
- Form extends true ? ElementInternals : ElementInternals | undefined;
210
- ```
211
-
212
- This means `internals` is strongly present when `form: true` is statically declared.
213
-
214
- ## 7. Lifecycle Contexts
215
-
216
- ```typescript
217
- export interface BaseLifecycleContext<
218
- Props extends PropsDefinition,
219
- Refs extends ElementRefs<Refs>,
220
- Form extends boolean = false
221
- > {
222
- el: ComponentHost<Props>;
223
- ctrl: AbortController;
224
- tags: TagsApi;
225
- on: EventDelegator;
226
- refs: Readonly<Refs>;
227
- watch: WatchApi;
228
- internals: InternalsFor<Form>;
229
- }
230
-
231
- export type MountContext<
232
- Props extends PropsDefinition,
233
- Refs extends RefsMap,
234
- Form extends boolean = false
235
- > = BaseLifecycleContext<Props, Refs, Form>;
236
-
237
- export type UnmountContext<
238
- Props extends PropsDefinition,
239
- Refs extends RefsMap,
240
- Form extends boolean = false
241
- > = Pick<
242
- BaseLifecycleContext<Props, Refs, Form>,
243
- 'el' | 'tags' | 'refs' | 'watch' | 'internals'
244
- >;
245
- ```
246
-
247
- ## 8. Strict Update Context
248
-
249
- Update must be a discriminated union by prop key:
250
-
251
- ```typescript
252
- export type UpdateContext<
253
- Props extends PropsDefinition,
254
- Refs extends RefsMap,
255
- Form extends boolean = false
256
- > = {
257
- [K in keyof Props & string]:
258
- BaseLifecycleContext<Props, Refs, Form> & {
259
- name: K;
260
- val: PropValueFromConstructor<Props[K]['type']>;
261
- prev: PropValueFromConstructor<Props[K]['type']>;
262
- old: PropValueFromConstructor<Props[K]['type']>;
263
- }
264
- }[keyof Props & string];
265
- ```
266
-
267
- This gives IDE narrowing:
268
-
269
- ```typescript
270
- update(ctx) {
271
- switch (ctx.name) {
272
- case 'disabled':
273
- ctx.val.valueOf(); // boolean
274
- break;
275
- case 'count':
276
- ctx.val.toFixed(); // number
277
- break;
278
- }
279
- }
280
- ```
281
-
282
- ## 9. Element Spec
283
-
284
- ```typescript
285
- export interface ElementSpec<
286
- Props extends PropsDefinition = {},
287
- Refs extends RefsMap = RefsMap,
288
- Form extends boolean = false
289
- > {
290
- template?: string;
291
- style?: string;
292
- mode?: ShadowRootMode;
293
- props?: Props;
294
- form?: Form;
295
- url?: string;
296
- container?: string;
297
- meta?: Record<string, unknown>;
298
-
299
- mount?: (
300
- context: MountContext<Props, Refs, Form>
301
- ) => void | Promise<void>;
302
-
303
- update?: (
304
- context: UpdateContext<Props, Refs, Form>
305
- ) => void | Promise<void>;
306
-
307
- unmount?: (
308
- context: UnmountContext<Props, Refs, Form>
309
- ) => void;
310
-
311
- methods?: Record<string, (...args: any[]) => any>;
312
- }
313
- ```
314
-
315
- Strictness details:
316
-
317
- - `props` is optional and defaults to `{}`.
318
- - `mount` and `update` may be async, but errors are not swallowed by the type system.
319
- - `form: true` narrows `internals` to `ElementInternals`.
320
- - `Refs` can be supplied manually or generated. Exact ref interfaces do not need a string index signature; every declared ref value only needs to extend `Element`.
321
-
322
- Manual refs typing:
323
-
324
- ```typescript
325
- interface ButtonRefs {
326
- button: HTMLButtonElement;
327
- status: HTMLSpanElement;
328
- }
329
-
330
- ui.element<'ui-button', typeof props, ButtonRefs>('ui-button', {
331
- props,
332
- mount({ refs }) {
333
- refs.button.disabled = true; // ok
334
- refs.missing; // error
335
- }
336
- });
337
- ```
338
-
339
- ## 10. Container Spec
340
-
341
- Containers share the element spec but have router layout behavior.
342
-
343
- ```typescript
344
- export type ContainerHost<Props extends PropsDefinition> =
345
- ComponentHost<Props> & {
346
- swapView(newElement: Element, options?: SwapViewOptions): Promise<void>;
347
- };
348
-
349
- export interface SwapViewOptions {
350
- direction?: 'push' | 'pop' | 'replace' | string;
351
- params?: Record<string, string>;
352
- }
353
-
354
- export type ContainerMountContext<
355
- Props extends PropsDefinition,
356
- Refs extends RefsMap
357
- > = Omit<MountContext<Props, Refs, false>, 'el'> & {
358
- el: ContainerHost<Props>;
359
- };
360
-
361
- export interface ContainerSpec<
362
- Props extends PropsDefinition = {},
363
- Refs extends RefsMap = RefsMap
364
- > extends Omit<ElementSpec<Props, Refs, false>, 'mount' | 'update' | 'unmount' | 'form'> {
365
- mount?: (context: ContainerMountContext<Props, Refs>) => void | Promise<void>;
366
- update?: (context: UpdateContext<Props, Refs, false>) => void | Promise<void>;
367
- unmount?: (context: UnmountContext<Props, Refs, false>) => void;
368
- }
369
- ```
370
-
371
- ## 11. `tags` API
372
-
373
- ```typescript
374
- export interface TagsApi {
375
- one<E extends Element = Element>(selector: string): E | null;
376
- all<E extends Element = Element>(selector: string): E[];
377
- each<E extends Element = Element>(
378
- selector: string,
379
- fn: (element: E, index: number) => void
380
- ): void;
381
- has(selector: string): boolean;
382
- clear(): void;
383
- }
384
- ```
385
-
386
- Usage:
387
-
388
- ```typescript
389
- const button = tags.one<HTMLButtonElement>('button');
390
- button?.disabled = true;
391
-
392
- const inputs = tags.all<HTMLInputElement>('input');
393
- ```
394
-
395
- Why generic selectors:
396
-
397
- - TypeScript cannot reliably infer element type from arbitrary CSS selectors.
398
- - Explicit generic arguments are simple and honest.
399
- - Generated refs should be preferred for strongly typed stable elements.
400
-
401
- ## 12. `refs` Typing
402
-
403
- Default:
404
-
405
- ```typescript
406
- refs: Readonly<Record<string, Element>>;
407
- ```
408
-
409
- Strict generated form:
410
-
411
- ```typescript
412
- export interface TemplateRefs {
413
- button: HTMLButtonElement;
414
- status: HTMLSpanElement;
415
- }
416
- ```
417
-
418
- Generated `index.tags.d.ts` should export:
419
-
420
- ```typescript
421
- export interface TemplateRefs {
422
- readonly button: HTMLButtonElement;
423
- readonly status: HTMLSpanElement;
424
- }
425
-
426
- export type TemplateRefName = keyof TemplateRefs;
427
- ```
428
-
429
- Component author usage:
430
-
431
- ```typescript
432
- import type { TemplateRefs } from './index.tags';
433
-
434
- ui.element<'ui-button', typeof props, TemplateRefs>('ui-button', {
435
- props,
436
- mount({ refs }) {
437
- refs.button.disabled = false;
438
- }
439
- }, import.meta.url);
440
- ```
441
-
442
- ## 13. `on` Event Delegator
443
-
444
- Runtime usage:
445
-
446
- ```javascript
447
- on.click('button', handler)
448
- on.click('button', handler, ctrl.signal)
449
- on.click('button', handler, { signal: ctrl.signal, once: true })
450
- on.click.once('button', handler)
451
- on['nav:change']('[data-tab]', handler)
452
- ```
453
-
454
- Types:
455
-
456
- ```typescript
457
- export interface DelegatedEventOptions extends AddEventListenerOptions {
458
- signal?: AbortSignal;
459
- once?: boolean;
460
- }
461
-
462
- export type DelegatedEventHandler<
463
- EventType extends Event = Event,
464
- Target extends Element = Element
465
- > = (event: EventType, target: Target) => void;
466
-
467
- export interface DelegatedEventBinder<EventType extends Event = Event> {
468
- <Target extends Element = Element>(
469
- selector: string,
470
- handler: DelegatedEventHandler<EventType, Target>,
471
- options?: AbortSignal | DelegatedEventOptions
472
- ): Disposer;
473
-
474
- once<Target extends Element = Element>(
475
- selector: string,
476
- handler: DelegatedEventHandler<EventType, Target>,
477
- options?: AbortSignal | Omit<DelegatedEventOptions, 'once'>
478
- ): Disposer;
479
- }
480
-
481
- export type EventDelegator = {
482
- [K in keyof GlobalEventHandlersEventMap]: DelegatedEventBinder<GlobalEventHandlersEventMap[K]>;
483
- } & {
484
- [customEvent: string]: DelegatedEventBinder<Event>;
485
- };
486
- ```
487
-
488
- Usage:
489
-
490
- ```typescript
491
- on.click<HTMLButtonElement>('button', (event, button) => {
492
- event.clientX; // MouseEvent
493
- button.disabled; // HTMLButtonElement
494
- });
495
-
496
- on.input<HTMLInputElement>('input', (event, input) => {
497
- input.value;
498
- });
499
- ```
500
-
501
- ## 14. `watch` API
502
-
503
- ```typescript
504
- export interface WatchOptions {
505
- signal?: AbortSignal;
506
- once?: boolean;
507
- }
508
-
509
- export type WatchTarget<T extends Element = Element> = string | T;
510
-
511
- export type AttrWatchHandler<T extends Element = Element> = (
512
- attrName: string,
513
- newValue: string | null,
514
- oldValue: string | null,
515
- element: T
516
- ) => void;
517
-
518
- export type KidsWatchHandler<T extends Element = Element> = (
519
- change: {
520
- added: Node[];
521
- removed: Node[];
522
- },
523
- element: T
524
- ) => void;
525
-
526
- export type TextWatchHandler<T extends Element = Element> = (
527
- newText: string,
528
- oldText: string | null,
529
- element: T | null
530
- ) => void;
531
-
532
- export type TreeWatchHandler<T extends Element = Element> = (
533
- records: MutationRecord[],
534
- element: T | null
535
- ) => void;
536
-
537
- export interface WatchMethod<Args extends any[]> {
538
- (...args: Args): Disposer;
539
- once(...args: Args): Disposer;
540
- }
541
- ```
542
-
543
- Concrete API:
544
-
545
- ```typescript
546
- export interface WatchApi {
547
- attr: {
548
- <T extends Element = Element>(
549
- target: WatchTarget<T>,
550
- attr: string | readonly string[] | '*',
551
- handler: AttrWatchHandler<T>,
552
- options?: AbortSignal | WatchOptions
553
- ): Disposer;
554
-
555
- once<T extends Element = Element>(
556
- target: WatchTarget<T>,
557
- attr: string | readonly string[] | '*',
558
- handler: AttrWatchHandler<T>,
559
- options?: AbortSignal | Omit<WatchOptions, 'once'>
560
- ): Disposer;
561
- };
562
-
563
- kids: {
564
- <T extends Element = Element>(
565
- target: WatchTarget<T>,
566
- handler: KidsWatchHandler<T>,
567
- options?: AbortSignal | WatchOptions
568
- ): Disposer;
569
-
570
- <T extends Element = Element>(
571
- target: WatchTarget<T>,
572
- config: { deep?: boolean },
573
- handler: KidsWatchHandler<T>,
574
- options?: AbortSignal | WatchOptions
575
- ): Disposer;
576
-
577
- once<T extends Element = Element>(
578
- target: WatchTarget<T>,
579
- handler: KidsWatchHandler<T>,
580
- options?: AbortSignal | Omit<WatchOptions, 'once'>
581
- ): Disposer;
582
-
583
- once<T extends Element = Element>(
584
- target: WatchTarget<T>,
585
- config: { deep?: boolean },
586
- handler: KidsWatchHandler<T>,
587
- options?: AbortSignal | Omit<WatchOptions, 'once'>
588
- ): Disposer;
589
- };
590
-
591
- text: {
592
- <T extends Element = Element>(
593
- target: WatchTarget<T>,
594
- handler: TextWatchHandler<T>,
595
- options?: AbortSignal | WatchOptions
596
- ): Disposer;
597
-
598
- once<T extends Element = Element>(
599
- target: WatchTarget<T>,
600
- handler: TextWatchHandler<T>,
601
- options?: AbortSignal | Omit<WatchOptions, 'once'>
602
- ): Disposer;
603
- };
604
-
605
- tree: {
606
- <T extends Element = Element>(
607
- target: WatchTarget<T>,
608
- handler: TreeWatchHandler<T>,
609
- options?: AbortSignal | WatchOptions
610
- ): Disposer;
611
-
612
- once<T extends Element = Element>(
613
- target: WatchTarget<T>,
614
- handler: TreeWatchHandler<T>,
615
- options?: AbortSignal | Omit<WatchOptions, 'once'>
616
- ): Disposer;
617
- };
618
- }
619
- ```
620
-
621
- ## 15. Observer API
622
-
623
- Runtime signatures from `observe.js`:
624
-
625
- ```typescript
626
- export interface ObserveApi {
627
- resize(
628
- el: Element,
629
- fn: (entries: ResizeObserverEntry[]) => void,
630
- signal?: AbortSignal
631
- ): Disposer;
632
-
633
- intersection(
634
- el: Element,
635
- fn: (entries: IntersectionObserverEntry[]) => void,
636
- signal?: AbortSignal,
637
- options?: IntersectionObserverInit
638
- ): Disposer;
639
-
640
- mutation(
641
- el: Node,
642
- fn: (mutations: MutationRecord[]) => void,
643
- signal?: AbortSignal,
644
- options?: MutationObserverInit
645
- ): Disposer;
646
-
647
- performance(
648
- types: string[],
649
- fn: (list: PerformanceObserverEntryList) => void,
650
- signal?: AbortSignal,
651
- options?: PerformanceObserverInit
652
- ): Disposer;
653
- }
654
- ```
655
-
656
- Important correction from old declarations:
657
-
658
- - `resize` callback receives `ResizeObserverEntry[]`, not one entry.
659
- - The exported method is `intersection`, not `intersect`.
660
- - `mutation` accepts `signal` before `options`.
661
-
662
- ## 16. Scheduler API
663
-
664
- Runtime signatures:
665
-
666
- ```typescript
667
- export function schedule<T>(
668
- fn: () => T | Promise<T>,
669
- priority?: TaskPriority
670
- ): Promise<T>;
671
-
672
- export function scheduleFrame<T>(fn: () => T): Promise<T>;
673
-
674
- export function yieldTask(): Promise<void>;
675
- ```
676
-
677
- `ui.yield` should be typed as `typeof yieldTask`.
678
-
679
- ## 17. Transition API
680
-
681
- Runtime `transition` accepts a callback, not keyframes:
682
-
683
- ```typescript
684
- export interface ViewTransitionLike<T = unknown> {
685
- finished: Promise<T>;
686
- updateCallbackDone: Promise<T>;
687
- ready: Promise<unknown>;
688
- skipTransition(): void;
689
- }
690
-
691
- export function transition<T>(
692
- fn: () => T
693
- ): Promise<ViewTransitionLike<T>>;
694
- ```
695
-
696
- Why not use the built-in `ViewTransition` type only:
697
-
698
- - Some environments may not include the newest DOM lib.
699
- - The runtime returns a compatible fallback object when unsupported or reduced-motion is active.
700
-
701
- ## 18. Template API
702
-
703
- Runtime `template` returns a cloned `DocumentFragment`:
704
-
705
- ```typescript
706
- export function template(
707
- strings: TemplateStringsArray,
708
- ...values: unknown[]
709
- ): DocumentFragment;
710
- ```
711
-
712
- Important correction from old declarations:
713
-
714
- - It does not return `HTMLTemplateElement`.
715
- - Interpolated values are currently ignored by runtime implementation, so `unknown[]` is safer than implying string interpolation.
716
-
717
- ## 19. `ui` Object Type
718
-
719
- ```typescript
720
- export interface UiApi {
721
- define: typeof define;
722
- element: typeof element;
723
- container: typeof container;
724
- schedule: typeof schedule;
725
- scheduleFrame: typeof scheduleFrame;
726
- yield: typeof yieldTask;
727
- transition: typeof transition;
728
- template: typeof template;
729
- observe: ObserveApi;
730
- }
731
-
732
- export const ui: UiApi;
733
- ```
734
-
735
- ## 20. Generated Type Strategy
736
-
737
- The Rust scanner should eventually generate `index.tags.d.ts` alongside `index.tags.json`.
738
-
739
- Input:
740
-
741
- ```html
742
- <button ref="button" id="button"></button>
743
- <span ref="status"></span>
744
- <input ref="email" type="email" />
745
- ```
746
-
747
- Output:
748
-
749
- ```typescript
750
- /* Auto-generated by anza. Do not edit. */
751
-
752
- export interface TemplateRefs {
753
- readonly button: HTMLButtonElement;
754
- readonly email: HTMLInputElement;
755
- readonly status: HTMLSpanElement;
756
- }
757
-
758
- export type TemplateRefName = keyof TemplateRefs;
759
- ```
760
-
761
- Type inference source:
762
-
763
- - `button` -> `HTMLButtonElement`
764
- - `input` -> `HTMLInputElement`
765
- - `form` -> `HTMLFormElement`
766
- - Unknown custom tags -> `HTMLElement`
767
-
768
- ## 21. JSDoc Consumer Pattern
769
-
770
- Plain JavaScript component files can opt into strict IDE types:
771
-
772
- ```javascript
773
- // @ts-check
774
- import { ui } from '@adukiorg/anza/ui';
775
-
776
- /** @type {const} */
777
- const props = {
778
- disabled: { type: Boolean, default: false },
779
- count: { type: Number, default: 0 }
780
- };
781
-
782
- /** @typedef {import('./index.tags').TemplateRefs} TemplateRefs */
783
-
784
- ui.element('ui-counter', {
785
- props,
786
-
787
- /** @param {import('@adukiorg/anza/ui').MountContext<typeof props, TemplateRefs>} ctx */
788
- mount({ refs, on }) {
789
- refs.button.disabled = false;
790
- on.click('button', (_event, button) => {
791
- button.disabled = true;
792
- });
793
- },
794
-
795
- /** @param {import('@adukiorg/anza/ui').UpdateContext<typeof props, TemplateRefs>} ctx */
796
- update(ctx) {
797
- if (ctx.name === 'count') {
798
- ctx.val.toFixed();
799
- }
800
- }
801
- }, import.meta.url);
802
- ```
803
-
804
- ## 22. Type Test Cases
805
-
806
- Use `tsd`, `vitest` with `expectTypeOf`, or a `tsc --noEmit` fixture.
807
-
808
- Required pass cases:
809
-
810
- ```typescript
811
- ui.element('ui-a', {
812
- props: {
813
- count: { type: Number, default: 0 },
814
- open: { type: Boolean, default: false },
815
- label: { type: String, default: '' }
816
- },
817
- mount({ el }) {
818
- el.count.toFixed();
819
- el.open.valueOf();
820
- el.label.trim();
821
- }
822
- });
823
- ```
824
-
825
- Required fail cases:
826
-
827
- ```typescript
828
- ui.element('ui-bad', {
829
- props: {
830
- count: { type: Number, default: 'wrong' } // error
831
- }
832
- });
833
-
834
- ui.element('ui-bad-update', {
835
- props: {
836
- count: { type: Number, default: 0 }
837
- },
838
- update(ctx) {
839
- if (ctx.name === 'count') {
840
- ctx.val.trim(); // error
841
- }
842
- }
843
- });
844
- ```
845
-
846
- ## 23. Strict `tsconfig` Recommendation
847
-
848
- For validating declarations:
849
-
850
- ```json
851
- {
852
- "compilerOptions": {
853
- "target": "ES2022",
854
- "module": "ESNext",
855
- "moduleResolution": "Bundler",
856
- "lib": ["ES2022", "DOM", "DOM.Iterable"],
857
- "strict": true,
858
- "noImplicitAny": true,
859
- "noUncheckedIndexedAccess": true,
860
- "exactOptionalPropertyTypes": true,
861
- "checkJs": true,
862
- "allowJs": true,
863
- "noEmit": true,
864
- "skipLibCheck": false
865
- }
866
- }
867
- ```
868
-
869
- ## 24. Implementation Order
870
-
871
- 1. Replace `types/core/ui/index.d.ts` with declarations matching this document.
872
- 2. Export all public utility types from that declaration file.
873
- 3. Add a `typesVersions` or package `types` entry if package consumers do not already resolve `types/index.d.ts`.
874
- 4. Add type tests for `ui.element`, props, refs, `on`, `watch`, observe, transition, and template.
875
- 5. Extend Rust scanner to emit `index.tags.d.ts`.
876
- 6. Update `usage.md` with a short "Typing Components" section linking this file.
877
-
878
- ## 25. Known Declaration Corrections Needed
879
-
880
- The previous `types/core/ui/index.d.ts` was corrected:
881
-
882
- - Remove stale `BaseElement.$`, `BaseElement.$$`, and `emit` unless the runtime implements them.
883
- - Add `element` and `container`.
884
- - Correct `transition` from animation keyframes to callback-based View Transition wrapper.
885
- - Correct `template` return type to `DocumentFragment`.
886
- - Correct `observe.resize` callback to entry arrays.
887
- - Correct `observe.intersection` name.
888
- - Add `observe.performance`.
889
- - Add `TagsApi`, `EventDelegator`, `WatchApi`, lifecycle contexts, prop inference, and `ElementSpec`.
890
- - Add `ui.observe.performance`, `ui.element`, and `ui.container`.