@lark.js/mvc 0.0.2 → 0.0.4

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/dist/index.d.cts CHANGED
@@ -1,14 +1,382 @@
1
+ /**
2
+ * Base View class.
3
+ * Views are created via View.extend() and mounted by Frame.
4
+ */
5
+ declare class View implements ViewInterface {
6
+ /** View ID (same as owner frame ID) */
7
+ id: string;
8
+ /** Owner frame */
9
+ owner: FrameInterface | number;
10
+ /** Updater instance */
11
+ updater: UpdaterInterface;
12
+ /** Signature: > 0 means active, incremented on render, 0 = destroyed */
13
+ signature: number;
14
+ /** Whether rendered at least once */
15
+ rendered?: boolean;
16
+ /** Whether view has template */
17
+ template?: (data: unknown, viewId: string, refData: unknown, ...rest: unknown[]) => string | string;
18
+ /** Location observation config */
19
+ locationObserved: ViewLocationObserved;
20
+ /** Observed state keys */
21
+ observedStateKeys?: string[];
22
+ /** Resource map */
23
+ resources: Record<string, ViewResourceEntry>;
24
+ /** Assign method reference */
25
+ assignMethod?: AnyFunc;
26
+ /** Whether endUpdate pending */
27
+ endUpdatePending?: number;
28
+ /** Internal event storage */
29
+ private _events;
30
+ /**
31
+ * Event bitmask map: eventType -> bitmask (1=root, 2=selector).
32
+ * Read from prototype ($evtObjMap) set by View.prepare.
33
+ * Using a getter avoids ES6 class field shadowing the prototype value.
34
+ */
35
+ get eventObjectMap(): Record<string, number>;
36
+ /**
37
+ * Selector event map: eventType -> selector list.
38
+ * Read from prototype ($selMap) set by View.prepare.
39
+ */
40
+ get eventSelectorMap(): Record<string, ViewEventSelectorEntry>;
41
+ /**
42
+ * Global event list: [{handler, element, eventName, modifiers}].
43
+ * Read from prototype ($globalEvtList) set by View.prepare.
44
+ */
45
+ get globalEventList(): ViewGlobalEventEntry[];
46
+ /**
47
+ * Initialize view (called by Frame when mounting).
48
+ */
49
+ init(): void;
50
+ /**
51
+ * Render view template (called by Frame after init).
52
+ * Wrapped by View.wrapMethod to manage signature + resources.
53
+ */
54
+ render(): void;
55
+ on(event: string, handler: AnyFunc): this;
56
+ off(event: string, handler?: AnyFunc): this;
57
+ fire(event: string, data?: Record<string, unknown>, remove?: boolean, lastToFirst?: boolean): this;
58
+ /**
59
+ * Notify view that HTML update is about to begin.
60
+ * Unmounts child frames in the update zone.
61
+ */
62
+ beginUpdate(id?: string): void;
63
+ /**
64
+ * Notify view that HTML update has ended.
65
+ * Mounts child frames in the update zone and runs deferred invokes.
66
+ */
67
+ endUpdate(id?: string, inner?: boolean): void;
68
+ /**
69
+ * Wrap an async callback to check view signature before executing.
70
+ * If the view has been re-rendered or destroyed, the callback is skipped.
71
+ */
72
+ wrapAsync<Fn extends AnyFunc>(fn: Fn, context?: unknown): (...args: Parameters<Fn>) => ReturnType<Fn> | undefined;
73
+ /**
74
+ * Observe location parameters or path changes.
75
+ * When observed keys change, render() is called automatically.
76
+ */
77
+ observeLocation(params: string | string[] | Record<string, unknown>, observePath?: boolean): void;
78
+ /**
79
+ * Observe State data keys for changes.
80
+ * When observed keys change via State.digest(), render() is called.
81
+ */
82
+ observeState(observedKeys: string | string[]): void;
83
+ /**
84
+ * Capture (register) a resource under a key.
85
+ * If a resource already exists at that key, it's destroyed first.
86
+ * When destroyOnRender=true, the resource is destroyed on next render call.
87
+ */
88
+ capture(key: string, resource?: unknown, destroyOnRender?: boolean): unknown;
89
+ /**
90
+ * Release a captured resource.
91
+ * If destroy=true, calls the resource's destroy() method.
92
+ */
93
+ release(key: string, destroy?: boolean): unknown;
94
+ /**
95
+ * Set up a leave confirmation for route changes and page unload.
96
+ */
97
+ leaveTip(message: string, condition: () => boolean): void;
98
+ /** Collected ctors from mixins */
99
+ static ctors?: AnyFunc[];
100
+ /**
101
+ * Prepare a View subclass by scanning its prototype for event method patterns.
102
+ * Pattern: `$?name<eventType1,eventType2>(&modifiers)`
103
+ *
104
+ * Only runs once per View subclass (guarded by ctors marker).
105
+ * Called from Frame.mountView before creating the view instance.
106
+ */
107
+ static prepare(oView: typeof View): AnyFunc[];
108
+ /**
109
+ * Bind or unbind event delegation for a view instance.
110
+ * Called from Frame during mount/unmount.
111
+ */
112
+ static delegateEvents(view: ViewInterface, destroy?: boolean): void;
113
+ /**
114
+ * Destroy all resources managed by a view.
115
+ * If lastly=true, destroy ALL resources; otherwise only destroyOnRender ones.
116
+ */
117
+ static destroyAllResources(view: ViewInterface, lastly: boolean): void;
118
+ /**
119
+ * Process deferred invoke calls on a frame.
120
+ */
121
+ static runInvokes(frame: FrameInterface): void;
122
+ /**
123
+ * Wrap a method on the prototype to add signature checking and resource cleanup.
124
+ */
125
+ private static wrapMethod;
126
+ /**
127
+ * When two mixins define the same event method, merge them into
128
+ * a single function that calls both in sequence.
129
+ */
130
+ private static processMixinsSameEvent;
131
+ /**
132
+ * Merge an array of mixin objects into the view prototype.
133
+ */
134
+ private static mergeMixins;
135
+ /**
136
+ * Destroy a single resource entry.
137
+ */
138
+ private static destroyResource;
139
+ /**
140
+ * Extend View to create a new View subclass.
141
+ *
142
+ * Supports:
143
+ * - props.make: constructor-like init (called with initParams + {node, deep})
144
+ * - props.mixins: array of mixin objects
145
+ * - Event method patterns: `'name<click>'` etc.
146
+ */
147
+ static extend(props?: ThisType<ViewInterface> & Record<string, unknown>, statics?: Record<string, unknown>): typeof View;
148
+ /**
149
+ * Merge mixins into View prototype.
150
+ */
151
+ static merge(this: typeof View, ...mixins: Record<string, unknown>[]): typeof View;
152
+ }
153
+
154
+ /**
155
+ * Multi-cast event emitter class.
156
+ *
157
+ * @example
158
+ * const emitter = new EventEmitter();
159
+ * emitter.on('change', (data) => console.log(data));
160
+ * emitter.fire('change', { key: 'value' });
161
+ */
162
+ declare class EventEmitter<T = unknown> implements EventEmitterInterface<T> {
163
+ /** Event listeners: prefixed key -> listener array */
164
+ listeners: Map<string, EventListenerEntry[]>;
165
+ /**
166
+ * Bind event listener.
167
+ */
168
+ on(event: string, handler: (this: T, e: ChangeEvent) => void): this;
169
+ /**
170
+ * Unbind event listener.
171
+ * If handler is provided, removes only that handler.
172
+ * If no handler, removes all handlers for the event.
173
+ */
174
+ off(event: string, handler?: AnyFunc): this;
175
+ /**
176
+ * Fire event, execute all bound handlers.
177
+ * Supports executing state management: handlers removed during
178
+ * execution are safely handled.
179
+ *
180
+ * @param event - Event name
181
+ * @param data - Event data (type property added automatically)
182
+ * @param remove - Whether to remove all handlers after firing
183
+ * @param lastToFirst - Whether to execute handlers in reverse order
184
+ */
185
+ fire(event: string, data?: Record<string, unknown>, remove?: boolean, lastToFirst?: boolean): this;
186
+ }
187
+
188
+ /**
189
+ * Frame (View Frame) class for view lifecycle management.
190
+ * Each frame owns a view and manages child frames.
191
+ *
192
+ */
193
+ declare class Frame extends EventEmitter implements FrameInterface {
194
+ /** Frame ID (same as owner DOM element ID) */
195
+ readonly id: string;
196
+ /** Parent Frame ID */
197
+ private _parentId;
198
+ get parentId(): string | undefined;
199
+ /** Children map: id -> id */
200
+ childrenMap: Record<string, string>;
201
+ /** Children count */
202
+ childrenCount: number;
203
+ /** Ready count (children that have fired 'created') */
204
+ readyCount: number;
205
+ /** Ready map: id -> 1 */
206
+ readyMap: Record<string, number>;
207
+ /** View instance */
208
+ viewInstance?: ViewInterface;
209
+ /** Get view instance (read-only) */
210
+ get view(): ViewInterface | undefined;
211
+ /** Invoke list for deferred method calls */
212
+ invokeList: FrameInvokeEntry[];
213
+ /** Signature for async operation tracking */
214
+ signature: number;
215
+ /** Whether view has altered */
216
+ hasAltered: number;
217
+ /** Whether view is destroyed */
218
+ destroyed: number;
219
+ /** View path (v-lark attribute value) */
220
+ viewPath?: string;
221
+ /** Original template before mount */
222
+ originalTemplate?: string;
223
+ /** Hold fire created flag */
224
+ holdFireCreated: number;
225
+ /** Children created flag */
226
+ childrenCreated: number;
227
+ /** Children alter flag */
228
+ childrenAlter: number;
229
+ constructor(id: string, parentId?: string);
230
+ /**
231
+ * Mount a view to this frame.
232
+ *
233
+ * Complete flow:
234
+ * 1. Parse viewPath, translate query params from parent
235
+ * 2. Unmount current view
236
+ * 3. Load View class (via require or provided ViewClass)
237
+ * 4. View_Prepare (scan event methods)
238
+ * 5. Create View instance
239
+ * 6. View_DelegateEvents (bind DOM events)
240
+ * 7. Call view.init()
241
+ * 8. If view has template, call render via Updater
242
+ * 9. If no template, call endUpdate directly
243
+ */
244
+ mountView(viewPath: string, viewInitParams?: Record<string, unknown>): void;
245
+ /**
246
+ * Internal: actually mount the view after class is loaded.
247
+ */
248
+ doMountView(ViewClass: typeof View, params: Record<string, string>, node: HTMLElement, sign: number): void;
249
+ /**
250
+ * Unmount current view.
251
+ */
252
+ unmountView(): void;
253
+ /**
254
+ * Mount a child frame.
255
+ */
256
+ mountFrame(frameId: string, viewPath: string, viewInitParams?: Record<string, unknown>): FrameInterface;
257
+ /**
258
+ * Unmount a child frame.
259
+ */
260
+ unmountFrame(id?: string, _inner?: boolean): void;
261
+ /**
262
+ * Mount all views in a zone.
263
+ */
264
+ mountZone(zoneId?: string, _inner?: boolean): void;
265
+ /**
266
+ * Unmount all views in a zone.
267
+ */
268
+ unmountZone(zoneId?: string, _inner?: boolean): void;
269
+ /**
270
+ * Get all child frame IDs.
271
+ */
272
+ children(): string[];
273
+ /**
274
+ * Get parent frame at given level.
275
+ * @param level - How many levels up (default 1)
276
+ */
277
+ parent(level?: number): Frame | undefined;
278
+ /**
279
+ * Invoke a method on the view.
280
+ */
281
+ invoke(name: string, args?: unknown[]): unknown;
282
+ /** Get frame by ID */
283
+ static get(id: string): Frame | undefined;
284
+ /** Get all frames */
285
+ static getAll(): Map<string, Frame>;
286
+ /** Get or create root frame */
287
+ static root(rootId?: string): Frame;
288
+ /** Bind event listener (static) */
289
+ static on(event: string, handler: AnyFunc): typeof Frame;
290
+ /** Unbind event listener (static) */
291
+ static off(event: string, handler?: AnyFunc): typeof Frame;
292
+ /** Fire event (static) */
293
+ static fire(event: string, data?: Record<string, unknown>): void;
294
+ }
295
+ /**
296
+ * Register a View class for a given view path.
297
+ * Called after module loading completes.
298
+ */
299
+ declare function registerViewClass(viewPath: string, ViewClass: typeof View): void;
300
+
301
+ /**
302
+ * Cache class with LFU-style eviction.
303
+ * Keys are prefixed with SPLITTER for namespace isolation.
304
+ *
305
+ * @example
306
+ * const cache = new Cache({ maxSize: 20, bufferSize: 5 });
307
+ * cache.set('user', { name: 'Alice' });
308
+ * const user = cache.get('user');
309
+ * cache.has('user'); // true
310
+ * cache.del('user');
311
+ */
312
+ declare class Cache<T = unknown> implements CacheInterface<T> {
313
+ /** Cache entries array */
314
+ private entries;
315
+ /** Fast lookup: prefixed key -> entry */
316
+ private lookup;
317
+ /** Buffer size for eviction */
318
+ private readonly bufferSize;
319
+ /** Maximum cache size */
320
+ private readonly maxSize;
321
+ /** Total capacity (maxSize + bufferSize) */
322
+ private readonly capacity;
323
+ /** Callback when entry is removed */
324
+ private readonly onRemove?;
325
+ /** Sort comparator */
326
+ private readonly comparator;
327
+ constructor(options?: CacheOptions<T>);
328
+ /** Prefix a key with SPLITTER for namespace isolation */
329
+ private prefixKey;
330
+ /**
331
+ * Get a cached value by key.
332
+ * Updates frequency and timestamp for cache sorting.
333
+ */
334
+ get(key: string): T | undefined;
335
+ /**
336
+ * Iterate all cached values.
337
+ */
338
+ forEach(callback: (value: T | undefined) => void): void;
339
+ /**
340
+ * Set or update a cached value.
341
+ * If key already exists, updates value and increments frequency.
342
+ * If cache exceeds capacity, triggers eviction.
343
+ */
344
+ set(key: string, value: T): void;
345
+ /**
346
+ * Delete a cached entry.
347
+ */
348
+ del(key: string): void;
349
+ /**
350
+ * Check if a key exists in cache.
351
+ */
352
+ has(key: string): boolean;
353
+ /** Get current cache size */
354
+ get size(): number;
355
+ /** Clear all entries */
356
+ clear(): void;
357
+ /** Evict least-used entries from cache */
358
+ private evictEntries;
359
+ }
360
+
1
361
  /**
2
362
  * Lark framework type definitions.
3
363
  * All shared types are defined here to eliminate type cheats across modules.
364
+ *
365
+ * Lark is a lightweight MVC frontend framework that provides:
366
+ * - View: base view class with extend/merge inheritance and mixin support
367
+ * - Router: hash-based two-phase route confirmation
368
+ * - State: cross-view observable global state management
369
+ * - Service: API request management with caching, queuing, and deduplication
370
+ * - Frame: view frame managing view mount/unmount lifecycle
371
+ * - Updater: view data binding and VDOM diff (in-memory real DOM diff) renderer
372
+ *
373
+ * Designed for single-page application (SPA) development.
4
374
  */
5
375
  /** Generic function type for event handlers and callbacks.
6
376
  * Uses any[] to accept callbacks with specific parameter types
7
377
  * (TypeScript function parameters are contravariant).
8
378
  */
9
379
  type AnyFunc = (...args: any[]) => unknown;
10
- /** A function that returns void. */
11
- type VoidFunc = (...args: any[]) => void;
12
380
  interface CacheEntry<T> {
13
381
  /** Original key without prefix */
14
382
  originalKey: string;
@@ -29,81 +397,184 @@ interface CacheOptions<T> {
29
397
  /** Comparator for sorting entries */
30
398
  sortComparator?: (a: CacheEntry<T>, b: CacheEntry<T>) => number;
31
399
  }
400
+ /**
401
+ * Cache interface providing LFU (Least Frequently Used) cache management.
402
+ * Cache keys use a special prefix internally for namespace isolation.
403
+ */
404
+ interface CacheInterface<T = unknown> {
405
+ /**
406
+ * Set a cache resource. If the key exists, updates the value and increments frequency.
407
+ * Triggers LFU eviction when cache entries exceed capacity (maxSize + bufferSize).
408
+ * @param key Unique identifier for the cached resource
409
+ * @param resource The resource to cache
410
+ */
411
+ set(key: string, resource: T): void;
412
+ /**
413
+ * Get a cached resource. Access increments frequency count and timestamp for LFU ranking.
414
+ * Returns undefined if the key does not exist.
415
+ * @param key Cache resource key
416
+ */
417
+ get(key: string): T | undefined;
418
+ /**
419
+ * Remove a resource from cache by key. Triggers onRemove callback on deletion.
420
+ * @param key Cache resource key to remove
421
+ */
422
+ del(key: string): void;
423
+ /**
424
+ * Check if cache contains a resource for the given key.
425
+ * @param key Cache resource key
426
+ */
427
+ has(key: string): boolean;
428
+ /**
429
+ * Iterate over all cached resource values.
430
+ * @param callback Iteration callback receiving the cached value (may be undefined)
431
+ */
432
+ forEach(callback: (value: T | undefined) => void): void;
433
+ /**
434
+ * Number of cache entries.
435
+ */
436
+ readonly size: number;
437
+ /**
438
+ * Clear all cache entries. Triggers onRemove callback for each deleted entry.
439
+ */
440
+ clear(): void;
441
+ }
32
442
  interface EventListenerEntry {
33
443
  /** Handler function */
34
444
  handler: AnyFunc;
35
445
  /** Whether currently executing (1 = executing, '' = done) */
36
446
  executing: number | string;
37
447
  }
448
+ /**
449
+ * Parsed URL result containing path and parameters.
450
+ * Returned by `Router.parse()`, includes the path string and parsed key-value parameter pairs.
451
+ */
38
452
  interface ParsedUri {
39
- /** Path portion (before ? or #) */
453
+ /** Path portion (before ? or #), excluding query parameters */
40
454
  path: string;
41
- /** Key-value params */
455
+ /** Key-value params parsed from the URL */
42
456
  params: Record<string, string>;
43
457
  }
458
+ /**
459
+ * Current URL parsing result interface.
460
+ * Returned by `Router.parse()`, includes both query (after ?) and hash (after #) sections.
461
+ */
44
462
  interface Location {
45
- /** Full href */
463
+ /** Full href, original href string */
46
464
  href: string;
47
- /** Query string (before #) */
465
+ /** Query string (before #), raw query string (after ?, before #) */
48
466
  srcQuery: string;
49
- /** Hash string (after #) */
467
+ /** Hash string (after #), raw hash string (after #) */
50
468
  srcHash: string;
51
- /** Parsed query object */
469
+ /** Parsed query object, path and params parsed from srcQuery */
52
470
  query: ParsedUri;
53
- /** Parsed hash object */
471
+ /** Parsed hash object, path and params parsed from srcHash */
54
472
  hash: ParsedUri;
55
- /** Merged params from query and hash */
473
+ /**
474
+ * Merged params from query and hash,
475
+ * hash values take precedence when keys conflict.
476
+ */
56
477
  params: Record<string, string>;
57
- /** Resolved view path */
478
+ /**
479
+ * Resolved view path for the current URL.
480
+ * May be undefined before framework boot.
481
+ */
58
482
  view?: string;
59
- /** Resolved path */
483
+ /**
484
+ * Resolved path computed from hash path and query path based on routing rules.
485
+ * May be undefined before framework boot.
486
+ */
60
487
  path?: string;
61
- /** Get param by key with optional default */
488
+ /**
489
+ * Get param by key with optional default value.
490
+ * Returns default value or empty string if key does not exist.
491
+ * @param key Parameter key name
492
+ * @param defaultValue Default value when key is missing, defaults to empty string
493
+ */
62
494
  get: (key: string, defaultValue?: string) => string;
63
495
  }
496
+ /**
497
+ * URL parameter change representing a parameter value transition from old to new.
498
+ * Used in `Router.diff()` return value to describe parameter changes.
499
+ */
64
500
  interface ParamDiff {
501
+ /** Value before the change */
65
502
  from: string;
503
+ /** Value after the change */
66
504
  to: string;
67
505
  }
506
+ /**
507
+ * URL route change object interface describing changes between two routing states.
508
+ * Returned by `Router.diff()`, includes changes in path, view, and other parameters.
509
+ */
68
510
  interface LocationDiff {
69
- /** Changed params (key -> {from, to}) */
511
+ /**
512
+ * Changed params (key -> {from, to}),
513
+ * diff for all changed parameters
514
+ */
70
515
  params: Record<string, ParamDiff>;
71
- /** Whether path changed */
516
+ /** Path diff when path has changed */
72
517
  path?: ParamDiff;
73
- /** Whether view changed */
518
+ /** View diff when rendered view has changed */
74
519
  view?: ParamDiff;
75
- /** Whether this is a first forced change */
520
+ /**
521
+ * Whether this is a first forced change during app initialization.
522
+ */
76
523
  force: boolean;
77
- /** Whether anything changed */
524
+ /** Whether any content has changed */
78
525
  changed: boolean;
79
526
  }
527
+ /**
528
+ * Route pre-change event interface (change phase).
529
+ * Provides two-phase confirmation: triggers change (can be rejected), then changed.
530
+ * Can prevent, reject, or accept route changes through this event object.
531
+ */
532
+ interface RouteChangeEvent extends ChangeEvent {
533
+ /**
534
+ * Reject the URL change, revert to previous URL.
535
+ */
536
+ reject: () => void;
537
+ /**
538
+ * Accept the URL change, continue navigation.
539
+ */
540
+ resolve: () => void;
541
+ /**
542
+ * Prevent the URL change, pause subsequent route processing.
543
+ */
544
+ prevent: () => void;
545
+ }
546
+ /**
547
+ * Route post-change event interface (changed phase).
548
+ * Carries route diff information. Triggered after route change is confirmed and URL is updated.
549
+ */
550
+ type RouteChangedEvent = LocationDiff & ChangeEvent;
80
551
  interface VDomRef {
81
552
  /** ID update list: [element, newId][] */
82
553
  idUpdates: [Element, string][];
83
554
  /** Views that need post-processing */
84
- views: ViewInstance[];
555
+ views: ViewInterface[];
85
556
  /** DOM operation list: [opCode, parent, newChild?, oldChild?][] */
86
557
  domOps: VDomOp[];
87
558
  /** Whether anything changed */
88
559
  hasChanged: number;
89
560
  }
90
561
  type VDomOp = [1, Element, Element] | [2, Element, Element] | [4, Element, Element, Element] | [8, Element, Element, Element];
91
- type FrameChildrenMap = Record<string, string>;
92
- type FrameReadyMap = Record<string, number>;
93
562
  interface FrameInvokeEntry {
94
563
  /** Method name */
95
564
  name: string;
96
565
  /** Method arguments */
97
566
  args: unknown[];
98
- /** Internal key for dedup */
567
+ /** Internal key */
99
568
  key: string;
100
569
  /** Whether removed (args match) */
101
570
  removed?: boolean;
102
571
  }
103
572
  /** Mixin event handler with internal merge marker and handler list */
104
573
  interface MixinEventHandler extends AnyFunc {
105
- /** Merged handler list */ a?: AnyFunc[];
106
- /** Mixin marker: 1 = this is a mixin function */ b?: number;
574
+ /** Merged handler list */
575
+ handlerList?: AnyFunc[];
576
+ /** Mixin marker: 1 = this is a mixin function */
577
+ marker?: number;
107
578
  }
108
579
  /** View event selector map entry: handler name list with selector presence tracking */
109
580
  interface ViewEventSelectorEntry {
@@ -112,8 +583,6 @@ interface ViewEventSelectorEntry {
112
583
  /** Index signature for checking if selector is already registered */
113
584
  [selector: string]: unknown;
114
585
  }
115
- /** View class internal markers (SPLITTER key) */
116
- type ViewClassInternal = Record<string, AnyFunc[]>;
117
586
  interface ViewLocationObserved {
118
587
  /** Whether observing location */
119
588
  flag: number;
@@ -128,8 +597,6 @@ interface ViewResourceEntry {
128
597
  /** Whether to destroy when render() is called */
129
598
  destroyOnRender: boolean;
130
599
  }
131
- type ViewResourceMap = Record<string, ViewResourceEntry>;
132
- type ViewEventSelectorMap = Record<string, ViewEventSelectorEntry>;
133
600
  interface ViewGlobalEventEntry {
134
601
  /** Handler function */
135
602
  handler: AnyFunc;
@@ -142,30 +609,105 @@ interface ViewGlobalEventEntry {
142
609
  /** Modifiers */
143
610
  modifiers: Record<string, boolean>;
144
611
  }
145
- type ViewEventObjectMap = Record<string, number>;
146
- interface ViewInstance {
147
- /** View ID (same as owner frame ID) */
612
+ /**
613
+ * Router interface providing URL parsing, navigation, diff, and event listening capabilities.
614
+ * Supports two-phase route confirmation mechanism: change (can reject) → changed.
615
+ * Hash-based implementation using #! as default hash prefix.
616
+ */
617
+ interface RouterInterface extends EventEmitterInterface<RouterInterface> {
618
+ /**
619
+ * Parse href into Location object.
620
+ * Parses query and hash sections of href, returns structured routing information.
621
+ * Defaults to parsing current page `location.href`.
622
+ * @param href URL to parse, uses `location.href` if not specified
623
+ */
624
+ parse(href?: string): Location;
625
+ /**
626
+ * Compute diff between current and previous location.
627
+ * Returns undefined if no routing changes have occurred yet.
628
+ */
629
+ diff(): LocationDiff | undefined;
630
+ /**
631
+ * Navigate to new URL.
632
+ * Supports two calling modes:
633
+ * - `Router.to("/list", { page: 2 })` specify path and params
634
+ * - `Router.to({ page: 2 })` update params only, keep current path
635
+ * @param pathOrParams Path string or params object
636
+ * @param params Query params object (only used when first arg is path string)
637
+ * @param replace Whether to replace current history entry instead of adding new one
638
+ * @param silent Whether to silently update without triggering change event
639
+ */
640
+ to(pathOrParams: string | Record<string, unknown>, params?: Record<string, unknown>, replace?: boolean, silent?: boolean): void;
641
+ /** Join path segments */
642
+ join(...paths: string[]): string;
643
+ /** Internal: bind hashchange (called by Framework.boot) */
644
+ _bind(): void;
645
+ /** Internal: set framework config */
646
+ _setConfig(cfg: FrameworkConfig): void;
647
+ /** Internal: notify hash change (for programmatic trigger) */
648
+ notify?(e?: Event): void;
649
+ /**
650
+ * Triggered when URL is about to change (change phase), can reject or prevent navigation via event object.
651
+ */
652
+ onChange?: (e?: RouteChangeEvent) => void;
653
+ /**
654
+ * Triggered after URL has changed (changed phase), carries route diff information.
655
+ */
656
+ onChanged?: (e?: RouteChangedEvent) => void;
657
+ }
658
+ /**
659
+ * Frame static event interface carrying associated Frame instance.
660
+ * Carried in Frame's add/remove static events.
661
+ */
662
+ interface FrameStaticEvent extends ChangeEvent {
663
+ /**
664
+ * Associated Frame instance object.
665
+ */
666
+ readonly frame: FrameInterface;
667
+ }
668
+ interface ViewInterface extends EventEmitterInterface<ViewInterface> {
669
+ /**
670
+ * View ID (same as owner frame ID),
671
+ * DOM node ID where current view resides.
672
+ */
148
673
  id: string;
149
- /** Owner frame */
150
- owner: FrameLike | number;
151
- /** Updater instance */
152
- updater: UpdaterLike;
153
- /** Signature: > 0 means active, incremented on render, 0 = destroyed */
674
+ /**
675
+ * Owner frame,
676
+ * Frame instance holding current view.
677
+ * May be numeric placeholder 0 before view initialization completes.
678
+ * TODO: Migrate numeric placeholder 0 to undefined or null
679
+ */
680
+ owner: FrameInterface | number;
681
+ /**
682
+ * Updater instance managing view data binding and VDOM rendering.
683
+ */
684
+ updater: UpdaterInterface;
685
+ /**
686
+ * Signature: > 0 means active, incremented on render, 0 = destroyed */
154
687
  signature: number;
155
688
  /** Whether rendered at least once */
156
689
  rendered?: boolean;
157
- /** Whether view has template */
158
- template?: AnyFunc | string;
690
+ /**
691
+ * View template supporting function or string template.
692
+ * Function template signature: `(data: unknown, viewId: string, refData: unknown, ...rest: unknown[]) => string`
693
+ */
694
+ template?: (data: unknown, viewId: string, refData: unknown, ...rest: unknown[]) => string | string;
695
+ /**
696
+ * Mixin object array for extending view functionality.
697
+ * Framework merges properties and methods from mixins into view prototype.
698
+ * Event method conflicts are automatically merged into sequential calls.
699
+ */
700
+ mixins?: Record<string, unknown>[];
159
701
  /** Location observation config */
160
702
  locationObserved: ViewLocationObserved;
161
703
  /** Observed state keys */
162
704
  observedStateKeys?: string[];
163
705
  /** Resource map */
164
- resources: ViewResourceMap;
706
+ resources: Record<string, ViewResourceEntry>;
165
707
  /** Selector event map: eventType -> selector list */
166
- eventSelectorMap: ViewEventSelectorMap;
708
+ eventSelectorMap: Record<string, ViewEventSelectorEntry>;
167
709
  /** Event object map: eventType -> bitmask */
168
- eventObjectMap: ViewEventObjectMap;
710
+ eventObjectMap: Record<string, number>;
169
711
  /** Global event list */
170
712
  globalEventList: ViewGlobalEventEntry[];
171
713
  /** Assign method reference */
@@ -173,53 +715,366 @@ interface ViewInstance {
173
715
  /** Whether endUpdate has been called (1 = pending) */
174
716
  endUpdatePending?: number;
175
717
  /** Render method (wrapped) */
176
- render: AnyFunc;
177
- /** init method */
178
- init: AnyFunc;
718
+ render(): void;
719
+ /**
720
+ * Init method called after view is mounted.
721
+ * Used for initialization logic.
722
+ * Framework passes two arguments during actual invocation:
723
+ * - initParams: initialization parameter object
724
+ * - options: contains `node: Element` and `deep: boolean`
725
+ */
726
+ init(): void;
179
727
  /** Wrapped render method */
180
- $b?: AnyFunc;
728
+ renderMethod?: AnyFunc;
181
729
  /** endUpdate pending flag */
182
- $e?: number;
183
- on: (event: string, handler: AnyFunc) => ViewInstance;
184
- off: (event: string, handler?: AnyFunc) => ViewInstance;
185
- fire: (event: string, data?: Record<string, unknown>, remove?: boolean, lastToFirst?: boolean) => ViewInstance;
730
+ endUpdatePendingFlag?: number;
731
+ /**
732
+ * Notify view that HTML update is about to begin for a specific region.
733
+ * Framework unmounts child Frames in that region to prevent VDOM diff from operating on unmounted nodes.
734
+ * @param id Region node ID to update, defaults to current view
735
+ */
186
736
  beginUpdate: (id?: string) => void;
737
+ /**
738
+ * Notify view that HTML update has completed for a specific region.
739
+ * Framework mounts child Frames in that region and executes deferred invoke queue.
740
+ * @param id Region node ID that finished updating, defaults to current view
741
+ * @param inner Whether this is an internal framework call
742
+ */
187
743
  endUpdate: (id?: string, inner?: boolean) => void;
744
+ /**
745
+ * Wrap async callback to ensure it only executes if view is not destroyed.
746
+ * In SPAs, async callbacks (e.g., setTimeout, AJAX) may execute after view is destroyed,
747
+ * causing errors when manipulating DOM.
748
+ * After wrapping with `wrapAsync`, framework automatically checks view state and only executes callback if view is alive.
749
+ * @param fn Callback function to wrap
750
+ * @param context `this` binding for callback execution, defaults to view itself
751
+ */
188
752
  wrapAsync: <Fn extends AnyFunc>(fn: Fn, context?: unknown) => (...args: Parameters<Fn>) => ReturnType<Fn> | undefined;
753
+ /**
754
+ * Listen for URL bar changes, supports two calling modes:
755
+ * - `observeLocation("page,size", true)` pass parameter keys (comma-separated) and whether to observe path
756
+ * - `observeLocation({ params: ["page", "size"], path: true })` pass config object
757
+ * View automatically re-renders when observed parameters or path change.
758
+ * @param params Parameter keys to observe, supports comma-separated string, string array, or config object
759
+ * @param observePath Whether to observe path changes
760
+ */
189
761
  observeLocation: (params: string | string[] | Record<string, unknown>, observePath?: boolean) => void;
762
+ /**
763
+ * Observe data changes for specified keys in State.
764
+ * View automatically re-renders when observed keys are updated via `State.digest()`.
765
+ * @param keys Comma-separated key string or string array
766
+ */
190
767
  observeState: (keys: string | string[]) => void;
768
+ /**
769
+ * Hand over resource to current view for lifecycle management.
770
+ * Framework automatically calls resource's destroy method at appropriate time when view unmounts or re-renders.
771
+ * @param key Unique key for managed resource; if key already manages different resource, old resource is auto-destroyed
772
+ * @param resource Resource object to manage
773
+ * @param destroyOnRender Whether to auto-destroy resource when render method is called; Service instances typically need auto-destroy on render
774
+ */
191
775
  capture: (key: string, resource?: unknown, destroyOnRender?: boolean) => unknown;
776
+ /**
777
+ * Release managed resource, returns the resource object regardless of destruction state.
778
+ * @param key Managed resource key
779
+ * @param destroy Whether to destroy resource (call its destroy method), defaults to true
780
+ */
192
781
  release: (key: string, destroy?: boolean) => unknown;
782
+ /**
783
+ * Set leave prompt, e.g., when form has unsaved changes.
784
+ * Can prompt user to choose between leaving directly or saving before leaving.
785
+ * Framework calls condition function during route changes (change phase) and page unloads (beforeunload).
786
+ * Navigation is prevented if condition returns true.
787
+ * @param message Leave prompt message
788
+ * @param condition Function to determine whether to show leave prompt; returns true to prevent navigation
789
+ */
193
790
  leaveTip: (message: string, condition: () => boolean) => void;
194
- assign: (options?: unknown) => boolean | undefined;
791
+ /**
792
+ * Assign method for incremental DOM updates.
793
+ * Framework uses VDOM diff (in-memory real DOM diff) to update only changed portions,
794
+ * automatically handling child view mounting and unmounting.
795
+ * Returns true if DOM changed, undefined if no change.
796
+ * @param options Incremental update config, used internally by framework
797
+ */
798
+ assign?: (options?: unknown) => boolean | undefined;
799
+ /**
800
+ * Triggered when view is destroyed.
801
+ */
802
+ onDestroy?: (e?: ChangeEvent) => void;
803
+ /**
804
+ * Triggered when render method is called.
805
+ */
806
+ onRender?: (e?: ChangeEvent) => void;
807
+ /**
808
+ * Inherit View to create new view subclass.
809
+ * Supports props.make constructor, props.mixins, and event methods (e.g., `'name<click>'`).
810
+ * @param props Prototype object containing init, render, and other methods
811
+ * @param statics Object of static methods or properties
812
+ */
813
+ extend?<TProps = object, TStatics = object>(props?: ExtendThisType<TProps & ViewInterface>, statics?: TStatics): ViewInterface & TStatics;
814
+ /**
815
+ * Merge multiple mixin objects into View prototype.
816
+ * Existing properties are not overwritten; event method conflicts are automatically merged into sequential calls.
817
+ * @param args Mixin object list
818
+ */
819
+ merge?(...args: ExtendThisType<ViewInterface>[]): ViewInterface;
195
820
  }
196
- /** Minimal Frame interface needed by View */
197
- interface FrameLike {
821
+ type ExtendThisType<T> = Record<string, unknown> & ThisType<T>;
822
+ /**
823
+ * Minimal Frame interface needed by View.
824
+ * Frame (View Frame) is a view container managing view mount, unmount, and parent-child hierarchy.
825
+ * Each Frame corresponds to one DOM node, associated with view via v-lark attribute.
826
+ */
827
+ interface FrameInterface extends EventEmitterInterface<FrameInterface> {
828
+ /**
829
+ * DOM node ID where Frame resides.
830
+ */
198
831
  id: string;
199
- parentId: string | undefined;
200
- unmountZone: (zoneId?: string, inner?: boolean) => void;
201
- mountZone: (zoneId?: string, inner?: boolean) => void;
202
- mountFrame: (frameId: string, viewPath: string, viewInitParams?: Record<string, unknown>) => FrameLike;
832
+ /**
833
+ * View module path currently rendered by this Frame, e.g., "app/views/default".
834
+ */
835
+ readonly viewPath?: string;
836
+ /**
837
+ * Parent Frame ID, undefined if this is a top-level Frame.
838
+ */
839
+ readonly parentId: string | undefined;
840
+ /**
841
+ * Mount view to current Frame.
842
+ * Framework loads view class, creates instance, and renders view.
843
+ * @param viewPath View module path, e.g., "app/views/default"
844
+ * @param viewInitParams Parameters passed during view initialization, accessible in view's init method
845
+ */
846
+ mountView(viewPath: string, viewInitParams?: Record<string, unknown>): void;
847
+ /**
848
+ * Unmount view from current Frame, triggers view's destroy event and cleans up resources.
849
+ */
850
+ unmountView(): void;
851
+ /**
852
+ * Mount child Frame on specified DOM node and render view.
853
+ * @param frameId DOM node ID for rendering
854
+ * @param viewPath View path
855
+ * @param viewInitParams Parameters passed during view initialization
856
+ */
857
+ mountFrame: (frameId: string, viewPath: string, viewInitParams?: Record<string, unknown>) => FrameInterface;
858
+ /**
859
+ * Unmount child Frame from specified DOM node.
860
+ * @param id DOM node ID, defaults to current Frame if omitted
861
+ */
203
862
  unmountFrame: (id?: string, inner?: boolean) => void;
204
- view: ViewInstance | undefined;
205
- children: () => string[];
863
+ /**
864
+ * Render all child views under specified node (scans v-lark attributes and mounts).
865
+ * @param zoneId DOM node ID, defaults to current Frame
866
+ * @param inner Whether this is an internal framework call
867
+ */
868
+ mountZone: (zoneId?: string, inner?: boolean) => void;
869
+ /**
870
+ * Unmount all child views under specified node.
871
+ * @param zoneId DOM node ID, defaults to current Frame
872
+ */
873
+ unmountZone: (zoneId?: string, inner?: boolean) => void;
874
+ /**
875
+ * Get ancestor Frame, defaults to parent Frame (level=1).
876
+ * @param level Levels to traverse upward, defaults to 1
877
+ */
878
+ parent(level?: number): FrameInterface | undefined;
879
+ /**
880
+ * Invoke specified method on view in current Frame.
881
+ * If view is not rendered yet, invocation is deferred until render completes.
882
+ * @param name Method name
883
+ * @param args Arguments array passed to method
884
+ */
206
885
  invoke: (name: string, args?: unknown[]) => unknown;
886
+ /**
887
+ * Triggered when all descendant views have been created.
888
+ */
889
+ onCreated?: (e?: ChangeEvent) => void;
890
+ /**
891
+ * Triggered when descendant views change.
892
+ */
893
+ onAlter?: (e?: ChangeEvent) => void;
894
+ /**
895
+ * Get Frame instance by ID, returns undefined if not exists.
896
+ * @param id Frame's DOM node ID
897
+ */
898
+ get?(id: string): FrameInterface | undefined;
899
+ /**
900
+ * Get all Frame instances map for current page.
901
+ */
902
+ getAll?(): Map<string, FrameInterface>;
903
+ /**
904
+ * Triggered when Frame is created and registered.
905
+ */
906
+ onAdd?: (e?: FrameStaticEvent) => void;
907
+ /**
908
+ * Triggered when Frame is destroyed and unregistered.
909
+ */
910
+ onRemove?: (e?: FrameStaticEvent) => void;
911
+ view: ViewInterface | undefined;
912
+ /**
913
+ * Get ID array of all child Frames for current Frame.
914
+ * Note: ID positions in array are not fixed.
915
+ */
916
+ children: () => string[];
207
917
  invokeList: FrameInvokeEntry[];
208
- on: (event: string, handler: AnyFunc) => FrameLike;
209
- off: (event: string, handler?: AnyFunc) => FrameLike;
210
- fire: (event: string, data?: Record<string, unknown>) => FrameLike;
211
918
  }
212
- /** Minimal Updater interface needed by View */
213
- interface UpdaterLike {
919
+ /**
920
+ * Minimal Updater interface needed by View.
921
+ * View updater responsible for view data binding and data/page updates.
922
+ * Each View instance has an Updater, triggering data/page updates via set/digest.
923
+ * Internally executes complete pipeline: template rendering → VDOM diff (in-memory real DOM diff) → DOM operations.
924
+ */
925
+ interface UpdaterInterface {
926
+ /**
927
+ * Get data that has been set.
928
+ * Returns complete data object if key is omitted, otherwise returns value for specified key.
929
+ * @param key Data key name, omitted returns complete data object
930
+ */
214
931
  get: <T = unknown>(key?: string) => T;
215
- set: (data: Record<string, unknown>, excludes?: Set<string>) => UpdaterLike;
216
- digest: (data?: Record<string, unknown>, excludes?: Set<string>) => void;
217
- /** Save a snapshot of current data for altered() detection */
218
- snapshot: () => this;
219
- /** Check if data has changed since last snapshot */
932
+ /**
933
+ * Set data and track changed keys.
934
+ * After set, must explicitly call `digest()` to commit changes to page.
935
+ * Returns this for chaining.
936
+ * @param data Data object, e.g., `{ a: 1, b: 2 }`
937
+ * @param excludes Set of keys to exclude from change tracking
938
+ */
939
+ set: (data: Record<string, unknown>, excludes?: Set<string>) => UpdaterInterface;
940
+ /**
941
+ * Trigger page re-render.
942
+ * After set, must explicitly call `digest()` to commit changes to page.
943
+ * Internally executes complete pipeline: template rendering → VDOM diff (in-memory real DOM diff) → DOM operations.
944
+ * @param data Optional data object, if provided calls `set()` first to set data
945
+ * @param excludes Set of keys to exclude from change tracking
946
+ * @param callback Callback executed after render completes
947
+ */
948
+ digest: (data?: Record<string, unknown>, excludes?: Set<string>, callback?: () => void) => void;
949
+ /**
950
+ * Save a snapshot of current data for altered() detection.
951
+ * Works with `altered()` method to detect whether data has changed.
952
+ */
953
+ snapshot: () => UpdaterInterface;
954
+ /**
955
+ * Check if data has changed since last snapshot.
956
+ * Returns undefined if `snapshot()` has not been called.
957
+ */
220
958
  altered: () => boolean | undefined;
221
959
  /** Ref data for template rendering */
222
960
  refData: Record<string, unknown>;
961
+ /**
962
+ * Translate raw reference data starting with @ symbol in template.
963
+ * Replaces `{{@refData}}` with actual value from refData.
964
+ * @param data Reference data to translate
965
+ */
966
+ translate(data: unknown): unknown;
967
+ /**
968
+ * Parse expression string.
969
+ * @param expr Expression string to parse
970
+ */
971
+ parse(expr: string): unknown;
972
+ }
973
+ /**
974
+ * Data payload interface wrapping API request response data, providing read/write methods.
975
+ * Payload instances are created internally by Service, developers access via all/one/save callbacks.
976
+ */
977
+ interface PayloadInterface {
978
+ /**
979
+ * Get data from Payload by key.
980
+ * @param key Data key name
981
+ */
982
+ get<T = unknown>(key: string): T;
983
+ /**
984
+ * Set data to Payload, supports three calling modes:
985
+ * - Key-value pair: `payload.set("name", "value")`
986
+ * - Data object: `payload.set({ name: "value" })`
987
+ * - Endpoint metadata object (for internal framework use)
988
+ * Returns this for chaining.
989
+ * @param keyOrData Key/value string, data object, or endpoint metadata object
990
+ * @param value Value when first parameter is a key
991
+ */
992
+ set(keyOrData: string | Record<string, unknown> | ServiceMetaEntry, value?: unknown): PayloadInterface;
993
+ data: Record<string, unknown>;
994
+ cacheInfo?: ServiceCacheInfo;
995
+ }
996
+ /**
997
+ * Change event object.
998
+ */
999
+ interface ChangeEvent {
1000
+ /**
1001
+ * Event type.
1002
+ */
1003
+ readonly type: string;
1004
+ /**
1005
+ * Set object of changed data keys, value 1 indicates key has changed.
1006
+ * TODO: Optimize to Set data structure.
1007
+ */
1008
+ readonly keys?: Readonly<Record<string, 1>>;
1009
+ }
1010
+ /**
1011
+ * Event emitter interface providing on/off/fire methods for publish-subscribe pattern.
1012
+ */
1013
+ interface EventEmitterInterface<T = unknown> {
1014
+ /**
1015
+ * Bind event listener, calls handler when event is triggered.
1016
+ * @param name Event name
1017
+ * @param fn Event handler function
1018
+ */
1019
+ on(name: string, fn: (this: T, e?: ChangeEvent) => void): EventEmitterInterface<T>;
1020
+ /**
1021
+ * Unbind event listener, removes all handlers for event if no handler function is provided.
1022
+ * @param name Event name
1023
+ * @param fn Optional event handler function, if omitted removes all handlers
1024
+ */
1025
+ off(name: string, fn?: AnyFunc): EventEmitterInterface<T>;
1026
+ /**
1027
+ * Fire event, executes all bound handlers, automatically adds type property to event data.
1028
+ * Supports removing all handlers after firing.
1029
+ * Supports executing handler list in reverse order.
1030
+ * @param name Event name
1031
+ * @param data Event data object
1032
+ * @param remove Whether to remove all handlers after firing
1033
+ * @param lastToFirst Whether to execute handler list in reverse order
1034
+ */
1035
+ fire(name: string, data?: Record<string, unknown>, remove?: boolean, lastToFirst?: boolean): EventEmitterInterface<T>;
1036
+ }
1037
+ /**
1038
+ * Global state interface providing cross-view data sharing and data change notification capabilities.
1039
+ * State is a singleton object managing app-level state data via get/set/digest.
1040
+ * Supports `clean()` method to create a mixin for automatic cleanup on view destruction.
1041
+ */
1042
+ interface StateInterface extends EventEmitterInterface<StateInterface> {
1043
+ /**
1044
+ * Get data from global state, returns complete state object if key is omitted.
1045
+ * @param key Data key name, omitted returns complete state object
1046
+ */
1047
+ get<T = unknown>(key?: string): T;
1048
+ /**
1049
+ * Set global state data.
1050
+ * After set, must explicitly call `digest()` to dispatch changed event and notify views to update.
1051
+ * @param data Data object, e.g., `{ a: 1, b: 2 }`
1052
+ * @param excludes Set of keys to exclude from change tracking
1053
+ */
1054
+ set(data: Record<string, unknown>, excludes?: Set<string>): this;
1055
+ /**
1056
+ * Clean data for specified keys in State, can only be used in view's mixins.
1057
+ * For example `mixins: [State.clean("a,b")]`.
1058
+ * Keys registered via this method are automatically cleaned when view is destroyed,
1059
+ * and corresponding key reference counts are decremented; data is auto-deleted when count reaches zero.
1060
+ * @param keys Comma-separated key string
1061
+ * @returns Object with make method, called by mixins mechanism
1062
+ */
1063
+ clean(keys: string): {
1064
+ make: AnyFunc;
1065
+ };
1066
+ /**
1067
+ * Detect data changes and dispatch changed event.
1068
+ * After set, must explicitly call `digest()` to dispatch changed event and notify views to update.
1069
+ * @param data Optional data object, if provided calls `set()` first to set data
1070
+ * @param excludes Set of keys to exclude from change tracking
1071
+ */
1072
+ digest(data?: Record<string, unknown>, excludes?: Set<string>): void;
1073
+ /**
1074
+ * Triggered when state data changes.
1075
+ */
1076
+ diff: () => Readonly<Record<string, number>>;
1077
+ onChanged?: (e?: ChangeEvent) => void;
223
1078
  }
224
1079
  interface ServiceOptions {
225
1080
  /** Request URL */
@@ -227,14 +1082,14 @@ interface ServiceOptions {
227
1082
  /** Request params */
228
1083
  params?: Record<string, unknown>;
229
1084
  /** HTTP method */
230
- method?: "GET" | "POST" | "PUT" | "DELETE";
1085
+ method?: "GET" | "POST" | "PUT" | "DELETE" | string;
231
1086
  /** Cache: true for default, number for TTL */
232
1087
  cache?: boolean | number;
233
1088
  /** POST data */
234
1089
  data?: unknown;
235
1090
  }
236
- interface BagEntry {
237
- /** Bag data */
1091
+ interface PayloadEntry {
1092
+ /** Payload data */
238
1093
  data: Record<string, unknown>;
239
1094
  }
240
1095
  interface ServiceEntry {
@@ -244,8 +1099,8 @@ interface ServiceEntry {
244
1099
  url: string;
245
1100
  /** Cache time in ms, 0 = no cache */
246
1101
  cacheTime: number;
247
- /** Bag instance */
248
- bag?: BagEntry;
1102
+ /** Payload instance */
1103
+ payload?: PayloadEntry;
249
1104
  /** Whether loading */
250
1105
  loading?: boolean;
251
1106
  /** Error info */
@@ -253,26 +1108,54 @@ interface ServiceEntry {
253
1108
  }
254
1109
  /** Pending cache entry for deduplication (internal to Service) */
255
1110
  interface PendingCacheEntry extends Array<unknown> {
256
- /** Reference to the pending Bag entity */
257
- e?: unknown;
1111
+ /** Reference to the pending Payload entity */
1112
+ entity?: unknown;
258
1113
  }
1114
+ /**
1115
+ * Endpoint metadata configuration for registering an API endpoint with Service.
1116
+ * Each meta describes endpoint's URL, cache strategy, before/after interceptors, etc.
1117
+ */
259
1118
  interface ServiceMetaEntry {
260
- /** Endpoint name */
1119
+ /**
1120
+ * Endpoint name,
1121
+ * Unique name for endpoint metadata, must be unique within same Service.
1122
+ */
261
1123
  name: string;
262
- /** URL */
1124
+ /** Request URL, required. */
263
1125
  url: string;
264
- /** Cache TTL in ms, 0 = no cache */
1126
+ /**
1127
+ * Cache TTL in ms, 0 = no cache.
1128
+ * Cache validity time in milliseconds.
1129
+ * 0 means no caching.
1130
+ * Greater than 0 means cache TTL, reuse cached data within this time range.
1131
+ */
265
1132
  cache?: number;
266
- /** Before-fetch hook */
267
- before?: AnyFunc;
268
- /** After-fetch hook */
269
- after?: AnyFunc;
270
- /** Clean keys on destroy */
271
- cleans?: string;
1133
+ /**
1134
+ * Before-fetch hook,
1135
+ * Hook function called before request is sent, can process request data.
1136
+ * `this` points to current Payload instance.
1137
+ * @param payload Data carrier for current request
1138
+ */
1139
+ before?: (this: PayloadInterface, payload: PayloadInterface) => void;
1140
+ /**
1141
+ * After-fetch hook.
1142
+ * Hook function called after request succeeds, before data is passed to view.
1143
+ * Can process response data in this method.
1144
+ * `this` points to current Payload instance.
1145
+ * @param payload Data carrier for current request
1146
+ */
1147
+ after?: (this: PayloadInterface, payload: PayloadInterface) => void;
1148
+ /** Clean keys on destroy,
1149
+ * Comma-separated endpoint name string for clearing other endpoints' cache.
1150
+ * For example, if an endpoint creates new data,
1151
+ * after successful call, should clear all data-fetching endpoints' cache,
1152
+ * otherwise new data cannot be retrieved.
1153
+ */
1154
+ cleanKeys?: string;
272
1155
  /** Additional properties */
273
1156
  [key: string]: unknown;
274
1157
  }
275
- /** Cache info attached to Bag entity */
1158
+ /** Cache info attached to Payload entity */
276
1159
  interface ServiceCacheInfo {
277
1160
  /** Endpoint name */
278
1161
  name: string;
@@ -285,34 +1168,252 @@ interface ServiceCacheInfo {
285
1168
  /** Timestamp when cached */
286
1169
  time: number;
287
1170
  }
1171
+ interface FrameworkInterface {
1172
+ /**
1173
+ * Get or update configuration.
1174
+ * - When passing config object: merges config and returns merged config
1175
+ * - When passing key name: returns config value for that key
1176
+ * - When no arguments: returns complete config object
1177
+ * @param cfg Config object or key name
1178
+ */
1179
+ config<T extends object = Partial<FrameworkConfig>>(cfg?: Partial<FrameworkConfig> & T): FrameworkConfig & T;
1180
+ config(key: string): unknown;
1181
+ /**
1182
+ * App initialization entry point, starts framework and renders root view.
1183
+ * After invocation: merge config → bind route events → create root Frame → mount default view.
1184
+ * @param cfg Config object
1185
+ */
1186
+ boot(cfg: FrameworkConfig): void;
1187
+ /**
1188
+ * Convert array to hash map object.
1189
+ * - Simple array: `Framework.toMap([1,2,3])` => `{1:1, 2:1, 3:1}`
1190
+ * - Object array: `Framework.toMap([{id:20},{id:30}], 'id')` => `{20:{id:20}, 30:{id:30}}`
1191
+ * @param list Source array
1192
+ * @param key Use object's key value from array as map key
1193
+ */
1194
+ toMap<T>(list: T[] | null | undefined, key?: keyof T): Record<string, T | number>;
1195
+ /**
1196
+ * Execute methods in try-catch manner, catches exceptions.
1197
+ * Returns return value of last successfully executed method.
1198
+ * @param fns Function or function array
1199
+ * @param args Arguments array passed to functions
1200
+ * @param context `this` binding during function execution
1201
+ */
1202
+ toTry(fns: AnyFunc | AnyFunc[], args?: unknown[], context?: unknown): unknown;
1203
+ /**
1204
+ * Convert path and params to URL string.
1205
+ * Example: `Framework.toUrl('/xxx/', {a:'b',c:'d'})` => `/xxx/?a=b&c=d`
1206
+ * @param path Path string
1207
+ * @param params Params object
1208
+ * @param keepEmpty Set of keys to keep empty values
1209
+ */
1210
+ toUrl(path: string, params?: Record<string, unknown>, keepEmpty?: Record<string, number>): string;
1211
+ /**
1212
+ * Parse URL string to path and params object.
1213
+ * Example: `Framework.parseUrl('/xxx/?a=b&c=d')` => `{path:'/xxx/', params:{a:'b',c:'d'}}`
1214
+ * @param url URL string
1215
+ */
1216
+ parseUrl(url: string): ParsedUri;
1217
+ /**
1218
+ * Merge source object properties into target object.
1219
+ * @param target Target object
1220
+ * @param sources One or more source objects
1221
+ */
1222
+ mix<T extends object>(target: T, ...sources: Partial<T>[]): T;
1223
+ /**
1224
+ * Check if object has specified own property (safe hasOwnProperty).
1225
+ * @param owner Object to check, supports undefined/null
1226
+ * @param prop Property key name
1227
+ */
1228
+ has<T extends object>(owner: T | undefined | null, prop: PropertyKey): boolean;
1229
+ /**
1230
+ * Get enumerable property keys of object as array.
1231
+ * @param src Source object
1232
+ */
1233
+ keys<T extends object>(src: T): string[];
1234
+ /**
1235
+ * Check if one DOM node is contained within another.
1236
+ * Returns true if both nodes are the same.
1237
+ * @param node Node or node ID
1238
+ * @param container Container node or node ID
1239
+ */
1240
+ inside(node: HTMLElement | string, container: HTMLElement | string): boolean;
1241
+ /**
1242
+ * Shorthand for document.getElementById.
1243
+ * Returns directly if Element is passed.
1244
+ * @param id Node ID or Element object
1245
+ */
1246
+ node(id: string | Element | null): Element | null;
1247
+ /**
1248
+ * Ensure DOM element has an ID, auto-generates one if missing.
1249
+ * Returns element's ID.
1250
+ * @param node DOM element object
1251
+ */
1252
+ nodeId(node: HTMLElement): string;
1253
+ /**
1254
+ * Load modules using configured module loader.
1255
+ * @param names Module names, supports string or string array
1256
+ * @param callback Callback after modules are loaded
1257
+ */
1258
+ use(names: string | string[], callback?: (...modules: unknown[]) => void): void;
1259
+ /**
1260
+ * Protect object from direct modification in debug mode.
1261
+ * Wraps data object with Proxy, intercepts read/write operations, warns to use State.set/digest for state management.
1262
+ * Only effective when `window.__lark_Debug` is true.
1263
+ * @param o Object to protect
1264
+ */
1265
+ guard<T extends object>(o: T): T;
1266
+ /**
1267
+ * Dynamically inject CSS styles into page. Returns cleanup function to remove injected styles.
1268
+ * Supports single and batch injection.
1269
+ * - `Framework.applyStyle("my-style", "body { color: red; }")` single injection
1270
+ * - `Framework.applyStyle(["style1", "css1", "style2", "css2"])` batch injection
1271
+ * @param styleIdOrPairs Style unique key or [id1, css1, id2, css2, ...] batch array
1272
+ * @param cssText CSS style string (only used when first param is string)
1273
+ */
1274
+ applyStyle(styleIdOrPairs: string | string[], cssText?: string): () => void;
1275
+ /**
1276
+ * Generate globally unique identifier (GUID).
1277
+ * @param prefix GUID prefix, defaults to "lark-"
1278
+ */
1279
+ guid(prefix?: string): string;
1280
+ /**
1281
+ * Create async callback validity marker.
1282
+ * Returns a check function; if host object is unmarked (e.g., view re-rendered), check function returns false, preventing expired async callbacks from executing.
1283
+ * Typical usage: `const check = Framework.mark(this, 'render'); setTimeout(() => { if (check()) ... })`
1284
+ * @param host Host object (usually view instance)
1285
+ * @param key Marker key name (usually "render" or specific async operation identifier)
1286
+ */
1287
+ mark(host: object, key: string): () => boolean;
1288
+ /**
1289
+ * Delay wait, Promise-based setTimeout wrapper.
1290
+ * @param time Delay time in milliseconds
1291
+ */
1292
+ delay(time: number): Promise<void>;
1293
+ /**
1294
+ * Whether framework has booted
1295
+ */
1296
+ isBooted(): boolean;
1297
+ /**
1298
+ * Invalidate (unmark) async callback markers for a host object.
1299
+ * Typically called when a view is re-rendered or destroyed.
1300
+ * @param host The host object whose markers should be invalidated
1301
+ */
1302
+ unmark(host: object): void;
1303
+ /**
1304
+ * Fire a custom DOM event on a target element.
1305
+ * @param target Target element or EventTarget
1306
+ * @param eventType Event type string
1307
+ * @param eventInit CustomEvent init options
1308
+ */
1309
+ dispatch(target: EventTarget, eventType: string, eventInit?: CustomEventInit): void;
1310
+ /**
1311
+ * Execute a function in try-catch with chunked scheduling.
1312
+ * @param fn Function to execute
1313
+ * @param args Arguments to pass
1314
+ * @param context `this` context
1315
+ */
1316
+ task(fn: AnyFunc, args?: unknown[], context?: unknown): void;
1317
+ /**
1318
+ * Wait for all views in a zone to be rendered.
1319
+ * Returns WAIT_OK if rendered, WAIT_TIMEOUT_OR_NOT_FOUND if timeout or not found.
1320
+ * @param viewId Zone view ID
1321
+ * @param timeout Timeout in milliseconds (default: 30000)
1322
+ */
1323
+ waitZoneViewsRendered(viewId: string, timeout?: number): Promise<number>;
1324
+ /** Wait result: views rendered successfully */
1325
+ WAIT_OK: number;
1326
+ /** Wait result: timeout or view not found */
1327
+ WAIT_TIMEOUT_OR_NOT_FOUND: number;
1328
+ /**
1329
+ * Base class with EventEmitter.
1330
+ * Inherits EventEmitter for use as a base class in the framework.
1331
+ */
1332
+ Base: typeof EventEmitter;
1333
+ /**
1334
+ * View class.
1335
+ * Use `View.extend()` to create subclasses.
1336
+ */
1337
+ View: typeof View;
1338
+ /**
1339
+ * Cache class.
1340
+ * Use `new Cache()` to create cache instances.
1341
+ */
1342
+ Cache: typeof Cache;
1343
+ /**
1344
+ * Global state object.
1345
+ */
1346
+ State: StateInterface;
1347
+ /**
1348
+ * Router object.
1349
+ */
1350
+ Router: RouterInterface;
1351
+ /**
1352
+ * Frame class.
1353
+ * Frame tree for view lifecycle management. Do not extend or instantiate directly.
1354
+ */
1355
+ Frame: typeof Frame;
1356
+ }
1357
+ /**
1358
+ * Framework configuration interface, global config passed to app during `Framework.boot()`.
1359
+ * All config items can be accessed at runtime via `Framework.config('key')`.
1360
+ */
288
1361
  interface FrameworkConfig {
289
- /** Root element ID */
1362
+ /**
1363
+ * Root element ID.
1364
+ * DOM root node ID where root view resides, framework renders root view within this node.
1365
+ * This field is required, defaults to "root".
1366
+ */
290
1367
  rootId: string;
291
- /** Default view path */
1368
+ /**
1369
+ * Default view path.
1370
+ * Default root view path to load when URL doesn't match any route.
1371
+ */
292
1372
  defaultView?: string;
293
- /** Default path when no hash present */
1373
+ /**
1374
+ * Default path when no hash present,
1375
+ * Path used when URL hash is empty, defaults to "/".
1376
+ */
294
1377
  defaultPath?: string;
295
- /** Route mapping: path -> view */
1378
+ /**
1379
+ * Route mapping: path -> view.
1380
+ * Mapping relationship between paths and views.
1381
+ * - Simple mapping: `{ "/home": "app/views/home" }`
1382
+ * - Config mapping: `{ "/detail": { view: "app/views/detail", title: "Detail" } }`
1383
+ * Use rewrite config item for path rewriting logic.
1384
+ */
296
1385
  routes?: Record<string, string | RouteViewConfig>;
297
1386
  /** Hashbang prefix */
298
1387
  hashbang?: string;
299
- /** Error handler */
1388
+ /**
1389
+ * Error handler.
1390
+ * Global error handling function, framework uses try-catch to execute some core logic.
1391
+ * When errors are thrown, allows developers to catch them via this config item.
1392
+ * Note: Do not re-throw any errors in this method.
1393
+ */
300
1394
  error?: (error: Error) => void;
301
- /** Extensions to load */
302
- exts?: string[];
1395
+ /**
1396
+ * Extensions to load.
1397
+ * Array of extension view paths to load during app startup.
1398
+ * These extension views are loaded before app initialization.
1399
+ */
1400
+ extensions?: string[];
303
1401
  /** Init module to load */
304
- ini?: string;
1402
+ initModule?: string;
305
1403
  /** Rewrite function for routes */
306
1404
  rewrite?: (path: string, params: Record<string, string>, routes: Record<string, string>) => string;
307
- /** Unmatched view (404) */
1405
+ /**
1406
+ * Unmatched view (404).
1407
+ * View path to use when no matching view is found in routes, e.g., 404 page.
1408
+ */
308
1409
  unmatchedView?: string;
309
- /** Module require function */
1410
+ /**
1411
+ * Module require function
1412
+ */
310
1413
  require?: (names: string[], params?: Record<string, unknown>) => Promise<unknown> | undefined;
311
1414
  /** Skip view rendered check */
312
1415
  skipViewRendered?: boolean;
313
- /** Remold function for event processing */
314
- remold?: (target: HTMLElement, type: string, event: Event) => boolean;
315
- /** Dynamic config access */
1416
+ /** Dynamic config access, custom config items */
316
1417
  [key: string]: unknown;
317
1418
  }
318
1419
  interface RouteViewConfig {
@@ -333,7 +1434,7 @@ interface VdomElement extends Element {
333
1434
  /** Element with frame binding */
334
1435
  interface FrameBoundElement extends HTMLElement {
335
1436
  /** Frame instance bound to this element */
336
- frame?: FrameLike;
1437
+ frame?: FrameInterface;
337
1438
  /** Whether frame is bound (1 = bound) */
338
1439
  frameBound?: number;
339
1440
  /** View rendered flag */
@@ -343,6 +1444,15 @@ interface FrameBoundElement extends HTMLElement {
343
1444
  /** Range element guid */
344
1445
  rangeElementGuid?: number;
345
1446
  }
1447
+ /** Options for compileTemplate() */
1448
+ interface CompileOptions {
1449
+ /** Enable debug mode with line tracking (default: false) */
1450
+ debug?: boolean;
1451
+ /** Global variable names to destructure from $$ (refData) */
1452
+ globalVars?: string[];
1453
+ /** File path for debug error messages (default: undefined) */
1454
+ file?: string;
1455
+ }
346
1456
 
347
1457
  /**
348
1458
  * Lark framework utility functions.
@@ -350,8 +1460,6 @@ interface FrameBoundElement extends HTMLElement {
350
1460
 
351
1461
  /** Check if value is a plain object (not null, not array, typeof object) */
352
1462
  declare function isPlainObject(value: unknown): value is Record<string, unknown>;
353
- /** Check if value is an array */
354
- declare const isArray: (arg: any) => arg is any[];
355
1463
  /** Check if value is primitive or function (not a complex object) */
356
1464
  declare function isPrimitiveOrFunc(value: unknown): boolean;
357
1465
  /** Check if value is primitive (not object, not function) */
@@ -370,7 +1478,7 @@ declare function assign<T extends object>(target: T, ...sources: Partial<T>[]):
370
1478
  * Execute functions in try-catch, ignoring errors.
371
1479
  * Returns the result of the last successfully executed function.
372
1480
  */
373
- declare function funcWithTry(fns: AnyFunc | AnyFunc[], args: unknown[], context: unknown, configError: (e: unknown) => void): unknown;
1481
+ declare function funcWithTry(fns: AnyFunc | AnyFunc[], args: unknown[], context: unknown, configError?: (e: unknown) => void): unknown;
374
1482
  /**
375
1483
  * Set newData into oldData, tracking changed keys.
376
1484
  * Returns whether any value changed.
@@ -417,7 +1525,7 @@ type Constructable = new (...args: never[]) => unknown;
417
1525
  * This is the only place where we touch constructor internals -
418
1526
  * it's a low-level framework utility for View.extend.
419
1527
  */
420
- declare function classExtend<Proto extends object, Statics extends object>(ctor: Constructable, base: Constructable, props: Proto, statics: Statics): void;
1528
+ declare function classExtend<Proto extends object, Statics extends object>(make: Constructable, base: Constructable, props: Proto, statics: Statics): void;
421
1529
 
422
1530
  /** Internal splitter character (invisible, used as namespace separator) */
423
1531
  declare const SPLITTER = "\u001E";
@@ -426,14 +1534,8 @@ declare const ROUTER_EVENTS: {
426
1534
  CHANGED: string;
427
1535
  PAGE_UNLOAD: string;
428
1536
  };
429
- /** Attribute name: lark-key (static key for skipping VDOM diff) */
430
- declare const TAG_KEY = "lark-key";
431
- /** Attribute name: lark-attr-key (static attribute key) */
432
- declare const TAG_ATTR_KEY = "lark-attr-key";
433
- /** Attribute name: l (view key for assign) */
434
- declare const TAG_VIEW_KEY = "lark-view-key";
435
- /** Attribute name: lark-view */
436
- declare const LARK_VIEW = "lark-view";
1537
+ /** Attribute name: v-lark */
1538
+ declare const LARK_VIEW = "v-lark";
437
1539
  /** View event method regex: e.g. "app\x1eclickHandler(click)" or "clickHandler()"
438
1540
  * Group 1: optional frame ID (before SPLITTER)
439
1541
  * Group 2: handler name
@@ -483,400 +1585,49 @@ declare function mark$1(host: object, key: string): () => boolean;
483
1585
  declare function unmark$1(host: object): void;
484
1586
 
485
1587
  /**
486
- * Safeguard: Proxy-based debug protection for data objects.
487
- *
488
- * In DEBUG mode, wraps data objects with Proxy to:
489
- * 1. Warn when data is read from a different page than where it was set
490
- * 2. Prevent direct mutation (forces use of State.set/digest)
491
- * 3. Track access patterns for debugging
492
- */
493
- /**
494
- * Wrap data with a Proxy for debug-mode protection.
495
- * Only active when window.__lark_debug is true and Proxy is available.
496
- *
497
- * @param data - Data to wrap
498
- * @param getter - Optional callback when properties are read
499
- * @param setter - Optional callback when properties are written
500
- * @param isRoot - Whether this is the root data object
501
- * @returns Proxied data or original data if debug mode is off
502
- */
503
- declare function safeguard<T>(data: T, getter?: ((key: string) => void) | null, setter?: ((path: string, value: unknown) => void) | null, isRoot?: boolean): T;
504
-
505
- /**
506
- * Cache class with LFU-style eviction.
507
- * Keys are prefixed with SPLITTER for namespace isolation.
508
- *
509
- * @example
510
- * const cache = new Cache({ maxSize: 20, bufferSize: 5 });
511
- * cache.set('user', { name: 'Alice' });
512
- * const user = cache.get('user');
513
- * cache.has('user'); // true
514
- * cache.del('user');
515
- */
516
- declare class Cache<T = unknown> {
517
- /** Cache entries array */
518
- private entries;
519
- /** Fast lookup: prefixed key -> entry */
520
- private lookup;
521
- /** Buffer size for eviction */
522
- private readonly bufferSize;
523
- /** Maximum cache size */
524
- private readonly maxSize;
525
- /** Total capacity (maxSize + bufferSize) */
526
- private readonly capacity;
527
- /** Callback when entry is removed */
528
- private readonly onRemove?;
529
- /** Sort comparator */
530
- private readonly comparator;
531
- constructor(options?: CacheOptions<T>);
532
- /** Prefix a key with SPLITTER for namespace isolation */
533
- private prefixKey;
534
- /**
535
- * Get a cached value by key.
536
- * Updates frequency and timestamp for cache sorting.
537
- */
538
- get(key: string): T | undefined;
539
- /**
540
- * Iterate all cached values.
541
- */
542
- forEach(callback: (value: T | undefined) => void): void;
543
- /**
544
- * Set or update a cached value.
545
- * If key already exists, updates value and increments frequency.
546
- * If cache exceeds capacity, triggers eviction.
547
- */
548
- set(key: string, value: T): void;
549
- /**
550
- * Delete a cached entry.
551
- */
552
- del(key: string): void;
553
- /**
554
- * Check if a key exists in cache.
555
- */
556
- has(key: string): boolean;
557
- /** Get current cache size */
558
- get size(): number;
559
- /** Clear all entries */
560
- clear(): void;
561
- /** Evict least-used entries from cache */
562
- private evictEntries;
563
- }
564
-
565
- /**
566
- * Multi-cast event emitter class.
567
- *
568
- * @example
569
- * const emitter = new EventEmitter();
570
- * emitter.on('change', (data) => console.log(data));
571
- * emitter.fire('change', { key: 'value' });
572
- */
573
- declare class EventEmitter {
574
- /** Event listeners: prefixed key -> listener array */
575
- private listeners;
576
- /**
577
- * Bind event listener.
578
- */
579
- on(event: string, handler: AnyFunc): this;
580
- /**
581
- * Unbind event listener.
582
- * If handler is provided, removes only that handler.
583
- * If no handler, removes all handlers for the event.
584
- */
585
- off(event: string, handler?: AnyFunc): this;
586
- /**
587
- * Fire event, execute all bound handlers.
588
- * Supports executing state management: handlers removed during
589
- * execution are safely handled.
590
- *
591
- * @param event - Event name
592
- * @param data - Event data (type property added automatically)
593
- * @param remove - Whether to remove all handlers after firing
594
- * @param lastToFirst - Whether to execute handlers in reverse order
595
- */
596
- fire(event: string, data?: Record<string, unknown>, remove?: boolean, lastToFirst?: boolean): this;
597
- }
598
-
599
- /** Mark framework as booted (called from Framework.boot) */
600
- declare function markBooted(): void;
601
- /**
602
- * Observable in-memory data object.
603
- * Provides get/set/digest/diff/clean methods for cross-view data sharing.
604
- */
605
- declare const State: {
606
- get<T = unknown>(key?: string): T;
607
- set(data: Record<string, unknown>, excludes?: Set<string>): typeof State;
608
- digest(data?: Record<string, unknown>, excludes?: Set<string>): void;
609
- diff(): Readonly<Record<string, number>>;
610
- clean(keys: string): {
611
- ctor: AnyFunc;
612
- };
613
- on(event: string, handler: AnyFunc): typeof State;
614
- off(event: string, handler?: AnyFunc): typeof State;
615
- fire(event: string, data?: Record<string, unknown>, remove?: boolean): typeof State;
616
- };
617
-
618
- /**
619
- * Hash-based router with two-phase change confirmation.
620
- *
621
- * @example
622
- * Router.to('/list', { page: 2 });
623
- * const loc = Router.parse();
624
- * const diff = Router.diff();
625
- */
626
- declare const Router: {
627
- /** Parse href into Location object */
628
- parse(href?: string): Location;
629
- /** Compute diff between current and previous location */
630
- diff(): LocationDiff | undefined;
631
- /** Navigate to new URL */
632
- to(pathOrParams: string | Record<string, unknown>, params?: Record<string, unknown>, replace?: boolean, silent?: boolean): void;
633
- /** Join path segments */
634
- join(...paths: string[]): string;
635
- /** Bind event listener */
636
- on(event: string, handler: AnyFunc): typeof Router;
637
- /** Unbind event listener */
638
- off(event: string, handler?: AnyFunc): typeof Router;
639
- /** Fire event */
640
- fire(event: string, data?: Record<string, unknown>, remove?: boolean): typeof Router;
641
- /** Internal: bind hashchange (called by Framework.boot) */
642
- _bind(): void;
643
- /** Internal: set framework config */
644
- _setConfig(cfg: FrameworkConfig): void;
645
- /** Internal: notify hash change (for programmatic trigger) */
646
- notify?(e?: Event): void;
647
- };
648
- /** Mark framework as booted (called by Framework.boot) */
649
- declare function markRouterBooted(): void;
650
-
651
- /**
652
- * Base View class.
653
- * Views are created via View.extend() and mounted by Frame.
654
- *
655
- */
656
- declare class View {
657
- /** View ID (same as owner frame ID) */
658
- id: string;
659
- /** Owner frame */
660
- owner: FrameLike | number;
661
- /** Updater instance */
662
- updater: UpdaterLike;
663
- /** Signature: > 0 means active, incremented on render, 0 = destroyed */
664
- signature: number;
665
- /** Whether rendered at least once */
666
- rendered?: boolean;
667
- /** Whether view has template */
668
- template?: AnyFunc | string;
669
- /** Location observation config */
670
- locationObserved: ViewLocationObserved;
671
- /** Observed state keys */
672
- observedStateKeys?: string[];
673
- /** Resource map */
674
- resources: ViewResourceMap;
675
- /** Selector event map: eventType -> handler name list */
676
- eventSelectorMap: ViewEventSelectorMap;
677
- /** Event object map: eventType -> bitmask */
678
- eventObjectMap: ViewEventObjectMap;
679
- /** Global event list */
680
- globalEventList: ViewGlobalEventEntry[];
681
- /** Assign method reference */
682
- assignMethod?: AnyFunc;
683
- /** Whether endUpdate pending */
684
- endUpdatePending?: number;
685
- /** Internal event storage */
686
- private _events;
687
- /**
688
- * Initialize view (called by Frame when mounting).
689
- */
690
- init(): void;
691
- /**
692
- * Render view template (called by Frame after init).
693
- * Wrapped by View_WrapMethod to manage signature + resources.
694
- *
695
- * Default implementation calls updater.digest() which:
696
- * 1. Executes the template function with current data
697
- * 2. Runs VDOM diff against previous DOM
698
- * 3. Applies DOM operations
699
- * 4. Calls endUpdate to mount child frames
700
- *
701
- */
702
- render(): void;
703
- on(event: string, handler: AnyFunc): this;
704
- off(event: string, handler?: AnyFunc): this;
705
- fire(event: string, data?: Record<string, unknown>, remove?: boolean, lastToFirst?: boolean): this;
706
- /**
707
- * Notify view that HTML update is about to begin.
708
- * Unmounts child frames in the update zone.
709
- */
710
- beginUpdate(id?: string): void;
711
- /**
712
- * Notify view that HTML update has ended.
713
- * Mounts child frames in the update zone and runs deferred invokes.
714
- */
715
- endUpdate(id?: string, inner?: boolean): void;
716
- /**
717
- * Wrap an async callback to check view signature before executing.
718
- * If the view has been re-rendered or destroyed, the callback is skipped.
719
- */
720
- wrapAsync<Fn extends AnyFunc>(fn: Fn, context?: unknown): (...args: Parameters<Fn>) => ReturnType<Fn> | undefined;
721
- /**
722
- * Observe location parameters or path changes.
723
- * When observed keys change, render() is called automatically.
724
- */
725
- observeLocation(params: string | string[] | Record<string, unknown>, observePath?: boolean): void;
726
- /**
727
- * Observe State data keys for changes.
728
- * When observed keys change via State.digest(), render() is called.
729
- */
730
- observeState(observedKeys: string | string[]): void;
731
- /**
732
- * Capture (register) a resource under a key.
733
- * If a resource already exists at that key, it's destroyed first.
734
- * When destroyOnRender=true, the resource is destroyed on next render call.
735
- */
736
- capture(key: string, resource?: unknown, destroyOnRender?: boolean): unknown;
737
- /**
738
- * Release a captured resource.
739
- * If destroy=true, calls the resource's destroy() method.
740
- */
741
- release(key: string, destroy?: boolean): unknown;
742
- /**
743
- * Set up a leave confirmation for route changes and page unload.
744
- */
745
- leaveTip(message: string, condition: () => boolean): void;
746
- /** Collected ctors from mixins */
747
- static ctors?: AnyFunc[];
748
- /**
749
- * Extend View to create a new View subclass.
750
- *
751
- * Supports:
752
- * - props.ctor: constructor-like init (called with initParams + {node, deep})
753
- * - props.mixins: array of mixin objects
754
- * - Event method patterns: `'name<click>'` etc.
755
- */
756
- static extend(props?: ThisType<ViewInstance> & Record<string, unknown>, statics?: Record<string, unknown>): typeof View;
757
- /**
758
- * Merge mixins into View prototype.
759
- */
760
- static merge(...mixins: Record<string, unknown>[]): typeof View;
761
- }
762
-
1588
+ * Safeguard: Proxy-based debug protection for data objects.
1589
+ *
1590
+ * In DEBUG mode, wraps data objects with Proxy to:
1591
+ * 1. Warn when data is read from a different page than where it was set
1592
+ * 2. Prevent direct mutation (forces use of State.set/digest)
1593
+ * 3. Track access patterns for debugging
1594
+ */
763
1595
  /**
764
- * Frame (View Frame) class for view lifecycle management.
765
- * Each frame owns a view and manages child frames.
1596
+ * Wrap data with a Proxy for debug-mode protection.
1597
+ * Only active when window.__lark_Debug is true and Proxy is available.
766
1598
  *
1599
+ * @param data - Data to wrap
1600
+ * @param getter - Optional callback when properties are read
1601
+ * @param setter - Optional callback when properties are written
1602
+ * @param isRoot - Whether this is the root data object
1603
+ * @returns Proxied data or original data if debug mode is off
767
1604
  */
768
- declare class Frame extends EventEmitter implements FrameLike {
769
- /** Frame ID (same as owner DOM element ID) */
770
- readonly id: string;
771
- /** Parent Frame ID */
772
- private _parentId;
773
- get parentId(): string | undefined;
774
- /** Children map: id -> id */
775
- childrenMap: FrameChildrenMap;
776
- /** Children count */
777
- childrenCount: number;
778
- /** Ready count (children that have fired 'created') */
779
- readyCount: number;
780
- /** Ready map: id -> 1 */
781
- readyMap: FrameReadyMap;
782
- /** View instance */
783
- viewInstance?: ViewInstance;
784
- /** Get view instance (read-only) */
785
- get view(): ViewInstance | undefined;
786
- /** Invoke list for deferred method calls */
787
- invokeList: FrameInvokeEntry[];
788
- /** Signature for async operation tracking */
789
- signature: number;
790
- /** Whether view has altered */
791
- hasAltered: number;
792
- /** Whether view is destroyed */
793
- destroyed: number;
794
- /** View path (lark-view attribute value) */
795
- viewPath?: string;
796
- /** Original template before mount */
797
- originalTemplate?: string;
798
- /** Hold fire created flag */
799
- holdFireCreated: number;
800
- /** Children created flag */
801
- childrenCreated: number;
802
- /** Children alter flag */
803
- childrenAlter: number;
804
- constructor(id: string, parentId?: string);
805
- /**
806
- * Mount a view to this frame.
807
- *
808
- * Complete flow:
809
- * 1. Parse viewPath, translate query params from parent
810
- * 2. Unmount current view
811
- * 3. Load View class (via require or provided ViewClass)
812
- * 4. View_Prepare (scan event methods)
813
- * 5. Create View instance
814
- * 6. View_DelegateEvents (bind DOM events)
815
- * 7. Call view.init()
816
- * 8. If view has template, call render via Updater
817
- * 9. If no template, call endUpdate directly
818
- */
819
- mountView(viewPath: string, viewInitParams?: Record<string, unknown>): void;
820
- /**
821
- * Internal: actually mount the view after class is loaded.
822
- */
823
- doMountView(ViewClass: typeof View, params: Record<string, string>, node: HTMLElement, sign: number): void;
824
- /**
825
- * Unmount current view.
826
- */
827
- unmountView(): void;
828
- /**
829
- * Mount a child frame.
830
- */
831
- mountFrame(frameId: string, viewPath: string, viewInitParams?: Record<string, unknown>): FrameLike;
832
- /**
833
- * Unmount a child frame.
834
- */
835
- unmountFrame(id?: string, _inner?: boolean): void;
836
- /**
837
- * Mount all views in a zone.
838
- */
839
- mountZone(zoneId?: string, _inner?: boolean): void;
840
- /**
841
- * Unmount all views in a zone.
842
- */
843
- unmountZone(zoneId?: string, _inner?: boolean): void;
844
- /**
845
- * Get all child frame IDs.
846
- */
847
- children(): string[];
848
- /**
849
- * Get parent frame at given level.
850
- * @param level - How many levels up (default 1)
851
- */
852
- parent(level?: number): Frame | undefined;
853
- /**
854
- * Invoke a method on the view.
855
- */
856
- invoke(name: string, args?: unknown[]): unknown;
857
- /** Get frame by ID */
858
- static get(id: string): Frame | undefined;
859
- /** Get all frames */
860
- static getAll(): Map<string, Frame>;
861
- /** Get or create root frame */
862
- static root(rootId?: string): Frame;
863
- /** Bind event listener (static) */
864
- static on(event: string, handler: AnyFunc): typeof Frame;
865
- /** Unbind event listener (static) */
866
- static off(event: string, handler?: AnyFunc): typeof Frame;
867
- /** Fire event (static) */
868
- static fire(event: string, data?: Record<string, unknown>): void;
869
- }
1605
+ declare function safeguard<T>(data: T, getter?: ((key: string) => void) | null, setter?: ((path: string, value: unknown) => void) | null, isRoot?: boolean): T;
1606
+
1607
+ /** Mark framework as booted (called from Framework.boot) */
1608
+ declare function markBooted(): void;
870
1609
  /**
871
- * Register a View class for a given view path.
872
- * Called after module loading completes.
1610
+ * Observable in-memory data object.
1611
+ * Provides get/set/digest/diff/clean methods for cross-view data sharing.
873
1612
  */
874
- declare function registerViewClass(viewPath: string, ViewClass: typeof View): void;
1613
+ declare const State: StateInterface;
1614
+
1615
+ /**
1616
+ * Hash-based router with two-phase change confirmation.
1617
+ *
1618
+ * @example
1619
+ * Router.to('/list', { page: 2 });
1620
+ * const loc = Router.parse();
1621
+ * const diff = Router.diff();
1622
+ */
1623
+ declare const Router: RouterInterface;
1624
+ /** Mark framework as booted (called by Framework.boot) */
1625
+ declare function markRouterBooted(): void;
875
1626
 
876
1627
  /**
877
1628
  * Unmount frames within a DOM node.
878
1629
  */
879
- declare function vdomUnmountFrames(frame: FrameLike, node: ChildNode): void;
1630
+ declare function vdomUnmountFrames(frame: FrameInterface, node: ChildNode): void;
880
1631
  /**
881
1632
  * Parse HTML string into a DOM element.
882
1633
  * Handles special elements (table, SVG, MathML) with wrapper elements.
@@ -884,7 +1635,7 @@ declare function vdomUnmountFrames(frame: FrameLike, node: ChildNode): void;
884
1635
  declare function vdomGetNode(html: string, refNode: Element): Element;
885
1636
  /**
886
1637
  * Get compare key for a DOM node (for keyed diff).
887
- * Uses id, lark-key (static key), or lark-view path.
1638
+ * Uses id, ldk (static key), or v-lark path.
888
1639
  */
889
1640
  declare function vdomGetCompareKey(node: ChildNode): string | undefined;
890
1641
  /**
@@ -898,11 +1649,11 @@ declare function vdomSetAttributes(oldNode: Element, newNode: Element, ref: VDom
898
1649
  /**
899
1650
  * Set child nodes from new parent onto old parent using keyed diff algorithm.
900
1651
  */
901
- declare function vdomSetChildNodes(oldParent: Element, newParent: Element, ref: VDomRef, frame: FrameLike, keys_?: Record<string, number>): void;
1652
+ declare function vdomSetChildNodes(oldParent: Element, newParent: Element, ref: VDomRef, frame: FrameInterface, keys_?: Record<string, number>): void;
902
1653
  /**
903
1654
  * Diff two DOM nodes and apply changes.
904
1655
  */
905
- declare function vdomSetNode(oldNode: ChildNode, newNode: ChildNode, oldParent: Element, ref: VDomRef, frame: FrameLike, keys_?: Record<string, number>): void;
1656
+ declare function vdomSetNode(oldNode: ChildNode, newNode: ChildNode, oldParent: Element, ref: VDomRef, frame: FrameInterface, keys_?: Record<string, number>): void;
906
1657
  /**
907
1658
  * Create an empty VDomRef for tracking diff operations.
908
1659
  */
@@ -929,7 +1680,7 @@ declare function encodeQ(v: unknown): string;
929
1680
  * Manages view-local data with change detection and VDOM diff triggering.
930
1681
  *
931
1682
  */
932
- declare class Updater implements UpdaterLike {
1683
+ declare class Updater implements UpdaterInterface {
933
1684
  /** View ID (same as owner frame ID) */
934
1685
  private viewId;
935
1686
  /** Current data object */
@@ -994,257 +1745,199 @@ declare class Updater implements UpdaterLike {
994
1745
  }
995
1746
 
996
1747
  /**
997
- * Bag wraps API response data with convenient access methods.
1748
+ * Payload wraps API response data with convenient access methods.
998
1749
  */
999
- declare class Bag {
1000
- /** Bag data */
1750
+ declare class Payload implements PayloadInterface {
1751
+ /** Payload data */
1001
1752
  data: Record<string, unknown>;
1002
1753
  /** Internal cache info */
1003
1754
  cacheInfo?: ServiceCacheInfo;
1004
1755
  constructor(data?: Record<string, unknown>);
1005
- /** Get a value from bag data */
1756
+ /** Get a value from payload data */
1006
1757
  get<T = unknown>(key: string): T;
1007
- /** Set a value in bag data */
1008
- set(keyOrData: string | Record<string, unknown> | ServiceMetaEntry, value?: unknown): this;
1758
+ /** Set a value in payload data */
1759
+ set(keyOrData: string | Record<string, unknown> | ServiceMetaEntry, value?: unknown): PayloadInterface;
1009
1760
  }
1010
- interface ServiceConstructor {
1011
- new (): ServiceInstance;
1012
- add(attrs: ServiceMetaEntry | ServiceMetaEntry[]): void;
1013
- meta(attrs: string | Record<string, unknown>): ServiceMetaEntry;
1014
- create(attrs: Record<string, unknown>): Bag;
1015
- get(attrs: Record<string, unknown>, createNew?: boolean): {
1016
- entity: Bag;
1017
- needsUpdate: boolean;
1761
+ /**
1762
+ * Minimal interface describing what serviceSend actually uses
1763
+ * from a service instance. This avoids coupling to the full
1764
+ * ServiceInterface which mixes instance and static methods.
1765
+ */
1766
+ interface ServiceSendTarget {
1767
+ destroyed: number;
1768
+ busy: number;
1769
+ internals: {
1770
+ metaList: Record<string, ServiceMetaEntry>;
1771
+ payloadCache: Cache<Payload>;
1772
+ pendingCacheKeys: Record<string, PendingCacheEntry>;
1773
+ syncFn: (payload: Payload, callback: () => void) => void;
1774
+ staticEmitter: EventEmitter;
1018
1775
  };
1019
- cached(attrs: Record<string, unknown>): Bag | undefined;
1020
- clear(names: string | string[]): void;
1021
- on(event: string, handler: AnyFunc): void;
1022
- off(event: string, handler?: AnyFunc): void;
1023
- fire(event: string, data?: Record<string, unknown>): void;
1024
- extend(newSyncFn: (bag: Bag, callback: () => void) => void, newCacheMax?: number, newCacheBuffer?: number): ServiceConstructor;
1025
- _internals: ServiceInternals;
1026
- }
1027
- interface ServiceInstance {
1028
- id: string;
1029
- [key: string]: unknown;
1030
- _emitter: EventEmitter;
1031
- _internals: ServiceInternals;
1032
- _type: ServiceStaticsInternal;
1033
- all(attrs: string | Record<string, unknown> | (string | Record<string, unknown>)[], done: AnyFunc): ServiceInstance;
1034
- one(attrs: string | Record<string, unknown> | (string | Record<string, unknown>)[], done: AnyFunc): ServiceInstance;
1035
- save(attrs: string | Record<string, unknown> | (string | Record<string, unknown>)[], done: AnyFunc): ServiceInstance;
1036
- enqueue(callback: AnyFunc): ServiceInstance;
1037
- dequeue(...args: unknown[]): void;
1038
- destroy(): void;
1039
- on(event: string, handler: AnyFunc): ServiceInstance;
1040
- off(event: string, handler?: AnyFunc): ServiceInstance;
1041
- fire(event: string, data?: Record<string, unknown>): ServiceInstance;
1042
- }
1043
- interface ServiceInternals {
1044
- metas: Record<string, ServiceMetaEntry>;
1045
- bagCache: Cache<Bag>;
1046
- pendingCacheKeys: Record<string, PendingCacheEntry>;
1047
- syncFn: (bag: Bag, callback: () => void) => void;
1048
- staticEmitter: EventEmitter;
1049
- }
1050
- /** Internal type for serviceType object (static methods) */
1051
- interface ServiceStaticsInternal {
1052
- add(attrs: ServiceMetaEntry | ServiceMetaEntry[]): void;
1053
- meta(attrs: string | Record<string, unknown>): ServiceMetaEntry;
1054
- create(attrs: Record<string, unknown>): Bag;
1055
- get(attrs: Record<string, unknown>, createNew?: boolean): {
1056
- entity: Bag;
1057
- needsUpdate: boolean;
1776
+ type: {
1777
+ get(attrs: Record<string, unknown>, createNew?: boolean): {
1778
+ entity: Payload;
1779
+ needsUpdate: boolean;
1780
+ };
1058
1781
  };
1059
- cached(attrs: Record<string, unknown>): Bag | undefined;
1060
- clear(names: string | string[]): void;
1061
- on(event: string, handler: AnyFunc): void;
1062
- off(event: string, handler?: AnyFunc): void;
1063
- fire(event: string, data?: Record<string, unknown>): void;
1064
- extend(newSyncFn: (bag: Bag, callback: () => void) => void, newCacheMax?: number, newCacheBuffer?: number): ServiceConstructor;
1782
+ enqueue(callback: AnyFunc): unknown;
1065
1783
  }
1066
1784
  /**
1067
- * Default Service base class.
1068
- * Use Service.extend() to create a usable subclass with a sync function.
1069
- */
1070
- declare const Service: ServiceConstructor;
1071
-
1072
- /**
1073
- * DOM event delegation system.
1074
- * Delegates events to document body for performance.
1785
+ * Service: API request management with caching, deduplication, and queue.
1075
1786
  *
1787
+ * - Service.extend(syncFn, cacheMax?, cacheBuffer?): creates subclass with sync function
1788
+ * - Service.add(attrs): registers API endpoint metadata
1789
+ * - new Service().all(attrs, done): fetch all, use cache when available
1790
+ * - new Service().one(attrs, done): fetch all, callback on each completion
1791
+ * - new Service().save(attrs, done): fetch all, skip cache (always request)
1792
+ * - enqueue/dequeue: task queue for sequential async operations
1793
+ * - destroy: cancel pending requests
1794
+ *
1795
+ * Per-type state (metaList, payloadCache, pendingCacheKeys, syncFn, staticEmitter)
1796
+ * is stored as static class properties. When extend() creates a subclass,
1797
+ * each subclass gets its own copies of these static properties, ensuring
1798
+ * isolation between different Service types.
1076
1799
  */
1077
- declare const EventDelegator: {
1078
- /**
1079
- * Bind a DOM event type to document body.
1080
- */
1081
- bind(eventType: string, hasSelector?: boolean): void;
1082
- /**
1083
- * Unbind a DOM event type from document body.
1084
- */
1085
- unbind(eventType: string, hasSelector?: boolean): void;
1800
+ declare class Service {
1801
+ /** Service instance ID */
1802
+ id: string;
1803
+ /** Whether service is busy (1 = busy) */
1804
+ busy: number;
1805
+ /** Whether service is destroyed (1 = destroyed) */
1806
+ destroyed: number;
1807
+ /** Task queue for sequential operations */
1808
+ taskQueue: AnyFunc[];
1809
+ /** Previous dequeue arguments */
1810
+ prevArgs: unknown[];
1811
+ /** Instance event emitter */
1812
+ private _emitter;
1813
+ constructor();
1814
+ /** Instance event emitter (public accessor) */
1815
+ get emitter(): EventEmitterInterface;
1086
1816
  /**
1087
- * Clean up range events for a destroyed view.
1817
+ * Get internals object for serviceSend compatibility.
1818
+ * References per-type static state from the current class.
1088
1819
  */
1089
- clearRangeEvents(viewId: string): void;
1820
+ get internals(): ServiceSendTarget["internals"];
1090
1821
  /**
1091
- * Set the frame getter function (called by Framework.boot).
1822
+ * Get type reference (the constructor) for serviceSend compatibility.
1823
+ * Static methods like get/create are accessible via the constructor.
1092
1824
  */
1093
- setFrameGetter(getter: (id: string) => FrameLike | undefined): void;
1825
+ get type(): ServiceSendTarget["type"];
1094
1826
  /**
1095
- * Get next element GUID.
1827
+ * Fetch all endpoints, callback when all complete.
1828
+ * Uses cache when available.
1096
1829
  */
1097
- nextElementGuid(): number;
1098
- };
1099
-
1100
- /**
1101
- * Queue a function for deferred, chunked execution.
1102
- *
1103
- * @param fn - Function to execute (wrapped in try-catch automatically)
1104
- * @param args - Arguments array to pass to the function
1105
- * @param context - `this` context for the function call
1106
- */
1107
- declare function task(fn: AnyFunc, args?: unknown[], context?: unknown): void;
1108
- /**
1109
- * Fire a custom DOM event on a target element.
1110
- */
1111
- declare function dispatchEvent(target: EventTarget, eventType: string, eventInit?: CustomEventInit): void;
1112
- /**
1113
- * Base class that can be extended by any object needing EventEmitter.
1114
- */
1115
- declare class Base extends EventEmitter {
1116
- }
1117
- /**
1118
- * Load modules via the configured require function.
1119
- */
1120
- declare function use(names: string | string[], callback?: (...modules: unknown[]) => void): void;
1121
- /**
1122
- * Wait for all views in a zone to be rendered.
1123
- */
1124
- declare function waitZoneViewsRendered(viewId: string, timeout?: number): Promise<number>;
1125
- /**
1126
- * Main framework object.
1127
- * Provides boot, config, and all global utility methods.
1128
- */
1129
- declare const Framework: {
1830
+ all(attrs: string | Record<string, unknown> | (string | Record<string, unknown>)[], done: AnyFunc): this;
1130
1831
  /**
1131
- * Get or set framework configuration.
1832
+ * Fetch all endpoints, callback on each completion.
1132
1833
  */
1133
- config(cfg?: FrameworkConfig | string): FrameworkConfig | unknown;
1834
+ one(attrs: string | Record<string, unknown> | (string | Record<string, unknown>)[], done: AnyFunc): this;
1134
1835
  /**
1135
- * Boot the framework.
1836
+ * Fetch all endpoints, skip cache (always request).
1136
1837
  */
1137
- boot(cfg?: FrameworkConfig): void;
1138
- /** Whether framework has booted */
1139
- isBooted(): boolean;
1140
- /** Mark async callback validity tracker */
1141
- mark: typeof mark$1;
1142
- /** Unmark (invalidate) async callbacks */
1143
- unmark: typeof unmark$1;
1144
- /** Fire a custom DOM event on a target */
1145
- dispatch: typeof dispatchEvent;
1146
- /** Execute function in try-catch, ignoring errors */
1147
- task: typeof task;
1148
- /** Promise-based setTimeout */
1149
- delay(time: number): Promise<void>;
1150
- /** Load modules via configured require */
1151
- use: typeof use;
1152
- /** Wait for zone views to be rendered */
1153
- waitZoneViewsRendered: typeof waitZoneViewsRendered;
1154
- WAIT_OK: number;
1155
- WAIT_TIMEOUT_OR_UNFOUND: number;
1838
+ save(attrs: string | Record<string, unknown> | (string | Record<string, unknown>)[], done: AnyFunc): this;
1156
1839
  /**
1157
- * Convert array to hash map.
1840
+ * Enqueue a task for sequential execution.
1158
1841
  */
1159
- toMap: typeof toMap;
1842
+ enqueue(callback: AnyFunc): this;
1160
1843
  /**
1161
- * Execute function in try-catch.
1844
+ * Dequeue and execute the next task in queue.
1162
1845
  */
1163
- toTry: typeof funcWithTry;
1846
+ dequeue(...args: unknown[]): void;
1164
1847
  /**
1165
- * Convert path + params to URL string.
1848
+ * Destroy the service instance.
1849
+ * After destruction, no new requests can be sent.
1166
1850
  */
1167
- toUrl: typeof toUri;
1851
+ destroy(): void;
1852
+ on(event: string, handler: AnyFunc): this;
1853
+ off(event: string, handler?: AnyFunc): this;
1854
+ fire(event: string, data?: Record<string, unknown>): this;
1855
+ /** Per-type metadata registry */
1856
+ static _metaList: Record<string, ServiceMetaEntry>;
1857
+ /** Per-type payload cache (LFU with frequency eviction) */
1858
+ static _payloadCache: Cache<Payload>;
1859
+ /** Per-type pending cache keys for deduplication */
1860
+ static _pendingCacheKeys: Record<string, PendingCacheEntry>;
1861
+ /** Per-type sync function */
1862
+ static _syncFn: (payload: Payload, callback: () => void) => void;
1863
+ /** Per-type static event emitter */
1864
+ static _staticEmitter: EventEmitter<unknown>;
1865
+ /** Per-type cache max size */
1866
+ static _cacheMax: number;
1867
+ /** Per-type cache buffer size */
1868
+ static _cacheBuffer: number;
1168
1869
  /**
1169
- * Parse URI string into path and params.
1870
+ * Register API endpoint metadata.
1170
1871
  */
1171
- parseUrl: typeof parseUri;
1872
+ static add(attrs: ServiceMetaEntry | ServiceMetaEntry[]): void;
1172
1873
  /**
1173
- * Mix properties from source to target.
1874
+ * Get metadata for an API endpoint.
1174
1875
  */
1175
- mix: typeof assign;
1876
+ static meta(attrs: string | Record<string, unknown>): ServiceMetaEntry;
1176
1877
  /**
1177
- * Check if object has own property.
1878
+ * Create a Payload for an API request.
1178
1879
  */
1179
- has: typeof has;
1880
+ static create(attrs: Record<string, unknown>): Payload;
1180
1881
  /**
1181
- * Get object keys.
1882
+ * Get or create a Payload for an API request.
1182
1883
  */
1183
- keys: typeof keys;
1884
+ static get(attrs: Record<string, unknown>, createNew?: boolean): {
1885
+ entity: Payload;
1886
+ needsUpdate: boolean;
1887
+ };
1184
1888
  /**
1185
- * Check if node A is inside node B.
1889
+ * Get cached Payload if available and not expired.
1186
1890
  */
1187
- inside: typeof nodeInside;
1891
+ static cached(attrs: Record<string, unknown>): Payload | undefined;
1188
1892
  /**
1189
- * Get element by ID (shorthand for document.getElementById).
1893
+ * Clear cached payloads by endpoint name.
1190
1894
  */
1191
- node: typeof getById;
1895
+ static clear(names: string | string[]): void;
1896
+ static on(event: string, handler: AnyFunc): void;
1897
+ static off(event: string, handler?: AnyFunc): void;
1898
+ static fire(event: string, data?: Record<string, unknown>): void;
1192
1899
  /**
1193
- * Apply CSS style.
1900
+ * Create a new Service subclass with a custom sync function.
1901
+ * Each subclass gets its own per-type state (metaList, cache, etc.)
1902
+ * to ensure isolation between different Service types.
1194
1903
  */
1195
- applyStyle: typeof applyStyle;
1904
+ static extend(newSyncFn: (payload: Payload, callback: () => void) => void, newCacheMax?: number, newCacheBuffer?: number): typeof Service;
1905
+ }
1906
+
1907
+ /**
1908
+ * DOM event delegation system.
1909
+ * Delegates events to document body for performance.
1910
+ *
1911
+ */
1912
+ declare const EventDelegator: {
1196
1913
  /**
1197
- * Generate globally unique ID.
1914
+ * Bind a DOM event type to document body.
1198
1915
  */
1199
- guid: typeof generateId;
1916
+ bind(eventType: string, hasSelector?: boolean): void;
1200
1917
  /**
1201
- * Proxy-based debug guard.
1918
+ * Unbind a DOM event type from document body.
1202
1919
  */
1203
- guard: typeof safeguard;
1920
+ unbind(eventType: string, hasSelector?: boolean): void;
1204
1921
  /**
1205
- * Cache class.
1922
+ * Clean up range events for a destroyed view.
1206
1923
  */
1207
- Cache: typeof Cache;
1924
+ clearRangeEvents(viewId: string): void;
1208
1925
  /**
1209
- * Ensure element has an ID.
1926
+ * Set the frame getter function (called by Framework.boot).
1210
1927
  */
1211
- nodeId(element: HTMLElement): string;
1928
+ setFrameGetter(getter: (id: string) => FrameInterface | undefined): void;
1212
1929
  /**
1213
- * Base class with EventEmitter.
1930
+ * Get next element GUID.
1214
1931
  */
1215
- Base: typeof Base;
1216
- /** Router module */
1217
- Router: {
1218
- parse(href?: string): Location;
1219
- diff(): LocationDiff | undefined;
1220
- to(pathOrParams: string | Record<string, unknown>, params?: Record<string, unknown>, replace?: boolean, silent?: boolean): void;
1221
- join(...paths: string[]): string;
1222
- on(event: string, handler: AnyFunc): typeof Router;
1223
- off(event: string, handler?: AnyFunc): typeof Router;
1224
- fire(event: string, data?: Record<string, unknown>, remove?: boolean): typeof Router;
1225
- _bind(): void;
1226
- _setConfig(cfg: FrameworkConfig): void;
1227
- notify?(e?: Event): void;
1228
- };
1229
- /** State module */
1230
- State: {
1231
- get<T = unknown>(key?: string): T;
1232
- set(data: Record<string, unknown>, excludes?: Set<string>): typeof State;
1233
- digest(data?: Record<string, unknown>, excludes?: Set<string>): void;
1234
- diff(): Readonly<Record<string, number>>;
1235
- clean(keys: string): {
1236
- ctor: AnyFunc;
1237
- };
1238
- on(event: string, handler: AnyFunc): typeof State;
1239
- off(event: string, handler?: AnyFunc): typeof State;
1240
- fire(event: string, data?: Record<string, unknown>, remove?: boolean): typeof State;
1241
- };
1242
- /** View class */
1243
- View: typeof View;
1244
- /** Frame class */
1245
- Frame: typeof Frame;
1932
+ nextElementGuid(): number;
1246
1933
  };
1247
1934
 
1935
+ /**
1936
+ * Main framework object.
1937
+ * Provides boot, config, and all global utility methods.
1938
+ */
1939
+ declare const Framework: FrameworkInterface;
1940
+
1248
1941
  /**
1249
1942
  * @lark/framework Store
1250
1943
  *
@@ -1258,8 +1951,6 @@ declare const Framework: {
1258
1951
  * - cell/observeCell: lightweight standalone reactive cells
1259
1952
  */
1260
1953
 
1261
- /** Reactive proxy object — all string keys map to unknown values */
1262
- type ProxyObject = Record<string, unknown>;
1263
1954
  interface Task {
1264
1955
  cb: AnyFunc;
1265
1956
  params?: Record<string, unknown>;
@@ -1310,10 +2001,10 @@ declare class Queue {
1310
2001
  declare const isState: (target: unknown) => boolean;
1311
2002
  declare const mark: (host: Record<string, unknown>, key: string) => (() => boolean);
1312
2003
  declare const unmark: (host: Record<string, unknown>) => void;
1313
- declare function createState(initialData: unknown, config?: StateConfig): ProxyObject;
1314
- declare function lazySet(target: ProxyObject, data: Record<string, unknown>): void;
2004
+ declare function createState(initialData: unknown, config?: StateConfig): Record<string, unknown>;
2005
+ declare function lazySet(target: Record<string, unknown>, data: Record<string, unknown>): void;
1315
2006
  /** Shallow set: only top-level properties are reactive */
1316
- declare function shallowSet(target: ProxyObject, key: string, data: unknown): unknown;
2007
+ declare function shallowSet(target: Record<string, unknown>, key: string, data: unknown): unknown;
1317
2008
  declare const _storeName: unique symbol;
1318
2009
  declare const _storeStatus: unique symbol;
1319
2010
  declare const _storeScheduler: unique symbol;
@@ -1333,10 +2024,10 @@ declare abstract class BaseStore {
1333
2024
  [_storeScheduler]: Scheduler;
1334
2025
  [_originState]: Record<string, unknown>;
1335
2026
  [_stateKeys]: string[];
1336
- [_storeState]: ProxyObject;
2027
+ [_storeState]: Record<string, unknown>;
1337
2028
  [_storeDefScheduler]: () => Queue;
1338
2029
  private [_outerStore];
1339
- [_storeBoot](): ProxyObject;
2030
+ [_storeBoot](): Record<string, unknown>;
1340
2031
  [_storeDestroy](): void;
1341
2032
  /**
1342
2033
  *
@@ -1345,11 +2036,11 @@ declare abstract class BaseStore {
1345
2036
  */
1346
2037
  [_storeCreate](body: Record<string, unknown>, excludeFns?: string[]): void;
1347
2038
  constructor(name: string, config?: StoreConfig);
1348
- [_innerStore](): ProxyObject;
2039
+ [_innerStore](): Record<string, unknown>;
1349
2040
  get status(): StoreStatus;
1350
2041
  get storeName(): string;
1351
2042
  get scheduler(): Scheduler;
1352
- [_storeProxy](toOut?: boolean): ProxyObject;
2043
+ [_storeProxy](toOut?: boolean): Record<string, unknown>;
1353
2044
  }
1354
2045
  interface LarkView {
1355
2046
  updater: {
@@ -1390,23 +2081,89 @@ declare function getUseStore(name: string): AnyFunc | undefined;
1390
2081
  declare function cloneStore(name: string, useStore: AnyFunc, config?: StoreConfig): unknown;
1391
2082
  declare function isStoreActive(name: string): boolean;
1392
2083
  declare function cell<T = unknown>(data: T): T;
1393
- declare function observeCell(state: ProxyObject, cb: AnyFunc, immediate?: boolean): () => void;
2084
+ declare function observeCell(state: Record<string, unknown>, cb: AnyFunc, immediate?: boolean): () => void;
1394
2085
  declare function multi<S = Record<string, unknown>>(useStore: LarkUseStore<S>): [LarkUseStore<S>, {
1395
- ctor: AnyFunc;
2086
+ make: AnyFunc;
1396
2087
  }];
1397
2088
 
2089
+ /** Serialized view info attached to a frame node */
2090
+ interface SerializedViewInfo {
2091
+ /** View ID (same as frame ID) */
2092
+ id: string;
2093
+ /** Whether the view has rendered at least once */
2094
+ rendered: boolean;
2095
+ /** View signature (> 0 = active) */
2096
+ signature: number;
2097
+ /** Observed state keys */
2098
+ observedStateKeys: string[] | null;
2099
+ /** Location observation config */
2100
+ locationObserved: {
2101
+ flag: number;
2102
+ keys: string[];
2103
+ observePath: boolean;
2104
+ };
2105
+ /** Whether view has a template function */
2106
+ hasTemplate: boolean;
2107
+ }
2108
+ /** A single node in the serialized frame tree */
2109
+ interface SerializedFrameNode {
2110
+ /** Frame ID (same as owner DOM element ID) */
2111
+ id: string;
2112
+ /** Parent frame ID (null for root) */
2113
+ parentId: string | null;
2114
+ /** View path (v-lark attribute value) */
2115
+ viewPath: string | null;
2116
+ /** Number of child frames */
2117
+ childrenCount: number;
2118
+ /** Number of children that have fired 'created' */
2119
+ readyCount: number;
2120
+ /** Whether children have been created */
2121
+ childrenCreated: number;
2122
+ /** Whether children are in alter state */
2123
+ childrenAlter: number;
2124
+ /** Whether this frame is destroyed */
2125
+ destroyed: number;
2126
+ /** Serialized view info (null if no view mounted) */
2127
+ view: SerializedViewInfo | null;
2128
+ /** Child frame nodes */
2129
+ children: SerializedFrameNode[];
2130
+ }
2131
+ /** Top-level serialized frame tree */
2132
+ interface SerializedFrameTree {
2133
+ /** Root frame node */
2134
+ root: SerializedFrameNode | null;
2135
+ /** Total frame count */
2136
+ totalFrames: number;
2137
+ /** Timestamp of serialization */
2138
+ timestamp: number;
2139
+ /** Root element ID */
2140
+ rootId: string;
2141
+ }
2142
+ /**
2143
+ * Serialize the entire Frame tree starting from root.
2144
+ */
2145
+ declare function serializeFrameTree(): SerializedFrameTree;
2146
+ /**
2147
+ * Install the Frame Visualizer Bridge.
2148
+ * Listens for postMessage events from the visualizer and responds
2149
+ * with serialized frame tree data.
2150
+ *
2151
+ * This should be called once during Framework.boot().
2152
+ */
2153
+ declare function installFrameVisualizerBridge(): void;
2154
+
1398
2155
  /**
1399
2156
  * @lark/framework Template Compiler
1400
2157
  *
1401
2158
  * convertArtSyntax() ({{}} → <% %>)
1402
- * processViewEvents() (v-event prefix + param encoding)
2159
+ * processViewEvents() (@event prefix + param encoding)
1403
2160
  * compileToFunction() (<% %> → JS template function)
1404
2161
  * extractGlobalVars() (AST-based global var analysis via @babel/parser)
1405
2162
  *
1406
2163
  * - All template operators: = (escape), ! (raw), @ (ref lookup), : (binding)
1407
- * - v-event attribute processing with $g prefix + \x1e separator
1408
- * - $n (null-safe toString), $e (HTML entity encode), $eu (URI encode), $eq (quote encode), $i (ref lookup)
1409
- * - Debug mode with line tracking ($expr/$art/$line) and try-catch error wrapper
2164
+ * - @event attribute processing with $splitter prefix + \x1e separator
2165
+ * - $strSafe (null-safe toString), $encHtml (HTML entity encode), $encUri (URI encode), $encQuote (quote encode), $refFn (ref lookup)
2166
+ * - Debug mode with line tracking ($dbgExpr/$dbgArt/$dbgLine) and try-catch error wrapper
1410
2167
  * - View ID injection (\x1f → '+$viewId+')
1411
2168
  * - Post-processing cleanup of empty concatenations
1412
2169
  * - 0 configuration: auto-extract variables via AST analysis
@@ -1416,25 +2173,17 @@ declare function multi<S = Record<string, unknown>>(useStore: LarkUseStore<S>):
1416
2173
  * {{:variable}} → two-way binding (same as = for rendering)
1417
2174
  * {{!variable}} → raw output (no HTML escaping)
1418
2175
  * {{@variable}} → reference lookup for component data passing
1419
- * {{each list as item}} → loop
1420
- * {{each list as item idx}} → loop with index
1421
- * {{parse obj as val key}} → object iteration
2176
+ * {{forOf list as item}} → loop
2177
+ * {{forOf list as item idx}} → loop with index
2178
+ * {{forIn obj as val key}} → object iteration
1422
2179
  * {{for(let i=0;i<n;i++)}} → generic for loop
1423
2180
  * {{if condition}} → conditional
1424
2181
  * {{else if condition}} → else-if
1425
2182
  * {{else}} → else
1426
- * {{/if}} / {{/each}} / {{/parse}} / {{/for}} → close blocks
2183
+ * {{/if}} / {{/forOf}} / {{/forIn}} / {{/for}} → close blocks
1427
2184
  * {{set a = b}} → variable declaration
1428
2185
  */
1429
- /** Options for compileTemplate() */
1430
- interface CompileOptions {
1431
- /** Enable debug mode with line tracking (default: false) */
1432
- debug?: boolean;
1433
- /** Global variable names to destructure from $$ (refData) */
1434
- globalVars?: string[];
1435
- /** File path for debug error messages (default: undefined) */
1436
- file?: string;
1437
- }
2186
+
1438
2187
  /**
1439
2188
  * Compile an HTML template string into a JS module string.
1440
2189
  * This is the main entry point for both Vite and Webpack loaders.
@@ -1443,7 +2192,7 @@ interface CompileOptions {
1443
2192
  * (data, selfId, refData) => string
1444
2193
  *
1445
2194
  * Internally it calls the compiled template function with the standard
1446
- * signature: ($$,$viewId,$$ref,$e,$n,$eu,$i,$eq)
2195
+ * signature: ($data,$viewId,$refAlt,$encHtml,$strSafe,$encUri,$refFn,$encQuote)
1447
2196
  *
1448
2197
  * @param source - The raw HTML template content
1449
2198
  * @param options - Compilation options
@@ -1468,4 +2217,4 @@ declare function compileTemplate(source: string, options?: CompileOptions): stri
1468
2217
  */
1469
2218
  declare function extractGlobalVars(source: string): string[];
1470
2219
 
1471
- export { type AnyFunc, Bag, type BagEntry, CALL_BREAK_TIME, Cache, type CacheEntry, type CacheOptions, type CompileOptions, type Constructable, EVENT_METHOD_REGEXP, EventDelegator, EventEmitter, type EventListenerEntry, Frame, type FrameBoundElement, type FrameChildrenMap, type FrameInvokeEntry, type FrameLike, type FrameReadyMap, Framework, type FrameworkConfig, LARK_VIEW, type LarkUseStore, type Location, type LocationDiff, type MixinEventHandler, type NodeUseStore, type ObservePayload, type ParamDiff, type ParsedUri, type PendingCacheEntry, Platform, ROUTER_EVENTS, type ReactUseStore, type RouteViewConfig, Router, SPLITTER, Service, type ServiceCacheInfo, type ServiceConstructor, type ServiceEntry, type ServiceMetaEntry, type ServiceOptions, State, type StoreConfig, type StoreMethods, TAG_ATTR_KEY, TAG_KEY, TAG_NAME_REGEXP, TAG_VIEW_KEY, Updater, type UpdaterLike, type VDomOp, type VDomRef, VIEW_EVENT_METHOD_REGEXP, type VdomElement, View, type ViewClassInternal, type ViewEventObjectMap, type ViewEventSelectorEntry, type ViewEventSelectorMap, type ViewGlobalEventEntry, type ViewInstance, type ViewLocationObserved, type ViewResourceEntry, type ViewResourceMap, type VoidFunc, applyIdUpdates, applyStyle, applyVdomOps, assign, cell, classExtend, cloneData, cloneStore, compileTemplate, createState, createVdomRef, defineStore, delStore, encodeHTML, encodeQ, encodeSafe, encodeURIExtra, ensureElementId, extractGlobalVars, funcWithTry, generateId, getAttribute, getById, getPlatform, getStore, getUseStore, has, isArray, isPlainObject, isPrimitive, isPrimitiveOrFunc, isState, isStoreActive, keys, lazySet, mark$1 as mark, markBooted, markRouterBooted, multi, nextCounter, nodeInside, noop, now, observeCell, parseUri, registerViewClass, safeguard, setData, shallowSet, mark as storeMark, unmark as storeUnmark, syncCounter, toMap, toUri, translateData, unmark$1 as unmark, vdomGetCompareKey, vdomGetNode, vdomSetAttributes, vdomSetChildNodes, vdomSetNode, vdomSpecialDiff, vdomUnmountFrames };
2220
+ export { CALL_BREAK_TIME, Cache, type CacheEntry, type CacheOptions, type CompileOptions, type Constructable, EVENT_METHOD_REGEXP, EventDelegator, EventEmitter, type EventListenerEntry, Frame, type FrameBoundElement, type FrameInterface, type FrameInvokeEntry, Framework, type FrameworkConfig, LARK_VIEW, type LarkUseStore, type Location, type LocationDiff, type MixinEventHandler, type NodeUseStore, type ObservePayload, type ParamDiff, type ParsedUri, Payload, type PayloadEntry, type PendingCacheEntry, Platform, ROUTER_EVENTS, type ReactUseStore, type RouteViewConfig, Router, SPLITTER, type SerializedFrameNode, type SerializedFrameTree, type SerializedViewInfo, Service, type ServiceCacheInfo, type ServiceEntry, type ServiceMetaEntry, type ServiceOptions, State, type StoreConfig, type StoreMethods, TAG_NAME_REGEXP, Updater, type UpdaterInterface, type VDomOp, type VDomRef, VIEW_EVENT_METHOD_REGEXP, type VdomElement, View, type ViewEventSelectorEntry, type ViewGlobalEventEntry, type ViewInterface, type ViewLocationObserved, type ViewResourceEntry, applyIdUpdates, applyStyle, applyVdomOps, assign, cell, classExtend, cloneData, cloneStore, compileTemplate, createState, createVdomRef, defineStore, delStore, encodeHTML, encodeQ, encodeSafe, encodeURIExtra, ensureElementId, extractGlobalVars, funcWithTry, generateId, getAttribute, getById, getPlatform, getStore, getUseStore, has, installFrameVisualizerBridge, isPlainObject, isPrimitive, isPrimitiveOrFunc, isState, isStoreActive, keys, lazySet, mark$1 as mark, markBooted, markRouterBooted, multi, nextCounter, nodeInside, noop, now, observeCell, parseUri, registerViewClass, safeguard, serializeFrameTree, setData, shallowSet, mark as storeMark, unmark as storeUnmark, syncCounter, toMap, toUri, translateData, unmark$1 as unmark, vdomGetCompareKey, vdomGetNode, vdomSetAttributes, vdomSetChildNodes, vdomSetNode, vdomSpecialDiff, vdomUnmountFrames };