@yoyaflow/yoya-ui 0.3.2 → 0.3.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yoyaflow/yoya-ui",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "A browser-native JS UI library with declarative HTML authoring: component library, router, i18n, theming and layout, works with or without a build tool, SSR-ready.",
5
5
  "type": "module",
6
6
  "private": false,
@@ -18,6 +18,10 @@
18
18
  "types": "./types/yoya.core.d.ts",
19
19
  "import": "./dist/yoya.core.js"
20
20
  },
21
+ "./devtools": {
22
+ "types": "./types/yoya.devtools.d.ts",
23
+ "import": "./dist/yoya.devtools.js"
24
+ },
21
25
  "./echart": {
22
26
  "types": "./types/yoya.echart.d.ts",
23
27
  "import": "./dist/yoya.echart.js"
@@ -81,12 +85,20 @@
81
85
  "node": "^20.19.0 || ^22.13.0 || >=24.0.0"
82
86
  },
83
87
  "devDependencies": {
88
+ "@codemirror/lang-javascript": "^6.2.5",
89
+ "@codemirror/state": "^6.7.4",
90
+ "@codemirror/theme-one-dark": "^6.1.3",
84
91
  "@eslint/js": "^10.0.1",
85
92
  "@preact/signals-core": "^1.14.4",
93
+ "@toast-ui/editor": "^3.2.2",
94
+ "ag-grid-community": "^36.1.0",
95
+ "codemirror": "^6.0.2",
86
96
  "eslint": "^10.8.1",
87
97
  "globals": "^17.11.0",
88
98
  "jsdom": "^29.1.1",
99
+ "leaflet": "^1.9.4",
89
100
  "prettier": "^3.9.6",
101
+ "quill": "^2.0.3",
90
102
  "typescript": "^7.0.2",
91
103
  "vite": "^8.2.0",
92
104
  "vitest": "^4.1.10"
package/types/core.d.ts CHANGED
@@ -58,6 +58,7 @@ export interface ElementOptions {
58
58
  attrs?: Record<string, AttrValue>;
59
59
  style?: StyleInput;
60
60
  children?: ChildInput;
61
+ access?: AccessSpec;
61
62
  [key: `on${string}`]: EventHandler | undefined;
62
63
  [key: string]: unknown;
63
64
  }
@@ -83,6 +84,114 @@ export type StateType = 'boolean' | 'string' | 'number' | null | undefined;
83
84
  /** State handler invoked when a registered state changes. */
84
85
  export type StateHandler<N = ViewNode> = (value: unknown, node: N, oldValue: unknown) => void;
85
86
 
87
+ // ---------------------------------------------------------------------------
88
+ // Access control
89
+ // ---------------------------------------------------------------------------
90
+
91
+ export type AccessLevel = 'read' | 'write';
92
+
93
+ /** Permission code on a node. Components pass bare resource codes, e.g. "system:member"; read/write level is decided by the user's grants. */
94
+ export type AccessSpec = string;
95
+
96
+ /** Options accepted by createAccess(). */
97
+ export interface AccessOptions {
98
+ /** Granted permission strings, e.g. ["r.system:member", "w.system:member"]. */
99
+ permissions?: string[];
100
+ /** Roles held by the current request, e.g. ["admin"]. */
101
+ roles?: string[];
102
+ /** Roles that bypass every check (default ["super_admin"]). */
103
+ superAdmins?: string[];
104
+ }
105
+
106
+ /** Per-request permission context: decided by withAccess scope. */
107
+ export interface AccessContext {
108
+ roles(): string[];
109
+ permissions(): string[];
110
+ isSuper(): boolean;
111
+ /** True when any grant (bare / r. / w.) covers the code. */
112
+ has(spec: AccessSpec): boolean;
113
+ /** True when a read grant (bare / r. / w.) covers the code. */
114
+ canRead(spec: AccessSpec): boolean;
115
+ /** True when a write grant (bare or w.) covers the code. */
116
+ canWrite(spec: AccessSpec): boolean;
117
+ setPermissions(permissions: string[]): AccessContext;
118
+ subscribe(listener: () => void): () => void;
119
+ }
120
+
121
+ /** Creates a per-request access context. */
122
+ export function createAccess(options?: AccessOptions): AccessContext;
123
+
124
+ /** Normalizes an access spec into { code, level }. */
125
+ export function parseAccessSpec(
126
+ spec: AccessSpec | { code: string; level?: AccessLevel }
127
+ ): { code: string; level: AccessLevel } | null;
128
+
129
+ /** Returns the resource code of a spec, without the read/write prefix. */
130
+ export function stripAccessCode(spec: AccessSpec): string;
131
+
132
+ /** Runs build() with the access context active, then restores the outer one. */
133
+ export function withAccess<T>(access: AccessContext | null, build: () => T): T;
134
+
135
+ /** Returns the access context active in the current render scope. */
136
+ export function currentAccess(): AccessContext | null;
137
+
138
+ /** Installs a global access context (single-user SPA); overridden by SSR options.access or withAccess scope. */
139
+ export function installAccess(access: AccessContext | null): AccessContext | null;
140
+
141
+ // ---
142
+ // Generic scoped context
143
+ // ---
144
+
145
+ /** Per-render provider map injected by withContext / SSR options.context. */
146
+ export type ContextProviders = Record<string, unknown>;
147
+
148
+ /** Runs build() with the providers active, then restores the outer scope. */
149
+ export function withContext<T>(providers: ContextProviders | null | undefined, build: () => T): T;
150
+
151
+ /** Installs a global fallback context (single-user SPA). */
152
+ export function installContext(
153
+ providers: ContextProviders | null | undefined
154
+ ): ContextProviders | null;
155
+
156
+ /** Removes the globally installed fallback context. */
157
+ export function clearInstalledContext(): null;
158
+
159
+ /** Returns the nearest value for a key; falls back to installed context and defaultValue. */
160
+ export function currentContext<T = unknown>(key: string, defaultValue?: T): T | undefined;
161
+
162
+ /** Returns a shallow merged snapshot of the active context. */
163
+ export function snapshotContext(): ContextProviders;
164
+
165
+ // ---
166
+ // Accessibility primitives
167
+ // ---
168
+
169
+ export interface FocusTrapOptions {
170
+ onEscape?: (event: Event) => void;
171
+ restoreFocus?: boolean;
172
+ }
173
+
174
+ export interface FocusTrapHandle {
175
+ activate(): void;
176
+ destroy(): void;
177
+ }
178
+
179
+ export function getFocusableElements(root: Element | null | undefined): Element[];
180
+ export function createFocusTrap(
181
+ root: Element | null | undefined,
182
+ options?: FocusTrapOptions
183
+ ): FocusTrapHandle;
184
+ export function announce(
185
+ message: string,
186
+ options?: { politeness?: 'polite' | 'assertive'; dedupeKey?: string }
187
+ ): HTMLElement | null;
188
+ export function moveByKey(options: {
189
+ key: string;
190
+ items: readonly unknown[];
191
+ currentIndex?: number;
192
+ shiftKey?: boolean;
193
+ }): number;
194
+
86
195
  // ---------------------------------------------------------------------------
87
196
  // View tree nodes
88
197
  // ---------------------------------------------------------------------------
@@ -109,6 +218,9 @@ export class ViewNode {
109
218
  /** Adds a text child. */
110
219
  text(content: string | number): this;
111
220
 
221
+ /** Declares access control: bare default read, "w.xxx" write, "r.xxx" read. */
222
+ access(spec: AccessSpec): this;
223
+
112
224
  /** Registers an event listener, bound immediately or at render time. */
113
225
  on(eventName: string, handler: EventHandler, options?: EventOptions): this;
114
226
 
@@ -362,6 +474,12 @@ export class I18n {
362
474
  /** Registers or merges a dictionary for one language. */
363
475
  register(language: string, messages?: Record<string, unknown>): this;
364
476
 
477
+ /** Lazily registers a language; returns a promise resolved after merge and notify. */
478
+ registerLocale(
479
+ name: string,
480
+ loader: () => Record<string, unknown> | Promise<Record<string, unknown>>
481
+ ): Promise<this>;
482
+
365
483
  /** Registers one or more corpora: multi-language files or { language, messages }. */
366
484
  registerMessages(corpus?: Record<string, unknown> | Array<unknown>): this;
367
485
 
@@ -559,7 +677,10 @@ export function bindWindowEvent(
559
677
  ): () => void;
560
678
 
561
679
  /** Injects a <style> into <head>; dataAttribute is used for dedup and identification. */
562
- export function injectDocumentStyle(styleText: string, dataAttribute?: string | null): HTMLStyleElement | null;
680
+ export function injectDocumentStyle(
681
+ styleText: string,
682
+ dataAttribute?: string | null
683
+ ): HTMLStyleElement | null;
563
684
 
564
685
  /** Initializes theme mode/name from explicit values or persisted storage. */
565
686
  export function initYoyaTheme(options?: InitYoyaThemeOptions): {
@@ -344,7 +344,9 @@ export interface TreeRanger {
344
344
  [key: string]: any;
345
345
  }
346
346
 
347
- export function vTreeRanger(setup?: TreeRangerColumn[] | TreeRangerOptions | SetupCallback<TreeRanger>): TreeRanger;
347
+ export function vTreeRanger(
348
+ setup?: TreeRangerColumn[] | TreeRangerOptions | SetupCallback<TreeRanger>
349
+ ): TreeRanger;
348
350
  export const treeRanger: typeof vTreeRanger;
349
351
  export function vTreeRangerColumn(setup?: unknown): unknown;
350
352
 
@@ -518,6 +520,39 @@ export const vProgress: ElementFactory<VProgress>;
518
520
  export const vRingStat: ElementFactory<VRingStat>;
519
521
  export const vScroll: ElementFactory<VScroll>;
520
522
  export const vSparkline: ElementFactory<VSparkline>;
523
+ export interface TreeTableColumn {
524
+ key?: string;
525
+ title?: ChildInput;
526
+ dataIndex?: string;
527
+ sorter?: boolean;
528
+ filterOptions?: Array<{ label: ChildInput; value: string | number }>;
529
+ render?: (value: unknown, row: Record<string, unknown>, index: number) => ChildInput;
530
+ editable?: boolean;
531
+ fixed?: 'left' | 'right' | string;
532
+ validate?: (value: unknown) => string | null | undefined;
533
+ width?: string | number;
534
+ [key: string]: any;
535
+ }
536
+ /** Tree table: flat rows with indent, expand/collapse, selection linkage, lazy load. */
537
+ export class VTreeTable extends HtmlElementNode {
538
+ columns(value?: Array<string | number | TreeTableColumn>): this;
539
+ nodes(value?: Array<Record<string, any>>): this;
540
+ rowKey(handler?: (node: Record<string, unknown>, index: number) => string | number): this;
541
+ rowSelection(value?: boolean): boolean | VTreeTable;
542
+ lazyLoad(
543
+ handler?: (
544
+ node: Record<string, unknown>
545
+ ) => Array<Record<string, unknown>> | Promise<Array<Record<string, unknown>>>
546
+ ): VTreeTable;
547
+ expandedKeys(value?: Array<string>): Array<string> | VTreeTable;
548
+ expandKeys(value?: Array<string>): VTreeTable;
549
+ checkedKeys(value?: Array<string>): Array<string> | VTreeTable;
550
+ expandAll(): VTreeTable;
551
+ collapseAll(): VTreeTable;
552
+ visibleRowCount(): number;
553
+ }
554
+
555
+ export const vTreeTable: ElementFactory<VTreeTable>;
521
556
  export const vTable: ElementFactory<VTable>;
522
557
  export const vTbody: ElementFactory<VTbody>;
523
558
  export const vTd: ElementFactory<VTd>;
@@ -556,7 +591,10 @@ export class VImagePreview extends HtmlElementNode {
556
591
  }
557
592
 
558
593
  export const vImagePreview: ElementFactory<VImagePreview> & {
559
- (first?: SetupInput<VImagePreview> | null, callback?: SetupCallback<VImagePreview>): VImagePreview;
594
+ (
595
+ first?: SetupInput<VImagePreview> | null,
596
+ callback?: SetupCallback<VImagePreview>
597
+ ): VImagePreview;
560
598
  };
561
599
 
562
600
  /** Parent-shortcut surface merged onto HtmlElementNode. */
@@ -601,6 +639,10 @@ export interface DataDisplayParentShortcuts {
601
639
  first?: SetupInput<VSparkline> | null,
602
640
  callback?: SetupCallback<VSparkline>
603
641
  ): VSparkline;
642
+ vTreeTable(
643
+ first?: SetupInput<VTreeTable> | null,
644
+ callback?: SetupCallback<VTreeTable>
645
+ ): VTreeTable;
604
646
  vTable(first?: SetupInput<VTable> | null, callback?: SetupCallback<VTable>): VTable;
605
647
  vTimeline(first?: SetupInput<VTimeline> | null, callback?: SetupCallback<VTimeline>): VTimeline;
606
648
  vTimelineItem(
@@ -0,0 +1,156 @@
1
+ /**
2
+ * yoya-ui devtools type declarations.
3
+ *
4
+ * The devtools runtime ships as a separate entry so the production main
5
+ * bundle never contains the debug surface. All hooks are opt-in and are
6
+ * no-ops until `enableDevtools()` is called.
7
+ */
8
+
9
+ import type { ViewNode } from './core.js';
10
+
11
+ /** Base shape shared by every devtools lifecycle event. */
12
+ export interface DevtoolsEventBase {
13
+ /** Event kind: 'commit' | 'destroy' (extended by update/state kinds). */
14
+ type: string;
15
+ /** The view node the event belongs to, when one exists. */
16
+ node?: unknown;
17
+ /** Monotonically increasing event sequence across the stream. */
18
+ seq: number;
19
+ /** Stable id of the node the event belongs to. */
20
+ nodeId?: number;
21
+ /** Human-readable node description (tag/classes/text/component). */
22
+ nodeLabel?: string;
23
+ }
24
+
25
+ /** Emitted when an element is first rendered into the DOM. */
26
+ export interface DevtoolsCommitEvent extends DevtoolsEventBase {
27
+ type: 'commit';
28
+ kind: 'mount' | 'update';
29
+ }
30
+
31
+ /** Emitted when a node is destroyed. */
32
+ export interface DevtoolsDestroyEvent extends DevtoolsEventBase {
33
+ type: 'destroy';
34
+ }
35
+
36
+ /** Emitted when an attribute (including the mirrored class) changes. */
37
+ export interface DevtoolsAttrEvent extends DevtoolsEventBase {
38
+ type: 'attr';
39
+ name: string;
40
+ previous?: DevtoolsAttrValue;
41
+ next?: DevtoolsAttrValue;
42
+ }
43
+
44
+ /** Emitted when an inline style property changes. */
45
+ export interface DevtoolsStyleEvent extends DevtoolsEventBase {
46
+ type: 'style';
47
+ name: string;
48
+ previous?: string;
49
+ next?: string;
50
+ }
51
+
52
+ /** Emitted when a child is added, removed, or reordered. */
53
+ export interface DevtoolsChildEvent extends DevtoolsEventBase {
54
+ type: 'child';
55
+ added?: number[];
56
+ removed?: number[];
57
+ reordered?: boolean;
58
+ }
59
+
60
+ /** Emitted when a mounted text node's content changes. */
61
+ export interface DevtoolsTextEvent extends DevtoolsEventBase {
62
+ type: 'text';
63
+ from?: string;
64
+ to: string;
65
+ }
66
+
67
+ /** Emitted when a vStateNode state change is applied. */
68
+ export interface DevtoolsStateEvent extends DevtoolsEventBase {
69
+ type: 'state';
70
+ /** Per-key before/after values for the changed state entries. */
71
+ changed: Record<string, { from: unknown; to: unknown }>;
72
+ /** Full state snapshot after the change was applied. */
73
+ state: Record<string, unknown>;
74
+ /** How the change was applied: update | bindings | rebuild | pending. */
75
+ handling: 'update' | 'bindings' | 'rebuild' | 'pending' | 'none';
76
+ }
77
+
78
+ /** Lifecycle events currently emitted by the devtools hook. */
79
+ export type DevtoolsEvent =
80
+ | DevtoolsCommitEvent
81
+ | DevtoolsDestroyEvent
82
+ | DevtoolsAttrEvent
83
+ | DevtoolsStyleEvent
84
+ | DevtoolsChildEvent
85
+ | DevtoolsTextEvent
86
+ | DevtoolsStateEvent;
87
+
88
+ /** Listener callback for the devtools event stream. */
89
+ export type DevtoolsListener = (event: DevtoolsEvent) => void;
90
+
91
+ /** Turns the devtools event stream on. Returns the new enabled state. */
92
+ export function enableDevtools(): boolean;
93
+
94
+ /** Turns the devtools event stream off. Returns the new enabled state. */
95
+ export function disableDevtools(): boolean;
96
+
97
+ /** Whether the devtools event stream is currently enabled. */
98
+ export function isDevtoolsEnabled(): boolean;
99
+
100
+ /**
101
+ * Subscribes to lifecycle events emitted while devtools is enabled.
102
+ * Returns an unsubscribe function; listener errors never break rendering.
103
+ */
104
+ export function subscribeDevtools(listener: DevtoolsListener): () => void;
105
+
106
+ /** Attribute values reflected from the node's attribute map. */
107
+ export type DevtoolsAttrValue = string | number | boolean;
108
+
109
+ /** Plain-shape snapshot of a node subtree for inspection. */
110
+ export interface DevtoolsSnapshot {
111
+ /** Node kind: element | text | component | view | root. */
112
+ kind: string;
113
+ /** Stable per-node identifier assigned while devtools is active. */
114
+ id?: number;
115
+ /** Element tag name for element nodes. */
116
+ tagName?: string;
117
+ /** Element attribute map, including the mirrored class attribute. */
118
+ attrs?: Record<string, DevtoolsAttrValue>;
119
+ /** Text content for text nodes. */
120
+ text?: string;
121
+ /** Child snapshots. */
122
+ children: DevtoolsSnapshot[];
123
+ }
124
+
125
+ /** Returns a plain-shape snapshot of a node subtree. */
126
+ export function getDevtoolsSnapshot(root: ViewNode | null): DevtoolsSnapshot;
127
+
128
+ /**
129
+ * Returns the rendered DOM node for a snapshot id (element or text node),
130
+ * or null when the node is not rendered or has been destroyed.
131
+ */
132
+ export function getDevtoolsDom(id: number): Element | Text | null;
133
+
134
+ /** Declared access spec on a node. */
135
+ export interface DevtoolsAccessDetail {
136
+ code: string;
137
+ level: 'write' | 'read';
138
+ }
139
+
140
+ /** Scope details for a snapshot id (access/context/i18n). */
141
+ export interface DevtoolsScope {
142
+ id: number;
143
+ access: DevtoolsAccessDetail | null;
144
+ /** Effective permission result for the node, when available. */
145
+ permissionState?: 'active' | 'readonly' | 'hidden';
146
+ /** Context captured while the node was built, when devtools was on. */
147
+ context: Record<string, unknown> | null;
148
+ /** i18n instance detail for translated text nodes. */
149
+ i18n: { key?: string; language: string } | null;
150
+ }
151
+
152
+ /**
153
+ * Returns scope details for a snapshot id, or null when the node has been
154
+ * destroyed (or the id is unknown).
155
+ */
156
+ export function getDevtoolsScope(id: number): DevtoolsScope | null;
@@ -93,6 +93,19 @@ export const toast: {
93
93
  clear(): VMessageContainer;
94
94
  };
95
95
 
96
+ export interface ConfirmOptions {
97
+ title?: ChildInput;
98
+ content?: ChildInput;
99
+ confirmText?: ChildInput;
100
+ cancelText?: ChildInput;
101
+ danger?: boolean;
102
+ onConfirm?: () => boolean | void | Promise<boolean>;
103
+ onCancel?: () => void;
104
+ }
105
+
106
+ /** 命令式确认弹窗:resolves true on confirm, false on cancel / danger. */
107
+ export function vConfirm(options?: ConfirmOptions): Promise<boolean>;
108
+
96
109
  export const vDialog: ElementFactory<VDialog>;
97
110
  export const vMessage: ElementFactory<VMessage>;
98
111
  export const vMessageContainer: ElementFactory<VMessageContainer>;
package/types/form.d.ts CHANGED
@@ -74,7 +74,7 @@ export class VSvgIconPicker extends HtmlElementNode {
74
74
  disabled(): boolean;
75
75
  disabled(next: boolean): VSvgIconPicker;
76
76
  name(): string;
77
- name(next: string): VSvgIconPicker;
77
+ name(next: string): this;
78
78
  required(): boolean;
79
79
  required(next: boolean): VSvgIconPicker;
80
80
  icons(): string[];
package/types/ssr.d.ts CHANGED
@@ -1,4 +1,11 @@
1
- import type { ComponentLike, I18n, ViewNode } from './core.js';
1
+ import type {
2
+ AccessContext,
3
+ ComponentLike,
4
+ ContextProviders,
5
+ ElementNode,
6
+ I18n,
7
+ ViewNode
8
+ } from './core.js';
2
9
  import type { HtmlElementNode } from './html.js';
3
10
 
4
11
  export type PageFactory<S = unknown> = (state: S) => ViewNode | ComponentLike | PageFactory<S>;
@@ -12,7 +19,18 @@ export interface SsrI18nOption<S = unknown> {
12
19
  i18n?: I18n | ((state: S | null) => I18n);
13
20
  }
14
21
 
15
- export interface RenderOptions<S = unknown> extends SsrI18nOption<S> {
22
+ /** Optional access binding: an AccessContext, or a factory resolved per build. */
23
+ export interface SsrAccessOption {
24
+ access?: AccessContext | (() => AccessContext | null) | null;
25
+ }
26
+
27
+ /** Optional generic context binding: a provider map, or a factory resolved per build. */
28
+ export interface SsrContextOption {
29
+ context?: ContextProviders | ((state: unknown) => ContextProviders);
30
+ }
31
+
32
+ export interface RenderOptions<S = unknown>
33
+ extends SsrI18nOption<S>, SsrAccessOption, SsrContextOption {
16
34
  maxNodes?: number;
17
35
  state?: S | null;
18
36
  }
@@ -70,7 +88,7 @@ export function mount<S = unknown>(
70
88
  component: ViewNode | ComponentLike | PageFactory<S>,
71
89
  target: string | ParentNode,
72
90
  state?: S | null,
73
- options?: SsrI18nOption<S>
91
+ options?: SsrI18nOption<S> & SsrAccessOption & SsrContextOption
74
92
  ): ViewNode;
75
93
 
76
94
  /** Hydrates server-rendered DOM: adopts elements and binds pending events. */
@@ -78,11 +96,11 @@ export function hydrate<S = unknown>(
78
96
  component: ViewNode | ComponentLike | PageFactory<S>,
79
97
  target: string | ParentNode,
80
98
  state?: S | null,
81
- options?: SsrI18nOption<S>
99
+ options?: SsrI18nOption<S> & SsrAccessOption & SsrContextOption
82
100
  ): ViewNode;
83
101
 
84
102
  /** Document builder node used by renderPage: head/body callbacks build the page. */
85
- export class PageDocumentNode extends HtmlElementNode {
103
+ export class PageDocumentNode extends ElementNode {
86
104
  head(callback: (node: HtmlElementNode, state: unknown) => void): PageDocumentNode;
87
105
  body(callback: (node: HtmlElementNode, state: unknown) => void): PageDocumentNode;
88
106
  vBody(...args: unknown[]): PageDocumentNode;
@@ -92,7 +110,8 @@ export interface RenderPageConfig<S = unknown> {
92
110
  page: (page: PageDocumentNode, state: S | null) => void;
93
111
  }
94
112
 
95
- export interface RenderPageOptions<S = unknown> extends SsrI18nOption<S> {
113
+ export interface RenderPageOptions<S = unknown>
114
+ extends SsrI18nOption<S>, SsrAccessOption, SsrContextOption {
96
115
  /** Per-request dictionary; builds an I18n instance from state.lang. */
97
116
  messages?: Record<string, any>;
98
117
  maxNodes?: number;
@@ -111,7 +130,8 @@ export function renderPage<S = unknown>(
111
130
  options?: RenderPageOptions<S>
112
131
  ): string;
113
132
 
114
- export interface HydrateOrMountOptions<S = unknown> extends SsrI18nOption<S> {
133
+ export interface HydrateOrMountOptions<S = unknown>
134
+ extends SsrI18nOption<S>, SsrAccessOption, SsrContextOption {
115
135
  messages?: Record<string, any>;
116
136
  stateId?: string;
117
137
  target?: string | ParentNode;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Entry types for `yoya-ui/devtools`: opt-in dev lifecycle event stream and
3
+ * view-tree snapshot helpers.
4
+ */
5
+ export * from './devtools.js';