@lark.js/mvc 0.0.4 → 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.ts CHANGED
@@ -1,3 +1,41 @@
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
+
1
39
  /**
2
40
  * Base View class.
3
41
  * Views are created via View.extend() and mounted by Frame.
@@ -14,7 +52,7 @@ declare class View implements ViewInterface {
14
52
  /** Whether rendered at least once */
15
53
  rendered?: boolean;
16
54
  /** Whether view has template */
17
- template?: (data: unknown, viewId: string, refData: unknown, ...rest: unknown[]) => string | string;
55
+ template?: ViewTemplate;
18
56
  /** Location observation config */
19
57
  locationObserved: ViewLocationObserved;
20
58
  /** Observed state keys */
@@ -27,6 +65,8 @@ declare class View implements ViewInterface {
27
65
  endUpdatePending?: number;
28
66
  /** Internal event storage */
29
67
  private _events;
68
+ /** Prototype-stored event maps shape (set by View.prepare). */
69
+ private get protoEventState();
30
70
  /**
31
71
  * Event bitmask map: eventType -> bitmask (1=root, 2=selector).
32
72
  * Read from prototype ($evtObjMap) set by View.prepare.
@@ -55,6 +95,8 @@ declare class View implements ViewInterface {
55
95
  on(event: string, handler: AnyFunc): this;
56
96
  off(event: string, handler?: AnyFunc): this;
57
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();
58
100
  /**
59
101
  * Notify view that HTML update is about to begin.
60
102
  * Unmounts child frames in the update zone.
@@ -150,40 +192,44 @@ declare class View implements ViewInterface {
150
192
  */
151
193
  static merge(this: typeof View, ...mixins: Record<string, unknown>[]): typeof View;
152
194
  }
153
-
154
195
  /**
155
- * Multi-cast event emitter class.
196
+ * Type-safe wrapper around `View.extend()`.
156
197
  *
157
- * @example
158
- * const emitter = new EventEmitter();
159
- * emitter.on('change', (data) => console.log(data));
160
- * emitter.fire('change', { key: 'value' });
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.
161
220
  */
162
- declare class EventEmitter<T = unknown> implements EventEmitterInterface<T> {
163
- /** Event listeners: prefixed key -> listener array */
164
- listeners: Map<string, EventListenerEntry[]>;
165
- /**
166
- * Bind event listener.
167
- */
168
- on(event: string, handler: (this: T, e: ChangeEvent) => void): this;
169
- /**
170
- * Unbind event listener.
171
- * If handler is provided, removes only that handler.
172
- * If no handler, removes all handlers for the event.
173
- */
174
- off(event: string, handler?: AnyFunc): this;
175
- /**
176
- * Fire event, execute all bound handlers.
177
- * Supports executing state management: handlers removed during
178
- * execution are safely handled.
179
- *
180
- * @param event - Event name
181
- * @param data - Event data (type property added automatically)
182
- * @param remove - Whether to remove all handlers after firing
183
- * @param lastToFirst - Whether to execute handlers in reverse order
184
- */
185
- fire(event: string, data?: Record<string, unknown>, remove?: boolean, lastToFirst?: boolean): this;
186
- }
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;
187
233
 
188
234
  /**
189
235
  * Frame (View Frame) class for view lifecycle management.
@@ -202,8 +248,8 @@ declare class Frame extends EventEmitter implements FrameInterface {
202
248
  childrenCount: number;
203
249
  /** Ready count (children that have fired 'created') */
204
250
  readyCount: number;
205
- /** Ready map: id -> 1 */
206
- readyMap: Record<string, number>;
251
+ /** Set of child frame IDs that have fired 'created' */
252
+ readyMap: Set<string>;
207
253
  /** View instance */
208
254
  viewInstance?: ViewInterface;
209
255
  /** Get view instance (read-only) */
@@ -245,7 +291,7 @@ declare class Frame extends EventEmitter implements FrameInterface {
245
291
  /**
246
292
  * Internal: actually mount the view after class is loaded.
247
293
  */
248
- doMountView(ViewClass: typeof View, params: Record<string, string>, node: HTMLElement, sign: number): void;
294
+ doMountView(ViewClass: typeof View, params: Record<string, unknown>, node: HTMLElement, sign: number): void;
249
295
  /**
250
296
  * Unmount current view.
251
297
  */
@@ -279,11 +325,52 @@ declare class Frame extends EventEmitter implements FrameInterface {
279
325
  * Invoke a method on the view.
280
326
  */
281
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;
282
345
  /** Get frame by ID */
283
346
  static get(id: string): Frame | undefined;
284
347
  /** Get all frames */
285
348
  static getAll(): Map<string, Frame>;
286
- /** Get or create root 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
+ */
287
374
  static root(rootId?: string): Frame;
288
375
  /** Bind event listener (static) */
289
376
  static on(event: string, handler: AnyFunc): typeof Frame;
@@ -292,11 +379,6 @@ declare class Frame extends EventEmitter implements FrameInterface {
292
379
  /** Fire event (static) */
293
380
  static fire(event: string, data?: Record<string, unknown>): void;
294
381
  }
295
- /**
296
- * Register a View class for a given view path.
297
- * Called after module loading completes.
298
- */
299
- declare function registerViewClass(viewPath: string, ViewClass: typeof View): void;
300
382
 
301
383
  /**
302
384
  * Cache class with LFU-style eviction.
@@ -343,7 +425,9 @@ declare class Cache<T = unknown> implements CacheInterface<T> {
343
425
  */
344
426
  set(key: string, value: T): void;
345
427
  /**
346
- * Delete a cached entry.
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.
347
431
  */
348
432
  del(key: string): void;
349
433
  /**
@@ -354,7 +438,14 @@ declare class Cache<T = unknown> implements CacheInterface<T> {
354
438
  get size(): number;
355
439
  /** Clear all entries */
356
440
  clear(): void;
357
- /** Evict least-used entries from cache */
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
+ */
358
449
  private evictEntries;
359
450
  }
360
451
 
@@ -365,7 +456,9 @@ declare class Cache<T = unknown> implements CacheInterface<T> {
365
456
  * Lark is a lightweight MVC frontend framework that provides:
366
457
  * - View: base view class with extend/merge inheritance and mixin support
367
458
  * - Router: hash-based two-phase route confirmation
368
- * - State: cross-view observable global state management
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)
369
462
  * - Service: API request management with caching, queuing, and deduplication
370
463
  * - Frame: view frame managing view mount/unmount lifecycle
371
464
  * - Updater: view data binding and VDOM diff (in-memory real DOM diff) renderer
@@ -377,6 +470,8 @@ declare class Cache<T = unknown> implements CacheInterface<T> {
377
470
  * (TypeScript function parameters are contravariant).
378
471
  */
379
472
  type AnyFunc = (...args: any[]) => unknown;
473
+ /** A function that returns void. */
474
+ type VoidFunc = (...args: any[]) => void;
380
475
  interface CacheEntry<T> {
381
476
  /** Original key without prefix */
382
477
  originalKey: string;
@@ -558,7 +653,13 @@ interface VDomRef {
558
653
  /** Whether anything changed */
559
654
  hasChanged: number;
560
655
  }
561
- type VDomOp = [1, Element, Element] | [2, Element, Element] | [4, Element, Element, Element] | [8, Element, Element, Element];
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];
562
663
  interface FrameInvokeEntry {
563
664
  /** Method name */
564
665
  name: string;
@@ -583,6 +684,12 @@ interface ViewEventSelectorEntry {
583
684
  /** Index signature for checking if selector is already registered */
584
685
  [selector: string]: unknown;
585
686
  }
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;
586
693
  interface ViewLocationObserved {
587
694
  /** Whether observing location */
588
695
  flag: number;
@@ -609,6 +716,20 @@ interface ViewGlobalEventEntry {
609
716
  /** Modifiers */
610
717
  modifiers: Record<string, boolean>;
611
718
  }
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
+ }
612
733
  /**
613
734
  * Router interface providing URL parsing, navigation, diff, and event listening capabilities.
614
735
  * Supports two-phase route confirmation mechanism: change (can reject) → changed.
@@ -640,6 +761,23 @@ interface RouterInterface extends EventEmitterInterface<RouterInterface> {
640
761
  to(pathOrParams: string | Record<string, unknown>, params?: Record<string, unknown>, replace?: boolean, silent?: boolean): void;
641
762
  /** Join path segments */
642
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;
643
781
  /** Internal: bind hashchange (called by Framework.boot) */
644
782
  _bind(): void;
645
783
  /** Internal: set framework config */
@@ -655,6 +793,30 @@ interface RouterInterface extends EventEmitterInterface<RouterInterface> {
655
793
  */
656
794
  onChanged?: (e?: RouteChangedEvent) => void;
657
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
+ }
658
820
  /**
659
821
  * Frame static event interface carrying associated Frame instance.
660
822
  * Carried in Frame's add/remove static events.
@@ -688,10 +850,10 @@ interface ViewInterface extends EventEmitterInterface<ViewInterface> {
688
850
  /** Whether rendered at least once */
689
851
  rendered?: boolean;
690
852
  /**
691
- * View template supporting function or string template.
692
- * Function template signature: `(data: unknown, viewId: string, refData: unknown, ...rest: unknown[]) => string`
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.
693
855
  */
694
- template?: (data: unknown, viewId: string, refData: unknown, ...rest: unknown[]) => string | string;
856
+ template?: ViewTemplate;
695
857
  /**
696
858
  * Mixin object array for extending view functionality.
697
859
  * Framework merges properties and methods from mixins into view prototype.
@@ -817,6 +979,7 @@ interface ViewInterface extends EventEmitterInterface<ViewInterface> {
817
979
  * @param args Mixin object list
818
980
  */
819
981
  merge?(...args: ExtendThisType<ViewInterface>[]): ViewInterface;
982
+ navigate?: (path: string, params?: Record<string, unknown>) => void;
820
983
  }
821
984
  type ExtendThisType<T> = Record<string, unknown> & ThisType<T>;
822
985
  /**
@@ -936,7 +1099,7 @@ interface UpdaterInterface {
936
1099
  * @param data Data object, e.g., `{ a: 1, b: 2 }`
937
1100
  * @param excludes Set of keys to exclude from change tracking
938
1101
  */
939
- set: (data: Record<string, unknown>, excludes?: Set<string>) => UpdaterInterface;
1102
+ set: (data: Record<string, unknown>, excludes?: ReadonlySet<string>) => UpdaterInterface;
940
1103
  /**
941
1104
  * Trigger page re-render.
942
1105
  * After set, must explicitly call `digest()` to commit changes to page.
@@ -945,7 +1108,7 @@ interface UpdaterInterface {
945
1108
  * @param excludes Set of keys to exclude from change tracking
946
1109
  * @param callback Callback executed after render completes
947
1110
  */
948
- digest: (data?: Record<string, unknown>, excludes?: Set<string>, callback?: () => void) => void;
1111
+ digest: (data?: Record<string, unknown>, excludes?: ReadonlySet<string>, callback?: () => void) => void;
949
1112
  /**
950
1113
  * Save a snapshot of current data for altered() detection.
951
1114
  * Works with `altered()` method to detect whether data has changed.
@@ -1002,10 +1165,9 @@ interface ChangeEvent {
1002
1165
  */
1003
1166
  readonly type: string;
1004
1167
  /**
1005
- * Set object of changed data keys, value 1 indicates key has changed.
1006
- * TODO: Optimize to Set data structure.
1168
+ * Set of changed data keys. Use `keys.has(name)` to check membership.
1007
1169
  */
1008
- readonly keys?: Readonly<Record<string, 1>>;
1170
+ readonly keys?: ReadonlySet<string>;
1009
1171
  }
1010
1172
  /**
1011
1173
  * Event emitter interface providing on/off/fire methods for publish-subscribe pattern.
@@ -1038,6 +1200,11 @@ interface EventEmitterInterface<T = unknown> {
1038
1200
  * Global state interface providing cross-view data sharing and data change notification capabilities.
1039
1201
  * State is a singleton object managing app-level state data via get/set/digest.
1040
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.
1041
1208
  */
1042
1209
  interface StateInterface extends EventEmitterInterface<StateInterface> {
1043
1210
  /**
@@ -1051,7 +1218,7 @@ interface StateInterface extends EventEmitterInterface<StateInterface> {
1051
1218
  * @param data Data object, e.g., `{ a: 1, b: 2 }`
1052
1219
  * @param excludes Set of keys to exclude from change tracking
1053
1220
  */
1054
- set(data: Record<string, unknown>, excludes?: Set<string>): this;
1221
+ set(data: Record<string, unknown>, excludes?: ReadonlySet<string>): this;
1055
1222
  /**
1056
1223
  * Clean data for specified keys in State, can only be used in view's mixins.
1057
1224
  * For example `mixins: [State.clean("a,b")]`.
@@ -1069,11 +1236,11 @@ interface StateInterface extends EventEmitterInterface<StateInterface> {
1069
1236
  * @param data Optional data object, if provided calls `set()` first to set data
1070
1237
  * @param excludes Set of keys to exclude from change tracking
1071
1238
  */
1072
- digest(data?: Record<string, unknown>, excludes?: Set<string>): void;
1239
+ digest(data?: Record<string, unknown>, excludes?: ReadonlySet<string>): void;
1073
1240
  /**
1074
- * Triggered when state data changes.
1241
+ * Get the set of keys changed in the most recent digest.
1075
1242
  */
1076
- diff: () => Readonly<Record<string, number>>;
1243
+ diff: () => ReadonlySet<string>;
1077
1244
  onChanged?: (e?: ChangeEvent) => void;
1078
1245
  }
1079
1246
  interface ServiceOptions {
@@ -1155,6 +1322,103 @@ interface ServiceMetaEntry {
1155
1322
  /** Additional properties */
1156
1323
  [key: string]: unknown;
1157
1324
  }
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
+ }
1158
1422
  /** Cache info attached to Payload entity */
1159
1423
  interface ServiceCacheInfo {
1160
1424
  /** Endpoint name */
@@ -1170,13 +1434,27 @@ interface ServiceCacheInfo {
1170
1434
  }
1171
1435
  interface FrameworkInterface {
1172
1436
  /**
1173
- * Get or update configuration.
1174
- * - When passing config object: merges config and returns merged config
1175
- * - When passing key name: returns config value for that key
1176
- * - When no arguments: returns complete config object
1177
- * @param cfg Config object or key name
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.
1178
1455
  */
1179
1456
  config<T extends object = Partial<FrameworkConfig>>(cfg?: Partial<FrameworkConfig> & T): FrameworkConfig & T;
1457
+ /** @deprecated See above. */
1180
1458
  config(key: string): unknown;
1181
1459
  /**
1182
1460
  * App initialization entry point, starts framework and renders root view.
@@ -1205,9 +1483,9 @@ interface FrameworkInterface {
1205
1483
  * Example: `Framework.toUrl('/xxx/', {a:'b',c:'d'})` => `/xxx/?a=b&c=d`
1206
1484
  * @param path Path string
1207
1485
  * @param params Params object
1208
- * @param keepEmpty Set of keys to keep empty values
1486
+ * @param keepEmpty Set of keys whose empty values should be preserved
1209
1487
  */
1210
- toUrl(path: string, params?: Record<string, unknown>, keepEmpty?: Record<string, number>): string;
1488
+ toUrl(path: string, params?: Record<string, unknown>, keepEmpty?: Set<string>): string;
1211
1489
  /**
1212
1490
  * Parse URL string to path and params object.
1213
1491
  * Example: `Framework.parseUrl('/xxx/?a=b&c=d')` => `{path:'/xxx/', params:{a:'b',c:'d'}}`
@@ -1408,11 +1686,29 @@ interface FrameworkConfig {
1408
1686
  */
1409
1687
  unmatchedView?: string;
1410
1688
  /**
1411
- * Module require function
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
1412
1696
  */
1413
- require?: (names: string[], params?: Record<string, unknown>) => Promise<unknown> | undefined;
1697
+ require?: (names: string[], params?: Record<string, unknown>) => Promise<unknown[]> | undefined;
1414
1698
  /** Skip view rendered check */
1415
1699
  skipViewRendered?: boolean;
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[];
1416
1712
  /** Dynamic config access, custom config items */
1417
1713
  [key: string]: unknown;
1418
1714
  }
@@ -1422,6 +1718,23 @@ interface RouteViewConfig {
1422
1718
  /** Additional properties merged into location */
1423
1719
  [key: string]: unknown;
1424
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
+ }
1425
1738
  /** Element with VDOM diff cached compare key */
1426
1739
  interface VdomElement extends Element {
1427
1740
  /** Whether compare key is cached */
@@ -1469,7 +1782,7 @@ declare function generateId(prefix?: string): string;
1469
1782
  declare function syncCounter(val: number): void;
1470
1783
  declare function noop(): void;
1471
1784
  /** Safe hasOwnProperty check */
1472
- 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;
1473
1786
  /** Get object keys (own enumerable) */
1474
1787
  declare function keys<T extends object>(obj: T): string[];
1475
1788
  /** Assign properties from sources to target (like Object.assign but safer) */
@@ -1483,11 +1796,7 @@ declare function funcWithTry(fns: AnyFunc | AnyFunc[], args: unknown[], context:
1483
1796
  * Set newData into oldData, tracking changed keys.
1484
1797
  * Returns whether any value changed.
1485
1798
  */
1486
- declare function setData(newData: Record<string, unknown>, oldData: Record<string, unknown>, changedKeys: Record<string, number>, excludes: Set<string>): boolean;
1487
- /**
1488
- * Translate data references: if a value starts with SPLITTER, replace it
1489
- * with the corresponding value from the data object.
1490
- */
1799
+ declare function setData(newData: Record<string, unknown>, oldData: Record<string, unknown>, changedKeys: Set<string>, excludes: ReadonlySet<string>): boolean;
1491
1800
  declare function translateData(data: object, value: unknown): unknown;
1492
1801
  /** Get element by ID, or return the element itself if already an element */
1493
1802
  declare function getById(id: string | Element | null): Element | null;
@@ -1503,13 +1812,16 @@ declare function nodeInside(a: string | HTMLElement, b: string | HTMLElement): b
1503
1812
  /**
1504
1813
  * Parse URI string into path and params object.
1505
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.
1506
1818
  */
1507
1819
  declare function parseUri(uri: string): ParsedUri;
1508
1820
  /**
1509
1821
  * Convert path and params to URI string.
1510
1822
  * e.g. toUri("/xxx/", { a: "b", c: "d" }) => "/xxx/?a=b&c=d"
1511
1823
  */
1512
- 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;
1513
1825
  /**
1514
1826
  * Convert array to map/hash object.
1515
1827
  * For simple arrays, counts occurrences.
@@ -1518,18 +1830,10 @@ declare function toUri(path: string, params: Record<string, unknown>, keepEmptyO
1518
1830
  declare function toMap<T>(list: T[] | null | undefined, key?: keyof T): Record<string, T | number>;
1519
1831
  /** Get current timestamp */
1520
1832
  declare function now(): number;
1521
- /** Constructable function type */
1522
- type Constructable = new (...args: never[]) => unknown;
1523
- /**
1524
- * Implement classical inheritance with prototype chain.
1525
- * This is the only place where we touch constructor internals -
1526
- * it's a low-level framework utility for View.extend.
1527
- */
1528
- declare function classExtend<Proto extends object, Statics extends object>(make: Constructable, base: Constructable, props: Proto, statics: Statics): void;
1529
1833
 
1530
1834
  /** Internal splitter character (invisible, used as namespace separator) */
1531
1835
  declare const SPLITTER = "\u001E";
1532
- declare const ROUTER_EVENTS: {
1836
+ declare const RouterEvents: {
1533
1837
  CHANGE: string;
1534
1838
  CHANGED: string;
1535
1839
  PAGE_UNLOAD: string;
@@ -1564,23 +1868,27 @@ declare function applyStyle(styleIdOrPairs: string | string[], css?: string): ()
1564
1868
  /**
1565
1869
  * Mark/Unmark: signature-based lifecycle tracking for async callbacks.
1566
1870
  *
1567
- * mark() returns a validity checker function. If the host is "unmarked"
1568
- * (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.
1569
1878
  */
1570
1879
  /**
1571
1880
  * Create a mark for tracking async callback validity.
1572
- * Returns a function that checks if the mark is still valid.
1881
+ * Returns a function that returns true while the mark is still valid.
1573
1882
  *
1574
- * @param host - Object to store mark state on (typically a view)
1575
- * @param key - Key to track (typically "render" or specific async operation)
1576
- * @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)
1577
1885
  */
1578
1886
  declare function mark$1(host: object, key: string): () => boolean;
1579
1887
  /**
1580
- * Clear all marks for a host object, invalidating all existing checkers.
1888
+ * Clear all marks for a host object, invalidating every existing checker.
1581
1889
  * Called when a view re-renders or is destroyed.
1582
1890
  *
1583
- * @param host - Object whose marks should be cleared
1891
+ * @param host - Object whose marks should be invalidated
1584
1892
  */
1585
1893
  declare function unmark$1(host: object): void;
1586
1894
 
@@ -1624,6 +1932,56 @@ declare const Router: RouterInterface;
1624
1932
  /** Mark framework as booted (called by Framework.boot) */
1625
1933
  declare function markRouterBooted(): void;
1626
1934
 
1935
+ /**
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
1967
+ */
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;
1984
+
1627
1985
  /**
1628
1986
  * Unmount frames within a DOM node.
1629
1987
  */
@@ -1640,6 +1998,8 @@ declare function vdomGetNode(html: string, refNode: Element): Element;
1640
1998
  declare function vdomGetCompareKey(node: ChildNode): string | undefined;
1641
1999
  /**
1642
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.
1643
2003
  */
1644
2004
  declare function vdomSpecialDiff(oldNode: ChildNode, newNode: ChildNode): number;
1645
2005
  /**
@@ -1649,11 +2009,11 @@ declare function vdomSetAttributes(oldNode: Element, newNode: Element, ref: VDom
1649
2009
  /**
1650
2010
  * Set child nodes from new parent onto old parent using keyed diff algorithm.
1651
2011
  */
1652
- declare function vdomSetChildNodes(oldParent: Element, newParent: Element, ref: VDomRef, frame: FrameInterface, keys_?: Record<string, number>): void;
2012
+ declare function vdomSetChildNodes(oldParent: Element, newParent: Element, ref: VDomRef, frame: FrameInterface, keys_?: ReadonlySet<string>): void;
1653
2013
  /**
1654
2014
  * Diff two DOM nodes and apply changes.
1655
2015
  */
1656
- declare function vdomSetNode(oldNode: ChildNode, newNode: ChildNode, oldParent: Element, ref: VDomRef, frame: FrameInterface, keys_?: Record<string, number>): void;
2016
+ declare function vdomSetNode(oldNode: ChildNode, newNode: ChildNode, oldParent: Element, ref: VDomRef, frame: FrameInterface, keys_?: ReadonlySet<string>): void;
1657
2017
  /**
1658
2018
  * Create an empty VDomRef for tracking diff operations.
1659
2019
  */
@@ -1691,10 +2051,16 @@ declare class Updater implements UpdaterInterface {
1691
2051
  private changedKeys;
1692
2052
  /** Whether data has changed since last digest */
1693
2053
  private hasChangedFlag;
1694
- /** 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
+ */
1695
2059
  private digestingQueue;
1696
- /** Snapshot JSON string for altered() detection */
1697
- 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;
1698
2064
  constructor(viewId: string);
1699
2065
  /**
1700
2066
  * Get data by key.
@@ -1705,7 +2071,7 @@ declare class Updater implements UpdaterInterface {
1705
2071
  * Set data, tracking changed keys.
1706
2072
  * Returns this for chaining.
1707
2073
  */
1708
- set(data: Record<string, unknown>, excludes?: Set<string>): this;
2074
+ set(data: Record<string, unknown>, excludes?: ReadonlySet<string>): this;
1709
2075
  /**
1710
2076
  * Detect changes and trigger VDOM re-render.
1711
2077
  *
@@ -1717,31 +2083,44 @@ declare class Updater implements UpdaterInterface {
1717
2083
  * 5. Call endUpdate on views that need re-rendering
1718
2084
  * 6. Support re-digest during digest via queue
1719
2085
  */
1720
- digest(data?: Record<string, unknown>, excludes?: Set<string>, callback?: () => void): void;
2086
+ digest(data?: Record<string, unknown>, excludes?: ReadonlySet<string>, callback?: () => void): void;
1721
2087
  /**
1722
2088
  * Core digest execution.
1723
2089
  */
1724
2090
  private runDigest;
1725
2091
  /**
1726
- * 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.
1727
2094
  */
1728
2095
  snapshot(): this;
1729
2096
  /**
1730
- * 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.
1731
2099
  */
1732
2100
  altered(): boolean | undefined;
1733
2101
  /**
1734
- * 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").
1735
2108
  */
1736
2109
  translate(data: unknown): unknown;
1737
2110
  /**
1738
- * 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.
1739
2118
  */
1740
2119
  parse(expr: string): unknown;
1741
2120
  /**
1742
- * Get changed keys (for external inspection).
2121
+ * Get the set of keys changed since the last digest (for external inspection).
1743
2122
  */
1744
- getChangedKeys(): Readonly<Record<string, number>>;
2123
+ getChangedKeys(): ReadonlySet<string>;
1745
2124
  }
1746
2125
 
1747
2126
  /**
@@ -1898,10 +2277,20 @@ declare class Service {
1898
2277
  static fire(event: string, data?: Record<string, unknown>): void;
1899
2278
  /**
1900
2279
  * Create a new Service subclass with a custom sync function.
1901
- * Each subclass gets its own per-type state (metaList, cache, etc.)
1902
- * to ensure isolation between different Service types.
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.
1903
2292
  */
1904
- static extend(newSyncFn: (payload: Payload, callback: () => void) => void, newCacheMax?: number, newCacheBuffer?: number): typeof Service;
2293
+ static extend(this: typeof Service, newSyncFn: (payload: Payload, callback: () => void) => void, newCacheMax?: number, newCacheBuffer?: number): typeof Service;
1905
2294
  }
1906
2295
 
1907
2296
  /**
@@ -1939,11 +2328,18 @@ declare const EventDelegator: {
1939
2328
  declare const Framework: FrameworkInterface;
1940
2329
 
1941
2330
  /**
1942
- * @lark/framework Store
2331
+ * @lark.js/mvc Store
1943
2332
  *
1944
2333
  * Reactive state management with Proxy-based dependency tracking.
1945
2334
  * Adapted for Lark / React / Node.js.
1946
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
+ *
1947
2343
  * Core concepts:
1948
2344
  * - createState: Proxy-based deep reactive state
1949
2345
  * - track/trigger/clear: publish-subscribe dependency tracking
@@ -2018,6 +2414,29 @@ declare const _stateKeys: unique symbol;
2018
2414
  declare const _storeState: unique symbol;
2019
2415
  declare const _storeDefScheduler: unique symbol;
2020
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;
2021
2440
  declare abstract class BaseStore {
2022
2441
  [_storeName]: string;
2023
2442
  [_storeStatus]: StoreStatus;
@@ -2025,6 +2444,7 @@ declare abstract class BaseStore {
2025
2444
  [_originState]: Record<string, unknown>;
2026
2445
  [_stateKeys]: string[];
2027
2446
  [_storeState]: Record<string, unknown>;
2447
+ [_computedKeys]: Set<string>;
2028
2448
  [_storeDefScheduler]: () => Queue;
2029
2449
  private [_outerStore];
2030
2450
  [_storeBoot](): Record<string, unknown>;
@@ -2056,13 +2476,22 @@ interface NodeStoreMethods {
2056
2476
  observe: (key: string, callback: AnyFunc, immediate?: boolean) => () => void;
2057
2477
  }
2058
2478
  interface StoreMethods {
2059
- 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;
2060
2488
  }
2061
2489
  /** Detect platform from a component instance */
2062
2490
  declare const getPlatform: (comp: unknown) => Platform | undefined;
2063
2491
  declare const extendApis: {
2064
2492
  lazySet: typeof lazySet;
2065
2493
  shallowSet: typeof shallowSet;
2494
+ computed: typeof computed;
2066
2495
  };
2067
2496
  /** Inner store type available inside defineStore creator */
2068
2497
  type InnerStore<S = Record<string, unknown>> = S & StoreMethods;
@@ -2139,8 +2568,16 @@ interface SerializedFrameTree {
2139
2568
  /** Root element ID */
2140
2569
  rootId: string;
2141
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
+ };
2142
2578
  /**
2143
2579
  * Serialize the entire Frame tree starting from root.
2580
+ * Returns an empty snapshot if the app hasn't booted yet.
2144
2581
  */
2145
2582
  declare function serializeFrameTree(): SerializedFrameTree;
2146
2583
  /**
@@ -2153,7 +2590,7 @@ declare function serializeFrameTree(): SerializedFrameTree;
2153
2590
  declare function installFrameVisualizerBridge(): void;
2154
2591
 
2155
2592
  /**
2156
- * @lark/framework Template Compiler
2593
+ * @lark.js/mvc Template Compiler
2157
2594
  *
2158
2595
  * convertArtSyntax() ({{}} → <% %>)
2159
2596
  * processViewEvents() (@event prefix + param encoding)
@@ -2189,7 +2626,7 @@ declare function installFrameVisualizerBridge(): void;
2189
2626
  * This is the main entry point for both Vite and Webpack loaders.
2190
2627
  *
2191
2628
  * The output is an ES module that exports a function with the signature:
2192
- * (data, selfId, refData) => string
2629
+ * (data, viewId, refData) => string
2193
2630
  *
2194
2631
  * Internally it calls the compiled template function with the standard
2195
2632
  * signature: ($data,$viewId,$refAlt,$encHtml,$strSafe,$encUri,$refFn,$encQuote)
@@ -2217,4 +2654,4 @@ declare function compileTemplate(source: string, options?: CompileOptions): stri
2217
2654
  */
2218
2655
  declare function extractGlobalVars(source: string): string[];
2219
2656
 
2220
- export { CALL_BREAK_TIME, Cache, type CacheEntry, type CacheOptions, type CompileOptions, type Constructable, EVENT_METHOD_REGEXP, EventDelegator, EventEmitter, type EventListenerEntry, Frame, type FrameBoundElement, type FrameInterface, type FrameInvokeEntry, Framework, type FrameworkConfig, LARK_VIEW, type LarkUseStore, type Location, type LocationDiff, type MixinEventHandler, type NodeUseStore, type ObservePayload, type ParamDiff, type ParsedUri, Payload, type PayloadEntry, type PendingCacheEntry, Platform, ROUTER_EVENTS, type ReactUseStore, type RouteViewConfig, Router, SPLITTER, type SerializedFrameNode, type SerializedFrameTree, type SerializedViewInfo, Service, type ServiceCacheInfo, type ServiceEntry, type ServiceMetaEntry, type ServiceOptions, State, type StoreConfig, type StoreMethods, TAG_NAME_REGEXP, Updater, type UpdaterInterface, type VDomOp, type VDomRef, VIEW_EVENT_METHOD_REGEXP, type VdomElement, View, type ViewEventSelectorEntry, type ViewGlobalEventEntry, type ViewInterface, type ViewLocationObserved, type ViewResourceEntry, applyIdUpdates, applyStyle, applyVdomOps, assign, cell, classExtend, cloneData, cloneStore, compileTemplate, createState, createVdomRef, defineStore, delStore, encodeHTML, encodeQ, encodeSafe, encodeURIExtra, ensureElementId, extractGlobalVars, funcWithTry, generateId, getAttribute, getById, getPlatform, getStore, getUseStore, has, installFrameVisualizerBridge, isPlainObject, isPrimitive, isPrimitiveOrFunc, isState, isStoreActive, keys, lazySet, mark$1 as mark, markBooted, markRouterBooted, multi, nextCounter, nodeInside, noop, now, observeCell, parseUri, registerViewClass, safeguard, serializeFrameTree, setData, shallowSet, mark as storeMark, unmark as storeUnmark, syncCounter, toMap, toUri, translateData, unmark$1 as unmark, vdomGetCompareKey, vdomGetNode, vdomSetAttributes, vdomSetChildNodes, vdomSetNode, vdomSpecialDiff, vdomUnmountFrames };
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 };