@lark.js/mvc 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  // src/constants.ts
2
2
  var globalCounter = 0;
3
3
  var SPLITTER = "";
4
- var ROUTER_EVENTS = {
4
+ var RouterEvents = {
5
5
  CHANGE: "change",
6
6
  CHANGED: "changed",
7
7
  PAGE_UNLOAD: "page_unload"
8
8
  };
9
- var LARK_KEYS = {
9
+ var LarkInnerKeys = {
10
10
  /** Attribute name: ldk (static key for skipping VDOM diff) */
11
11
  DIFF_KEY: "ldk",
12
12
  /** Attribute name: lak (static attribute key) */
@@ -53,13 +53,13 @@ function syncCounter(val) {
53
53
  }
54
54
  function noop() {
55
55
  }
56
- function has(owner, prop) {
56
+ function hasOwnProperty(owner, prop) {
57
57
  return owner != null && Object.prototype.hasOwnProperty.call(owner, prop);
58
58
  }
59
59
  function keys(obj) {
60
60
  const result = [];
61
61
  for (const p in obj) {
62
- if (has(obj, p)) {
62
+ if (hasOwnProperty(obj, p)) {
63
63
  result.push(p);
64
64
  }
65
65
  }
@@ -69,7 +69,7 @@ function assign(target, ...sources) {
69
69
  for (const source of sources) {
70
70
  if (source) {
71
71
  for (const p in source) {
72
- if (has(source, p)) {
72
+ if (hasOwnProperty(source, p)) {
73
73
  target[p] = source[p];
74
74
  }
75
75
  }
@@ -82,21 +82,22 @@ function funcWithTry(fns, args, context, configError) {
82
82
  let ret;
83
83
  for (const fn of fnArray) {
84
84
  try {
85
- ret = Function.prototype.apply.call(fn, context, args);
85
+ ret = fn.apply(context, args);
86
86
  } catch (e) {
87
87
  configError?.(e);
88
88
  }
89
89
  }
90
90
  return ret;
91
91
  }
92
+ var EMPTY_STRING_SET = /* @__PURE__ */ new Set();
92
93
  function setData(newData, oldData, changedKeys2, excludes) {
93
94
  let changed = false;
94
95
  for (const p in newData) {
95
- if (has(newData, p)) {
96
+ if (hasOwnProperty(newData, p)) {
96
97
  const now2 = newData[p];
97
98
  const old = oldData[p];
98
99
  if ((!isPrimitiveOrFunc(now2) || old !== now2) && !excludes.has(p)) {
99
- changedKeys2[p] = 1;
100
+ changedKeys2.add(p);
100
101
  changed = true;
101
102
  }
102
103
  oldData[p] = now2;
@@ -104,17 +105,25 @@ function setData(newData, oldData, changedKeys2, excludes) {
104
105
  }
105
106
  return changed;
106
107
  }
108
+ function isRefToken(s) {
109
+ if (s.length < 2 || s[0] !== SPLITTER) return false;
110
+ for (let i = 1; i < s.length; i++) {
111
+ const c = s.charCodeAt(i);
112
+ if (c < 48 || c > 57) return false;
113
+ }
114
+ return true;
115
+ }
107
116
  function translateData(data, value) {
108
117
  if (isPrimitive(value)) {
109
118
  const prop = String(value);
110
- if (prop[0] === SPLITTER && has(data, prop)) {
119
+ if (isRefToken(prop) && hasOwnProperty(data, prop)) {
111
120
  return data[prop];
112
121
  }
113
122
  return value;
114
123
  }
115
124
  if (isPlainObject(value) || Array.isArray(value)) {
116
125
  for (const p in value) {
117
- if (has(value, p)) {
126
+ if (hasOwnProperty(value, p)) {
118
127
  const val = value[p];
119
128
  const newVal = translateData(data, val);
120
129
  value[p] = newVal;
@@ -151,38 +160,33 @@ function nodeInside(a, b) {
151
160
  return false;
152
161
  }
153
162
  }
154
- var paramsTemp = {};
155
- function paramsReplacer(_match, name, value) {
156
- try {
157
- paramsTemp[name] = decodeURIComponent(value || "");
158
- } catch {
159
- paramsTemp[name] = value || "";
160
- }
161
- return "";
162
- }
163
163
  function parseUri(uri) {
164
- paramsTemp = {};
164
+ const params = {};
165
165
  const path = uri.replace(URL_QUERY_HASH_REGEXP, "");
166
166
  const pathname = path;
167
167
  const actualPath = uri === pathname && IS_URL_PARAMS.test(pathname) ? "" : pathname;
168
- uri.replace(actualPath, "").replace(URL_PARAM_REGEXP, paramsReplacer);
169
- return {
170
- path: actualPath,
171
- params: { ...paramsTemp }
172
- };
168
+ uri.replace(actualPath, "").replace(URL_PARAM_REGEXP, (_match, name, value) => {
169
+ try {
170
+ params[name] = decodeURIComponent(value || "");
171
+ } catch {
172
+ params[name] = value || "";
173
+ }
174
+ return "";
175
+ });
176
+ return { path: actualPath, params };
173
177
  }
174
178
  var IS_URL_PARAMS = {
175
179
  test(s) {
176
180
  return /(?!^)=|&/.test(s);
177
181
  }
178
182
  };
179
- function toUri(path, params, keepEmptyObject) {
183
+ function toUri(path, params, keepEmpty) {
180
184
  const pairs = [];
181
185
  let hasParams = false;
182
186
  for (const p in params) {
183
- if (has(params, p)) {
187
+ if (hasOwnProperty(params, p)) {
184
188
  const v = String(params[p] ?? "");
185
- if (!keepEmptyObject || v || has(keepEmptyObject, p)) {
189
+ if (!keepEmpty || v || keepEmpty.has(p)) {
186
190
  pairs.push(`${p}=${encodeURIComponent(v)}`);
187
191
  hasParams = true;
188
192
  }
@@ -205,14 +209,6 @@ function toMap(list, key) {
205
209
  function now() {
206
210
  return Date.now ? Date.now() : (/* @__PURE__ */ new Date()).getTime();
207
211
  }
208
- function classExtend(make, base, props, statics) {
209
- const baseProto = base["prototype"] ?? {};
210
- const classProto = Object.create(baseProto);
211
- assign(classProto, props);
212
- Object.assign(make, statics);
213
- classProto.constructor = make;
214
- make["prototype"] = classProto;
215
- }
216
212
 
217
213
  // src/apply-style.ts
218
214
  var injectedStyleIds = /* @__PURE__ */ new Set();
@@ -252,27 +248,35 @@ function applyStyle(styleIdOrPairs, css) {
252
248
  }
253
249
 
254
250
  // src/mark.ts
255
- var DELETED_KEY = SPLITTER + "$delFlag";
256
- var MARK_OBJECT_KEY = SPLITTER + "$markKey";
251
+ var hostStore = /* @__PURE__ */ new WeakMap();
252
+ function getOrCreate(host) {
253
+ let record = hostStore.get(host);
254
+ if (!record) {
255
+ record = { signs: /* @__PURE__ */ new Map(), deleted: false };
256
+ hostStore.set(host, record);
257
+ }
258
+ return record;
259
+ }
257
260
  function mark(host, key) {
258
- let sign = 0;
259
- const hostRecord = host;
260
- if (!hostRecord[DELETED_KEY]) {
261
- const markHost = hostRecord[MARK_OBJECT_KEY] || (hostRecord[MARK_OBJECT_KEY] = {});
262
- if (!Object.prototype.hasOwnProperty.call(markHost, key)) {
263
- markHost[key] = 0;
264
- }
265
- sign = ++markHost[key];
261
+ const record = getOrCreate(host);
262
+ if (record.deleted) {
263
+ return () => false;
266
264
  }
265
+ const sign = (record.signs.get(key) ?? 0) + 1;
266
+ record.signs.set(key, sign);
267
267
  return () => {
268
- const temp = hostRecord[MARK_OBJECT_KEY];
269
- return !!(temp && sign === temp[key]);
268
+ const current = hostStore.get(host);
269
+ return !!current && !current.deleted && current.signs.get(key) === sign;
270
270
  };
271
271
  }
272
272
  function unmark(host) {
273
- const hostRecord = host;
274
- hostRecord[MARK_OBJECT_KEY] = 0;
275
- hostRecord[DELETED_KEY] = 1;
273
+ const record = hostStore.get(host);
274
+ if (record) {
275
+ record.deleted = true;
276
+ record.signs.clear();
277
+ } else {
278
+ hostStore.set(host, { signs: /* @__PURE__ */ new Map(), deleted: true });
279
+ }
276
280
  }
277
281
 
278
282
  // src/safeguard.ts
@@ -318,7 +322,7 @@ function safeguard(data, getter, setter, isRoot) {
318
322
  if (!prefix && getter) {
319
323
  getter(property);
320
324
  }
321
- if (!isRoot && has(target, property) && (Array.isArray(out) || isPlainObject(out))) {
325
+ if (!isRoot && hasOwnProperty(target, property) && (Array.isArray(out) || isPlainObject(out))) {
322
326
  return build(prefix + property + ".", out);
323
327
  }
324
328
  return out;
@@ -407,15 +411,17 @@ var Cache = class {
407
411
  this.lookup.set(prefixedKey, entry);
408
412
  }
409
413
  /**
410
- * Delete a cached entry.
414
+ * Delete a cached entry. Removes it immediately from both the lookup map
415
+ * and the entries array so the GC can reclaim the value without waiting
416
+ * for the next eviction sweep.
411
417
  */
412
418
  del(key) {
413
419
  const prefixedKey = this.prefixKey(key);
414
420
  const entry = this.lookup.get(prefixedKey);
415
421
  if (!entry) return;
416
- entry.frequency = -1;
417
- entry.value = void 0;
418
422
  this.lookup.delete(prefixedKey);
423
+ const idx = this.entries.indexOf(entry);
424
+ if (idx !== -1) this.entries.splice(idx, 1);
419
425
  if (this.onRemove) {
420
426
  this.onRemove(key);
421
427
  }
@@ -440,20 +446,46 @@ var Cache = class {
440
446
  this.entries = [];
441
447
  this.lookup.clear();
442
448
  }
443
- /** Evict least-used entries from cache */
449
+ /**
450
+ * Evict the `bufferSize` worst entries from the cache.
451
+ *
452
+ * Uses single-pass partial selection (O(n·k)) instead of sorting the entire
453
+ * `entries` array (O(n log n)). For the typical `bufferSize = 5` this is
454
+ * effectively a linear scan with at most 5 in-bucket comparisons per
455
+ * iteration — and it avoids mutating the rest of `entries`.
456
+ */
444
457
  evictEntries() {
445
- this.entries.sort(this.comparator);
446
- let count = this.bufferSize;
447
- while (count-- > 0 && this.entries.length > 0) {
448
- const entry = this.entries.pop();
449
- if (entry && entry.frequency > 0) {
450
- this.lookup.delete(this.prefixKey(entry.originalKey));
451
- if (this.onRemove) {
452
- this.onRemove(entry.originalKey);
453
- }
458
+ const entries = this.entries;
459
+ const k = this.bufferSize;
460
+ if (k <= 0 || entries.length === 0) return;
461
+ if (entries.length <= k) {
462
+ for (const e of entries) {
463
+ this.lookup.delete(this.prefixKey(e.originalKey));
464
+ if (this.onRemove) this.onRemove(e.originalKey);
454
465
  }
466
+ this.entries = [];
467
+ return;
468
+ }
469
+ const cmp = this.comparator;
470
+ const worst = [];
471
+ for (const entry of entries) {
472
+ if (worst.length < k) {
473
+ let i = worst.length;
474
+ while (i > 0 && cmp(entry, worst[i - 1]) > 0) i--;
475
+ worst.splice(i, 0, entry);
476
+ } else if (cmp(entry, worst[k - 1]) > 0) {
477
+ worst.pop();
478
+ let i = worst.length;
479
+ while (i > 0 && cmp(entry, worst[i - 1]) > 0) i--;
480
+ worst.splice(i, 0, entry);
481
+ }
482
+ }
483
+ const evictSet = new Set(worst);
484
+ for (const e of worst) {
485
+ this.lookup.delete(this.prefixKey(e.originalKey));
486
+ if (this.onRemove) this.onRemove(e.originalKey);
455
487
  }
456
- this.entries = this.entries.filter((e) => e.frequency !== -1);
488
+ this.entries = entries.filter((e) => !evictSet.has(e));
457
489
  }
458
490
  };
459
491
 
@@ -461,6 +493,10 @@ var Cache = class {
461
493
  var EventEmitter = class {
462
494
  /** Event listeners: prefixed key -> listener array */
463
495
  listeners = /* @__PURE__ */ new Map();
496
+ /** Number of `fire()` calls currently on the stack (re-entrancy depth). */
497
+ firingDepth = 0;
498
+ /** Keys whose listener list needs compaction after firing settles. */
499
+ pendingCompaction;
464
500
  /**
465
501
  * Bind event listener.
466
502
  */
@@ -483,13 +519,23 @@ var EventEmitter = class {
483
519
  const key = SPLITTER + event;
484
520
  if (handler) {
485
521
  const list = this.listeners.get(key);
486
- if (list) {
522
+ if (!list) return this;
523
+ if (this.firingDepth > 0) {
487
524
  for (const listener of list) {
488
525
  if (listener.handler === handler) {
489
526
  listener.handler = noop;
527
+ (this.pendingCompaction ??= /* @__PURE__ */ new Set()).add(key);
528
+ break;
529
+ }
530
+ }
531
+ } else {
532
+ for (let i = 0; i < list.length; i++) {
533
+ if (list[i].handler === handler) {
534
+ list.splice(i, 1);
490
535
  break;
491
536
  }
492
537
  }
538
+ if (list.length === 0) this.listeners.delete(key);
493
539
  }
494
540
  } else {
495
541
  this.listeners.delete(key);
@@ -501,9 +547,9 @@ var EventEmitter = class {
501
547
  return this;
502
548
  }
503
549
  /**
504
- * Fire event, execute all bound handlers.
505
- * Supports executing state management: handlers removed during
506
- * execution are safely handled.
550
+ * Fire event, execute all bound handlers. Safe for re-entrant `off()` calls
551
+ * during dispatch: removed handlers are replaced with noop and compacted
552
+ * after the outermost fire returns.
507
553
  *
508
554
  * @param event - Event name
509
555
  * @param data - Event data (type property added automatically)
@@ -517,38 +563,41 @@ var EventEmitter = class {
517
563
  data = {};
518
564
  }
519
565
  data["type"] = event;
520
- if (list) {
521
- let end = list.length;
522
- const len = end - 1;
523
- while (end--) {
524
- const idx = lastToFirst ? end : len - end;
525
- const listener = list[idx];
526
- if (listener.handler !== noop) {
566
+ this.firingDepth++;
567
+ try {
568
+ if (list) {
569
+ const len = list.length;
570
+ for (let i = 0; i < len; i++) {
571
+ const idx = lastToFirst ? len - 1 - i : i;
572
+ const listener = list[idx];
573
+ if (!listener) continue;
574
+ if (listener.handler === noop) continue;
527
575
  listener.executing = 1;
528
- funcWithTry(
529
- [listener.handler],
530
- [data],
531
- this,
532
- noop
533
- );
576
+ funcWithTry([listener.handler], [data], this, noop);
534
577
  listener.executing = "";
535
- } else if (!listener.executing) {
536
- list.splice(idx, 1);
537
578
  }
538
579
  }
539
- }
540
- const onMethodName = `on${event[0].toUpperCase() + event.slice(1)}`;
541
- const onMethod = this[onMethodName];
542
- if (typeof onMethod === "function") {
543
- funcWithTry(
544
- [onMethod],
545
- [data],
546
- this,
547
- noop
548
- );
549
- }
550
- if (remove) {
551
- this.off(event);
580
+ const onMethodName = `on${event[0].toUpperCase() + event.slice(1)}`;
581
+ const onMethod = this[onMethodName];
582
+ if (typeof onMethod === "function") {
583
+ funcWithTry([onMethod], [data], this, noop);
584
+ }
585
+ if (remove) {
586
+ this.off(event);
587
+ }
588
+ } finally {
589
+ this.firingDepth--;
590
+ if (this.firingDepth === 0 && this.pendingCompaction) {
591
+ for (const k of this.pendingCompaction) {
592
+ const l = this.listeners.get(k);
593
+ if (!l) continue;
594
+ for (let i = l.length - 1; i >= 0; i--) {
595
+ if (l[i].handler === noop) l.splice(i, 1);
596
+ }
597
+ if (l.length === 0) this.listeners.delete(k);
598
+ }
599
+ this.pendingCompaction = void 0;
600
+ }
552
601
  }
553
602
  return this;
554
603
  }
@@ -557,8 +606,8 @@ var EventEmitter = class {
557
606
  // src/state.ts
558
607
  var appData = {};
559
608
  var keyRefCounts = {};
560
- var changedKeys = {};
561
- var stashedChangedKeys = {};
609
+ var changedKeys = /* @__PURE__ */ new Set();
610
+ var stashedChangedKeys = EMPTY_STRING_SET;
562
611
  var dataIsChanged = false;
563
612
  var dataWhereSet = {};
564
613
  var emitter = new EventEmitter();
@@ -569,7 +618,7 @@ function markBooted() {
569
618
  function setupKeysRef(keys2) {
570
619
  const keyList = keys2.split(",");
571
620
  for (const key of keyList) {
572
- if (has(keyRefCounts, key)) {
621
+ if (hasOwnProperty(keyRefCounts, key)) {
573
622
  keyRefCounts[key]++;
574
623
  } else {
575
624
  keyRefCounts[key] = 1;
@@ -579,7 +628,7 @@ function setupKeysRef(keys2) {
579
628
  }
580
629
  function teardownKeysRef(keyList) {
581
630
  for (const key of keyList) {
582
- if (has(keyRefCounts, key)) {
631
+ if (hasOwnProperty(keyRefCounts, key)) {
583
632
  const count = --keyRefCounts[key];
584
633
  if (count <= 0) {
585
634
  Reflect.deleteProperty(keyRefCounts, key);
@@ -591,35 +640,14 @@ function teardownKeysRef(keyList) {
591
640
  }
592
641
  }
593
642
  }
594
- var notifyStarted = 0;
595
- var notifyList = [];
596
- var notifyTimer;
643
+ var warnedKeys = /* @__PURE__ */ new Set();
597
644
  function clearNotify(key) {
598
- for (let i = notifyList.length; i--; ) {
599
- if (notifyList[i]?.key === key) {
600
- notifyList.splice(i, 1);
601
- }
602
- }
603
- }
604
- function doNotify() {
605
- const locker = {};
606
- for (const n of notifyList) {
607
- if (!locker[n.key]) {
608
- console.warn(n.message);
609
- locker[n.key] = 1;
610
- }
611
- }
612
- notifyList.length = 0;
613
- notifyStarted = 0;
645
+ warnedKeys.delete(key);
614
646
  }
615
647
  function delayNotify(key, message) {
616
- clearTimeout(notifyTimer);
617
- notifyStarted = 0;
618
- notifyList.push({ key, message });
619
- if (!notifyStarted) {
620
- notifyStarted = 1;
621
- notifyTimer = setTimeout(doNotify, 500);
622
- }
648
+ if (warnedKeys.has(key)) return;
649
+ warnedKeys.add(key);
650
+ console.warn(message);
623
651
  }
624
652
  var State = {
625
653
  /**
@@ -631,7 +659,7 @@ var State = {
631
659
  return safeguard(
632
660
  result,
633
661
  (dataKey) => {
634
- if (booted && has(dataWhereSet, dataKey) && dataWhereSet[dataKey] !== window.location.pathname) {
662
+ if (booted && hasOwnProperty(dataWhereSet, dataKey) && dataWhereSet[dataKey] !== window.location.pathname) {
635
663
  console.warn(
636
664
  `beware! You get state:"{State}.${dataKey}" where it set by page:${dataWhereSet[dataKey]}`
637
665
  );
@@ -652,7 +680,7 @@ var State = {
652
680
  * Set data to state.
653
681
  */
654
682
  set(data, excludes) {
655
- dataIsChanged = setData(data, appData, changedKeys, excludes || /* @__PURE__ */ new Set()) || dataIsChanged;
683
+ dataIsChanged = setData(data, appData, changedKeys, excludes || EMPTY_STRING_SET) || dataIsChanged;
656
684
  if (typeof window.__lark_Debug !== "undefined" && window.__lark_Debug && booted) {
657
685
  for (const p in data) {
658
686
  dataWhereSet[p] = window.location.pathname;
@@ -669,26 +697,19 @@ var State = {
669
697
  }
670
698
  if (dataIsChanged) {
671
699
  if (typeof window.__lark_Debug !== "undefined" && window.__lark_Debug) {
672
- for (const p in changedKeys) {
673
- if (has(changedKeys, p)) {
674
- clearNotify(p);
675
- }
700
+ for (const p of changedKeys) {
701
+ clearNotify(p);
676
702
  }
677
703
  }
678
704
  dataIsChanged = false;
679
- const keys2 = {};
680
- for (const k in changedKeys) {
681
- if (has(changedKeys, k)) {
682
- keys2[k] = 1;
683
- }
684
- }
705
+ const keys2 = changedKeys;
685
706
  stashedChangedKeys = keys2;
686
- changedKeys = {};
687
- emitter.fire(ROUTER_EVENTS.CHANGED, { keys: keys2 });
707
+ changedKeys = /* @__PURE__ */ new Set();
708
+ emitter.fire(RouterEvents.CHANGED, { keys: keys2 });
688
709
  }
689
710
  },
690
711
  /**
691
- * Get diff of what changed in last digest.
712
+ * Get the set of keys changed in the most recent digest.
692
713
  */
693
714
  diff() {
694
715
  return stashedChangedKeys;
@@ -746,6 +767,7 @@ var cachedDefaultPath;
746
767
  var cachedRewrite;
747
768
  var defaultTitle;
748
769
  var frameworkConfig;
770
+ var beforeEachGuards = [];
749
771
  function createEmptyLocation() {
750
772
  return {
751
773
  href: "",
@@ -900,7 +922,7 @@ var Router = {
900
922
  document.title = defaultTitle || document.title;
901
923
  }
902
924
  emitter2.fire(
903
- ROUTER_EVENTS.CHANGED,
925
+ RouterEvents.CHANGED,
904
926
  lastChanged
905
927
  );
906
928
  }
@@ -933,16 +955,16 @@ var Router = {
933
955
  }
934
956
  const lPath = lastLocation["path"] || "";
935
957
  const lParams = lastLocation["params"];
936
- const lQuery = {};
958
+ const lQuery = /* @__PURE__ */ new Set();
937
959
  for (const k in lastLocation.query["params"]) {
938
- if (has(lastLocation.query["params"], k)) {
939
- lQuery[k] = 1;
960
+ if (hasOwnProperty(lastLocation.query["params"], k)) {
961
+ lQuery.add(k);
940
962
  }
941
963
  }
942
964
  if (tPath) {
943
- if (!has(window, "history")) {
944
- for (const qKey in lQuery) {
945
- if (has(lQuery, qKey) && !has(tParams, qKey)) {
965
+ if (!hasOwnProperty(window, "history")) {
966
+ for (const qKey of lQuery) {
967
+ if (!hasOwnProperty(tParams, qKey)) {
946
968
  tParams[qKey] = "";
947
969
  }
948
970
  }
@@ -951,14 +973,17 @@ var Router = {
951
973
  tPath = lPath;
952
974
  tParams = assign({}, lParams, tParams);
953
975
  }
954
- updateUrl(
955
- tPath,
956
- tParams,
957
- lastLocation,
958
- replace,
959
- silentFlag,
960
- lQuery
961
- );
976
+ updateUrl(tPath, tParams, lastLocation, replace, silentFlag, lQuery);
977
+ },
978
+ /**
979
+ * Register an async-friendly navigation guard. See `RouterInterface.beforeEach`.
980
+ */
981
+ beforeEach(guard) {
982
+ beforeEachGuards.push(guard);
983
+ return () => {
984
+ const idx = beforeEachGuards.indexOf(guard);
985
+ if (idx !== -1) beforeEachGuards.splice(idx, 1);
986
+ };
962
987
  },
963
988
  /**
964
989
  * Join multiple path segments into a single path.
@@ -1022,12 +1047,39 @@ var Router = {
1022
1047
  }
1023
1048
  };
1024
1049
  Router.fire(
1025
- ROUTER_EVENTS.CHANGE,
1050
+ RouterEvents.CHANGE,
1026
1051
  changeEvent
1027
1052
  );
1028
- if (!suspend && !changeEvent.p) {
1053
+ if (suspend || changeEvent.p) {
1054
+ return;
1055
+ }
1056
+ if (beforeEachGuards.length === 0) {
1029
1057
  changeEvent.resolve();
1058
+ return;
1030
1059
  }
1060
+ const from = lastLocation;
1061
+ const to = loc;
1062
+ const guards = beforeEachGuards.slice();
1063
+ let chain = Promise.resolve(true);
1064
+ for (const guard of guards) {
1065
+ chain = chain.then((prev) => {
1066
+ if (prev === false) return false;
1067
+ return guard(to, from);
1068
+ });
1069
+ }
1070
+ chain.then(
1071
+ (result) => {
1072
+ if (changeEvent.p) return;
1073
+ if (result === false) {
1074
+ changeEvent.reject();
1075
+ } else {
1076
+ changeEvent.resolve();
1077
+ }
1078
+ },
1079
+ () => {
1080
+ if (!changeEvent.p) changeEvent.reject();
1081
+ }
1082
+ );
1031
1083
  }
1032
1084
  };
1033
1085
  Router.notify = watchChange;
@@ -1035,7 +1087,7 @@ var Router = {
1035
1087
  window.addEventListener("popstate", watchChange);
1036
1088
  window.addEventListener("beforeunload", (domEvent) => {
1037
1089
  const data = {};
1038
- Router.fire(ROUTER_EVENTS.PAGE_UNLOAD, data);
1090
+ Router.fire(RouterEvents.PAGE_UNLOAD, data);
1039
1091
  const msg = data["msg"];
1040
1092
  if (msg) {
1041
1093
  domEvent.returnValue = msg;
@@ -1081,13 +1133,17 @@ function parseEventInfo(eventInfo) {
1081
1133
  }
1082
1134
  function findFrameInfo(current, eventType) {
1083
1135
  const eventInfos = [];
1084
- let begin = current;
1085
1136
  const info = current.getAttribute(`@${eventType}`);
1137
+ const hasSelectorEvents = !!selectorEvents[eventType];
1138
+ if (!info && !hasSelectorEvents) {
1139
+ return eventInfos;
1140
+ }
1141
+ let begin = current;
1086
1142
  let match;
1087
1143
  if (info) {
1088
1144
  match = parseEventInfo(info);
1089
1145
  }
1090
- if (match && !match.id || selectorEvents[eventType]) {
1146
+ if (match && !match.id || hasSelectorEvents) {
1091
1147
  let selectorFrameId = "#";
1092
1148
  let backtrace = 0;
1093
1149
  while (begin && begin !== document.body) {
@@ -1260,7 +1316,7 @@ var EventDelegator = {
1260
1316
  };
1261
1317
 
1262
1318
  // src/vdom.ts
1263
- var WrapMeta = {
1319
+ var wrapMeta = {
1264
1320
  option: [1, "<select multiple>"],
1265
1321
  thead: [1, "<table>"],
1266
1322
  col: [2, "<table><colgroup>"],
@@ -1272,9 +1328,9 @@ var WrapMeta = {
1272
1328
  math: [1, '<math xmlns="' + MATH_NS + '">'],
1273
1329
  _: [0, ""]
1274
1330
  };
1275
- WrapMeta["optgroup"] = WrapMeta["option"];
1276
- WrapMeta["tbody"] = WrapMeta["tfoot"] = WrapMeta["colgroup"] = WrapMeta["caption"] = WrapMeta["thead"];
1277
- WrapMeta["th"] = WrapMeta["td"];
1331
+ wrapMeta["optgroup"] = wrapMeta["option"];
1332
+ wrapMeta["tbody"] = wrapMeta["tfoot"] = wrapMeta["colgroup"] = wrapMeta["caption"] = wrapMeta["thead"];
1333
+ wrapMeta["th"] = wrapMeta["td"];
1278
1334
  var VDoc = document.implementation.createHTMLDocument("");
1279
1335
  var VBase = VDoc.createElement("base");
1280
1336
  VBase.href = document.location.href;
@@ -1285,16 +1341,12 @@ var VDomSpecials = {
1285
1341
  OPTION: ["selected"]
1286
1342
  };
1287
1343
  function vdomUnmountFrames(frame, node) {
1288
- if (node.nodeType === 1) {
1289
- const el = node;
1290
- const id = el.getAttribute("id");
1291
- if (id) {
1292
- frame.unmountZone(id);
1293
- const children = frame.children();
1294
- if (children.includes(id)) {
1295
- frame.unmountFrame(id);
1296
- }
1297
- }
1344
+ if (!(node instanceof Element)) return;
1345
+ const id = node.getAttribute("id");
1346
+ if (!id) return;
1347
+ frame.unmountZone(id);
1348
+ if (frame.children().includes(id)) {
1349
+ frame.unmountFrame(id);
1298
1350
  }
1299
1351
  }
1300
1352
  function vdomGetNode(html, refNode) {
@@ -1309,7 +1361,7 @@ function vdomGetNode(html, refNode) {
1309
1361
  const match = TAG_NAME_REGEXP.exec(html);
1310
1362
  tag = match ? match[1] : "";
1311
1363
  }
1312
- const wrap = WrapMeta[tag] || WrapMeta["_"];
1364
+ const wrap = wrapMeta[tag] || wrapMeta["_"];
1313
1365
  tmp.innerHTML = wrap[1] + html;
1314
1366
  let j = wrap[0];
1315
1367
  while (j--) {
@@ -1326,7 +1378,7 @@ function vdomGetCompareKey(node) {
1326
1378
  }
1327
1379
  let key = el.autoId ? "" : el.getAttribute("id") || void 0;
1328
1380
  if (!key) {
1329
- key = el.getAttribute(LARK_KEYS.DIFF_KEY) || void 0;
1381
+ key = el.getAttribute(LarkInnerKeys.DIFF_KEY) || void 0;
1330
1382
  }
1331
1383
  if (!key) {
1332
1384
  const larkView = el.getAttribute(LARK_VIEW);
@@ -1339,8 +1391,7 @@ function vdomGetCompareKey(node) {
1339
1391
  return key;
1340
1392
  }
1341
1393
  function vdomSpecialDiff(oldNode, newNode) {
1342
- const nodeName = oldNode.nodeName;
1343
- const specials = VDomSpecials[nodeName];
1394
+ const specials = VDomSpecials[oldNode.nodeName];
1344
1395
  if (!specials) return 0;
1345
1396
  const oldEl = oldNode;
1346
1397
  const newEl = newNode;
@@ -1389,13 +1440,17 @@ function vdomSetChildNodes(oldParent, newParent, ref, frame, keys_) {
1389
1440
  let oldNode = oldParent.lastChild;
1390
1441
  let newNode = newParent.firstChild;
1391
1442
  let extra = 0;
1392
- const keyedNodes = {};
1393
- const newKeyedNodes = {};
1443
+ const keyedNodes = /* @__PURE__ */ new Map();
1444
+ const newKeyedNodes = /* @__PURE__ */ new Map();
1394
1445
  while (oldNode) {
1395
1446
  extra++;
1396
1447
  const nodeKey = vdomGetCompareKey(oldNode);
1397
1448
  if (nodeKey) {
1398
- const bucket = keyedNodes[nodeKey] || (keyedNodes[nodeKey] = []);
1449
+ let bucket = keyedNodes.get(nodeKey);
1450
+ if (!bucket) {
1451
+ bucket = [];
1452
+ keyedNodes.set(nodeKey, bucket);
1453
+ }
1399
1454
  bucket.push(oldNode);
1400
1455
  }
1401
1456
  oldNode = oldNode.previousSibling;
@@ -1403,7 +1458,7 @@ function vdomSetChildNodes(oldParent, newParent, ref, frame, keys_) {
1403
1458
  while (newNode) {
1404
1459
  const nodeKey = vdomGetCompareKey(newNode);
1405
1460
  if (nodeKey) {
1406
- newKeyedNodes[nodeKey] = (newKeyedNodes[nodeKey] || 0) + 1;
1461
+ newKeyedNodes.set(nodeKey, (newKeyedNodes.get(nodeKey) ?? 0) + 1);
1407
1462
  }
1408
1463
  newNode = newNode.nextSibling;
1409
1464
  }
@@ -1414,23 +1469,25 @@ function vdomSetChildNodes(oldParent, newParent, ref, frame, keys_) {
1414
1469
  const tempNew = newNode;
1415
1470
  newNode = newNode.nextSibling;
1416
1471
  const nodeKey = vdomGetCompareKey(tempNew);
1417
- let foundNode = nodeKey ? keyedNodes[nodeKey] : void 0;
1472
+ let foundNode = nodeKey ? keyedNodes.get(nodeKey) : void 0;
1418
1473
  if (foundNode && (foundNode = foundNode.slice()) && foundNode.length) {
1419
1474
  const matched = foundNode.pop();
1420
1475
  while (matched !== oldNode) {
1421
- const next = oldNode?.nextSibling ?? null;
1476
+ if (!oldNode) break;
1477
+ const next = oldNode.nextSibling;
1422
1478
  oldParent.appendChild(oldNode);
1423
1479
  oldNode = next;
1424
1480
  }
1425
1481
  oldNode = matched.nextSibling;
1426
- if (nodeKey && newKeyedNodes[nodeKey]) {
1427
- newKeyedNodes[nodeKey]--;
1482
+ if (nodeKey) {
1483
+ const c = newKeyedNodes.get(nodeKey);
1484
+ if (c) newKeyedNodes.set(nodeKey, c - 1);
1428
1485
  }
1429
1486
  vdomSetNode(matched, tempNew, oldParent, ref, frame, keys_);
1430
1487
  } else if (oldNode) {
1431
1488
  const tempOld2 = oldNode;
1432
1489
  const oldKey = vdomGetCompareKey(tempOld2);
1433
- if (oldKey && keyedNodes[oldKey] && newKeyedNodes[oldKey]) {
1490
+ if (oldKey && keyedNodes.has(oldKey) && newKeyedNodes.get(oldKey)) {
1434
1491
  extra++;
1435
1492
  ref.hasChanged = 1;
1436
1493
  ref.domOps.push([8, oldParent, tempNew, tempOld2]);
@@ -1454,17 +1511,21 @@ function vdomSetChildNodes(oldParent, newParent, ref, frame, keys_) {
1454
1511
  }
1455
1512
  }
1456
1513
  function vdomSetNode(oldNode, newNode, oldParent, ref, frame, keys_) {
1457
- if (vdomSpecialDiff(oldNode, newNode) || oldNode.nodeType === 1 && oldNode.hasAttribute(LARK_KEYS.VIEW_KEY) || !(oldNode.isEqualNode && oldNode.isEqualNode(newNode))) {
1514
+ const oldAsEl = oldNode instanceof Element ? oldNode : null;
1515
+ const newAsEl = newNode instanceof Element ? newNode : null;
1516
+ const hasViewKey = !!oldAsEl?.hasAttribute(LarkInnerKeys.VIEW_KEY);
1517
+ const equalAsNodes = oldAsEl !== null && newAsEl !== null && oldAsEl.isEqualNode && oldAsEl.isEqualNode(newAsEl);
1518
+ if (vdomSpecialDiff(oldNode, newNode) || hasViewKey || !equalAsNodes) {
1458
1519
  if (oldNode.nodeType === newNode.nodeType && oldNode.nodeName === newNode.nodeName) {
1459
- if (oldNode.nodeType === 1) {
1460
- const oldEl = oldNode;
1461
- const newEl = newNode;
1462
- const staticKey = newEl.getAttribute(LARK_KEYS.DIFF_KEY);
1463
- if (staticKey && staticKey === oldEl.getAttribute(LARK_KEYS.DIFF_KEY)) {
1520
+ if (oldAsEl !== null && newAsEl !== null) {
1521
+ const oldEl = oldAsEl;
1522
+ const newEl = newAsEl;
1523
+ const staticKey = newEl.getAttribute(LarkInnerKeys.DIFF_KEY);
1524
+ if (staticKey && staticKey === oldEl.getAttribute(LarkInnerKeys.DIFF_KEY)) {
1464
1525
  return;
1465
1526
  }
1466
1527
  const newLarkView = newEl.getAttribute(LARK_VIEW);
1467
- const updateAttribute = !newEl.getAttribute(LARK_KEYS.ATTR_KEY) || newEl.getAttribute(LARK_KEYS.ATTR_KEY) !== oldEl.getAttribute(LARK_KEYS.ATTR_KEY);
1528
+ const updateAttribute = !newEl.getAttribute(LarkInnerKeys.ATTR_KEY) || newEl.getAttribute(LarkInnerKeys.ATTR_KEY) !== oldEl.getAttribute(LarkInnerKeys.ATTR_KEY);
1468
1529
  let updateChildren = true;
1469
1530
  if (newLarkView) {
1470
1531
  const oldFrameId = oldEl.getAttribute("id") || "";
@@ -1565,7 +1626,8 @@ function encodeQ(v) {
1565
1626
  }
1566
1627
 
1567
1628
  // src/updater.ts
1568
- function updaterRef(refData, value, key) {
1629
+ function updaterRef(refDataIn, value, key) {
1630
+ const refData = refDataIn;
1569
1631
  const counter = refData[SPLITTER];
1570
1632
  for (let i = counter; --i; ) {
1571
1633
  key = SPLITTER + i;
@@ -1585,13 +1647,19 @@ var Updater = class {
1585
1647
  /** Ref data for template rendering */
1586
1648
  refData;
1587
1649
  /** Changed keys in current digest cycle */
1588
- changedKeys = {};
1650
+ changedKeys = /* @__PURE__ */ new Set();
1589
1651
  /** Whether data has changed since last digest */
1590
1652
  hasChangedFlag = 0;
1591
- /** Digesting queue: supports re-digest during digest */
1653
+ /**
1654
+ * Digesting queue: supports re-digest during digest.
1655
+ * Holds pending callbacks; `null` is used as a sentinel marking the start
1656
+ * of an active digest cycle, so `runDigest` can detect re-entrant calls.
1657
+ */
1592
1658
  digestingQueue = [];
1593
- /** Snapshot JSON string for altered() detection */
1594
- snapshotJson;
1659
+ /** Monotonically increasing version, bumped each time data actually changes. */
1660
+ version = 0;
1661
+ /** Snapshot of `version` taken by `snapshot()`, used by `altered()`. */
1662
+ snapshotVersion;
1595
1663
  constructor(viewId) {
1596
1664
  this.viewId = viewId;
1597
1665
  this.data = { vId: viewId };
@@ -1619,7 +1687,16 @@ var Updater = class {
1619
1687
  * Returns this for chaining.
1620
1688
  */
1621
1689
  set(data, excludes) {
1622
- this.hasChangedFlag = setData(data, this.data, this.changedKeys, excludes || /* @__PURE__ */ new Set()) || this.hasChangedFlag ? 1 : 0;
1690
+ const changed = setData(
1691
+ data,
1692
+ this.data,
1693
+ this.changedKeys,
1694
+ excludes || EMPTY_STRING_SET
1695
+ );
1696
+ if (changed) {
1697
+ this.version++;
1698
+ this.hasChangedFlag = 1;
1699
+ }
1623
1700
  return this;
1624
1701
  }
1625
1702
  /**
@@ -1655,13 +1732,13 @@ var Updater = class {
1655
1732
  const keys2 = this.changedKeys;
1656
1733
  const changed = this.hasChangedFlag;
1657
1734
  this.hasChangedFlag = 0;
1658
- this.changedKeys = {};
1735
+ this.changedKeys = /* @__PURE__ */ new Set();
1659
1736
  const frame = Frame.get(this.viewId);
1660
1737
  const view = frame?.view;
1661
1738
  const node = getById(this.viewId);
1662
- if (changed && view && node && view.signature > 0) {
1739
+ if (changed && view && node && view.signature > 0 && frame) {
1663
1740
  const template = view.template;
1664
- if (template && typeof template === "function") {
1741
+ if (typeof template === "function") {
1665
1742
  const html = template(
1666
1743
  this.data,
1667
1744
  this.viewId,
@@ -1698,43 +1775,65 @@ var Updater = class {
1698
1775
  }
1699
1776
  }
1700
1777
  /**
1701
- * Save a snapshot of current data for altered() detection.
1778
+ * Save a snapshot of the current data version for `altered()` detection.
1779
+ * Cheap O(1) — records the current monotonic version, no serialization.
1702
1780
  */
1703
1781
  snapshot() {
1704
- this.snapshotJson = JSON.stringify(this.data);
1782
+ this.snapshotVersion = this.version;
1705
1783
  return this;
1706
1784
  }
1707
1785
  /**
1708
- * Check if data has changed since last snapshot.
1786
+ * Check whether data has changed since the last snapshot.
1787
+ * Returns undefined when no snapshot has been taken yet.
1709
1788
  */
1710
1789
  altered() {
1711
- if (this.snapshotJson) {
1712
- return this.snapshotJson !== JSON.stringify(this.data);
1713
- }
1714
- return void 0;
1790
+ if (this.snapshotVersion === void 0) return void 0;
1791
+ return this.version !== this.snapshotVersion;
1715
1792
  }
1716
1793
  /**
1717
- * Translate data references (SPLITTER-prefixed values).
1794
+ * Translate a refData reference back to its original value.
1795
+ *
1796
+ * The ref protocol is `SPLITTER` + ascii decimal digits — the exact format
1797
+ * emitted by `updaterRef`. We require that exact shape so a user-supplied
1798
+ * string that merely begins with SPLITTER is never accidentally resolved
1799
+ * (or mishandled as a "missing ref").
1718
1800
  */
1719
1801
  translate(data) {
1720
- if (typeof data === "string" && data[0] === SPLITTER) {
1721
- return has(this.refData, data) ? this.refData[data] : data;
1802
+ if (typeof data !== "string") return data;
1803
+ if (data.length < 2 || data[0] !== SPLITTER) return data;
1804
+ for (let i = 1; i < data.length; i++) {
1805
+ const c = data.charCodeAt(i);
1806
+ if (c < 48 || c > 57) return data;
1722
1807
  }
1723
- return data;
1808
+ return hasOwnProperty(this.refData, data) ? this.refData[data] : data;
1724
1809
  }
1725
1810
  /**
1726
- * Parse expression with data context.
1811
+ * Resolve a dotted property path against refData.
1812
+ *
1813
+ * Only safe property-path syntax is supported: `a`, `a.b`, `a.b.c`.
1814
+ * Numeric literals (e.g. `1`, `1.5`) are returned as numbers. Anything else
1815
+ * returns `undefined` — we no longer evaluate arbitrary JavaScript via
1816
+ * `new Function`, so the method is CSP-safe and cannot be used as an
1817
+ * injection vector.
1727
1818
  */
1728
1819
  parse(expr) {
1729
- try {
1730
- const fn = new Function("data", `with(data) { return ${expr}; }`);
1731
- return fn(this.refData);
1732
- } catch {
1820
+ const trimmed = expr.trim();
1821
+ if (!trimmed) return void 0;
1822
+ if (/^-?\d+(?:\.\d+)?$/.test(trimmed)) {
1823
+ return Number(trimmed);
1824
+ }
1825
+ if (!/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*$/.test(trimmed)) {
1733
1826
  return void 0;
1734
1827
  }
1828
+ let cur = this.refData;
1829
+ for (const segment of trimmed.split(".")) {
1830
+ if (cur == null || typeof cur !== "object") return void 0;
1831
+ cur = cur[segment];
1832
+ }
1833
+ return cur;
1735
1834
  }
1736
1835
  /**
1737
- * Get changed keys (for external inspection).
1836
+ * Get the set of keys changed since the last digest (for external inspection).
1738
1837
  */
1739
1838
  getChangedKeys() {
1740
1839
  return this.changedKeys;
@@ -1781,30 +1880,31 @@ var View = class _View {
1781
1880
  // ============================================================
1782
1881
  // Getters for prototype-stored event maps
1783
1882
  // ============================================================
1883
+ /** Prototype-stored event maps shape (set by View.prepare). */
1884
+ get protoEventState() {
1885
+ return Object.getPrototypeOf(this);
1886
+ }
1784
1887
  /**
1785
1888
  * Event bitmask map: eventType -> bitmask (1=root, 2=selector).
1786
1889
  * Read from prototype ($evtObjMap) set by View.prepare.
1787
1890
  * Using a getter avoids ES6 class field shadowing the prototype value.
1788
1891
  */
1789
1892
  get eventObjectMap() {
1790
- const proto = Object.getPrototypeOf(this);
1791
- return proto["$evtObjMap"] || {};
1893
+ return this.protoEventState.$evtObjMap ?? {};
1792
1894
  }
1793
1895
  /**
1794
1896
  * Selector event map: eventType -> selector list.
1795
1897
  * Read from prototype ($selMap) set by View.prepare.
1796
1898
  */
1797
1899
  get eventSelectorMap() {
1798
- const proto = Object.getPrototypeOf(this);
1799
- return proto["$selMap"] || {};
1900
+ return this.protoEventState.$selMap ?? {};
1800
1901
  }
1801
1902
  /**
1802
1903
  * Global event list: [{handler, element, eventName, modifiers}].
1803
1904
  * Read from prototype ($globalEvtList) set by View.prepare.
1804
1905
  */
1805
1906
  get globalEventList() {
1806
- const proto = Object.getPrototypeOf(this);
1807
- return proto["$globalEvtList"] || [];
1907
+ return this.protoEventState.$globalEvtList ?? [];
1808
1908
  }
1809
1909
  // ============================================================
1810
1910
  // Instance lifecycle methods
@@ -1839,13 +1939,17 @@ var View = class _View {
1839
1939
  // ============================================================
1840
1940
  // Update methods
1841
1941
  // ============================================================
1942
+ /** Get the owning frame, asserting it has been bound. */
1943
+ get ownerFrame() {
1944
+ return this.owner;
1945
+ }
1842
1946
  /**
1843
1947
  * Notify view that HTML update is about to begin.
1844
1948
  * Unmounts child frames in the update zone.
1845
1949
  */
1846
1950
  beginUpdate(id) {
1847
1951
  if (this.signature > 0 && this.endUpdatePending !== void 0) {
1848
- this.owner.unmountZone(id, true);
1952
+ this.ownerFrame.unmountZone(id, true);
1849
1953
  }
1850
1954
  }
1851
1955
  /**
@@ -1863,7 +1967,7 @@ var View = class _View {
1863
1967
  this.endUpdatePending = 1;
1864
1968
  this.rendered = true;
1865
1969
  }
1866
- const ownerFrame = this.owner;
1970
+ const ownerFrame = this.ownerFrame;
1867
1971
  ownerFrame.mountZone(updateId, inner);
1868
1972
  if (!flag) {
1869
1973
  setTimeout(
@@ -1902,11 +2006,12 @@ var View = class _View {
1902
2006
  const loc = this.locationObserved;
1903
2007
  loc.flag = 1;
1904
2008
  if (typeof params === "object" && !Array.isArray(params)) {
1905
- if (params["path"]) {
2009
+ const opts = params;
2010
+ if (opts["path"]) {
1906
2011
  observePath = true;
1907
2012
  }
1908
- const paramKeys = params["params"];
1909
- if (paramKeys) {
2013
+ const paramKeys = opts["params"];
2014
+ if (typeof paramKeys === "string" || Array.isArray(paramKeys)) {
1910
2015
  params = paramKeys;
1911
2016
  }
1912
2017
  }
@@ -1970,24 +2075,16 @@ var View = class _View {
1970
2075
  */
1971
2076
  leaveTip(message, condition) {
1972
2077
  const changeListener = function(e) {
1973
- const isRouterChange = e.type === ROUTER_EVENTS.CHANGE;
2078
+ const isRouterChange = e.type === RouterEvents.CHANGE;
1974
2079
  const aKey = isRouterChange ? "a" : "b";
1975
2080
  const bKey = isRouterChange ? "b" : "a";
1976
2081
  if (changeListener[aKey]) {
1977
- if (typeof e.prevent === "function") {
1978
- e.prevent();
1979
- }
1980
- if (typeof e.reject === "function") {
1981
- e.reject();
1982
- }
2082
+ e.prevent?.();
2083
+ e.reject?.();
1983
2084
  } else if (condition()) {
1984
- if (typeof e.prevent === "function") {
1985
- e.prevent();
1986
- }
2085
+ e.prevent?.();
1987
2086
  changeListener[bKey] = 1;
1988
- if (typeof e.resolve === "function") {
1989
- e.resolve();
1990
- }
2087
+ e.resolve?.();
1991
2088
  }
1992
2089
  };
1993
2090
  const unloadListener = (e) => {
@@ -1995,14 +2092,12 @@ var View = class _View {
1995
2092
  e["msg"] = message;
1996
2093
  }
1997
2094
  };
1998
- const changeFn = changeListener;
1999
- const unloadFn = unloadListener;
2000
- Router.on(ROUTER_EVENTS.CHANGE, changeFn);
2001
- Router.on(ROUTER_EVENTS.PAGE_UNLOAD, unloadFn);
2002
- this.on("unload", changeFn);
2095
+ Router.on(RouterEvents.CHANGE, changeListener);
2096
+ Router.on(RouterEvents.PAGE_UNLOAD, unloadListener);
2097
+ this.on("unload", changeListener);
2003
2098
  this.on("destroy", () => {
2004
- Router.off(ROUTER_EVENTS.CHANGE, changeFn);
2005
- Router.off(ROUTER_EVENTS.PAGE_UNLOAD, unloadFn);
2099
+ Router.off(RouterEvents.CHANGE, changeListener);
2100
+ Router.off(RouterEvents.PAGE_UNLOAD, unloadListener);
2006
2101
  });
2007
2102
  }
2008
2103
  // ============================================================
@@ -2032,7 +2127,7 @@ var View = class _View {
2032
2127
  _View.mergeMixins(mixins, oView, ctors);
2033
2128
  }
2034
2129
  for (const p in proto) {
2035
- if (!has(proto, p)) continue;
2130
+ if (!hasOwnProperty(proto, p)) continue;
2036
2131
  const currentFn = proto[p];
2037
2132
  if (typeof currentFn !== "function") continue;
2038
2133
  const matches = p.match(VIEW_EVENT_METHOD_REGEXP);
@@ -2078,16 +2173,16 @@ var View = class _View {
2078
2173
  const existingFn = proto[combinedKey];
2079
2174
  if (!existingFn) {
2080
2175
  proto[combinedKey] = currentFn;
2081
- } else {
2176
+ } else if (typeof existingFn === "function") {
2082
2177
  const mixinFn = currentFn;
2083
2178
  const existingMixin = existingFn;
2084
2179
  if (existingMixin.marker) {
2085
2180
  if (mixinFn.marker) {
2086
2181
  proto[combinedKey] = _View.processMixinsSameEvent(
2087
- currentFn,
2088
- existingFn
2182
+ mixinFn,
2183
+ existingMixin
2089
2184
  );
2090
- } else if (has(proto, p)) {
2185
+ } else if (hasOwnProperty(proto, p)) {
2091
2186
  proto[combinedKey] = currentFn;
2092
2187
  }
2093
2188
  }
@@ -2110,7 +2205,7 @@ var View = class _View {
2110
2205
  const selectorObject = view.eventSelectorMap;
2111
2206
  const eventsList = view.globalEventList;
2112
2207
  for (const e in eventsObject) {
2113
- if (has(eventsObject, e)) {
2208
+ if (hasOwnProperty(eventsObject, e)) {
2114
2209
  if (destroy) {
2115
2210
  EventDelegator.unbind(e, !!selectorObject[e]);
2116
2211
  } else {
@@ -2153,7 +2248,7 @@ var View = class _View {
2153
2248
  static destroyAllResources(view, lastly) {
2154
2249
  const cache = view.resources;
2155
2250
  for (const p in cache) {
2156
- if (has(cache, p)) {
2251
+ if (hasOwnProperty(cache, p)) {
2157
2252
  const entry = cache[p];
2158
2253
  if (lastly || entry.destroyOnRender) {
2159
2254
  _View.destroyResource(cache, p, true);
@@ -2183,13 +2278,16 @@ var View = class _View {
2183
2278
  static wrapMethod(proto, fnName, shortKey) {
2184
2279
  const originalFn = proto[fnName];
2185
2280
  if (typeof originalFn !== "function") return;
2281
+ const originalAsFn = originalFn;
2186
2282
  const wrapped = function(...args) {
2187
2283
  if (this.signature > 0) {
2188
2284
  this.signature++;
2189
2285
  this.fire("render");
2190
2286
  _View.destroyAllResources(this, false);
2191
- const instanceFn = typeof this[fnName] === "function" ? this[fnName] : originalFn;
2192
- const fnToCall = instanceFn === wrapped ? originalFn : instanceFn;
2287
+ const lookup = this;
2288
+ const candidate = lookup[fnName];
2289
+ const instanceFn = typeof candidate === "function" ? candidate : originalAsFn;
2290
+ const fnToCall = instanceFn === wrapped ? originalAsFn : instanceFn;
2193
2291
  return funcWithTry(fnToCall, args, this, noop);
2194
2292
  }
2195
2293
  return void 0;
@@ -2203,19 +2301,18 @@ var View = class _View {
2203
2301
  */
2204
2302
  static processMixinsSameEvent(additional, exist) {
2205
2303
  let temp;
2206
- const existMixin = exist;
2207
- if (existMixin.handlerList) {
2208
- temp = existMixin;
2304
+ if (exist.handlerList) {
2305
+ temp = exist;
2209
2306
  } else {
2210
- temp = function(...e) {
2211
- funcWithTry(temp.handlerList ?? [], e, this, noop);
2307
+ const merged = function(...e) {
2308
+ funcWithTry(merged.handlerList ?? [], e, this, noop);
2212
2309
  };
2213
- temp.handlerList = [exist];
2214
- temp.marker = 1;
2310
+ merged.handlerList = [exist];
2311
+ merged.marker = 1;
2312
+ temp = merged;
2215
2313
  }
2216
- const additionalMixin = additional;
2217
2314
  temp.handlerList = (temp.handlerList ?? []).concat(
2218
- additionalMixin.handlerList ?? [additional]
2315
+ additional.handlerList ?? [additional]
2219
2316
  );
2220
2317
  return temp;
2221
2318
  }
@@ -2227,29 +2324,29 @@ var View = class _View {
2227
2324
  const temp = {};
2228
2325
  for (const node of mixins) {
2229
2326
  for (const p in node) {
2230
- if (!has(node, p)) continue;
2327
+ if (!hasOwnProperty(node, p)) continue;
2231
2328
  const fn = node[p];
2329
+ if (typeof fn !== "function") continue;
2330
+ const mixinFn = fn;
2232
2331
  const exist = temp[p];
2233
2332
  if (p === "make") {
2234
- ctors.push(fn);
2333
+ ctors.push(mixinFn);
2235
2334
  continue;
2236
2335
  }
2237
2336
  if (VIEW_EVENT_METHOD_REGEXP.test(p)) {
2238
2337
  if (exist) {
2239
- temp[p] = _View.processMixinsSameEvent(fn, exist);
2338
+ temp[p] = _View.processMixinsSameEvent(mixinFn, exist);
2240
2339
  } else {
2241
- fn.marker = 1;
2242
- temp[p] = fn;
2243
- }
2244
- } else {
2245
- if (!exist) {
2246
- temp[p] = fn;
2340
+ mixinFn.marker = 1;
2341
+ temp[p] = mixinFn;
2247
2342
  }
2343
+ } else if (!exist) {
2344
+ temp[p] = mixinFn;
2248
2345
  }
2249
2346
  }
2250
2347
  }
2251
2348
  for (const p in temp) {
2252
- if (!has(proto, p)) {
2349
+ if (!hasOwnProperty(proto, p)) {
2253
2350
  proto[p] = temp[p];
2254
2351
  }
2255
2352
  }
@@ -2282,19 +2379,20 @@ var View = class _View {
2282
2379
  * - Event method patterns: `'name<click>'` etc.
2283
2380
  */
2284
2381
  static extend(props, statics) {
2285
- props = props || {};
2286
- const make = props["make"];
2382
+ const definedProps = props ?? {};
2383
+ const make = definedProps["make"];
2287
2384
  const ctors = [];
2288
- if (make) {
2385
+ if (typeof make === "function") {
2289
2386
  ctors.push(make);
2290
2387
  }
2291
2388
  const ParentView = this;
2292
2389
  const ChildView = class extends ParentView {
2293
2390
  constructor(nodeId, ownerFrame, initParams, node, mixinCtors) {
2294
2391
  super(nodeId, ownerFrame, initParams, node, []);
2295
- for (const key in props) {
2296
- if (has(props, key) && key !== "make" && key !== "render") {
2297
- this[key] = props[key];
2392
+ const instanceProps = this;
2393
+ for (const key in definedProps) {
2394
+ if (hasOwnProperty(definedProps, key) && key !== "make" && key !== "render") {
2395
+ instanceProps[key] = definedProps[key];
2298
2396
  }
2299
2397
  }
2300
2398
  this.id = nodeId;
@@ -2314,15 +2412,16 @@ var View = class _View {
2314
2412
  }
2315
2413
  };
2316
2414
  const proto = ChildView.prototype;
2317
- for (const key in props) {
2318
- if (has(props, key) && key !== "make") {
2319
- proto[key] = props[key];
2415
+ for (const key in definedProps) {
2416
+ if (hasOwnProperty(definedProps, key) && key !== "make") {
2417
+ proto[key] = definedProps[key];
2320
2418
  }
2321
2419
  }
2322
2420
  if (statics) {
2421
+ const staticTarget = ChildView;
2323
2422
  for (const key in statics) {
2324
- if (has(statics, key)) {
2325
- ChildView[key] = statics[key];
2423
+ if (hasOwnProperty(statics, key)) {
2424
+ staticTarget[key] = statics[key];
2326
2425
  }
2327
2426
  }
2328
2427
  }
@@ -2337,14 +2436,86 @@ var View = class _View {
2337
2436
  return this;
2338
2437
  }
2339
2438
  };
2439
+ function defineView(props, statics) {
2440
+ return View.extend(props, statics);
2441
+ }
2442
+
2443
+ // src/module-loader.ts
2444
+ var config = {
2445
+ rootId: "root",
2446
+ hashbang: "#!",
2447
+ error: (error) => {
2448
+ throw error;
2449
+ }
2450
+ };
2451
+ function use(names, callback) {
2452
+ const nameList = typeof names === "string" ? [names] : names;
2453
+ const loadPromise = (() => {
2454
+ if (config.require) {
2455
+ const result = config.require(nameList);
2456
+ if (result && typeof result.then === "function") {
2457
+ return result;
2458
+ }
2459
+ return Promise.resolve([]);
2460
+ }
2461
+ return Promise.all(
2462
+ nameList.map((name) => {
2463
+ const importPath = name.startsWith(".") || name.startsWith("/") ? name : `./${name}`;
2464
+ return import(
2465
+ /* @vite-ignore */
2466
+ /* webpackIgnore: true */
2467
+ importPath
2468
+ ).then((mod) => {
2469
+ return mod && (mod["__esModule"] || // For Webpack
2470
+ typeof mod["default"] === "function") ? mod["default"] : mod;
2471
+ }).catch((err) => {
2472
+ const errorHandler = config.error;
2473
+ if (errorHandler) {
2474
+ errorHandler(err instanceof Error ? err : new Error(String(err)));
2475
+ }
2476
+ return void 0;
2477
+ });
2478
+ })
2479
+ );
2480
+ })();
2481
+ if (callback) {
2482
+ loadPromise.then((modules) => {
2483
+ callback(...modules);
2484
+ });
2485
+ }
2486
+ return loadPromise;
2487
+ }
2488
+
2489
+ // src/view-registry.ts
2490
+ var viewClassRegistry = {};
2491
+ function getViewClass(path) {
2492
+ return viewClassRegistry[path];
2493
+ }
2494
+ function registerViewClass(viewPath, ViewClass) {
2495
+ const parsed = parseUri(viewPath);
2496
+ const path = parsed.path;
2497
+ if (path) {
2498
+ viewClassRegistry[path] = ViewClass;
2499
+ }
2500
+ }
2501
+ function invalidateViewClass(viewPath) {
2502
+ const parsed = parseUri(viewPath);
2503
+ const path = parsed.path;
2504
+ if (path) {
2505
+ Reflect.deleteProperty(viewClassRegistry, path);
2506
+ }
2507
+ }
2508
+ function getViewClassRegistry() {
2509
+ return viewClassRegistry;
2510
+ }
2340
2511
 
2341
2512
  // src/frame.ts
2342
2513
  var frameRegistry = /* @__PURE__ */ new Map();
2343
2514
  var rootFrame;
2344
2515
  var globalAlter;
2516
+ var MAX_FRAME_POOL = 64;
2345
2517
  var frameCache = [];
2346
2518
  var staticEmitter = new EventEmitter();
2347
- var viewClassRegistry = {};
2348
2519
  var Frame = class _Frame extends EventEmitter {
2349
2520
  /** Frame ID (same as owner DOM element ID) */
2350
2521
  id;
@@ -2359,8 +2530,8 @@ var Frame = class _Frame extends EventEmitter {
2359
2530
  childrenCount = 0;
2360
2531
  /** Ready count (children that have fired 'created') */
2361
2532
  readyCount = 0;
2362
- /** Ready map: id -> 1 */
2363
- readyMap = {};
2533
+ /** Set of child frame IDs that have fired 'created' */
2534
+ readyMap = /* @__PURE__ */ new Set();
2364
2535
  /** View instance */
2365
2536
  viewInstance;
2366
2537
  /** Get view instance (read-only) */
@@ -2431,13 +2602,30 @@ var Frame = class _Frame extends EventEmitter {
2431
2602
  this.viewPath = viewPath;
2432
2603
  const params = parsed["params"];
2433
2604
  translateQuery(pId || this.id, viewPath, params);
2605
+ const initParams = { ...params };
2434
2606
  if (viewInitParams) {
2435
- assign(params, viewInitParams);
2607
+ assign(initParams, viewInitParams);
2436
2608
  }
2437
2609
  const sign = this.signature;
2438
- if (viewClassRegistry[viewClassName]) {
2439
- this.doMountView(viewClassRegistry[viewClassName], params, node, sign);
2610
+ const registered = getViewClass(viewClassName);
2611
+ if (registered) {
2612
+ this.doMountView(registered, initParams, node, sign);
2613
+ return;
2440
2614
  }
2615
+ use(viewClassName, (ViewClass) => {
2616
+ if (sign !== this.signature) return;
2617
+ if (typeof ViewClass === "function") {
2618
+ const ViewClassTyped = ViewClass;
2619
+ registerViewClass(viewClassName, ViewClassTyped);
2620
+ this.doMountView(ViewClassTyped, initParams, node, sign);
2621
+ } else {
2622
+ const error = new Error(`Cannot load view: ${viewClassName}`);
2623
+ const errorHandler = config.error;
2624
+ if (errorHandler) {
2625
+ errorHandler(error);
2626
+ }
2627
+ }
2628
+ });
2441
2629
  }
2442
2630
  /**
2443
2631
  * Internal: actually mount the view after class is loaded.
@@ -2445,14 +2633,8 @@ var Frame = class _Frame extends EventEmitter {
2445
2633
  doMountView(ViewClass, params, node, sign) {
2446
2634
  if (sign !== this.signature) return;
2447
2635
  const mixinCtors = View.prepare(ViewClass);
2448
- const ViewConstructor = ViewClass;
2449
- const view = new ViewConstructor(
2450
- this.id,
2451
- this,
2452
- params,
2453
- node,
2454
- mixinCtors
2455
- );
2636
+ const Ctor = ViewClass;
2637
+ const view = new Ctor(this.id, this, params, node, mixinCtors);
2456
2638
  this.viewInstance = view;
2457
2639
  view.signature = 1;
2458
2640
  View.delegateEvents(view);
@@ -2533,7 +2715,9 @@ var Frame = class _Frame extends EventEmitter {
2533
2715
  frame.unmountView();
2534
2716
  removeFrame(targetId, wasCreated);
2535
2717
  reInitFrameForCache(frame);
2536
- frameCache.push(frame);
2718
+ if (frameCache.length < MAX_FRAME_POOL) {
2719
+ frameCache.push(frame);
2720
+ }
2537
2721
  const parent = frameRegistry.get(pId || "");
2538
2722
  if (parent && parent.childrenMap[targetId]) {
2539
2723
  Reflect.deleteProperty(parent.childrenMap, targetId);
@@ -2552,13 +2736,12 @@ var Frame = class _Frame extends EventEmitter {
2552
2736
  const viewElements = rootEl.querySelectorAll(`[${LARK_VIEW}]`);
2553
2737
  const frames = [];
2554
2738
  viewElements.forEach((el) => {
2555
- const htmlEl = el;
2556
- if (!htmlEl.frameBound) {
2557
- const elId = ensureElementId2(htmlEl);
2558
- htmlEl.frameBound = 1;
2559
- const viewPath = getAttribute(htmlEl, LARK_VIEW);
2560
- frames.push([elId, viewPath]);
2561
- }
2739
+ if (!(el instanceof HTMLElement)) return;
2740
+ if (htmlElIsBound(el)) return;
2741
+ const elId = ensureElementId2(el);
2742
+ el.frameBound = 1;
2743
+ const viewPath = getAttribute(el, LARK_VIEW);
2744
+ frames.push([elId, viewPath]);
2562
2745
  });
2563
2746
  for (const [frameId, viewPath] of frames) {
2564
2747
  this.mountFrame(frameId, viewPath);
@@ -2571,7 +2754,7 @@ var Frame = class _Frame extends EventEmitter {
2571
2754
  */
2572
2755
  unmountZone(zoneId, _inner) {
2573
2756
  for (const childId in this.childrenMap) {
2574
- if (has(this.childrenMap, childId)) {
2757
+ if (hasOwnProperty(this.childrenMap, childId)) {
2575
2758
  if (!zoneId || childId !== zoneId) {
2576
2759
  this.unmountFrame(childId);
2577
2760
  }
@@ -2585,7 +2768,7 @@ var Frame = class _Frame extends EventEmitter {
2585
2768
  children() {
2586
2769
  const result = [];
2587
2770
  for (const id in this.childrenMap) {
2588
- if (has(this.childrenMap, id)) {
2771
+ if (hasOwnProperty(this.childrenMap, id)) {
2589
2772
  result.push(id);
2590
2773
  }
2591
2774
  }
@@ -2612,14 +2795,10 @@ var Frame = class _Frame extends EventEmitter {
2612
2795
  let result;
2613
2796
  const view = this.view;
2614
2797
  if (view && view.rendered) {
2615
- const fn = view[name];
2798
+ const lookup = view;
2799
+ const fn = lookup[name];
2616
2800
  if (typeof fn === "function") {
2617
- result = funcWithTry(
2618
- fn,
2619
- args || [],
2620
- view,
2621
- noop
2622
- );
2801
+ result = funcWithTry(fn, args || [], view, noop);
2623
2802
  }
2624
2803
  } else {
2625
2804
  const key = SPLITTER + name;
@@ -2642,6 +2821,25 @@ var Frame = class _Frame extends EventEmitter {
2642
2821
  }
2643
2822
  return result;
2644
2823
  }
2824
+ /**
2825
+ * Type-safe variant of `invoke`.
2826
+ *
2827
+ * `invoke()` accepts any string and any args, which silently hides
2828
+ * mismatched call sites when a method gets renamed. `invokeTyped` carries
2829
+ * the view's method signature through TypeScript so the compiler catches
2830
+ * those mistakes:
2831
+ *
2832
+ * ```ts
2833
+ * type Home = View & { loadData(id: string): Promise<void> };
2834
+ * frame.invokeTyped<Home, "loadData">("loadData", ["user-1"]);
2835
+ * ```
2836
+ *
2837
+ * Behavior is identical to `invoke` at runtime — same defer / direct-call
2838
+ * paths — so it's a drop-in safer overload.
2839
+ */
2840
+ invokeTyped(name, args) {
2841
+ return this.invoke(name, args);
2842
+ }
2645
2843
  // ============================================================
2646
2844
  // Static methods
2647
2845
  // ============================================================
@@ -2653,8 +2851,25 @@ var Frame = class _Frame extends EventEmitter {
2653
2851
  static getAll() {
2654
2852
  return frameRegistry;
2655
2853
  }
2656
- /** Get or create root frame */
2657
- static root(rootId) {
2854
+ /**
2855
+ * Returns the existing root frame, or `undefined` if none has been created.
2856
+ * Pure getter — never creates a Frame, never touches the DOM.
2857
+ *
2858
+ * Use `Frame.createRoot(id)` to create the root explicitly during framework
2859
+ * boot. For Micro-Frontend hosts that own multiple independent containers,
2860
+ * use `new Frame(containerId)` directly so each MF mount has its own root.
2861
+ */
2862
+ static getRoot() {
2863
+ return rootFrame;
2864
+ }
2865
+ /**
2866
+ * Create (or return) the singleton root frame for this app.
2867
+ *
2868
+ * Idempotent: subsequent calls always return the original root regardless
2869
+ * of `rootId` — so passing a different id later is silently ignored.
2870
+ * `Framework.boot()` is the canonical caller; user code rarely needs this.
2871
+ */
2872
+ static createRoot(rootId) {
2658
2873
  if (!rootFrame) {
2659
2874
  rootId = rootId || "root";
2660
2875
  let rootElement = document.getElementById(rootId);
@@ -2666,6 +2881,17 @@ var Frame = class _Frame extends EventEmitter {
2666
2881
  }
2667
2882
  return rootFrame;
2668
2883
  }
2884
+ /**
2885
+ * @deprecated Use `Frame.getRoot()` for read-only access or
2886
+ * `Frame.createRoot(id)` to create the root explicitly. The single-method
2887
+ * `root()` blurred the distinction and was a common source of bugs in
2888
+ * Micro-Frontend hosts.
2889
+ *
2890
+ * Kept for backward compatibility — behavior unchanged.
2891
+ */
2892
+ static root(rootId) {
2893
+ return _Frame.createRoot(rootId);
2894
+ }
2669
2895
  /** Bind event listener (static) */
2670
2896
  static on(event, handler) {
2671
2897
  staticEmitter.on(event, handler);
@@ -2681,6 +2907,9 @@ var Frame = class _Frame extends EventEmitter {
2681
2907
  staticEmitter.fire(event, data);
2682
2908
  }
2683
2909
  };
2910
+ function htmlElIsBound(element) {
2911
+ return !!element.frameBound;
2912
+ }
2684
2913
  function ensureElementId2(element) {
2685
2914
  const id = element.getAttribute("id");
2686
2915
  if (id) return id;
@@ -2710,8 +2939,8 @@ function notifyCreated(frameInstance) {
2710
2939
  const pId = frameInstance.parentId;
2711
2940
  if (pId) {
2712
2941
  const parent = frameRegistry.get(pId);
2713
- if (parent && !parent.readyMap[frameInstance.id]) {
2714
- parent.readyMap[frameInstance.id] = 1;
2942
+ if (parent && !parent.readyMap.has(frameInstance.id)) {
2943
+ parent.readyMap.add(frameInstance.id);
2715
2944
  parent.readyCount++;
2716
2945
  notifyCreated(parent);
2717
2946
  }
@@ -2726,9 +2955,9 @@ function notifyAlter(frameInstance, data) {
2726
2955
  const pId = frameInstance.parentId;
2727
2956
  if (pId) {
2728
2957
  const parent = frameRegistry.get(pId);
2729
- if (parent && parent.readyMap[frameInstance.id]) {
2958
+ if (parent && parent.readyMap.has(frameInstance.id)) {
2730
2959
  parent.readyCount--;
2731
- Reflect.deleteProperty(parent.readyMap, frameInstance.id);
2960
+ parent.readyMap.delete(frameInstance.id);
2732
2961
  notifyAlter(parent, data);
2733
2962
  }
2734
2963
  }
@@ -2741,7 +2970,7 @@ function reInitFrame(frame, id, parentId) {
2741
2970
  frame["childrenCount"] = 0;
2742
2971
  frame["readyCount"] = 0;
2743
2972
  frame["signature"] = 1;
2744
- frame["readyMap"] = {};
2973
+ frame["readyMap"] = /* @__PURE__ */ new Set();
2745
2974
  frame["invokeList"] = [];
2746
2975
  frameRegistry.set(id, frame);
2747
2976
  }
@@ -2749,7 +2978,7 @@ function reInitFrameForCache(frame) {
2749
2978
  Reflect.set(frame, "id", "");
2750
2979
  frame["_parentId"] = void 0;
2751
2980
  frame["childrenMap"] = {};
2752
- frame["readyMap"] = {};
2981
+ frame["readyMap"] = /* @__PURE__ */ new Set();
2753
2982
  }
2754
2983
  function translateQuery(pId, src, params) {
2755
2984
  const parentFrame = frameRegistry.get(pId);
@@ -2759,22 +2988,138 @@ function translateQuery(pId, src, params) {
2759
2988
  if (!parentRefData) return;
2760
2989
  if (src.indexOf(SPLITTER) > 0) {
2761
2990
  translateData(parentRefData, params);
2762
- if (params[SPLITTER]) {
2763
- assign(
2764
- params,
2765
- params[SPLITTER]
2766
- );
2991
+ const paramsRec = params;
2992
+ const splitterValue = paramsRec[SPLITTER];
2993
+ if (splitterValue && typeof splitterValue === "object") {
2994
+ assign(params, splitterValue);
2767
2995
  Reflect.deleteProperty(params, SPLITTER);
2768
2996
  }
2769
2997
  }
2770
2998
  }
2771
- function registerViewClass(viewPath, ViewClass) {
2772
- const parsed = parseUri(viewPath);
2773
- const path = parsed.path;
2774
- if (path) {
2775
- viewClassRegistry[path] = ViewClass;
2999
+
3000
+ // src/cross-site.ts
3001
+ var preparePromises = {};
3002
+ var projectsMap = null;
3003
+ function loadRemoteView(viewPath, bizCode) {
3004
+ const crossConfigs = config.crossConfigs || window.crossConfigs;
3005
+ const currentName = config.projectName || "";
3006
+ const slashIndex = viewPath.indexOf("/");
3007
+ const projectName = slashIndex > -1 ? viewPath.substring(0, slashIndex) : viewPath;
3008
+ if (projectName === currentName) return Promise.resolve();
3009
+ if (!preparePromises[projectName]) {
3010
+ if (!projectsMap) {
3011
+ const map = toMap(crossConfigs || [], "projectName");
3012
+ projectsMap = map;
3013
+ }
3014
+ const info = projectsMap[projectName];
3015
+ if (!info) {
3016
+ return Promise.reject(
3017
+ new Error(`Cannot find ${projectName} from crossConfigs`)
3018
+ );
3019
+ }
3020
+ preparePromises[projectName] = use(`${projectName}/prepare`).then((modules) => {
3021
+ let mod = modules[0];
3022
+ if (mod && typeof mod === "object" && mod !== null) {
3023
+ const rec = mod;
3024
+ if (rec["__esModule"]) {
3025
+ mod = rec["default"];
3026
+ }
3027
+ }
3028
+ if (typeof mod === "function") {
3029
+ return mod({ bizCode });
3030
+ }
3031
+ return void 0;
3032
+ }).catch((err) => {
3033
+ Reflect.deleteProperty(preparePromises, projectName);
3034
+ throw err;
3035
+ });
2776
3036
  }
3037
+ return preparePromises[projectName];
3038
+ }
3039
+ function resetProjectsMap() {
3040
+ projectsMap = null;
2777
3041
  }
3042
+ var skeletonTemplate = (data, viewId) => {
3043
+ let skeletonHtml = "<div>Loading...</div>";
3044
+ if (data && typeof data === "object") {
3045
+ const candidate = data.skeleton;
3046
+ if (typeof candidate === "string") skeletonHtml = candidate;
3047
+ }
3048
+ return `<div id="mf_${viewId}">${skeletonHtml}</div>`;
3049
+ };
3050
+ var CrossSite = View.extend({
3051
+ /** Skeleton template renders loading state + child container */
3052
+ template: skeletonTemplate,
3053
+ init(params) {
3054
+ this.$sign = 0;
3055
+ this.on("destroy", () => {
3056
+ this.$sign = -1;
3057
+ });
3058
+ this.assign?.(params);
3059
+ },
3060
+ assign(data) {
3061
+ this.$view = typeof data["view"] === "string" ? data["view"] : "";
3062
+ const nested = data["params"];
3063
+ const nestedParams = nested && typeof nested === "object" ? nested : {};
3064
+ this.$params = { ...data, ...nestedParams };
3065
+ this.updater.set({
3066
+ skeleton: data["skeleton"],
3067
+ skeletonParams: data["skeletonParams"] || {},
3068
+ bizCode: data["bizCode"]
3069
+ });
3070
+ if (this.$sign > 0) {
3071
+ this.updateView();
3072
+ }
3073
+ return false;
3074
+ },
3075
+ async updateView() {
3076
+ const sign = ++this.$sign;
3077
+ const stored = this.updater.get();
3078
+ const bizCode = stored.bizCode;
3079
+ try {
3080
+ await loadRemoteView(this.$view, bizCode);
3081
+ } catch (ex) {
3082
+ const node = document.getElementById("mf_" + this.id);
3083
+ if (node) {
3084
+ const err = ex instanceof Error ? ex : new Error(String(ex));
3085
+ node.innerHTML = err.message || String(err);
3086
+ }
3087
+ }
3088
+ if (this.$sign !== sign) return;
3089
+ const mf = Frame.get("mf_" + this.id);
3090
+ const parsedNew = parseUri(this.$view);
3091
+ const newPath = parsedNew.path;
3092
+ const oldPath = mf?.viewPath ? parseUri(mf.viewPath).path : "";
3093
+ const view = mf?.view;
3094
+ if (newPath === oldPath && view && typeof view.assign === "function") {
3095
+ const result = funcWithTry(view.assign, [this.$params], view, noop);
3096
+ if (result) {
3097
+ view.render();
3098
+ }
3099
+ return;
3100
+ }
3101
+ const owner = this.owner;
3102
+ if (owner && typeof owner !== "number") {
3103
+ owner.mountFrame("mf_" + this.id, this.$view, this.$params);
3104
+ }
3105
+ },
3106
+ render() {
3107
+ const params = this.$params;
3108
+ this.updater.digest({
3109
+ skeleton: params?.["skeleton"]
3110
+ });
3111
+ this.updateView();
3112
+ },
3113
+ /**
3114
+ * Invoke a method on the remote view.
3115
+ * Usage: mf.invoke('callView', methodName, ...args)
3116
+ */
3117
+ callView(name, ...args) {
3118
+ const mf = Frame.get("mf_" + this.id);
3119
+ return mf?.invoke(name, args);
3120
+ }
3121
+ });
3122
+ var cross_site_default = CrossSite;
2778
3123
 
2779
3124
  // src/service.ts
2780
3125
  var Payload = class {
@@ -2962,29 +3307,31 @@ var Service = class {
2962
3307
  * Get metadata for an API endpoint.
2963
3308
  */
2964
3309
  static meta(attrs) {
2965
- const name = typeof attrs === "string" ? attrs : attrs["name"];
2966
- return this._metaList[name] || attrs;
3310
+ const name = typeof attrs === "string" ? attrs : String(attrs["name"] ?? "");
3311
+ const known = this._metaList[name];
3312
+ if (known) return known;
3313
+ return attrs;
2967
3314
  }
2968
3315
  /**
2969
3316
  * Create a Payload for an API request.
2970
3317
  */
2971
3318
  static create(attrs) {
2972
3319
  const meta = this.meta(attrs);
2973
- const cache = attrs["cache"] | 0 || meta.cache || 0;
3320
+ const cache = toCacheValue(attrs["cache"]) || meta.cache || 0;
2974
3321
  const entity = new Payload();
2975
3322
  entity.set(meta);
2976
3323
  entity.cacheInfo = {
2977
3324
  name: meta.name,
2978
- after: meta.after,
2979
- cleans: meta.cleanKeys,
3325
+ after: typeof meta.after === "function" ? meta.after : void 0,
3326
+ cleans: typeof meta.cleanKeys === "string" ? meta.cleanKeys : void 0,
2980
3327
  key: cache ? defaultCacheKey(meta, attrs) : "",
2981
3328
  time: 0
2982
3329
  };
2983
- if (typeof attrs === "object" && attrs !== null) {
3330
+ if (attrs !== null) {
2984
3331
  entity.set(attrs);
2985
3332
  }
2986
3333
  const before = meta.before;
2987
- if (before) {
3334
+ if (typeof before === "function") {
2988
3335
  funcWithTry(before, [entity], entity, noop);
2989
3336
  }
2990
3337
  this._staticEmitter.fire("begin", { payload: entity });
@@ -3010,7 +3357,7 @@ var Service = class {
3010
3357
  */
3011
3358
  static cached(attrs) {
3012
3359
  const meta = this.meta(attrs);
3013
- const cache = attrs["cache"] | 0 || meta.cache || 0;
3360
+ const cache = toCacheValue(attrs["cache"]) || meta.cache || 0;
3014
3361
  let cacheKey = "";
3015
3362
  if (cache) {
3016
3363
  cacheKey = defaultCacheKey(meta, attrs);
@@ -3018,7 +3365,8 @@ var Service = class {
3018
3365
  if (cacheKey) {
3019
3366
  const info = this._pendingCacheKeys[cacheKey];
3020
3367
  if (info) {
3021
- return info.entity;
3368
+ const entity = info.entity;
3369
+ return entity instanceof Payload ? entity : void 0;
3022
3370
  }
3023
3371
  const cached = this._payloadCache.get(cacheKey);
3024
3372
  if (cached && cached.cacheInfo) {
@@ -3035,17 +3383,14 @@ var Service = class {
3035
3383
  * Clear cached payloads by endpoint name.
3036
3384
  */
3037
3385
  static clear(names) {
3038
- const nameList = (typeof names === "string" ? names : names.join(",")).split(",");
3039
- const nameSet = {};
3040
- for (const n of nameList) {
3041
- nameSet[n] = 1;
3042
- }
3386
+ const nameSet = new Set(
3387
+ (typeof names === "string" ? names : names.join(",")).split(",")
3388
+ );
3043
3389
  const keysToDelete = [];
3044
3390
  this._payloadCache.forEach((payload) => {
3045
- if (payload?.cacheInfo && nameSet[payload.cacheInfo.name]) {
3046
- if (payload.cacheInfo.key) {
3047
- keysToDelete.push(payload.cacheInfo.key);
3048
- }
3391
+ const info = payload?.cacheInfo;
3392
+ if (info && info.key && nameSet.has(info.name)) {
3393
+ keysToDelete.push(info.key);
3049
3394
  }
3050
3395
  });
3051
3396
  for (const key of keysToDelete) {
@@ -3064,12 +3409,23 @@ var Service = class {
3064
3409
  }
3065
3410
  /**
3066
3411
  * Create a new Service subclass with a custom sync function.
3067
- * Each subclass gets its own per-type state (metaList, cache, etc.)
3068
- * to ensure isolation between different Service types.
3412
+ *
3413
+ * Each subclass gets its OWN copies of every per-type static field
3414
+ * (`_metaList`, `_payloadCache`, `_pendingCacheKeys`, `_syncFn`,
3415
+ * `_staticEmitter`, `_cacheMax`, `_cacheBuffer`) via `static override`.
3416
+ * This is intentional: it ensures that endpoint metadata, cache state,
3417
+ * in-flight dedup keys, and event subscribers are fully isolated between
3418
+ * different Service types, even when one extends another.
3419
+ *
3420
+ * **Do not refactor these `static override` declarations away** — sharing
3421
+ * them through prototype inheritance would let endpoints registered on one
3422
+ * subclass leak into another, and the LFU cache evictions of one type
3423
+ * would race with those of another.
3069
3424
  */
3070
3425
  static extend(newSyncFn, newCacheMax, newCacheBuffer) {
3071
3426
  const ParentService = this;
3072
3427
  class ChildService extends ParentService {
3428
+ // Intentionally per-subclass — see Service.extend doc.
3073
3429
  static _metaList = {};
3074
3430
  static _payloadCache = new Cache({
3075
3431
  maxSize: newCacheMax || ParentService._cacheMax,
@@ -3084,18 +3440,34 @@ var Service = class {
3084
3440
  return ChildService;
3085
3441
  }
3086
3442
  };
3443
+ var metaJsonCache = /* @__PURE__ */ new WeakMap();
3444
+ function getMetaJson(meta) {
3445
+ let cached = metaJsonCache.get(meta);
3446
+ if (cached === void 0) {
3447
+ cached = JSON.stringify(meta);
3448
+ metaJsonCache.set(meta, cached);
3449
+ }
3450
+ return cached;
3451
+ }
3087
3452
  function defaultCacheKey(meta, attrs) {
3088
- return JSON.stringify(attrs) + SPLITTER + JSON.stringify(meta);
3453
+ return JSON.stringify(attrs) + SPLITTER + getMetaJson(meta);
3454
+ }
3455
+ function toCacheValue(v) {
3456
+ if (typeof v === "number") return v | 0;
3457
+ if (typeof v === "string") {
3458
+ const n = Number(v);
3459
+ return Number.isFinite(n) ? n | 0 : 0;
3460
+ }
3461
+ return 0;
3089
3462
  }
3090
3463
  function serviceSend(service, attrs, done, flag, save) {
3091
- if (service["destroyed"]) return;
3092
- if (service["busy"]) {
3093
- service.enqueue(
3094
- serviceSend.bind(null, service, attrs, done, flag, save)
3095
- );
3464
+ if (service.destroyed) return;
3465
+ if (service.busy) {
3466
+ const queued = () => serviceSend(service, attrs, done, flag, save);
3467
+ service.enqueue(queued);
3096
3468
  return;
3097
3469
  }
3098
- service["busy"] = 1;
3470
+ service.busy = 1;
3099
3471
  let attrList;
3100
3472
  if (typeof attrs === "string") {
3101
3473
  attrList = [{ name: attrs }];
@@ -3120,10 +3492,10 @@ function serviceSend(service, attrs, done, flag, save) {
3120
3492
  newPayload = true;
3121
3493
  staticEmitter2.fire("done", { payload });
3122
3494
  }
3123
- if (!service["destroyed"]) {
3495
+ if (!service.destroyed) {
3124
3496
  const finish = requestCount === total;
3125
3497
  if (finish) {
3126
- service["busy"] = 0;
3498
+ service.busy = 0;
3127
3499
  if (flag === FETCH_FLAGS_ALL) {
3128
3500
  doneArr[0] = errorArgs;
3129
3501
  funcWithTry(done, doneArr, service, noop);
@@ -3140,10 +3512,7 @@ function serviceSend(service, attrs, done, flag, save) {
3140
3512
  for (const attr of attrList) {
3141
3513
  if (!attr) continue;
3142
3514
  const attrObj = typeof attr === "string" ? { name: attr } : attr;
3143
- const payloadInfo = service.type.get(
3144
- attrObj,
3145
- save
3146
- );
3515
+ const payloadInfo = service.type.get(attrObj, save);
3147
3516
  const payloadEntity = payloadInfo.entity;
3148
3517
  const cacheKey = payloadEntity.cacheInfo?.key || "";
3149
3518
  const complete = remoteComplete.bind(null, requestCount++);
@@ -3157,15 +3526,13 @@ function serviceSend(service, attrs, done, flag, save) {
3157
3526
  const cacheComplete = () => {
3158
3527
  const list = pendingCacheKeys[cacheKey];
3159
3528
  const entity = list.entity;
3160
- if (entity.cacheInfo) {
3529
+ if (entity instanceof Payload && entity.cacheInfo) {
3161
3530
  entity.cacheInfo.time = now();
3531
+ internals.payloadCache.set(cacheKey, entity);
3162
3532
  }
3163
- internals.payloadCache.set(cacheKey, entity);
3164
3533
  Reflect.deleteProperty(pendingCacheKeys, cacheKey);
3165
3534
  for (const cb of list) {
3166
- if (typeof cb === "function") {
3167
- cb();
3168
- }
3535
+ if (typeof cb === "function") cb();
3169
3536
  }
3170
3537
  };
3171
3538
  syncFn(payloadEntity, cacheComplete);
@@ -3178,12 +3545,14 @@ function serviceSend(service, attrs, done, flag, save) {
3178
3545
  }
3179
3546
  }
3180
3547
 
3181
- // src/frame-visualizer.ts
3182
- var MSG_PING = "LARK_VISUALIZER_PING";
3183
- var MSG_PONG = "LARK_VISUALIZER_PONG";
3184
- var MSG_REQUEST_TREE = "LARK_VISUALIZER_REQUEST_TREE";
3185
- var MSG_TREE = "LARK_VISUALIZER_TREE";
3186
- var MSG_TREE_DELTA = "LARK_VISUALIZER_TREE_DELTA";
3548
+ // src/frame-visual.ts
3549
+ var FrameVisualBridge = {
3550
+ MSG_PING: "LARK_VIS_PING",
3551
+ MSG_PONG: "LARK_VIS_PONG",
3552
+ MSG_REQUEST_TREE: "LARK_VIS_REQUEST_TREE",
3553
+ MSG_TREE: "LARK_VIS_TREE",
3554
+ MSG_TREE_DELTA: "LARK_VIS_TREE_DELTA"
3555
+ };
3187
3556
  function serializeView(view) {
3188
3557
  return {
3189
3558
  id: view.id,
@@ -3223,7 +3592,10 @@ function serializeFrame(frameId) {
3223
3592
  };
3224
3593
  }
3225
3594
  function serializeFrameTree() {
3226
- const root = Frame.root();
3595
+ const root = Frame.getRoot();
3596
+ if (!root) {
3597
+ return { root: null, totalFrames: 0, timestamp: Date.now(), rootId: "" };
3598
+ }
3227
3599
  const rootNode = serializeFrame(root.id);
3228
3600
  let totalFrames = 0;
3229
3601
  const countFrames = (node) => {
@@ -3251,19 +3623,22 @@ function installFrameVisualizerBridge() {
3251
3623
  const data = event.data;
3252
3624
  if (!data || typeof data !== "object") return;
3253
3625
  const type = data.type;
3254
- if (type === MSG_PING) {
3626
+ if (type === FrameVisualBridge.MSG_PING) {
3255
3627
  const source = event.source;
3256
3628
  if (source) {
3257
- source.postMessage({ type: MSG_PONG }, { targetOrigin: "*" });
3629
+ source.postMessage(
3630
+ { type: FrameVisualBridge.MSG_PONG },
3631
+ { targetOrigin: "*" }
3632
+ );
3258
3633
  }
3259
3634
  return;
3260
3635
  }
3261
- if (type === MSG_REQUEST_TREE) {
3636
+ if (type === FrameVisualBridge.MSG_REQUEST_TREE) {
3262
3637
  const tree = serializeFrameTree();
3263
3638
  const source = event.source;
3264
3639
  if (source) {
3265
3640
  source.postMessage(
3266
- { type: MSG_TREE, data: tree },
3641
+ { type: FrameVisualBridge.MSG_TREE, data: tree },
3267
3642
  { targetOrigin: "*" }
3268
3643
  );
3269
3644
  }
@@ -3282,18 +3657,14 @@ function pushTreeUpdate() {
3282
3657
  const treeJson = JSON.stringify(tree);
3283
3658
  if (treeJson !== lastTreeJson) {
3284
3659
  lastTreeJson = treeJson;
3285
- window.parent.postMessage({ type: MSG_TREE_DELTA, data: tree }, "*");
3660
+ window.parent.postMessage(
3661
+ { type: FrameVisualBridge.MSG_TREE_DELTA, data: tree },
3662
+ "*"
3663
+ );
3286
3664
  }
3287
3665
  }
3288
3666
 
3289
3667
  // src/framework.ts
3290
- var config = {
3291
- rootId: "root",
3292
- hashbang: "#!",
3293
- error: (error) => {
3294
- throw error;
3295
- }
3296
- };
3297
3668
  var booted3 = false;
3298
3669
  var taskList = [];
3299
3670
  var taskIndex = 0;
@@ -3342,6 +3713,9 @@ function task(fn, args, context) {
3342
3713
  }
3343
3714
  }
3344
3715
  var dispatcherUpdateTag = 0;
3716
+ function isThenable(value) {
3717
+ return !!value && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
3718
+ }
3345
3719
  function viewIsObserveChanged(view) {
3346
3720
  const loc = view.locationObserved;
3347
3721
  let result = false;
@@ -3355,7 +3729,7 @@ function viewIsObserveChanged(view) {
3355
3729
  const changedParams = lastChanged2?.params;
3356
3730
  if (changedParams) {
3357
3731
  for (const key of loc.keys) {
3358
- result = has(changedParams, key);
3732
+ result = hasOwnProperty(changedParams, key);
3359
3733
  if (result) break;
3360
3734
  }
3361
3735
  }
@@ -3367,48 +3741,59 @@ function stateIsObserveChanged(view, stateKeys) {
3367
3741
  const observedKeys = view.observedStateKeys;
3368
3742
  if (!observedKeys) return false;
3369
3743
  for (const key of observedKeys) {
3370
- if (has(stateKeys, key)) return true;
3744
+ if (stateKeys.has(key)) return true;
3371
3745
  }
3372
3746
  return false;
3373
3747
  }
3374
3748
  function dispatcherUpdate(frame, stateKeys) {
3375
- const frameInternal = frame;
3376
- const view = frame.view;
3377
- if (!view || frameInternal.dispatcherUpdateTag === dispatcherUpdateTag || view.signature <= 1) {
3378
- return;
3379
- }
3380
- frameInternal.dispatcherUpdateTag = dispatcherUpdateTag;
3381
- const isChanged = stateKeys ? stateIsObserveChanged(view, stateKeys) : viewIsObserveChanged(view);
3382
- let renderPromise;
3383
- if (isChanged) {
3384
- const renderResult = funcWithTry(
3385
- view.renderMethod ?? view.render,
3386
- [],
3387
- view,
3388
- noop
3389
- );
3390
- if (renderResult && typeof renderResult.then === "function") {
3391
- renderPromise = renderResult;
3392
- }
3393
- }
3394
- const children = frame.children();
3395
- const recurse = () => {
3396
- for (const childId of children) {
3397
- const childFrame = Frame.get(childId);
3398
- if (childFrame) {
3399
- dispatcherUpdate(childFrame, stateKeys);
3749
+ const stack = [frame];
3750
+ const drain = (s) => {
3751
+ while (s.length > 0) {
3752
+ const current = s.pop();
3753
+ const tagged = current;
3754
+ const view = current.view;
3755
+ if (!view || tagged.dispatcherUpdateTag === dispatcherUpdateTag || view.signature <= 1) {
3756
+ continue;
3757
+ }
3758
+ tagged.dispatcherUpdateTag = dispatcherUpdateTag;
3759
+ const isChanged = stateKeys ? stateIsObserveChanged(view, stateKeys) : viewIsObserveChanged(view);
3760
+ let renderPromise;
3761
+ if (isChanged) {
3762
+ const renderResult = funcWithTry(
3763
+ view.renderMethod ?? view.render,
3764
+ [],
3765
+ view,
3766
+ noop
3767
+ );
3768
+ if (isThenable(renderResult)) {
3769
+ renderPromise = renderResult;
3770
+ }
3771
+ }
3772
+ const children = current.children();
3773
+ if (renderPromise) {
3774
+ renderPromise.then(() => {
3775
+ const subStack = [];
3776
+ for (let i = children.length - 1; i >= 0; i--) {
3777
+ const child = Frame.get(children[i]);
3778
+ if (child) subStack.push(child);
3779
+ }
3780
+ drain(subStack);
3781
+ });
3782
+ } else {
3783
+ for (let i = children.length - 1; i >= 0; i--) {
3784
+ const child = Frame.get(children[i]);
3785
+ if (child) s.push(child);
3786
+ }
3400
3787
  }
3401
3788
  }
3402
3789
  };
3403
- if (renderPromise) {
3404
- renderPromise.then(recurse);
3405
- } else {
3406
- recurse();
3407
- }
3790
+ drain(stack);
3408
3791
  }
3409
3792
  function dispatcherNotifyChange(e) {
3410
- const rootFrame2 = Frame.root();
3411
- const view = e.view;
3793
+ const rootFrame2 = Frame.getRoot();
3794
+ if (!rootFrame2) return;
3795
+ const routeEvent = e;
3796
+ const view = routeEvent.view;
3412
3797
  if (view) {
3413
3798
  const viewPath = typeof view === "object" && view !== null ? String(view.to || "") : String(view);
3414
3799
  rootFrame2.mountView(viewPath);
@@ -3425,19 +3810,6 @@ function dispatchEvent(target, eventType, eventInit) {
3425
3810
  });
3426
3811
  target.dispatchEvent(event);
3427
3812
  }
3428
- function use(names, callback) {
3429
- if (!config.require) {
3430
- if (callback) callback();
3431
- return;
3432
- }
3433
- const nameList = typeof names === "string" ? [names] : names;
3434
- const result = config.require(nameList);
3435
- if (result && typeof result.then === "function") {
3436
- result.then((modules) => {
3437
- if (callback) callback(modules);
3438
- });
3439
- }
3440
- }
3441
3813
  var WAIT_OK = 1;
3442
3814
  var WAIT_TIMEOUT_OR_NOT_FOUND = 0;
3443
3815
  function waitZoneViewsRendered(viewId, timeout) {
@@ -3460,12 +3832,27 @@ function waitZoneViewsRendered(viewId, timeout) {
3460
3832
  setTimeout(check, 9);
3461
3833
  });
3462
3834
  }
3835
+ function getConfigImpl(key) {
3836
+ if (key === void 0) return config;
3837
+ return config[key];
3838
+ }
3463
3839
  var Framework = {
3464
3840
  // ============================================================
3465
3841
  // Lifecycle
3466
3842
  // ============================================================
3843
+ /** Read framework configuration. See `FrameworkInterface.getConfig`. */
3844
+ getConfig: getConfigImpl,
3467
3845
  /**
3468
- * Get or set framework configuration.
3846
+ * Merge a patch into framework configuration. See `FrameworkInterface.setConfig`.
3847
+ */
3848
+ setConfig(patch) {
3849
+ if (patch && typeof patch === "object") {
3850
+ assign(config, patch);
3851
+ }
3852
+ return config;
3853
+ },
3854
+ /**
3855
+ * @deprecated Use `getConfig()` / `setConfig()`. Behavior unchanged.
3469
3856
  */
3470
3857
  config(cfg) {
3471
3858
  if (!cfg) {
@@ -3486,17 +3873,17 @@ var Framework = {
3486
3873
  }
3487
3874
  Router._setConfig(config);
3488
3875
  EventDelegator.setFrameGetter((id) => Frame.get(id));
3489
- Router.on(ROUTER_EVENTS.CHANGED, (data) => {
3876
+ Router.on(RouterEvents.CHANGED, (data) => {
3490
3877
  if (data) dispatcherNotifyChange(data);
3491
3878
  });
3492
- State.on(ROUTER_EVENTS.CHANGED, (data) => {
3879
+ State.on(RouterEvents.CHANGED, (data) => {
3493
3880
  if (data) dispatcherNotifyChange(data);
3494
3881
  });
3495
3882
  booted3 = true;
3496
3883
  markBooted();
3497
3884
  markRouterBooted();
3498
3885
  installFrameVisualizerBridge();
3499
- const rootFrame2 = Frame.root(config.rootId);
3886
+ const rootFrame2 = Frame.createRoot(config.rootId);
3500
3887
  Router._bind();
3501
3888
  const defaultView = config.defaultView || "";
3502
3889
  if (defaultView && !rootFrame2.view) {
@@ -3551,7 +3938,7 @@ var Framework = {
3551
3938
  /**
3552
3939
  * Check if object has own property.
3553
3940
  */
3554
- has,
3941
+ has: hasOwnProperty,
3555
3942
  /**
3556
3943
  * Get object keys.
3557
3944
  */
@@ -3611,6 +3998,9 @@ if (typeof window !== "undefined") {
3611
3998
  window.__lark_Router = Router;
3612
3999
  window.__lark_Frame = Frame;
3613
4000
  window.__lark_View = View;
4001
+ window.__lark_invalidateViewClass = invalidateViewClass;
4002
+ window.__lark_getViewClassRegistry = getViewClassRegistry;
4003
+ window.__lark_registerViewClass = registerViewClass;
3614
4004
  }
3615
4005
 
3616
4006
  // src/store.ts
@@ -3624,28 +4014,38 @@ var Platform = /* @__PURE__ */ ((Platform2) => {
3624
4014
  var isFunction = (val) => typeof val === "function";
3625
4015
  var isObject = (val) => val !== null && typeof val === "object";
3626
4016
  var isPromise = (val) => isObject(val) && isFunction(val["then"]);
3627
- var hasOwnProperty = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
3628
- var deepClone = (obj) => {
4017
+ var hasOwnProperty2 = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
4018
+ var hasStructuredClone = typeof globalThis !== "undefined" && typeof globalThis.structuredClone === "function";
4019
+ var deepCloneFallback = (obj) => {
3629
4020
  if (!obj || !isObject(obj)) return {};
3630
- const newData = Array.isArray(obj) ? [] : Object.create(Object.getPrototypeOf(obj));
4021
+ const newData = Array.isArray(obj) ? [] : {};
3631
4022
  for (const key in obj) {
3632
- if (hasOwnProperty(obj, key)) {
4023
+ if (hasOwnProperty2(obj, key)) {
3633
4024
  const value = obj[key];
3634
- newData[key] = isObject(value) ? deepClone(value) : value;
4025
+ newData[key] = isObject(value) ? deepCloneFallback(value) : value;
3635
4026
  }
3636
4027
  }
3637
4028
  return newData;
3638
4029
  };
4030
+ var deepClone = (obj) => {
4031
+ if (hasStructuredClone) {
4032
+ try {
4033
+ return structuredClone(obj);
4034
+ } catch {
4035
+ return deepCloneFallback(obj);
4036
+ }
4037
+ }
4038
+ return deepCloneFallback(obj);
4039
+ };
3639
4040
  var cloneData = (data) => isObject(data) ? deepClone(data) : data;
3640
4041
  var getDataByKey = (target, key) => {
3641
- let data;
3642
- const rec = target;
3643
- if (key.includes(".")) {
3644
- key.split(".").forEach((k, index) => {
3645
- data = index === 0 ? rec?.[k] : data?.[k];
3646
- });
3647
- } else {
3648
- data = rec?.[key];
4042
+ if (!key.includes(".")) {
4043
+ return target[key];
4044
+ }
4045
+ let data = target;
4046
+ for (const k of key.split(".")) {
4047
+ if (!isObject(data)) return void 0;
4048
+ data = data[k];
3649
4049
  }
3650
4050
  return data;
3651
4051
  };
@@ -3693,17 +4093,18 @@ var Queue = class {
3693
4093
  };
3694
4094
  var addParams2Callback = (cb, params) => {
3695
4095
  if (!cb || !params) return;
3696
- const cbObj = cb;
3697
- if (isObject(cb) && isObject(cbObj["params"])) {
3698
- Object.assign(cbObj["params"], params);
4096
+ const tagged = cb;
4097
+ const existing = tagged.params;
4098
+ if (isObject(existing)) {
4099
+ Object.assign(existing, params);
3699
4100
  } else {
3700
- cbObj["params"] = params;
4101
+ tagged.params = params;
3701
4102
  }
3702
4103
  };
3703
4104
  var runTask = (cb) => {
3704
- const cbObj = cb;
3705
- const params = cbObj["params"];
3706
- delete cbObj["params"];
4105
+ const tagged = cb;
4106
+ const params = tagged.params;
4107
+ delete tagged.params;
3707
4108
  try {
3708
4109
  cb(params);
3709
4110
  } catch {
@@ -3729,7 +4130,9 @@ var ArrMethods = {};
3729
4130
  if (res === -1 || res === false) {
3730
4131
  res = rawMethod.apply(
3731
4132
  this,
3732
- args.map((item) => ProxyCache.get(item) ?? item)
4133
+ args.map(
4134
+ (item) => isObject(item) ? ProxyCache.get(item) ?? item : item
4135
+ )
3733
4136
  );
3734
4137
  }
3735
4138
  return res;
@@ -3747,6 +4150,7 @@ var inArrUpdate = false;
3747
4150
  });
3748
4151
  function needKeepArrItem(target, newVal, key) {
3749
4152
  if (!Array.isArray(target) || !inArrUpdate) return false;
4153
+ if (!isObject(newVal)) return false;
3750
4154
  return getLinkKeys(newVal) === createLinkKeys(target, key);
3751
4155
  }
3752
4156
  var defStateConfig = {
@@ -3758,13 +4162,13 @@ var StateConfigMap = /* @__PURE__ */ new WeakMap();
3758
4162
  var setStateConfig = (target, config2) => {
3759
4163
  if (target && isObject(config2)) StateConfigMap.set(target, config2);
3760
4164
  };
3761
- var getStateConfig = (target, key) => {
4165
+ function getStateConfig(target, key) {
3762
4166
  if (!StateConfigMap.has(target)) {
3763
4167
  return void 0;
3764
4168
  }
3765
4169
  const config2 = StateConfigMap.get(target);
3766
4170
  return key ? config2?.[key] : config2;
3767
- };
4171
+ }
3768
4172
  var isState = (target) => isObject(target) && StateConfigMap.has(target);
3769
4173
  var createLinkKeys = (target, property) => {
3770
4174
  if (!hasLinkKeys(target)) return property;
@@ -3802,7 +4206,7 @@ var needKeep = (key) => {
3802
4206
  };
3803
4207
  var canSetNewVal = (params) => {
3804
4208
  const { property, newVal, oldVal } = params;
3805
- if (hasOwnProperty(params.target, property) && newVal === oldVal)
4209
+ if (hasOwnProperty2(params.target, property) && newVal === oldVal)
3806
4210
  return false;
3807
4211
  return true;
3808
4212
  };
@@ -3814,16 +4218,13 @@ var getNewVal = (params) => {
3814
4218
  if (config2?.shallow) return newVal;
3815
4219
  if (needKeepVal) return newVal;
3816
4220
  const linkKeys = createLinkKeys(target, property);
3817
- const newState = createState(newVal, {
3818
- ...config2,
3819
- linkKeys
3820
- });
4221
+ const newState = createState(newVal, { ...config2, linkKeys });
3821
4222
  if (isPromise(newVal)) handlePromise(newVal, target, property);
3822
4223
  return newState;
3823
4224
  };
3824
4225
  var genPayload = (params) => {
3825
4226
  const { target, property, newVal, oldVal } = params;
3826
- const config2 = getStateConfig(target) || defStateConfig;
4227
+ const config2 = getStateConfig(target) ?? defStateConfig;
3827
4228
  return {
3828
4229
  belong: config2.belong || LARK_GLOBAL,
3829
4230
  target,
@@ -3833,11 +4234,12 @@ var genPayload = (params) => {
3833
4234
  };
3834
4235
  };
3835
4236
  var handlePromise = (child, parent, key) => {
4237
+ const childObj = child;
3836
4238
  child.then((res) => {
3837
4239
  const parentProxy = ProxyCache.get(parent);
3838
4240
  if (parentProxy) {
3839
4241
  const oldVal = parentProxy[key];
3840
- if (ProxyCache.get(child) === oldVal) {
4242
+ if (ProxyCache.get(childObj) === oldVal) {
3841
4243
  parentProxy[key] = res;
3842
4244
  }
3843
4245
  }
@@ -3845,7 +4247,7 @@ var handlePromise = (child, parent, key) => {
3845
4247
  const parentProxy = ProxyCache.get(parent);
3846
4248
  if (parentProxy) {
3847
4249
  const oldVal = parentProxy[key];
3848
- if (ProxyCache.get(child) === oldVal) {
4250
+ if (ProxyCache.get(childObj) === oldVal) {
3849
4251
  parentProxy[key] = err;
3850
4252
  }
3851
4253
  }
@@ -3857,7 +4259,7 @@ var mark2 = (host, key) => {
3857
4259
  let sign;
3858
4260
  if (!host[_deleteKey]) {
3859
4261
  const markHost = host[_markObjKey] || (host[_markObjKey] = {});
3860
- if (!hasOwnProperty(markHost, key)) {
4262
+ if (!hasOwnProperty2(markHost, key)) {
3861
4263
  markHost[key] = 0;
3862
4264
  }
3863
4265
  sign = ++markHost[key];
@@ -3894,7 +4296,7 @@ function createState(initialData, config2) {
3894
4296
  oldVal
3895
4297
  });
3896
4298
  if (!canSet) return true;
3897
- const proxyTarget = receiver || state;
4299
+ const proxyTarget = isObject(receiver) && receiver !== null ? receiver : state;
3898
4300
  newVal = getNewVal({
3899
4301
  target: proxyTarget,
3900
4302
  property: strProp,
@@ -3914,7 +4316,7 @@ function createState(initialData, config2) {
3914
4316
  },
3915
4317
  deleteProperty(target, property) {
3916
4318
  const strProp = property;
3917
- if (!hasOwnProperty(target, strProp)) return true;
4319
+ if (!hasOwnProperty2(target, strProp)) return true;
3918
4320
  const oldVal = Reflect.get(target, property);
3919
4321
  Reflect.deleteProperty(target, property);
3920
4322
  const payload = genPayload({
@@ -3958,11 +4360,8 @@ function shallowSet(target, key, data) {
3958
4360
  keep(key);
3959
4361
  const config2 = getStateConfig(target);
3960
4362
  const linkKeys = createLinkKeys(target, key);
3961
- target[key] = createState(data, {
3962
- ...config2,
3963
- linkKeys,
3964
- shallow: true
3965
- });
4363
+ target[key] = createState(data, { ...config2, linkKeys, shallow: true });
4364
+ return target[key];
3966
4365
  }
3967
4366
  var GlobalDeps = /* @__PURE__ */ new Map();
3968
4367
  function track(payload) {
@@ -4043,8 +4442,22 @@ var _stateKeys = /* @__PURE__ */ Symbol("state-keys");
4043
4442
  var _storeState = /* @__PURE__ */ Symbol("store-state");
4044
4443
  var _storeDefScheduler = /* @__PURE__ */ Symbol("store-def-scheduler");
4045
4444
  var _storeProxy = /* @__PURE__ */ Symbol("fn:store-proxy");
4445
+ var _computedKeys = /* @__PURE__ */ Symbol("store-computed-keys");
4446
+ var COMPUTED_BRAND = /* @__PURE__ */ Symbol("store-computed-brand");
4447
+ function computed(deps, fn) {
4448
+ const marker = {
4449
+ [COMPUTED_BRAND]: true,
4450
+ deps,
4451
+ fn
4452
+ };
4453
+ return marker;
4454
+ }
4455
+ function isComputedMarker(val) {
4456
+ return isObject(val) && val[COMPUTED_BRAND] === true;
4457
+ }
4046
4458
  var BaseStore = class {
4047
4459
  [_storeStatus] = 0 /* BEFORE_CREATE */;
4460
+ [_computedKeys] = /* @__PURE__ */ new Set();
4048
4461
  [_storeDefScheduler] = getDefScheduler;
4049
4462
  [_storeBoot]() {
4050
4463
  this[_storeStatus] = 2 /* ACTIVE */;
@@ -4067,10 +4480,16 @@ var BaseStore = class {
4067
4480
  if (isObject(body)) {
4068
4481
  const state = {};
4069
4482
  const handlers = {};
4483
+ const computedDefs = {};
4484
+ const computedKeys = this[_computedKeys];
4070
4485
  Reflect.ownKeys(body).forEach((key) => {
4071
4486
  const strKey = key;
4072
4487
  const val = body[strKey];
4073
- if (isFunction(val)) {
4488
+ if (isComputedMarker(val)) {
4489
+ computedDefs[strKey] = val;
4490
+ state[strKey] = void 0;
4491
+ computedKeys.add(strKey);
4492
+ } else if (isFunction(val)) {
4074
4493
  if (!excludeFns.includes(strKey)) handlers[strKey] = val;
4075
4494
  } else {
4076
4495
  state[strKey] = val;
@@ -4080,6 +4499,22 @@ var BaseStore = class {
4080
4499
  this[_originState] = cloneData(state);
4081
4500
  this[_stateKeys] = Object.keys(state);
4082
4501
  this[_storeState] = createState(state, { belong: this[_storeName] });
4502
+ if (Object.keys(computedDefs).length > 0) {
4503
+ const belong = this[_storeName];
4504
+ const storeState = this[_storeState];
4505
+ for (const key of Object.keys(computedDefs)) {
4506
+ const def = computedDefs[key];
4507
+ const recompute = () => {
4508
+ storeState[key] = def.fn();
4509
+ };
4510
+ recompute();
4511
+ const trackList = def.deps.map((depKey) => ({
4512
+ key: depKey,
4513
+ cb: recompute
4514
+ }));
4515
+ if (trackList.length > 0) track({ belong, trackList });
4516
+ }
4517
+ }
4083
4518
  }
4084
4519
  }
4085
4520
  constructor(name, config2) {
@@ -4101,18 +4536,21 @@ var BaseStore = class {
4101
4536
  }
4102
4537
  [_storeProxy](toOut = false) {
4103
4538
  const self = this;
4104
- return new Proxy(self, {
4539
+ const proxy = new Proxy(self, {
4105
4540
  get(target, property) {
4106
- if (self[_stateKeys].includes(property)) {
4107
- const val = self[_storeState][property];
4541
+ const strProp = property;
4542
+ if (self[_stateKeys].includes(strProp)) {
4543
+ const val = self[_storeState][strProp];
4108
4544
  return toOut ? cloneData(val) : val;
4109
4545
  }
4110
4546
  return Reflect.get(target, property);
4111
4547
  },
4112
4548
  set(_target, property, val) {
4113
4549
  if (toOut) return true;
4114
- if (self[_stateKeys].includes(property)) {
4115
- self[_storeState][property] = val;
4550
+ const strProp = property;
4551
+ if (self[_computedKeys].has(strProp)) return true;
4552
+ if (self[_stateKeys].includes(strProp)) {
4553
+ self[_storeState][strProp] = val;
4116
4554
  }
4117
4555
  return true;
4118
4556
  },
@@ -4120,11 +4558,14 @@ var BaseStore = class {
4120
4558
  return Reflect.has(target, property) || self[_stateKeys].includes(property);
4121
4559
  }
4122
4560
  });
4561
+ return proxy;
4123
4562
  }
4124
4563
  };
4125
4564
  var LarkUtils = {
4126
4565
  isLarkView(instance) {
4127
- return isObject(instance) && !!instance.updater;
4566
+ if (!isObject(instance)) return false;
4567
+ const updater = instance["updater"];
4568
+ return isObject(updater) && isFunction(updater["set"]) && isFunction(updater["digest"]);
4128
4569
  },
4129
4570
  getRender(view) {
4130
4571
  return view.updater.digest.bind(view.updater);
@@ -4140,41 +4581,45 @@ var getLarkAdapter = (storeName) => ({
4140
4581
  Store: LarkStore,
4141
4582
  useStore: ((view) => {
4142
4583
  const store = getStore(storeName);
4584
+ if (!(store instanceof LarkStore)) return {};
4143
4585
  return store[_storeBoot](view);
4144
4586
  })
4145
4587
  });
4146
- var innerObserveFlags = /* @__PURE__ */ Symbol("store-inner-observe-flags");
4147
- var boundViews = /* @__PURE__ */ Symbol("bound-views");
4588
+ var _innerObserveFlags = /* @__PURE__ */ Symbol("store-inner-observe-flags");
4589
+ var _boundViews = /* @__PURE__ */ Symbol("store-bound-views");
4148
4590
  var LarkStore = class extends BaseStore {
4149
- [boundViews] = /* @__PURE__ */ new Set();
4150
- [innerObserveFlags] = /* @__PURE__ */ new Set();
4591
+ [_boundViews] = /* @__PURE__ */ new Set();
4592
+ [_innerObserveFlags] = /* @__PURE__ */ new Set();
4151
4593
  [_storeBoot](view) {
4152
- if (view && LarkUtils.isLarkView(view) && !this[boundViews].has(view)) {
4153
- this[boundViews].add(view);
4594
+ if (view && LarkUtils.isLarkView(view) && !this[_boundViews].has(view)) {
4595
+ this[_boundViews].add(view);
4154
4596
  LarkUtils.onDestroy(view, () => {
4155
- this[boundViews].delete(view);
4597
+ this[_boundViews].delete(view);
4156
4598
  });
4157
4599
  }
4158
4600
  return super[_storeBoot]();
4159
4601
  }
4160
4602
  [_storeDestroy]() {
4161
- this[boundViews].clear();
4162
- this[innerObserveFlags].clear();
4603
+ this[_boundViews].clear();
4604
+ this[_innerObserveFlags].clear();
4163
4605
  super[_storeDestroy]();
4164
4606
  }
4165
4607
  observe(view, keys2, defCallback) {
4166
4608
  if (this[_storeStatus] !== 2 /* ACTIVE */) return noop;
4167
- let observeKeys = keys2;
4609
+ let observeKeys = Array.isArray(keys2) ? keys2 : [];
4168
4610
  const _view = view;
4169
4611
  const renderFn = _view ? LarkUtils.getRender(_view) : noop;
4170
4612
  const dateSetterFn = _view ? LarkUtils.getDataSetter(_view) : noop;
4171
4613
  const isInnerObserve = !view;
4172
4614
  const innerFlags = /* @__PURE__ */ new Set();
4173
- const storeInnerObserveFlags = this[innerObserveFlags];
4615
+ const storeInnerObserveFlags = this[_innerObserveFlags];
4174
4616
  if (isFunction(keys2)) {
4175
4617
  const res = keys2();
4176
4618
  if (Array.isArray(res)) observeKeys = res;
4177
4619
  }
4620
+ if (keys2 === void 0 && _view && observeKeys.length === 0 && Array.isArray(this[_stateKeys])) {
4621
+ observeKeys = this[_stateKeys].slice();
4622
+ }
4178
4623
  const defSetter = (immediate, key, alias, transform) => () => {
4179
4624
  const stateVal = getDataByKey(this, key);
4180
4625
  let data = { [alias || key]: stateVal };
@@ -4229,11 +4674,12 @@ var getReactAdapter = (storeName) => ({
4229
4674
  Store: ReactStore,
4230
4675
  useStore: (() => {
4231
4676
  const store = getStore(storeName);
4677
+ if (!(store instanceof ReactStore)) return {};
4232
4678
  return store[_storeBoot]();
4233
4679
  })
4234
4680
  });
4235
- var observeSym = /* @__PURE__ */ Symbol("observe");
4236
- var getLastState = /* @__PURE__ */ Symbol("get-last-state");
4681
+ var _observe = /* @__PURE__ */ Symbol("store-observe");
4682
+ var _getLastState = /* @__PURE__ */ Symbol("store-get-last-state");
4237
4683
  var ReactStore = class extends BaseStore {
4238
4684
  stateChangeCount = 0;
4239
4685
  lastCount = 0;
@@ -4247,7 +4693,7 @@ var ReactStore = class extends BaseStore {
4247
4693
  }
4248
4694
  [_storeCreate](body) {
4249
4695
  super[_storeCreate](body);
4250
- this[observeSym](() => {
4696
+ this[_observe](() => {
4251
4697
  this.stateChangeCount += 1;
4252
4698
  });
4253
4699
  }
@@ -4257,7 +4703,7 @@ var ReactStore = class extends BaseStore {
4257
4703
  this.lastCount = currCount;
4258
4704
  return changed;
4259
4705
  }
4260
- [getLastState](handlers) {
4706
+ [_getLastState](handlers) {
4261
4707
  if (this.isStateChanged() || !this.lastState) {
4262
4708
  const state = this[_storeState];
4263
4709
  const immutableState = freezeData(state);
@@ -4265,7 +4711,7 @@ var ReactStore = class extends BaseStore {
4265
4711
  }
4266
4712
  return this.lastState;
4267
4713
  }
4268
- [observeSym](cb) {
4714
+ [_observe](cb) {
4269
4715
  const tasks = this[_stateKeys].map((key) => ({ key, cb }));
4270
4716
  track({ belong: this[_storeName], trackList: tasks });
4271
4717
  return () => clear({ belong: this[_storeName], clearList: tasks });
@@ -4292,6 +4738,7 @@ var getNodeAdapter = (storeName) => ({
4292
4738
  Store: NodeStore,
4293
4739
  useStore: (() => {
4294
4740
  const store = getStore(storeName);
4741
+ if (!(store instanceof NodeStore)) return {};
4295
4742
  return store[_storeBoot]();
4296
4743
  })
4297
4744
  });
@@ -4318,7 +4765,7 @@ var getPlatform = (comp) => {
4318
4765
  if (LarkUtils.isLarkView(comp)) return "lark" /* Lark */;
4319
4766
  return void 0;
4320
4767
  };
4321
- var extendApis = { lazySet, shallowSet };
4768
+ var extendApis = { lazySet, shallowSet, computed };
4322
4769
  var StoreCache = /* @__PURE__ */ new Map();
4323
4770
  function defineStore(name, creator, config2) {
4324
4771
  if (StoreCache.has(name)) {
@@ -4329,15 +4776,20 @@ function defineStore(name, creator, config2) {
4329
4776
  const StoreClass = adapter.Store;
4330
4777
  const useStore = adapter.useStore;
4331
4778
  const store = new StoreClass(name, config2);
4332
- store[_storeCreate](
4333
- creator(store[_innerStore](), extendApis)
4334
- );
4779
+ const innerProxy = store[_innerStore]();
4780
+ const body = creator(innerProxy, extendApis);
4781
+ store[_storeCreate](body);
4335
4782
  Object.defineProperties(useStore, {
4336
4783
  $storeName: { value: name, configurable: true },
4337
4784
  $destroyFn: { value: () => store[_storeDestroy](), configurable: true }
4338
4785
  });
4339
4786
  if (!StoreCache.has(name)) {
4340
- StoreCache.set(name, { store, creator, config: config2, useStore });
4787
+ StoreCache.set(name, {
4788
+ store,
4789
+ creator,
4790
+ config: config2,
4791
+ useStore
4792
+ });
4341
4793
  }
4342
4794
  return useStore;
4343
4795
  }
@@ -4359,7 +4811,7 @@ function getUseStore(name) {
4359
4811
  return void 0;
4360
4812
  }
4361
4813
  function cloneStore(name, useStore, config2) {
4362
- const oldStoreName = useStore["$storeName"];
4814
+ const oldStoreName = useStore.$storeName ?? "";
4363
4815
  const cached = StoreCache.get(oldStoreName);
4364
4816
  const oldStoreCreator = cached?.creator;
4365
4817
  const oldConfig = cached?.config || {};
@@ -4380,16 +4832,12 @@ function isStoreActive(name) {
4380
4832
  }
4381
4833
  var cellCount = 0;
4382
4834
  function cell(data) {
4383
- return createState(data, {
4384
- belong: LARK_GLOBAL,
4385
- linkKeys: `${LARK_GLOBAL}_${cellCount++}`
4386
- });
4835
+ const linkKeys = `${LARK_GLOBAL}_${cellCount++}`;
4836
+ return createState(data, { belong: LARK_GLOBAL, linkKeys });
4387
4837
  }
4388
4838
  function observeCell(state, cb, immediate = true) {
4389
4839
  const linkKeys = getLinkKeys(state);
4390
- if (!linkKeys)
4391
- return () => {
4392
- };
4840
+ if (!linkKeys) return noop;
4393
4841
  const keys2 = linkKeys.split(".");
4394
4842
  const key = keys2[keys2.length - 1];
4395
4843
  const list = [{ key, cb }];
@@ -4398,30 +4846,32 @@ function observeCell(state, cb, immediate = true) {
4398
4846
  return () => clear({ belong: LARK_GLOBAL, clearList: list });
4399
4847
  }
4400
4848
  function multi(useStore) {
4401
- const storeName = useStore["$storeName"];
4849
+ const storeName = useStore.$storeName ?? "";
4402
4850
  const flagSym = `lark-comp-${storeName}`;
4403
4851
  const map = /* @__PURE__ */ new Map();
4404
4852
  let rootViewPath;
4405
4853
  const getFlag = (viewContext) => {
4406
- const owner = viewContext["owner"];
4407
- const viewPath = owner?.["path"] || "";
4408
- const viewId = owner?.["id"] || "";
4854
+ const owner = viewContext.owner;
4855
+ const viewPath = owner?.path ?? "";
4856
+ const viewId = owner?.id ?? "";
4409
4857
  let flag;
4410
4858
  if (viewPath === rootViewPath) {
4411
4859
  flag = `${flagSym}-${viewId}`;
4412
4860
  } else {
4413
- flag = owner?.["viewInitParams"]?.[flagSym];
4861
+ const initParams = owner?.viewInitParams;
4862
+ const candidate = initParams?.[flagSym];
4863
+ flag = typeof candidate === "string" ? candidate : void 0;
4414
4864
  }
4415
- if (owner && isFunction(owner["mountFrame"])) {
4416
- const rawMountFrame = owner["mountFrame"];
4417
- owner["mountFrame"] = (vfId, viewPath2, viewInitParams = {}) => rawMountFrame.call(
4865
+ if (owner && isFunction(owner.mountFrame)) {
4866
+ const rawMountFrame = owner.mountFrame;
4867
+ owner.mountFrame = (frameId, viewPath2, viewInitParams = {}) => rawMountFrame.call(
4418
4868
  owner,
4419
- vfId,
4869
+ frameId,
4420
4870
  viewPath2,
4421
4871
  Object.assign(viewInitParams, { [flagSym]: flag })
4422
4872
  );
4423
4873
  }
4424
- return flag;
4874
+ return flag ?? "";
4425
4875
  };
4426
4876
  const useFn = ((view) => {
4427
4877
  if (!view)
@@ -4429,7 +4879,8 @@ function multi(useStore) {
4429
4879
  "[@lark.js/mvc error] multi: cannot find the view instance"
4430
4880
  );
4431
4881
  const viewCtx = view;
4432
- const flag = viewCtx[flagSym];
4882
+ const tag = viewCtx[flagSym];
4883
+ const flag = typeof tag === "string" ? tag : "";
4433
4884
  if (map.has(flag)) return map.get(flag);
4434
4885
  const newFn = cloneStore(flag, useStore);
4435
4886
  map.set(flag, newFn);
@@ -4438,8 +4889,7 @@ function multi(useStore) {
4438
4889
  const mixinObj = {
4439
4890
  make() {
4440
4891
  if (!rootViewPath) {
4441
- const owner = this["owner"];
4442
- rootViewPath = owner?.["path"] || "";
4892
+ rootViewPath = this.owner?.path ?? "";
4443
4893
  }
4444
4894
  this[flagSym] = getFlag(this);
4445
4895
  }
@@ -4646,7 +5096,7 @@ function convertArtExpression(code, debug, lineNo, blockStack = []) {
4646
5096
  return `${debugPrefix}<%for(${forExpr}){%>`;
4647
5097
  }
4648
5098
  const tokens = code.split(/\s+/);
4649
- const keyword = tokens.shift();
5099
+ const keyword = tokens.shift() ?? "";
4650
5100
  switch (keyword) {
4651
5101
  case "if": {
4652
5102
  blockStack.push({ ctrl: "if", line: lineNo });
@@ -4870,13 +5320,9 @@ function compileToFunction(source, debug, file) {
4870
5320
  }
4871
5321
  const viewIdRegExp = new RegExp(String.fromCharCode(31), "g");
4872
5322
  funcSource = funcSource.replace(viewIdRegExp, `'+$viewId+'`);
4873
- 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;}}` : "";
4874
- 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)}`;
4875
- 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)}`;
4876
- const encodeQuote = `if(!$encQuote){let $qReg=/['"\\\\]/g;$encQuote=v=>$strSafe(v).replace($qReg,'\\\\$&')}`;
5323
+ void hasAtRule;
4877
5324
  const refFallback = "if(!$refAlt)$refAlt=$data;";
4878
- const fns = `${refFallback}${encode}${encodeURIMore}${encodeQuote}${atRule};`;
4879
- const fullSource = `${fns}let $splitter='\\x1e',$tmp,$out=''{{VARS}};${funcSource}return $out`;
5325
+ const fullSource = `${refFallback}let $splitter='\\x1e',$tmp,$out=''{{VARS}};${funcSource}return $out`;
4880
5326
  return `($data,$viewId,$refAlt,$encHtml,$strSafe,$encUri,$refFn,$encQuote)=>{${fullSource}}`;
4881
5327
  }
4882
5328
  function compileTemplate(source, options = {}) {
@@ -4888,15 +5334,12 @@ function compileTemplate(source, options = {}) {
4888
5334
  const funcBody = compileToFunction(finalSource, debug, file);
4889
5335
  const varDeclarations = globalVars.map((key) => `,${key}=$data.${key}`).join("");
4890
5336
  const funcWithVars = funcBody.replace("{{VARS}}", () => varDeclarations);
4891
- return `export default function(data, selfId, refData) {
5337
+ return `import { encHtml as __larkEncHtml, strSafe as __larkStrSafe, encUri as __larkEncUri, encQuote as __larkEncQuote, refFn as __larkRefFn } from "@lark.js/mvc/runtime";
5338
+ export default function(data, viewId, refData) {
4892
5339
  let $data = data || {},
4893
- $viewId = selfId || '';
5340
+ $viewId = viewId || '';
4894
5341
  return (${funcWithVars})($data, $viewId, refData,
4895
- /* $encHtml */ v => String(v == null ? '' : v).replace(/[&<>"'\`]/g, m => '&' + ({'&':'amp','<':'lt','>':'gt','"':'#34',"'":'#39','\`':'#96'})[m] + ';'),
4896
- /* $strSafe */ v => String(v == null ? '' : v),
4897
- /* $encUri */ null,
4898
- /* $refFn */ null,
4899
- /* $encQuote */ null
5342
+ __larkEncHtml, __larkStrSafe, __larkEncUri, __larkRefFn, __larkEncQuote
4900
5343
  );
4901
5344
  }`;
4902
5345
  }
@@ -5021,31 +5464,37 @@ function walkAst(ast, visitors) {
5021
5464
  if (visitors[type]) {
5022
5465
  visitors[type](node);
5023
5466
  }
5467
+ const bag = node;
5024
5468
  for (const key of Object.keys(node)) {
5025
5469
  if (key === "type" || key === "start" || key === "end" || key === "loc" || key === "range")
5026
5470
  continue;
5027
- if (type === "MemberExpression" && key === "property" && !node.computed)
5028
- continue;
5029
- if (type === "ObjectProperty" && key === "key" && !node.computed) {
5030
- continue;
5471
+ if (type === "MemberExpression" && key === "property") {
5472
+ const me = node;
5473
+ if (!me.computed) continue;
5031
5474
  }
5032
- if (type === "ObjectMethod" && key === "key" && !node.computed) {
5033
- continue;
5475
+ if (type === "ObjectProperty" && key === "key") {
5476
+ const op = node;
5477
+ if (!op.computed) continue;
5034
5478
  }
5035
- const child = node[key];
5479
+ if (type === "ObjectMethod" && key === "key") {
5480
+ const om = node;
5481
+ if (!om.computed) continue;
5482
+ }
5483
+ const child = bag[key];
5036
5484
  if (Array.isArray(child)) {
5037
5485
  for (const item of child) {
5038
- if (item && typeof item === "object" && typeof item.type === "string") {
5039
- visit(item);
5040
- }
5486
+ if (isAstNode(item)) visit(item);
5041
5487
  }
5042
- } else if (child && typeof child === "object" && typeof child.type === "string") {
5488
+ } else if (isAstNode(child)) {
5043
5489
  visit(child);
5044
5490
  }
5045
5491
  }
5046
5492
  }
5047
5493
  visit(ast);
5048
5494
  }
5495
+ function isAstNode(v) {
5496
+ return !!v && typeof v === "object" && typeof v.type === "string";
5497
+ }
5049
5498
  var BUILTIN_GLOBALS = {
5050
5499
  // ─── Template runtime helpers (injected by compileToFunction) ───────
5051
5500
  //
@@ -5199,15 +5648,17 @@ var BUILTIN_GLOBAL_SET = new Set(Object.keys(BUILTIN_GLOBALS));
5199
5648
  export {
5200
5649
  CALL_BREAK_TIME,
5201
5650
  Cache,
5651
+ cross_site_default as CrossSite,
5202
5652
  EVENT_METHOD_REGEXP,
5203
5653
  EventDelegator,
5204
5654
  EventEmitter,
5205
5655
  Frame,
5656
+ FrameVisualBridge,
5206
5657
  Framework,
5207
5658
  LARK_VIEW,
5208
5659
  Payload,
5209
5660
  Platform,
5210
- ROUTER_EVENTS,
5661
+ RouterEvents as ROUTER_EVENTS,
5211
5662
  Router,
5212
5663
  SPLITTER,
5213
5664
  Service,
@@ -5221,13 +5672,14 @@ export {
5221
5672
  applyVdomOps,
5222
5673
  assign,
5223
5674
  cell,
5224
- classExtend,
5225
5675
  cloneData,
5226
5676
  cloneStore,
5227
5677
  compileTemplate,
5678
+ computed,
5228
5679
  createState,
5229
5680
  createVdomRef,
5230
5681
  defineStore,
5682
+ defineView,
5231
5683
  delStore,
5232
5684
  encodeHTML,
5233
5685
  encodeQ,
@@ -5235,6 +5687,7 @@ export {
5235
5687
  encodeURIExtra,
5236
5688
  ensureElementId,
5237
5689
  extractGlobalVars,
5690
+ config as frameworkConfig,
5238
5691
  funcWithTry,
5239
5692
  generateId,
5240
5693
  getAttribute,
@@ -5242,8 +5695,9 @@ export {
5242
5695
  getPlatform,
5243
5696
  getStore,
5244
5697
  getUseStore,
5245
- has,
5698
+ hasOwnProperty,
5246
5699
  installFrameVisualizerBridge,
5700
+ invalidateViewClass,
5247
5701
  isPlainObject,
5248
5702
  isPrimitive,
5249
5703
  isPrimitiveOrFunc,
@@ -5262,6 +5716,7 @@ export {
5262
5716
  observeCell,
5263
5717
  parseUri,
5264
5718
  registerViewClass,
5719
+ resetProjectsMap,
5265
5720
  safeguard,
5266
5721
  serializeFrameTree,
5267
5722
  setData,
@@ -5273,6 +5728,7 @@ export {
5273
5728
  toUri,
5274
5729
  translateData,
5275
5730
  unmark,
5731
+ use,
5276
5732
  vdomGetCompareKey,
5277
5733
  vdomGetNode,
5278
5734
  vdomSetAttributes,