@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.cjs CHANGED
@@ -22,15 +22,16 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  CALL_BREAK_TIME: () => CALL_BREAK_TIME,
24
24
  Cache: () => Cache,
25
+ CrossSite: () => cross_site_default,
25
26
  EVENT_METHOD_REGEXP: () => EVENT_METHOD_REGEXP,
26
27
  EventDelegator: () => EventDelegator,
27
28
  EventEmitter: () => EventEmitter,
28
29
  Frame: () => Frame,
30
+ FrameVisualBridge: () => FrameVisualBridge,
29
31
  Framework: () => Framework,
30
32
  LARK_VIEW: () => LARK_VIEW,
31
33
  Payload: () => Payload,
32
- Platform: () => Platform,
33
- ROUTER_EVENTS: () => ROUTER_EVENTS,
34
+ ROUTER_EVENTS: () => RouterEvents,
34
35
  Router: () => Router,
35
36
  SPLITTER: () => SPLITTER,
36
37
  Service: () => Service,
@@ -43,59 +44,52 @@ __export(index_exports, {
43
44
  applyStyle: () => applyStyle,
44
45
  applyVdomOps: () => applyVdomOps,
45
46
  assign: () => assign,
46
- cell: () => cell,
47
- classExtend: () => classExtend,
48
- cloneData: () => cloneData,
49
- cloneStore: () => cloneStore,
47
+ bindStore: () => bindStore,
50
48
  compileTemplate: () => compileTemplate,
51
- createState: () => createState,
49
+ computed: () => computed,
50
+ create: () => create,
52
51
  createVdomRef: () => createVdomRef,
53
52
  defineStore: () => defineStore,
54
- delStore: () => delStore,
53
+ defineView: () => defineView,
55
54
  encodeHTML: () => encodeHTML,
56
55
  encodeQ: () => encodeQ,
57
56
  encodeSafe: () => encodeSafe,
58
57
  encodeURIExtra: () => encodeURIExtra,
59
58
  ensureElementId: () => ensureElementId,
60
59
  extractGlobalVars: () => extractGlobalVars,
60
+ frameworkConfig: () => config,
61
61
  funcWithTry: () => funcWithTry,
62
62
  generateId: () => generateId,
63
63
  getAttribute: () => getAttribute,
64
64
  getById: () => getById,
65
- getPlatform: () => getPlatform,
66
- getStore: () => getStore,
67
- getUseStore: () => getUseStore,
68
- has: () => has,
65
+ getRouteMode: () => getRouteMode,
66
+ hasOwnProperty: () => hasOwnProperty,
69
67
  installFrameVisualizerBridge: () => installFrameVisualizerBridge,
68
+ invalidateViewClass: () => invalidateViewClass,
70
69
  isPlainObject: () => isPlainObject,
71
70
  isPrimitive: () => isPrimitive,
72
71
  isPrimitiveOrFunc: () => isPrimitiveOrFunc,
73
- isState: () => isState,
74
- isStoreActive: () => isStoreActive,
75
72
  keys: () => keys,
76
- lazySet: () => lazySet,
77
73
  mark: () => mark,
78
74
  markBooted: () => markBooted,
79
75
  markRouterBooted: () => markRouterBooted,
80
- multi: () => multi,
81
76
  nextCounter: () => nextCounter,
82
77
  nodeInside: () => nodeInside,
83
78
  noop: () => noop,
84
79
  now: () => now,
85
- observeCell: () => observeCell,
86
80
  parseUri: () => parseUri,
87
81
  registerViewClass: () => registerViewClass,
82
+ resetProjectsMap: () => resetProjectsMap,
88
83
  safeguard: () => safeguard,
89
84
  serializeFrameTree: () => serializeFrameTree,
90
85
  setData: () => setData,
91
- shallowSet: () => shallowSet,
92
- storeMark: () => mark2,
93
- storeUnmark: () => unmark2,
94
86
  syncCounter: () => syncCounter,
95
87
  toMap: () => toMap,
96
88
  toUri: () => toUri,
97
89
  translateData: () => translateData,
98
90
  unmark: () => unmark,
91
+ use: () => use,
92
+ useUrlState: () => useUrlState,
99
93
  vdomGetCompareKey: () => vdomGetCompareKey,
100
94
  vdomGetNode: () => vdomGetNode,
101
95
  vdomSetAttributes: () => vdomSetAttributes,
@@ -108,13 +102,13 @@ module.exports = __toCommonJS(index_exports);
108
102
 
109
103
  // src/constants.ts
110
104
  var globalCounter = 0;
111
- var SPLITTER = "";
112
- var ROUTER_EVENTS = {
105
+ var SPLITTER = String.fromCharCode(30);
106
+ var RouterEvents = {
113
107
  CHANGE: "change",
114
108
  CHANGED: "changed",
115
109
  PAGE_UNLOAD: "page_unload"
116
110
  };
117
- var LARK_KEYS = {
111
+ var LarkInnerKeys = {
118
112
  /** Attribute name: ldk (static key for skipping VDOM diff) */
119
113
  DIFF_KEY: "ldk",
120
114
  /** Attribute name: lak (static attribute key) */
@@ -161,13 +155,13 @@ function syncCounter(val) {
161
155
  }
162
156
  function noop() {
163
157
  }
164
- function has(owner, prop) {
158
+ function hasOwnProperty(owner, prop) {
165
159
  return owner != null && Object.prototype.hasOwnProperty.call(owner, prop);
166
160
  }
167
161
  function keys(obj) {
168
162
  const result = [];
169
163
  for (const p in obj) {
170
- if (has(obj, p)) {
164
+ if (hasOwnProperty(obj, p)) {
171
165
  result.push(p);
172
166
  }
173
167
  }
@@ -177,7 +171,7 @@ function assign(target, ...sources) {
177
171
  for (const source of sources) {
178
172
  if (source) {
179
173
  for (const p in source) {
180
- if (has(source, p)) {
174
+ if (hasOwnProperty(source, p)) {
181
175
  target[p] = source[p];
182
176
  }
183
177
  }
@@ -190,21 +184,22 @@ function funcWithTry(fns, args, context, configError) {
190
184
  let ret;
191
185
  for (const fn of fnArray) {
192
186
  try {
193
- ret = Function.prototype.apply.call(fn, context, args);
187
+ ret = fn.apply(context, args);
194
188
  } catch (e) {
195
189
  configError?.(e);
196
190
  }
197
191
  }
198
192
  return ret;
199
193
  }
194
+ var EMPTY_STRING_SET = /* @__PURE__ */ new Set();
200
195
  function setData(newData, oldData, changedKeys2, excludes) {
201
196
  let changed = false;
202
197
  for (const p in newData) {
203
- if (has(newData, p)) {
198
+ if (hasOwnProperty(newData, p)) {
204
199
  const now2 = newData[p];
205
200
  const old = oldData[p];
206
201
  if ((!isPrimitiveOrFunc(now2) || old !== now2) && !excludes.has(p)) {
207
- changedKeys2[p] = 1;
202
+ changedKeys2.add(p);
208
203
  changed = true;
209
204
  }
210
205
  oldData[p] = now2;
@@ -212,17 +207,25 @@ function setData(newData, oldData, changedKeys2, excludes) {
212
207
  }
213
208
  return changed;
214
209
  }
210
+ function isRefToken(s) {
211
+ if (s.length < 2 || s[0] !== SPLITTER) return false;
212
+ for (let i = 1; i < s.length; i++) {
213
+ const c = s.charCodeAt(i);
214
+ if (c < 48 || c > 57) return false;
215
+ }
216
+ return true;
217
+ }
215
218
  function translateData(data, value) {
216
219
  if (isPrimitive(value)) {
217
220
  const prop = String(value);
218
- if (prop[0] === SPLITTER && has(data, prop)) {
221
+ if (isRefToken(prop) && hasOwnProperty(data, prop)) {
219
222
  return data[prop];
220
223
  }
221
224
  return value;
222
225
  }
223
226
  if (isPlainObject(value) || Array.isArray(value)) {
224
227
  for (const p in value) {
225
- if (has(value, p)) {
228
+ if (hasOwnProperty(value, p)) {
226
229
  const val = value[p];
227
230
  const newVal = translateData(data, val);
228
231
  value[p] = newVal;
@@ -259,38 +262,33 @@ function nodeInside(a, b) {
259
262
  return false;
260
263
  }
261
264
  }
262
- var paramsTemp = {};
263
- function paramsReplacer(_match, name, value) {
264
- try {
265
- paramsTemp[name] = decodeURIComponent(value || "");
266
- } catch {
267
- paramsTemp[name] = value || "";
268
- }
269
- return "";
270
- }
271
265
  function parseUri(uri) {
272
- paramsTemp = {};
266
+ const params = {};
273
267
  const path = uri.replace(URL_QUERY_HASH_REGEXP, "");
274
268
  const pathname = path;
275
269
  const actualPath = uri === pathname && IS_URL_PARAMS.test(pathname) ? "" : pathname;
276
- uri.replace(actualPath, "").replace(URL_PARAM_REGEXP, paramsReplacer);
277
- return {
278
- path: actualPath,
279
- params: { ...paramsTemp }
280
- };
270
+ uri.replace(actualPath, "").replace(URL_PARAM_REGEXP, (_match, name, value) => {
271
+ try {
272
+ params[name] = decodeURIComponent(value || "");
273
+ } catch {
274
+ params[name] = value || "";
275
+ }
276
+ return "";
277
+ });
278
+ return { path: actualPath, params };
281
279
  }
282
280
  var IS_URL_PARAMS = {
283
281
  test(s) {
284
282
  return /(?!^)=|&/.test(s);
285
283
  }
286
284
  };
287
- function toUri(path, params, keepEmptyObject) {
285
+ function toUri(path, params, keepEmpty) {
288
286
  const pairs = [];
289
287
  let hasParams = false;
290
288
  for (const p in params) {
291
- if (has(params, p)) {
289
+ if (hasOwnProperty(params, p)) {
292
290
  const v = String(params[p] ?? "");
293
- if (!keepEmptyObject || v || has(keepEmptyObject, p)) {
291
+ if (!keepEmpty || v || keepEmpty.has(p)) {
294
292
  pairs.push(`${p}=${encodeURIComponent(v)}`);
295
293
  hasParams = true;
296
294
  }
@@ -313,14 +311,6 @@ function toMap(list, key) {
313
311
  function now() {
314
312
  return Date.now ? Date.now() : (/* @__PURE__ */ new Date()).getTime();
315
313
  }
316
- function classExtend(make, base, props, statics) {
317
- const baseProto = base["prototype"] ?? {};
318
- const classProto = Object.create(baseProto);
319
- assign(classProto, props);
320
- Object.assign(make, statics);
321
- classProto.constructor = make;
322
- make["prototype"] = classProto;
323
- }
324
314
 
325
315
  // src/apply-style.ts
326
316
  var injectedStyleIds = /* @__PURE__ */ new Set();
@@ -360,27 +350,35 @@ function applyStyle(styleIdOrPairs, css) {
360
350
  }
361
351
 
362
352
  // src/mark.ts
363
- var DELETED_KEY = SPLITTER + "$delFlag";
364
- var MARK_OBJECT_KEY = SPLITTER + "$markKey";
353
+ var hostStore = /* @__PURE__ */ new WeakMap();
354
+ function getOrCreate(host) {
355
+ let record = hostStore.get(host);
356
+ if (!record) {
357
+ record = { signs: /* @__PURE__ */ new Map(), deleted: false };
358
+ hostStore.set(host, record);
359
+ }
360
+ return record;
361
+ }
365
362
  function mark(host, key) {
366
- let sign = 0;
367
- const hostRecord = host;
368
- if (!hostRecord[DELETED_KEY]) {
369
- const markHost = hostRecord[MARK_OBJECT_KEY] || (hostRecord[MARK_OBJECT_KEY] = {});
370
- if (!Object.prototype.hasOwnProperty.call(markHost, key)) {
371
- markHost[key] = 0;
372
- }
373
- sign = ++markHost[key];
363
+ const record = getOrCreate(host);
364
+ if (record.deleted) {
365
+ return () => false;
374
366
  }
367
+ const sign = (record.signs.get(key) ?? 0) + 1;
368
+ record.signs.set(key, sign);
375
369
  return () => {
376
- const temp = hostRecord[MARK_OBJECT_KEY];
377
- return !!(temp && sign === temp[key]);
370
+ const current = hostStore.get(host);
371
+ return !!current && !current.deleted && current.signs.get(key) === sign;
378
372
  };
379
373
  }
380
374
  function unmark(host) {
381
- const hostRecord = host;
382
- hostRecord[MARK_OBJECT_KEY] = 0;
383
- hostRecord[DELETED_KEY] = 1;
375
+ const record = hostStore.get(host);
376
+ if (record) {
377
+ record.deleted = true;
378
+ record.signs.clear();
379
+ } else {
380
+ hostStore.set(host, { signs: /* @__PURE__ */ new Map(), deleted: true });
381
+ }
384
382
  }
385
383
 
386
384
  // src/safeguard.ts
@@ -409,7 +407,7 @@ function safeguard(data, getter, setter, isRoot) {
409
407
  set(target, property, value) {
410
408
  if (!setter && !prefix) {
411
409
  throw new Error(
412
- "Avoid write back, key: " + prefix + property + " value:" + value + " more: https://github.com/hangtiancheng/h"
410
+ "Avoid write back, key: " + prefix + property + " value:" + value + " more: https://github.com/hangtiancheng/lark"
413
411
  );
414
412
  }
415
413
  Reflect.set(target, property, value);
@@ -426,7 +424,7 @@ function safeguard(data, getter, setter, isRoot) {
426
424
  if (!prefix && getter) {
427
425
  getter(property);
428
426
  }
429
- if (!isRoot && has(target, property) && (Array.isArray(out) || isPlainObject(out))) {
427
+ if (!isRoot && hasOwnProperty(target, property) && (Array.isArray(out) || isPlainObject(out))) {
430
428
  return build(prefix + property + ".", out);
431
429
  }
432
430
  return out;
@@ -515,15 +513,17 @@ var Cache = class {
515
513
  this.lookup.set(prefixedKey, entry);
516
514
  }
517
515
  /**
518
- * Delete a cached entry.
516
+ * Delete a cached entry. Removes it immediately from both the lookup map
517
+ * and the entries array so the GC can reclaim the value without waiting
518
+ * for the next eviction sweep.
519
519
  */
520
520
  del(key) {
521
521
  const prefixedKey = this.prefixKey(key);
522
522
  const entry = this.lookup.get(prefixedKey);
523
523
  if (!entry) return;
524
- entry.frequency = -1;
525
- entry.value = void 0;
526
524
  this.lookup.delete(prefixedKey);
525
+ const idx = this.entries.indexOf(entry);
526
+ if (idx !== -1) this.entries.splice(idx, 1);
527
527
  if (this.onRemove) {
528
528
  this.onRemove(key);
529
529
  }
@@ -548,20 +548,46 @@ var Cache = class {
548
548
  this.entries = [];
549
549
  this.lookup.clear();
550
550
  }
551
- /** Evict least-used entries from cache */
551
+ /**
552
+ * Evict the `bufferSize` worst entries from the cache.
553
+ *
554
+ * Uses single-pass partial selection (O(n·k)) instead of sorting the entire
555
+ * `entries` array (O(n log n)). For the typical `bufferSize = 5` this is
556
+ * effectively a linear scan with at most 5 in-bucket comparisons per
557
+ * iteration — and it avoids mutating the rest of `entries`.
558
+ */
552
559
  evictEntries() {
553
- this.entries.sort(this.comparator);
554
- let count = this.bufferSize;
555
- while (count-- > 0 && this.entries.length > 0) {
556
- const entry = this.entries.pop();
557
- if (entry && entry.frequency > 0) {
558
- this.lookup.delete(this.prefixKey(entry.originalKey));
559
- if (this.onRemove) {
560
- this.onRemove(entry.originalKey);
561
- }
560
+ const entries = this.entries;
561
+ const k = this.bufferSize;
562
+ if (k <= 0 || entries.length === 0) return;
563
+ if (entries.length <= k) {
564
+ for (const e of entries) {
565
+ this.lookup.delete(this.prefixKey(e.originalKey));
566
+ if (this.onRemove) this.onRemove(e.originalKey);
562
567
  }
568
+ this.entries = [];
569
+ return;
563
570
  }
564
- this.entries = this.entries.filter((e) => e.frequency !== -1);
571
+ const cmp = this.comparator;
572
+ const worst = [];
573
+ for (const entry of entries) {
574
+ if (worst.length < k) {
575
+ let i = worst.length;
576
+ while (i > 0 && cmp(entry, worst[i - 1]) > 0) i--;
577
+ worst.splice(i, 0, entry);
578
+ } else if (cmp(entry, worst[k - 1]) > 0) {
579
+ worst.pop();
580
+ let i = worst.length;
581
+ while (i > 0 && cmp(entry, worst[i - 1]) > 0) i--;
582
+ worst.splice(i, 0, entry);
583
+ }
584
+ }
585
+ const evictSet = new Set(worst);
586
+ for (const e of worst) {
587
+ this.lookup.delete(this.prefixKey(e.originalKey));
588
+ if (this.onRemove) this.onRemove(e.originalKey);
589
+ }
590
+ this.entries = entries.filter((e) => !evictSet.has(e));
565
591
  }
566
592
  };
567
593
 
@@ -569,6 +595,10 @@ var Cache = class {
569
595
  var EventEmitter = class {
570
596
  /** Event listeners: prefixed key -> listener array */
571
597
  listeners = /* @__PURE__ */ new Map();
598
+ /** Number of `fire()` calls currently on the stack (re-entrancy depth). */
599
+ firingDepth = 0;
600
+ /** Keys whose listener list needs compaction after firing settles. */
601
+ pendingCompaction;
572
602
  /**
573
603
  * Bind event listener.
574
604
  */
@@ -591,13 +621,23 @@ var EventEmitter = class {
591
621
  const key = SPLITTER + event;
592
622
  if (handler) {
593
623
  const list = this.listeners.get(key);
594
- if (list) {
624
+ if (!list) return this;
625
+ if (this.firingDepth > 0) {
595
626
  for (const listener of list) {
596
627
  if (listener.handler === handler) {
597
628
  listener.handler = noop;
629
+ (this.pendingCompaction ??= /* @__PURE__ */ new Set()).add(key);
598
630
  break;
599
631
  }
600
632
  }
633
+ } else {
634
+ for (let i = 0; i < list.length; i++) {
635
+ if (list[i].handler === handler) {
636
+ list.splice(i, 1);
637
+ break;
638
+ }
639
+ }
640
+ if (list.length === 0) this.listeners.delete(key);
601
641
  }
602
642
  } else {
603
643
  this.listeners.delete(key);
@@ -609,9 +649,9 @@ var EventEmitter = class {
609
649
  return this;
610
650
  }
611
651
  /**
612
- * Fire event, execute all bound handlers.
613
- * Supports executing state management: handlers removed during
614
- * execution are safely handled.
652
+ * Fire event, execute all bound handlers. Safe for re-entrant `off()` calls
653
+ * during dispatch: removed handlers are replaced with noop and compacted
654
+ * after the outermost fire returns.
615
655
  *
616
656
  * @param event - Event name
617
657
  * @param data - Event data (type property added automatically)
@@ -625,38 +665,41 @@ var EventEmitter = class {
625
665
  data = {};
626
666
  }
627
667
  data["type"] = event;
628
- if (list) {
629
- let end = list.length;
630
- const len = end - 1;
631
- while (end--) {
632
- const idx = lastToFirst ? end : len - end;
633
- const listener = list[idx];
634
- if (listener.handler !== noop) {
668
+ this.firingDepth++;
669
+ try {
670
+ if (list) {
671
+ const len = list.length;
672
+ for (let i = 0; i < len; i++) {
673
+ const idx = lastToFirst ? len - 1 - i : i;
674
+ const listener = list[idx];
675
+ if (!listener) continue;
676
+ if (listener.handler === noop) continue;
635
677
  listener.executing = 1;
636
- funcWithTry(
637
- [listener.handler],
638
- [data],
639
- this,
640
- noop
641
- );
678
+ funcWithTry([listener.handler], [data], this, noop);
642
679
  listener.executing = "";
643
- } else if (!listener.executing) {
644
- list.splice(idx, 1);
645
680
  }
646
681
  }
647
- }
648
- const onMethodName = `on${event[0].toUpperCase() + event.slice(1)}`;
649
- const onMethod = this[onMethodName];
650
- if (typeof onMethod === "function") {
651
- funcWithTry(
652
- [onMethod],
653
- [data],
654
- this,
655
- noop
656
- );
657
- }
658
- if (remove) {
659
- this.off(event);
682
+ const onMethodName = `on${event[0].toUpperCase() + event.slice(1)}`;
683
+ const onMethod = this[onMethodName];
684
+ if (typeof onMethod === "function") {
685
+ funcWithTry([onMethod], [data], this, noop);
686
+ }
687
+ if (remove) {
688
+ this.off(event);
689
+ }
690
+ } finally {
691
+ this.firingDepth--;
692
+ if (this.firingDepth === 0 && this.pendingCompaction) {
693
+ for (const k of this.pendingCompaction) {
694
+ const l = this.listeners.get(k);
695
+ if (!l) continue;
696
+ for (let i = l.length - 1; i >= 0; i--) {
697
+ if (l[i].handler === noop) l.splice(i, 1);
698
+ }
699
+ if (l.length === 0) this.listeners.delete(k);
700
+ }
701
+ this.pendingCompaction = void 0;
702
+ }
660
703
  }
661
704
  return this;
662
705
  }
@@ -665,8 +708,8 @@ var EventEmitter = class {
665
708
  // src/state.ts
666
709
  var appData = {};
667
710
  var keyRefCounts = {};
668
- var changedKeys = {};
669
- var stashedChangedKeys = {};
711
+ var changedKeys = /* @__PURE__ */ new Set();
712
+ var stashedChangedKeys = EMPTY_STRING_SET;
670
713
  var dataIsChanged = false;
671
714
  var dataWhereSet = {};
672
715
  var emitter = new EventEmitter();
@@ -677,7 +720,7 @@ function markBooted() {
677
720
  function setupKeysRef(keys2) {
678
721
  const keyList = keys2.split(",");
679
722
  for (const key of keyList) {
680
- if (has(keyRefCounts, key)) {
723
+ if (hasOwnProperty(keyRefCounts, key)) {
681
724
  keyRefCounts[key]++;
682
725
  } else {
683
726
  keyRefCounts[key] = 1;
@@ -687,7 +730,7 @@ function setupKeysRef(keys2) {
687
730
  }
688
731
  function teardownKeysRef(keyList) {
689
732
  for (const key of keyList) {
690
- if (has(keyRefCounts, key)) {
733
+ if (hasOwnProperty(keyRefCounts, key)) {
691
734
  const count = --keyRefCounts[key];
692
735
  if (count <= 0) {
693
736
  Reflect.deleteProperty(keyRefCounts, key);
@@ -699,35 +742,14 @@ function teardownKeysRef(keyList) {
699
742
  }
700
743
  }
701
744
  }
702
- var notifyStarted = 0;
703
- var notifyList = [];
704
- var notifyTimer;
745
+ var warnedKeys = /* @__PURE__ */ new Set();
705
746
  function clearNotify(key) {
706
- for (let i = notifyList.length; i--; ) {
707
- if (notifyList[i]?.key === key) {
708
- notifyList.splice(i, 1);
709
- }
710
- }
711
- }
712
- function doNotify() {
713
- const locker = {};
714
- for (const n of notifyList) {
715
- if (!locker[n.key]) {
716
- console.warn(n.message);
717
- locker[n.key] = 1;
718
- }
719
- }
720
- notifyList.length = 0;
721
- notifyStarted = 0;
747
+ warnedKeys.delete(key);
722
748
  }
723
749
  function delayNotify(key, message) {
724
- clearTimeout(notifyTimer);
725
- notifyStarted = 0;
726
- notifyList.push({ key, message });
727
- if (!notifyStarted) {
728
- notifyStarted = 1;
729
- notifyTimer = setTimeout(doNotify, 500);
730
- }
750
+ if (warnedKeys.has(key)) return;
751
+ warnedKeys.add(key);
752
+ console.warn(message);
731
753
  }
732
754
  var State = {
733
755
  /**
@@ -739,7 +761,7 @@ var State = {
739
761
  return safeguard(
740
762
  result,
741
763
  (dataKey) => {
742
- if (booted && has(dataWhereSet, dataKey) && dataWhereSet[dataKey] !== window.location.pathname) {
764
+ if (booted && hasOwnProperty(dataWhereSet, dataKey) && dataWhereSet[dataKey] !== window.location.pathname) {
743
765
  console.warn(
744
766
  `beware! You get state:"{State}.${dataKey}" where it set by page:${dataWhereSet[dataKey]}`
745
767
  );
@@ -760,7 +782,7 @@ var State = {
760
782
  * Set data to state.
761
783
  */
762
784
  set(data, excludes) {
763
- dataIsChanged = setData(data, appData, changedKeys, excludes || /* @__PURE__ */ new Set()) || dataIsChanged;
785
+ dataIsChanged = setData(data, appData, changedKeys, excludes || EMPTY_STRING_SET) || dataIsChanged;
764
786
  if (typeof window.__lark_Debug !== "undefined" && window.__lark_Debug && booted) {
765
787
  for (const p in data) {
766
788
  dataWhereSet[p] = window.location.pathname;
@@ -777,26 +799,19 @@ var State = {
777
799
  }
778
800
  if (dataIsChanged) {
779
801
  if (typeof window.__lark_Debug !== "undefined" && window.__lark_Debug) {
780
- for (const p in changedKeys) {
781
- if (has(changedKeys, p)) {
782
- clearNotify(p);
783
- }
802
+ for (const p of changedKeys) {
803
+ clearNotify(p);
784
804
  }
785
805
  }
786
806
  dataIsChanged = false;
787
- const keys2 = {};
788
- for (const k in changedKeys) {
789
- if (has(changedKeys, k)) {
790
- keys2[k] = 1;
791
- }
792
- }
807
+ const keys2 = changedKeys;
793
808
  stashedChangedKeys = keys2;
794
- changedKeys = {};
795
- emitter.fire(ROUTER_EVENTS.CHANGED, { keys: keys2 });
809
+ changedKeys = /* @__PURE__ */ new Set();
810
+ emitter.fire(RouterEvents.CHANGED, { keys: keys2 });
796
811
  }
797
812
  },
798
813
  /**
799
- * Get diff of what changed in last digest.
814
+ * Get the set of keys changed in the most recent digest.
800
815
  */
801
816
  diff() {
802
817
  return stashedChangedKeys;
@@ -854,6 +869,8 @@ var cachedDefaultPath;
854
869
  var cachedRewrite;
855
870
  var defaultTitle;
856
871
  var frameworkConfig;
872
+ var routeMode = "history";
873
+ var beforeEachGuards = [];
857
874
  function createEmptyLocation() {
858
875
  return {
859
876
  href: "",
@@ -878,7 +895,8 @@ function attachViewAndPath(loc) {
878
895
  cachedRewrite = frameworkConfig.rewrite;
879
896
  }
880
897
  if (!loc.view) {
881
- let path = loc.hash["path"] || cachedDefaultPath || "/";
898
+ const rawPath = routeMode === "history" ? loc.query["path"] || loc.hash["path"] : loc.hash["path"];
899
+ let path = rawPath || cachedDefaultPath || "/";
882
900
  if (cachedRewrite) {
883
901
  path = cachedRewrite(
884
902
  path,
@@ -944,7 +962,16 @@ function getChanged(oldLoc, newLoc) {
944
962
  changedCache.set(tKey, finalResult);
945
963
  return finalResult;
946
964
  }
947
- function updateHash(path, replace) {
965
+ function updateBrowserUrl(path, replace) {
966
+ if (routeMode === "history") {
967
+ const url = path || "/";
968
+ if (replace) {
969
+ window.history.replaceState(null, "", url);
970
+ } else {
971
+ window.history.pushState(null, "", url);
972
+ }
973
+ return;
974
+ }
948
975
  const hashbang = frameworkConfig?.hashbang || "#!";
949
976
  const fullPath = path === "" ? "" : hashbang + path;
950
977
  if (replace) {
@@ -955,9 +982,10 @@ function updateHash(path, replace) {
955
982
  }
956
983
  function updateUrl(path, params, loc, replace, silentFlag, lQuery) {
957
984
  path = toUri(path, params, lQuery);
958
- if (path !== loc.srcHash) {
985
+ const currentSrc = routeMode === "history" ? loc.srcQuery : loc.srcHash;
986
+ if (path !== currentSrc) {
959
987
  silent = silentFlag ? 1 : 0;
960
- updateHash(path, replace);
988
+ updateBrowserUrl(path, replace);
961
989
  }
962
990
  }
963
991
  var Router = {
@@ -971,10 +999,29 @@ var Router = {
971
999
  if (cached) {
972
1000
  return cached;
973
1001
  }
974
- const srcQuery = href.replace(URL_TRIM_HASH_REGEXP, "");
975
- const srcHash = href.replace(URL_TRIM_QUERY_REGEXP, "");
976
- const query = parseUri(srcQuery);
977
- const hash = parseUri(srcHash);
1002
+ let srcQuery;
1003
+ let srcHash;
1004
+ let query;
1005
+ let hash;
1006
+ if (routeMode === "history") {
1007
+ try {
1008
+ const urlObj = new URL(href, window.location.origin);
1009
+ srcQuery = urlObj.pathname + urlObj.search;
1010
+ srcHash = urlObj.hash ? urlObj.hash.replace(/^#!?/, "") : "";
1011
+ query = parseUri(srcQuery);
1012
+ hash = srcHash ? parseUri(srcHash) : { path: "", params: {} };
1013
+ } catch {
1014
+ srcQuery = href.replace(URL_TRIM_HASH_REGEXP, "");
1015
+ srcHash = href.replace(URL_TRIM_QUERY_REGEXP, "");
1016
+ query = parseUri(srcQuery);
1017
+ hash = parseUri(srcHash);
1018
+ }
1019
+ } else {
1020
+ srcQuery = href.replace(URL_TRIM_HASH_REGEXP, "");
1021
+ srcHash = href.replace(URL_TRIM_QUERY_REGEXP, "");
1022
+ query = parseUri(srcQuery);
1023
+ hash = parseUri(srcHash);
1024
+ }
978
1025
  const params = assign({}, query["params"], hash["params"]);
979
1026
  const location = {
980
1027
  href,
@@ -1008,7 +1055,7 @@ var Router = {
1008
1055
  document.title = defaultTitle || document.title;
1009
1056
  }
1010
1057
  emitter2.fire(
1011
- ROUTER_EVENTS.CHANGED,
1058
+ RouterEvents.CHANGED,
1012
1059
  lastChanged
1013
1060
  );
1014
1061
  }
@@ -1041,16 +1088,16 @@ var Router = {
1041
1088
  }
1042
1089
  const lPath = lastLocation["path"] || "";
1043
1090
  const lParams = lastLocation["params"];
1044
- const lQuery = {};
1091
+ const lQuery = /* @__PURE__ */ new Set();
1045
1092
  for (const k in lastLocation.query["params"]) {
1046
- if (has(lastLocation.query["params"], k)) {
1047
- lQuery[k] = 1;
1093
+ if (hasOwnProperty(lastLocation.query["params"], k)) {
1094
+ lQuery.add(k);
1048
1095
  }
1049
1096
  }
1050
1097
  if (tPath) {
1051
- if (!has(window, "history")) {
1052
- for (const qKey in lQuery) {
1053
- if (has(lQuery, qKey) && !has(tParams, qKey)) {
1098
+ if (routeMode === "hash" && !hasOwnProperty(window, "history")) {
1099
+ for (const qKey of lQuery) {
1100
+ if (!hasOwnProperty(tParams, qKey)) {
1054
1101
  tParams[qKey] = "";
1055
1102
  }
1056
1103
  }
@@ -1059,14 +1106,17 @@ var Router = {
1059
1106
  tPath = lPath;
1060
1107
  tParams = assign({}, lParams, tParams);
1061
1108
  }
1062
- updateUrl(
1063
- tPath,
1064
- tParams,
1065
- lastLocation,
1066
- replace,
1067
- silentFlag,
1068
- lQuery
1069
- );
1109
+ updateUrl(tPath, tParams, lastLocation, replace, silentFlag, lQuery);
1110
+ },
1111
+ /**
1112
+ * Register an async-friendly navigation guard. See `RouterInterface.beforeEach`.
1113
+ */
1114
+ beforeEach(guard) {
1115
+ beforeEachGuards.push(guard);
1116
+ return () => {
1117
+ const idx = beforeEachGuards.indexOf(guard);
1118
+ if (idx !== -1) beforeEachGuards.splice(idx, 1);
1119
+ };
1070
1120
  },
1071
1121
  /**
1072
1122
  * Join multiple path segments into a single path.
@@ -1097,32 +1147,41 @@ var Router = {
1097
1147
  return Router;
1098
1148
  },
1099
1149
  /**
1100
- * Internal: bind hashchange and beforeunload events.
1150
+ * Internal: bind routing events and beforeunload.
1101
1151
  * Called by Framework.boot().
1152
+ * In hash mode, listens to hashchange + popstate.
1153
+ * In history mode, listens to popstate only.
1102
1154
  */
1103
1155
  _bind() {
1104
1156
  defaultTitle = document.title;
1105
- let lastHash = Router.parse().srcHash;
1157
+ const getLocationKey = () => {
1158
+ if (routeMode === "history") {
1159
+ return window.location.pathname + window.location.search;
1160
+ }
1161
+ return Router.parse().srcHash;
1162
+ };
1163
+ let lastKey = getLocationKey();
1106
1164
  let suspend;
1107
1165
  const watchChange = () => {
1108
1166
  if (suspend) {
1109
1167
  return;
1110
1168
  }
1169
+ hrefCache.clear();
1111
1170
  const loc = Router.parse();
1112
- const newHash = loc.srcHash;
1113
- if (newHash !== lastHash) {
1171
+ const newKey = routeMode === "history" ? loc.srcQuery : loc.srcHash;
1172
+ if (newKey !== lastKey) {
1114
1173
  const changeEvent = {
1115
1174
  p: 0,
1116
1175
  reject: () => {
1117
1176
  changeEvent.p = 1;
1118
1177
  suspend = "";
1119
- updateHash(lastHash);
1178
+ updateBrowserUrl(lastKey);
1120
1179
  },
1121
1180
  resolve: () => {
1122
1181
  changeEvent.p = 1;
1123
- lastHash = newHash;
1182
+ lastKey = newKey;
1124
1183
  suspend = "";
1125
- updateHash(newHash);
1184
+ updateBrowserUrl(newKey);
1126
1185
  Router.diff();
1127
1186
  },
1128
1187
  prevent: () => {
@@ -1130,20 +1189,51 @@ var Router = {
1130
1189
  }
1131
1190
  };
1132
1191
  Router.fire(
1133
- ROUTER_EVENTS.CHANGE,
1192
+ RouterEvents.CHANGE,
1134
1193
  changeEvent
1135
1194
  );
1136
- if (!suspend && !changeEvent.p) {
1195
+ if (suspend || changeEvent.p) {
1196
+ return;
1197
+ }
1198
+ if (beforeEachGuards.length === 0) {
1137
1199
  changeEvent.resolve();
1200
+ return;
1201
+ }
1202
+ const from = lastLocation;
1203
+ const to = loc;
1204
+ const guards = beforeEachGuards.slice();
1205
+ let chain = Promise.resolve(true);
1206
+ for (const guard of guards) {
1207
+ chain = chain.then((prev) => {
1208
+ if (prev === false) return false;
1209
+ return guard(to, from);
1210
+ });
1138
1211
  }
1212
+ chain.then(
1213
+ (result) => {
1214
+ if (changeEvent.p) return;
1215
+ if (result === false) {
1216
+ changeEvent.reject();
1217
+ } else {
1218
+ changeEvent.resolve();
1219
+ }
1220
+ },
1221
+ () => {
1222
+ if (!changeEvent.p) changeEvent.reject();
1223
+ }
1224
+ );
1139
1225
  }
1140
1226
  };
1141
1227
  Router.notify = watchChange;
1142
- window.addEventListener("hashchange", watchChange);
1143
- window.addEventListener("popstate", watchChange);
1228
+ if (routeMode === "history") {
1229
+ window.addEventListener("popstate", watchChange);
1230
+ } else {
1231
+ window.addEventListener("hashchange", watchChange);
1232
+ window.addEventListener("popstate", watchChange);
1233
+ }
1144
1234
  window.addEventListener("beforeunload", (domEvent) => {
1145
1235
  const data = {};
1146
- Router.fire(ROUTER_EVENTS.PAGE_UNLOAD, data);
1236
+ Router.fire(RouterEvents.PAGE_UNLOAD, data);
1147
1237
  const msg = data["msg"];
1148
1238
  if (msg) {
1149
1239
  domEvent.returnValue = msg;
@@ -1156,11 +1246,15 @@ var Router = {
1156
1246
  */
1157
1247
  _setConfig(cfg) {
1158
1248
  frameworkConfig = cfg;
1249
+ routeMode = cfg.routeMode || "history";
1159
1250
  }
1160
1251
  };
1161
1252
  function markRouterBooted() {
1162
1253
  booted2 = true;
1163
1254
  }
1255
+ function getRouteMode() {
1256
+ return routeMode;
1257
+ }
1164
1258
 
1165
1259
  // src/event-delegator.ts
1166
1260
  var rootEvents = {};
@@ -1189,13 +1283,17 @@ function parseEventInfo(eventInfo) {
1189
1283
  }
1190
1284
  function findFrameInfo(current, eventType) {
1191
1285
  const eventInfos = [];
1192
- let begin = current;
1193
1286
  const info = current.getAttribute(`@${eventType}`);
1287
+ const hasSelectorEvents = !!selectorEvents[eventType];
1288
+ if (!info && !hasSelectorEvents) {
1289
+ return eventInfos;
1290
+ }
1291
+ let begin = current;
1194
1292
  let match;
1195
1293
  if (info) {
1196
1294
  match = parseEventInfo(info);
1197
1295
  }
1198
- if (match && !match.id || selectorEvents[eventType]) {
1296
+ if (match && !match.id || hasSelectorEvents) {
1199
1297
  let selectorFrameId = "#";
1200
1298
  let backtrace = 0;
1201
1299
  while (begin && begin !== document.body) {
@@ -1368,7 +1466,7 @@ var EventDelegator = {
1368
1466
  };
1369
1467
 
1370
1468
  // src/vdom.ts
1371
- var WrapMeta = {
1469
+ var wrapMeta = {
1372
1470
  option: [1, "<select multiple>"],
1373
1471
  thead: [1, "<table>"],
1374
1472
  col: [2, "<table><colgroup>"],
@@ -1380,9 +1478,9 @@ var WrapMeta = {
1380
1478
  math: [1, '<math xmlns="' + MATH_NS + '">'],
1381
1479
  _: [0, ""]
1382
1480
  };
1383
- WrapMeta["optgroup"] = WrapMeta["option"];
1384
- WrapMeta["tbody"] = WrapMeta["tfoot"] = WrapMeta["colgroup"] = WrapMeta["caption"] = WrapMeta["thead"];
1385
- WrapMeta["th"] = WrapMeta["td"];
1481
+ wrapMeta["optgroup"] = wrapMeta["option"];
1482
+ wrapMeta["tbody"] = wrapMeta["tfoot"] = wrapMeta["colgroup"] = wrapMeta["caption"] = wrapMeta["thead"];
1483
+ wrapMeta["th"] = wrapMeta["td"];
1386
1484
  var VDoc = document.implementation.createHTMLDocument("");
1387
1485
  var VBase = VDoc.createElement("base");
1388
1486
  VBase.href = document.location.href;
@@ -1393,16 +1491,12 @@ var VDomSpecials = {
1393
1491
  OPTION: ["selected"]
1394
1492
  };
1395
1493
  function vdomUnmountFrames(frame, node) {
1396
- if (node.nodeType === 1) {
1397
- const el = node;
1398
- const id = el.getAttribute("id");
1399
- if (id) {
1400
- frame.unmountZone(id);
1401
- const children = frame.children();
1402
- if (children.includes(id)) {
1403
- frame.unmountFrame(id);
1404
- }
1405
- }
1494
+ if (!(node instanceof Element)) return;
1495
+ const id = node.getAttribute("id");
1496
+ if (!id) return;
1497
+ frame.unmountZone(id);
1498
+ if (frame.children().includes(id)) {
1499
+ frame.unmountFrame(id);
1406
1500
  }
1407
1501
  }
1408
1502
  function vdomGetNode(html, refNode) {
@@ -1417,7 +1511,7 @@ function vdomGetNode(html, refNode) {
1417
1511
  const match = TAG_NAME_REGEXP.exec(html);
1418
1512
  tag = match ? match[1] : "";
1419
1513
  }
1420
- const wrap = WrapMeta[tag] || WrapMeta["_"];
1514
+ const wrap = wrapMeta[tag] || wrapMeta["_"];
1421
1515
  tmp.innerHTML = wrap[1] + html;
1422
1516
  let j = wrap[0];
1423
1517
  while (j--) {
@@ -1434,7 +1528,7 @@ function vdomGetCompareKey(node) {
1434
1528
  }
1435
1529
  let key = el.autoId ? "" : el.getAttribute("id") || void 0;
1436
1530
  if (!key) {
1437
- key = el.getAttribute(LARK_KEYS.DIFF_KEY) || void 0;
1531
+ key = el.getAttribute(LarkInnerKeys.DIFF_KEY) || void 0;
1438
1532
  }
1439
1533
  if (!key) {
1440
1534
  const larkView = el.getAttribute(LARK_VIEW);
@@ -1447,8 +1541,7 @@ function vdomGetCompareKey(node) {
1447
1541
  return key;
1448
1542
  }
1449
1543
  function vdomSpecialDiff(oldNode, newNode) {
1450
- const nodeName = oldNode.nodeName;
1451
- const specials = VDomSpecials[nodeName];
1544
+ const specials = VDomSpecials[oldNode.nodeName];
1452
1545
  if (!specials) return 0;
1453
1546
  const oldEl = oldNode;
1454
1547
  const newEl = newNode;
@@ -1497,13 +1590,17 @@ function vdomSetChildNodes(oldParent, newParent, ref, frame, keys_) {
1497
1590
  let oldNode = oldParent.lastChild;
1498
1591
  let newNode = newParent.firstChild;
1499
1592
  let extra = 0;
1500
- const keyedNodes = {};
1501
- const newKeyedNodes = {};
1593
+ const keyedNodes = /* @__PURE__ */ new Map();
1594
+ const newKeyedNodes = /* @__PURE__ */ new Map();
1502
1595
  while (oldNode) {
1503
1596
  extra++;
1504
1597
  const nodeKey = vdomGetCompareKey(oldNode);
1505
1598
  if (nodeKey) {
1506
- const bucket = keyedNodes[nodeKey] || (keyedNodes[nodeKey] = []);
1599
+ let bucket = keyedNodes.get(nodeKey);
1600
+ if (!bucket) {
1601
+ bucket = [];
1602
+ keyedNodes.set(nodeKey, bucket);
1603
+ }
1507
1604
  bucket.push(oldNode);
1508
1605
  }
1509
1606
  oldNode = oldNode.previousSibling;
@@ -1511,7 +1608,7 @@ function vdomSetChildNodes(oldParent, newParent, ref, frame, keys_) {
1511
1608
  while (newNode) {
1512
1609
  const nodeKey = vdomGetCompareKey(newNode);
1513
1610
  if (nodeKey) {
1514
- newKeyedNodes[nodeKey] = (newKeyedNodes[nodeKey] || 0) + 1;
1611
+ newKeyedNodes.set(nodeKey, (newKeyedNodes.get(nodeKey) ?? 0) + 1);
1515
1612
  }
1516
1613
  newNode = newNode.nextSibling;
1517
1614
  }
@@ -1522,23 +1619,25 @@ function vdomSetChildNodes(oldParent, newParent, ref, frame, keys_) {
1522
1619
  const tempNew = newNode;
1523
1620
  newNode = newNode.nextSibling;
1524
1621
  const nodeKey = vdomGetCompareKey(tempNew);
1525
- let foundNode = nodeKey ? keyedNodes[nodeKey] : void 0;
1622
+ let foundNode = nodeKey ? keyedNodes.get(nodeKey) : void 0;
1526
1623
  if (foundNode && (foundNode = foundNode.slice()) && foundNode.length) {
1527
1624
  const matched = foundNode.pop();
1528
1625
  while (matched !== oldNode) {
1529
- const next = oldNode?.nextSibling ?? null;
1626
+ if (!oldNode) break;
1627
+ const next = oldNode.nextSibling;
1530
1628
  oldParent.appendChild(oldNode);
1531
1629
  oldNode = next;
1532
1630
  }
1533
1631
  oldNode = matched.nextSibling;
1534
- if (nodeKey && newKeyedNodes[nodeKey]) {
1535
- newKeyedNodes[nodeKey]--;
1632
+ if (nodeKey) {
1633
+ const c = newKeyedNodes.get(nodeKey);
1634
+ if (c) newKeyedNodes.set(nodeKey, c - 1);
1536
1635
  }
1537
1636
  vdomSetNode(matched, tempNew, oldParent, ref, frame, keys_);
1538
1637
  } else if (oldNode) {
1539
1638
  const tempOld2 = oldNode;
1540
1639
  const oldKey = vdomGetCompareKey(tempOld2);
1541
- if (oldKey && keyedNodes[oldKey] && newKeyedNodes[oldKey]) {
1640
+ if (oldKey && keyedNodes.has(oldKey) && newKeyedNodes.get(oldKey)) {
1542
1641
  extra++;
1543
1642
  ref.hasChanged = 1;
1544
1643
  ref.domOps.push([8, oldParent, tempNew, tempOld2]);
@@ -1562,17 +1661,21 @@ function vdomSetChildNodes(oldParent, newParent, ref, frame, keys_) {
1562
1661
  }
1563
1662
  }
1564
1663
  function vdomSetNode(oldNode, newNode, oldParent, ref, frame, keys_) {
1565
- if (vdomSpecialDiff(oldNode, newNode) || oldNode.nodeType === 1 && oldNode.hasAttribute(LARK_KEYS.VIEW_KEY) || !(oldNode.isEqualNode && oldNode.isEqualNode(newNode))) {
1664
+ const oldAsEl = oldNode instanceof Element ? oldNode : null;
1665
+ const newAsEl = newNode instanceof Element ? newNode : null;
1666
+ const hasViewKey = !!oldAsEl?.hasAttribute(LarkInnerKeys.VIEW_KEY);
1667
+ const equalAsNodes = oldAsEl !== null && newAsEl !== null && oldAsEl.isEqualNode && oldAsEl.isEqualNode(newAsEl);
1668
+ if (vdomSpecialDiff(oldNode, newNode) || hasViewKey || !equalAsNodes) {
1566
1669
  if (oldNode.nodeType === newNode.nodeType && oldNode.nodeName === newNode.nodeName) {
1567
- if (oldNode.nodeType === 1) {
1568
- const oldEl = oldNode;
1569
- const newEl = newNode;
1570
- const staticKey = newEl.getAttribute(LARK_KEYS.DIFF_KEY);
1571
- if (staticKey && staticKey === oldEl.getAttribute(LARK_KEYS.DIFF_KEY)) {
1670
+ if (oldAsEl !== null && newAsEl !== null) {
1671
+ const oldEl = oldAsEl;
1672
+ const newEl = newAsEl;
1673
+ const staticKey = newEl.getAttribute(LarkInnerKeys.DIFF_KEY);
1674
+ if (staticKey && staticKey === oldEl.getAttribute(LarkInnerKeys.DIFF_KEY)) {
1572
1675
  return;
1573
1676
  }
1574
1677
  const newLarkView = newEl.getAttribute(LARK_VIEW);
1575
- const updateAttribute = !newEl.getAttribute(LARK_KEYS.ATTR_KEY) || newEl.getAttribute(LARK_KEYS.ATTR_KEY) !== oldEl.getAttribute(LARK_KEYS.ATTR_KEY);
1678
+ const updateAttribute = !newEl.getAttribute(LarkInnerKeys.ATTR_KEY) || newEl.getAttribute(LarkInnerKeys.ATTR_KEY) !== oldEl.getAttribute(LarkInnerKeys.ATTR_KEY);
1576
1679
  let updateChildren = true;
1577
1680
  if (newLarkView) {
1578
1681
  const oldFrameId = oldEl.getAttribute("id") || "";
@@ -1673,7 +1776,8 @@ function encodeQ(v) {
1673
1776
  }
1674
1777
 
1675
1778
  // src/updater.ts
1676
- function updaterRef(refData, value, key) {
1779
+ function updaterRef(refDataIn, value, key) {
1780
+ const refData = refDataIn;
1677
1781
  const counter = refData[SPLITTER];
1678
1782
  for (let i = counter; --i; ) {
1679
1783
  key = SPLITTER + i;
@@ -1693,13 +1797,19 @@ var Updater = class {
1693
1797
  /** Ref data for template rendering */
1694
1798
  refData;
1695
1799
  /** Changed keys in current digest cycle */
1696
- changedKeys = {};
1800
+ changedKeys = /* @__PURE__ */ new Set();
1697
1801
  /** Whether data has changed since last digest */
1698
1802
  hasChangedFlag = 0;
1699
- /** Digesting queue: supports re-digest during digest */
1803
+ /**
1804
+ * Digesting queue: supports re-digest during digest.
1805
+ * Holds pending callbacks; `null` is used as a sentinel marking the start
1806
+ * of an active digest cycle, so `runDigest` can detect re-entrant calls.
1807
+ */
1700
1808
  digestingQueue = [];
1701
- /** Snapshot JSON string for altered() detection */
1702
- snapshotJson;
1809
+ /** Monotonically increasing version, bumped each time data actually changes. */
1810
+ version = 0;
1811
+ /** Snapshot of `version` taken by `snapshot()`, used by `altered()`. */
1812
+ snapshotVersion;
1703
1813
  constructor(viewId) {
1704
1814
  this.viewId = viewId;
1705
1815
  this.data = { vId: viewId };
@@ -1727,7 +1837,16 @@ var Updater = class {
1727
1837
  * Returns this for chaining.
1728
1838
  */
1729
1839
  set(data, excludes) {
1730
- this.hasChangedFlag = setData(data, this.data, this.changedKeys, excludes || /* @__PURE__ */ new Set()) || this.hasChangedFlag ? 1 : 0;
1840
+ const changed = setData(
1841
+ data,
1842
+ this.data,
1843
+ this.changedKeys,
1844
+ excludes || EMPTY_STRING_SET
1845
+ );
1846
+ if (changed) {
1847
+ this.version++;
1848
+ this.hasChangedFlag = 1;
1849
+ }
1731
1850
  return this;
1732
1851
  }
1733
1852
  /**
@@ -1763,13 +1882,13 @@ var Updater = class {
1763
1882
  const keys2 = this.changedKeys;
1764
1883
  const changed = this.hasChangedFlag;
1765
1884
  this.hasChangedFlag = 0;
1766
- this.changedKeys = {};
1885
+ this.changedKeys = /* @__PURE__ */ new Set();
1767
1886
  const frame = Frame.get(this.viewId);
1768
1887
  const view = frame?.view;
1769
1888
  const node = getById(this.viewId);
1770
- if (changed && view && node && view.signature > 0) {
1889
+ if (changed && view && node && view.signature > 0 && frame) {
1771
1890
  const template = view.template;
1772
- if (template && typeof template === "function") {
1891
+ if (typeof template === "function") {
1773
1892
  const html = template(
1774
1893
  this.data,
1775
1894
  this.viewId,
@@ -1806,43 +1925,65 @@ var Updater = class {
1806
1925
  }
1807
1926
  }
1808
1927
  /**
1809
- * Save a snapshot of current data for altered() detection.
1928
+ * Save a snapshot of the current data version for `altered()` detection.
1929
+ * Cheap O(1) — records the current monotonic version, no serialization.
1810
1930
  */
1811
1931
  snapshot() {
1812
- this.snapshotJson = JSON.stringify(this.data);
1932
+ this.snapshotVersion = this.version;
1813
1933
  return this;
1814
1934
  }
1815
1935
  /**
1816
- * Check if data has changed since last snapshot.
1936
+ * Check whether data has changed since the last snapshot.
1937
+ * Returns undefined when no snapshot has been taken yet.
1817
1938
  */
1818
1939
  altered() {
1819
- if (this.snapshotJson) {
1820
- return this.snapshotJson !== JSON.stringify(this.data);
1821
- }
1822
- return void 0;
1940
+ if (this.snapshotVersion === void 0) return void 0;
1941
+ return this.version !== this.snapshotVersion;
1823
1942
  }
1824
1943
  /**
1825
- * Translate data references (SPLITTER-prefixed values).
1944
+ * Translate a refData reference back to its original value.
1945
+ *
1946
+ * The ref protocol is `SPLITTER` + ascii decimal digits — the exact format
1947
+ * emitted by `updaterRef`. We require that exact shape so a user-supplied
1948
+ * string that merely begins with SPLITTER is never accidentally resolved
1949
+ * (or mishandled as a "missing ref").
1826
1950
  */
1827
1951
  translate(data) {
1828
- if (typeof data === "string" && data[0] === SPLITTER) {
1829
- return has(this.refData, data) ? this.refData[data] : data;
1952
+ if (typeof data !== "string") return data;
1953
+ if (data.length < 2 || data[0] !== SPLITTER) return data;
1954
+ for (let i = 1; i < data.length; i++) {
1955
+ const c = data.charCodeAt(i);
1956
+ if (c < 48 || c > 57) return data;
1830
1957
  }
1831
- return data;
1958
+ return hasOwnProperty(this.refData, data) ? this.refData[data] : data;
1832
1959
  }
1833
1960
  /**
1834
- * Parse expression with data context.
1961
+ * Resolve a dotted property path against refData.
1962
+ *
1963
+ * Only safe property-path syntax is supported: `a`, `a.b`, `a.b.c`.
1964
+ * Numeric literals (e.g. `1`, `1.5`) are returned as numbers. Anything else
1965
+ * returns `undefined` — we no longer evaluate arbitrary JavaScript via
1966
+ * `new Function`, so the method is CSP-safe and cannot be used as an
1967
+ * injection vector.
1835
1968
  */
1836
1969
  parse(expr) {
1837
- try {
1838
- const fn = new Function("data", `with(data) { return ${expr}; }`);
1839
- return fn(this.refData);
1840
- } catch {
1970
+ const trimmed = expr.trim();
1971
+ if (!trimmed) return void 0;
1972
+ if (/^-?\d+(?:\.\d+)?$/.test(trimmed)) {
1973
+ return Number(trimmed);
1974
+ }
1975
+ if (!/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*$/.test(trimmed)) {
1841
1976
  return void 0;
1842
1977
  }
1978
+ let cur = this.refData;
1979
+ for (const segment of trimmed.split(".")) {
1980
+ if (cur == null || typeof cur !== "object") return void 0;
1981
+ cur = cur[segment];
1982
+ }
1983
+ return cur;
1843
1984
  }
1844
1985
  /**
1845
- * Get changed keys (for external inspection).
1986
+ * Get the set of keys changed since the last digest (for external inspection).
1846
1987
  */
1847
1988
  getChangedKeys() {
1848
1989
  return this.changedKeys;
@@ -1889,30 +2030,31 @@ var View = class _View {
1889
2030
  // ============================================================
1890
2031
  // Getters for prototype-stored event maps
1891
2032
  // ============================================================
2033
+ /** Prototype-stored event maps shape (set by View.prepare). */
2034
+ get protoEventState() {
2035
+ return Object.getPrototypeOf(this);
2036
+ }
1892
2037
  /**
1893
2038
  * Event bitmask map: eventType -> bitmask (1=root, 2=selector).
1894
2039
  * Read from prototype ($evtObjMap) set by View.prepare.
1895
2040
  * Using a getter avoids ES6 class field shadowing the prototype value.
1896
2041
  */
1897
2042
  get eventObjectMap() {
1898
- const proto = Object.getPrototypeOf(this);
1899
- return proto["$evtObjMap"] || {};
2043
+ return this.protoEventState.$evtObjMap ?? {};
1900
2044
  }
1901
2045
  /**
1902
2046
  * Selector event map: eventType -> selector list.
1903
2047
  * Read from prototype ($selMap) set by View.prepare.
1904
2048
  */
1905
2049
  get eventSelectorMap() {
1906
- const proto = Object.getPrototypeOf(this);
1907
- return proto["$selMap"] || {};
2050
+ return this.protoEventState.$selMap ?? {};
1908
2051
  }
1909
2052
  /**
1910
2053
  * Global event list: [{handler, element, eventName, modifiers}].
1911
2054
  * Read from prototype ($globalEvtList) set by View.prepare.
1912
2055
  */
1913
2056
  get globalEventList() {
1914
- const proto = Object.getPrototypeOf(this);
1915
- return proto["$globalEvtList"] || [];
2057
+ return this.protoEventState.$globalEvtList ?? [];
1916
2058
  }
1917
2059
  // ============================================================
1918
2060
  // Instance lifecycle methods
@@ -1947,13 +2089,17 @@ var View = class _View {
1947
2089
  // ============================================================
1948
2090
  // Update methods
1949
2091
  // ============================================================
2092
+ /** Get the owning frame, asserting it has been bound. */
2093
+ get ownerFrame() {
2094
+ return this.owner;
2095
+ }
1950
2096
  /**
1951
2097
  * Notify view that HTML update is about to begin.
1952
2098
  * Unmounts child frames in the update zone.
1953
2099
  */
1954
2100
  beginUpdate(id) {
1955
2101
  if (this.signature > 0 && this.endUpdatePending !== void 0) {
1956
- this.owner.unmountZone(id, true);
2102
+ this.ownerFrame.unmountZone(id, true);
1957
2103
  }
1958
2104
  }
1959
2105
  /**
@@ -1971,7 +2117,7 @@ var View = class _View {
1971
2117
  this.endUpdatePending = 1;
1972
2118
  this.rendered = true;
1973
2119
  }
1974
- const ownerFrame = this.owner;
2120
+ const ownerFrame = this.ownerFrame;
1975
2121
  ownerFrame.mountZone(updateId, inner);
1976
2122
  if (!flag) {
1977
2123
  setTimeout(
@@ -2010,11 +2156,12 @@ var View = class _View {
2010
2156
  const loc = this.locationObserved;
2011
2157
  loc.flag = 1;
2012
2158
  if (typeof params === "object" && !Array.isArray(params)) {
2013
- if (params["path"]) {
2159
+ const opts = params;
2160
+ if (opts["path"]) {
2014
2161
  observePath = true;
2015
2162
  }
2016
- const paramKeys = params["params"];
2017
- if (paramKeys) {
2163
+ const paramKeys = opts["params"];
2164
+ if (typeof paramKeys === "string" || Array.isArray(paramKeys)) {
2018
2165
  params = paramKeys;
2019
2166
  }
2020
2167
  }
@@ -2078,24 +2225,16 @@ var View = class _View {
2078
2225
  */
2079
2226
  leaveTip(message, condition) {
2080
2227
  const changeListener = function(e) {
2081
- const isRouterChange = e.type === ROUTER_EVENTS.CHANGE;
2228
+ const isRouterChange = e.type === RouterEvents.CHANGE;
2082
2229
  const aKey = isRouterChange ? "a" : "b";
2083
2230
  const bKey = isRouterChange ? "b" : "a";
2084
2231
  if (changeListener[aKey]) {
2085
- if (typeof e.prevent === "function") {
2086
- e.prevent();
2087
- }
2088
- if (typeof e.reject === "function") {
2089
- e.reject();
2090
- }
2232
+ e.prevent?.();
2233
+ e.reject?.();
2091
2234
  } else if (condition()) {
2092
- if (typeof e.prevent === "function") {
2093
- e.prevent();
2094
- }
2235
+ e.prevent?.();
2095
2236
  changeListener[bKey] = 1;
2096
- if (typeof e.resolve === "function") {
2097
- e.resolve();
2098
- }
2237
+ e.resolve?.();
2099
2238
  }
2100
2239
  };
2101
2240
  const unloadListener = (e) => {
@@ -2103,14 +2242,12 @@ var View = class _View {
2103
2242
  e["msg"] = message;
2104
2243
  }
2105
2244
  };
2106
- const changeFn = changeListener;
2107
- const unloadFn = unloadListener;
2108
- Router.on(ROUTER_EVENTS.CHANGE, changeFn);
2109
- Router.on(ROUTER_EVENTS.PAGE_UNLOAD, unloadFn);
2110
- this.on("unload", changeFn);
2245
+ Router.on(RouterEvents.CHANGE, changeListener);
2246
+ Router.on(RouterEvents.PAGE_UNLOAD, unloadListener);
2247
+ this.on("unload", changeListener);
2111
2248
  this.on("destroy", () => {
2112
- Router.off(ROUTER_EVENTS.CHANGE, changeFn);
2113
- Router.off(ROUTER_EVENTS.PAGE_UNLOAD, unloadFn);
2249
+ Router.off(RouterEvents.CHANGE, changeListener);
2250
+ Router.off(RouterEvents.PAGE_UNLOAD, unloadListener);
2114
2251
  });
2115
2252
  }
2116
2253
  // ============================================================
@@ -2140,7 +2277,7 @@ var View = class _View {
2140
2277
  _View.mergeMixins(mixins, oView, ctors);
2141
2278
  }
2142
2279
  for (const p in proto) {
2143
- if (!has(proto, p)) continue;
2280
+ if (!hasOwnProperty(proto, p)) continue;
2144
2281
  const currentFn = proto[p];
2145
2282
  if (typeof currentFn !== "function") continue;
2146
2283
  const matches = p.match(VIEW_EVENT_METHOD_REGEXP);
@@ -2186,16 +2323,16 @@ var View = class _View {
2186
2323
  const existingFn = proto[combinedKey];
2187
2324
  if (!existingFn) {
2188
2325
  proto[combinedKey] = currentFn;
2189
- } else {
2326
+ } else if (typeof existingFn === "function") {
2190
2327
  const mixinFn = currentFn;
2191
2328
  const existingMixin = existingFn;
2192
2329
  if (existingMixin.marker) {
2193
2330
  if (mixinFn.marker) {
2194
2331
  proto[combinedKey] = _View.processMixinsSameEvent(
2195
- currentFn,
2196
- existingFn
2332
+ mixinFn,
2333
+ existingMixin
2197
2334
  );
2198
- } else if (has(proto, p)) {
2335
+ } else if (hasOwnProperty(proto, p)) {
2199
2336
  proto[combinedKey] = currentFn;
2200
2337
  }
2201
2338
  }
@@ -2218,7 +2355,7 @@ var View = class _View {
2218
2355
  const selectorObject = view.eventSelectorMap;
2219
2356
  const eventsList = view.globalEventList;
2220
2357
  for (const e in eventsObject) {
2221
- if (has(eventsObject, e)) {
2358
+ if (hasOwnProperty(eventsObject, e)) {
2222
2359
  if (destroy) {
2223
2360
  EventDelegator.unbind(e, !!selectorObject[e]);
2224
2361
  } else {
@@ -2261,7 +2398,7 @@ var View = class _View {
2261
2398
  static destroyAllResources(view, lastly) {
2262
2399
  const cache = view.resources;
2263
2400
  for (const p in cache) {
2264
- if (has(cache, p)) {
2401
+ if (hasOwnProperty(cache, p)) {
2265
2402
  const entry = cache[p];
2266
2403
  if (lastly || entry.destroyOnRender) {
2267
2404
  _View.destroyResource(cache, p, true);
@@ -2291,13 +2428,16 @@ var View = class _View {
2291
2428
  static wrapMethod(proto, fnName, shortKey) {
2292
2429
  const originalFn = proto[fnName];
2293
2430
  if (typeof originalFn !== "function") return;
2431
+ const originalAsFn = originalFn;
2294
2432
  const wrapped = function(...args) {
2295
2433
  if (this.signature > 0) {
2296
2434
  this.signature++;
2297
2435
  this.fire("render");
2298
2436
  _View.destroyAllResources(this, false);
2299
- const instanceFn = typeof this[fnName] === "function" ? this[fnName] : originalFn;
2300
- const fnToCall = instanceFn === wrapped ? originalFn : instanceFn;
2437
+ const lookup = this;
2438
+ const candidate = lookup[fnName];
2439
+ const instanceFn = typeof candidate === "function" ? candidate : originalAsFn;
2440
+ const fnToCall = instanceFn === wrapped ? originalAsFn : instanceFn;
2301
2441
  return funcWithTry(fnToCall, args, this, noop);
2302
2442
  }
2303
2443
  return void 0;
@@ -2311,19 +2451,18 @@ var View = class _View {
2311
2451
  */
2312
2452
  static processMixinsSameEvent(additional, exist) {
2313
2453
  let temp;
2314
- const existMixin = exist;
2315
- if (existMixin.handlerList) {
2316
- temp = existMixin;
2454
+ if (exist.handlerList) {
2455
+ temp = exist;
2317
2456
  } else {
2318
- temp = function(...e) {
2319
- funcWithTry(temp.handlerList ?? [], e, this, noop);
2457
+ const merged = function(...e) {
2458
+ funcWithTry(merged.handlerList ?? [], e, this, noop);
2320
2459
  };
2321
- temp.handlerList = [exist];
2322
- temp.marker = 1;
2460
+ merged.handlerList = [exist];
2461
+ merged.marker = 1;
2462
+ temp = merged;
2323
2463
  }
2324
- const additionalMixin = additional;
2325
2464
  temp.handlerList = (temp.handlerList ?? []).concat(
2326
- additionalMixin.handlerList ?? [additional]
2465
+ additional.handlerList ?? [additional]
2327
2466
  );
2328
2467
  return temp;
2329
2468
  }
@@ -2335,29 +2474,29 @@ var View = class _View {
2335
2474
  const temp = {};
2336
2475
  for (const node of mixins) {
2337
2476
  for (const p in node) {
2338
- if (!has(node, p)) continue;
2477
+ if (!hasOwnProperty(node, p)) continue;
2339
2478
  const fn = node[p];
2479
+ if (typeof fn !== "function") continue;
2480
+ const mixinFn = fn;
2340
2481
  const exist = temp[p];
2341
2482
  if (p === "make") {
2342
- ctors.push(fn);
2483
+ ctors.push(mixinFn);
2343
2484
  continue;
2344
2485
  }
2345
2486
  if (VIEW_EVENT_METHOD_REGEXP.test(p)) {
2346
2487
  if (exist) {
2347
- temp[p] = _View.processMixinsSameEvent(fn, exist);
2488
+ temp[p] = _View.processMixinsSameEvent(mixinFn, exist);
2348
2489
  } else {
2349
- fn.marker = 1;
2350
- temp[p] = fn;
2351
- }
2352
- } else {
2353
- if (!exist) {
2354
- temp[p] = fn;
2490
+ mixinFn.marker = 1;
2491
+ temp[p] = mixinFn;
2355
2492
  }
2493
+ } else if (!exist) {
2494
+ temp[p] = mixinFn;
2356
2495
  }
2357
2496
  }
2358
2497
  }
2359
2498
  for (const p in temp) {
2360
- if (!has(proto, p)) {
2499
+ if (!hasOwnProperty(proto, p)) {
2361
2500
  proto[p] = temp[p];
2362
2501
  }
2363
2502
  }
@@ -2390,19 +2529,20 @@ var View = class _View {
2390
2529
  * - Event method patterns: `'name<click>'` etc.
2391
2530
  */
2392
2531
  static extend(props, statics) {
2393
- props = props || {};
2394
- const make = props["make"];
2532
+ const definedProps = props ?? {};
2533
+ const make = definedProps["make"];
2395
2534
  const ctors = [];
2396
- if (make) {
2535
+ if (typeof make === "function") {
2397
2536
  ctors.push(make);
2398
2537
  }
2399
2538
  const ParentView = this;
2400
2539
  const ChildView = class extends ParentView {
2401
2540
  constructor(nodeId, ownerFrame, initParams, node, mixinCtors) {
2402
2541
  super(nodeId, ownerFrame, initParams, node, []);
2403
- for (const key in props) {
2404
- if (has(props, key) && key !== "make" && key !== "render") {
2405
- this[key] = props[key];
2542
+ const instanceProps = this;
2543
+ for (const key in definedProps) {
2544
+ if (hasOwnProperty(definedProps, key) && key !== "make" && key !== "render") {
2545
+ instanceProps[key] = definedProps[key];
2406
2546
  }
2407
2547
  }
2408
2548
  this.id = nodeId;
@@ -2422,15 +2562,16 @@ var View = class _View {
2422
2562
  }
2423
2563
  };
2424
2564
  const proto = ChildView.prototype;
2425
- for (const key in props) {
2426
- if (has(props, key) && key !== "make") {
2427
- proto[key] = props[key];
2565
+ for (const key in definedProps) {
2566
+ if (hasOwnProperty(definedProps, key) && key !== "make") {
2567
+ proto[key] = definedProps[key];
2428
2568
  }
2429
2569
  }
2430
2570
  if (statics) {
2571
+ const staticTarget = ChildView;
2431
2572
  for (const key in statics) {
2432
- if (has(statics, key)) {
2433
- ChildView[key] = statics[key];
2573
+ if (hasOwnProperty(statics, key)) {
2574
+ staticTarget[key] = statics[key];
2434
2575
  }
2435
2576
  }
2436
2577
  }
@@ -2445,14 +2586,87 @@ var View = class _View {
2445
2586
  return this;
2446
2587
  }
2447
2588
  };
2589
+ function defineView(props, statics) {
2590
+ return View.extend(props, statics);
2591
+ }
2592
+
2593
+ // src/module-loader.ts
2594
+ var config = {
2595
+ rootId: "root",
2596
+ routeMode: "history",
2597
+ hashbang: "#!",
2598
+ error: (error) => {
2599
+ throw error;
2600
+ }
2601
+ };
2602
+ function use(names, callback) {
2603
+ const nameList = typeof names === "string" ? [names] : names;
2604
+ const loadPromise = (() => {
2605
+ if (config.require) {
2606
+ const result = config.require(nameList);
2607
+ if (result && typeof result.then === "function") {
2608
+ return result;
2609
+ }
2610
+ return Promise.resolve([]);
2611
+ }
2612
+ return Promise.all(
2613
+ nameList.map((name) => {
2614
+ const importPath = name.startsWith(".") || name.startsWith("/") ? name : `./${name}`;
2615
+ return import(
2616
+ /* @vite-ignore */
2617
+ /* webpackIgnore: true */
2618
+ importPath
2619
+ ).then((mod) => {
2620
+ return mod && (mod["__esModule"] || // For Webpack
2621
+ typeof mod["default"] === "function") ? mod["default"] : mod;
2622
+ }).catch((err) => {
2623
+ const errorHandler = config.error;
2624
+ if (errorHandler) {
2625
+ errorHandler(err instanceof Error ? err : new Error(String(err)));
2626
+ }
2627
+ return void 0;
2628
+ });
2629
+ })
2630
+ );
2631
+ })();
2632
+ if (callback) {
2633
+ loadPromise.then((modules) => {
2634
+ callback(...modules);
2635
+ });
2636
+ }
2637
+ return loadPromise;
2638
+ }
2639
+
2640
+ // src/view-registry.ts
2641
+ var viewClassRegistry = {};
2642
+ function getViewClass(path) {
2643
+ return viewClassRegistry[path];
2644
+ }
2645
+ function registerViewClass(viewPath, ViewClass) {
2646
+ const parsed = parseUri(viewPath);
2647
+ const path = parsed.path;
2648
+ if (path) {
2649
+ viewClassRegistry[path] = ViewClass;
2650
+ }
2651
+ }
2652
+ function invalidateViewClass(viewPath) {
2653
+ const parsed = parseUri(viewPath);
2654
+ const path = parsed.path;
2655
+ if (path) {
2656
+ Reflect.deleteProperty(viewClassRegistry, path);
2657
+ }
2658
+ }
2659
+ function getViewClassRegistry() {
2660
+ return viewClassRegistry;
2661
+ }
2448
2662
 
2449
2663
  // src/frame.ts
2450
2664
  var frameRegistry = /* @__PURE__ */ new Map();
2451
2665
  var rootFrame;
2452
2666
  var globalAlter;
2667
+ var MAX_FRAME_POOL = 64;
2453
2668
  var frameCache = [];
2454
2669
  var staticEmitter = new EventEmitter();
2455
- var viewClassRegistry = {};
2456
2670
  var Frame = class _Frame extends EventEmitter {
2457
2671
  /** Frame ID (same as owner DOM element ID) */
2458
2672
  id;
@@ -2467,8 +2681,8 @@ var Frame = class _Frame extends EventEmitter {
2467
2681
  childrenCount = 0;
2468
2682
  /** Ready count (children that have fired 'created') */
2469
2683
  readyCount = 0;
2470
- /** Ready map: id -> 1 */
2471
- readyMap = {};
2684
+ /** Set of child frame IDs that have fired 'created' */
2685
+ readyMap = /* @__PURE__ */ new Set();
2472
2686
  /** View instance */
2473
2687
  viewInstance;
2474
2688
  /** Get view instance (read-only) */
@@ -2539,13 +2753,30 @@ var Frame = class _Frame extends EventEmitter {
2539
2753
  this.viewPath = viewPath;
2540
2754
  const params = parsed["params"];
2541
2755
  translateQuery(pId || this.id, viewPath, params);
2756
+ const initParams = { ...params };
2542
2757
  if (viewInitParams) {
2543
- assign(params, viewInitParams);
2758
+ assign(initParams, viewInitParams);
2544
2759
  }
2545
2760
  const sign = this.signature;
2546
- if (viewClassRegistry[viewClassName]) {
2547
- this.doMountView(viewClassRegistry[viewClassName], params, node, sign);
2761
+ const registered = getViewClass(viewClassName);
2762
+ if (registered) {
2763
+ this.doMountView(registered, initParams, node, sign);
2764
+ return;
2548
2765
  }
2766
+ use(viewClassName, (ViewClass) => {
2767
+ if (sign !== this.signature) return;
2768
+ if (typeof ViewClass === "function") {
2769
+ const ViewClassTyped = ViewClass;
2770
+ registerViewClass(viewClassName, ViewClassTyped);
2771
+ this.doMountView(ViewClassTyped, initParams, node, sign);
2772
+ } else {
2773
+ const error = new Error(`Cannot load view: ${viewClassName}`);
2774
+ const errorHandler = config.error;
2775
+ if (errorHandler) {
2776
+ errorHandler(error);
2777
+ }
2778
+ }
2779
+ });
2549
2780
  }
2550
2781
  /**
2551
2782
  * Internal: actually mount the view after class is loaded.
@@ -2553,14 +2784,8 @@ var Frame = class _Frame extends EventEmitter {
2553
2784
  doMountView(ViewClass, params, node, sign) {
2554
2785
  if (sign !== this.signature) return;
2555
2786
  const mixinCtors = View.prepare(ViewClass);
2556
- const ViewConstructor = ViewClass;
2557
- const view = new ViewConstructor(
2558
- this.id,
2559
- this,
2560
- params,
2561
- node,
2562
- mixinCtors
2563
- );
2787
+ const Ctor = ViewClass;
2788
+ const view = new Ctor(this.id, this, params, node, mixinCtors);
2564
2789
  this.viewInstance = view;
2565
2790
  view.signature = 1;
2566
2791
  View.delegateEvents(view);
@@ -2641,7 +2866,9 @@ var Frame = class _Frame extends EventEmitter {
2641
2866
  frame.unmountView();
2642
2867
  removeFrame(targetId, wasCreated);
2643
2868
  reInitFrameForCache(frame);
2644
- frameCache.push(frame);
2869
+ if (frameCache.length < MAX_FRAME_POOL) {
2870
+ frameCache.push(frame);
2871
+ }
2645
2872
  const parent = frameRegistry.get(pId || "");
2646
2873
  if (parent && parent.childrenMap[targetId]) {
2647
2874
  Reflect.deleteProperty(parent.childrenMap, targetId);
@@ -2660,13 +2887,12 @@ var Frame = class _Frame extends EventEmitter {
2660
2887
  const viewElements = rootEl.querySelectorAll(`[${LARK_VIEW}]`);
2661
2888
  const frames = [];
2662
2889
  viewElements.forEach((el) => {
2663
- const htmlEl = el;
2664
- if (!htmlEl.frameBound) {
2665
- const elId = ensureElementId2(htmlEl);
2666
- htmlEl.frameBound = 1;
2667
- const viewPath = getAttribute(htmlEl, LARK_VIEW);
2668
- frames.push([elId, viewPath]);
2669
- }
2890
+ if (!(el instanceof HTMLElement)) return;
2891
+ if (htmlElIsBound(el)) return;
2892
+ const elId = ensureElementId2(el);
2893
+ el.frameBound = 1;
2894
+ const viewPath = getAttribute(el, LARK_VIEW);
2895
+ frames.push([elId, viewPath]);
2670
2896
  });
2671
2897
  for (const [frameId, viewPath] of frames) {
2672
2898
  this.mountFrame(frameId, viewPath);
@@ -2679,7 +2905,7 @@ var Frame = class _Frame extends EventEmitter {
2679
2905
  */
2680
2906
  unmountZone(zoneId, _inner) {
2681
2907
  for (const childId in this.childrenMap) {
2682
- if (has(this.childrenMap, childId)) {
2908
+ if (hasOwnProperty(this.childrenMap, childId)) {
2683
2909
  if (!zoneId || childId !== zoneId) {
2684
2910
  this.unmountFrame(childId);
2685
2911
  }
@@ -2693,7 +2919,7 @@ var Frame = class _Frame extends EventEmitter {
2693
2919
  children() {
2694
2920
  const result = [];
2695
2921
  for (const id in this.childrenMap) {
2696
- if (has(this.childrenMap, id)) {
2922
+ if (hasOwnProperty(this.childrenMap, id)) {
2697
2923
  result.push(id);
2698
2924
  }
2699
2925
  }
@@ -2720,14 +2946,10 @@ var Frame = class _Frame extends EventEmitter {
2720
2946
  let result;
2721
2947
  const view = this.view;
2722
2948
  if (view && view.rendered) {
2723
- const fn = view[name];
2949
+ const lookup = view;
2950
+ const fn = lookup[name];
2724
2951
  if (typeof fn === "function") {
2725
- result = funcWithTry(
2726
- fn,
2727
- args || [],
2728
- view,
2729
- noop
2730
- );
2952
+ result = funcWithTry(fn, args || [], view, noop);
2731
2953
  }
2732
2954
  } else {
2733
2955
  const key = SPLITTER + name;
@@ -2750,6 +2972,25 @@ var Frame = class _Frame extends EventEmitter {
2750
2972
  }
2751
2973
  return result;
2752
2974
  }
2975
+ /**
2976
+ * Type-safe variant of `invoke`.
2977
+ *
2978
+ * `invoke()` accepts any string and any args, which silently hides
2979
+ * mismatched call sites when a method gets renamed. `invokeTyped` carries
2980
+ * the view's method signature through TypeScript so the compiler catches
2981
+ * those mistakes:
2982
+ *
2983
+ * ```ts
2984
+ * type Home = View & { loadData(id: string): Promise<void> };
2985
+ * frame.invokeTyped<Home, "loadData">("loadData", ["user-1"]);
2986
+ * ```
2987
+ *
2988
+ * Behavior is identical to `invoke` at runtime — same defer / direct-call
2989
+ * paths — so it's a drop-in safer overload.
2990
+ */
2991
+ invokeTyped(name, args) {
2992
+ return this.invoke(name, args);
2993
+ }
2753
2994
  // ============================================================
2754
2995
  // Static methods
2755
2996
  // ============================================================
@@ -2761,8 +3002,25 @@ var Frame = class _Frame extends EventEmitter {
2761
3002
  static getAll() {
2762
3003
  return frameRegistry;
2763
3004
  }
2764
- /** Get or create root frame */
2765
- static root(rootId) {
3005
+ /**
3006
+ * Returns the existing root frame, or `undefined` if none has been created.
3007
+ * Pure getter — never creates a Frame, never touches the DOM.
3008
+ *
3009
+ * Use `Frame.createRoot(id)` to create the root explicitly during framework
3010
+ * boot. For Micro-Frontend hosts that own multiple independent containers,
3011
+ * use `new Frame(containerId)` directly so each MF mount has its own root.
3012
+ */
3013
+ static getRoot() {
3014
+ return rootFrame;
3015
+ }
3016
+ /**
3017
+ * Create (or return) the singleton root frame for this app.
3018
+ *
3019
+ * Idempotent: subsequent calls always return the original root regardless
3020
+ * of `rootId` — so passing a different id later is silently ignored.
3021
+ * `Framework.boot()` is the canonical caller; user code rarely needs this.
3022
+ */
3023
+ static createRoot(rootId) {
2766
3024
  if (!rootFrame) {
2767
3025
  rootId = rootId || "root";
2768
3026
  let rootElement = document.getElementById(rootId);
@@ -2774,6 +3032,17 @@ var Frame = class _Frame extends EventEmitter {
2774
3032
  }
2775
3033
  return rootFrame;
2776
3034
  }
3035
+ /**
3036
+ * @deprecated Use `Frame.getRoot()` for read-only access or
3037
+ * `Frame.createRoot(id)` to create the root explicitly. The single-method
3038
+ * `root()` blurred the distinction and was a common source of bugs in
3039
+ * Micro-Frontend hosts.
3040
+ *
3041
+ * Kept for backward compatibility — behavior unchanged.
3042
+ */
3043
+ static root(rootId) {
3044
+ return _Frame.createRoot(rootId);
3045
+ }
2777
3046
  /** Bind event listener (static) */
2778
3047
  static on(event, handler) {
2779
3048
  staticEmitter.on(event, handler);
@@ -2789,6 +3058,9 @@ var Frame = class _Frame extends EventEmitter {
2789
3058
  staticEmitter.fire(event, data);
2790
3059
  }
2791
3060
  };
3061
+ function htmlElIsBound(element) {
3062
+ return !!element.frameBound;
3063
+ }
2792
3064
  function ensureElementId2(element) {
2793
3065
  const id = element.getAttribute("id");
2794
3066
  if (id) return id;
@@ -2818,8 +3090,8 @@ function notifyCreated(frameInstance) {
2818
3090
  const pId = frameInstance.parentId;
2819
3091
  if (pId) {
2820
3092
  const parent = frameRegistry.get(pId);
2821
- if (parent && !parent.readyMap[frameInstance.id]) {
2822
- parent.readyMap[frameInstance.id] = 1;
3093
+ if (parent && !parent.readyMap.has(frameInstance.id)) {
3094
+ parent.readyMap.add(frameInstance.id);
2823
3095
  parent.readyCount++;
2824
3096
  notifyCreated(parent);
2825
3097
  }
@@ -2834,9 +3106,9 @@ function notifyAlter(frameInstance, data) {
2834
3106
  const pId = frameInstance.parentId;
2835
3107
  if (pId) {
2836
3108
  const parent = frameRegistry.get(pId);
2837
- if (parent && parent.readyMap[frameInstance.id]) {
3109
+ if (parent && parent.readyMap.has(frameInstance.id)) {
2838
3110
  parent.readyCount--;
2839
- Reflect.deleteProperty(parent.readyMap, frameInstance.id);
3111
+ parent.readyMap.delete(frameInstance.id);
2840
3112
  notifyAlter(parent, data);
2841
3113
  }
2842
3114
  }
@@ -2849,7 +3121,7 @@ function reInitFrame(frame, id, parentId) {
2849
3121
  frame["childrenCount"] = 0;
2850
3122
  frame["readyCount"] = 0;
2851
3123
  frame["signature"] = 1;
2852
- frame["readyMap"] = {};
3124
+ frame["readyMap"] = /* @__PURE__ */ new Set();
2853
3125
  frame["invokeList"] = [];
2854
3126
  frameRegistry.set(id, frame);
2855
3127
  }
@@ -2857,7 +3129,7 @@ function reInitFrameForCache(frame) {
2857
3129
  Reflect.set(frame, "id", "");
2858
3130
  frame["_parentId"] = void 0;
2859
3131
  frame["childrenMap"] = {};
2860
- frame["readyMap"] = {};
3132
+ frame["readyMap"] = /* @__PURE__ */ new Set();
2861
3133
  }
2862
3134
  function translateQuery(pId, src, params) {
2863
3135
  const parentFrame = frameRegistry.get(pId);
@@ -2867,22 +3139,138 @@ function translateQuery(pId, src, params) {
2867
3139
  if (!parentRefData) return;
2868
3140
  if (src.indexOf(SPLITTER) > 0) {
2869
3141
  translateData(parentRefData, params);
2870
- if (params[SPLITTER]) {
2871
- assign(
2872
- params,
2873
- params[SPLITTER]
2874
- );
3142
+ const paramsRec = params;
3143
+ const splitterValue = paramsRec[SPLITTER];
3144
+ if (splitterValue && typeof splitterValue === "object") {
3145
+ assign(params, splitterValue);
2875
3146
  Reflect.deleteProperty(params, SPLITTER);
2876
3147
  }
2877
3148
  }
2878
3149
  }
2879
- function registerViewClass(viewPath, ViewClass) {
2880
- const parsed = parseUri(viewPath);
2881
- const path = parsed.path;
2882
- if (path) {
2883
- viewClassRegistry[path] = ViewClass;
3150
+
3151
+ // src/cross-site.ts
3152
+ var preparePromises = {};
3153
+ var projectsMap = null;
3154
+ function loadRemoteView(viewPath, bizCode) {
3155
+ const crossConfigs = config.crossConfigs || window.crossConfigs;
3156
+ const currentName = config.projectName || "";
3157
+ const slashIndex = viewPath.indexOf("/");
3158
+ const projectName = slashIndex > -1 ? viewPath.substring(0, slashIndex) : viewPath;
3159
+ if (projectName === currentName) return Promise.resolve();
3160
+ if (!preparePromises[projectName]) {
3161
+ if (!projectsMap) {
3162
+ const map = toMap(crossConfigs || [], "projectName");
3163
+ projectsMap = map;
3164
+ }
3165
+ const info = projectsMap[projectName];
3166
+ if (!info) {
3167
+ return Promise.reject(
3168
+ new Error(`Cannot find ${projectName} from crossConfigs`)
3169
+ );
3170
+ }
3171
+ preparePromises[projectName] = use(`${projectName}/prepare`).then((modules) => {
3172
+ let mod = modules[0];
3173
+ if (mod && typeof mod === "object" && mod !== null) {
3174
+ const rec = mod;
3175
+ if (rec["__esModule"]) {
3176
+ mod = rec["default"];
3177
+ }
3178
+ }
3179
+ if (typeof mod === "function") {
3180
+ return mod({ bizCode });
3181
+ }
3182
+ return void 0;
3183
+ }).catch((err) => {
3184
+ Reflect.deleteProperty(preparePromises, projectName);
3185
+ throw err;
3186
+ });
2884
3187
  }
3188
+ return preparePromises[projectName];
2885
3189
  }
3190
+ function resetProjectsMap() {
3191
+ projectsMap = null;
3192
+ }
3193
+ var skeletonTemplate = (data, viewId) => {
3194
+ let skeletonHtml = "<div>Loading...</div>";
3195
+ if (data && typeof data === "object") {
3196
+ const candidate = data.skeleton;
3197
+ if (typeof candidate === "string") skeletonHtml = candidate;
3198
+ }
3199
+ return `<div id="mf_${viewId}">${skeletonHtml}</div>`;
3200
+ };
3201
+ var CrossSite = View.extend({
3202
+ /** Skeleton template renders loading state + child container */
3203
+ template: skeletonTemplate,
3204
+ init(params) {
3205
+ this.$sign = 0;
3206
+ this.on("destroy", () => {
3207
+ this.$sign = -1;
3208
+ });
3209
+ this.assign?.(params);
3210
+ },
3211
+ assign(data) {
3212
+ this.$view = typeof data["view"] === "string" ? data["view"] : "";
3213
+ const nested = data["params"];
3214
+ const nestedParams = nested && typeof nested === "object" ? nested : {};
3215
+ this.$params = { ...data, ...nestedParams };
3216
+ this.updater.set({
3217
+ skeleton: data["skeleton"],
3218
+ skeletonParams: data["skeletonParams"] || {},
3219
+ bizCode: data["bizCode"]
3220
+ });
3221
+ if (this.$sign > 0) {
3222
+ this.updateView();
3223
+ }
3224
+ return false;
3225
+ },
3226
+ async updateView() {
3227
+ const sign = ++this.$sign;
3228
+ const stored = this.updater.get();
3229
+ const bizCode = stored.bizCode;
3230
+ try {
3231
+ await loadRemoteView(this.$view, bizCode);
3232
+ } catch (ex) {
3233
+ const node = document.getElementById("mf_" + this.id);
3234
+ if (node) {
3235
+ const err = ex instanceof Error ? ex : new Error(String(ex));
3236
+ node.innerHTML = err.message || String(err);
3237
+ }
3238
+ }
3239
+ if (this.$sign !== sign) return;
3240
+ const mf = Frame.get("mf_" + this.id);
3241
+ const parsedNew = parseUri(this.$view);
3242
+ const newPath = parsedNew.path;
3243
+ const oldPath = mf?.viewPath ? parseUri(mf.viewPath).path : "";
3244
+ const view = mf?.view;
3245
+ if (newPath === oldPath && view && typeof view.assign === "function") {
3246
+ const result = funcWithTry(view.assign, [this.$params], view, noop);
3247
+ if (result) {
3248
+ view.render();
3249
+ }
3250
+ return;
3251
+ }
3252
+ const owner = this.owner;
3253
+ if (owner && typeof owner !== "number") {
3254
+ owner.mountFrame("mf_" + this.id, this.$view, this.$params);
3255
+ }
3256
+ },
3257
+ render() {
3258
+ const params = this.$params;
3259
+ this.updater.digest({
3260
+ skeleton: params?.["skeleton"]
3261
+ });
3262
+ this.updateView();
3263
+ },
3264
+ /**
3265
+ * Invoke a method on the remote view.
3266
+ * Usage: mf.invoke('callView', methodName, ...args)
3267
+ */
3268
+ callView(name, ...args) {
3269
+ const mf = Frame.get("mf_" + this.id);
3270
+ return mf?.invoke(name, args);
3271
+ }
3272
+ });
3273
+ var cross_site_default = CrossSite;
2886
3274
 
2887
3275
  // src/service.ts
2888
3276
  var Payload = class {
@@ -3070,29 +3458,31 @@ var Service = class {
3070
3458
  * Get metadata for an API endpoint.
3071
3459
  */
3072
3460
  static meta(attrs) {
3073
- const name = typeof attrs === "string" ? attrs : attrs["name"];
3074
- return this._metaList[name] || attrs;
3461
+ const name = typeof attrs === "string" ? attrs : String(attrs["name"] ?? "");
3462
+ const known = this._metaList[name];
3463
+ if (known) return known;
3464
+ return attrs;
3075
3465
  }
3076
3466
  /**
3077
3467
  * Create a Payload for an API request.
3078
3468
  */
3079
3469
  static create(attrs) {
3080
3470
  const meta = this.meta(attrs);
3081
- const cache = attrs["cache"] | 0 || meta.cache || 0;
3471
+ const cache = toCacheValue(attrs["cache"]) || meta.cache || 0;
3082
3472
  const entity = new Payload();
3083
3473
  entity.set(meta);
3084
3474
  entity.cacheInfo = {
3085
3475
  name: meta.name,
3086
- after: meta.after,
3087
- cleans: meta.cleanKeys,
3476
+ after: typeof meta.after === "function" ? meta.after : void 0,
3477
+ cleans: typeof meta.cleanKeys === "string" ? meta.cleanKeys : void 0,
3088
3478
  key: cache ? defaultCacheKey(meta, attrs) : "",
3089
3479
  time: 0
3090
3480
  };
3091
- if (typeof attrs === "object" && attrs !== null) {
3481
+ if (attrs !== null) {
3092
3482
  entity.set(attrs);
3093
3483
  }
3094
3484
  const before = meta.before;
3095
- if (before) {
3485
+ if (typeof before === "function") {
3096
3486
  funcWithTry(before, [entity], entity, noop);
3097
3487
  }
3098
3488
  this._staticEmitter.fire("begin", { payload: entity });
@@ -3118,7 +3508,7 @@ var Service = class {
3118
3508
  */
3119
3509
  static cached(attrs) {
3120
3510
  const meta = this.meta(attrs);
3121
- const cache = attrs["cache"] | 0 || meta.cache || 0;
3511
+ const cache = toCacheValue(attrs["cache"]) || meta.cache || 0;
3122
3512
  let cacheKey = "";
3123
3513
  if (cache) {
3124
3514
  cacheKey = defaultCacheKey(meta, attrs);
@@ -3126,7 +3516,8 @@ var Service = class {
3126
3516
  if (cacheKey) {
3127
3517
  const info = this._pendingCacheKeys[cacheKey];
3128
3518
  if (info) {
3129
- return info.entity;
3519
+ const entity = info.entity;
3520
+ return entity instanceof Payload ? entity : void 0;
3130
3521
  }
3131
3522
  const cached = this._payloadCache.get(cacheKey);
3132
3523
  if (cached && cached.cacheInfo) {
@@ -3143,17 +3534,14 @@ var Service = class {
3143
3534
  * Clear cached payloads by endpoint name.
3144
3535
  */
3145
3536
  static clear(names) {
3146
- const nameList = (typeof names === "string" ? names : names.join(",")).split(",");
3147
- const nameSet = {};
3148
- for (const n of nameList) {
3149
- nameSet[n] = 1;
3150
- }
3537
+ const nameSet = new Set(
3538
+ (typeof names === "string" ? names : names.join(",")).split(",")
3539
+ );
3151
3540
  const keysToDelete = [];
3152
3541
  this._payloadCache.forEach((payload) => {
3153
- if (payload?.cacheInfo && nameSet[payload.cacheInfo.name]) {
3154
- if (payload.cacheInfo.key) {
3155
- keysToDelete.push(payload.cacheInfo.key);
3156
- }
3542
+ const info = payload?.cacheInfo;
3543
+ if (info && info.key && nameSet.has(info.name)) {
3544
+ keysToDelete.push(info.key);
3157
3545
  }
3158
3546
  });
3159
3547
  for (const key of keysToDelete) {
@@ -3172,12 +3560,23 @@ var Service = class {
3172
3560
  }
3173
3561
  /**
3174
3562
  * Create a new Service subclass with a custom sync function.
3175
- * Each subclass gets its own per-type state (metaList, cache, etc.)
3176
- * to ensure isolation between different Service types.
3563
+ *
3564
+ * Each subclass gets its OWN copies of every per-type static field
3565
+ * (`_metaList`, `_payloadCache`, `_pendingCacheKeys`, `_syncFn`,
3566
+ * `_staticEmitter`, `_cacheMax`, `_cacheBuffer`) via `static override`.
3567
+ * This is intentional: it ensures that endpoint metadata, cache state,
3568
+ * in-flight dedup keys, and event subscribers are fully isolated between
3569
+ * different Service types, even when one extends another.
3570
+ *
3571
+ * **Do not refactor these `static override` declarations away** — sharing
3572
+ * them through prototype inheritance would let endpoints registered on one
3573
+ * subclass leak into another, and the LFU cache evictions of one type
3574
+ * would race with those of another.
3177
3575
  */
3178
3576
  static extend(newSyncFn, newCacheMax, newCacheBuffer) {
3179
3577
  const ParentService = this;
3180
3578
  class ChildService extends ParentService {
3579
+ // Intentionally per-subclass — see Service.extend doc.
3181
3580
  static _metaList = {};
3182
3581
  static _payloadCache = new Cache({
3183
3582
  maxSize: newCacheMax || ParentService._cacheMax,
@@ -3192,18 +3591,34 @@ var Service = class {
3192
3591
  return ChildService;
3193
3592
  }
3194
3593
  };
3594
+ var metaJsonCache = /* @__PURE__ */ new WeakMap();
3595
+ function getMetaJson(meta) {
3596
+ let cached = metaJsonCache.get(meta);
3597
+ if (cached === void 0) {
3598
+ cached = JSON.stringify(meta);
3599
+ metaJsonCache.set(meta, cached);
3600
+ }
3601
+ return cached;
3602
+ }
3195
3603
  function defaultCacheKey(meta, attrs) {
3196
- return JSON.stringify(attrs) + SPLITTER + JSON.stringify(meta);
3604
+ return JSON.stringify(attrs) + SPLITTER + getMetaJson(meta);
3605
+ }
3606
+ function toCacheValue(v) {
3607
+ if (typeof v === "number") return v | 0;
3608
+ if (typeof v === "string") {
3609
+ const n = Number(v);
3610
+ return Number.isFinite(n) ? n | 0 : 0;
3611
+ }
3612
+ return 0;
3197
3613
  }
3198
3614
  function serviceSend(service, attrs, done, flag, save) {
3199
- if (service["destroyed"]) return;
3200
- if (service["busy"]) {
3201
- service.enqueue(
3202
- serviceSend.bind(null, service, attrs, done, flag, save)
3203
- );
3615
+ if (service.destroyed) return;
3616
+ if (service.busy) {
3617
+ const queued = () => serviceSend(service, attrs, done, flag, save);
3618
+ service.enqueue(queued);
3204
3619
  return;
3205
3620
  }
3206
- service["busy"] = 1;
3621
+ service.busy = 1;
3207
3622
  let attrList;
3208
3623
  if (typeof attrs === "string") {
3209
3624
  attrList = [{ name: attrs }];
@@ -3228,10 +3643,10 @@ function serviceSend(service, attrs, done, flag, save) {
3228
3643
  newPayload = true;
3229
3644
  staticEmitter2.fire("done", { payload });
3230
3645
  }
3231
- if (!service["destroyed"]) {
3646
+ if (!service.destroyed) {
3232
3647
  const finish = requestCount === total;
3233
3648
  if (finish) {
3234
- service["busy"] = 0;
3649
+ service.busy = 0;
3235
3650
  if (flag === FETCH_FLAGS_ALL) {
3236
3651
  doneArr[0] = errorArgs;
3237
3652
  funcWithTry(done, doneArr, service, noop);
@@ -3248,10 +3663,7 @@ function serviceSend(service, attrs, done, flag, save) {
3248
3663
  for (const attr of attrList) {
3249
3664
  if (!attr) continue;
3250
3665
  const attrObj = typeof attr === "string" ? { name: attr } : attr;
3251
- const payloadInfo = service.type.get(
3252
- attrObj,
3253
- save
3254
- );
3666
+ const payloadInfo = service.type.get(attrObj, save);
3255
3667
  const payloadEntity = payloadInfo.entity;
3256
3668
  const cacheKey = payloadEntity.cacheInfo?.key || "";
3257
3669
  const complete = remoteComplete.bind(null, requestCount++);
@@ -3265,15 +3677,13 @@ function serviceSend(service, attrs, done, flag, save) {
3265
3677
  const cacheComplete = () => {
3266
3678
  const list = pendingCacheKeys[cacheKey];
3267
3679
  const entity = list.entity;
3268
- if (entity.cacheInfo) {
3680
+ if (entity instanceof Payload && entity.cacheInfo) {
3269
3681
  entity.cacheInfo.time = now();
3682
+ internals.payloadCache.set(cacheKey, entity);
3270
3683
  }
3271
- internals.payloadCache.set(cacheKey, entity);
3272
3684
  Reflect.deleteProperty(pendingCacheKeys, cacheKey);
3273
3685
  for (const cb of list) {
3274
- if (typeof cb === "function") {
3275
- cb();
3276
- }
3686
+ if (typeof cb === "function") cb();
3277
3687
  }
3278
3688
  };
3279
3689
  syncFn(payloadEntity, cacheComplete);
@@ -3286,13 +3696,32 @@ function serviceSend(service, attrs, done, flag, save) {
3286
3696
  }
3287
3697
  }
3288
3698
 
3289
- // src/frame-visualizer.ts
3290
- var MSG_PING = "LARK_VISUALIZER_PING";
3291
- var MSG_PONG = "LARK_VISUALIZER_PONG";
3292
- var MSG_REQUEST_TREE = "LARK_VISUALIZER_REQUEST_TREE";
3293
- var MSG_TREE = "LARK_VISUALIZER_TREE";
3294
- var MSG_TREE_DELTA = "LARK_VISUALIZER_TREE_DELTA";
3699
+ // src/frame-visual.ts
3700
+ var FrameVisualBridge = {
3701
+ MSG_PING: "LARK_VIS_PING",
3702
+ MSG_PONG: "LARK_VIS_PONG",
3703
+ MSG_REQUEST_TREE: "LARK_VIS_REQUEST_TREE",
3704
+ MSG_TREE: "LARK_VIS_TREE",
3705
+ MSG_TREE_DELTA: "LARK_VIS_TREE_DELTA"
3706
+ };
3295
3707
  function serializeView(view) {
3708
+ const evtMap = view.eventObjectMap;
3709
+ const eventMethodKeys = evtMap ? Object.keys(evtMap) : [];
3710
+ const resourceKeys = view.resources ? Object.keys(view.resources) : [];
3711
+ const lookup = view;
3712
+ const hasAssign = typeof lookup["assign"] === "function";
3713
+ let updaterData = null;
3714
+ try {
3715
+ const ref = view.updater?.refData;
3716
+ if (ref && typeof ref === "object") {
3717
+ updaterData = {};
3718
+ for (const k of Object.keys(ref)) {
3719
+ const v = ref[k];
3720
+ updaterData[k] = v === null || typeof v !== "object" ? v : `[${typeof v}]`;
3721
+ }
3722
+ }
3723
+ } catch {
3724
+ }
3296
3725
  return {
3297
3726
  id: view.id,
3298
3727
  rendered: !!view.rendered,
@@ -3303,7 +3732,11 @@ function serializeView(view) {
3303
3732
  keys: view.locationObserved.keys,
3304
3733
  observePath: view.locationObserved.observePath
3305
3734
  },
3306
- hasTemplate: !!view.template
3735
+ hasTemplate: !!view.template,
3736
+ eventMethodKeys,
3737
+ resourceKeys,
3738
+ hasAssign,
3739
+ updaterData
3307
3740
  };
3308
3741
  }
3309
3742
  function serializeFrame(frameId) {
@@ -3331,7 +3764,10 @@ function serializeFrame(frameId) {
3331
3764
  };
3332
3765
  }
3333
3766
  function serializeFrameTree() {
3334
- const root = Frame.root();
3767
+ const root = Frame.getRoot();
3768
+ if (!root) {
3769
+ return { root: null, totalFrames: 0, timestamp: Date.now(), rootId: "" };
3770
+ }
3335
3771
  const rootNode = serializeFrame(root.id);
3336
3772
  let totalFrames = 0;
3337
3773
  const countFrames = (node) => {
@@ -3359,19 +3795,22 @@ function installFrameVisualizerBridge() {
3359
3795
  const data = event.data;
3360
3796
  if (!data || typeof data !== "object") return;
3361
3797
  const type = data.type;
3362
- if (type === MSG_PING) {
3798
+ if (type === FrameVisualBridge.MSG_PING) {
3363
3799
  const source = event.source;
3364
3800
  if (source) {
3365
- source.postMessage({ type: MSG_PONG }, { targetOrigin: "*" });
3801
+ source.postMessage(
3802
+ { type: FrameVisualBridge.MSG_PONG },
3803
+ { targetOrigin: "*" }
3804
+ );
3366
3805
  }
3367
3806
  return;
3368
3807
  }
3369
- if (type === MSG_REQUEST_TREE) {
3808
+ if (type === FrameVisualBridge.MSG_REQUEST_TREE) {
3370
3809
  const tree = serializeFrameTree();
3371
3810
  const source = event.source;
3372
3811
  if (source) {
3373
3812
  source.postMessage(
3374
- { type: MSG_TREE, data: tree },
3813
+ { type: FrameVisualBridge.MSG_TREE, data: tree },
3375
3814
  { targetOrigin: "*" }
3376
3815
  );
3377
3816
  }
@@ -3390,18 +3829,14 @@ function pushTreeUpdate() {
3390
3829
  const treeJson = JSON.stringify(tree);
3391
3830
  if (treeJson !== lastTreeJson) {
3392
3831
  lastTreeJson = treeJson;
3393
- window.parent.postMessage({ type: MSG_TREE_DELTA, data: tree }, "*");
3832
+ window.parent.postMessage(
3833
+ { type: FrameVisualBridge.MSG_TREE_DELTA, data: tree },
3834
+ "*"
3835
+ );
3394
3836
  }
3395
3837
  }
3396
3838
 
3397
3839
  // src/framework.ts
3398
- var config = {
3399
- rootId: "root",
3400
- hashbang: "#!",
3401
- error: (error) => {
3402
- throw error;
3403
- }
3404
- };
3405
3840
  var booted3 = false;
3406
3841
  var taskList = [];
3407
3842
  var taskIndex = 0;
@@ -3450,6 +3885,9 @@ function task(fn, args, context) {
3450
3885
  }
3451
3886
  }
3452
3887
  var dispatcherUpdateTag = 0;
3888
+ function isThenable(value) {
3889
+ return !!value && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
3890
+ }
3453
3891
  function viewIsObserveChanged(view) {
3454
3892
  const loc = view.locationObserved;
3455
3893
  let result = false;
@@ -3463,7 +3901,7 @@ function viewIsObserveChanged(view) {
3463
3901
  const changedParams = lastChanged2?.params;
3464
3902
  if (changedParams) {
3465
3903
  for (const key of loc.keys) {
3466
- result = has(changedParams, key);
3904
+ result = hasOwnProperty(changedParams, key);
3467
3905
  if (result) break;
3468
3906
  }
3469
3907
  }
@@ -3475,48 +3913,59 @@ function stateIsObserveChanged(view, stateKeys) {
3475
3913
  const observedKeys = view.observedStateKeys;
3476
3914
  if (!observedKeys) return false;
3477
3915
  for (const key of observedKeys) {
3478
- if (has(stateKeys, key)) return true;
3916
+ if (stateKeys.has(key)) return true;
3479
3917
  }
3480
3918
  return false;
3481
3919
  }
3482
3920
  function dispatcherUpdate(frame, stateKeys) {
3483
- const frameInternal = frame;
3484
- const view = frame.view;
3485
- if (!view || frameInternal.dispatcherUpdateTag === dispatcherUpdateTag || view.signature <= 1) {
3486
- return;
3487
- }
3488
- frameInternal.dispatcherUpdateTag = dispatcherUpdateTag;
3489
- const isChanged = stateKeys ? stateIsObserveChanged(view, stateKeys) : viewIsObserveChanged(view);
3490
- let renderPromise;
3491
- if (isChanged) {
3492
- const renderResult = funcWithTry(
3493
- view.renderMethod ?? view.render,
3494
- [],
3495
- view,
3496
- noop
3497
- );
3498
- if (renderResult && typeof renderResult.then === "function") {
3499
- renderPromise = renderResult;
3500
- }
3501
- }
3502
- const children = frame.children();
3503
- const recurse = () => {
3504
- for (const childId of children) {
3505
- const childFrame = Frame.get(childId);
3506
- if (childFrame) {
3507
- dispatcherUpdate(childFrame, stateKeys);
3921
+ const stack = [frame];
3922
+ const drain = (s) => {
3923
+ while (s.length > 0) {
3924
+ const current = s.pop();
3925
+ const tagged = current;
3926
+ const view = current.view;
3927
+ if (!view || tagged.dispatcherUpdateTag === dispatcherUpdateTag || view.signature <= 1) {
3928
+ continue;
3929
+ }
3930
+ tagged.dispatcherUpdateTag = dispatcherUpdateTag;
3931
+ const isChanged = stateKeys ? stateIsObserveChanged(view, stateKeys) : viewIsObserveChanged(view);
3932
+ let renderPromise;
3933
+ if (isChanged) {
3934
+ const renderResult = funcWithTry(
3935
+ view.renderMethod ?? view.render,
3936
+ [],
3937
+ view,
3938
+ noop
3939
+ );
3940
+ if (isThenable(renderResult)) {
3941
+ renderPromise = renderResult;
3942
+ }
3943
+ }
3944
+ const children = current.children();
3945
+ if (renderPromise) {
3946
+ renderPromise.then(() => {
3947
+ const subStack = [];
3948
+ for (let i = children.length - 1; i >= 0; i--) {
3949
+ const child = Frame.get(children[i]);
3950
+ if (child) subStack.push(child);
3951
+ }
3952
+ drain(subStack);
3953
+ });
3954
+ } else {
3955
+ for (let i = children.length - 1; i >= 0; i--) {
3956
+ const child = Frame.get(children[i]);
3957
+ if (child) s.push(child);
3958
+ }
3508
3959
  }
3509
3960
  }
3510
3961
  };
3511
- if (renderPromise) {
3512
- renderPromise.then(recurse);
3513
- } else {
3514
- recurse();
3515
- }
3962
+ drain(stack);
3516
3963
  }
3517
3964
  function dispatcherNotifyChange(e) {
3518
- const rootFrame2 = Frame.root();
3519
- const view = e.view;
3965
+ const rootFrame2 = Frame.getRoot();
3966
+ if (!rootFrame2) return;
3967
+ const routeEvent = e;
3968
+ const view = routeEvent.view;
3520
3969
  if (view) {
3521
3970
  const viewPath = typeof view === "object" && view !== null ? String(view.to || "") : String(view);
3522
3971
  rootFrame2.mountView(viewPath);
@@ -3533,19 +3982,6 @@ function dispatchEvent(target, eventType, eventInit) {
3533
3982
  });
3534
3983
  target.dispatchEvent(event);
3535
3984
  }
3536
- function use(names, callback) {
3537
- if (!config.require) {
3538
- if (callback) callback();
3539
- return;
3540
- }
3541
- const nameList = typeof names === "string" ? [names] : names;
3542
- const result = config.require(nameList);
3543
- if (result && typeof result.then === "function") {
3544
- result.then((modules) => {
3545
- if (callback) callback(modules);
3546
- });
3547
- }
3548
- }
3549
3985
  var WAIT_OK = 1;
3550
3986
  var WAIT_TIMEOUT_OR_NOT_FOUND = 0;
3551
3987
  function waitZoneViewsRendered(viewId, timeout) {
@@ -3568,12 +4004,27 @@ function waitZoneViewsRendered(viewId, timeout) {
3568
4004
  setTimeout(check, 9);
3569
4005
  });
3570
4006
  }
4007
+ function getConfigImpl(key) {
4008
+ if (key === void 0) return config;
4009
+ return config[key];
4010
+ }
3571
4011
  var Framework = {
3572
4012
  // ============================================================
3573
4013
  // Lifecycle
3574
4014
  // ============================================================
4015
+ /** Read framework configuration. See `FrameworkInterface.getConfig`. */
4016
+ getConfig: getConfigImpl,
4017
+ /**
4018
+ * Merge a patch into framework configuration. See `FrameworkInterface.setConfig`.
4019
+ */
4020
+ setConfig(patch) {
4021
+ if (patch && typeof patch === "object") {
4022
+ assign(config, patch);
4023
+ }
4024
+ return config;
4025
+ },
3575
4026
  /**
3576
- * Get or set framework configuration.
4027
+ * @deprecated Use `getConfig()` / `setConfig()`. Behavior unchanged.
3577
4028
  */
3578
4029
  config(cfg) {
3579
4030
  if (!cfg) {
@@ -3594,17 +4045,17 @@ var Framework = {
3594
4045
  }
3595
4046
  Router._setConfig(config);
3596
4047
  EventDelegator.setFrameGetter((id) => Frame.get(id));
3597
- Router.on(ROUTER_EVENTS.CHANGED, (data) => {
4048
+ Router.on(RouterEvents.CHANGED, (data) => {
3598
4049
  if (data) dispatcherNotifyChange(data);
3599
4050
  });
3600
- State.on(ROUTER_EVENTS.CHANGED, (data) => {
4051
+ State.on(RouterEvents.CHANGED, (data) => {
3601
4052
  if (data) dispatcherNotifyChange(data);
3602
4053
  });
3603
4054
  booted3 = true;
3604
4055
  markBooted();
3605
4056
  markRouterBooted();
3606
4057
  installFrameVisualizerBridge();
3607
- const rootFrame2 = Frame.root(config.rootId);
4058
+ const rootFrame2 = Frame.createRoot(config.rootId);
3608
4059
  Router._bind();
3609
4060
  const defaultView = config.defaultView || "";
3610
4061
  if (defaultView && !rootFrame2.view) {
@@ -3659,7 +4110,7 @@ var Framework = {
3659
4110
  /**
3660
4111
  * Check if object has own property.
3661
4112
  */
3662
- has,
4113
+ has: hasOwnProperty,
3663
4114
  /**
3664
4115
  * Get object keys.
3665
4116
  */
@@ -3719,846 +4170,166 @@ if (typeof window !== "undefined") {
3719
4170
  window.__lark_Router = Router;
3720
4171
  window.__lark_Frame = Frame;
3721
4172
  window.__lark_View = View;
4173
+ window.__lark_invalidateViewClass = invalidateViewClass;
4174
+ window.__lark_getViewClassRegistry = getViewClassRegistry;
4175
+ window.__lark_registerViewClass = registerViewClass;
3722
4176
  }
3723
4177
 
3724
- // src/store.ts
3725
- var LARK_GLOBAL = "lark-global";
3726
- var Platform = /* @__PURE__ */ ((Platform2) => {
3727
- Platform2["Lark"] = "lark";
3728
- Platform2["React"] = "react";
3729
- Platform2["Node"] = "node";
3730
- return Platform2;
3731
- })(Platform || {});
3732
- var isFunction = (val) => typeof val === "function";
3733
- var isObject = (val) => val !== null && typeof val === "object";
3734
- var isPromise = (val) => isObject(val) && isFunction(val["then"]);
3735
- var hasOwnProperty = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
3736
- var deepClone = (obj) => {
3737
- if (!obj || !isObject(obj)) return {};
3738
- const newData = Array.isArray(obj) ? [] : Object.create(Object.getPrototypeOf(obj));
3739
- for (const key in obj) {
3740
- if (hasOwnProperty(obj, key)) {
3741
- const value = obj[key];
3742
- newData[key] = isObject(value) ? deepClone(value) : value;
3743
- }
3744
- }
3745
- return newData;
3746
- };
3747
- var cloneData = (data) => isObject(data) ? deepClone(data) : data;
3748
- var getDataByKey = (target, key) => {
3749
- let data;
3750
- const rec = target;
3751
- if (key.includes(".")) {
3752
- key.split(".").forEach((k, index) => {
3753
- data = index === 0 ? rec?.[k] : data?.[k];
3754
- });
3755
- } else {
3756
- data = rec?.[key];
3757
- }
3758
- return data;
3759
- };
3760
- var Queue = class {
3761
- pendingTasks = /* @__PURE__ */ new Set();
3762
- queue = [];
3763
- flushTasks() {
3764
- const { pendingTasks, queue } = this;
3765
- const flushTickTask = () => {
3766
- while (queue.length > 0) {
3767
- const task2 = queue.shift();
3768
- if (task2) {
3769
- pendingTasks.delete(task2);
3770
- runTask(task2);
3771
- }
3772
- }
3773
- };
3774
- Promise.resolve().then(flushTickTask);
3775
- }
3776
- add(tasks) {
3777
- const isQueueEmpty = this.queue.length === 0;
3778
- for (const { cb, params } of tasks) {
3779
- addParams2Callback(cb, params);
3780
- if (!this.pendingTasks.has(cb)) {
3781
- this.queue.push(cb);
3782
- this.pendingTasks.add(cb);
3783
- }
4178
+ // src/url-state.ts
4179
+ function useUrlState(view, initialState) {
4180
+ const keys2 = initialState ? Object.keys(initialState) : [];
4181
+ if (keys2.length > 0) {
4182
+ view.observeLocation(keys2);
4183
+ }
4184
+ const getState = () => {
4185
+ const loc = Router.parse();
4186
+ const result = { ...initialState || {} };
4187
+ for (const key of keys2) {
4188
+ const val = loc.get(key);
4189
+ if (val) result[key] = val;
3784
4190
  }
3785
- if (isQueueEmpty) this.flushTasks();
3786
- }
3787
- delete(tasks) {
3788
- if (this.pendingTasks.size === 0) return;
3789
- for (const { cb } of tasks) {
3790
- if (this.pendingTasks.has(cb)) {
3791
- this.pendingTasks.delete(cb);
3792
- const index = this.queue.findIndex((item) => item === cb);
3793
- if (index !== -1) this.queue.splice(index, 1);
3794
- }
3795
- }
3796
- }
3797
- clear() {
3798
- this.queue = [];
3799
- this.pendingTasks.clear();
3800
- }
3801
- };
3802
- var addParams2Callback = (cb, params) => {
3803
- if (!cb || !params) return;
3804
- const cbObj = cb;
3805
- if (isObject(cb) && isObject(cbObj["params"])) {
3806
- Object.assign(cbObj["params"], params);
3807
- } else {
3808
- cbObj["params"] = params;
3809
- }
3810
- };
3811
- var runTask = (cb) => {
3812
- const cbObj = cb;
3813
- const params = cbObj["params"];
3814
- delete cbObj["params"];
3815
- try {
3816
- cb(params);
3817
- } catch {
3818
- }
3819
- };
3820
- var getDefScheduler = () => new Queue();
3821
- var run = (tasks, scheduler) => {
3822
- if (scheduler) {
3823
- if (isFunction(scheduler)) {
3824
- for (const { cb, params } of tasks) scheduler(cb, params);
3825
- } else {
3826
- scheduler.add(tasks);
3827
- }
3828
- } else {
3829
- for (const { cb, params } of tasks) cb(params);
3830
- }
3831
- };
3832
- var ArrMethods = {};
3833
- ["indexOf", "lastIndexOf", "includes"].forEach((key) => {
3834
- const rawMethod = Array.prototype[key];
3835
- ArrMethods[key] = function(...args) {
3836
- let res = rawMethod.apply(this, args);
3837
- if (res === -1 || res === false) {
3838
- res = rawMethod.apply(
3839
- this,
3840
- args.map((item) => ProxyCache.get(item) ?? item)
3841
- );
3842
- }
3843
- return res;
3844
- };
3845
- });
3846
- var inArrUpdate = false;
3847
- ["unshift", "shift", "push", "pop", "splice"].forEach((key) => {
3848
- const rawMethod = Array.prototype[key];
3849
- ArrMethods[key] = function(...args) {
3850
- inArrUpdate = true;
3851
- const result = rawMethod.apply(this, args);
3852
- inArrUpdate = false;
3853
4191
  return result;
3854
4192
  };
3855
- });
3856
- function needKeepArrItem(target, newVal, key) {
3857
- if (!Array.isArray(target) || !inArrUpdate) return false;
3858
- return getLinkKeys(newVal) === createLinkKeys(target, key);
3859
- }
3860
- var defStateConfig = {
3861
- belong: LARK_GLOBAL,
3862
- linkKeys: "",
3863
- shallow: false
3864
- };
3865
- var StateConfigMap = /* @__PURE__ */ new WeakMap();
3866
- var setStateConfig = (target, config2) => {
3867
- if (target && isObject(config2)) StateConfigMap.set(target, config2);
3868
- };
3869
- var getStateConfig = (target, key) => {
3870
- if (!StateConfigMap.has(target)) {
3871
- return void 0;
3872
- }
3873
- const config2 = StateConfigMap.get(target);
3874
- return key ? config2?.[key] : config2;
3875
- };
3876
- var isState = (target) => isObject(target) && StateConfigMap.has(target);
3877
- var createLinkKeys = (target, property) => {
3878
- if (!hasLinkKeys(target)) return property;
3879
- if (Array.isArray(target)) return getLinkKeys(target) ?? property;
3880
- return `${getLinkKeys(target)}.${property}`;
3881
- };
3882
- var formatLinkKeys = (target, key) => {
3883
- const originKeysStr = getLinkKeys(target);
3884
- if (!originKeysStr) return null;
3885
- const linkKeys = [];
3886
- const originKeys = originKeysStr.split(".");
3887
- let pre = "";
3888
- for (const k of originKeys) {
3889
- const curr = pre ? `${pre}.${k}` : k;
3890
- linkKeys.push(curr);
3891
- pre = curr;
3892
- }
3893
- linkKeys.push(`${originKeysStr}.${key}`);
3894
- return linkKeys;
3895
- };
3896
- var getLinkKeys = (target) => getStateConfig(target, "linkKeys");
3897
- var hasLinkKeys = (target) => !!getLinkKeys(target);
3898
- var ProxyCache = /* @__PURE__ */ new WeakMap();
3899
- var keepKey = null;
3900
- var keep = (key) => {
3901
- if (keepKey) throw new Error("[lark-store] keepKey is not null");
3902
- keepKey = key;
3903
- };
3904
- var needKeep = (key) => {
3905
- if (keepKey === key) {
3906
- keepKey = null;
3907
- return true;
3908
- }
3909
- return false;
3910
- };
3911
- var canSetNewVal = (params) => {
3912
- const { property, newVal, oldVal } = params;
3913
- if (hasOwnProperty(params.target, property) && newVal === oldVal)
3914
- return false;
3915
- return true;
3916
- };
3917
- var getNewVal = (params) => {
3918
- const { target, newVal, property, needKeepVal = false } = params;
3919
- const config2 = getStateConfig(target);
3920
- if (!isObject(newVal)) return newVal;
3921
- if (needKeepArrItem(target, newVal, property)) return newVal;
3922
- if (config2?.shallow) return newVal;
3923
- if (needKeepVal) return newVal;
3924
- const linkKeys = createLinkKeys(target, property);
3925
- const newState = createState(newVal, {
3926
- ...config2,
3927
- linkKeys
3928
- });
3929
- if (isPromise(newVal)) handlePromise(newVal, target, property);
3930
- return newState;
3931
- };
3932
- var genPayload = (params) => {
3933
- const { target, property, newVal, oldVal } = params;
3934
- const config2 = getStateConfig(target) || defStateConfig;
3935
- return {
3936
- belong: config2.belong || LARK_GLOBAL,
3937
- target,
3938
- keys: formatLinkKeys(target, property) || [property],
3939
- newVal,
3940
- oldVal
3941
- };
3942
- };
3943
- var handlePromise = (child, parent, key) => {
3944
- child.then((res) => {
3945
- const parentProxy = ProxyCache.get(parent);
3946
- if (parentProxy) {
3947
- const oldVal = parentProxy[key];
3948
- if (ProxyCache.get(child) === oldVal) {
3949
- parentProxy[key] = res;
3950
- }
3951
- }
3952
- }).catch((err) => {
3953
- const parentProxy = ProxyCache.get(parent);
3954
- if (parentProxy) {
3955
- const oldVal = parentProxy[key];
3956
- if (ProxyCache.get(child) === oldVal) {
3957
- parentProxy[key] = err;
3958
- }
3959
- }
3960
- });
3961
- };
3962
- var _deleteKey = "_delete";
3963
- var _markObjKey = "_markObj";
3964
- var mark2 = (host, key) => {
3965
- let sign;
3966
- if (!host[_deleteKey]) {
3967
- const markHost = host[_markObjKey] || (host[_markObjKey] = {});
3968
- if (!hasOwnProperty(markHost, key)) {
3969
- markHost[key] = 0;
3970
- }
3971
- sign = ++markHost[key];
3972
- }
3973
- return () => {
3974
- const temp = host[_markObjKey];
3975
- return !!temp && sign === temp[key];
4193
+ const setState = (patch) => {
4194
+ const current = getState();
4195
+ const resolved = typeof patch === "function" ? patch(current) : patch;
4196
+ Router.to(resolved);
3976
4197
  };
3977
- };
3978
- var unmark2 = (host) => {
3979
- host[_deleteKey] = 1;
3980
- host[_markObjKey] = 0;
3981
- };
3982
- function createState(initialData, config2) {
3983
- if (!isObject(initialData)) return initialData;
3984
- const mergedConfig = Object.assign({ ...defStateConfig }, config2);
3985
- const data = Array.isArray(initialData) ? [] : Object.create(Object.getPrototypeOf(initialData));
3986
- const handler = {
3987
- get(target, property, _receiver) {
3988
- if (Array.isArray(target) && property in ArrMethods) {
3989
- return ArrMethods[property];
3990
- }
3991
- return Reflect.get(target, property);
3992
- },
3993
- set(target, property, newVal, receiver) {
3994
- const strProp = property;
3995
- const oldVal = Reflect.get(target, property);
3996
- const preventTrigger = isLazySet(strProp);
3997
- const needKeepVal = needKeep(strProp);
3998
- const canSet = canSetNewVal({
3999
- target,
4000
- property: strProp,
4001
- newVal,
4002
- oldVal
4003
- });
4004
- if (!canSet) return true;
4005
- const proxyTarget = receiver || state;
4006
- newVal = getNewVal({
4007
- target: proxyTarget,
4008
- property: strProp,
4009
- newVal,
4010
- needKeepVal
4011
- });
4012
- Reflect.set(target, property, newVal, receiver);
4013
- if (preventTrigger) return true;
4014
- const payload = genPayload({
4015
- target: proxyTarget,
4016
- property: strProp,
4017
- newVal,
4018
- oldVal
4019
- });
4020
- trigger(payload);
4021
- return true;
4022
- },
4023
- deleteProperty(target, property) {
4024
- const strProp = property;
4025
- if (!hasOwnProperty(target, strProp)) return true;
4026
- const oldVal = Reflect.get(target, property);
4027
- Reflect.deleteProperty(target, property);
4028
- const payload = genPayload({
4029
- target: state,
4030
- property: strProp,
4031
- newVal: null,
4032
- oldVal
4033
- });
4034
- trigger(payload);
4035
- return true;
4036
- }
4037
- };
4038
- const state = new Proxy(data, handler);
4039
- ProxyCache.set(initialData, state);
4040
- setStateConfig(state, mergedConfig);
4041
- lazySet(state, initialData);
4042
- return state;
4043
- }
4044
- var curLazySetKey = null;
4045
- function lazySet(target, data) {
4046
- if (isObject(data)) {
4047
- Reflect.ownKeys(data).forEach((key) => {
4048
- const strKey = key;
4049
- if (curLazySetKey) throw new Error("[lark-store] lazy set key conflict");
4050
- curLazySetKey = strKey;
4051
- target[strKey] = data[strKey];
4052
- });
4053
- }
4198
+ return [getState(), setState];
4054
4199
  }
4055
- function isLazySet(property) {
4056
- if (curLazySetKey === property) {
4057
- curLazySetKey = "";
4058
- return true;
4059
- }
4060
- return false;
4200
+
4201
+ // src/store.ts
4202
+ var COMPUTED_BRAND = /* @__PURE__ */ Symbol("lark-store-computed");
4203
+ function isComputedMarker(val) {
4204
+ return val !== null && typeof val === "object" && val[COMPUTED_BRAND] === true;
4061
4205
  }
4062
- function shallowSet(target, key, data) {
4063
- if (!isState(target))
4064
- throw new Error("[lark-store] shallowSet only supports state objects");
4065
- if (!isObject(data)) return target[key] = data;
4066
- keep(key);
4067
- const config2 = getStateConfig(target);
4068
- const linkKeys = createLinkKeys(target, key);
4069
- target[key] = createState(data, {
4070
- ...config2,
4071
- linkKeys,
4072
- shallow: true
4073
- });
4206
+ function computed(deps, fn) {
4207
+ return { [COMPUTED_BRAND]: true, deps, fn };
4074
4208
  }
4075
- var GlobalDeps = /* @__PURE__ */ new Map();
4076
- function track(payload) {
4077
- const { belong, trackList = [] } = payload;
4078
- let deps = GlobalDeps.get(belong);
4079
- if (!deps) GlobalDeps.set(belong, deps = /* @__PURE__ */ new Map());
4080
- for (const { key, cb } of trackList) {
4081
- if (isFunction(cb)) {
4082
- let callbacks = deps.get(key);
4083
- if (!callbacks) deps.set(key, callbacks = /* @__PURE__ */ new Set());
4084
- callbacks.add(cb);
4085
- }
4086
- }
4087
- }
4088
- function trigger(payload) {
4089
- const { belong, keys: keys2 } = payload;
4090
- const store = getStore(belong);
4091
- if (store && store.status !== 2 /* ACTIVE */ && belong !== LARK_GLOBAL)
4092
- return;
4093
- const deps = GlobalDeps.get(belong);
4094
- if (!deps) return;
4095
- const tasks = /* @__PURE__ */ new Set();
4096
- for (const key of keys2) {
4097
- const callbacks = deps.get(key);
4098
- if (callbacks) {
4099
- for (const cb of callbacks) {
4100
- tasks.add({ cb, params: { [key]: true } });
4101
- }
4102
- }
4103
- }
4104
- if (tasks.size === 0) return;
4105
- const scheduler = store?.scheduler;
4106
- run(Array.from(tasks), scheduler);
4107
- }
4108
- function clear(payload) {
4109
- if (!payload) {
4110
- GlobalDeps.clear();
4111
- return;
4112
- }
4113
- const { belong, clearList } = payload;
4114
- const deps = GlobalDeps.get(belong);
4115
- if (!deps) return;
4116
- const store = getStore(belong);
4117
- const scheduler = store?.scheduler;
4118
- if (clearList) {
4119
- for (const { key, cb } of clearList) {
4120
- const callbacks = deps.get(key);
4121
- if (callbacks) {
4122
- if (!cb) {
4123
- deps.delete(key);
4124
- } else if (callbacks.has(cb)) {
4125
- callbacks.delete(cb);
4126
- if (scheduler && !isFunction(scheduler)) {
4127
- scheduler.delete([{ cb }]);
4128
- }
4129
- if (callbacks.size === 0) deps.delete(key);
4209
+ var storeRegistry = /* @__PURE__ */ new Map();
4210
+ function create(name, creator) {
4211
+ const listeners = /* @__PURE__ */ new Set();
4212
+ const computedDefs = /* @__PURE__ */ new Map();
4213
+ const computedKeys = /* @__PURE__ */ new Set();
4214
+ const actionKeys = /* @__PURE__ */ new Set();
4215
+ let state;
4216
+ let destroyed = false;
4217
+ const getState = () => state;
4218
+ const setState = (partial) => {
4219
+ if (destroyed) return;
4220
+ const prevState = state;
4221
+ const resolved = typeof partial === "function" ? partial(prevState) : partial;
4222
+ const nextState = { ...prevState };
4223
+ let changed = false;
4224
+ for (const key in resolved) {
4225
+ if (Object.prototype.hasOwnProperty.call(resolved, key) && !computedKeys.has(key) && !actionKeys.has(key)) {
4226
+ const newVal = resolved[key];
4227
+ if (!Object.is(prevState[key], newVal)) {
4228
+ nextState[key] = newVal;
4229
+ changed = true;
4130
4230
  }
4131
4231
  }
4132
4232
  }
4133
- } else {
4134
- deps.clear();
4135
- GlobalDeps.delete(belong);
4136
- if (scheduler && !isFunction(scheduler)) {
4137
- scheduler.clear();
4138
- }
4139
- }
4140
- }
4141
- var _storeName = /* @__PURE__ */ Symbol("store-name");
4142
- var _storeStatus = /* @__PURE__ */ Symbol("store-status");
4143
- var _storeScheduler = /* @__PURE__ */ Symbol("store-scheduler");
4144
- var _storeCreate = /* @__PURE__ */ Symbol("fn:store-create");
4145
- var _storeBoot = /* @__PURE__ */ Symbol("fn:store-boot");
4146
- var _storeDestroy = /* @__PURE__ */ Symbol("fn:store-destroy");
4147
- var _innerStore = /* @__PURE__ */ Symbol("inner-store");
4148
- var _outerStore = /* @__PURE__ */ Symbol("outer-store");
4149
- var _originState = /* @__PURE__ */ Symbol("origin-state");
4150
- var _stateKeys = /* @__PURE__ */ Symbol("state-keys");
4151
- var _storeState = /* @__PURE__ */ Symbol("store-state");
4152
- var _storeDefScheduler = /* @__PURE__ */ Symbol("store-def-scheduler");
4153
- var _storeProxy = /* @__PURE__ */ Symbol("fn:store-proxy");
4154
- var BaseStore = class {
4155
- [_storeStatus] = 0 /* BEFORE_CREATE */;
4156
- [_storeDefScheduler] = getDefScheduler;
4157
- [_storeBoot]() {
4158
- this[_storeStatus] = 2 /* ACTIVE */;
4159
- return this[_storeProxy](true);
4160
- }
4161
- [_storeDestroy]() {
4162
- clear({ belong: this[_storeName] });
4163
- this[_storeState] = createState(this[_originState], {
4164
- belong: this[_storeName]
4165
- });
4166
- this[_storeStatus] = 3 /* DESTROYED */;
4167
- }
4168
- /**
4169
- *
4170
- * @param body - The object returned by the creator function
4171
- * @param excludeFns - Function keys to exclude from handlers (e.g. ['observe'])
4172
- */
4173
- [_storeCreate](body, excludeFns = ["observe"]) {
4174
- this[_storeStatus] = 1 /* CREATED */;
4175
- if (isObject(body)) {
4176
- const state = {};
4177
- const handlers = {};
4178
- Reflect.ownKeys(body).forEach((key) => {
4179
- const strKey = key;
4180
- const val = body[strKey];
4181
- if (isFunction(val)) {
4182
- if (!excludeFns.includes(strKey)) handlers[strKey] = val;
4183
- } else {
4184
- state[strKey] = val;
4185
- }
4186
- });
4187
- Object.assign(this, handlers);
4188
- this[_originState] = cloneData(state);
4189
- this[_stateKeys] = Object.keys(state);
4190
- this[_storeState] = createState(state, { belong: this[_storeName] });
4233
+ if (!changed) return;
4234
+ state = nextState;
4235
+ recomputeIfNeeded(prevState);
4236
+ for (const listener of listeners) {
4237
+ listener(state, prevState);
4191
4238
  }
4192
- }
4193
- constructor(name, config2) {
4194
- this[_storeName] = name;
4195
- this[_storeScheduler] = config2?.scheduler || this[_storeDefScheduler]();
4196
- this[_outerStore] = this[_storeProxy](true);
4197
- }
4198
- [_innerStore]() {
4199
- return this[_storeProxy]();
4200
- }
4201
- get status() {
4202
- return this[_storeStatus];
4203
- }
4204
- get storeName() {
4205
- return this[_storeName];
4206
- }
4207
- get scheduler() {
4208
- return this[_storeScheduler];
4209
- }
4210
- [_storeProxy](toOut = false) {
4211
- const self = this;
4212
- return new Proxy(self, {
4213
- get(target, property) {
4214
- if (self[_stateKeys].includes(property)) {
4215
- const val = self[_storeState][property];
4216
- return toOut ? cloneData(val) : val;
4217
- }
4218
- return Reflect.get(target, property);
4219
- },
4220
- set(_target, property, val) {
4221
- if (toOut) return true;
4222
- if (self[_stateKeys].includes(property)) {
4223
- self[_storeState][property] = val;
4224
- }
4225
- return true;
4226
- },
4227
- has(target, property) {
4228
- return Reflect.has(target, property) || self[_stateKeys].includes(property);
4239
+ };
4240
+ const recomputeIfNeeded = (prevState) => {
4241
+ if (computedDefs.size === 0) return;
4242
+ const changedKeys2 = /* @__PURE__ */ new Set();
4243
+ for (const key of Object.keys(state)) {
4244
+ if (!Object.is(
4245
+ state[key],
4246
+ prevState[key]
4247
+ )) {
4248
+ changedKeys2.add(key);
4229
4249
  }
4230
- });
4231
- }
4232
- };
4233
- var LarkUtils = {
4234
- isLarkView(instance) {
4235
- return isObject(instance) && !!instance.updater;
4236
- },
4237
- getRender(view) {
4238
- return view.updater.digest.bind(view.updater);
4239
- },
4240
- getDataSetter(view) {
4241
- return view.updater.set.bind(view.updater);
4242
- },
4243
- onDestroy(view, cb) {
4244
- view.on("destroy", cb);
4245
- }
4246
- };
4247
- var getLarkAdapter = (storeName) => ({
4248
- Store: LarkStore,
4249
- useStore: ((view) => {
4250
- const store = getStore(storeName);
4251
- return store[_storeBoot](view);
4252
- })
4253
- });
4254
- var innerObserveFlags = /* @__PURE__ */ Symbol("store-inner-observe-flags");
4255
- var boundViews = /* @__PURE__ */ Symbol("bound-views");
4256
- var LarkStore = class extends BaseStore {
4257
- [boundViews] = /* @__PURE__ */ new Set();
4258
- [innerObserveFlags] = /* @__PURE__ */ new Set();
4259
- [_storeBoot](view) {
4260
- if (view && LarkUtils.isLarkView(view) && !this[boundViews].has(view)) {
4261
- this[boundViews].add(view);
4262
- LarkUtils.onDestroy(view, () => {
4263
- this[boundViews].delete(view);
4264
- });
4265
4250
  }
4266
- return super[_storeBoot]();
4267
- }
4268
- [_storeDestroy]() {
4269
- this[boundViews].clear();
4270
- this[innerObserveFlags].clear();
4271
- super[_storeDestroy]();
4272
- }
4273
- observe(view, keys2, defCallback) {
4274
- if (this[_storeStatus] !== 2 /* ACTIVE */) return noop;
4275
- let observeKeys = keys2;
4276
- const _view = view;
4277
- const renderFn = _view ? LarkUtils.getRender(_view) : noop;
4278
- const dateSetterFn = _view ? LarkUtils.getDataSetter(_view) : noop;
4279
- const isInnerObserve = !view;
4280
- const innerFlags = /* @__PURE__ */ new Set();
4281
- const storeInnerObserveFlags = this[innerObserveFlags];
4282
- if (isFunction(keys2)) {
4283
- const res = keys2();
4284
- if (Array.isArray(res)) observeKeys = res;
4285
- }
4286
- const defSetter = (immediate, key, alias, transform) => () => {
4287
- const stateVal = getDataByKey(this, key);
4288
- let data = { [alias || key]: stateVal };
4289
- if (transform && isFunction(transform)) {
4290
- const newData = transform(stateVal);
4291
- if (isObject(newData)) data = newData;
4292
- }
4293
- if (immediate) dateSetterFn(data);
4294
- else renderFn(data);
4295
- };
4296
- const getList = (immediate = false) => {
4297
- const list = [];
4298
- for (const item of observeKeys) {
4299
- if (!item) continue;
4300
- const payload = typeof item === "string" ? { key: item } : item;
4301
- const { cb: cbDefault = defCallback, key } = payload;
4302
- let cb = cbDefault;
4303
- const { alias, lazy = true, transform } = payload;
4304
- if (!key) continue;
4305
- const c1 = !immediate;
4306
- const c2 = !cb && !!_view;
4307
- const c3 = !!cb && String(lazy) === "false";
4308
- if (!(c1 || c2 || c3)) continue;
4309
- if (isInnerObserve && cb) {
4310
- const flag = `storeInner_${key}_${observeKeys.join("-")}_${cb.toString()}`;
4311
- if (!storeInnerObserveFlags.has(flag)) {
4312
- storeInnerObserveFlags.add(flag);
4313
- innerFlags.add(flag);
4314
- }
4251
+ let recomputed = false;
4252
+ for (const [key, def] of computedDefs) {
4253
+ if (def.deps.some((dep) => changedKeys2.has(dep))) {
4254
+ const newVal = def.fn();
4255
+ if (!Object.is(state[key], newVal)) {
4256
+ state[key] = newVal;
4257
+ recomputed = true;
4315
4258
  }
4316
- if (c2) cb = defSetter(immediate, key, alias, transform);
4317
- if (cb) list.push({ key, cb });
4318
4259
  }
4319
- return list;
4320
- };
4321
- const trackList = getList();
4322
- track({ belong: this[_storeName], trackList });
4323
- if (_view) {
4324
- LarkUtils.onDestroy(
4325
- _view,
4326
- () => clear({ belong: this[_storeName], clearList: trackList })
4327
- );
4328
4260
  }
4329
- if (!isInnerObserve) run(getList(true));
4261
+ if (recomputed) {
4262
+ }
4263
+ };
4264
+ const subscribe = (listener) => {
4265
+ listeners.add(listener);
4330
4266
  return () => {
4331
- clear({ belong: this[_storeName], clearList: trackList });
4332
- innerFlags.forEach((flag) => storeInnerObserveFlags.delete(flag));
4267
+ listeners.delete(listener);
4333
4268
  };
4334
- }
4335
- };
4336
- var getReactAdapter = (storeName) => ({
4337
- Store: ReactStore,
4338
- useStore: (() => {
4339
- const store = getStore(storeName);
4340
- return store[_storeBoot]();
4341
- })
4342
- });
4343
- var observeSym = /* @__PURE__ */ Symbol("observe");
4344
- var getLastState = /* @__PURE__ */ Symbol("get-last-state");
4345
- var ReactStore = class extends BaseStore {
4346
- stateChangeCount = 0;
4347
- lastCount = 0;
4348
- lastState = null;
4349
- constructor(name, config2) {
4350
- const effectiveConfig = config2 && !config2.scheduler ? {
4351
- ...config2,
4352
- scheduler: (cb, params) => cb(params)
4353
- } : config2;
4354
- super(name, effectiveConfig);
4355
- }
4356
- [_storeCreate](body) {
4357
- super[_storeCreate](body);
4358
- this[observeSym](() => {
4359
- this.stateChangeCount += 1;
4360
- });
4361
- }
4362
- isStateChanged() {
4363
- const currCount = this.stateChangeCount;
4364
- const changed = this.lastCount !== currCount;
4365
- this.lastCount = currCount;
4366
- return changed;
4367
- }
4368
- [getLastState](handlers) {
4369
- if (this.isStateChanged() || !this.lastState) {
4370
- const state = this[_storeState];
4371
- const immutableState = freezeData(state);
4372
- this.lastState = Object.assign({}, handlers, immutableState);
4373
- }
4374
- return this.lastState;
4375
- }
4376
- [observeSym](cb) {
4377
- const tasks = this[_stateKeys].map((key) => ({ key, cb }));
4378
- track({ belong: this[_storeName], trackList: tasks });
4379
- return () => clear({ belong: this[_storeName], clearList: tasks });
4380
- }
4381
- };
4382
- var freezeData = (target) => {
4383
- const data = {};
4384
- const keys2 = Object.keys(target);
4385
- for (const key of keys2) {
4386
- const value = target[key];
4387
- if (Array.isArray(value)) {
4388
- data[key] = value.map(
4389
- (item) => isObject(item) ? freezeData(item) : item
4390
- );
4391
- } else if (isObject(value)) {
4392
- data[key] = freezeData(value);
4269
+ };
4270
+ const destroy = () => {
4271
+ destroyed = true;
4272
+ listeners.clear();
4273
+ storeRegistry.delete(name);
4274
+ };
4275
+ const api = { getState, setState, subscribe, destroy };
4276
+ const body = creator(setState, getState);
4277
+ const initialState = {};
4278
+ const actions = {};
4279
+ for (const key of Object.keys(body)) {
4280
+ const val = body[key];
4281
+ if (isComputedMarker(val)) {
4282
+ computedDefs.set(key, val);
4283
+ computedKeys.add(key);
4284
+ initialState[key] = void 0;
4285
+ } else if (typeof val === "function") {
4286
+ actions[key] = val;
4287
+ actionKeys.add(key);
4393
4288
  } else {
4394
- data[key] = value;
4289
+ initialState[key] = val;
4395
4290
  }
4396
4291
  }
4397
- return data;
4398
- };
4399
- var getNodeAdapter = (storeName) => ({
4400
- Store: NodeStore,
4401
- useStore: (() => {
4402
- const store = getStore(storeName);
4403
- return store[_storeBoot]();
4404
- })
4405
- });
4406
- var NodeStore = class extends BaseStore {
4407
- observe(key, callback, immediate = true) {
4408
- const tasks = [{ key, cb: callback }];
4409
- track({ belong: this[_storeName], trackList: tasks });
4410
- if (immediate) run(tasks);
4411
- return () => clear({ belong: this[_storeName], clearList: tasks });
4412
- }
4413
- };
4414
- var getAdapter = (platform, storeName) => {
4415
- switch (platform) {
4416
- case "react" /* React */:
4417
- return getReactAdapter(storeName);
4418
- case "node" /* Node */:
4419
- return getNodeAdapter(storeName);
4420
- case "lark" /* Lark */:
4421
- default:
4422
- return getLarkAdapter(storeName);
4423
- }
4424
- };
4425
- var getPlatform = (comp) => {
4426
- if (LarkUtils.isLarkView(comp)) return "lark" /* Lark */;
4427
- return void 0;
4428
- };
4429
- var extendApis = { lazySet, shallowSet };
4430
- var StoreCache = /* @__PURE__ */ new Map();
4431
- function defineStore(name, creator, config2) {
4432
- if (StoreCache.has(name)) {
4433
- name = name + "_copy";
4434
- }
4435
- const { platform = "lark" /* Lark */ } = config2 || {};
4436
- const adapter = getAdapter(platform, name);
4437
- const StoreClass = adapter.Store;
4438
- const useStore = adapter.useStore;
4439
- const store = new StoreClass(name, config2);
4440
- store[_storeCreate](
4441
- creator(store[_innerStore](), extendApis)
4442
- );
4443
- Object.defineProperties(useStore, {
4444
- $storeName: { value: name, configurable: true },
4445
- $destroyFn: { value: () => store[_storeDestroy](), configurable: true }
4446
- });
4447
- if (!StoreCache.has(name)) {
4448
- StoreCache.set(name, { store, creator, config: config2, useStore });
4449
- }
4450
- return useStore;
4451
- }
4452
- function getStore(name) {
4453
- if (name && StoreCache.has(name)) {
4454
- const entry = StoreCache.get(name);
4455
- return entry ? entry.store : void 0;
4456
- }
4457
- return void 0;
4458
- }
4459
- function delStore(name) {
4460
- if (name && StoreCache.has(name)) StoreCache.delete(name);
4461
- }
4462
- function getUseStore(name) {
4463
- if (name && StoreCache.has(name)) {
4464
- const entry = StoreCache.get(name);
4465
- return entry ? entry.useStore : void 0;
4292
+ state = { ...initialState, ...actions };
4293
+ for (const [key, def] of computedDefs) {
4294
+ state[key] = def.fn();
4466
4295
  }
4467
- return void 0;
4468
- }
4469
- function cloneStore(name, useStore, config2) {
4470
- const oldStoreName = useStore["$storeName"];
4471
- const cached = StoreCache.get(oldStoreName);
4472
- const oldStoreCreator = cached?.creator;
4473
- const oldConfig = cached?.config || {};
4474
- if (!name || !oldStoreCreator) return;
4475
- const mergedConfig = { ...oldConfig, ...config2 || {} };
4476
- return defineStore(
4477
- name,
4478
- oldStoreCreator,
4479
- mergedConfig
4480
- );
4296
+ storeRegistry.set(name, api);
4297
+ return api;
4481
4298
  }
4482
- function isStoreActive(name) {
4483
- if (name && StoreCache.has(name)) {
4484
- const entry = StoreCache.get(name);
4485
- return entry ? entry.store.status === 2 /* ACTIVE */ : false;
4486
- }
4487
- return false;
4488
- }
4489
- var cellCount = 0;
4490
- function cell(data) {
4491
- return createState(data, {
4492
- belong: LARK_GLOBAL,
4493
- linkKeys: `${LARK_GLOBAL}_${cellCount++}`
4494
- });
4495
- }
4496
- function observeCell(state, cb, immediate = true) {
4497
- const linkKeys = getLinkKeys(state);
4498
- if (!linkKeys)
4499
- return () => {
4500
- };
4501
- const keys2 = linkKeys.split(".");
4502
- const key = keys2[keys2.length - 1];
4503
- const list = [{ key, cb }];
4504
- track({ belong: LARK_GLOBAL, trackList: list });
4505
- if (immediate) cb();
4506
- return () => clear({ belong: LARK_GLOBAL, clearList: list });
4299
+ function isLarkView(instance) {
4300
+ if (!instance || typeof instance !== "object") return false;
4301
+ const obj = instance;
4302
+ const updater = obj["updater"];
4303
+ return updater !== null && typeof updater === "object" && typeof updater["set"] === "function" && typeof updater["digest"] === "function";
4507
4304
  }
4508
- function multi(useStore) {
4509
- const storeName = useStore["$storeName"];
4510
- const flagSym = `lark-comp-${storeName}`;
4511
- const map = /* @__PURE__ */ new Map();
4512
- let rootViewPath;
4513
- const getFlag = (viewContext) => {
4514
- const owner = viewContext["owner"];
4515
- const viewPath = owner?.["path"] || "";
4516
- const viewId = owner?.["id"] || "";
4517
- let flag;
4518
- if (viewPath === rootViewPath) {
4519
- flag = `${flagSym}-${viewId}`;
4520
- } else {
4521
- flag = owner?.["viewInitParams"]?.[flagSym];
4522
- }
4523
- if (owner && isFunction(owner["mountFrame"])) {
4524
- const rawMountFrame = owner["mountFrame"];
4525
- owner["mountFrame"] = (vfId, viewPath2, viewInitParams = {}) => rawMountFrame.call(
4526
- owner,
4527
- vfId,
4528
- viewPath2,
4529
- Object.assign(viewInitParams, { [flagSym]: flag })
4530
- );
4531
- }
4532
- return flag;
4305
+ function bindStore(view, store, selector) {
4306
+ if (!isLarkView(view)) return () => {
4533
4307
  };
4534
- const useFn = ((view) => {
4535
- if (!view)
4536
- throw new Error(
4537
- "[@lark.js/mvc error] multi: cannot find the view instance"
4538
- );
4539
- const viewCtx = view;
4540
- const flag = viewCtx[flagSym];
4541
- if (map.has(flag)) return map.get(flag);
4542
- const newFn = cloneStore(flag, useStore);
4543
- map.set(flag, newFn);
4544
- return useFn(view);
4545
- });
4546
- const mixinObj = {
4547
- make() {
4548
- if (!rootViewPath) {
4549
- const owner = this["owner"];
4550
- rootViewPath = owner?.["path"] || "";
4308
+ const extract = (s) => {
4309
+ if (selector) return selector(s);
4310
+ const result = {};
4311
+ for (const key in s) {
4312
+ if (Object.prototype.hasOwnProperty.call(s, key) && typeof s[key] !== "function") {
4313
+ result[key] = s[key];
4551
4314
  }
4552
- this[flagSym] = getFlag(this);
4553
4315
  }
4316
+ return result;
4554
4317
  };
4555
- return [useFn, mixinObj];
4318
+ view.updater.set(extract(store.getState()));
4319
+ view.updater.digest();
4320
+ const off = store.subscribe((state) => {
4321
+ view.updater.set(extract(state));
4322
+ view.updater.digest();
4323
+ });
4324
+ view.on("destroy", off);
4325
+ return off;
4556
4326
  }
4327
+ var defineStore = create;
4557
4328
 
4558
4329
  // src/compiler.ts
4559
4330
  var import_parser = require("@babel/parser");
4560
- var SPLITTER2 = "";
4561
- var VIEW_ID_PLACEHOLDER = "";
4331
+ var SPLITTER2 = String.fromCharCode(30);
4332
+ var VIEW_ID_PLACEHOLDER = String.fromCharCode(31);
4562
4333
  function jsObjectToUrlParams(paramsStr) {
4563
4334
  const trimmed = paramsStr.trim();
4564
4335
  if (!/^[{[]/.test(trimmed) && /=/.test(trimmed)) {
@@ -4618,7 +4389,7 @@ function addLineMarkers(source) {
4618
4389
  if (parts.length > 1) {
4619
4390
  const reconstructed = parts.map((part, i) => {
4620
4391
  if (i === 0) return part;
4621
- return openTag + SPLITTER2 + ++lineNo;
4392
+ return openTag + SPLITTER2 + ++lineNo + part;
4622
4393
  }).join("");
4623
4394
  result.push(reconstructed);
4624
4395
  } else {
@@ -4754,7 +4525,7 @@ function convertArtExpression(code, debug, lineNo, blockStack = []) {
4754
4525
  return `${debugPrefix}<%for(${forExpr}){%>`;
4755
4526
  }
4756
4527
  const tokens = code.split(/\s+/);
4757
- const keyword = tokens.shift();
4528
+ const keyword = tokens.shift() ?? "";
4758
4529
  switch (keyword) {
4759
4530
  case "if": {
4760
4531
  blockStack.push({ ctrl: "if", line: lineNo });
@@ -4978,13 +4749,9 @@ function compileToFunction(source, debug, file) {
4978
4749
  }
4979
4750
  const viewIdRegExp = new RegExp(String.fromCharCode(31), "g");
4980
4751
  funcSource = funcSource.replace(viewIdRegExp, `'+$viewId+'`);
4981
- const atRule = hasAtRule ? `if(!$refFn){$refFn=(ref,v,k,f)=>{for(f=ref[$splitter];--f;)if(ref[k=$splitter+f]===v)return k;ref[k=$splitter+ref[$splitter]++]=v;return k;}}` : "";
4982
- const encode = `if(!$strSafe){let $entMap={'&':'amp','<':'lt','>':'gt','"':'#34','\\'':'#39','\`':'#96'},$entReg=/[&<>"'\`]/g,$entFn=m=>'&'+$entMap[m]+';';$strSafe=v=>''+(v==null?'':v);$encHtml=v=>$strSafe(v).replace($entReg,$entFn)}`;
4983
- const encodeURIMore = `if(!$encUri){let $uriMap={'!':'%21','\\'':'%27','(':'%28',')':'%29','*':'%2A'},$uriFn=m=>$uriMap[m],$uriReg=/[!')(*]/g;$encUri=v=>encodeURIComponent($strSafe(v)).replace($uriReg,$uriFn)}`;
4984
- const encodeQuote = `if(!$encQuote){let $qReg=/['"\\\\]/g;$encQuote=v=>$strSafe(v).replace($qReg,'\\\\$&')}`;
4752
+ void hasAtRule;
4985
4753
  const refFallback = "if(!$refAlt)$refAlt=$data;";
4986
- const fns = `${refFallback}${encode}${encodeURIMore}${encodeQuote}${atRule};`;
4987
- const fullSource = `${fns}let $splitter='\\x1e',$tmp,$out=''{{VARS}};${funcSource}return $out`;
4754
+ const fullSource = `${refFallback}let $splitter='\\x1e',$tmp,$out=''{{VARS}};${funcSource}return $out`;
4988
4755
  return `($data,$viewId,$refAlt,$encHtml,$strSafe,$encUri,$refFn,$encQuote)=>{${fullSource}}`;
4989
4756
  }
4990
4757
  function compileTemplate(source, options = {}) {
@@ -4996,15 +4763,12 @@ function compileTemplate(source, options = {}) {
4996
4763
  const funcBody = compileToFunction(finalSource, debug, file);
4997
4764
  const varDeclarations = globalVars.map((key) => `,${key}=$data.${key}`).join("");
4998
4765
  const funcWithVars = funcBody.replace("{{VARS}}", () => varDeclarations);
4999
- return `export default function(data, selfId, refData) {
4766
+ return `import { encHtml as __larkEncHtml, strSafe as __larkStrSafe, encUri as __larkEncUri, encQuote as __larkEncQuote, refFn as __larkRefFn } from "@lark.js/mvc/runtime";
4767
+ export default function(data, viewId, refData) {
5000
4768
  let $data = data || {},
5001
- $viewId = selfId || '';
4769
+ $viewId = viewId || '';
5002
4770
  return (${funcWithVars})($data, $viewId, refData,
5003
- /* $encHtml */ v => String(v == null ? '' : v).replace(/[&<>"'\`]/g, m => '&' + ({'&':'amp','<':'lt','>':'gt','"':'#34',"'":'#39','\`':'#96'})[m] + ';'),
5004
- /* $strSafe */ v => String(v == null ? '' : v),
5005
- /* $encUri */ null,
5006
- /* $refFn */ null,
5007
- /* $encQuote */ null
4771
+ __larkEncHtml, __larkStrSafe, __larkEncUri, __larkRefFn, __larkEncQuote
5008
4772
  );
5009
4773
  }`;
5010
4774
  }
@@ -5018,7 +4782,7 @@ function extractGlobalVars(source) {
5018
4782
  const htmlStore = {};
5019
4783
  let htmlIndex = 0;
5020
4784
  let lastIndex = 0;
5021
- const htmlKey = "";
4785
+ const htmlKey = String.fromCharCode(5);
5022
4786
  template.replace(
5023
4787
  templateCmdRegExp,
5024
4788
  (match, operate, content, offset) => {
@@ -5129,31 +4893,37 @@ function walkAst(ast, visitors) {
5129
4893
  if (visitors[type]) {
5130
4894
  visitors[type](node);
5131
4895
  }
4896
+ const bag = node;
5132
4897
  for (const key of Object.keys(node)) {
5133
4898
  if (key === "type" || key === "start" || key === "end" || key === "loc" || key === "range")
5134
4899
  continue;
5135
- if (type === "MemberExpression" && key === "property" && !node.computed)
5136
- continue;
5137
- if (type === "ObjectProperty" && key === "key" && !node.computed) {
5138
- continue;
4900
+ if (type === "MemberExpression" && key === "property") {
4901
+ const me = node;
4902
+ if (!me.computed) continue;
5139
4903
  }
5140
- if (type === "ObjectMethod" && key === "key" && !node.computed) {
5141
- continue;
4904
+ if (type === "ObjectProperty" && key === "key") {
4905
+ const op = node;
4906
+ if (!op.computed) continue;
4907
+ }
4908
+ if (type === "ObjectMethod" && key === "key") {
4909
+ const om = node;
4910
+ if (!om.computed) continue;
5142
4911
  }
5143
- const child = node[key];
4912
+ const child = bag[key];
5144
4913
  if (Array.isArray(child)) {
5145
4914
  for (const item of child) {
5146
- if (item && typeof item === "object" && typeof item.type === "string") {
5147
- visit(item);
5148
- }
4915
+ if (isAstNode(item)) visit(item);
5149
4916
  }
5150
- } else if (child && typeof child === "object" && typeof child.type === "string") {
4917
+ } else if (isAstNode(child)) {
5151
4918
  visit(child);
5152
4919
  }
5153
4920
  }
5154
4921
  }
5155
4922
  visit(ast);
5156
4923
  }
4924
+ function isAstNode(v) {
4925
+ return !!v && typeof v === "object" && typeof v.type === "string";
4926
+ }
5157
4927
  var BUILTIN_GLOBALS = {
5158
4928
  // ─── Template runtime helpers (injected by compileToFunction) ───────
5159
4929
  //
@@ -5308,14 +5078,15 @@ var BUILTIN_GLOBAL_SET = new Set(Object.keys(BUILTIN_GLOBALS));
5308
5078
  0 && (module.exports = {
5309
5079
  CALL_BREAK_TIME,
5310
5080
  Cache,
5081
+ CrossSite,
5311
5082
  EVENT_METHOD_REGEXP,
5312
5083
  EventDelegator,
5313
5084
  EventEmitter,
5314
5085
  Frame,
5086
+ FrameVisualBridge,
5315
5087
  Framework,
5316
5088
  LARK_VIEW,
5317
5089
  Payload,
5318
- Platform,
5319
5090
  ROUTER_EVENTS,
5320
5091
  Router,
5321
5092
  SPLITTER,
@@ -5329,59 +5100,52 @@ var BUILTIN_GLOBAL_SET = new Set(Object.keys(BUILTIN_GLOBALS));
5329
5100
  applyStyle,
5330
5101
  applyVdomOps,
5331
5102
  assign,
5332
- cell,
5333
- classExtend,
5334
- cloneData,
5335
- cloneStore,
5103
+ bindStore,
5336
5104
  compileTemplate,
5337
- createState,
5105
+ computed,
5106
+ create,
5338
5107
  createVdomRef,
5339
5108
  defineStore,
5340
- delStore,
5109
+ defineView,
5341
5110
  encodeHTML,
5342
5111
  encodeQ,
5343
5112
  encodeSafe,
5344
5113
  encodeURIExtra,
5345
5114
  ensureElementId,
5346
5115
  extractGlobalVars,
5116
+ frameworkConfig,
5347
5117
  funcWithTry,
5348
5118
  generateId,
5349
5119
  getAttribute,
5350
5120
  getById,
5351
- getPlatform,
5352
- getStore,
5353
- getUseStore,
5354
- has,
5121
+ getRouteMode,
5122
+ hasOwnProperty,
5355
5123
  installFrameVisualizerBridge,
5124
+ invalidateViewClass,
5356
5125
  isPlainObject,
5357
5126
  isPrimitive,
5358
5127
  isPrimitiveOrFunc,
5359
- isState,
5360
- isStoreActive,
5361
5128
  keys,
5362
- lazySet,
5363
5129
  mark,
5364
5130
  markBooted,
5365
5131
  markRouterBooted,
5366
- multi,
5367
5132
  nextCounter,
5368
5133
  nodeInside,
5369
5134
  noop,
5370
5135
  now,
5371
- observeCell,
5372
5136
  parseUri,
5373
5137
  registerViewClass,
5138
+ resetProjectsMap,
5374
5139
  safeguard,
5375
5140
  serializeFrameTree,
5376
5141
  setData,
5377
- shallowSet,
5378
- storeMark,
5379
- storeUnmark,
5380
5142
  syncCounter,
5381
5143
  toMap,
5382
5144
  toUri,
5383
5145
  translateData,
5384
5146
  unmark,
5147
+ use,
5148
+ useUrlState,
5385
5149
  vdomGetCompareKey,
5386
5150
  vdomGetNode,
5387
5151
  vdomSetAttributes,