@lark.js/mvc 0.0.3 → 0.0.5

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,6 +1,469 @@
1
+ /**
2
+ * Multi-cast event emitter class.
3
+ *
4
+ * @example
5
+ * const emitter = new EventEmitter();
6
+ * emitter.on('change', (data) => console.log(data));
7
+ * emitter.fire('change', { key: 'value' });
8
+ */
9
+ declare class EventEmitter<T = unknown> implements EventEmitterInterface<T> {
10
+ /** Event listeners: prefixed key -> listener array */
11
+ listeners: Map<string, EventListenerEntry[]>;
12
+ /** Number of `fire()` calls currently on the stack (re-entrancy depth). */
13
+ private firingDepth;
14
+ /** Keys whose listener list needs compaction after firing settles. */
15
+ private pendingCompaction;
16
+ /**
17
+ * Bind event listener.
18
+ */
19
+ on(event: string, handler: (this: T, e: ChangeEvent) => void): this;
20
+ /**
21
+ * Unbind event listener.
22
+ * If handler is provided, removes only that handler.
23
+ * If no handler, removes all handlers for the event.
24
+ */
25
+ off(event: string, handler?: AnyFunc): this;
26
+ /**
27
+ * Fire event, execute all bound handlers. Safe for re-entrant `off()` calls
28
+ * during dispatch: removed handlers are replaced with noop and compacted
29
+ * after the outermost fire returns.
30
+ *
31
+ * @param event - Event name
32
+ * @param data - Event data (type property added automatically)
33
+ * @param remove - Whether to remove all handlers after firing
34
+ * @param lastToFirst - Whether to execute handlers in reverse order
35
+ */
36
+ fire(event: string, data?: Record<string, unknown>, remove?: boolean, lastToFirst?: boolean): this;
37
+ }
38
+
39
+ /**
40
+ * Base View class.
41
+ * Views are created via View.extend() and mounted by Frame.
42
+ */
43
+ declare class View implements ViewInterface {
44
+ /** View ID (same as owner frame ID) */
45
+ id: string;
46
+ /** Owner frame */
47
+ owner: FrameInterface | number;
48
+ /** Updater instance */
49
+ updater: UpdaterInterface;
50
+ /** Signature: > 0 means active, incremented on render, 0 = destroyed */
51
+ signature: number;
52
+ /** Whether rendered at least once */
53
+ rendered?: boolean;
54
+ /** Whether view has template */
55
+ template?: ViewTemplate;
56
+ /** Location observation config */
57
+ locationObserved: ViewLocationObserved;
58
+ /** Observed state keys */
59
+ observedStateKeys?: string[];
60
+ /** Resource map */
61
+ resources: Record<string, ViewResourceEntry>;
62
+ /** Assign method reference */
63
+ assignMethod?: AnyFunc;
64
+ /** Whether endUpdate pending */
65
+ endUpdatePending?: number;
66
+ /** Internal event storage */
67
+ private _events;
68
+ /** Prototype-stored event maps shape (set by View.prepare). */
69
+ private get protoEventState();
70
+ /**
71
+ * Event bitmask map: eventType -> bitmask (1=root, 2=selector).
72
+ * Read from prototype ($evtObjMap) set by View.prepare.
73
+ * Using a getter avoids ES6 class field shadowing the prototype value.
74
+ */
75
+ get eventObjectMap(): Record<string, number>;
76
+ /**
77
+ * Selector event map: eventType -> selector list.
78
+ * Read from prototype ($selMap) set by View.prepare.
79
+ */
80
+ get eventSelectorMap(): Record<string, ViewEventSelectorEntry>;
81
+ /**
82
+ * Global event list: [{handler, element, eventName, modifiers}].
83
+ * Read from prototype ($globalEvtList) set by View.prepare.
84
+ */
85
+ get globalEventList(): ViewGlobalEventEntry[];
86
+ /**
87
+ * Initialize view (called by Frame when mounting).
88
+ */
89
+ init(): void;
90
+ /**
91
+ * Render view template (called by Frame after init).
92
+ * Wrapped by View.wrapMethod to manage signature + resources.
93
+ */
94
+ render(): void;
95
+ on(event: string, handler: AnyFunc): this;
96
+ off(event: string, handler?: AnyFunc): this;
97
+ fire(event: string, data?: Record<string, unknown>, remove?: boolean, lastToFirst?: boolean): this;
98
+ /** Get the owning frame, asserting it has been bound. */
99
+ private get ownerFrame();
100
+ /**
101
+ * Notify view that HTML update is about to begin.
102
+ * Unmounts child frames in the update zone.
103
+ */
104
+ beginUpdate(id?: string): void;
105
+ /**
106
+ * Notify view that HTML update has ended.
107
+ * Mounts child frames in the update zone and runs deferred invokes.
108
+ */
109
+ endUpdate(id?: string, inner?: boolean): void;
110
+ /**
111
+ * Wrap an async callback to check view signature before executing.
112
+ * If the view has been re-rendered or destroyed, the callback is skipped.
113
+ */
114
+ wrapAsync<Fn extends AnyFunc>(fn: Fn, context?: unknown): (...args: Parameters<Fn>) => ReturnType<Fn> | undefined;
115
+ /**
116
+ * Observe location parameters or path changes.
117
+ * When observed keys change, render() is called automatically.
118
+ */
119
+ observeLocation(params: string | string[] | Record<string, unknown>, observePath?: boolean): void;
120
+ /**
121
+ * Observe State data keys for changes.
122
+ * When observed keys change via State.digest(), render() is called.
123
+ */
124
+ observeState(observedKeys: string | string[]): void;
125
+ /**
126
+ * Capture (register) a resource under a key.
127
+ * If a resource already exists at that key, it's destroyed first.
128
+ * When destroyOnRender=true, the resource is destroyed on next render call.
129
+ */
130
+ capture(key: string, resource?: unknown, destroyOnRender?: boolean): unknown;
131
+ /**
132
+ * Release a captured resource.
133
+ * If destroy=true, calls the resource's destroy() method.
134
+ */
135
+ release(key: string, destroy?: boolean): unknown;
136
+ /**
137
+ * Set up a leave confirmation for route changes and page unload.
138
+ */
139
+ leaveTip(message: string, condition: () => boolean): void;
140
+ /** Collected ctors from mixins */
141
+ static ctors?: AnyFunc[];
142
+ /**
143
+ * Prepare a View subclass by scanning its prototype for event method patterns.
144
+ * Pattern: `$?name<eventType1,eventType2>(&modifiers)`
145
+ *
146
+ * Only runs once per View subclass (guarded by ctors marker).
147
+ * Called from Frame.mountView before creating the view instance.
148
+ */
149
+ static prepare(oView: typeof View): AnyFunc[];
150
+ /**
151
+ * Bind or unbind event delegation for a view instance.
152
+ * Called from Frame during mount/unmount.
153
+ */
154
+ static delegateEvents(view: ViewInterface, destroy?: boolean): void;
155
+ /**
156
+ * Destroy all resources managed by a view.
157
+ * If lastly=true, destroy ALL resources; otherwise only destroyOnRender ones.
158
+ */
159
+ static destroyAllResources(view: ViewInterface, lastly: boolean): void;
160
+ /**
161
+ * Process deferred invoke calls on a frame.
162
+ */
163
+ static runInvokes(frame: FrameInterface): void;
164
+ /**
165
+ * Wrap a method on the prototype to add signature checking and resource cleanup.
166
+ */
167
+ private static wrapMethod;
168
+ /**
169
+ * When two mixins define the same event method, merge them into
170
+ * a single function that calls both in sequence.
171
+ */
172
+ private static processMixinsSameEvent;
173
+ /**
174
+ * Merge an array of mixin objects into the view prototype.
175
+ */
176
+ private static mergeMixins;
177
+ /**
178
+ * Destroy a single resource entry.
179
+ */
180
+ private static destroyResource;
181
+ /**
182
+ * Extend View to create a new View subclass.
183
+ *
184
+ * Supports:
185
+ * - props.make: constructor-like init (called with initParams + {node, deep})
186
+ * - props.mixins: array of mixin objects
187
+ * - Event method patterns: `'name<click>'` etc.
188
+ */
189
+ static extend(props?: ThisType<ViewInterface> & Record<string, unknown>, statics?: Record<string, unknown>): typeof View;
190
+ /**
191
+ * Merge mixins into View prototype.
192
+ */
193
+ static merge(this: typeof View, ...mixins: Record<string, unknown>[]): typeof View;
194
+ }
195
+ /**
196
+ * Type-safe wrapper around `View.extend()`.
197
+ *
198
+ * `View.extend({...})` accepts any object literal, and inside its methods
199
+ * `this` is typed only as the base `ViewInterface` — so any custom state
200
+ * field or helper method requires a `(this as MyView).foo` strong-cast at
201
+ * every call site.
202
+ *
203
+ * `defineView()` threads the literal's own shape back into `this` via
204
+ * `ThisType<P & ViewInterface>`, so `this.foo` is typed automatically:
205
+ *
206
+ * ```ts
207
+ * const HomeView = defineView({
208
+ * $title: "Home",
209
+ * init() {
210
+ * this.updater.set({ title: this.$title }); // both typed
211
+ * },
212
+ * greet() {
213
+ * return `hello ${this.$title}`;
214
+ * },
215
+ * });
216
+ * ```
217
+ *
218
+ * Runtime semantics are identical to `View.extend(props, statics)` — this is
219
+ * a zero-cost type-only wrapper.
220
+ */
221
+ declare function defineView<P extends Record<string, unknown>>(props: P & ThisType<P & ViewInterface>, statics?: Record<string, unknown>): typeof View;
222
+
223
+ /**
224
+ * Register a View class for a given view path.
225
+ * Called after module loading completes (or up front during boot).
226
+ */
227
+ declare function registerViewClass(viewPath: string, ViewClass: typeof View): void;
228
+ /**
229
+ * Invalidate a View class from the registry.
230
+ * Used by HMR to force re-loading of a view module.
231
+ */
232
+ declare function invalidateViewClass(viewPath: string): void;
233
+
234
+ /**
235
+ * Frame (View Frame) class for view lifecycle management.
236
+ * Each frame owns a view and manages child frames.
237
+ *
238
+ */
239
+ declare class Frame extends EventEmitter implements FrameInterface {
240
+ /** Frame ID (same as owner DOM element ID) */
241
+ readonly id: string;
242
+ /** Parent Frame ID */
243
+ private _parentId;
244
+ get parentId(): string | undefined;
245
+ /** Children map: id -> id */
246
+ childrenMap: Record<string, string>;
247
+ /** Children count */
248
+ childrenCount: number;
249
+ /** Ready count (children that have fired 'created') */
250
+ readyCount: number;
251
+ /** Set of child frame IDs that have fired 'created' */
252
+ readyMap: Set<string>;
253
+ /** View instance */
254
+ viewInstance?: ViewInterface;
255
+ /** Get view instance (read-only) */
256
+ get view(): ViewInterface | undefined;
257
+ /** Invoke list for deferred method calls */
258
+ invokeList: FrameInvokeEntry[];
259
+ /** Signature for async operation tracking */
260
+ signature: number;
261
+ /** Whether view has altered */
262
+ hasAltered: number;
263
+ /** Whether view is destroyed */
264
+ destroyed: number;
265
+ /** View path (v-lark attribute value) */
266
+ viewPath?: string;
267
+ /** Original template before mount */
268
+ originalTemplate?: string;
269
+ /** Hold fire created flag */
270
+ holdFireCreated: number;
271
+ /** Children created flag */
272
+ childrenCreated: number;
273
+ /** Children alter flag */
274
+ childrenAlter: number;
275
+ constructor(id: string, parentId?: string);
276
+ /**
277
+ * Mount a view to this frame.
278
+ *
279
+ * Complete flow:
280
+ * 1. Parse viewPath, translate query params from parent
281
+ * 2. Unmount current view
282
+ * 3. Load View class (via require or provided ViewClass)
283
+ * 4. View_Prepare (scan event methods)
284
+ * 5. Create View instance
285
+ * 6. View_DelegateEvents (bind DOM events)
286
+ * 7. Call view.init()
287
+ * 8. If view has template, call render via Updater
288
+ * 9. If no template, call endUpdate directly
289
+ */
290
+ mountView(viewPath: string, viewInitParams?: Record<string, unknown>): void;
291
+ /**
292
+ * Internal: actually mount the view after class is loaded.
293
+ */
294
+ doMountView(ViewClass: typeof View, params: Record<string, unknown>, node: HTMLElement, sign: number): void;
295
+ /**
296
+ * Unmount current view.
297
+ */
298
+ unmountView(): void;
299
+ /**
300
+ * Mount a child frame.
301
+ */
302
+ mountFrame(frameId: string, viewPath: string, viewInitParams?: Record<string, unknown>): FrameInterface;
303
+ /**
304
+ * Unmount a child frame.
305
+ */
306
+ unmountFrame(id?: string, _inner?: boolean): void;
307
+ /**
308
+ * Mount all views in a zone.
309
+ */
310
+ mountZone(zoneId?: string, _inner?: boolean): void;
311
+ /**
312
+ * Unmount all views in a zone.
313
+ */
314
+ unmountZone(zoneId?: string, _inner?: boolean): void;
315
+ /**
316
+ * Get all child frame IDs.
317
+ */
318
+ children(): string[];
319
+ /**
320
+ * Get parent frame at given level.
321
+ * @param level - How many levels up (default 1)
322
+ */
323
+ parent(level?: number): Frame | undefined;
324
+ /**
325
+ * Invoke a method on the view.
326
+ */
327
+ invoke(name: string, args?: unknown[]): unknown;
328
+ /**
329
+ * Type-safe variant of `invoke`.
330
+ *
331
+ * `invoke()` accepts any string and any args, which silently hides
332
+ * mismatched call sites when a method gets renamed. `invokeTyped` carries
333
+ * the view's method signature through TypeScript so the compiler catches
334
+ * those mistakes:
335
+ *
336
+ * ```ts
337
+ * type Home = View & { loadData(id: string): Promise<void> };
338
+ * frame.invokeTyped<Home, "loadData">("loadData", ["user-1"]);
339
+ * ```
340
+ *
341
+ * Behavior is identical to `invoke` at runtime — same defer / direct-call
342
+ * paths — so it's a drop-in safer overload.
343
+ */
344
+ invokeTyped<V extends Record<string, unknown>, K extends keyof V & string>(name: K, args: V[K] extends (...a: infer A) => unknown ? A : never[]): V[K] extends (...a: never[]) => infer R ? R | undefined : unknown;
345
+ /** Get frame by ID */
346
+ static get(id: string): Frame | undefined;
347
+ /** Get all frames */
348
+ static getAll(): Map<string, Frame>;
349
+ /**
350
+ * Returns the existing root frame, or `undefined` if none has been created.
351
+ * Pure getter — never creates a Frame, never touches the DOM.
352
+ *
353
+ * Use `Frame.createRoot(id)` to create the root explicitly during framework
354
+ * boot. For Micro-Frontend hosts that own multiple independent containers,
355
+ * use `new Frame(containerId)` directly so each MF mount has its own root.
356
+ */
357
+ static getRoot(): Frame | undefined;
358
+ /**
359
+ * Create (or return) the singleton root frame for this app.
360
+ *
361
+ * Idempotent: subsequent calls always return the original root regardless
362
+ * of `rootId` — so passing a different id later is silently ignored.
363
+ * `Framework.boot()` is the canonical caller; user code rarely needs this.
364
+ */
365
+ static createRoot(rootId?: string): Frame;
366
+ /**
367
+ * @deprecated Use `Frame.getRoot()` for read-only access or
368
+ * `Frame.createRoot(id)` to create the root explicitly. The single-method
369
+ * `root()` blurred the distinction and was a common source of bugs in
370
+ * Micro-Frontend hosts.
371
+ *
372
+ * Kept for backward compatibility — behavior unchanged.
373
+ */
374
+ static root(rootId?: string): Frame;
375
+ /** Bind event listener (static) */
376
+ static on(event: string, handler: AnyFunc): typeof Frame;
377
+ /** Unbind event listener (static) */
378
+ static off(event: string, handler?: AnyFunc): typeof Frame;
379
+ /** Fire event (static) */
380
+ static fire(event: string, data?: Record<string, unknown>): void;
381
+ }
382
+
383
+ /**
384
+ * Cache class with LFU-style eviction.
385
+ * Keys are prefixed with SPLITTER for namespace isolation.
386
+ *
387
+ * @example
388
+ * const cache = new Cache({ maxSize: 20, bufferSize: 5 });
389
+ * cache.set('user', { name: 'Alice' });
390
+ * const user = cache.get('user');
391
+ * cache.has('user'); // true
392
+ * cache.del('user');
393
+ */
394
+ declare class Cache<T = unknown> implements CacheInterface<T> {
395
+ /** Cache entries array */
396
+ private entries;
397
+ /** Fast lookup: prefixed key -> entry */
398
+ private lookup;
399
+ /** Buffer size for eviction */
400
+ private readonly bufferSize;
401
+ /** Maximum cache size */
402
+ private readonly maxSize;
403
+ /** Total capacity (maxSize + bufferSize) */
404
+ private readonly capacity;
405
+ /** Callback when entry is removed */
406
+ private readonly onRemove?;
407
+ /** Sort comparator */
408
+ private readonly comparator;
409
+ constructor(options?: CacheOptions<T>);
410
+ /** Prefix a key with SPLITTER for namespace isolation */
411
+ private prefixKey;
412
+ /**
413
+ * Get a cached value by key.
414
+ * Updates frequency and timestamp for cache sorting.
415
+ */
416
+ get(key: string): T | undefined;
417
+ /**
418
+ * Iterate all cached values.
419
+ */
420
+ forEach(callback: (value: T | undefined) => void): void;
421
+ /**
422
+ * Set or update a cached value.
423
+ * If key already exists, updates value and increments frequency.
424
+ * If cache exceeds capacity, triggers eviction.
425
+ */
426
+ set(key: string, value: T): void;
427
+ /**
428
+ * Delete a cached entry. Removes it immediately from both the lookup map
429
+ * and the entries array so the GC can reclaim the value without waiting
430
+ * for the next eviction sweep.
431
+ */
432
+ del(key: string): void;
433
+ /**
434
+ * Check if a key exists in cache.
435
+ */
436
+ has(key: string): boolean;
437
+ /** Get current cache size */
438
+ get size(): number;
439
+ /** Clear all entries */
440
+ clear(): void;
441
+ /**
442
+ * Evict the `bufferSize` worst entries from the cache.
443
+ *
444
+ * Uses single-pass partial selection (O(n·k)) instead of sorting the entire
445
+ * `entries` array (O(n log n)). For the typical `bufferSize = 5` this is
446
+ * effectively a linear scan with at most 5 in-bucket comparisons per
447
+ * iteration — and it avoids mutating the rest of `entries`.
448
+ */
449
+ private evictEntries;
450
+ }
451
+
1
452
  /**
2
453
  * Lark framework type definitions.
3
454
  * All shared types are defined here to eliminate type cheats across modules.
455
+ *
456
+ * Lark is a lightweight MVC frontend framework that provides:
457
+ * - View: base view class with extend/merge inheritance and mixin support
458
+ * - Router: hash-based two-phase route confirmation
459
+ * - State: simple cross-view observable singleton (recommended for simple cases)
460
+ * - Store: complex Proxy-based reactive state with observe/handlers/multi-instance
461
+ * (recommended for complex cases)
462
+ * - Service: API request management with caching, queuing, and deduplication
463
+ * - Frame: view frame managing view mount/unmount lifecycle
464
+ * - Updater: view data binding and VDOM diff (in-memory real DOM diff) renderer
465
+ *
466
+ * Designed for single-page application (SPA) development.
4
467
  */
5
468
  /** Generic function type for event handlers and callbacks.
6
469
  * Uses any[] to accept callbacks with specific parameter types
@@ -29,81 +492,190 @@ interface CacheOptions<T> {
29
492
  /** Comparator for sorting entries */
30
493
  sortComparator?: (a: CacheEntry<T>, b: CacheEntry<T>) => number;
31
494
  }
495
+ /**
496
+ * Cache interface providing LFU (Least Frequently Used) cache management.
497
+ * Cache keys use a special prefix internally for namespace isolation.
498
+ */
499
+ interface CacheInterface<T = unknown> {
500
+ /**
501
+ * Set a cache resource. If the key exists, updates the value and increments frequency.
502
+ * Triggers LFU eviction when cache entries exceed capacity (maxSize + bufferSize).
503
+ * @param key Unique identifier for the cached resource
504
+ * @param resource The resource to cache
505
+ */
506
+ set(key: string, resource: T): void;
507
+ /**
508
+ * Get a cached resource. Access increments frequency count and timestamp for LFU ranking.
509
+ * Returns undefined if the key does not exist.
510
+ * @param key Cache resource key
511
+ */
512
+ get(key: string): T | undefined;
513
+ /**
514
+ * Remove a resource from cache by key. Triggers onRemove callback on deletion.
515
+ * @param key Cache resource key to remove
516
+ */
517
+ del(key: string): void;
518
+ /**
519
+ * Check if cache contains a resource for the given key.
520
+ * @param key Cache resource key
521
+ */
522
+ has(key: string): boolean;
523
+ /**
524
+ * Iterate over all cached resource values.
525
+ * @param callback Iteration callback receiving the cached value (may be undefined)
526
+ */
527
+ forEach(callback: (value: T | undefined) => void): void;
528
+ /**
529
+ * Number of cache entries.
530
+ */
531
+ readonly size: number;
532
+ /**
533
+ * Clear all cache entries. Triggers onRemove callback for each deleted entry.
534
+ */
535
+ clear(): void;
536
+ }
32
537
  interface EventListenerEntry {
33
538
  /** Handler function */
34
539
  handler: AnyFunc;
35
540
  /** Whether currently executing (1 = executing, '' = done) */
36
541
  executing: number | string;
37
542
  }
543
+ /**
544
+ * Parsed URL result containing path and parameters.
545
+ * Returned by `Router.parse()`, includes the path string and parsed key-value parameter pairs.
546
+ */
38
547
  interface ParsedUri {
39
- /** Path portion (before ? or #) */
548
+ /** Path portion (before ? or #), excluding query parameters */
40
549
  path: string;
41
- /** Key-value params */
550
+ /** Key-value params parsed from the URL */
42
551
  params: Record<string, string>;
43
552
  }
553
+ /**
554
+ * Current URL parsing result interface.
555
+ * Returned by `Router.parse()`, includes both query (after ?) and hash (after #) sections.
556
+ */
44
557
  interface Location {
45
- /** Full href */
558
+ /** Full href, original href string */
46
559
  href: string;
47
- /** Query string (before #) */
560
+ /** Query string (before #), raw query string (after ?, before #) */
48
561
  srcQuery: string;
49
- /** Hash string (after #) */
562
+ /** Hash string (after #), raw hash string (after #) */
50
563
  srcHash: string;
51
- /** Parsed query object */
564
+ /** Parsed query object, path and params parsed from srcQuery */
52
565
  query: ParsedUri;
53
- /** Parsed hash object */
566
+ /** Parsed hash object, path and params parsed from srcHash */
54
567
  hash: ParsedUri;
55
- /** Merged params from query and hash */
568
+ /**
569
+ * Merged params from query and hash,
570
+ * hash values take precedence when keys conflict.
571
+ */
56
572
  params: Record<string, string>;
57
- /** Resolved view path */
573
+ /**
574
+ * Resolved view path for the current URL.
575
+ * May be undefined before framework boot.
576
+ */
58
577
  view?: string;
59
- /** Resolved path */
578
+ /**
579
+ * Resolved path computed from hash path and query path based on routing rules.
580
+ * May be undefined before framework boot.
581
+ */
60
582
  path?: string;
61
- /** Get param by key with optional default */
583
+ /**
584
+ * Get param by key with optional default value.
585
+ * Returns default value or empty string if key does not exist.
586
+ * @param key Parameter key name
587
+ * @param defaultValue Default value when key is missing, defaults to empty string
588
+ */
62
589
  get: (key: string, defaultValue?: string) => string;
63
590
  }
591
+ /**
592
+ * URL parameter change representing a parameter value transition from old to new.
593
+ * Used in `Router.diff()` return value to describe parameter changes.
594
+ */
64
595
  interface ParamDiff {
596
+ /** Value before the change */
65
597
  from: string;
598
+ /** Value after the change */
66
599
  to: string;
67
600
  }
601
+ /**
602
+ * URL route change object interface describing changes between two routing states.
603
+ * Returned by `Router.diff()`, includes changes in path, view, and other parameters.
604
+ */
68
605
  interface LocationDiff {
69
- /** Changed params (key -> {from, to}) */
606
+ /**
607
+ * Changed params (key -> {from, to}),
608
+ * diff for all changed parameters
609
+ */
70
610
  params: Record<string, ParamDiff>;
71
- /** Whether path changed */
611
+ /** Path diff when path has changed */
72
612
  path?: ParamDiff;
73
- /** Whether view changed */
613
+ /** View diff when rendered view has changed */
74
614
  view?: ParamDiff;
75
- /** Whether this is a first forced change */
615
+ /**
616
+ * Whether this is a first forced change during app initialization.
617
+ */
76
618
  force: boolean;
77
- /** Whether anything changed */
619
+ /** Whether any content has changed */
78
620
  changed: boolean;
79
621
  }
622
+ /**
623
+ * Route pre-change event interface (change phase).
624
+ * Provides two-phase confirmation: triggers change (can be rejected), then changed.
625
+ * Can prevent, reject, or accept route changes through this event object.
626
+ */
627
+ interface RouteChangeEvent extends ChangeEvent {
628
+ /**
629
+ * Reject the URL change, revert to previous URL.
630
+ */
631
+ reject: () => void;
632
+ /**
633
+ * Accept the URL change, continue navigation.
634
+ */
635
+ resolve: () => void;
636
+ /**
637
+ * Prevent the URL change, pause subsequent route processing.
638
+ */
639
+ prevent: () => void;
640
+ }
641
+ /**
642
+ * Route post-change event interface (changed phase).
643
+ * Carries route diff information. Triggered after route change is confirmed and URL is updated.
644
+ */
645
+ type RouteChangedEvent = LocationDiff & ChangeEvent;
80
646
  interface VDomRef {
81
647
  /** ID update list: [element, newId][] */
82
648
  idUpdates: [Element, string][];
83
649
  /** Views that need post-processing */
84
- views: ViewInstance[];
650
+ views: ViewInterface[];
85
651
  /** DOM operation list: [opCode, parent, newChild?, oldChild?][] */
86
652
  domOps: VDomOp[];
87
653
  /** Whether anything changed */
88
654
  hasChanged: number;
89
655
  }
90
- 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>;
656
+ /**
657
+ * Encoded VDOM mutation. The op code matches `Node.appendChild` family at the
658
+ * DOM level — parents are always Elements (you can't appendChild onto text)
659
+ * but the moving / replaced child can be any ChildNode (Element / Text /
660
+ * Comment), so the child slots are typed as ChildNode.
661
+ */
662
+ type VDomOp = [1, Element, ChildNode] | [2, Element, ChildNode] | [4, Element, ChildNode, ChildNode] | [8, Element, ChildNode, ChildNode];
93
663
  interface FrameInvokeEntry {
94
664
  /** Method name */
95
665
  name: string;
96
666
  /** Method arguments */
97
667
  args: unknown[];
98
- /** Internal key for dedup */
668
+ /** Internal key */
99
669
  key: string;
100
670
  /** Whether removed (args match) */
101
671
  removed?: boolean;
102
672
  }
103
673
  /** Mixin event handler with internal merge marker and handler list */
104
674
  interface MixinEventHandler extends AnyFunc {
105
- /** Merged handler list */ a?: AnyFunc[];
106
- /** Mixin marker: 1 = this is a mixin function */ b?: number;
675
+ /** Merged handler list */
676
+ handlerList?: AnyFunc[];
677
+ /** Mixin marker: 1 = this is a mixin function */
678
+ marker?: number;
107
679
  }
108
680
  /** View event selector map entry: handler name list with selector presence tracking */
109
681
  interface ViewEventSelectorEntry {
@@ -112,8 +684,12 @@ interface ViewEventSelectorEntry {
112
684
  /** Index signature for checking if selector is already registered */
113
685
  [selector: string]: unknown;
114
686
  }
115
- /** View class internal markers (SPLITTER key) */
116
- type ViewClassInternal = Record<string, AnyFunc[]>;
687
+ /**
688
+ * Compiled template function signature.
689
+ * `data`/`viewId`/`refData` are required; subsequent encoder args are
690
+ * injected by the Updater (encodeHTML/encodeSafe/encodeURIExtra/refFn/encodeQ).
691
+ */
692
+ type ViewTemplate = (data: unknown, viewId: string, refData: unknown, ...encoders: unknown[]) => string;
117
693
  interface ViewLocationObserved {
118
694
  /** Whether observing location */
119
695
  flag: number;
@@ -128,8 +704,6 @@ interface ViewResourceEntry {
128
704
  /** Whether to destroy when render() is called */
129
705
  destroyOnRender: boolean;
130
706
  }
131
- type ViewResourceMap = Record<string, ViewResourceEntry>;
132
- type ViewEventSelectorMap = Record<string, ViewEventSelectorEntry>;
133
707
  interface ViewGlobalEventEntry {
134
708
  /** Handler function */
135
709
  handler: AnyFunc;
@@ -142,30 +716,160 @@ interface ViewGlobalEventEntry {
142
716
  /** Modifiers */
143
717
  modifiers: Record<string, boolean>;
144
718
  }
145
- type ViewEventObjectMap = Record<string, number>;
146
- interface ViewInstance {
147
- /** View ID (same as owner frame ID) */
719
+ /**
720
+ * View configuration for listening to URL changes.
721
+ * Used as object parameter for `observeLocation()` method.
722
+ */
723
+ interface ViewObserveLocation {
724
+ /**
725
+ * Whether to listen for path changes.
726
+ */
727
+ observePath?: boolean;
728
+ /**
729
+ * Parameter keys to observe, supports comma-separated string or string array.
730
+ */
731
+ params?: string | string[];
732
+ }
733
+ /**
734
+ * Router interface providing URL parsing, navigation, diff, and event listening capabilities.
735
+ * Supports two-phase route confirmation mechanism: change (can reject) → changed.
736
+ * Hash-based implementation using #! as default hash prefix.
737
+ */
738
+ interface RouterInterface extends EventEmitterInterface<RouterInterface> {
739
+ /**
740
+ * Parse href into Location object.
741
+ * Parses query and hash sections of href, returns structured routing information.
742
+ * Defaults to parsing current page `location.href`.
743
+ * @param href URL to parse, uses `location.href` if not specified
744
+ */
745
+ parse(href?: string): Location;
746
+ /**
747
+ * Compute diff between current and previous location.
748
+ * Returns undefined if no routing changes have occurred yet.
749
+ */
750
+ diff(): LocationDiff | undefined;
751
+ /**
752
+ * Navigate to new URL.
753
+ * Supports two calling modes:
754
+ * - `Router.to("/list", { page: 2 })` specify path and params
755
+ * - `Router.to({ page: 2 })` update params only, keep current path
756
+ * @param pathOrParams Path string or params object
757
+ * @param params Query params object (only used when first arg is path string)
758
+ * @param replace Whether to replace current history entry instead of adding new one
759
+ * @param silent Whether to silently update without triggering change event
760
+ */
761
+ to(pathOrParams: string | Record<string, unknown>, params?: Record<string, unknown>, replace?: boolean, silent?: boolean): void;
762
+ /** Join path segments */
763
+ join(...paths: string[]): string;
764
+ /**
765
+ * Register an async-friendly navigation guard.
766
+ *
767
+ * Each guard is invoked with the parsed `(to, from)` Locations. Guards
768
+ * may return a Promise; the router awaits all guards in registration
769
+ * order. If any guard:
770
+ *
771
+ * - returns / resolves to `false`,
772
+ * - throws or rejects,
773
+ *
774
+ * the navigation is aborted and the URL is reverted. Returning `true`,
775
+ * `undefined`, or any non-false value permits the navigation.
776
+ *
777
+ * Returns an unsubscribe function so the guard can be torn down (e.g.
778
+ * inside a view's `destroy` handler).
779
+ */
780
+ beforeEach(guard: (to: Location, from: Location) => boolean | void | Promise<boolean | void>): () => void;
781
+ /** Internal: bind hashchange (called by Framework.boot) */
782
+ _bind(): void;
783
+ /** Internal: set framework config */
784
+ _setConfig(cfg: FrameworkConfig): void;
785
+ /** Internal: notify hash change (for programmatic trigger) */
786
+ notify?(e?: Event): void;
787
+ /**
788
+ * Triggered when URL is about to change (change phase), can reject or prevent navigation via event object.
789
+ */
790
+ onChange?: (e?: RouteChangeEvent) => void;
791
+ /**
792
+ * Triggered after URL has changed (changed phase), carries route diff information.
793
+ */
794
+ onChanged?: (e?: RouteChangedEvent) => void;
795
+ }
796
+ /**
797
+ * Service event interface, triggered when request starts or ends.
798
+ * Includes begin/done/fail/end event types.
799
+ */
800
+ interface ServiceEvent extends ChangeEvent {
801
+ /**
802
+ * Data payload object carrying this request's data.
803
+ */
804
+ readonly payload: PayloadInterface;
805
+ /**
806
+ * Error object, present if request throws an error, otherwise null.
807
+ */
808
+ readonly error: object | string | null;
809
+ }
810
+ /**
811
+ * View event interface carrying the ID of the node that triggered the event.
812
+ * Carried in DOM events bound via @event attribute.
813
+ */
814
+ interface ViewEvent extends ChangeEvent {
815
+ /**
816
+ * DOM node ID that triggered the event.
817
+ */
818
+ readonly id: string;
819
+ }
820
+ /**
821
+ * Frame static event interface carrying associated Frame instance.
822
+ * Carried in Frame's add/remove static events.
823
+ */
824
+ interface FrameStaticEvent extends ChangeEvent {
825
+ /**
826
+ * Associated Frame instance object.
827
+ */
828
+ readonly frame: FrameInterface;
829
+ }
830
+ interface ViewInterface extends EventEmitterInterface<ViewInterface> {
831
+ /**
832
+ * View ID (same as owner frame ID),
833
+ * DOM node ID where current view resides.
834
+ */
148
835
  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 */
836
+ /**
837
+ * Owner frame,
838
+ * Frame instance holding current view.
839
+ * May be numeric placeholder 0 before view initialization completes.
840
+ * TODO: Migrate numeric placeholder 0 to undefined or null
841
+ */
842
+ owner: FrameInterface | number;
843
+ /**
844
+ * Updater instance managing view data binding and VDOM rendering.
845
+ */
846
+ updater: UpdaterInterface;
847
+ /**
848
+ * Signature: > 0 means active, incremented on render, 0 = destroyed */
154
849
  signature: number;
155
850
  /** Whether rendered at least once */
156
851
  rendered?: boolean;
157
- /** Whether view has template */
158
- template?: AnyFunc | string;
852
+ /**
853
+ * View template function. Receives data + viewId + refData and a set of
854
+ * encoder helpers wired in by the Updater, and returns the rendered HTML.
855
+ */
856
+ template?: ViewTemplate;
857
+ /**
858
+ * Mixin object array for extending view functionality.
859
+ * Framework merges properties and methods from mixins into view prototype.
860
+ * Event method conflicts are automatically merged into sequential calls.
861
+ */
862
+ mixins?: Record<string, unknown>[];
159
863
  /** Location observation config */
160
864
  locationObserved: ViewLocationObserved;
161
865
  /** Observed state keys */
162
866
  observedStateKeys?: string[];
163
867
  /** Resource map */
164
- resources: ViewResourceMap;
868
+ resources: Record<string, ViewResourceEntry>;
165
869
  /** Selector event map: eventType -> selector list */
166
- eventSelectorMap: ViewEventSelectorMap;
870
+ eventSelectorMap: Record<string, ViewEventSelectorEntry>;
167
871
  /** Event object map: eventType -> bitmask */
168
- eventObjectMap: ViewEventObjectMap;
872
+ eventObjectMap: Record<string, number>;
169
873
  /** Global event list */
170
874
  globalEventList: ViewGlobalEventEntry[];
171
875
  /** Assign method reference */
@@ -173,53 +877,371 @@ interface ViewInstance {
173
877
  /** Whether endUpdate has been called (1 = pending) */
174
878
  endUpdatePending?: number;
175
879
  /** Render method (wrapped) */
176
- render: AnyFunc;
177
- /** init method */
178
- init: AnyFunc;
880
+ render(): void;
881
+ /**
882
+ * Init method called after view is mounted.
883
+ * Used for initialization logic.
884
+ * Framework passes two arguments during actual invocation:
885
+ * - initParams: initialization parameter object
886
+ * - options: contains `node: Element` and `deep: boolean`
887
+ */
888
+ init(): void;
179
889
  /** Wrapped render method */
180
- $b?: AnyFunc;
890
+ renderMethod?: AnyFunc;
181
891
  /** 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;
892
+ endUpdatePendingFlag?: number;
893
+ /**
894
+ * Notify view that HTML update is about to begin for a specific region.
895
+ * Framework unmounts child Frames in that region to prevent VDOM diff from operating on unmounted nodes.
896
+ * @param id Region node ID to update, defaults to current view
897
+ */
186
898
  beginUpdate: (id?: string) => void;
899
+ /**
900
+ * Notify view that HTML update has completed for a specific region.
901
+ * Framework mounts child Frames in that region and executes deferred invoke queue.
902
+ * @param id Region node ID that finished updating, defaults to current view
903
+ * @param inner Whether this is an internal framework call
904
+ */
187
905
  endUpdate: (id?: string, inner?: boolean) => void;
906
+ /**
907
+ * Wrap async callback to ensure it only executes if view is not destroyed.
908
+ * In SPAs, async callbacks (e.g., setTimeout, AJAX) may execute after view is destroyed,
909
+ * causing errors when manipulating DOM.
910
+ * After wrapping with `wrapAsync`, framework automatically checks view state and only executes callback if view is alive.
911
+ * @param fn Callback function to wrap
912
+ * @param context `this` binding for callback execution, defaults to view itself
913
+ */
188
914
  wrapAsync: <Fn extends AnyFunc>(fn: Fn, context?: unknown) => (...args: Parameters<Fn>) => ReturnType<Fn> | undefined;
915
+ /**
916
+ * Listen for URL bar changes, supports two calling modes:
917
+ * - `observeLocation("page,size", true)` pass parameter keys (comma-separated) and whether to observe path
918
+ * - `observeLocation({ params: ["page", "size"], path: true })` pass config object
919
+ * View automatically re-renders when observed parameters or path change.
920
+ * @param params Parameter keys to observe, supports comma-separated string, string array, or config object
921
+ * @param observePath Whether to observe path changes
922
+ */
189
923
  observeLocation: (params: string | string[] | Record<string, unknown>, observePath?: boolean) => void;
924
+ /**
925
+ * Observe data changes for specified keys in State.
926
+ * View automatically re-renders when observed keys are updated via `State.digest()`.
927
+ * @param keys Comma-separated key string or string array
928
+ */
190
929
  observeState: (keys: string | string[]) => void;
930
+ /**
931
+ * Hand over resource to current view for lifecycle management.
932
+ * Framework automatically calls resource's destroy method at appropriate time when view unmounts or re-renders.
933
+ * @param key Unique key for managed resource; if key already manages different resource, old resource is auto-destroyed
934
+ * @param resource Resource object to manage
935
+ * @param destroyOnRender Whether to auto-destroy resource when render method is called; Service instances typically need auto-destroy on render
936
+ */
191
937
  capture: (key: string, resource?: unknown, destroyOnRender?: boolean) => unknown;
938
+ /**
939
+ * Release managed resource, returns the resource object regardless of destruction state.
940
+ * @param key Managed resource key
941
+ * @param destroy Whether to destroy resource (call its destroy method), defaults to true
942
+ */
192
943
  release: (key: string, destroy?: boolean) => unknown;
944
+ /**
945
+ * Set leave prompt, e.g., when form has unsaved changes.
946
+ * Can prompt user to choose between leaving directly or saving before leaving.
947
+ * Framework calls condition function during route changes (change phase) and page unloads (beforeunload).
948
+ * Navigation is prevented if condition returns true.
949
+ * @param message Leave prompt message
950
+ * @param condition Function to determine whether to show leave prompt; returns true to prevent navigation
951
+ */
193
952
  leaveTip: (message: string, condition: () => boolean) => void;
194
- assign: (options?: unknown) => boolean | undefined;
953
+ /**
954
+ * Assign method for incremental DOM updates.
955
+ * Framework uses VDOM diff (in-memory real DOM diff) to update only changed portions,
956
+ * automatically handling child view mounting and unmounting.
957
+ * Returns true if DOM changed, undefined if no change.
958
+ * @param options Incremental update config, used internally by framework
959
+ */
960
+ assign?: (options?: unknown) => boolean | undefined;
961
+ /**
962
+ * Triggered when view is destroyed.
963
+ */
964
+ onDestroy?: (e?: ChangeEvent) => void;
965
+ /**
966
+ * Triggered when render method is called.
967
+ */
968
+ onRender?: (e?: ChangeEvent) => void;
969
+ /**
970
+ * Inherit View to create new view subclass.
971
+ * Supports props.make constructor, props.mixins, and event methods (e.g., `'name<click>'`).
972
+ * @param props Prototype object containing init, render, and other methods
973
+ * @param statics Object of static methods or properties
974
+ */
975
+ extend?<TProps = object, TStatics = object>(props?: ExtendThisType<TProps & ViewInterface>, statics?: TStatics): ViewInterface & TStatics;
976
+ /**
977
+ * Merge multiple mixin objects into View prototype.
978
+ * Existing properties are not overwritten; event method conflicts are automatically merged into sequential calls.
979
+ * @param args Mixin object list
980
+ */
981
+ merge?(...args: ExtendThisType<ViewInterface>[]): ViewInterface;
982
+ navigate?: (path: string, params?: Record<string, unknown>) => void;
195
983
  }
196
- /** Minimal Frame interface needed by View */
197
- interface FrameLike {
984
+ type ExtendThisType<T> = Record<string, unknown> & ThisType<T>;
985
+ /**
986
+ * Minimal Frame interface needed by View.
987
+ * Frame (View Frame) is a view container managing view mount, unmount, and parent-child hierarchy.
988
+ * Each Frame corresponds to one DOM node, associated with view via v-lark attribute.
989
+ */
990
+ interface FrameInterface extends EventEmitterInterface<FrameInterface> {
991
+ /**
992
+ * DOM node ID where Frame resides.
993
+ */
198
994
  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;
995
+ /**
996
+ * View module path currently rendered by this Frame, e.g., "app/views/default".
997
+ */
998
+ readonly viewPath?: string;
999
+ /**
1000
+ * Parent Frame ID, undefined if this is a top-level Frame.
1001
+ */
1002
+ readonly parentId: string | undefined;
1003
+ /**
1004
+ * Mount view to current Frame.
1005
+ * Framework loads view class, creates instance, and renders view.
1006
+ * @param viewPath View module path, e.g., "app/views/default"
1007
+ * @param viewInitParams Parameters passed during view initialization, accessible in view's init method
1008
+ */
1009
+ mountView(viewPath: string, viewInitParams?: Record<string, unknown>): void;
1010
+ /**
1011
+ * Unmount view from current Frame, triggers view's destroy event and cleans up resources.
1012
+ */
1013
+ unmountView(): void;
1014
+ /**
1015
+ * Mount child Frame on specified DOM node and render view.
1016
+ * @param frameId DOM node ID for rendering
1017
+ * @param viewPath View path
1018
+ * @param viewInitParams Parameters passed during view initialization
1019
+ */
1020
+ mountFrame: (frameId: string, viewPath: string, viewInitParams?: Record<string, unknown>) => FrameInterface;
1021
+ /**
1022
+ * Unmount child Frame from specified DOM node.
1023
+ * @param id DOM node ID, defaults to current Frame if omitted
1024
+ */
203
1025
  unmountFrame: (id?: string, inner?: boolean) => void;
204
- view: ViewInstance | undefined;
205
- children: () => string[];
1026
+ /**
1027
+ * Render all child views under specified node (scans v-lark attributes and mounts).
1028
+ * @param zoneId DOM node ID, defaults to current Frame
1029
+ * @param inner Whether this is an internal framework call
1030
+ */
1031
+ mountZone: (zoneId?: string, inner?: boolean) => void;
1032
+ /**
1033
+ * Unmount all child views under specified node.
1034
+ * @param zoneId DOM node ID, defaults to current Frame
1035
+ */
1036
+ unmountZone: (zoneId?: string, inner?: boolean) => void;
1037
+ /**
1038
+ * Get ancestor Frame, defaults to parent Frame (level=1).
1039
+ * @param level Levels to traverse upward, defaults to 1
1040
+ */
1041
+ parent(level?: number): FrameInterface | undefined;
1042
+ /**
1043
+ * Invoke specified method on view in current Frame.
1044
+ * If view is not rendered yet, invocation is deferred until render completes.
1045
+ * @param name Method name
1046
+ * @param args Arguments array passed to method
1047
+ */
206
1048
  invoke: (name: string, args?: unknown[]) => unknown;
1049
+ /**
1050
+ * Triggered when all descendant views have been created.
1051
+ */
1052
+ onCreated?: (e?: ChangeEvent) => void;
1053
+ /**
1054
+ * Triggered when descendant views change.
1055
+ */
1056
+ onAlter?: (e?: ChangeEvent) => void;
1057
+ /**
1058
+ * Get Frame instance by ID, returns undefined if not exists.
1059
+ * @param id Frame's DOM node ID
1060
+ */
1061
+ get?(id: string): FrameInterface | undefined;
1062
+ /**
1063
+ * Get all Frame instances map for current page.
1064
+ */
1065
+ getAll?(): Map<string, FrameInterface>;
1066
+ /**
1067
+ * Triggered when Frame is created and registered.
1068
+ */
1069
+ onAdd?: (e?: FrameStaticEvent) => void;
1070
+ /**
1071
+ * Triggered when Frame is destroyed and unregistered.
1072
+ */
1073
+ onRemove?: (e?: FrameStaticEvent) => void;
1074
+ view: ViewInterface | undefined;
1075
+ /**
1076
+ * Get ID array of all child Frames for current Frame.
1077
+ * Note: ID positions in array are not fixed.
1078
+ */
1079
+ children: () => string[];
207
1080
  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
1081
  }
212
- /** Minimal Updater interface needed by View */
213
- interface UpdaterLike {
1082
+ /**
1083
+ * Minimal Updater interface needed by View.
1084
+ * View updater responsible for view data binding and data/page updates.
1085
+ * Each View instance has an Updater, triggering data/page updates via set/digest.
1086
+ * Internally executes complete pipeline: template rendering → VDOM diff (in-memory real DOM diff) → DOM operations.
1087
+ */
1088
+ interface UpdaterInterface {
1089
+ /**
1090
+ * Get data that has been set.
1091
+ * Returns complete data object if key is omitted, otherwise returns value for specified key.
1092
+ * @param key Data key name, omitted returns complete data object
1093
+ */
214
1094
  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 */
1095
+ /**
1096
+ * Set data and track changed keys.
1097
+ * After set, must explicitly call `digest()` to commit changes to page.
1098
+ * Returns this for chaining.
1099
+ * @param data Data object, e.g., `{ a: 1, b: 2 }`
1100
+ * @param excludes Set of keys to exclude from change tracking
1101
+ */
1102
+ set: (data: Record<string, unknown>, excludes?: ReadonlySet<string>) => UpdaterInterface;
1103
+ /**
1104
+ * Trigger page re-render.
1105
+ * After set, must explicitly call `digest()` to commit changes to page.
1106
+ * Internally executes complete pipeline: template rendering → VDOM diff (in-memory real DOM diff) → DOM operations.
1107
+ * @param data Optional data object, if provided calls `set()` first to set data
1108
+ * @param excludes Set of keys to exclude from change tracking
1109
+ * @param callback Callback executed after render completes
1110
+ */
1111
+ digest: (data?: Record<string, unknown>, excludes?: ReadonlySet<string>, callback?: () => void) => void;
1112
+ /**
1113
+ * Save a snapshot of current data for altered() detection.
1114
+ * Works with `altered()` method to detect whether data has changed.
1115
+ */
1116
+ snapshot: () => UpdaterInterface;
1117
+ /**
1118
+ * Check if data has changed since last snapshot.
1119
+ * Returns undefined if `snapshot()` has not been called.
1120
+ */
220
1121
  altered: () => boolean | undefined;
221
1122
  /** Ref data for template rendering */
222
1123
  refData: Record<string, unknown>;
1124
+ /**
1125
+ * Translate raw reference data starting with @ symbol in template.
1126
+ * Replaces `{{@refData}}` with actual value from refData.
1127
+ * @param data Reference data to translate
1128
+ */
1129
+ translate(data: unknown): unknown;
1130
+ /**
1131
+ * Parse expression string.
1132
+ * @param expr Expression string to parse
1133
+ */
1134
+ parse(expr: string): unknown;
1135
+ }
1136
+ /**
1137
+ * Data payload interface wrapping API request response data, providing read/write methods.
1138
+ * Payload instances are created internally by Service, developers access via all/one/save callbacks.
1139
+ */
1140
+ interface PayloadInterface {
1141
+ /**
1142
+ * Get data from Payload by key.
1143
+ * @param key Data key name
1144
+ */
1145
+ get<T = unknown>(key: string): T;
1146
+ /**
1147
+ * Set data to Payload, supports three calling modes:
1148
+ * - Key-value pair: `payload.set("name", "value")`
1149
+ * - Data object: `payload.set({ name: "value" })`
1150
+ * - Endpoint metadata object (for internal framework use)
1151
+ * Returns this for chaining.
1152
+ * @param keyOrData Key/value string, data object, or endpoint metadata object
1153
+ * @param value Value when first parameter is a key
1154
+ */
1155
+ set(keyOrData: string | Record<string, unknown> | ServiceMetaEntry, value?: unknown): PayloadInterface;
1156
+ data: Record<string, unknown>;
1157
+ cacheInfo?: ServiceCacheInfo;
1158
+ }
1159
+ /**
1160
+ * Change event object.
1161
+ */
1162
+ interface ChangeEvent {
1163
+ /**
1164
+ * Event type.
1165
+ */
1166
+ readonly type: string;
1167
+ /**
1168
+ * Set of changed data keys. Use `keys.has(name)` to check membership.
1169
+ */
1170
+ readonly keys?: ReadonlySet<string>;
1171
+ }
1172
+ /**
1173
+ * Event emitter interface providing on/off/fire methods for publish-subscribe pattern.
1174
+ */
1175
+ interface EventEmitterInterface<T = unknown> {
1176
+ /**
1177
+ * Bind event listener, calls handler when event is triggered.
1178
+ * @param name Event name
1179
+ * @param fn Event handler function
1180
+ */
1181
+ on(name: string, fn: (this: T, e?: ChangeEvent) => void): EventEmitterInterface<T>;
1182
+ /**
1183
+ * Unbind event listener, removes all handlers for event if no handler function is provided.
1184
+ * @param name Event name
1185
+ * @param fn Optional event handler function, if omitted removes all handlers
1186
+ */
1187
+ off(name: string, fn?: AnyFunc): EventEmitterInterface<T>;
1188
+ /**
1189
+ * Fire event, executes all bound handlers, automatically adds type property to event data.
1190
+ * Supports removing all handlers after firing.
1191
+ * Supports executing handler list in reverse order.
1192
+ * @param name Event name
1193
+ * @param data Event data object
1194
+ * @param remove Whether to remove all handlers after firing
1195
+ * @param lastToFirst Whether to execute handler list in reverse order
1196
+ */
1197
+ fire(name: string, data?: Record<string, unknown>, remove?: boolean, lastToFirst?: boolean): EventEmitterInterface<T>;
1198
+ }
1199
+ /**
1200
+ * Global state interface providing cross-view data sharing and data change notification capabilities.
1201
+ * State is a singleton object managing app-level state data via get/set/digest.
1202
+ * Supports `clean()` method to create a mixin for automatic cleanup on view destruction.
1203
+ *
1204
+ * Use State for SIMPLE cross-view data (lightweight shared values: counters,
1205
+ * toggles, page title, session info, etc.). For COMPLEX reactive state —
1206
+ * handlers, derived data, multi-instance isolation, or Proxy-based fine-grained
1207
+ * dependency tracking — use `defineStore` instead.
1208
+ */
1209
+ interface StateInterface extends EventEmitterInterface<StateInterface> {
1210
+ /**
1211
+ * Get data from global state, returns complete state object if key is omitted.
1212
+ * @param key Data key name, omitted returns complete state object
1213
+ */
1214
+ get<T = unknown>(key?: string): T;
1215
+ /**
1216
+ * Set global state data.
1217
+ * After set, must explicitly call `digest()` to dispatch changed event and notify views to update.
1218
+ * @param data Data object, e.g., `{ a: 1, b: 2 }`
1219
+ * @param excludes Set of keys to exclude from change tracking
1220
+ */
1221
+ set(data: Record<string, unknown>, excludes?: ReadonlySet<string>): this;
1222
+ /**
1223
+ * Clean data for specified keys in State, can only be used in view's mixins.
1224
+ * For example `mixins: [State.clean("a,b")]`.
1225
+ * Keys registered via this method are automatically cleaned when view is destroyed,
1226
+ * and corresponding key reference counts are decremented; data is auto-deleted when count reaches zero.
1227
+ * @param keys Comma-separated key string
1228
+ * @returns Object with make method, called by mixins mechanism
1229
+ */
1230
+ clean(keys: string): {
1231
+ make: AnyFunc;
1232
+ };
1233
+ /**
1234
+ * Detect data changes and dispatch changed event.
1235
+ * After set, must explicitly call `digest()` to dispatch changed event and notify views to update.
1236
+ * @param data Optional data object, if provided calls `set()` first to set data
1237
+ * @param excludes Set of keys to exclude from change tracking
1238
+ */
1239
+ digest(data?: Record<string, unknown>, excludes?: ReadonlySet<string>): void;
1240
+ /**
1241
+ * Get the set of keys changed in the most recent digest.
1242
+ */
1243
+ diff: () => ReadonlySet<string>;
1244
+ onChanged?: (e?: ChangeEvent) => void;
223
1245
  }
224
1246
  interface ServiceOptions {
225
1247
  /** Request URL */
@@ -227,14 +1249,14 @@ interface ServiceOptions {
227
1249
  /** Request params */
228
1250
  params?: Record<string, unknown>;
229
1251
  /** HTTP method */
230
- method?: "GET" | "POST" | "PUT" | "DELETE";
1252
+ method?: "GET" | "POST" | "PUT" | "DELETE" | string;
231
1253
  /** Cache: true for default, number for TTL */
232
1254
  cache?: boolean | number;
233
1255
  /** POST data */
234
1256
  data?: unknown;
235
1257
  }
236
- interface BagEntry {
237
- /** Bag data */
1258
+ interface PayloadEntry {
1259
+ /** Payload data */
238
1260
  data: Record<string, unknown>;
239
1261
  }
240
1262
  interface ServiceEntry {
@@ -244,8 +1266,8 @@ interface ServiceEntry {
244
1266
  url: string;
245
1267
  /** Cache time in ms, 0 = no cache */
246
1268
  cacheTime: number;
247
- /** Bag instance */
248
- bag?: BagEntry;
1269
+ /** Payload instance */
1270
+ payload?: PayloadEntry;
249
1271
  /** Whether loading */
250
1272
  loading?: boolean;
251
1273
  /** Error info */
@@ -253,26 +1275,151 @@ interface ServiceEntry {
253
1275
  }
254
1276
  /** Pending cache entry for deduplication (internal to Service) */
255
1277
  interface PendingCacheEntry extends Array<unknown> {
256
- /** Reference to the pending Bag entity */
257
- e?: unknown;
1278
+ /** Reference to the pending Payload entity */
1279
+ entity?: unknown;
258
1280
  }
1281
+ /**
1282
+ * Endpoint metadata configuration for registering an API endpoint with Service.
1283
+ * Each meta describes endpoint's URL, cache strategy, before/after interceptors, etc.
1284
+ */
259
1285
  interface ServiceMetaEntry {
260
- /** Endpoint name */
1286
+ /**
1287
+ * Endpoint name,
1288
+ * Unique name for endpoint metadata, must be unique within same Service.
1289
+ */
261
1290
  name: string;
262
- /** URL */
1291
+ /** Request URL, required. */
263
1292
  url: string;
264
- /** Cache TTL in ms, 0 = no cache */
1293
+ /**
1294
+ * Cache TTL in ms, 0 = no cache.
1295
+ * Cache validity time in milliseconds.
1296
+ * 0 means no caching.
1297
+ * Greater than 0 means cache TTL, reuse cached data within this time range.
1298
+ */
265
1299
  cache?: number;
266
- /** Before-fetch hook */
267
- before?: AnyFunc;
268
- /** After-fetch hook */
269
- after?: AnyFunc;
270
- /** Clean keys on destroy */
271
- cleans?: string;
1300
+ /**
1301
+ * Before-fetch hook,
1302
+ * Hook function called before request is sent, can process request data.
1303
+ * `this` points to current Payload instance.
1304
+ * @param payload Data carrier for current request
1305
+ */
1306
+ before?: (this: PayloadInterface, payload: PayloadInterface) => void;
1307
+ /**
1308
+ * After-fetch hook.
1309
+ * Hook function called after request succeeds, before data is passed to view.
1310
+ * Can process response data in this method.
1311
+ * `this` points to current Payload instance.
1312
+ * @param payload Data carrier for current request
1313
+ */
1314
+ after?: (this: PayloadInterface, payload: PayloadInterface) => void;
1315
+ /** Clean keys on destroy,
1316
+ * Comma-separated endpoint name string for clearing other endpoints' cache.
1317
+ * For example, if an endpoint creates new data,
1318
+ * after successful call, should clear all data-fetching endpoints' cache,
1319
+ * otherwise new data cannot be retrieved.
1320
+ */
1321
+ cleanKeys?: string;
272
1322
  /** Additional properties */
273
1323
  [key: string]: unknown;
274
1324
  }
275
- /** Cache info attached to Bag entity */
1325
+ interface ServiceInternals {
1326
+ metaList: Record<string, ServiceMetaEntry>;
1327
+ payloadCache: CacheInterface<PayloadInterface>;
1328
+ pendingCacheKeys: Record<string, PendingCacheEntry>;
1329
+ syncFn: (payload: PayloadInterface, callback: () => void) => void;
1330
+ staticEmitter: EventEmitterInterface;
1331
+ }
1332
+ interface ServiceInterface {
1333
+ id: string;
1334
+ destroyed: number;
1335
+ busy: number;
1336
+ taskQueue: AnyFunc[];
1337
+ prevArgs: unknown[];
1338
+ emitter: EventEmitterInterface;
1339
+ internals: ServiceInternals;
1340
+ /**
1341
+ * Send all requests in parallel, execute done callback when all requests complete (success or failure).
1342
+ * If endpoint specifies cache and cache is valid, uses cached data directly.
1343
+ * @param metaList Endpoint name string, params object, or array of them
1344
+ * @param done Callback when all requests complete, first param is error array, followed by each endpoint's Payload
1345
+ */
1346
+ all(metaList: string | Record<string, unknown> | (string | Record<string, unknown>)[], done: AnyFunc): ServiceInterface;
1347
+ /**
1348
+ * Execute callback after each request succeeds, callback may be called multiple times.
1349
+ * @param metaList Endpoint name string, params object, or array of them
1350
+ * @param done Callback
1351
+ */
1352
+ one(attrs: string | Record<string, unknown> | (string | Record<string, unknown>)[], done: AnyFunc): ServiceInterface;
1353
+ /**
1354
+ * Similar to all, but always skips cache and forces actual requests.
1355
+ * @param metaList Endpoint name string, params object, or array of them
1356
+ * @param done Callback
1357
+ */
1358
+ save(metaList: string | Record<string, unknown> | (string | Record<string, unknown>)[], done: AnyFunc): ServiceInterface;
1359
+ /**
1360
+ * Queue task for execution, executes next task after previous all/one/save task completes, similar to Promise chain.
1361
+ * @param callback Callback invoked after task completes
1362
+ */
1363
+ enqueue(callback: AnyFunc): ServiceInterface;
1364
+ /**
1365
+ * Dequeue and execute next task in queue.
1366
+ */
1367
+ dequeue(...args: unknown[]): void;
1368
+ /**
1369
+ * Destroy current Service instance, cannot send new requests or invoke callbacks after destruction.
1370
+ */
1371
+ destroy(): void;
1372
+ /**
1373
+ * Add endpoint metadata, register one or more API endpoints.
1374
+ * @param metaList Endpoint metadata array, or single metadata object
1375
+ */
1376
+ add(attrs: ServiceMetaEntry | ServiceMetaEntry[]): void;
1377
+ /**
1378
+ * Get metadata object.
1379
+ * @param attrs Endpoint metadata object or name string
1380
+ */
1381
+ meta(attrs: string | Record<string, unknown>): ServiceMetaEntry;
1382
+ /**
1383
+ * Create Payload object from endpoint metadata.
1384
+ * @param meta Endpoint metadata object or name string
1385
+ */
1386
+ create(meta: Record<string, unknown>): PayloadInterface;
1387
+ /**
1388
+ * Get or create Payload object from cache.
1389
+ * @param meta Endpoint metadata object
1390
+ * @param createNew Whether to create new Payload object; if false, prioritizes getting from cache
1391
+ */
1392
+ get(meta: Record<string, unknown>, createNew?: boolean): {
1393
+ entity: PayloadInterface;
1394
+ needsUpdate: boolean;
1395
+ };
1396
+ /**
1397
+ * Get Payload object from cache, returns undefined if cache doesn't exist or has expired.
1398
+ * @param meta Endpoint metadata object
1399
+ */
1400
+ cached(meta: Record<string, unknown>): PayloadInterface | undefined;
1401
+ /**
1402
+ * Clear cached data for specified endpoint.
1403
+ * @param names Comma-separated endpoint name string or string array
1404
+ */
1405
+ clear(names: string | string[]): void;
1406
+ /**
1407
+ * Inherit to create new Service subclass, bind custom data sync function.
1408
+ * @param sync Method to sync data, typically exchanges data with server
1409
+ * @param cacheMax Maximum cache entries
1410
+ * @param cacheBuffer Cache buffer size
1411
+ */
1412
+ extend(newSyncFn: (payload: PayloadInterface, callback: () => void) => void, newCacheMax?: number, newCacheBuffer?: number): ServiceInterface;
1413
+ /**
1414
+ * Triggered before endpoint sends request.
1415
+ */
1416
+ onBegin?: (e?: ServiceEvent) => void;
1417
+ /**
1418
+ * Triggered when endpoint request completes, whether success or failure.
1419
+ */
1420
+ onEnd?: (e?: ServiceEvent) => void;
1421
+ }
1422
+ /** Cache info attached to Payload entity */
276
1423
  interface ServiceCacheInfo {
277
1424
  /** Endpoint name */
278
1425
  name: string;
@@ -285,34 +1432,284 @@ interface ServiceCacheInfo {
285
1432
  /** Timestamp when cached */
286
1433
  time: number;
287
1434
  }
1435
+ interface FrameworkInterface {
1436
+ /**
1437
+ * Read framework configuration.
1438
+ * - Without arguments: returns the complete config object.
1439
+ * - With a key: returns just `config[key]` (untyped — use a generic to
1440
+ * constrain the return type if you know the key's shape).
1441
+ *
1442
+ * `getConfig` is a pure read — call `setConfig(patch)` to mutate.
1443
+ */
1444
+ getConfig(): FrameworkConfig;
1445
+ getConfig<T = unknown>(key: string): T | undefined;
1446
+ /**
1447
+ * Merge a patch into the framework configuration and return the merged
1448
+ * config object. Replaces the dual-purpose overload of `config()`.
1449
+ */
1450
+ setConfig<T extends object = Partial<FrameworkConfig>>(patch: Partial<FrameworkConfig> & T): FrameworkConfig & T;
1451
+ /**
1452
+ * @deprecated Use `getConfig()` / `getConfig(key)` for reads and
1453
+ * `setConfig(patch)` for writes. The overloaded `config()` blurred the
1454
+ * two and confused TypeScript inference; the split is a drop-in upgrade.
1455
+ */
1456
+ config<T extends object = Partial<FrameworkConfig>>(cfg?: Partial<FrameworkConfig> & T): FrameworkConfig & T;
1457
+ /** @deprecated See above. */
1458
+ config(key: string): unknown;
1459
+ /**
1460
+ * App initialization entry point, starts framework and renders root view.
1461
+ * After invocation: merge config → bind route events → create root Frame → mount default view.
1462
+ * @param cfg Config object
1463
+ */
1464
+ boot(cfg: FrameworkConfig): void;
1465
+ /**
1466
+ * Convert array to hash map object.
1467
+ * - Simple array: `Framework.toMap([1,2,3])` => `{1:1, 2:1, 3:1}`
1468
+ * - Object array: `Framework.toMap([{id:20},{id:30}], 'id')` => `{20:{id:20}, 30:{id:30}}`
1469
+ * @param list Source array
1470
+ * @param key Use object's key value from array as map key
1471
+ */
1472
+ toMap<T>(list: T[] | null | undefined, key?: keyof T): Record<string, T | number>;
1473
+ /**
1474
+ * Execute methods in try-catch manner, catches exceptions.
1475
+ * Returns return value of last successfully executed method.
1476
+ * @param fns Function or function array
1477
+ * @param args Arguments array passed to functions
1478
+ * @param context `this` binding during function execution
1479
+ */
1480
+ toTry(fns: AnyFunc | AnyFunc[], args?: unknown[], context?: unknown): unknown;
1481
+ /**
1482
+ * Convert path and params to URL string.
1483
+ * Example: `Framework.toUrl('/xxx/', {a:'b',c:'d'})` => `/xxx/?a=b&c=d`
1484
+ * @param path Path string
1485
+ * @param params Params object
1486
+ * @param keepEmpty Set of keys whose empty values should be preserved
1487
+ */
1488
+ toUrl(path: string, params?: Record<string, unknown>, keepEmpty?: Set<string>): string;
1489
+ /**
1490
+ * Parse URL string to path and params object.
1491
+ * Example: `Framework.parseUrl('/xxx/?a=b&c=d')` => `{path:'/xxx/', params:{a:'b',c:'d'}}`
1492
+ * @param url URL string
1493
+ */
1494
+ parseUrl(url: string): ParsedUri;
1495
+ /**
1496
+ * Merge source object properties into target object.
1497
+ * @param target Target object
1498
+ * @param sources One or more source objects
1499
+ */
1500
+ mix<T extends object>(target: T, ...sources: Partial<T>[]): T;
1501
+ /**
1502
+ * Check if object has specified own property (safe hasOwnProperty).
1503
+ * @param owner Object to check, supports undefined/null
1504
+ * @param prop Property key name
1505
+ */
1506
+ has<T extends object>(owner: T | undefined | null, prop: PropertyKey): boolean;
1507
+ /**
1508
+ * Get enumerable property keys of object as array.
1509
+ * @param src Source object
1510
+ */
1511
+ keys<T extends object>(src: T): string[];
1512
+ /**
1513
+ * Check if one DOM node is contained within another.
1514
+ * Returns true if both nodes are the same.
1515
+ * @param node Node or node ID
1516
+ * @param container Container node or node ID
1517
+ */
1518
+ inside(node: HTMLElement | string, container: HTMLElement | string): boolean;
1519
+ /**
1520
+ * Shorthand for document.getElementById.
1521
+ * Returns directly if Element is passed.
1522
+ * @param id Node ID or Element object
1523
+ */
1524
+ node(id: string | Element | null): Element | null;
1525
+ /**
1526
+ * Ensure DOM element has an ID, auto-generates one if missing.
1527
+ * Returns element's ID.
1528
+ * @param node DOM element object
1529
+ */
1530
+ nodeId(node: HTMLElement): string;
1531
+ /**
1532
+ * Load modules using configured module loader.
1533
+ * @param names Module names, supports string or string array
1534
+ * @param callback Callback after modules are loaded
1535
+ */
1536
+ use(names: string | string[], callback?: (...modules: unknown[]) => void): void;
1537
+ /**
1538
+ * Protect object from direct modification in debug mode.
1539
+ * Wraps data object with Proxy, intercepts read/write operations, warns to use State.set/digest for state management.
1540
+ * Only effective when `window.__lark_Debug` is true.
1541
+ * @param o Object to protect
1542
+ */
1543
+ guard<T extends object>(o: T): T;
1544
+ /**
1545
+ * Dynamically inject CSS styles into page. Returns cleanup function to remove injected styles.
1546
+ * Supports single and batch injection.
1547
+ * - `Framework.applyStyle("my-style", "body { color: red; }")` single injection
1548
+ * - `Framework.applyStyle(["style1", "css1", "style2", "css2"])` batch injection
1549
+ * @param styleIdOrPairs Style unique key or [id1, css1, id2, css2, ...] batch array
1550
+ * @param cssText CSS style string (only used when first param is string)
1551
+ */
1552
+ applyStyle(styleIdOrPairs: string | string[], cssText?: string): () => void;
1553
+ /**
1554
+ * Generate globally unique identifier (GUID).
1555
+ * @param prefix GUID prefix, defaults to "lark-"
1556
+ */
1557
+ guid(prefix?: string): string;
1558
+ /**
1559
+ * Create async callback validity marker.
1560
+ * Returns a check function; if host object is unmarked (e.g., view re-rendered), check function returns false, preventing expired async callbacks from executing.
1561
+ * Typical usage: `const check = Framework.mark(this, 'render'); setTimeout(() => { if (check()) ... })`
1562
+ * @param host Host object (usually view instance)
1563
+ * @param key Marker key name (usually "render" or specific async operation identifier)
1564
+ */
1565
+ mark(host: object, key: string): () => boolean;
1566
+ /**
1567
+ * Delay wait, Promise-based setTimeout wrapper.
1568
+ * @param time Delay time in milliseconds
1569
+ */
1570
+ delay(time: number): Promise<void>;
1571
+ /**
1572
+ * Whether framework has booted
1573
+ */
1574
+ isBooted(): boolean;
1575
+ /**
1576
+ * Invalidate (unmark) async callback markers for a host object.
1577
+ * Typically called when a view is re-rendered or destroyed.
1578
+ * @param host The host object whose markers should be invalidated
1579
+ */
1580
+ unmark(host: object): void;
1581
+ /**
1582
+ * Fire a custom DOM event on a target element.
1583
+ * @param target Target element or EventTarget
1584
+ * @param eventType Event type string
1585
+ * @param eventInit CustomEvent init options
1586
+ */
1587
+ dispatch(target: EventTarget, eventType: string, eventInit?: CustomEventInit): void;
1588
+ /**
1589
+ * Execute a function in try-catch with chunked scheduling.
1590
+ * @param fn Function to execute
1591
+ * @param args Arguments to pass
1592
+ * @param context `this` context
1593
+ */
1594
+ task(fn: AnyFunc, args?: unknown[], context?: unknown): void;
1595
+ /**
1596
+ * Wait for all views in a zone to be rendered.
1597
+ * Returns WAIT_OK if rendered, WAIT_TIMEOUT_OR_NOT_FOUND if timeout or not found.
1598
+ * @param viewId Zone view ID
1599
+ * @param timeout Timeout in milliseconds (default: 30000)
1600
+ */
1601
+ waitZoneViewsRendered(viewId: string, timeout?: number): Promise<number>;
1602
+ /** Wait result: views rendered successfully */
1603
+ WAIT_OK: number;
1604
+ /** Wait result: timeout or view not found */
1605
+ WAIT_TIMEOUT_OR_NOT_FOUND: number;
1606
+ /**
1607
+ * Base class with EventEmitter.
1608
+ * Inherits EventEmitter for use as a base class in the framework.
1609
+ */
1610
+ Base: typeof EventEmitter;
1611
+ /**
1612
+ * View class.
1613
+ * Use `View.extend()` to create subclasses.
1614
+ */
1615
+ View: typeof View;
1616
+ /**
1617
+ * Cache class.
1618
+ * Use `new Cache()` to create cache instances.
1619
+ */
1620
+ Cache: typeof Cache;
1621
+ /**
1622
+ * Global state object.
1623
+ */
1624
+ State: StateInterface;
1625
+ /**
1626
+ * Router object.
1627
+ */
1628
+ Router: RouterInterface;
1629
+ /**
1630
+ * Frame class.
1631
+ * Frame tree for view lifecycle management. Do not extend or instantiate directly.
1632
+ */
1633
+ Frame: typeof Frame;
1634
+ }
1635
+ /**
1636
+ * Framework configuration interface, global config passed to app during `Framework.boot()`.
1637
+ * All config items can be accessed at runtime via `Framework.config('key')`.
1638
+ */
288
1639
  interface FrameworkConfig {
289
- /** Root element ID */
1640
+ /**
1641
+ * Root element ID.
1642
+ * DOM root node ID where root view resides, framework renders root view within this node.
1643
+ * This field is required, defaults to "root".
1644
+ */
290
1645
  rootId: string;
291
- /** Default view path */
1646
+ /**
1647
+ * Default view path.
1648
+ * Default root view path to load when URL doesn't match any route.
1649
+ */
292
1650
  defaultView?: string;
293
- /** Default path when no hash present */
1651
+ /**
1652
+ * Default path when no hash present,
1653
+ * Path used when URL hash is empty, defaults to "/".
1654
+ */
294
1655
  defaultPath?: string;
295
- /** Route mapping: path -> view */
1656
+ /**
1657
+ * Route mapping: path -> view.
1658
+ * Mapping relationship between paths and views.
1659
+ * - Simple mapping: `{ "/home": "app/views/home" }`
1660
+ * - Config mapping: `{ "/detail": { view: "app/views/detail", title: "Detail" } }`
1661
+ * Use rewrite config item for path rewriting logic.
1662
+ */
296
1663
  routes?: Record<string, string | RouteViewConfig>;
297
1664
  /** Hashbang prefix */
298
1665
  hashbang?: string;
299
- /** Error handler */
1666
+ /**
1667
+ * Error handler.
1668
+ * Global error handling function, framework uses try-catch to execute some core logic.
1669
+ * When errors are thrown, allows developers to catch them via this config item.
1670
+ * Note: Do not re-throw any errors in this method.
1671
+ */
300
1672
  error?: (error: Error) => void;
301
- /** Extensions to load */
302
- exts?: string[];
1673
+ /**
1674
+ * Extensions to load.
1675
+ * Array of extension view paths to load during app startup.
1676
+ * These extension views are loaded before app initialization.
1677
+ */
1678
+ extensions?: string[];
303
1679
  /** Init module to load */
304
- ini?: string;
1680
+ initModule?: string;
305
1681
  /** Rewrite function for routes */
306
1682
  rewrite?: (path: string, params: Record<string, string>, routes: Record<string, string>) => string;
307
- /** Unmatched view (404) */
1683
+ /**
1684
+ * Unmatched view (404).
1685
+ * View path to use when no matching view is found in routes, e.g., 404 page.
1686
+ */
308
1687
  unmatchedView?: string;
309
- /** Module require function */
310
- require?: (names: string[], params?: Record<string, unknown>) => Promise<unknown> | undefined;
1688
+ /**
1689
+ * Module require function for asynchronous view loading.
1690
+ * Called by `Framework.use()` when a view class is not found in the registry.
1691
+ * Integrate with Webpack Module Federation or other dynamic loading strategies.
1692
+ *
1693
+ * @param names - Array of module names to load (e.g., `["remote-app/views/home"]`)
1694
+ * @param params - Optional parameters passed to the module initializer
1695
+ * @returns Promise resolving to an array of loaded modules, or undefined if not available
1696
+ */
1697
+ require?: (names: string[], params?: Record<string, unknown>) => Promise<unknown[]> | undefined;
311
1698
  /** Skip view rendered check */
312
1699
  skipViewRendered?: boolean;
313
- /** Remold function for event processing */
314
- remold?: (target: HTMLElement, type: string, event: Event) => boolean;
315
- /** Dynamic config access */
1700
+ /**
1701
+ * Project name of the current application.
1702
+ * Used by the micro-frontend bridge to determine if a view path
1703
+ * belongs to the current project or a remote project.
1704
+ */
1705
+ projectName?: string;
1706
+ /**
1707
+ * Cross-site (micro-frontend) configuration list.
1708
+ * Defines remote projects that can be loaded via Module Federation.
1709
+ * Also accessible via `window.crossConfigs` for build-time injection.
1710
+ */
1711
+ crossConfigs?: CrossSiteConfig[];
1712
+ /** Dynamic config access, custom config items */
316
1713
  [key: string]: unknown;
317
1714
  }
318
1715
  interface RouteViewConfig {
@@ -321,6 +1718,23 @@ interface RouteViewConfig {
321
1718
  /** Additional properties merged into location */
322
1719
  [key: string]: unknown;
323
1720
  }
1721
+ /**
1722
+ * Configuration for a remote (cross-site) project in the micro-frontend setup.
1723
+ * Each entry defines how to load views from a different project via Module Federation.
1724
+ */
1725
+ interface CrossSiteConfig {
1726
+ /** Project name, used as the prefix in view paths (e.g., "remote-app" in "remote-app/views/home") */
1727
+ projectName: string;
1728
+ /**
1729
+ * Remote source URL or Module Federation remote name.
1730
+ * For Webpack MF: the remote entry URL (e.g., "remote_app@//cdn.example.com/remote-app/remoteEntry.js")
1731
+ */
1732
+ source: string;
1733
+ /** Optional API host for the remote project */
1734
+ apiHost?: string;
1735
+ /** Optional business code for multi-tenant scenarios */
1736
+ bizCode?: string;
1737
+ }
324
1738
  /** Element with VDOM diff cached compare key */
325
1739
  interface VdomElement extends Element {
326
1740
  /** Whether compare key is cached */
@@ -333,7 +1747,7 @@ interface VdomElement extends Element {
333
1747
  /** Element with frame binding */
334
1748
  interface FrameBoundElement extends HTMLElement {
335
1749
  /** Frame instance bound to this element */
336
- frame?: FrameLike;
1750
+ frame?: FrameInterface;
337
1751
  /** Whether frame is bound (1 = bound) */
338
1752
  frameBound?: number;
339
1753
  /** View rendered flag */
@@ -343,6 +1757,15 @@ interface FrameBoundElement extends HTMLElement {
343
1757
  /** Range element guid */
344
1758
  rangeElementGuid?: number;
345
1759
  }
1760
+ /** Options for compileTemplate() */
1761
+ interface CompileOptions {
1762
+ /** Enable debug mode with line tracking (default: false) */
1763
+ debug?: boolean;
1764
+ /** Global variable names to destructure from $$ (refData) */
1765
+ globalVars?: string[];
1766
+ /** File path for debug error messages (default: undefined) */
1767
+ file?: string;
1768
+ }
346
1769
 
347
1770
  /**
348
1771
  * Lark framework utility functions.
@@ -350,8 +1773,6 @@ interface FrameBoundElement extends HTMLElement {
350
1773
 
351
1774
  /** Check if value is a plain object (not null, not array, typeof object) */
352
1775
  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
1776
  /** Check if value is primitive or function (not a complex object) */
356
1777
  declare function isPrimitiveOrFunc(value: unknown): boolean;
357
1778
  /** Check if value is primitive (not object, not function) */
@@ -361,7 +1782,7 @@ declare function generateId(prefix?: string): string;
361
1782
  declare function syncCounter(val: number): void;
362
1783
  declare function noop(): void;
363
1784
  /** Safe hasOwnProperty check */
364
- declare function has<T extends object>(owner: T | undefined | null, prop: PropertyKey): boolean;
1785
+ declare function hasOwnProperty<T extends object>(owner: T | undefined | null, prop: PropertyKey): boolean;
365
1786
  /** Get object keys (own enumerable) */
366
1787
  declare function keys<T extends object>(obj: T): string[];
367
1788
  /** Assign properties from sources to target (like Object.assign but safer) */
@@ -370,16 +1791,12 @@ declare function assign<T extends object>(target: T, ...sources: Partial<T>[]):
370
1791
  * Execute functions in try-catch, ignoring errors.
371
1792
  * Returns the result of the last successfully executed function.
372
1793
  */
373
- declare function funcWithTry(fns: AnyFunc | AnyFunc[], args: unknown[], context: unknown, configError: (e: unknown) => void): unknown;
1794
+ declare function funcWithTry(fns: AnyFunc | AnyFunc[], args: unknown[], context: unknown, configError?: (e: unknown) => void): unknown;
374
1795
  /**
375
1796
  * Set newData into oldData, tracking changed keys.
376
1797
  * Returns whether any value changed.
377
1798
  */
378
- declare function setData(newData: Record<string, unknown>, oldData: Record<string, unknown>, changedKeys: Record<string, number>, excludes: Set<string>): boolean;
379
- /**
380
- * Translate data references: if a value starts with SPLITTER, replace it
381
- * with the corresponding value from the data object.
382
- */
1799
+ declare function setData(newData: Record<string, unknown>, oldData: Record<string, unknown>, changedKeys: Set<string>, excludes: ReadonlySet<string>): boolean;
383
1800
  declare function translateData(data: object, value: unknown): unknown;
384
1801
  /** Get element by ID, or return the element itself if already an element */
385
1802
  declare function getById(id: string | Element | null): Element | null;
@@ -395,13 +1812,16 @@ declare function nodeInside(a: string | HTMLElement, b: string | HTMLElement): b
395
1812
  /**
396
1813
  * Parse URI string into path and params object.
397
1814
  * e.g. "/xxx/?a=b&c=d" => { path: "/xxx/", params: { a: "b", c: "d" } }
1815
+ *
1816
+ * The accumulator is function-local, so nested / re-entrant calls
1817
+ * (e.g. invoking `parseUri` again inside a replace callback) are safe.
398
1818
  */
399
1819
  declare function parseUri(uri: string): ParsedUri;
400
1820
  /**
401
1821
  * Convert path and params to URI string.
402
1822
  * e.g. toUri("/xxx/", { a: "b", c: "d" }) => "/xxx/?a=b&c=d"
403
1823
  */
404
- declare function toUri(path: string, params: Record<string, unknown>, keepEmptyObject?: Record<string, number>): string;
1824
+ declare function toUri(path: string, params: Record<string, unknown>, keepEmpty?: ReadonlySet<string>): string;
405
1825
  /**
406
1826
  * Convert array to map/hash object.
407
1827
  * For simple arrays, counts occurrences.
@@ -410,18 +1830,10 @@ declare function toUri(path: string, params: Record<string, unknown>, keepEmptyO
410
1830
  declare function toMap<T>(list: T[] | null | undefined, key?: keyof T): Record<string, T | number>;
411
1831
  /** Get current timestamp */
412
1832
  declare function now(): number;
413
- /** Constructable function type */
414
- type Constructable = new (...args: never[]) => unknown;
415
- /**
416
- * Implement classical inheritance with prototype chain.
417
- * This is the only place where we touch constructor internals -
418
- * it's a low-level framework utility for View.extend.
419
- */
420
- declare function classExtend<Proto extends object, Statics extends object>(make: Constructable, base: Constructable, props: Proto, statics: Statics): void;
421
1833
 
422
1834
  /** Internal splitter character (invisible, used as namespace separator) */
423
1835
  declare const SPLITTER = "\u001E";
424
- declare const ROUTER_EVENTS: {
1836
+ declare const RouterEvents: {
425
1837
  CHANGE: string;
426
1838
  CHANGED: string;
427
1839
  PAGE_UNLOAD: string;
@@ -456,23 +1868,27 @@ declare function applyStyle(styleIdOrPairs: string | string[], css?: string): ()
456
1868
  /**
457
1869
  * Mark/Unmark: signature-based lifecycle tracking for async callbacks.
458
1870
  *
459
- * mark() returns a validity checker function. If the host is "unmarked"
460
- * (e.g. view re-rendered), the checker returns false, preventing stale callbacks.
1871
+ * `mark(host, key)` returns a validity checker. The checker returns `false`
1872
+ * once the host is unmarked (e.g. when a view re-renders or is destroyed),
1873
+ * so stale async callbacks can short-circuit and skip work.
1874
+ *
1875
+ * State is stored in a module-level WeakMap, not on the host object, so
1876
+ * `mark/unmark` never pollutes user objects with magic keys, never breaks
1877
+ * on `Object.freeze`-ed inputs, and never shows up in debug snapshots.
461
1878
  */
462
1879
  /**
463
1880
  * Create a mark for tracking async callback validity.
464
- * Returns a function that checks if the mark is still valid.
1881
+ * Returns a function that returns true while the mark is still valid.
465
1882
  *
466
- * @param host - Object to store mark state on (typically a view)
467
- * @param key - Key to track (typically "render" or specific async operation)
468
- * @returns Checker function that returns true if mark is still valid
1883
+ * @param host - Object to associate the mark with (typically a view)
1884
+ * @param key - Key to track (typically "render" or a specific async-op identifier)
469
1885
  */
470
1886
  declare function mark$1(host: object, key: string): () => boolean;
471
1887
  /**
472
- * Clear all marks for a host object, invalidating all existing checkers.
1888
+ * Clear all marks for a host object, invalidating every existing checker.
473
1889
  * Called when a view re-renders or is destroyed.
474
1890
  *
475
- * @param host - Object whose marks should be cleared
1891
+ * @param host - Object whose marks should be invalidated
476
1892
  */
477
1893
  declare function unmark$1(host: object): void;
478
1894
 
@@ -496,381 +1912,80 @@ declare function unmark$1(host: object): void;
496
1912
  */
497
1913
  declare function safeguard<T>(data: T, getter?: ((key: string) => void) | null, setter?: ((path: string, value: unknown) => void) | null, isRoot?: boolean): T;
498
1914
 
499
- /**
500
- * Cache class with LFU-style eviction.
501
- * Keys are prefixed with SPLITTER for namespace isolation.
502
- *
503
- * @example
504
- * const cache = new Cache({ maxSize: 20, bufferSize: 5 });
505
- * cache.set('user', { name: 'Alice' });
506
- * const user = cache.get('user');
507
- * cache.has('user'); // true
508
- * cache.del('user');
509
- */
510
- declare class Cache<T = unknown> {
511
- /** Cache entries array */
512
- private entries;
513
- /** Fast lookup: prefixed key -> entry */
514
- private lookup;
515
- /** Buffer size for eviction */
516
- private readonly bufferSize;
517
- /** Maximum cache size */
518
- private readonly maxSize;
519
- /** Total capacity (maxSize + bufferSize) */
520
- private readonly capacity;
521
- /** Callback when entry is removed */
522
- private readonly onRemove?;
523
- /** Sort comparator */
524
- private readonly comparator;
525
- constructor(options?: CacheOptions<T>);
526
- /** Prefix a key with SPLITTER for namespace isolation */
527
- private prefixKey;
528
- /**
529
- * Get a cached value by key.
530
- * Updates frequency and timestamp for cache sorting.
531
- */
532
- get(key: string): T | undefined;
533
- /**
534
- * Iterate all cached values.
535
- */
536
- forEach(callback: (value: T | undefined) => void): void;
537
- /**
538
- * Set or update a cached value.
539
- * If key already exists, updates value and increments frequency.
540
- * If cache exceeds capacity, triggers eviction.
541
- */
542
- set(key: string, value: T): void;
543
- /**
544
- * Delete a cached entry.
545
- */
546
- del(key: string): void;
547
- /**
548
- * Check if a key exists in cache.
549
- */
550
- has(key: string): boolean;
551
- /** Get current cache size */
552
- get size(): number;
553
- /** Clear all entries */
554
- clear(): void;
555
- /** Evict least-used entries from cache */
556
- private evictEntries;
557
- }
558
-
559
- /**
560
- * Multi-cast event emitter class.
561
- *
562
- * @example
563
- * const emitter = new EventEmitter();
564
- * emitter.on('change', (data) => console.log(data));
565
- * emitter.fire('change', { key: 'value' });
566
- */
567
- declare class EventEmitter {
568
- /** Event listeners: prefixed key -> listener array */
569
- private listeners;
570
- /**
571
- * Bind event listener.
572
- */
573
- on(event: string, handler: AnyFunc): this;
574
- /**
575
- * Unbind event listener.
576
- * If handler is provided, removes only that handler.
577
- * If no handler, removes all handlers for the event.
578
- */
579
- off(event: string, handler?: AnyFunc): this;
580
- /**
581
- * Fire event, execute all bound handlers.
582
- * Supports executing state management: handlers removed during
583
- * execution are safely handled.
584
- *
585
- * @param event - Event name
586
- * @param data - Event data (type property added automatically)
587
- * @param remove - Whether to remove all handlers after firing
588
- * @param lastToFirst - Whether to execute handlers in reverse order
589
- */
590
- fire(event: string, data?: Record<string, unknown>, remove?: boolean, lastToFirst?: boolean): this;
591
- }
592
-
593
1915
  /** Mark framework as booted (called from Framework.boot) */
594
1916
  declare function markBooted(): void;
595
- /**
596
- * Observable in-memory data object.
597
- * Provides get/set/digest/diff/clean methods for cross-view data sharing.
598
- */
599
- declare const State: {
600
- get<T = unknown>(key?: string): T;
601
- set(data: Record<string, unknown>, excludes?: Set<string>): typeof State;
602
- digest(data?: Record<string, unknown>, excludes?: Set<string>): void;
603
- diff(): Readonly<Record<string, number>>;
604
- clean(keys: string): {
605
- make: AnyFunc;
606
- };
607
- on(event: string, handler: AnyFunc): typeof State;
608
- off(event: string, handler?: AnyFunc): typeof State;
609
- fire(event: string, data?: Record<string, unknown>, remove?: boolean): typeof State;
610
- };
611
-
612
- /**
613
- * Hash-based router with two-phase change confirmation.
614
- *
615
- * @example
616
- * Router.to('/list', { page: 2 });
617
- * const loc = Router.parse();
618
- * const diff = Router.diff();
619
- */
620
- declare const Router: {
621
- /** Parse href into Location object */
622
- parse(href?: string): Location;
623
- /** Compute diff between current and previous location */
624
- diff(): LocationDiff | undefined;
625
- /** Navigate to new URL */
626
- to(pathOrParams: string | Record<string, unknown>, params?: Record<string, unknown>, replace?: boolean, silent?: boolean): void;
627
- /** Join path segments */
628
- join(...paths: string[]): string;
629
- /** Bind event listener */
630
- on(event: string, handler: AnyFunc): typeof Router;
631
- /** Unbind event listener */
632
- off(event: string, handler?: AnyFunc): typeof Router;
633
- /** Fire event */
634
- fire(event: string, data?: Record<string, unknown>, remove?: boolean): typeof Router;
635
- /** Internal: bind hashchange (called by Framework.boot) */
636
- _bind(): void;
637
- /** Internal: set framework config */
638
- _setConfig(cfg: FrameworkConfig): void;
639
- /** Internal: notify hash change (for programmatic trigger) */
640
- notify?(e?: Event): void;
641
- };
642
- /** Mark framework as booted (called by Framework.boot) */
643
- declare function markRouterBooted(): void;
644
-
645
- /**
646
- * Base View class.
647
- * Views are created via View.extend() and mounted by Frame.
648
- *
649
- */
650
- declare class View {
651
- /** View ID (same as owner frame ID) */
652
- id: string;
653
- /** Owner frame */
654
- owner: FrameLike | number;
655
- /** Updater instance */
656
- updater: UpdaterLike;
657
- /** Signature: > 0 means active, incremented on render, 0 = destroyed */
658
- signature: number;
659
- /** Whether rendered at least once */
660
- rendered?: boolean;
661
- /** Whether view has template */
662
- template?: AnyFunc | string;
663
- /** Location observation config */
664
- locationObserved: ViewLocationObserved;
665
- /** Observed state keys */
666
- observedStateKeys?: string[];
667
- /** Resource map */
668
- resources: ViewResourceMap;
669
- /** Selector event map: eventType -> handler name list */
670
- eventSelectorMap: ViewEventSelectorMap;
671
- /** Event object map: eventType -> bitmask */
672
- eventObjectMap: ViewEventObjectMap;
673
- /** Global event list */
674
- globalEventList: ViewGlobalEventEntry[];
675
- /** Assign method reference */
676
- assignMethod?: AnyFunc;
677
- /** Whether endUpdate pending */
678
- endUpdatePending?: number;
679
- /** Internal event storage */
680
- private _events;
681
- /**
682
- * Initialize view (called by Frame when mounting).
683
- */
684
- init(): void;
685
- /**
686
- * Render view template (called by Frame after init).
687
- * Wrapped by View_WrapMethod to manage signature + resources.
688
- *
689
- * Default implementation calls updater.digest() which:
690
- * 1. Executes the template function with current data
691
- * 2. Runs VDOM diff against previous DOM
692
- * 3. Applies DOM operations
693
- * 4. Calls endUpdate to mount child frames
694
- *
695
- */
696
- render(): void;
697
- on(event: string, handler: AnyFunc): this;
698
- off(event: string, handler?: AnyFunc): this;
699
- fire(event: string, data?: Record<string, unknown>, remove?: boolean, lastToFirst?: boolean): this;
700
- /**
701
- * Notify view that HTML update is about to begin.
702
- * Unmounts child frames in the update zone.
703
- */
704
- beginUpdate(id?: string): void;
705
- /**
706
- * Notify view that HTML update has ended.
707
- * Mounts child frames in the update zone and runs deferred invokes.
708
- */
709
- endUpdate(id?: string, inner?: boolean): void;
710
- /**
711
- * Wrap an async callback to check view signature before executing.
712
- * If the view has been re-rendered or destroyed, the callback is skipped.
713
- */
714
- wrapAsync<Fn extends AnyFunc>(fn: Fn, context?: unknown): (...args: Parameters<Fn>) => ReturnType<Fn> | undefined;
715
- /**
716
- * Observe location parameters or path changes.
717
- * When observed keys change, render() is called automatically.
718
- */
719
- observeLocation(params: string | string[] | Record<string, unknown>, observePath?: boolean): void;
720
- /**
721
- * Observe State data keys for changes.
722
- * When observed keys change via State.digest(), render() is called.
723
- */
724
- observeState(observedKeys: string | string[]): void;
725
- /**
726
- * Capture (register) a resource under a key.
727
- * If a resource already exists at that key, it's destroyed first.
728
- * When destroyOnRender=true, the resource is destroyed on next render call.
729
- */
730
- capture(key: string, resource?: unknown, destroyOnRender?: boolean): unknown;
731
- /**
732
- * Release a captured resource.
733
- * If destroy=true, calls the resource's destroy() method.
734
- */
735
- release(key: string, destroy?: boolean): unknown;
736
- /**
737
- * Set up a leave confirmation for route changes and page unload.
738
- */
739
- leaveTip(message: string, condition: () => boolean): void;
740
- /** Collected ctors from mixins */
741
- static ctors?: AnyFunc[];
742
- /**
743
- * Extend View to create a new View subclass.
744
- *
745
- * Supports:
746
- * - props.make: constructor-like init (called with initParams + {node, deep})
747
- * - props.mixins: array of mixin objects
748
- * - Event method patterns: `'name<click>'` etc.
749
- */
750
- static extend(props?: ThisType<ViewInstance> & Record<string, unknown>, statics?: Record<string, unknown>): typeof View;
751
- /**
752
- * Merge mixins into View prototype.
753
- */
754
- static merge(...mixins: Record<string, unknown>[]): typeof View;
755
- }
1917
+ /**
1918
+ * Observable in-memory data object.
1919
+ * Provides get/set/digest/diff/clean methods for cross-view data sharing.
1920
+ */
1921
+ declare const State: StateInterface;
756
1922
 
757
1923
  /**
758
- * Frame (View Frame) class for view lifecycle management.
759
- * Each frame owns a view and manages child frames.
1924
+ * Hash-based router with two-phase change confirmation.
760
1925
  *
1926
+ * @example
1927
+ * Router.to('/list', { page: 2 });
1928
+ * const loc = Router.parse();
1929
+ * const diff = Router.diff();
761
1930
  */
762
- declare class Frame extends EventEmitter implements FrameLike {
763
- /** Frame ID (same as owner DOM element ID) */
764
- readonly id: string;
765
- /** Parent Frame ID */
766
- private _parentId;
767
- get parentId(): string | undefined;
768
- /** Children map: id -> id */
769
- childrenMap: FrameChildrenMap;
770
- /** Children count */
771
- childrenCount: number;
772
- /** Ready count (children that have fired 'created') */
773
- readyCount: number;
774
- /** Ready map: id -> 1 */
775
- readyMap: FrameReadyMap;
776
- /** View instance */
777
- viewInstance?: ViewInstance;
778
- /** Get view instance (read-only) */
779
- get view(): ViewInstance | undefined;
780
- /** Invoke list for deferred method calls */
781
- invokeList: FrameInvokeEntry[];
782
- /** Signature for async operation tracking */
783
- signature: number;
784
- /** Whether view has altered */
785
- hasAltered: number;
786
- /** Whether view is destroyed */
787
- destroyed: number;
788
- /** View path (v-lark attribute value) */
789
- viewPath?: string;
790
- /** Original template before mount */
791
- originalTemplate?: string;
792
- /** Hold fire created flag */
793
- holdFireCreated: number;
794
- /** Children created flag */
795
- childrenCreated: number;
796
- /** Children alter flag */
797
- childrenAlter: number;
798
- constructor(id: string, parentId?: string);
799
- /**
800
- * Mount a view to this frame.
801
- *
802
- * Complete flow:
803
- * 1. Parse viewPath, translate query params from parent
804
- * 2. Unmount current view
805
- * 3. Load View class (via require or provided ViewClass)
806
- * 4. View_Prepare (scan event methods)
807
- * 5. Create View instance
808
- * 6. View_DelegateEvents (bind DOM events)
809
- * 7. Call view.init()
810
- * 8. If view has template, call render via Updater
811
- * 9. If no template, call endUpdate directly
812
- */
813
- mountView(viewPath: string, viewInitParams?: Record<string, unknown>): void;
814
- /**
815
- * Internal: actually mount the view after class is loaded.
816
- */
817
- doMountView(ViewClass: typeof View, params: Record<string, string>, node: HTMLElement, sign: number): void;
818
- /**
819
- * Unmount current view.
820
- */
821
- unmountView(): void;
822
- /**
823
- * Mount a child frame.
824
- */
825
- mountFrame(frameId: string, viewPath: string, viewInitParams?: Record<string, unknown>): FrameLike;
826
- /**
827
- * Unmount a child frame.
828
- */
829
- unmountFrame(id?: string, _inner?: boolean): void;
830
- /**
831
- * Mount all views in a zone.
832
- */
833
- mountZone(zoneId?: string, _inner?: boolean): void;
834
- /**
835
- * Unmount all views in a zone.
836
- */
837
- unmountZone(zoneId?: string, _inner?: boolean): void;
838
- /**
839
- * Get all child frame IDs.
840
- */
841
- children(): string[];
842
- /**
843
- * Get parent frame at given level.
844
- * @param level - How many levels up (default 1)
845
- */
846
- parent(level?: number): Frame | undefined;
847
- /**
848
- * Invoke a method on the view.
849
- */
850
- invoke(name: string, args?: unknown[]): unknown;
851
- /** Get frame by ID */
852
- static get(id: string): Frame | undefined;
853
- /** Get all frames */
854
- static getAll(): Map<string, Frame>;
855
- /** Get or create root frame */
856
- static root(rootId?: string): Frame;
857
- /** Bind event listener (static) */
858
- static on(event: string, handler: AnyFunc): typeof Frame;
859
- /** Unbind event listener (static) */
860
- static off(event: string, handler?: AnyFunc): typeof Frame;
861
- /** Fire event (static) */
862
- static fire(event: string, data?: Record<string, unknown>): void;
863
- }
1931
+ declare const Router: RouterInterface;
1932
+ /** Mark framework as booted (called by Framework.boot) */
1933
+ declare function markRouterBooted(): void;
1934
+
864
1935
  /**
865
- * Register a View class for a given view path.
866
- * Called after module loading completes.
1936
+ * Module loader: async view loading via FrameworkConfig.require or dynamic import.
1937
+ *
1938
+ * Extracted from framework.ts to avoid circular dependency with frame.ts.
1939
+ * Both framework.ts and frame.ts import from this module.
1940
+ */
1941
+
1942
+ /** Framework configuration */
1943
+ declare const config: FrameworkConfig;
1944
+ /**
1945
+ * Load modules via the configured require function or dynamic import fallback.
1946
+ *
1947
+ * Two calling conventions:
1948
+ * 1. `use(name | name[], callback)`
1949
+ * 2. `use(name | name[])` — returns Promise<unknown[]> (no callback)
1950
+ *
1951
+ * When `FrameworkConfig.require` is configured, delegates to it (e.g., Webpack Module Federation).
1952
+ * When not configured, falls back to `dynamic import()` for ESM-based loading.
1953
+ */
1954
+ declare function use(names: string | string[], callback?: (...modules: unknown[]) => void): Promise<unknown[]>;
1955
+
1956
+ /**
1957
+ * CrossSite: Micro-frontend bridge View for cross-project view loading.
1958
+ *
1959
+ * For Lark + Webpack Module Federation.
1960
+ * Provides skeleton rendering, prepare preloading, assign reuse, and remote view mounting.
1961
+ *
1962
+ * Usage (in host project):
1963
+ * 1. Register CrossSite as the bridge view for cross-site paths:
1964
+ * registerViewClass('cross-site', CrossSite);
1965
+ * 2. Use v-lark="cross-site?view=remote-app/views/home&param=1" in template
1966
+ * 3. CrossSite loads the remote project's prepare module, then mounts the actual view
867
1967
  */
868
- declare function registerViewClass(viewPath: string, ViewClass: typeof View): void;
1968
+
1969
+ /**
1970
+ * Reset the projects map cache (useful when crossConfigs change at runtime).
1971
+ */
1972
+ declare function resetProjectsMap(): void;
1973
+ /**
1974
+ * CrossSite bridge View for micro-frontend integration.
1975
+ *
1976
+ * Flow:
1977
+ * 1. CrossSite is mounted as a regular view (registered as "cross-site")
1978
+ * 2. render() shows skeleton template with a child container
1979
+ * 3. updateView() loads the remote project's prepare module
1980
+ * 4. Once loaded, mounts the actual remote view into the child container
1981
+ * 5. On re-assign (same view path), calls assign on the remote view instead of re-mounting
1982
+ */
1983
+ declare const CrossSite: typeof View;
869
1984
 
870
1985
  /**
871
1986
  * Unmount frames within a DOM node.
872
1987
  */
873
- declare function vdomUnmountFrames(frame: FrameLike, node: ChildNode): void;
1988
+ declare function vdomUnmountFrames(frame: FrameInterface, node: ChildNode): void;
874
1989
  /**
875
1990
  * Parse HTML string into a DOM element.
876
1991
  * Handles special elements (table, SVG, MathML) with wrapper elements.
@@ -883,6 +1998,8 @@ declare function vdomGetNode(html: string, refNode: Element): Element;
883
1998
  declare function vdomGetCompareKey(node: ChildNode): string | undefined;
884
1999
  /**
885
2000
  * Special diff for form elements (value, checked, selected).
2001
+ * Form elements carry state on the DOM node (e.g. `input.value`) that isn't
2002
+ * reflected in attributes, so we have to sync those properties separately.
886
2003
  */
887
2004
  declare function vdomSpecialDiff(oldNode: ChildNode, newNode: ChildNode): number;
888
2005
  /**
@@ -892,11 +2009,11 @@ declare function vdomSetAttributes(oldNode: Element, newNode: Element, ref: VDom
892
2009
  /**
893
2010
  * Set child nodes from new parent onto old parent using keyed diff algorithm.
894
2011
  */
895
- declare function vdomSetChildNodes(oldParent: Element, newParent: Element, ref: VDomRef, frame: FrameLike, keys_?: Record<string, number>): void;
2012
+ declare function vdomSetChildNodes(oldParent: Element, newParent: Element, ref: VDomRef, frame: FrameInterface, keys_?: ReadonlySet<string>): void;
896
2013
  /**
897
2014
  * Diff two DOM nodes and apply changes.
898
2015
  */
899
- declare function vdomSetNode(oldNode: ChildNode, newNode: ChildNode, oldParent: Element, ref: VDomRef, frame: FrameLike, keys_?: Record<string, number>): void;
2016
+ declare function vdomSetNode(oldNode: ChildNode, newNode: ChildNode, oldParent: Element, ref: VDomRef, frame: FrameInterface, keys_?: ReadonlySet<string>): void;
900
2017
  /**
901
2018
  * Create an empty VDomRef for tracking diff operations.
902
2019
  */
@@ -923,7 +2040,7 @@ declare function encodeQ(v: unknown): string;
923
2040
  * Manages view-local data with change detection and VDOM diff triggering.
924
2041
  *
925
2042
  */
926
- declare class Updater implements UpdaterLike {
2043
+ declare class Updater implements UpdaterInterface {
927
2044
  /** View ID (same as owner frame ID) */
928
2045
  private viewId;
929
2046
  /** Current data object */
@@ -934,10 +2051,16 @@ declare class Updater implements UpdaterLike {
934
2051
  private changedKeys;
935
2052
  /** Whether data has changed since last digest */
936
2053
  private hasChangedFlag;
937
- /** Digesting queue: supports re-digest during digest */
2054
+ /**
2055
+ * Digesting queue: supports re-digest during digest.
2056
+ * Holds pending callbacks; `null` is used as a sentinel marking the start
2057
+ * of an active digest cycle, so `runDigest` can detect re-entrant calls.
2058
+ */
938
2059
  private digestingQueue;
939
- /** Snapshot JSON string for altered() detection */
940
- private snapshotJson;
2060
+ /** Monotonically increasing version, bumped each time data actually changes. */
2061
+ private version;
2062
+ /** Snapshot of `version` taken by `snapshot()`, used by `altered()`. */
2063
+ private snapshotVersion;
941
2064
  constructor(viewId: string);
942
2065
  /**
943
2066
  * Get data by key.
@@ -948,7 +2071,7 @@ declare class Updater implements UpdaterLike {
948
2071
  * Set data, tracking changed keys.
949
2072
  * Returns this for chaining.
950
2073
  */
951
- set(data: Record<string, unknown>, excludes?: Set<string>): this;
2074
+ set(data: Record<string, unknown>, excludes?: ReadonlySet<string>): this;
952
2075
  /**
953
2076
  * Detect changes and trigger VDOM re-render.
954
2077
  *
@@ -960,291 +2083,263 @@ declare class Updater implements UpdaterLike {
960
2083
  * 5. Call endUpdate on views that need re-rendering
961
2084
  * 6. Support re-digest during digest via queue
962
2085
  */
963
- digest(data?: Record<string, unknown>, excludes?: Set<string>, callback?: () => void): void;
2086
+ digest(data?: Record<string, unknown>, excludes?: ReadonlySet<string>, callback?: () => void): void;
964
2087
  /**
965
2088
  * Core digest execution.
966
2089
  */
967
2090
  private runDigest;
968
2091
  /**
969
- * Save a snapshot of current data for altered() detection.
2092
+ * Save a snapshot of the current data version for `altered()` detection.
2093
+ * Cheap O(1) — records the current monotonic version, no serialization.
970
2094
  */
971
2095
  snapshot(): this;
972
2096
  /**
973
- * Check if data has changed since last snapshot.
2097
+ * Check whether data has changed since the last snapshot.
2098
+ * Returns undefined when no snapshot has been taken yet.
974
2099
  */
975
2100
  altered(): boolean | undefined;
976
2101
  /**
977
- * Translate data references (SPLITTER-prefixed values).
2102
+ * Translate a refData reference back to its original value.
2103
+ *
2104
+ * The ref protocol is `SPLITTER` + ascii decimal digits — the exact format
2105
+ * emitted by `updaterRef`. We require that exact shape so a user-supplied
2106
+ * string that merely begins with SPLITTER is never accidentally resolved
2107
+ * (or mishandled as a "missing ref").
978
2108
  */
979
2109
  translate(data: unknown): unknown;
980
2110
  /**
981
- * Parse expression with data context.
2111
+ * Resolve a dotted property path against refData.
2112
+ *
2113
+ * Only safe property-path syntax is supported: `a`, `a.b`, `a.b.c`.
2114
+ * Numeric literals (e.g. `1`, `1.5`) are returned as numbers. Anything else
2115
+ * returns `undefined` — we no longer evaluate arbitrary JavaScript via
2116
+ * `new Function`, so the method is CSP-safe and cannot be used as an
2117
+ * injection vector.
982
2118
  */
983
2119
  parse(expr: string): unknown;
984
2120
  /**
985
- * Get changed keys (for external inspection).
2121
+ * Get the set of keys changed since the last digest (for external inspection).
986
2122
  */
987
- getChangedKeys(): Readonly<Record<string, number>>;
2123
+ getChangedKeys(): ReadonlySet<string>;
988
2124
  }
989
2125
 
990
2126
  /**
991
- * Bag wraps API response data with convenient access methods.
2127
+ * Payload wraps API response data with convenient access methods.
992
2128
  */
993
- declare class Bag {
994
- /** Bag data */
2129
+ declare class Payload implements PayloadInterface {
2130
+ /** Payload data */
995
2131
  data: Record<string, unknown>;
996
2132
  /** Internal cache info */
997
2133
  cacheInfo?: ServiceCacheInfo;
998
2134
  constructor(data?: Record<string, unknown>);
999
- /** Get a value from bag data */
2135
+ /** Get a value from payload data */
1000
2136
  get<T = unknown>(key: string): T;
1001
- /** Set a value in bag data */
1002
- set(keyOrData: string | Record<string, unknown> | ServiceMetaEntry, value?: unknown): this;
2137
+ /** Set a value in payload data */
2138
+ set(keyOrData: string | Record<string, unknown> | ServiceMetaEntry, value?: unknown): PayloadInterface;
1003
2139
  }
1004
- interface ServiceConstructor {
1005
- new (): ServiceInstance;
1006
- add(attrs: ServiceMetaEntry | ServiceMetaEntry[]): void;
1007
- meta(attrs: string | Record<string, unknown>): ServiceMetaEntry;
1008
- create(attrs: Record<string, unknown>): Bag;
1009
- get(attrs: Record<string, unknown>, createNew?: boolean): {
1010
- entity: Bag;
1011
- needsUpdate: boolean;
2140
+ /**
2141
+ * Minimal interface describing what serviceSend actually uses
2142
+ * from a service instance. This avoids coupling to the full
2143
+ * ServiceInterface which mixes instance and static methods.
2144
+ */
2145
+ interface ServiceSendTarget {
2146
+ destroyed: number;
2147
+ busy: number;
2148
+ internals: {
2149
+ metaList: Record<string, ServiceMetaEntry>;
2150
+ payloadCache: Cache<Payload>;
2151
+ pendingCacheKeys: Record<string, PendingCacheEntry>;
2152
+ syncFn: (payload: Payload, callback: () => void) => void;
2153
+ staticEmitter: EventEmitter;
1012
2154
  };
1013
- cached(attrs: Record<string, unknown>): Bag | undefined;
1014
- clear(names: string | string[]): void;
1015
- on(event: string, handler: AnyFunc): void;
1016
- off(event: string, handler?: AnyFunc): void;
1017
- fire(event: string, data?: Record<string, unknown>): void;
1018
- extend(newSyncFn: (bag: Bag, callback: () => void) => void, newCacheMax?: number, newCacheBuffer?: number): ServiceConstructor;
1019
- _internals: ServiceInternals;
1020
- }
1021
- interface ServiceInstance {
1022
- id: string;
1023
- [key: string]: unknown;
1024
- _emitter: EventEmitter;
1025
- _internals: ServiceInternals;
1026
- _type: ServiceStaticsInternal;
1027
- all(attrs: string | Record<string, unknown> | (string | Record<string, unknown>)[], done: AnyFunc): ServiceInstance;
1028
- one(attrs: string | Record<string, unknown> | (string | Record<string, unknown>)[], done: AnyFunc): ServiceInstance;
1029
- save(attrs: string | Record<string, unknown> | (string | Record<string, unknown>)[], done: AnyFunc): ServiceInstance;
1030
- enqueue(callback: AnyFunc): ServiceInstance;
1031
- dequeue(...args: unknown[]): void;
1032
- destroy(): void;
1033
- on(event: string, handler: AnyFunc): ServiceInstance;
1034
- off(event: string, handler?: AnyFunc): ServiceInstance;
1035
- fire(event: string, data?: Record<string, unknown>): ServiceInstance;
1036
- }
1037
- interface ServiceInternals {
1038
- metas: Record<string, ServiceMetaEntry>;
1039
- bagCache: Cache<Bag>;
1040
- pendingCacheKeys: Record<string, PendingCacheEntry>;
1041
- syncFn: (bag: Bag, callback: () => void) => void;
1042
- staticEmitter: EventEmitter;
1043
- }
1044
- /** Internal type for serviceType object (static methods) */
1045
- interface ServiceStaticsInternal {
1046
- add(attrs: ServiceMetaEntry | ServiceMetaEntry[]): void;
1047
- meta(attrs: string | Record<string, unknown>): ServiceMetaEntry;
1048
- create(attrs: Record<string, unknown>): Bag;
1049
- get(attrs: Record<string, unknown>, createNew?: boolean): {
1050
- entity: Bag;
1051
- needsUpdate: boolean;
2155
+ type: {
2156
+ get(attrs: Record<string, unknown>, createNew?: boolean): {
2157
+ entity: Payload;
2158
+ needsUpdate: boolean;
2159
+ };
1052
2160
  };
1053
- cached(attrs: Record<string, unknown>): Bag | undefined;
1054
- clear(names: string | string[]): void;
1055
- on(event: string, handler: AnyFunc): void;
1056
- off(event: string, handler?: AnyFunc): void;
1057
- fire(event: string, data?: Record<string, unknown>): void;
1058
- extend(newSyncFn: (bag: Bag, callback: () => void) => void, newCacheMax?: number, newCacheBuffer?: number): ServiceConstructor;
2161
+ enqueue(callback: AnyFunc): unknown;
1059
2162
  }
1060
2163
  /**
1061
- * Default Service base class.
1062
- * Use Service.extend() to create a usable subclass with a sync function.
1063
- */
1064
- declare const Service: ServiceConstructor;
1065
-
1066
- /**
1067
- * DOM event delegation system.
1068
- * Delegates events to document body for performance.
2164
+ * Service: API request management with caching, deduplication, and queue.
1069
2165
  *
2166
+ * - Service.extend(syncFn, cacheMax?, cacheBuffer?): creates subclass with sync function
2167
+ * - Service.add(attrs): registers API endpoint metadata
2168
+ * - new Service().all(attrs, done): fetch all, use cache when available
2169
+ * - new Service().one(attrs, done): fetch all, callback on each completion
2170
+ * - new Service().save(attrs, done): fetch all, skip cache (always request)
2171
+ * - enqueue/dequeue: task queue for sequential async operations
2172
+ * - destroy: cancel pending requests
2173
+ *
2174
+ * Per-type state (metaList, payloadCache, pendingCacheKeys, syncFn, staticEmitter)
2175
+ * is stored as static class properties. When extend() creates a subclass,
2176
+ * each subclass gets its own copies of these static properties, ensuring
2177
+ * isolation between different Service types.
1070
2178
  */
1071
- declare const EventDelegator: {
1072
- /**
1073
- * Bind a DOM event type to document body.
1074
- */
1075
- bind(eventType: string, hasSelector?: boolean): void;
1076
- /**
1077
- * Unbind a DOM event type from document body.
1078
- */
1079
- unbind(eventType: string, hasSelector?: boolean): void;
2179
+ declare class Service {
2180
+ /** Service instance ID */
2181
+ id: string;
2182
+ /** Whether service is busy (1 = busy) */
2183
+ busy: number;
2184
+ /** Whether service is destroyed (1 = destroyed) */
2185
+ destroyed: number;
2186
+ /** Task queue for sequential operations */
2187
+ taskQueue: AnyFunc[];
2188
+ /** Previous dequeue arguments */
2189
+ prevArgs: unknown[];
2190
+ /** Instance event emitter */
2191
+ private _emitter;
2192
+ constructor();
2193
+ /** Instance event emitter (public accessor) */
2194
+ get emitter(): EventEmitterInterface;
1080
2195
  /**
1081
- * Clean up range events for a destroyed view.
2196
+ * Get internals object for serviceSend compatibility.
2197
+ * References per-type static state from the current class.
1082
2198
  */
1083
- clearRangeEvents(viewId: string): void;
2199
+ get internals(): ServiceSendTarget["internals"];
1084
2200
  /**
1085
- * Set the frame getter function (called by Framework.boot).
2201
+ * Get type reference (the constructor) for serviceSend compatibility.
2202
+ * Static methods like get/create are accessible via the constructor.
1086
2203
  */
1087
- setFrameGetter(getter: (id: string) => FrameLike | undefined): void;
2204
+ get type(): ServiceSendTarget["type"];
1088
2205
  /**
1089
- * Get next element GUID.
2206
+ * Fetch all endpoints, callback when all complete.
2207
+ * Uses cache when available.
1090
2208
  */
1091
- nextElementGuid(): number;
1092
- };
1093
-
1094
- /**
1095
- * Queue a function for deferred, chunked execution.
1096
- *
1097
- * @param fn - Function to execute (wrapped in try-catch automatically)
1098
- * @param args - Arguments array to pass to the function
1099
- * @param context - `this` context for the function call
1100
- */
1101
- declare function task(fn: AnyFunc, args?: unknown[], context?: unknown): void;
1102
- /**
1103
- * Fire a custom DOM event on a target element.
1104
- */
1105
- declare function dispatchEvent(target: EventTarget, eventType: string, eventInit?: CustomEventInit): void;
1106
- /**
1107
- * Base class that can be extended by any object needing EventEmitter.
1108
- */
1109
- declare class Base extends EventEmitter {
1110
- }
1111
- /**
1112
- * Load modules via the configured require function.
1113
- */
1114
- declare function use(names: string | string[], callback?: (...modules: unknown[]) => void): void;
1115
- /**
1116
- * Wait for all views in a zone to be rendered.
1117
- */
1118
- declare function waitZoneViewsRendered(viewId: string, timeout?: number): Promise<number>;
1119
- /**
1120
- * Main framework object.
1121
- * Provides boot, config, and all global utility methods.
1122
- */
1123
- declare const Framework: {
2209
+ all(attrs: string | Record<string, unknown> | (string | Record<string, unknown>)[], done: AnyFunc): this;
1124
2210
  /**
1125
- * Get or set framework configuration.
2211
+ * Fetch all endpoints, callback on each completion.
1126
2212
  */
1127
- config(cfg?: FrameworkConfig | string): FrameworkConfig | unknown;
2213
+ one(attrs: string | Record<string, unknown> | (string | Record<string, unknown>)[], done: AnyFunc): this;
1128
2214
  /**
1129
- * Boot the framework.
2215
+ * Fetch all endpoints, skip cache (always request).
1130
2216
  */
1131
- boot(cfg?: FrameworkConfig): void;
1132
- /** Whether framework has booted */
1133
- isBooted(): boolean;
1134
- /** Mark async callback validity tracker */
1135
- mark: typeof mark$1;
1136
- /** Unmark (invalidate) async callbacks */
1137
- unmark: typeof unmark$1;
1138
- /** Fire a custom DOM event on a target */
1139
- dispatch: typeof dispatchEvent;
1140
- /** Execute function in try-catch, ignoring errors */
1141
- task: typeof task;
1142
- /** Promise-based setTimeout */
1143
- delay(time: number): Promise<void>;
1144
- /** Load modules via configured require */
1145
- use: typeof use;
1146
- /** Wait for zone views to be rendered */
1147
- waitZoneViewsRendered: typeof waitZoneViewsRendered;
1148
- WAIT_OK: number;
1149
- WAIT_TIMEOUT_OR_UNFOUND: number;
2217
+ save(attrs: string | Record<string, unknown> | (string | Record<string, unknown>)[], done: AnyFunc): this;
1150
2218
  /**
1151
- * Convert array to hash map.
2219
+ * Enqueue a task for sequential execution.
1152
2220
  */
1153
- toMap: typeof toMap;
2221
+ enqueue(callback: AnyFunc): this;
1154
2222
  /**
1155
- * Execute function in try-catch.
2223
+ * Dequeue and execute the next task in queue.
1156
2224
  */
1157
- toTry: typeof funcWithTry;
2225
+ dequeue(...args: unknown[]): void;
1158
2226
  /**
1159
- * Convert path + params to URL string.
2227
+ * Destroy the service instance.
2228
+ * After destruction, no new requests can be sent.
1160
2229
  */
1161
- toUrl: typeof toUri;
2230
+ destroy(): void;
2231
+ on(event: string, handler: AnyFunc): this;
2232
+ off(event: string, handler?: AnyFunc): this;
2233
+ fire(event: string, data?: Record<string, unknown>): this;
2234
+ /** Per-type metadata registry */
2235
+ static _metaList: Record<string, ServiceMetaEntry>;
2236
+ /** Per-type payload cache (LFU with frequency eviction) */
2237
+ static _payloadCache: Cache<Payload>;
2238
+ /** Per-type pending cache keys for deduplication */
2239
+ static _pendingCacheKeys: Record<string, PendingCacheEntry>;
2240
+ /** Per-type sync function */
2241
+ static _syncFn: (payload: Payload, callback: () => void) => void;
2242
+ /** Per-type static event emitter */
2243
+ static _staticEmitter: EventEmitter<unknown>;
2244
+ /** Per-type cache max size */
2245
+ static _cacheMax: number;
2246
+ /** Per-type cache buffer size */
2247
+ static _cacheBuffer: number;
1162
2248
  /**
1163
- * Parse URI string into path and params.
2249
+ * Register API endpoint metadata.
1164
2250
  */
1165
- parseUrl: typeof parseUri;
2251
+ static add(attrs: ServiceMetaEntry | ServiceMetaEntry[]): void;
1166
2252
  /**
1167
- * Mix properties from source to target.
2253
+ * Get metadata for an API endpoint.
1168
2254
  */
1169
- mix: typeof assign;
2255
+ static meta(attrs: string | Record<string, unknown>): ServiceMetaEntry;
1170
2256
  /**
1171
- * Check if object has own property.
2257
+ * Create a Payload for an API request.
1172
2258
  */
1173
- has: typeof has;
2259
+ static create(attrs: Record<string, unknown>): Payload;
1174
2260
  /**
1175
- * Get object keys.
2261
+ * Get or create a Payload for an API request.
1176
2262
  */
1177
- keys: typeof keys;
2263
+ static get(attrs: Record<string, unknown>, createNew?: boolean): {
2264
+ entity: Payload;
2265
+ needsUpdate: boolean;
2266
+ };
1178
2267
  /**
1179
- * Check if node A is inside node B.
2268
+ * Get cached Payload if available and not expired.
1180
2269
  */
1181
- inside: typeof nodeInside;
2270
+ static cached(attrs: Record<string, unknown>): Payload | undefined;
1182
2271
  /**
1183
- * Get element by ID (shorthand for document.getElementById).
2272
+ * Clear cached payloads by endpoint name.
1184
2273
  */
1185
- node: typeof getById;
2274
+ static clear(names: string | string[]): void;
2275
+ static on(event: string, handler: AnyFunc): void;
2276
+ static off(event: string, handler?: AnyFunc): void;
2277
+ static fire(event: string, data?: Record<string, unknown>): void;
1186
2278
  /**
1187
- * Apply CSS style.
2279
+ * Create a new Service subclass with a custom sync function.
2280
+ *
2281
+ * Each subclass gets its OWN copies of every per-type static field
2282
+ * (`_metaList`, `_payloadCache`, `_pendingCacheKeys`, `_syncFn`,
2283
+ * `_staticEmitter`, `_cacheMax`, `_cacheBuffer`) via `static override`.
2284
+ * This is intentional: it ensures that endpoint metadata, cache state,
2285
+ * in-flight dedup keys, and event subscribers are fully isolated between
2286
+ * different Service types, even when one extends another.
2287
+ *
2288
+ * **Do not refactor these `static override` declarations away** — sharing
2289
+ * them through prototype inheritance would let endpoints registered on one
2290
+ * subclass leak into another, and the LFU cache evictions of one type
2291
+ * would race with those of another.
1188
2292
  */
1189
- applyStyle: typeof applyStyle;
2293
+ static extend(this: typeof Service, newSyncFn: (payload: Payload, callback: () => void) => void, newCacheMax?: number, newCacheBuffer?: number): typeof Service;
2294
+ }
2295
+
2296
+ /**
2297
+ * DOM event delegation system.
2298
+ * Delegates events to document body for performance.
2299
+ *
2300
+ */
2301
+ declare const EventDelegator: {
1190
2302
  /**
1191
- * Generate globally unique ID.
2303
+ * Bind a DOM event type to document body.
1192
2304
  */
1193
- guid: typeof generateId;
2305
+ bind(eventType: string, hasSelector?: boolean): void;
1194
2306
  /**
1195
- * Proxy-based debug guard.
2307
+ * Unbind a DOM event type from document body.
1196
2308
  */
1197
- guard: typeof safeguard;
2309
+ unbind(eventType: string, hasSelector?: boolean): void;
1198
2310
  /**
1199
- * Cache class.
2311
+ * Clean up range events for a destroyed view.
1200
2312
  */
1201
- Cache: typeof Cache;
2313
+ clearRangeEvents(viewId: string): void;
1202
2314
  /**
1203
- * Ensure element has an ID.
2315
+ * Set the frame getter function (called by Framework.boot).
1204
2316
  */
1205
- nodeId(element: HTMLElement): string;
2317
+ setFrameGetter(getter: (id: string) => FrameInterface | undefined): void;
1206
2318
  /**
1207
- * Base class with EventEmitter.
2319
+ * Get next element GUID.
1208
2320
  */
1209
- Base: typeof Base;
1210
- /** Router module */
1211
- Router: {
1212
- parse(href?: string): Location;
1213
- diff(): LocationDiff | undefined;
1214
- to(pathOrParams: string | Record<string, unknown>, params?: Record<string, unknown>, replace?: boolean, silent?: boolean): void;
1215
- join(...paths: string[]): string;
1216
- on(event: string, handler: AnyFunc): typeof Router;
1217
- off(event: string, handler?: AnyFunc): typeof Router;
1218
- fire(event: string, data?: Record<string, unknown>, remove?: boolean): typeof Router;
1219
- _bind(): void;
1220
- _setConfig(cfg: FrameworkConfig): void;
1221
- notify?(e?: Event): void;
1222
- };
1223
- /** State module */
1224
- State: {
1225
- get<T = unknown>(key?: string): T;
1226
- set(data: Record<string, unknown>, excludes?: Set<string>): typeof State;
1227
- digest(data?: Record<string, unknown>, excludes?: Set<string>): void;
1228
- diff(): Readonly<Record<string, number>>;
1229
- clean(keys: string): {
1230
- make: AnyFunc;
1231
- };
1232
- on(event: string, handler: AnyFunc): typeof State;
1233
- off(event: string, handler?: AnyFunc): typeof State;
1234
- fire(event: string, data?: Record<string, unknown>, remove?: boolean): typeof State;
1235
- };
1236
- /** View class */
1237
- View: typeof View;
1238
- /** Frame class */
1239
- Frame: typeof Frame;
2321
+ nextElementGuid(): number;
1240
2322
  };
1241
2323
 
1242
2324
  /**
1243
- * @lark/framework Store
2325
+ * Main framework object.
2326
+ * Provides boot, config, and all global utility methods.
2327
+ */
2328
+ declare const Framework: FrameworkInterface;
2329
+
2330
+ /**
2331
+ * @lark.js/mvc Store
1244
2332
  *
1245
2333
  * Reactive state management with Proxy-based dependency tracking.
1246
2334
  * Adapted for Lark / React / Node.js.
1247
2335
  *
2336
+ * Store is the recommended choice for COMPLEX cross-view state:
2337
+ * reactive handlers, derived data, multi-instance isolation (`multi()`),
2338
+ * store-internal reactions (inner `observe`), and Proxy-based fine-grained
2339
+ * dependency tracking. For SIMPLE shared values (counters, toggles, page
2340
+ * title, session info, etc.) prefer `State` from `./state` — fewer concepts
2341
+ * and no Proxy overhead.
2342
+ *
1248
2343
  * Core concepts:
1249
2344
  * - createState: Proxy-based deep reactive state
1250
2345
  * - track/trigger/clear: publish-subscribe dependency tracking
@@ -1252,8 +2347,6 @@ declare const Framework: {
1252
2347
  * - cell/observeCell: lightweight standalone reactive cells
1253
2348
  */
1254
2349
 
1255
- /** Reactive proxy object — all string keys map to unknown values */
1256
- type ProxyObject = Record<string, unknown>;
1257
2350
  interface Task {
1258
2351
  cb: AnyFunc;
1259
2352
  params?: Record<string, unknown>;
@@ -1304,10 +2397,10 @@ declare class Queue {
1304
2397
  declare const isState: (target: unknown) => boolean;
1305
2398
  declare const mark: (host: Record<string, unknown>, key: string) => (() => boolean);
1306
2399
  declare const unmark: (host: Record<string, unknown>) => void;
1307
- declare function createState(initialData: unknown, config?: StateConfig): ProxyObject;
1308
- declare function lazySet(target: ProxyObject, data: Record<string, unknown>): void;
2400
+ declare function createState(initialData: unknown, config?: StateConfig): Record<string, unknown>;
2401
+ declare function lazySet(target: Record<string, unknown>, data: Record<string, unknown>): void;
1309
2402
  /** Shallow set: only top-level properties are reactive */
1310
- declare function shallowSet(target: ProxyObject, key: string, data: unknown): unknown;
2403
+ declare function shallowSet(target: Record<string, unknown>, key: string, data: unknown): unknown;
1311
2404
  declare const _storeName: unique symbol;
1312
2405
  declare const _storeStatus: unique symbol;
1313
2406
  declare const _storeScheduler: unique symbol;
@@ -1321,16 +2414,40 @@ declare const _stateKeys: unique symbol;
1321
2414
  declare const _storeState: unique symbol;
1322
2415
  declare const _storeDefScheduler: unique symbol;
1323
2416
  declare const _storeProxy: unique symbol;
2417
+ declare const _computedKeys: unique symbol;
2418
+ /**
2419
+ * Declare a derived (computed) store property.
2420
+ *
2421
+ * Usage (inside a `defineStore` creator):
2422
+ *
2423
+ * ```ts
2424
+ * defineStore("count", (store, { computed }) => ({
2425
+ * count: 0,
2426
+ * doubled: computed(["count"], () => store.count * 2),
2427
+ * increment() { store.count++ },
2428
+ * }));
2429
+ * ```
2430
+ *
2431
+ * `deps` lists the state keys the computed reads. Whenever any of those keys
2432
+ * changes, the computed function re-runs and the new value flows out through
2433
+ * the store like a regular state update — meaning views that
2434
+ * `store.observe(this, ["doubled"])` re-render automatically.
2435
+ *
2436
+ * The returned value is typed as `T`, so the store body still type-checks as
2437
+ * `{ doubled: number; ... }`. Writes to a computed key are ignored at runtime.
2438
+ */
2439
+ declare function computed<T>(deps: readonly string[], fn: () => T): T;
1324
2440
  declare abstract class BaseStore {
1325
2441
  [_storeName]: string;
1326
2442
  [_storeStatus]: StoreStatus;
1327
2443
  [_storeScheduler]: Scheduler;
1328
2444
  [_originState]: Record<string, unknown>;
1329
2445
  [_stateKeys]: string[];
1330
- [_storeState]: ProxyObject;
2446
+ [_storeState]: Record<string, unknown>;
2447
+ [_computedKeys]: Set<string>;
1331
2448
  [_storeDefScheduler]: () => Queue;
1332
2449
  private [_outerStore];
1333
- [_storeBoot](): ProxyObject;
2450
+ [_storeBoot](): Record<string, unknown>;
1334
2451
  [_storeDestroy](): void;
1335
2452
  /**
1336
2453
  *
@@ -1339,11 +2456,11 @@ declare abstract class BaseStore {
1339
2456
  */
1340
2457
  [_storeCreate](body: Record<string, unknown>, excludeFns?: string[]): void;
1341
2458
  constructor(name: string, config?: StoreConfig);
1342
- [_innerStore](): ProxyObject;
2459
+ [_innerStore](): Record<string, unknown>;
1343
2460
  get status(): StoreStatus;
1344
2461
  get storeName(): string;
1345
2462
  get scheduler(): Scheduler;
1346
- [_storeProxy](toOut?: boolean): ProxyObject;
2463
+ [_storeProxy](toOut?: boolean): Record<string, unknown>;
1347
2464
  }
1348
2465
  interface LarkView {
1349
2466
  updater: {
@@ -1359,13 +2476,22 @@ interface NodeStoreMethods {
1359
2476
  observe: (key: string, callback: AnyFunc, immediate?: boolean) => () => void;
1360
2477
  }
1361
2478
  interface StoreMethods {
1362
- observe: (view: LarkView | undefined, keys: (string | ObservePayload)[] | (() => (string | ObservePayload)[]), defCallback?: (changedMap: Record<string, unknown>) => void) => () => void;
2479
+ /**
2480
+ * Subscribe a view to store changes.
2481
+ *
2482
+ * - `store.observe(view)` — observe every state key (D5 default).
2483
+ * - `store.observe(view, ["k1", "k2"])` — observe specific keys only.
2484
+ * - `store.observe(view, ["k1"], (changes) => ...)` — explicit callback.
2485
+ * - `store.observe(undefined, ["k1"], cb)` — inner observe (no view binding).
2486
+ */
2487
+ observe: (view: LarkView | undefined, keys?: (string | ObservePayload)[] | (() => (string | ObservePayload)[]), defCallback?: (changedMap: Record<string, unknown>) => void) => () => void;
1363
2488
  }
1364
2489
  /** Detect platform from a component instance */
1365
2490
  declare const getPlatform: (comp: unknown) => Platform | undefined;
1366
2491
  declare const extendApis: {
1367
2492
  lazySet: typeof lazySet;
1368
2493
  shallowSet: typeof shallowSet;
2494
+ computed: typeof computed;
1369
2495
  };
1370
2496
  /** Inner store type available inside defineStore creator */
1371
2497
  type InnerStore<S = Record<string, unknown>> = S & StoreMethods;
@@ -1384,7 +2510,7 @@ declare function getUseStore(name: string): AnyFunc | undefined;
1384
2510
  declare function cloneStore(name: string, useStore: AnyFunc, config?: StoreConfig): unknown;
1385
2511
  declare function isStoreActive(name: string): boolean;
1386
2512
  declare function cell<T = unknown>(data: T): T;
1387
- declare function observeCell(state: ProxyObject, cb: AnyFunc, immediate?: boolean): () => void;
2513
+ declare function observeCell(state: Record<string, unknown>, cb: AnyFunc, immediate?: boolean): () => void;
1388
2514
  declare function multi<S = Record<string, unknown>>(useStore: LarkUseStore<S>): [LarkUseStore<S>, {
1389
2515
  make: AnyFunc;
1390
2516
  }];
@@ -1442,8 +2568,16 @@ interface SerializedFrameTree {
1442
2568
  /** Root element ID */
1443
2569
  rootId: string;
1444
2570
  }
2571
+ declare const FrameVisualBridge: {
2572
+ MSG_PING: string;
2573
+ MSG_PONG: string;
2574
+ MSG_REQUEST_TREE: string;
2575
+ MSG_TREE: string;
2576
+ MSG_TREE_DELTA: string;
2577
+ };
1445
2578
  /**
1446
2579
  * Serialize the entire Frame tree starting from root.
2580
+ * Returns an empty snapshot if the app hasn't booted yet.
1447
2581
  */
1448
2582
  declare function serializeFrameTree(): SerializedFrameTree;
1449
2583
  /**
@@ -1456,7 +2590,7 @@ declare function serializeFrameTree(): SerializedFrameTree;
1456
2590
  declare function installFrameVisualizerBridge(): void;
1457
2591
 
1458
2592
  /**
1459
- * @lark/framework Template Compiler
2593
+ * @lark.js/mvc Template Compiler
1460
2594
  *
1461
2595
  * convertArtSyntax() ({{}} → <% %>)
1462
2596
  * processViewEvents() (@event prefix + param encoding)
@@ -1464,9 +2598,9 @@ declare function installFrameVisualizerBridge(): void;
1464
2598
  * extractGlobalVars() (AST-based global var analysis via @babel/parser)
1465
2599
  *
1466
2600
  * - All template operators: = (escape), ! (raw), @ (ref lookup), : (binding)
1467
- * - @event attribute processing with $g prefix + \x1e separator
1468
- * - $n (null-safe toString), $e (HTML entity encode), $eu (URI encode), $eq (quote encode), $i (ref lookup)
1469
- * - Debug mode with line tracking ($expr/$art/$line) and try-catch error wrapper
2601
+ * - @event attribute processing with $splitter prefix + \x1e separator
2602
+ * - $strSafe (null-safe toString), $encHtml (HTML entity encode), $encUri (URI encode), $encQuote (quote encode), $refFn (ref lookup)
2603
+ * - Debug mode with line tracking ($dbgExpr/$dbgArt/$dbgLine) and try-catch error wrapper
1470
2604
  * - View ID injection (\x1f → '+$viewId+')
1471
2605
  * - Post-processing cleanup of empty concatenations
1472
2606
  * - 0 configuration: auto-extract variables via AST analysis
@@ -1476,34 +2610,26 @@ declare function installFrameVisualizerBridge(): void;
1476
2610
  * {{:variable}} → two-way binding (same as = for rendering)
1477
2611
  * {{!variable}} → raw output (no HTML escaping)
1478
2612
  * {{@variable}} → reference lookup for component data passing
1479
- * {{each list as item}} → loop
1480
- * {{each list as item idx}} → loop with index
1481
- * {{parse obj as val key}} → object iteration
2613
+ * {{forOf list as item}} → loop
2614
+ * {{forOf list as item idx}} → loop with index
2615
+ * {{forIn obj as val key}} → object iteration
1482
2616
  * {{for(let i=0;i<n;i++)}} → generic for loop
1483
2617
  * {{if condition}} → conditional
1484
2618
  * {{else if condition}} → else-if
1485
2619
  * {{else}} → else
1486
- * {{/if}} / {{/each}} / {{/parse}} / {{/for}} → close blocks
2620
+ * {{/if}} / {{/forOf}} / {{/forIn}} / {{/for}} → close blocks
1487
2621
  * {{set a = b}} → variable declaration
1488
2622
  */
1489
- /** Options for compileTemplate() */
1490
- interface CompileOptions {
1491
- /** Enable debug mode with line tracking (default: false) */
1492
- debug?: boolean;
1493
- /** Global variable names to destructure from $$ (refData) */
1494
- globalVars?: string[];
1495
- /** File path for debug error messages (default: undefined) */
1496
- file?: string;
1497
- }
2623
+
1498
2624
  /**
1499
2625
  * Compile an HTML template string into a JS module string.
1500
2626
  * This is the main entry point for both Vite and Webpack loaders.
1501
2627
  *
1502
2628
  * The output is an ES module that exports a function with the signature:
1503
- * (data, selfId, refData) => string
2629
+ * (data, viewId, refData) => string
1504
2630
  *
1505
2631
  * Internally it calls the compiled template function with the standard
1506
- * signature: ($$,$viewId,$$ref,$e,$n,$eu,$i,$eq)
2632
+ * signature: ($data,$viewId,$refAlt,$encHtml,$strSafe,$encUri,$refFn,$encQuote)
1507
2633
  *
1508
2634
  * @param source - The raw HTML template content
1509
2635
  * @param options - Compilation options
@@ -1528,4 +2654,4 @@ declare function compileTemplate(source: string, options?: CompileOptions): stri
1528
2654
  */
1529
2655
  declare function extractGlobalVars(source: string): string[];
1530
2656
 
1531
- 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, type SerializedFrameNode, type SerializedFrameTree, type SerializedViewInfo, Service, type ServiceCacheInfo, type ServiceConstructor, type ServiceEntry, type ServiceMetaEntry, type ServiceOptions, State, type StoreConfig, type StoreMethods, TAG_NAME_REGEXP, 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, installFrameVisualizerBridge, isArray, 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 };
2657
+ export { type AnyFunc, CALL_BREAK_TIME, Cache, type CacheEntry, type CacheInterface, type CacheOptions, type ChangeEvent, type CompileOptions, CrossSite, type CrossSiteConfig, EVENT_METHOD_REGEXP, EventDelegator, EventEmitter, type EventEmitterInterface, type EventListenerEntry, Frame, type FrameBoundElement, type FrameInterface, type FrameInvokeEntry, type FrameStaticEvent, FrameVisualBridge, Framework, type FrameworkConfig, type FrameworkInterface, LARK_VIEW, type LarkUseStore, type Location, type LocationDiff, type MixinEventHandler, type NodeUseStore, type ObservePayload, type ParamDiff, type ParsedUri, Payload, type PayloadEntry, type PayloadInterface, type PendingCacheEntry, Platform, RouterEvents as ROUTER_EVENTS, type ReactUseStore, type RouteChangeEvent, type RouteChangedEvent, type RouteViewConfig, Router, type RouterInterface, SPLITTER, type SerializedFrameNode, type SerializedFrameTree, type SerializedViewInfo, Service, type ServiceCacheInfo, type ServiceEntry, type ServiceEvent, type ServiceInterface, type ServiceMetaEntry, type ServiceOptions, State, type StateInterface, type StoreConfig, type StoreMethods, TAG_NAME_REGEXP, Updater, type UpdaterInterface, type VDomOp, type VDomRef, VIEW_EVENT_METHOD_REGEXP, type VdomElement, View, type ViewEvent, type ViewEventSelectorEntry, type ViewGlobalEventEntry, type ViewInterface, type ViewLocationObserved, type ViewObserveLocation, type ViewResourceEntry, type ViewTemplate, type VoidFunc, applyIdUpdates, applyStyle, applyVdomOps, assign, cell, cloneData, cloneStore, compileTemplate, computed, createState, createVdomRef, defineStore, defineView, delStore, encodeHTML, encodeQ, encodeSafe, encodeURIExtra, ensureElementId, extractGlobalVars, config as frameworkConfig, funcWithTry, generateId, getAttribute, getById, getPlatform, getStore, getUseStore, hasOwnProperty, installFrameVisualizerBridge, invalidateViewClass, isPlainObject, isPrimitive, isPrimitiveOrFunc, isState, isStoreActive, keys, lazySet, mark$1 as mark, markBooted, markRouterBooted, multi, nextCounter, nodeInside, noop, now, observeCell, parseUri, registerViewClass, resetProjectsMap, safeguard, serializeFrameTree, setData, shallowSet, mark as storeMark, unmark as storeUnmark, syncCounter, toMap, toUri, translateData, unmark$1 as unmark, use, vdomGetCompareKey, vdomGetNode, vdomSetAttributes, vdomSetChildNodes, vdomSetNode, vdomSpecialDiff, vdomUnmountFrames };