@lark.js/mvc 0.0.4 → 0.0.6

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,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: zustand-aligned state management with create/getState/setState/subscribe
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 | Promise<boolean>): () => 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,10 @@ 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, or fine-grained subscriptions — use `create` instead.
1041
1207
  */
1042
1208
  interface StateInterface extends EventEmitterInterface<StateInterface> {
1043
1209
  /**
@@ -1051,7 +1217,7 @@ interface StateInterface extends EventEmitterInterface<StateInterface> {
1051
1217
  * @param data Data object, e.g., `{ a: 1, b: 2 }`
1052
1218
  * @param excludes Set of keys to exclude from change tracking
1053
1219
  */
1054
- set(data: Record<string, unknown>, excludes?: Set<string>): this;
1220
+ set(data: Record<string, unknown>, excludes?: ReadonlySet<string>): this;
1055
1221
  /**
1056
1222
  * Clean data for specified keys in State, can only be used in view's mixins.
1057
1223
  * For example `mixins: [State.clean("a,b")]`.
@@ -1069,11 +1235,11 @@ interface StateInterface extends EventEmitterInterface<StateInterface> {
1069
1235
  * @param data Optional data object, if provided calls `set()` first to set data
1070
1236
  * @param excludes Set of keys to exclude from change tracking
1071
1237
  */
1072
- digest(data?: Record<string, unknown>, excludes?: Set<string>): void;
1238
+ digest(data?: Record<string, unknown>, excludes?: ReadonlySet<string>): void;
1073
1239
  /**
1074
- * Triggered when state data changes.
1240
+ * Get the set of keys changed in the most recent digest.
1075
1241
  */
1076
- diff: () => Readonly<Record<string, number>>;
1242
+ diff: () => ReadonlySet<string>;
1077
1243
  onChanged?: (e?: ChangeEvent) => void;
1078
1244
  }
1079
1245
  interface ServiceOptions {
@@ -1155,6 +1321,103 @@ interface ServiceMetaEntry {
1155
1321
  /** Additional properties */
1156
1322
  [key: string]: unknown;
1157
1323
  }
1324
+ interface ServiceInternals {
1325
+ metaList: Record<string, ServiceMetaEntry>;
1326
+ payloadCache: CacheInterface<PayloadInterface>;
1327
+ pendingCacheKeys: Record<string, PendingCacheEntry>;
1328
+ syncFn: (payload: PayloadInterface, callback: () => void) => void;
1329
+ staticEmitter: EventEmitterInterface;
1330
+ }
1331
+ interface ServiceInterface {
1332
+ id: string;
1333
+ destroyed: number;
1334
+ busy: number;
1335
+ taskQueue: AnyFunc[];
1336
+ prevArgs: unknown[];
1337
+ emitter: EventEmitterInterface;
1338
+ internals: ServiceInternals;
1339
+ /**
1340
+ * Send all requests in parallel, execute done callback when all requests complete (success or failure).
1341
+ * If endpoint specifies cache and cache is valid, uses cached data directly.
1342
+ * @param metaList Endpoint name string, params object, or array of them
1343
+ * @param done Callback when all requests complete, first param is error array, followed by each endpoint's Payload
1344
+ */
1345
+ all(metaList: string | Record<string, unknown> | (string | Record<string, unknown>)[], done: AnyFunc): ServiceInterface;
1346
+ /**
1347
+ * Execute callback after each request succeeds, callback may be called multiple times.
1348
+ * @param metaList Endpoint name string, params object, or array of them
1349
+ * @param done Callback
1350
+ */
1351
+ one(attrs: string | Record<string, unknown> | (string | Record<string, unknown>)[], done: AnyFunc): ServiceInterface;
1352
+ /**
1353
+ * Similar to all, but always skips cache and forces actual requests.
1354
+ * @param metaList Endpoint name string, params object, or array of them
1355
+ * @param done Callback
1356
+ */
1357
+ save(metaList: string | Record<string, unknown> | (string | Record<string, unknown>)[], done: AnyFunc): ServiceInterface;
1358
+ /**
1359
+ * Queue task for execution, executes next task after previous all/one/save task completes, similar to Promise chain.
1360
+ * @param callback Callback invoked after task completes
1361
+ */
1362
+ enqueue(callback: AnyFunc): ServiceInterface;
1363
+ /**
1364
+ * Dequeue and execute next task in queue.
1365
+ */
1366
+ dequeue(...args: unknown[]): void;
1367
+ /**
1368
+ * Destroy current Service instance, cannot send new requests or invoke callbacks after destruction.
1369
+ */
1370
+ destroy(): void;
1371
+ /**
1372
+ * Add endpoint metadata, register one or more API endpoints.
1373
+ * @param metaList Endpoint metadata array, or single metadata object
1374
+ */
1375
+ add(attrs: ServiceMetaEntry | ServiceMetaEntry[]): void;
1376
+ /**
1377
+ * Get metadata object.
1378
+ * @param attrs Endpoint metadata object or name string
1379
+ */
1380
+ meta(attrs: string | Record<string, unknown>): ServiceMetaEntry;
1381
+ /**
1382
+ * Create Payload object from endpoint metadata.
1383
+ * @param meta Endpoint metadata object or name string
1384
+ */
1385
+ create(meta: Record<string, unknown>): PayloadInterface;
1386
+ /**
1387
+ * Get or create Payload object from cache.
1388
+ * @param meta Endpoint metadata object
1389
+ * @param createNew Whether to create new Payload object; if false, prioritizes getting from cache
1390
+ */
1391
+ get(meta: Record<string, unknown>, createNew?: boolean): {
1392
+ entity: PayloadInterface;
1393
+ needsUpdate: boolean;
1394
+ };
1395
+ /**
1396
+ * Get Payload object from cache, returns undefined if cache doesn't exist or has expired.
1397
+ * @param meta Endpoint metadata object
1398
+ */
1399
+ cached(meta: Record<string, unknown>): PayloadInterface | undefined;
1400
+ /**
1401
+ * Clear cached data for specified endpoint.
1402
+ * @param names Comma-separated endpoint name string or string array
1403
+ */
1404
+ clear(names: string | string[]): void;
1405
+ /**
1406
+ * Inherit to create new Service subclass, bind custom data sync function.
1407
+ * @param sync Method to sync data, typically exchanges data with server
1408
+ * @param cacheMax Maximum cache entries
1409
+ * @param cacheBuffer Cache buffer size
1410
+ */
1411
+ extend(newSyncFn: (payload: PayloadInterface, callback: () => void) => void, newCacheMax?: number, newCacheBuffer?: number): ServiceInterface;
1412
+ /**
1413
+ * Triggered before endpoint sends request.
1414
+ */
1415
+ onBegin?: (e?: ServiceEvent) => void;
1416
+ /**
1417
+ * Triggered when endpoint request completes, whether success or failure.
1418
+ */
1419
+ onEnd?: (e?: ServiceEvent) => void;
1420
+ }
1158
1421
  /** Cache info attached to Payload entity */
1159
1422
  interface ServiceCacheInfo {
1160
1423
  /** Endpoint name */
@@ -1170,13 +1433,27 @@ interface ServiceCacheInfo {
1170
1433
  }
1171
1434
  interface FrameworkInterface {
1172
1435
  /**
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
1436
+ * Read framework configuration.
1437
+ * - Without arguments: returns the complete config object.
1438
+ * - With a key: returns just `config[key]` (untyped use a generic to
1439
+ * constrain the return type if you know the key's shape).
1440
+ *
1441
+ * `getConfig` is a pure read — call `setConfig(patch)` to mutate.
1442
+ */
1443
+ getConfig(): FrameworkConfig;
1444
+ getConfig<T = unknown>(key: string): T | undefined;
1445
+ /**
1446
+ * Merge a patch into the framework configuration and return the merged
1447
+ * config object. Replaces the dual-purpose overload of `config()`.
1448
+ */
1449
+ setConfig<T extends object = Partial<FrameworkConfig>>(patch: Partial<FrameworkConfig> & T): FrameworkConfig & T;
1450
+ /**
1451
+ * @deprecated Use `getConfig()` / `getConfig(key)` for reads and
1452
+ * `setConfig(patch)` for writes. The overloaded `config()` blurred the
1453
+ * two and confused TypeScript inference; the split is a drop-in upgrade.
1178
1454
  */
1179
1455
  config<T extends object = Partial<FrameworkConfig>>(cfg?: Partial<FrameworkConfig> & T): FrameworkConfig & T;
1456
+ /** @deprecated See above. */
1180
1457
  config(key: string): unknown;
1181
1458
  /**
1182
1459
  * App initialization entry point, starts framework and renders root view.
@@ -1205,9 +1482,9 @@ interface FrameworkInterface {
1205
1482
  * Example: `Framework.toUrl('/xxx/', {a:'b',c:'d'})` => `/xxx/?a=b&c=d`
1206
1483
  * @param path Path string
1207
1484
  * @param params Params object
1208
- * @param keepEmpty Set of keys to keep empty values
1485
+ * @param keepEmpty Set of keys whose empty values should be preserved
1209
1486
  */
1210
- toUrl(path: string, params?: Record<string, unknown>, keepEmpty?: Record<string, number>): string;
1487
+ toUrl(path: string, params?: Record<string, unknown>, keepEmpty?: Set<string>): string;
1211
1488
  /**
1212
1489
  * Parse URL string to path and params object.
1213
1490
  * Example: `Framework.parseUrl('/xxx/?a=b&c=d')` => `{path:'/xxx/', params:{a:'b',c:'d'}}`
@@ -1365,6 +1642,12 @@ interface FrameworkConfig {
1365
1642
  * This field is required, defaults to "root".
1366
1643
  */
1367
1644
  rootId: string;
1645
+ /**
1646
+ * Routing mode.
1647
+ * - `"history"` (default): uses `history.pushState` / `popstate`, clean URLs like `/home`
1648
+ * - `"hash"`: uses URL hash fragment with `#!` prefix, e.g. `#!/home`
1649
+ */
1650
+ routeMode?: "history" | "hash";
1368
1651
  /**
1369
1652
  * Default view path.
1370
1653
  * Default root view path to load when URL doesn't match any route.
@@ -1383,7 +1666,7 @@ interface FrameworkConfig {
1383
1666
  * Use rewrite config item for path rewriting logic.
1384
1667
  */
1385
1668
  routes?: Record<string, string | RouteViewConfig>;
1386
- /** Hashbang prefix */
1669
+ /** Hashbang prefix (only used in hash mode) */
1387
1670
  hashbang?: string;
1388
1671
  /**
1389
1672
  * Error handler.
@@ -1408,11 +1691,29 @@ interface FrameworkConfig {
1408
1691
  */
1409
1692
  unmatchedView?: string;
1410
1693
  /**
1411
- * Module require function
1694
+ * Module require function for asynchronous view loading.
1695
+ * Called by `Framework.use()` when a view class is not found in the registry.
1696
+ * Integrate with Webpack Module Federation or other dynamic loading strategies.
1697
+ *
1698
+ * @param names - Array of module names to load (e.g., `["remote-app/views/home"]`)
1699
+ * @param params - Optional parameters passed to the module initializer
1700
+ * @returns Promise resolving to an array of loaded modules, or undefined if not available
1412
1701
  */
1413
- require?: (names: string[], params?: Record<string, unknown>) => Promise<unknown> | undefined;
1702
+ require?: (names: string[], params?: Record<string, unknown>) => Promise<unknown[]> | undefined;
1414
1703
  /** Skip view rendered check */
1415
1704
  skipViewRendered?: boolean;
1705
+ /**
1706
+ * Project name of the current application.
1707
+ * Used by the micro-frontend bridge to determine if a view path
1708
+ * belongs to the current project or a remote project.
1709
+ */
1710
+ projectName?: string;
1711
+ /**
1712
+ * Cross-site (micro-frontend) configuration list.
1713
+ * Defines remote projects that can be loaded via Module Federation.
1714
+ * Also accessible via `window.crossConfigs` for build-time injection.
1715
+ */
1716
+ crossConfigs?: CrossSiteConfig[];
1416
1717
  /** Dynamic config access, custom config items */
1417
1718
  [key: string]: unknown;
1418
1719
  }
@@ -1422,6 +1723,23 @@ interface RouteViewConfig {
1422
1723
  /** Additional properties merged into location */
1423
1724
  [key: string]: unknown;
1424
1725
  }
1726
+ /**
1727
+ * Configuration for a remote (cross-site) project in the micro-frontend setup.
1728
+ * Each entry defines how to load views from a different project via Module Federation.
1729
+ */
1730
+ interface CrossSiteConfig {
1731
+ /** Project name, used as the prefix in view paths (e.g., "remote-app" in "remote-app/views/home") */
1732
+ projectName: string;
1733
+ /**
1734
+ * Remote source URL or Module Federation remote name.
1735
+ * For Webpack MF: the remote entry URL (e.g., "remote_app@//cdn.example.com/remote-app/remoteEntry.js")
1736
+ */
1737
+ source: string;
1738
+ /** Optional API host for the remote project */
1739
+ apiHost?: string;
1740
+ /** Optional business code for multi-tenant scenarios */
1741
+ bizCode?: string;
1742
+ }
1425
1743
  /** Element with VDOM diff cached compare key */
1426
1744
  interface VdomElement extends Element {
1427
1745
  /** Whether compare key is cached */
@@ -1469,7 +1787,7 @@ declare function generateId(prefix?: string): string;
1469
1787
  declare function syncCounter(val: number): void;
1470
1788
  declare function noop(): void;
1471
1789
  /** Safe hasOwnProperty check */
1472
- declare function has<T extends object>(owner: T | undefined | null, prop: PropertyKey): boolean;
1790
+ declare function hasOwnProperty<T extends object>(owner: T | undefined | null, prop: PropertyKey): boolean;
1473
1791
  /** Get object keys (own enumerable) */
1474
1792
  declare function keys<T extends object>(obj: T): string[];
1475
1793
  /** Assign properties from sources to target (like Object.assign but safer) */
@@ -1483,11 +1801,7 @@ declare function funcWithTry(fns: AnyFunc | AnyFunc[], args: unknown[], context:
1483
1801
  * Set newData into oldData, tracking changed keys.
1484
1802
  * Returns whether any value changed.
1485
1803
  */
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
- */
1804
+ declare function setData(newData: Record<string, unknown>, oldData: Record<string, unknown>, changedKeys: Set<string>, excludes: ReadonlySet<string>): boolean;
1491
1805
  declare function translateData(data: object, value: unknown): unknown;
1492
1806
  /** Get element by ID, or return the element itself if already an element */
1493
1807
  declare function getById(id: string | Element | null): Element | null;
@@ -1503,13 +1817,16 @@ declare function nodeInside(a: string | HTMLElement, b: string | HTMLElement): b
1503
1817
  /**
1504
1818
  * Parse URI string into path and params object.
1505
1819
  * e.g. "/xxx/?a=b&c=d" => { path: "/xxx/", params: { a: "b", c: "d" } }
1820
+ *
1821
+ * The accumulator is function-local, so nested / re-entrant calls
1822
+ * (e.g. invoking `parseUri` again inside a replace callback) are safe.
1506
1823
  */
1507
1824
  declare function parseUri(uri: string): ParsedUri;
1508
1825
  /**
1509
1826
  * Convert path and params to URI string.
1510
1827
  * e.g. toUri("/xxx/", { a: "b", c: "d" }) => "/xxx/?a=b&c=d"
1511
1828
  */
1512
- declare function toUri(path: string, params: Record<string, unknown>, keepEmptyObject?: Record<string, number>): string;
1829
+ declare function toUri(path: string, params: Record<string, unknown>, keepEmpty?: ReadonlySet<string>): string;
1513
1830
  /**
1514
1831
  * Convert array to map/hash object.
1515
1832
  * For simple arrays, counts occurrences.
@@ -1518,18 +1835,11 @@ declare function toUri(path: string, params: Record<string, unknown>, keepEmptyO
1518
1835
  declare function toMap<T>(list: T[] | null | undefined, key?: keyof T): Record<string, T | number>;
1519
1836
  /** Get current timestamp */
1520
1837
  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
1838
 
1530
- /** Internal splitter character (invisible, used as namespace separator) */
1531
- declare const SPLITTER = "\u001E";
1532
- declare const ROUTER_EVENTS: {
1839
+ /** Internal splitter character (U+001E Record Separator, invisible, used as namespace separator).
1840
+ * Uses String.fromCharCode to survive bundlers that strip control-char literals. */
1841
+ declare const SPLITTER: string;
1842
+ declare const RouterEvents: {
1533
1843
  CHANGE: string;
1534
1844
  CHANGED: string;
1535
1845
  PAGE_UNLOAD: string;
@@ -1564,25 +1874,29 @@ declare function applyStyle(styleIdOrPairs: string | string[], css?: string): ()
1564
1874
  /**
1565
1875
  * Mark/Unmark: signature-based lifecycle tracking for async callbacks.
1566
1876
  *
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.
1877
+ * `mark(host, key)` returns a validity checker. The checker returns `false`
1878
+ * once the host is unmarked (e.g. when a view re-renders or is destroyed),
1879
+ * so stale async callbacks can short-circuit and skip work.
1880
+ *
1881
+ * State is stored in a module-level WeakMap, not on the host object, so
1882
+ * `mark/unmark` never pollutes user objects with magic keys, never breaks
1883
+ * on `Object.freeze`-ed inputs, and never shows up in debug snapshots.
1569
1884
  */
1570
1885
  /**
1571
1886
  * Create a mark for tracking async callback validity.
1572
- * Returns a function that checks if the mark is still valid.
1887
+ * Returns a function that returns true while the mark is still valid.
1573
1888
  *
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
1889
+ * @param host - Object to associate the mark with (typically a view)
1890
+ * @param key - Key to track (typically "render" or a specific async-op identifier)
1577
1891
  */
1578
- declare function mark$1(host: object, key: string): () => boolean;
1892
+ declare function mark(host: object, key: string): () => boolean;
1579
1893
  /**
1580
- * Clear all marks for a host object, invalidating all existing checkers.
1894
+ * Clear all marks for a host object, invalidating every existing checker.
1581
1895
  * Called when a view re-renders or is destroyed.
1582
1896
  *
1583
- * @param host - Object whose marks should be cleared
1897
+ * @param host - Object whose marks should be invalidated
1584
1898
  */
1585
- declare function unmark$1(host: object): void;
1899
+ declare function unmark(host: object): void;
1586
1900
 
1587
1901
  /**
1588
1902
  * Safeguard: Proxy-based debug protection for data objects.
@@ -1613,7 +1927,7 @@ declare function markBooted(): void;
1613
1927
  declare const State: StateInterface;
1614
1928
 
1615
1929
  /**
1616
- * Hash-based router with two-phase change confirmation.
1930
+ * Router with two-phase change confirmation (supports history and hash modes).
1617
1931
  *
1618
1932
  * @example
1619
1933
  * Router.to('/list', { page: 2 });
@@ -1623,6 +1937,58 @@ declare const State: StateInterface;
1623
1937
  declare const Router: RouterInterface;
1624
1938
  /** Mark framework as booted (called by Framework.boot) */
1625
1939
  declare function markRouterBooted(): void;
1940
+ /** Get current routing mode */
1941
+ declare function getRouteMode(): "history" | "hash";
1942
+
1943
+ /**
1944
+ * Module loader: async view loading via FrameworkConfig.require or dynamic import.
1945
+ *
1946
+ * Extracted from framework.ts to avoid circular dependency with frame.ts.
1947
+ * Both framework.ts and frame.ts import from this module.
1948
+ */
1949
+
1950
+ /** Framework configuration */
1951
+ declare const config: FrameworkConfig;
1952
+ /**
1953
+ * Load modules via the configured require function or dynamic import fallback.
1954
+ *
1955
+ * Two calling conventions:
1956
+ * 1. `use(name | name[], callback)`
1957
+ * 2. `use(name | name[])` — returns Promise<unknown[]> (no callback)
1958
+ *
1959
+ * When `FrameworkConfig.require` is configured, delegates to it (e.g., Webpack Module Federation).
1960
+ * When not configured, falls back to `dynamic import()` for ESM-based loading.
1961
+ */
1962
+ declare function use(names: string | string[], callback?: (...modules: unknown[]) => void): Promise<unknown[]>;
1963
+
1964
+ /**
1965
+ * CrossSite: Micro-frontend bridge View for cross-project view loading.
1966
+ *
1967
+ * For Lark + Webpack Module Federation.
1968
+ * Provides skeleton rendering, prepare preloading, assign reuse, and remote view mounting.
1969
+ *
1970
+ * Usage (in host project):
1971
+ * 1. Register CrossSite as the bridge view for cross-site paths:
1972
+ * registerViewClass('cross-site', CrossSite);
1973
+ * 2. Use v-lark="cross-site?view=remote-app/views/home&param=1" in template
1974
+ * 3. CrossSite loads the remote project's prepare module, then mounts the actual view
1975
+ */
1976
+
1977
+ /**
1978
+ * Reset the projects map cache (useful when crossConfigs change at runtime).
1979
+ */
1980
+ declare function resetProjectsMap(): void;
1981
+ /**
1982
+ * CrossSite bridge View for micro-frontend integration.
1983
+ *
1984
+ * Flow:
1985
+ * 1. CrossSite is mounted as a regular view (registered as "cross-site")
1986
+ * 2. render() shows skeleton template with a child container
1987
+ * 3. updateView() loads the remote project's prepare module
1988
+ * 4. Once loaded, mounts the actual remote view into the child container
1989
+ * 5. On re-assign (same view path), calls assign on the remote view instead of re-mounting
1990
+ */
1991
+ declare const CrossSite: typeof View;
1626
1992
 
1627
1993
  /**
1628
1994
  * Unmount frames within a DOM node.
@@ -1640,6 +2006,8 @@ declare function vdomGetNode(html: string, refNode: Element): Element;
1640
2006
  declare function vdomGetCompareKey(node: ChildNode): string | undefined;
1641
2007
  /**
1642
2008
  * Special diff for form elements (value, checked, selected).
2009
+ * Form elements carry state on the DOM node (e.g. `input.value`) that isn't
2010
+ * reflected in attributes, so we have to sync those properties separately.
1643
2011
  */
1644
2012
  declare function vdomSpecialDiff(oldNode: ChildNode, newNode: ChildNode): number;
1645
2013
  /**
@@ -1649,11 +2017,11 @@ declare function vdomSetAttributes(oldNode: Element, newNode: Element, ref: VDom
1649
2017
  /**
1650
2018
  * Set child nodes from new parent onto old parent using keyed diff algorithm.
1651
2019
  */
1652
- declare function vdomSetChildNodes(oldParent: Element, newParent: Element, ref: VDomRef, frame: FrameInterface, keys_?: Record<string, number>): void;
2020
+ declare function vdomSetChildNodes(oldParent: Element, newParent: Element, ref: VDomRef, frame: FrameInterface, keys_?: ReadonlySet<string>): void;
1653
2021
  /**
1654
2022
  * Diff two DOM nodes and apply changes.
1655
2023
  */
1656
- declare function vdomSetNode(oldNode: ChildNode, newNode: ChildNode, oldParent: Element, ref: VDomRef, frame: FrameInterface, keys_?: Record<string, number>): void;
2024
+ declare function vdomSetNode(oldNode: ChildNode, newNode: ChildNode, oldParent: Element, ref: VDomRef, frame: FrameInterface, keys_?: ReadonlySet<string>): void;
1657
2025
  /**
1658
2026
  * Create an empty VDomRef for tracking diff operations.
1659
2027
  */
@@ -1691,10 +2059,16 @@ declare class Updater implements UpdaterInterface {
1691
2059
  private changedKeys;
1692
2060
  /** Whether data has changed since last digest */
1693
2061
  private hasChangedFlag;
1694
- /** Digesting queue: supports re-digest during digest */
2062
+ /**
2063
+ * Digesting queue: supports re-digest during digest.
2064
+ * Holds pending callbacks; `null` is used as a sentinel marking the start
2065
+ * of an active digest cycle, so `runDigest` can detect re-entrant calls.
2066
+ */
1695
2067
  private digestingQueue;
1696
- /** Snapshot JSON string for altered() detection */
1697
- private snapshotJson;
2068
+ /** Monotonically increasing version, bumped each time data actually changes. */
2069
+ private version;
2070
+ /** Snapshot of `version` taken by `snapshot()`, used by `altered()`. */
2071
+ private snapshotVersion;
1698
2072
  constructor(viewId: string);
1699
2073
  /**
1700
2074
  * Get data by key.
@@ -1705,7 +2079,7 @@ declare class Updater implements UpdaterInterface {
1705
2079
  * Set data, tracking changed keys.
1706
2080
  * Returns this for chaining.
1707
2081
  */
1708
- set(data: Record<string, unknown>, excludes?: Set<string>): this;
2082
+ set(data: Record<string, unknown>, excludes?: ReadonlySet<string>): this;
1709
2083
  /**
1710
2084
  * Detect changes and trigger VDOM re-render.
1711
2085
  *
@@ -1717,31 +2091,44 @@ declare class Updater implements UpdaterInterface {
1717
2091
  * 5. Call endUpdate on views that need re-rendering
1718
2092
  * 6. Support re-digest during digest via queue
1719
2093
  */
1720
- digest(data?: Record<string, unknown>, excludes?: Set<string>, callback?: () => void): void;
2094
+ digest(data?: Record<string, unknown>, excludes?: ReadonlySet<string>, callback?: () => void): void;
1721
2095
  /**
1722
2096
  * Core digest execution.
1723
2097
  */
1724
2098
  private runDigest;
1725
2099
  /**
1726
- * Save a snapshot of current data for altered() detection.
2100
+ * Save a snapshot of the current data version for `altered()` detection.
2101
+ * Cheap O(1) — records the current monotonic version, no serialization.
1727
2102
  */
1728
2103
  snapshot(): this;
1729
2104
  /**
1730
- * Check if data has changed since last snapshot.
2105
+ * Check whether data has changed since the last snapshot.
2106
+ * Returns undefined when no snapshot has been taken yet.
1731
2107
  */
1732
2108
  altered(): boolean | undefined;
1733
2109
  /**
1734
- * Translate data references (SPLITTER-prefixed values).
2110
+ * Translate a refData reference back to its original value.
2111
+ *
2112
+ * The ref protocol is `SPLITTER` + ascii decimal digits — the exact format
2113
+ * emitted by `updaterRef`. We require that exact shape so a user-supplied
2114
+ * string that merely begins with SPLITTER is never accidentally resolved
2115
+ * (or mishandled as a "missing ref").
1735
2116
  */
1736
2117
  translate(data: unknown): unknown;
1737
2118
  /**
1738
- * Parse expression with data context.
2119
+ * Resolve a dotted property path against refData.
2120
+ *
2121
+ * Only safe property-path syntax is supported: `a`, `a.b`, `a.b.c`.
2122
+ * Numeric literals (e.g. `1`, `1.5`) are returned as numbers. Anything else
2123
+ * returns `undefined` — we no longer evaluate arbitrary JavaScript via
2124
+ * `new Function`, so the method is CSP-safe and cannot be used as an
2125
+ * injection vector.
1739
2126
  */
1740
2127
  parse(expr: string): unknown;
1741
2128
  /**
1742
- * Get changed keys (for external inspection).
2129
+ * Get the set of keys changed since the last digest (for external inspection).
1743
2130
  */
1744
- getChangedKeys(): Readonly<Record<string, number>>;
2131
+ getChangedKeys(): ReadonlySet<string>;
1745
2132
  }
1746
2133
 
1747
2134
  /**
@@ -1898,10 +2285,20 @@ declare class Service {
1898
2285
  static fire(event: string, data?: Record<string, unknown>): void;
1899
2286
  /**
1900
2287
  * 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.
2288
+ *
2289
+ * Each subclass gets its OWN copies of every per-type static field
2290
+ * (`_metaList`, `_payloadCache`, `_pendingCacheKeys`, `_syncFn`,
2291
+ * `_staticEmitter`, `_cacheMax`, `_cacheBuffer`) via `static override`.
2292
+ * This is intentional: it ensures that endpoint metadata, cache state,
2293
+ * in-flight dedup keys, and event subscribers are fully isolated between
2294
+ * different Service types, even when one extends another.
2295
+ *
2296
+ * **Do not refactor these `static override` declarations away** — sharing
2297
+ * them through prototype inheritance would let endpoints registered on one
2298
+ * subclass leak into another, and the LFU cache evictions of one type
2299
+ * would race with those of another.
1903
2300
  */
1904
- static extend(newSyncFn: (payload: Payload, callback: () => void) => void, newCacheMax?: number, newCacheBuffer?: number): typeof Service;
2301
+ static extend(this: typeof Service, newSyncFn: (payload: Payload, callback: () => void) => void, newCacheMax?: number, newCacheBuffer?: number): typeof Service;
1905
2302
  }
1906
2303
 
1907
2304
  /**
@@ -1939,152 +2336,100 @@ declare const EventDelegator: {
1939
2336
  declare const Framework: FrameworkInterface;
1940
2337
 
1941
2338
  /**
1942
- * @lark/framework Store
2339
+ * Sync view state with URL query parameters.
1943
2340
  *
1944
- * Reactive state management with Proxy-based dependency tracking.
1945
- * Adapted for Lark / React / Node.js.
2341
+ * @param view - The view instance to bind to (for auto location observation and lifecycle)
2342
+ * @param initialState - Default values for each URL param key. Keys not present
2343
+ * in the URL will use these defaults. Keys present in the URL override defaults.
2344
+ * @returns A tuple `[state, setState]`:
2345
+ * - `state`: current values read from the URL, merged with defaults
2346
+ * - `setState`: update URL params. Accepts a partial object or an updater function.
2347
+ * Only the specified keys are changed; other URL params are preserved.
1946
2348
  *
1947
- * Core concepts:
1948
- * - createState: Proxy-based deep reactive state
1949
- * - track/trigger/clear: publish-subscribe dependency tracking
1950
- * - defineStore: declarative store definition with platform adapters
1951
- * - cell/observeCell: lightweight standalone reactive cells
1952
- */
2349
+ * @example
2350
+ * ```ts
2351
+ * export default View.extend({
2352
+ * template,
2353
+ * init() {
2354
+ * const [state, setState] = useUrlState(this, { page: "1", size: "20" });
2355
+ * this.updater.set({ page: state.page, size: state.size }).digest();
2356
+ * this.setState = setState;
2357
+ * },
2358
+ * assign() {
2359
+ * const [state] = useUrlState(this, { page: "1", size: "20" });
2360
+ * this.updater.set({ page: state.page, size: state.size });
2361
+ * },
2362
+ * "nextPage<click>"() {
2363
+ * this.setState((prev) => ({ page: String(Number(prev.page) + 1) }));
2364
+ * },
2365
+ * });
2366
+ * ```
2367
+ */
2368
+ declare function useUrlState<S extends Record<string, string>>(view: ViewInterface, initialState?: S): [Readonly<S>, (patch: Partial<S> | ((prev: S) => Partial<S>)) => void];
1953
2369
 
1954
- interface Task {
1955
- cb: AnyFunc;
1956
- params?: Record<string, unknown>;
1957
- }
1958
- interface SchedulerObject {
1959
- add: (tasks: Task[]) => void;
1960
- clear: () => void;
1961
- delete: (tasks: Task[]) => void;
1962
- }
1963
- type Scheduler = SchedulerObject | ((cb: AnyFunc, payload: unknown) => void);
1964
- interface StateConfig {
1965
- belong?: string;
1966
- linkKeys?: string;
1967
- shallow?: boolean;
1968
- }
1969
- interface StoreConfig {
1970
- platform?: Platform;
1971
- scheduler?: Scheduler;
1972
- }
1973
- interface ObservePayload {
1974
- key: string;
1975
- alias?: string;
1976
- cb?: (changedMap: Record<string, unknown>) => void;
1977
- lazy?: boolean;
1978
- transform?: (val: unknown) => Record<string, unknown>;
1979
- }
1980
- declare enum StoreStatus {
1981
- BEFORE_CREATE = 0,
1982
- CREATED = 1,
1983
- ACTIVE = 2,
1984
- DESTROYED = 3
1985
- }
1986
- declare enum Platform {
1987
- Lark = "lark",
1988
- React = "react",
1989
- Node = "node"
1990
- }
1991
- declare const cloneData: <T>(data: T) => T;
1992
- declare class Queue {
1993
- private pendingTasks;
1994
- private queue;
1995
- flushTasks(): void;
1996
- add(tasks: Task[]): void;
1997
- delete(tasks: Task[]): void;
1998
- clear(): void;
1999
- }
2000
- /** Check if value is a reactive state */
2001
- declare const isState: (target: unknown) => boolean;
2002
- declare const mark: (host: Record<string, unknown>, key: string) => (() => boolean);
2003
- declare const unmark: (host: Record<string, unknown>) => void;
2004
- declare function createState(initialData: unknown, config?: StateConfig): Record<string, unknown>;
2005
- declare function lazySet(target: Record<string, unknown>, data: Record<string, unknown>): void;
2006
- /** Shallow set: only top-level properties are reactive */
2007
- declare function shallowSet(target: Record<string, unknown>, key: string, data: unknown): unknown;
2008
- declare const _storeName: unique symbol;
2009
- declare const _storeStatus: unique symbol;
2010
- declare const _storeScheduler: unique symbol;
2011
- declare const _storeCreate: unique symbol;
2012
- declare const _storeBoot: unique symbol;
2013
- declare const _storeDestroy: unique symbol;
2014
- declare const _innerStore: unique symbol;
2015
- declare const _outerStore: unique symbol;
2016
- declare const _originState: unique symbol;
2017
- declare const _stateKeys: unique symbol;
2018
- declare const _storeState: unique symbol;
2019
- declare const _storeDefScheduler: unique symbol;
2020
- declare const _storeProxy: unique symbol;
2021
- declare abstract class BaseStore {
2022
- [_storeName]: string;
2023
- [_storeStatus]: StoreStatus;
2024
- [_storeScheduler]: Scheduler;
2025
- [_originState]: Record<string, unknown>;
2026
- [_stateKeys]: string[];
2027
- [_storeState]: Record<string, unknown>;
2028
- [_storeDefScheduler]: () => Queue;
2029
- private [_outerStore];
2030
- [_storeBoot](): Record<string, unknown>;
2031
- [_storeDestroy](): void;
2032
- /**
2033
- *
2034
- * @param body - The object returned by the creator function
2035
- * @param excludeFns - Function keys to exclude from handlers (e.g. ['observe'])
2036
- */
2037
- [_storeCreate](body: Record<string, unknown>, excludeFns?: string[]): void;
2038
- constructor(name: string, config?: StoreConfig);
2039
- [_innerStore](): Record<string, unknown>;
2040
- get status(): StoreStatus;
2041
- get storeName(): string;
2042
- get scheduler(): Scheduler;
2043
- [_storeProxy](toOut?: boolean): Record<string, unknown>;
2044
- }
2045
- interface LarkView {
2046
- updater: {
2047
- set: (data: Record<string, unknown>) => unknown;
2048
- digest: (data?: Record<string, unknown>) => void;
2049
- };
2050
- on: (event: string, handler: AnyFunc) => unknown;
2051
- }
2052
- type LarkUseStore<S = Record<string, unknown>> = (view?: LarkView) => S & StoreMethods;
2053
- type ReactUseStore<S = Record<string, unknown>, StateSlice = S> = (selector?: (store: S & StoreMethods) => StateSlice) => StateSlice;
2054
- type NodeUseStore<S = Record<string, unknown>> = () => S & NodeStoreMethods;
2055
- interface NodeStoreMethods {
2056
- observe: (key: string, callback: AnyFunc, immediate?: boolean) => () => void;
2057
- }
2058
- interface StoreMethods {
2059
- observe: (view: LarkView | undefined, keys: (string | ObservePayload)[] | (() => (string | ObservePayload)[]), defCallback?: (changedMap: Record<string, unknown>) => void) => () => void;
2370
+ /**
2371
+ * @lark.js/mvc Store
2372
+ *
2373
+ * Zustand-aligned state management for Lark MVC.
2374
+ *
2375
+ * Core API:
2376
+ * - create(name, creator): define a store with (set, get) => initialState
2377
+ * - store.getState(): read current state snapshot
2378
+ * - store.setState(partial | updater): shallow-merge state and notify listeners
2379
+ * - store.subscribe(listener): listen for state changes
2380
+ * - store.destroy(): tear down the store
2381
+ * - computed(deps, fn): derived state that auto-recomputes when deps change
2382
+ * - bindStore(view, store, selector?): Lark View lifecycle binding
2383
+ */
2384
+ type Listener<T> = (state: T, prevState: T) => void;
2385
+ interface StoreApi<T = Record<string, unknown>> {
2386
+ getState(): T;
2387
+ setState(partial: Partial<T> | ((prev: T) => Partial<T>)): void;
2388
+ subscribe(listener: Listener<T>): () => void;
2389
+ destroy(): void;
2060
2390
  }
2061
- /** Detect platform from a component instance */
2062
- declare const getPlatform: (comp: unknown) => Platform | undefined;
2063
- declare const extendApis: {
2064
- lazySet: typeof lazySet;
2065
- shallowSet: typeof shallowSet;
2066
- };
2067
- /** Inner store type available inside defineStore creator */
2068
- type InnerStore<S = Record<string, unknown>> = S & StoreMethods;
2069
- declare function defineStore<S = Record<string, unknown>>(name: string, creator: (store: InnerStore<S>, apis: typeof extendApis) => S, config?: StoreConfig & {
2070
- platform: Platform.Lark;
2071
- }): LarkUseStore<S>;
2072
- declare function defineStore<S = Record<string, unknown>>(name: string, creator: (store: InnerStore<S>, apis: typeof extendApis) => S, config?: StoreConfig & {
2073
- platform: Platform.React;
2074
- }): ReactUseStore<S>;
2075
- declare function defineStore<S = Record<string, unknown>>(name: string, creator: (store: InnerStore<S>, apis: typeof extendApis) => S, config?: StoreConfig & {
2076
- platform: Platform.Node;
2077
- }): NodeUseStore<S>;
2078
- declare function getStore(name: string): BaseStore | undefined;
2079
- declare function delStore(name: string): void;
2080
- declare function getUseStore(name: string): AnyFunc | undefined;
2081
- declare function cloneStore(name: string, useStore: AnyFunc, config?: StoreConfig): unknown;
2082
- declare function isStoreActive(name: string): boolean;
2083
- declare function cell<T = unknown>(data: T): T;
2084
- declare function observeCell(state: Record<string, unknown>, cb: AnyFunc, immediate?: boolean): () => void;
2085
- declare function multi<S = Record<string, unknown>>(useStore: LarkUseStore<S>): [LarkUseStore<S>, {
2086
- make: AnyFunc;
2087
- }];
2391
+ type StateCreator<T> = (set: (partial: Partial<T> | ((prev: T) => Partial<T>)) => void, get: () => T) => T;
2392
+ /**
2393
+ * Declare a derived (computed) store property.
2394
+ *
2395
+ * Usage inside a `create` creator:
2396
+ * ```ts
2397
+ * const store = create("counter", (set, get) => ({
2398
+ * count: 0,
2399
+ * doubled: computed(["count"], () => get().count * 2),
2400
+ * }));
2401
+ * ```
2402
+ *
2403
+ * `deps` lists the state keys the computed reads. Whenever any dep changes
2404
+ * via `setState`, the computed re-evaluates before listeners are notified.
2405
+ * Writes to a computed key via `setState` are silently ignored.
2406
+ */
2407
+ declare function computed<T>(deps: readonly string[], fn: () => T): T;
2408
+ declare function create<T>(name: string, creator: StateCreator<T>): StoreApi<T>;
2409
+ /**
2410
+ * Bind a store to a Lark View. Subscribes to state changes and auto-unsubscribes
2411
+ * when the view is destroyed.
2412
+ *
2413
+ * @param view - Lark View instance (must have updater.set/digest and on("destroy"))
2414
+ * @param store - Store created via `create()`
2415
+ * @param selector - Optional function to pick a subset of state for the updater.
2416
+ * If omitted, only non-function state keys are forwarded.
2417
+ * @returns unsubscribe function
2418
+ *
2419
+ * @example
2420
+ * ```ts
2421
+ * // Observe all state
2422
+ * bindStore(this, useCountStore);
2423
+ *
2424
+ * // Observe with selector
2425
+ * bindStore(this, useCountStore, (s) => ({ count: s.count }));
2426
+ * ```
2427
+ */
2428
+ declare function bindStore<T extends Record<string, unknown>>(view: unknown, store: StoreApi<T>, selector?: (state: T) => Record<string, unknown>): () => void;
2429
+ /**
2430
+ * @deprecated Use `create()` instead. This is a temporary alias.
2431
+ */
2432
+ declare const defineStore: typeof create;
2088
2433
 
2089
2434
  /** Serialized view info attached to a frame node */
2090
2435
  interface SerializedViewInfo {
@@ -2104,6 +2449,14 @@ interface SerializedViewInfo {
2104
2449
  };
2105
2450
  /** Whether view has a template function */
2106
2451
  hasTemplate: boolean;
2452
+ /** Delegated event types (keys from $evtObjMap) */
2453
+ eventMethodKeys: string[];
2454
+ /** Captured resource keys */
2455
+ resourceKeys: string[];
2456
+ /** Whether view exposes an assign method (supports CrossSite reuse) */
2457
+ hasAssign: boolean;
2458
+ /** Updater refData snapshot (shallow copy of current data) */
2459
+ updaterData: Record<string, unknown> | null;
2107
2460
  }
2108
2461
  /** A single node in the serialized frame tree */
2109
2462
  interface SerializedFrameNode {
@@ -2139,8 +2492,16 @@ interface SerializedFrameTree {
2139
2492
  /** Root element ID */
2140
2493
  rootId: string;
2141
2494
  }
2495
+ declare const FrameVisualBridge: {
2496
+ MSG_PING: string;
2497
+ MSG_PONG: string;
2498
+ MSG_REQUEST_TREE: string;
2499
+ MSG_TREE: string;
2500
+ MSG_TREE_DELTA: string;
2501
+ };
2142
2502
  /**
2143
2503
  * Serialize the entire Frame tree starting from root.
2504
+ * Returns an empty snapshot if the app hasn't booted yet.
2144
2505
  */
2145
2506
  declare function serializeFrameTree(): SerializedFrameTree;
2146
2507
  /**
@@ -2153,7 +2514,7 @@ declare function serializeFrameTree(): SerializedFrameTree;
2153
2514
  declare function installFrameVisualizerBridge(): void;
2154
2515
 
2155
2516
  /**
2156
- * @lark/framework Template Compiler
2517
+ * @lark.js/mvc Template Compiler
2157
2518
  *
2158
2519
  * convertArtSyntax() ({{}} → <% %>)
2159
2520
  * processViewEvents() (@event prefix + param encoding)
@@ -2189,7 +2550,7 @@ declare function installFrameVisualizerBridge(): void;
2189
2550
  * This is the main entry point for both Vite and Webpack loaders.
2190
2551
  *
2191
2552
  * The output is an ES module that exports a function with the signature:
2192
- * (data, selfId, refData) => string
2553
+ * (data, viewId, refData) => string
2193
2554
  *
2194
2555
  * Internally it calls the compiled template function with the standard
2195
2556
  * signature: ($data,$viewId,$refAlt,$encHtml,$strSafe,$encUri,$refFn,$encQuote)
@@ -2217,4 +2578,4 @@ declare function compileTemplate(source: string, options?: CompileOptions): stri
2217
2578
  */
2218
2579
  declare function extractGlobalVars(source: string): string[];
2219
2580
 
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 };
2581
+ 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 Location, type LocationDiff, type MixinEventHandler, type ParamDiff, type ParsedUri, Payload, type PayloadEntry, type PayloadInterface, type PendingCacheEntry, RouterEvents as ROUTER_EVENTS, 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 StoreApi, 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, bindStore, compileTemplate, computed, create, createVdomRef, defineStore, defineView, encodeHTML, encodeQ, encodeSafe, encodeURIExtra, ensureElementId, extractGlobalVars, config as frameworkConfig, funcWithTry, generateId, getAttribute, getById, getRouteMode, hasOwnProperty, installFrameVisualizerBridge, invalidateViewClass, isPlainObject, isPrimitive, isPrimitiveOrFunc, keys, mark, markBooted, markRouterBooted, nextCounter, nodeInside, noop, now, parseUri, registerViewClass, resetProjectsMap, safeguard, serializeFrameTree, setData, syncCounter, toMap, toUri, translateData, unmark, use, useUrlState, vdomGetCompareKey, vdomGetNode, vdomSetAttributes, vdomSetChildNodes, vdomSetNode, vdomSpecialDiff, vdomUnmountFrames };