@lark.js/mvc 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.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) */
@@ -38,7 +38,6 @@ function isPlainObject(value) {
38
38
  if (proto === null) return true;
39
39
  return proto === Object.prototype || proto === null;
40
40
  }
41
- var isArray = Array.isArray;
42
41
  function isPrimitiveOrFunc(value) {
43
42
  return !value || typeof value !== "object" && typeof value !== "function";
44
43
  }
@@ -54,13 +53,13 @@ function syncCounter(val) {
54
53
  }
55
54
  function noop() {
56
55
  }
57
- function has(owner, prop) {
56
+ function hasOwnProperty(owner, prop) {
58
57
  return owner != null && Object.prototype.hasOwnProperty.call(owner, prop);
59
58
  }
60
59
  function keys(obj) {
61
60
  const result = [];
62
61
  for (const p in obj) {
63
- if (has(obj, p)) {
62
+ if (hasOwnProperty(obj, p)) {
64
63
  result.push(p);
65
64
  }
66
65
  }
@@ -70,7 +69,7 @@ function assign(target, ...sources) {
70
69
  for (const source of sources) {
71
70
  if (source) {
72
71
  for (const p in source) {
73
- if (has(source, p)) {
72
+ if (hasOwnProperty(source, p)) {
74
73
  target[p] = source[p];
75
74
  }
76
75
  }
@@ -79,25 +78,26 @@ function assign(target, ...sources) {
79
78
  return target;
80
79
  }
81
80
  function funcWithTry(fns, args, context, configError) {
82
- const fnArray = isArray(fns) ? fns : [fns];
81
+ const fnArray = Array.isArray(fns) ? fns : [fns];
83
82
  let ret;
84
83
  for (const fn of fnArray) {
85
84
  try {
86
- ret = Function.prototype.apply.call(fn, context, args);
85
+ ret = fn.apply(context, args);
87
86
  } catch (e) {
88
- configError(e);
87
+ configError?.(e);
89
88
  }
90
89
  }
91
90
  return ret;
92
91
  }
92
+ var EMPTY_STRING_SET = /* @__PURE__ */ new Set();
93
93
  function setData(newData, oldData, changedKeys2, excludes) {
94
94
  let changed = false;
95
95
  for (const p in newData) {
96
- if (has(newData, p)) {
96
+ if (hasOwnProperty(newData, p)) {
97
97
  const now2 = newData[p];
98
98
  const old = oldData[p];
99
99
  if ((!isPrimitiveOrFunc(now2) || old !== now2) && !excludes.has(p)) {
100
- changedKeys2[p] = 1;
100
+ changedKeys2.add(p);
101
101
  changed = true;
102
102
  }
103
103
  oldData[p] = now2;
@@ -105,17 +105,25 @@ function setData(newData, oldData, changedKeys2, excludes) {
105
105
  }
106
106
  return changed;
107
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
+ }
108
116
  function translateData(data, value) {
109
117
  if (isPrimitive(value)) {
110
118
  const prop = String(value);
111
- if (prop[0] === SPLITTER && has(data, prop)) {
119
+ if (isRefToken(prop) && hasOwnProperty(data, prop)) {
112
120
  return data[prop];
113
121
  }
114
122
  return value;
115
123
  }
116
- if (isPlainObject(value) || isArray(value)) {
124
+ if (isPlainObject(value) || Array.isArray(value)) {
117
125
  for (const p in value) {
118
- if (has(value, p)) {
126
+ if (hasOwnProperty(value, p)) {
119
127
  const val = value[p];
120
128
  const newVal = translateData(data, val);
121
129
  value[p] = newVal;
@@ -152,38 +160,33 @@ function nodeInside(a, b) {
152
160
  return false;
153
161
  }
154
162
  }
155
- var paramsTemp = {};
156
- function paramsReplacer(_match, name, value) {
157
- try {
158
- paramsTemp[name] = decodeURIComponent(value || "");
159
- } catch {
160
- paramsTemp[name] = value || "";
161
- }
162
- return "";
163
- }
164
163
  function parseUri(uri) {
165
- paramsTemp = {};
164
+ const params = {};
166
165
  const path = uri.replace(URL_QUERY_HASH_REGEXP, "");
167
166
  const pathname = path;
168
167
  const actualPath = uri === pathname && IS_URL_PARAMS.test(pathname) ? "" : pathname;
169
- uri.replace(actualPath, "").replace(URL_PARAM_REGEXP, paramsReplacer);
170
- return {
171
- path: actualPath,
172
- params: { ...paramsTemp }
173
- };
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 };
174
177
  }
175
178
  var IS_URL_PARAMS = {
176
179
  test(s) {
177
180
  return /(?!^)=|&/.test(s);
178
181
  }
179
182
  };
180
- function toUri(path, params, keepEmptyObject) {
183
+ function toUri(path, params, keepEmpty) {
181
184
  const pairs = [];
182
185
  let hasParams = false;
183
186
  for (const p in params) {
184
- if (has(params, p)) {
187
+ if (hasOwnProperty(params, p)) {
185
188
  const v = String(params[p] ?? "");
186
- if (!keepEmptyObject || v || has(keepEmptyObject, p)) {
189
+ if (!keepEmpty || v || keepEmpty.has(p)) {
187
190
  pairs.push(`${p}=${encodeURIComponent(v)}`);
188
191
  hasParams = true;
189
192
  }
@@ -206,14 +209,6 @@ function toMap(list, key) {
206
209
  function now() {
207
210
  return Date.now ? Date.now() : (/* @__PURE__ */ new Date()).getTime();
208
211
  }
209
- function classExtend(make, base, props, statics) {
210
- const baseProto = base["prototype"] ?? {};
211
- const cProto = Object.create(baseProto);
212
- assign(cProto, props);
213
- Object.assign(make, statics);
214
- cProto.constructor = make;
215
- make["prototype"] = cProto;
216
- }
217
212
 
218
213
  // src/apply-style.ts
219
214
  var injectedStyleIds = /* @__PURE__ */ new Set();
@@ -253,32 +248,40 @@ function applyStyle(styleIdOrPairs, css) {
253
248
  }
254
249
 
255
250
  // src/mark.ts
256
- var DELETED_KEY = SPLITTER + "$a";
257
- var MARK_OBJECT_KEY = SPLITTER + "$b";
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
+ }
258
260
  function mark(host, key) {
259
- let sign = 0;
260
- const hostRecord = host;
261
- if (!hostRecord[DELETED_KEY]) {
262
- const markHost = hostRecord[MARK_OBJECT_KEY] || (hostRecord[MARK_OBJECT_KEY] = {});
263
- if (!Object.prototype.hasOwnProperty.call(markHost, key)) {
264
- markHost[key] = 0;
265
- }
266
- sign = ++markHost[key];
261
+ const record = getOrCreate(host);
262
+ if (record.deleted) {
263
+ return () => false;
267
264
  }
265
+ const sign = (record.signs.get(key) ?? 0) + 1;
266
+ record.signs.set(key, sign);
268
267
  return () => {
269
- const temp = hostRecord[MARK_OBJECT_KEY];
270
- return !!(temp && sign === temp[key]);
268
+ const current = hostStore.get(host);
269
+ return !!current && !current.deleted && current.signs.get(key) === sign;
271
270
  };
272
271
  }
273
272
  function unmark(host) {
274
- const hostRecord = host;
275
- hostRecord[MARK_OBJECT_KEY] = 0;
276
- 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
+ }
277
280
  }
278
281
 
279
282
  // src/safeguard.ts
280
283
  var proxiesPool = /* @__PURE__ */ new Map();
281
- var SAFEGUARD_SENTINEL = "_sf_";
284
+ var SAFEGUARD_SENTINEL = "_safe_";
282
285
  function safeguard(data, getter, setter, isRoot) {
283
286
  if (typeof window.__lark_Debug === "undefined" || !window.__lark_Debug) {
284
287
  return data;
@@ -319,7 +322,7 @@ function safeguard(data, getter, setter, isRoot) {
319
322
  if (!prefix && getter) {
320
323
  getter(property);
321
324
  }
322
- if (!isRoot && has(target, property) && (isArray(out) || isPlainObject(out))) {
325
+ if (!isRoot && hasOwnProperty(target, property) && (Array.isArray(out) || isPlainObject(out))) {
323
326
  return build(prefix + property + ".", out);
324
327
  }
325
328
  return out;
@@ -408,15 +411,17 @@ var Cache = class {
408
411
  this.lookup.set(prefixedKey, entry);
409
412
  }
410
413
  /**
411
- * 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.
412
417
  */
413
418
  del(key) {
414
419
  const prefixedKey = this.prefixKey(key);
415
420
  const entry = this.lookup.get(prefixedKey);
416
421
  if (!entry) return;
417
- entry.frequency = -1;
418
- entry.value = void 0;
419
422
  this.lookup.delete(prefixedKey);
423
+ const idx = this.entries.indexOf(entry);
424
+ if (idx !== -1) this.entries.splice(idx, 1);
420
425
  if (this.onRemove) {
421
426
  this.onRemove(key);
422
427
  }
@@ -441,20 +446,46 @@ var Cache = class {
441
446
  this.entries = [];
442
447
  this.lookup.clear();
443
448
  }
444
- /** 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
+ */
445
457
  evictEntries() {
446
- this.entries.sort(this.comparator);
447
- let count = this.bufferSize;
448
- while (count-- > 0 && this.entries.length > 0) {
449
- const entry = this.entries.pop();
450
- if (entry && entry.frequency > 0) {
451
- this.lookup.delete(this.prefixKey(entry.originalKey));
452
- if (this.onRemove) {
453
- this.onRemove(entry.originalKey);
454
- }
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);
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);
455
481
  }
456
482
  }
457
- this.entries = this.entries.filter((e) => e.frequency !== -1);
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);
487
+ }
488
+ this.entries = entries.filter((e) => !evictSet.has(e));
458
489
  }
459
490
  };
460
491
 
@@ -462,6 +493,10 @@ var Cache = class {
462
493
  var EventEmitter = class {
463
494
  /** Event listeners: prefixed key -> listener array */
464
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;
465
500
  /**
466
501
  * Bind event listener.
467
502
  */
@@ -484,24 +519,37 @@ var EventEmitter = class {
484
519
  const key = SPLITTER + event;
485
520
  if (handler) {
486
521
  const list = this.listeners.get(key);
487
- if (list) {
522
+ if (!list) return this;
523
+ if (this.firingDepth > 0) {
488
524
  for (const listener of list) {
489
525
  if (listener.handler === handler) {
490
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);
491
535
  break;
492
536
  }
493
537
  }
538
+ if (list.length === 0) this.listeners.delete(key);
494
539
  }
495
540
  } else {
496
541
  this.listeners.delete(key);
497
- Reflect.deleteProperty(this, `on${event}`);
542
+ Reflect.deleteProperty(
543
+ this,
544
+ `on${event[0].toUpperCase() + event.slice(1)}`
545
+ );
498
546
  }
499
547
  return this;
500
548
  }
501
549
  /**
502
- * Fire event, execute all bound handlers.
503
- * Supports executing state management: handlers removed during
504
- * 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.
505
553
  *
506
554
  * @param event - Event name
507
555
  * @param data - Event data (type property added automatically)
@@ -515,38 +563,41 @@ var EventEmitter = class {
515
563
  data = {};
516
564
  }
517
565
  data["type"] = event;
518
- if (list) {
519
- let end = list.length;
520
- const len = end - 1;
521
- while (end--) {
522
- const idx = lastToFirst ? end : len - end;
523
- const listener = list[idx];
524
- 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;
525
575
  listener.executing = 1;
526
- funcWithTry(
527
- [listener.handler],
528
- [data],
529
- this,
530
- noop
531
- );
576
+ funcWithTry([listener.handler], [data], this, noop);
532
577
  listener.executing = "";
533
- } else if (!listener.executing) {
534
- list.splice(idx, 1);
535
578
  }
536
579
  }
537
- }
538
- const onMethodName = `on${event}`;
539
- const onMethod = this[onMethodName];
540
- if (typeof onMethod === "function") {
541
- funcWithTry(
542
- [onMethod],
543
- [data],
544
- this,
545
- noop
546
- );
547
- }
548
- if (remove) {
549
- 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
+ }
550
601
  }
551
602
  return this;
552
603
  }
@@ -555,8 +606,8 @@ var EventEmitter = class {
555
606
  // src/state.ts
556
607
  var appData = {};
557
608
  var keyRefCounts = {};
558
- var changedKeys = {};
559
- var stashedChangedKeys = {};
609
+ var changedKeys = /* @__PURE__ */ new Set();
610
+ var stashedChangedKeys = EMPTY_STRING_SET;
560
611
  var dataIsChanged = false;
561
612
  var dataWhereSet = {};
562
613
  var emitter = new EventEmitter();
@@ -567,7 +618,7 @@ function markBooted() {
567
618
  function setupKeysRef(keys2) {
568
619
  const keyList = keys2.split(",");
569
620
  for (const key of keyList) {
570
- if (has(keyRefCounts, key)) {
621
+ if (hasOwnProperty(keyRefCounts, key)) {
571
622
  keyRefCounts[key]++;
572
623
  } else {
573
624
  keyRefCounts[key] = 1;
@@ -577,7 +628,7 @@ function setupKeysRef(keys2) {
577
628
  }
578
629
  function teardownKeysRef(keyList) {
579
630
  for (const key of keyList) {
580
- if (has(keyRefCounts, key)) {
631
+ if (hasOwnProperty(keyRefCounts, key)) {
581
632
  const count = --keyRefCounts[key];
582
633
  if (count <= 0) {
583
634
  Reflect.deleteProperty(keyRefCounts, key);
@@ -589,35 +640,14 @@ function teardownKeysRef(keyList) {
589
640
  }
590
641
  }
591
642
  }
592
- var notifyStarted = 0;
593
- var notifyList = [];
594
- var notifyTimer;
643
+ var warnedKeys = /* @__PURE__ */ new Set();
595
644
  function clearNotify(key) {
596
- for (let i = notifyList.length; i--; ) {
597
- if (notifyList[i]?.key === key) {
598
- notifyList.splice(i, 1);
599
- }
600
- }
601
- }
602
- function doNotify() {
603
- const locker = {};
604
- for (const n of notifyList) {
605
- if (!locker[n.key]) {
606
- console.warn(n.message);
607
- locker[n.key] = 1;
608
- }
609
- }
610
- notifyList.length = 0;
611
- notifyStarted = 0;
645
+ warnedKeys.delete(key);
612
646
  }
613
647
  function delayNotify(key, message) {
614
- clearTimeout(notifyTimer);
615
- notifyStarted = 0;
616
- notifyList.push({ key, message });
617
- if (!notifyStarted) {
618
- notifyStarted = 1;
619
- notifyTimer = setTimeout(doNotify, 500);
620
- }
648
+ if (warnedKeys.has(key)) return;
649
+ warnedKeys.add(key);
650
+ console.warn(message);
621
651
  }
622
652
  var State = {
623
653
  /**
@@ -629,7 +659,7 @@ var State = {
629
659
  return safeguard(
630
660
  result,
631
661
  (dataKey) => {
632
- if (booted && has(dataWhereSet, dataKey) && dataWhereSet[dataKey] !== window.location.pathname) {
662
+ if (booted && hasOwnProperty(dataWhereSet, dataKey) && dataWhereSet[dataKey] !== window.location.pathname) {
633
663
  console.warn(
634
664
  `beware! You get state:"{State}.${dataKey}" where it set by page:${dataWhereSet[dataKey]}`
635
665
  );
@@ -650,7 +680,7 @@ var State = {
650
680
  * Set data to state.
651
681
  */
652
682
  set(data, excludes) {
653
- dataIsChanged = setData(data, appData, changedKeys, excludes || /* @__PURE__ */ new Set()) || dataIsChanged;
683
+ dataIsChanged = setData(data, appData, changedKeys, excludes || EMPTY_STRING_SET) || dataIsChanged;
654
684
  if (typeof window.__lark_Debug !== "undefined" && window.__lark_Debug && booted) {
655
685
  for (const p in data) {
656
686
  dataWhereSet[p] = window.location.pathname;
@@ -667,26 +697,19 @@ var State = {
667
697
  }
668
698
  if (dataIsChanged) {
669
699
  if (typeof window.__lark_Debug !== "undefined" && window.__lark_Debug) {
670
- for (const p in changedKeys) {
671
- if (has(changedKeys, p)) {
672
- clearNotify(p);
673
- }
700
+ for (const p of changedKeys) {
701
+ clearNotify(p);
674
702
  }
675
703
  }
676
704
  dataIsChanged = false;
677
- const keys2 = {};
678
- for (const k in changedKeys) {
679
- if (has(changedKeys, k)) {
680
- keys2[k] = 1;
681
- }
682
- }
705
+ const keys2 = changedKeys;
683
706
  stashedChangedKeys = keys2;
684
- changedKeys = {};
685
- emitter.fire(ROUTER_EVENTS.CHANGED, { keys: keys2 });
707
+ changedKeys = /* @__PURE__ */ new Set();
708
+ emitter.fire(RouterEvents.CHANGED, { keys: keys2 });
686
709
  }
687
710
  },
688
711
  /**
689
- * Get diff of what changed in last digest.
712
+ * Get the set of keys changed in the most recent digest.
690
713
  */
691
714
  diff() {
692
715
  return stashedChangedKeys;
@@ -726,6 +749,7 @@ var State = {
726
749
  emitter.fire(event, data, remove);
727
750
  return State;
728
751
  }
752
+ // onChanged: noop,
729
753
  };
730
754
 
731
755
  // src/router.ts
@@ -743,6 +767,7 @@ var cachedDefaultPath;
743
767
  var cachedRewrite;
744
768
  var defaultTitle;
745
769
  var frameworkConfig;
770
+ var beforeEachGuards = [];
746
771
  function createEmptyLocation() {
747
772
  return {
748
773
  href: "",
@@ -897,7 +922,7 @@ var Router = {
897
922
  document.title = defaultTitle || document.title;
898
923
  }
899
924
  emitter2.fire(
900
- ROUTER_EVENTS.CHANGED,
925
+ RouterEvents.CHANGED,
901
926
  lastChanged
902
927
  );
903
928
  }
@@ -930,16 +955,16 @@ var Router = {
930
955
  }
931
956
  const lPath = lastLocation["path"] || "";
932
957
  const lParams = lastLocation["params"];
933
- const lQuery = {};
958
+ const lQuery = /* @__PURE__ */ new Set();
934
959
  for (const k in lastLocation.query["params"]) {
935
- if (has(lastLocation.query["params"], k)) {
936
- lQuery[k] = 1;
960
+ if (hasOwnProperty(lastLocation.query["params"], k)) {
961
+ lQuery.add(k);
937
962
  }
938
963
  }
939
964
  if (tPath) {
940
- if (!has(window, "history")) {
941
- for (const qKey in lQuery) {
942
- if (has(lQuery, qKey) && !has(tParams, qKey)) {
965
+ if (!hasOwnProperty(window, "history")) {
966
+ for (const qKey of lQuery) {
967
+ if (!hasOwnProperty(tParams, qKey)) {
943
968
  tParams[qKey] = "";
944
969
  }
945
970
  }
@@ -948,14 +973,17 @@ var Router = {
948
973
  tPath = lPath;
949
974
  tParams = assign({}, lParams, tParams);
950
975
  }
951
- updateUrl(
952
- tPath,
953
- tParams,
954
- lastLocation,
955
- replace,
956
- silentFlag,
957
- lQuery
958
- );
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
+ };
959
987
  },
960
988
  /**
961
989
  * Join multiple path segments into a single path.
@@ -1019,21 +1047,48 @@ var Router = {
1019
1047
  }
1020
1048
  };
1021
1049
  Router.fire(
1022
- ROUTER_EVENTS.CHANGE,
1050
+ RouterEvents.CHANGE,
1023
1051
  changeEvent
1024
1052
  );
1025
- if (!suspend && !changeEvent.p) {
1053
+ if (suspend || changeEvent.p) {
1054
+ return;
1055
+ }
1056
+ if (beforeEachGuards.length === 0) {
1026
1057
  changeEvent.resolve();
1058
+ return;
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
+ });
1027
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
+ );
1028
1083
  }
1029
1084
  };
1030
1085
  Router.notify = watchChange;
1031
1086
  window.addEventListener("hashchange", watchChange);
1032
1087
  window.addEventListener("popstate", watchChange);
1033
1088
  window.addEventListener("beforeunload", (domEvent) => {
1034
- const te = {};
1035
- Router.fire(ROUTER_EVENTS.PAGE_UNLOAD, te);
1036
- const msg = te["msg"];
1089
+ const data = {};
1090
+ Router.fire(RouterEvents.PAGE_UNLOAD, data);
1091
+ const msg = data["msg"];
1037
1092
  if (msg) {
1038
1093
  domEvent.returnValue = msg;
1039
1094
  }
@@ -1065,26 +1120,30 @@ var frameGetter;
1065
1120
  function parseEventInfo(eventInfo) {
1066
1121
  const cached = eventInfoCache.get(eventInfo);
1067
1122
  if (cached) {
1068
- return assign({}, cached, { r: eventInfo });
1123
+ return assign({}, cached, { value: eventInfo });
1069
1124
  }
1070
1125
  const match = eventInfo.match(EVENT_METHOD_REGEXP) || [];
1071
1126
  const result = {
1072
- v: match[1] || "",
1073
- n: match[2] || "",
1074
- i: match[3] || ""
1127
+ id: match[1] || "",
1128
+ name: match[2] || "",
1129
+ params: match[3] || ""
1075
1130
  };
1076
1131
  eventInfoCache.set(eventInfo, result);
1077
- return assign({}, result, { r: eventInfo });
1132
+ return assign({}, result, { value: eventInfo });
1078
1133
  }
1079
1134
  function findFrameInfo(current, eventType) {
1080
1135
  const eventInfos = [];
1081
- let begin = current;
1082
1136
  const info = current.getAttribute(`@${eventType}`);
1137
+ const hasSelectorEvents = !!selectorEvents[eventType];
1138
+ if (!info && !hasSelectorEvents) {
1139
+ return eventInfos;
1140
+ }
1141
+ let begin = current;
1083
1142
  let match;
1084
1143
  if (info) {
1085
1144
  match = parseEventInfo(info);
1086
1145
  }
1087
- if (match && !match.v || selectorEvents[eventType]) {
1146
+ if (match && !match.id || hasSelectorEvents) {
1088
1147
  let selectorFrameId = "#";
1089
1148
  let backtrace = 0;
1090
1149
  while (begin && begin !== document.body) {
@@ -1110,10 +1169,10 @@ function findFrameInfo(current, eventType) {
1110
1169
  if (selectorEntry) {
1111
1170
  for (const selectorName of selectorEntry.selectors) {
1112
1171
  const entry = {
1113
- r: selectorName,
1114
- v: frameId,
1115
- n: selectorName,
1116
- i: ""
1172
+ value: selectorName,
1173
+ id: frameId,
1174
+ name: selectorName,
1175
+ params: ""
1117
1176
  };
1118
1177
  if (selectorName) {
1119
1178
  if (!backtrace && elementMatchesSelector(current, selectorName)) {
@@ -1125,8 +1184,8 @@ function findFrameInfo(current, eventType) {
1125
1184
  }
1126
1185
  }
1127
1186
  if (view.template && !backtrace) {
1128
- if (match && !match.v) {
1129
- match.v = frameId;
1187
+ if (match && !match.id) {
1188
+ match.id = frameId;
1130
1189
  }
1131
1190
  break;
1132
1191
  }
@@ -1142,10 +1201,10 @@ function findFrameInfo(current, eventType) {
1142
1201
  }
1143
1202
  if (match) {
1144
1203
  eventInfos.push({
1145
- v: match.v,
1146
- r: match.r,
1147
- n: match.n,
1148
- i: match.i
1204
+ id: match.id,
1205
+ value: match.value,
1206
+ name: match.name,
1207
+ params: match.params
1149
1208
  });
1150
1209
  }
1151
1210
  return eventInfos;
@@ -1166,7 +1225,7 @@ function domEventProcessor(domEvent) {
1166
1225
  const eventInfos = findFrameInfo(current, eventType);
1167
1226
  if (eventInfos.length) {
1168
1227
  for (const info of eventInfos) {
1169
- const { v: frameId, n: handlerName, i: params } = info;
1228
+ const { id: frameId, name: handlerName, params } = info;
1170
1229
  if (lastFrameId !== frameId) {
1171
1230
  if (lastFrameId && domEvent.isPropagationStopped?.()) {
1172
1231
  break;
@@ -1257,7 +1316,7 @@ var EventDelegator = {
1257
1316
  };
1258
1317
 
1259
1318
  // src/vdom.ts
1260
- var WrapMeta = {
1319
+ var wrapMeta = {
1261
1320
  option: [1, "<select multiple>"],
1262
1321
  thead: [1, "<table>"],
1263
1322
  col: [2, "<table><colgroup>"],
@@ -1265,13 +1324,13 @@ var WrapMeta = {
1265
1324
  td: [3, "<table><tbody><tr>"],
1266
1325
  area: [1, "<map>"],
1267
1326
  param: [1, "<object>"],
1268
- g: [1, '<svg xmlns="' + SVG_NS + '">'],
1269
- m: [1, '<math xmlns="' + MATH_NS + '">'],
1327
+ svg: [1, '<svg xmlns="' + SVG_NS + '">'],
1328
+ math: [1, '<math xmlns="' + MATH_NS + '">'],
1270
1329
  _: [0, ""]
1271
1330
  };
1272
- WrapMeta["optgroup"] = WrapMeta["option"];
1273
- WrapMeta["tbody"] = WrapMeta["tfoot"] = WrapMeta["colgroup"] = WrapMeta["caption"] = WrapMeta["thead"];
1274
- 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"];
1275
1334
  var VDoc = document.implementation.createHTMLDocument("");
1276
1335
  var VBase = VDoc.createElement("base");
1277
1336
  VBase.href = document.location.href;
@@ -1282,16 +1341,12 @@ var VDomSpecials = {
1282
1341
  OPTION: ["selected"]
1283
1342
  };
1284
1343
  function vdomUnmountFrames(frame, node) {
1285
- if (node.nodeType === 1) {
1286
- const el = node;
1287
- const id = el.getAttribute("id");
1288
- if (id) {
1289
- frame.unmountZone(id);
1290
- const children = frame.children();
1291
- if (children.includes(id)) {
1292
- frame.unmountFrame(id);
1293
- }
1294
- }
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);
1295
1350
  }
1296
1351
  }
1297
1352
  function vdomGetNode(html, refNode) {
@@ -1299,14 +1354,14 @@ function vdomGetNode(html, refNode) {
1299
1354
  const ns = refNode.namespaceURI;
1300
1355
  let tag;
1301
1356
  if (ns === SVG_NS) {
1302
- tag = "g";
1357
+ tag = "svg";
1303
1358
  } else if (ns === MATH_NS) {
1304
- tag = "m";
1359
+ tag = "math";
1305
1360
  } else {
1306
1361
  const match = TAG_NAME_REGEXP.exec(html);
1307
1362
  tag = match ? match[1] : "";
1308
1363
  }
1309
- const wrap = WrapMeta[tag] || WrapMeta["_"];
1364
+ const wrap = wrapMeta[tag] || wrapMeta["_"];
1310
1365
  tmp.innerHTML = wrap[1] + html;
1311
1366
  let j = wrap[0];
1312
1367
  while (j--) {
@@ -1323,7 +1378,7 @@ function vdomGetCompareKey(node) {
1323
1378
  }
1324
1379
  let key = el.autoId ? "" : el.getAttribute("id") || void 0;
1325
1380
  if (!key) {
1326
- key = el.getAttribute(LARK_KEYS.DIFF_KEY) || void 0;
1381
+ key = el.getAttribute(LarkInnerKeys.DIFF_KEY) || void 0;
1327
1382
  }
1328
1383
  if (!key) {
1329
1384
  const larkView = el.getAttribute(LARK_VIEW);
@@ -1336,8 +1391,7 @@ function vdomGetCompareKey(node) {
1336
1391
  return key;
1337
1392
  }
1338
1393
  function vdomSpecialDiff(oldNode, newNode) {
1339
- const nodeName = oldNode.nodeName;
1340
- const specials = VDomSpecials[nodeName];
1394
+ const specials = VDomSpecials[oldNode.nodeName];
1341
1395
  if (!specials) return 0;
1342
1396
  const oldEl = oldNode;
1343
1397
  const newEl = newNode;
@@ -1386,13 +1440,17 @@ function vdomSetChildNodes(oldParent, newParent, ref, frame, keys_) {
1386
1440
  let oldNode = oldParent.lastChild;
1387
1441
  let newNode = newParent.firstChild;
1388
1442
  let extra = 0;
1389
- const keyedNodes = {};
1390
- const newKeyedNodes = {};
1443
+ const keyedNodes = /* @__PURE__ */ new Map();
1444
+ const newKeyedNodes = /* @__PURE__ */ new Map();
1391
1445
  while (oldNode) {
1392
1446
  extra++;
1393
1447
  const nodeKey = vdomGetCompareKey(oldNode);
1394
1448
  if (nodeKey) {
1395
- const bucket = keyedNodes[nodeKey] || (keyedNodes[nodeKey] = []);
1449
+ let bucket = keyedNodes.get(nodeKey);
1450
+ if (!bucket) {
1451
+ bucket = [];
1452
+ keyedNodes.set(nodeKey, bucket);
1453
+ }
1396
1454
  bucket.push(oldNode);
1397
1455
  }
1398
1456
  oldNode = oldNode.previousSibling;
@@ -1400,7 +1458,7 @@ function vdomSetChildNodes(oldParent, newParent, ref, frame, keys_) {
1400
1458
  while (newNode) {
1401
1459
  const nodeKey = vdomGetCompareKey(newNode);
1402
1460
  if (nodeKey) {
1403
- newKeyedNodes[nodeKey] = (newKeyedNodes[nodeKey] || 0) + 1;
1461
+ newKeyedNodes.set(nodeKey, (newKeyedNodes.get(nodeKey) ?? 0) + 1);
1404
1462
  }
1405
1463
  newNode = newNode.nextSibling;
1406
1464
  }
@@ -1411,23 +1469,25 @@ function vdomSetChildNodes(oldParent, newParent, ref, frame, keys_) {
1411
1469
  const tempNew = newNode;
1412
1470
  newNode = newNode.nextSibling;
1413
1471
  const nodeKey = vdomGetCompareKey(tempNew);
1414
- let foundNode = nodeKey ? keyedNodes[nodeKey] : void 0;
1472
+ let foundNode = nodeKey ? keyedNodes.get(nodeKey) : void 0;
1415
1473
  if (foundNode && (foundNode = foundNode.slice()) && foundNode.length) {
1416
1474
  const matched = foundNode.pop();
1417
1475
  while (matched !== oldNode) {
1418
- const next = oldNode?.nextSibling ?? null;
1476
+ if (!oldNode) break;
1477
+ const next = oldNode.nextSibling;
1419
1478
  oldParent.appendChild(oldNode);
1420
1479
  oldNode = next;
1421
1480
  }
1422
1481
  oldNode = matched.nextSibling;
1423
- if (nodeKey && newKeyedNodes[nodeKey]) {
1424
- newKeyedNodes[nodeKey]--;
1482
+ if (nodeKey) {
1483
+ const c = newKeyedNodes.get(nodeKey);
1484
+ if (c) newKeyedNodes.set(nodeKey, c - 1);
1425
1485
  }
1426
1486
  vdomSetNode(matched, tempNew, oldParent, ref, frame, keys_);
1427
1487
  } else if (oldNode) {
1428
1488
  const tempOld2 = oldNode;
1429
1489
  const oldKey = vdomGetCompareKey(tempOld2);
1430
- if (oldKey && keyedNodes[oldKey] && newKeyedNodes[oldKey]) {
1490
+ if (oldKey && keyedNodes.has(oldKey) && newKeyedNodes.get(oldKey)) {
1431
1491
  extra++;
1432
1492
  ref.hasChanged = 1;
1433
1493
  ref.domOps.push([8, oldParent, tempNew, tempOld2]);
@@ -1451,17 +1511,21 @@ function vdomSetChildNodes(oldParent, newParent, ref, frame, keys_) {
1451
1511
  }
1452
1512
  }
1453
1513
  function vdomSetNode(oldNode, newNode, oldParent, ref, frame, keys_) {
1454
- 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) {
1455
1519
  if (oldNode.nodeType === newNode.nodeType && oldNode.nodeName === newNode.nodeName) {
1456
- if (oldNode.nodeType === 1) {
1457
- const oldEl = oldNode;
1458
- const newEl = newNode;
1459
- const staticKey = newEl.getAttribute(LARK_KEYS.DIFF_KEY);
1460
- 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)) {
1461
1525
  return;
1462
1526
  }
1463
1527
  const newLarkView = newEl.getAttribute(LARK_VIEW);
1464
- 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);
1465
1529
  let updateChildren = true;
1466
1530
  if (newLarkView) {
1467
1531
  const oldFrameId = oldEl.getAttribute("id") || "";
@@ -1562,7 +1626,8 @@ function encodeQ(v) {
1562
1626
  }
1563
1627
 
1564
1628
  // src/updater.ts
1565
- function updaterRef(refData, value, key) {
1629
+ function updaterRef(refDataIn, value, key) {
1630
+ const refData = refDataIn;
1566
1631
  const counter = refData[SPLITTER];
1567
1632
  for (let i = counter; --i; ) {
1568
1633
  key = SPLITTER + i;
@@ -1582,13 +1647,19 @@ var Updater = class {
1582
1647
  /** Ref data for template rendering */
1583
1648
  refData;
1584
1649
  /** Changed keys in current digest cycle */
1585
- changedKeys = {};
1650
+ changedKeys = /* @__PURE__ */ new Set();
1586
1651
  /** Whether data has changed since last digest */
1587
1652
  hasChangedFlag = 0;
1588
- /** 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
+ */
1589
1658
  digestingQueue = [];
1590
- /** Snapshot JSON string for altered() detection */
1591
- 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;
1592
1663
  constructor(viewId) {
1593
1664
  this.viewId = viewId;
1594
1665
  this.data = { vId: viewId };
@@ -1616,7 +1687,16 @@ var Updater = class {
1616
1687
  * Returns this for chaining.
1617
1688
  */
1618
1689
  set(data, excludes) {
1619
- 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
+ }
1620
1700
  return this;
1621
1701
  }
1622
1702
  /**
@@ -1652,13 +1732,13 @@ var Updater = class {
1652
1732
  const keys2 = this.changedKeys;
1653
1733
  const changed = this.hasChangedFlag;
1654
1734
  this.hasChangedFlag = 0;
1655
- this.changedKeys = {};
1735
+ this.changedKeys = /* @__PURE__ */ new Set();
1656
1736
  const frame = Frame.get(this.viewId);
1657
1737
  const view = frame?.view;
1658
1738
  const node = getById(this.viewId);
1659
- if (changed && view && node && view.signature > 0) {
1739
+ if (changed && view && node && view.signature > 0 && frame) {
1660
1740
  const template = view.template;
1661
- if (template && typeof template === "function") {
1741
+ if (typeof template === "function") {
1662
1742
  const html = template(
1663
1743
  this.data,
1664
1744
  this.viewId,
@@ -1695,43 +1775,65 @@ var Updater = class {
1695
1775
  }
1696
1776
  }
1697
1777
  /**
1698
- * 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.
1699
1780
  */
1700
1781
  snapshot() {
1701
- this.snapshotJson = JSON.stringify(this.data);
1782
+ this.snapshotVersion = this.version;
1702
1783
  return this;
1703
1784
  }
1704
1785
  /**
1705
- * 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.
1706
1788
  */
1707
1789
  altered() {
1708
- if (this.snapshotJson) {
1709
- return this.snapshotJson !== JSON.stringify(this.data);
1710
- }
1711
- return void 0;
1790
+ if (this.snapshotVersion === void 0) return void 0;
1791
+ return this.version !== this.snapshotVersion;
1712
1792
  }
1713
1793
  /**
1714
- * 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").
1715
1800
  */
1716
1801
  translate(data) {
1717
- if (typeof data === "string" && data[0] === SPLITTER) {
1718
- 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;
1719
1807
  }
1720
- return data;
1808
+ return hasOwnProperty(this.refData, data) ? this.refData[data] : data;
1721
1809
  }
1722
1810
  /**
1723
- * 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.
1724
1818
  */
1725
1819
  parse(expr) {
1726
- try {
1727
- const fn = new Function("data", `with(data) { return ${expr}; }`);
1728
- return fn(this.refData);
1729
- } 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)) {
1730
1826
  return void 0;
1731
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;
1732
1834
  }
1733
1835
  /**
1734
- * Get changed keys (for external inspection).
1836
+ * Get the set of keys changed since the last digest (for external inspection).
1735
1837
  */
1736
1838
  getChangedKeys() {
1737
1839
  return this.changedKeys;
@@ -1746,221 +1848,6 @@ if (typeof window !== "undefined") {
1746
1848
  if (typeof document !== "undefined") {
1747
1849
  VIEW_GLOBALS["document"] = document;
1748
1850
  }
1749
- function viewPrepare(oView) {
1750
- if (oView.ctors) {
1751
- return oView.ctors;
1752
- }
1753
- const ctors = [];
1754
- oView.ctors = ctors;
1755
- const proto = oView.prototype;
1756
- const eventsObject = {};
1757
- const eventsList = [];
1758
- const selectorObject = {};
1759
- const mixins = proto["mixins"];
1760
- if (mixins && Array.isArray(mixins)) {
1761
- viewMergeMixins(mixins, oView, ctors);
1762
- }
1763
- for (const p in proto) {
1764
- if (!has(proto, p)) continue;
1765
- const currentFn = proto[p];
1766
- if (typeof currentFn !== "function") continue;
1767
- const matches = p.match(VIEW_EVENT_METHOD_REGEXP);
1768
- if (!matches) continue;
1769
- const isSelector = matches[1];
1770
- const selectorOrCallback = matches[2];
1771
- const events = matches[3];
1772
- const modifiers = matches[4];
1773
- const mod = {};
1774
- if (modifiers) {
1775
- for (const item of modifiers.split(",")) {
1776
- mod[item] = true;
1777
- }
1778
- }
1779
- const eventTypes = events.split(",");
1780
- for (const item of eventTypes) {
1781
- const globalNode = VIEW_GLOBALS[selectorOrCallback];
1782
- let mask = 1;
1783
- if (isSelector) {
1784
- if (globalNode) {
1785
- eventsList.push({
1786
- handler: currentFn,
1787
- element: globalNode,
1788
- eventName: item,
1789
- modifiers: mod
1790
- });
1791
- continue;
1792
- }
1793
- mask = 2;
1794
- let selectorEntry = selectorObject[item];
1795
- if (!selectorEntry) {
1796
- selectorEntry = selectorObject[item] = {
1797
- selectors: []
1798
- };
1799
- }
1800
- if (!selectorEntry[selectorOrCallback]) {
1801
- selectorEntry[selectorOrCallback] = 1;
1802
- selectorEntry.selectors.push(selectorOrCallback);
1803
- }
1804
- }
1805
- eventsObject[item] = (eventsObject[item] || 0) | mask;
1806
- const combinedKey = selectorOrCallback + SPLITTER + item;
1807
- const existingFn = proto[combinedKey];
1808
- if (!existingFn) {
1809
- proto[combinedKey] = currentFn;
1810
- } else {
1811
- const mixinFn = currentFn;
1812
- const existingMixin = existingFn;
1813
- if (existingMixin.b) {
1814
- if (mixinFn.b) {
1815
- proto[combinedKey] = processMixinsSameEvent(
1816
- currentFn,
1817
- existingFn
1818
- );
1819
- } else if (has(proto, p)) {
1820
- proto[combinedKey] = currentFn;
1821
- }
1822
- }
1823
- }
1824
- }
1825
- }
1826
- viewWrapMethod(proto, "render", "$b");
1827
- proto["$eo"] = eventsObject;
1828
- proto["$el"] = eventsList;
1829
- proto["$so"] = selectorObject;
1830
- proto["$f"] = proto["assign"];
1831
- return ctors;
1832
- }
1833
- function viewWrapMethod(proto, fnName, shortKey) {
1834
- const originalFn = proto[fnName];
1835
- if (typeof originalFn !== "function") return;
1836
- const wrapped = function(...args) {
1837
- if (this.signature > 0) {
1838
- this.signature++;
1839
- this.fire("rendercall");
1840
- destroyAllResources(this, false);
1841
- const instanceFn = typeof this[fnName] === "function" ? this[fnName] : originalFn;
1842
- const fnToCall = instanceFn === wrapped ? originalFn : instanceFn;
1843
- return funcWithTry(fnToCall, args, this, noop);
1844
- }
1845
- return void 0;
1846
- };
1847
- proto[fnName] = wrapped;
1848
- proto[shortKey] = wrapped;
1849
- }
1850
- function processMixinsSameEvent(additional, exist) {
1851
- let temp;
1852
- const existMixin = exist;
1853
- if (existMixin.a) {
1854
- temp = existMixin;
1855
- } else {
1856
- temp = function(...e) {
1857
- funcWithTry(temp.a ?? [], e, this, noop);
1858
- };
1859
- temp.a = [exist];
1860
- temp.b = 1;
1861
- }
1862
- const additionalMixin = additional;
1863
- temp.a = (temp.a ?? []).concat(additionalMixin.a ?? [additional]);
1864
- return temp;
1865
- }
1866
- function viewMergeMixins(mixins, viewClass, ctors) {
1867
- const proto = viewClass.prototype;
1868
- const temp = {};
1869
- for (const node of mixins) {
1870
- for (const p in node) {
1871
- if (!has(node, p)) continue;
1872
- const fn = node[p];
1873
- const exist = temp[p];
1874
- if (p === "make") {
1875
- ctors.push(fn);
1876
- continue;
1877
- }
1878
- if (VIEW_EVENT_METHOD_REGEXP.test(p)) {
1879
- if (exist) {
1880
- temp[p] = processMixinsSameEvent(fn, exist);
1881
- } else {
1882
- fn.b = 1;
1883
- temp[p] = fn;
1884
- }
1885
- } else {
1886
- if (!exist) {
1887
- temp[p] = fn;
1888
- }
1889
- }
1890
- }
1891
- }
1892
- for (const p in temp) {
1893
- if (!has(proto, p)) {
1894
- proto[p] = temp[p];
1895
- }
1896
- }
1897
- }
1898
- function viewDelegateEvents(view, destroy = false) {
1899
- const proto = Object.getPrototypeOf(view) ?? {};
1900
- const eventsObject = proto["$eo"] || view.eventObjectMap;
1901
- const selectorObject = proto["$so"] || view.eventSelectorMap;
1902
- const eventsList = proto["$el"] || view.globalEventList;
1903
- for (const e in eventsObject) {
1904
- if (has(eventsObject, e)) {
1905
- if (destroy) {
1906
- EventDelegator.unbind(e, !!selectorObject[e]);
1907
- } else {
1908
- EventDelegator.bind(e, !!selectorObject[e]);
1909
- }
1910
- }
1911
- }
1912
- for (const entry of eventsList) {
1913
- if (destroy) {
1914
- entry.element.removeEventListener(
1915
- entry.eventName,
1916
- entry.boundHandler
1917
- );
1918
- } else {
1919
- const handler = entry.handler;
1920
- const element = entry.element;
1921
- const modifiers = entry.modifiers;
1922
- entry.boundHandler = function(domEvent) {
1923
- const extendedEvent = domEvent;
1924
- extendedEvent.eventTarget = element;
1925
- if (modifiers) {
1926
- const kbEvent = domEvent;
1927
- if (modifiers["ctrl"] && !kbEvent.ctrlKey || modifiers["shift"] && !kbEvent.shiftKey || modifiers["alt"] && !kbEvent.altKey || modifiers["meta"] && !kbEvent.metaKey) {
1928
- return;
1929
- }
1930
- }
1931
- funcWithTry(handler, [domEvent], view, noop);
1932
- };
1933
- entry.element.addEventListener(
1934
- entry.eventName,
1935
- entry.boundHandler
1936
- );
1937
- }
1938
- }
1939
- }
1940
- function destroyAllResources(view, lastly) {
1941
- const cache = view.resources;
1942
- for (const p in cache) {
1943
- if (has(cache, p)) {
1944
- const entry = cache[p];
1945
- if (lastly || entry.destroyOnRender) {
1946
- destroyResource(cache, p, true);
1947
- }
1948
- }
1949
- }
1950
- }
1951
- function destroyResource(cache, key, callDestroy, oldEntity) {
1952
- const entry = cache[key];
1953
- if (!entry || entry.entity === oldEntity) return void 0;
1954
- const entity = entry.entity;
1955
- if (entity && typeof entity === "object") {
1956
- const destroyFn = entity["destroy"];
1957
- if (typeof destroyFn === "function" && callDestroy) {
1958
- funcWithTry(destroyFn, [], entity, noop);
1959
- }
1960
- }
1961
- Reflect.deleteProperty(cache, key);
1962
- return entity;
1963
- }
1964
1851
  var View = class _View {
1965
1852
  /** View ID (same as owner frame ID) */
1966
1853
  id = "";
@@ -1984,18 +1871,44 @@ var View = class _View {
1984
1871
  observedStateKeys;
1985
1872
  /** Resource map */
1986
1873
  resources = {};
1987
- /** Selector event map: eventType -> handler name list */
1988
- eventSelectorMap = {};
1989
- /** Event object map: eventType -> bitmask */
1990
- eventObjectMap = {};
1991
- /** Global event list */
1992
- globalEventList = [];
1993
1874
  /** Assign method reference */
1994
1875
  assignMethod;
1995
1876
  /** Whether endUpdate pending */
1996
1877
  endUpdatePending;
1997
1878
  /** Internal event storage */
1998
1879
  _events = new EventEmitter();
1880
+ // ============================================================
1881
+ // Getters for prototype-stored event maps
1882
+ // ============================================================
1883
+ /** Prototype-stored event maps shape (set by View.prepare). */
1884
+ get protoEventState() {
1885
+ return Object.getPrototypeOf(this);
1886
+ }
1887
+ /**
1888
+ * Event bitmask map: eventType -> bitmask (1=root, 2=selector).
1889
+ * Read from prototype ($evtObjMap) set by View.prepare.
1890
+ * Using a getter avoids ES6 class field shadowing the prototype value.
1891
+ */
1892
+ get eventObjectMap() {
1893
+ return this.protoEventState.$evtObjMap ?? {};
1894
+ }
1895
+ /**
1896
+ * Selector event map: eventType -> selector list.
1897
+ * Read from prototype ($selMap) set by View.prepare.
1898
+ */
1899
+ get eventSelectorMap() {
1900
+ return this.protoEventState.$selMap ?? {};
1901
+ }
1902
+ /**
1903
+ * Global event list: [{handler, element, eventName, modifiers}].
1904
+ * Read from prototype ($globalEvtList) set by View.prepare.
1905
+ */
1906
+ get globalEventList() {
1907
+ return this.protoEventState.$globalEvtList ?? [];
1908
+ }
1909
+ // ============================================================
1910
+ // Instance lifecycle methods
1911
+ // ============================================================
1999
1912
  /**
2000
1913
  * Initialize view (called by Frame when mounting).
2001
1914
  */
@@ -2003,14 +1916,7 @@ var View = class _View {
2003
1916
  }
2004
1917
  /**
2005
1918
  * Render view template (called by Frame after init).
2006
- * Wrapped by View_WrapMethod to manage signature + resources.
2007
- *
2008
- * Default implementation calls updater.digest() which:
2009
- * 1. Executes the template function with current data
2010
- * 2. Runs VDOM diff against previous DOM
2011
- * 3. Applies DOM operations
2012
- * 4. Calls endUpdate to mount child frames
2013
- *
1919
+ * Wrapped by View.wrapMethod to manage signature + resources.
2014
1920
  */
2015
1921
  render() {
2016
1922
  this.updater.digest();
@@ -2033,13 +1939,17 @@ var View = class _View {
2033
1939
  // ============================================================
2034
1940
  // Update methods
2035
1941
  // ============================================================
1942
+ /** Get the owning frame, asserting it has been bound. */
1943
+ get ownerFrame() {
1944
+ return this.owner;
1945
+ }
2036
1946
  /**
2037
1947
  * Notify view that HTML update is about to begin.
2038
1948
  * Unmounts child frames in the update zone.
2039
1949
  */
2040
1950
  beginUpdate(id) {
2041
1951
  if (this.signature > 0 && this.endUpdatePending !== void 0) {
2042
- this.owner.unmountZone(id, true);
1952
+ this.ownerFrame.unmountZone(id, true);
2043
1953
  }
2044
1954
  }
2045
1955
  /**
@@ -2057,12 +1967,12 @@ var View = class _View {
2057
1967
  this.endUpdatePending = 1;
2058
1968
  this.rendered = true;
2059
1969
  }
2060
- const ownerFrame = this.owner;
1970
+ const ownerFrame = this.ownerFrame;
2061
1971
  ownerFrame.mountZone(updateId, inner);
2062
1972
  if (!flag) {
2063
1973
  setTimeout(
2064
1974
  this.wrapAsync(() => {
2065
- runInvokes(ownerFrame);
1975
+ _View.runInvokes(ownerFrame);
2066
1976
  }),
2067
1977
  0
2068
1978
  );
@@ -2096,11 +2006,12 @@ var View = class _View {
2096
2006
  const loc = this.locationObserved;
2097
2007
  loc.flag = 1;
2098
2008
  if (typeof params === "object" && !Array.isArray(params)) {
2099
- if (params["path"]) {
2009
+ const opts = params;
2010
+ if (opts["path"]) {
2100
2011
  observePath = true;
2101
2012
  }
2102
- const paramKeys = params["params"];
2103
- if (paramKeys) {
2013
+ const paramKeys = opts["params"];
2014
+ if (typeof paramKeys === "string" || Array.isArray(paramKeys)) {
2104
2015
  params = paramKeys;
2105
2016
  }
2106
2017
  }
@@ -2138,7 +2049,7 @@ var View = class _View {
2138
2049
  capture(key, resource, destroyOnRender = false) {
2139
2050
  const cache = this.resources;
2140
2051
  if (resource) {
2141
- destroyResource(cache, key, true, resource);
2052
+ _View.destroyResource(cache, key, true, resource);
2142
2053
  cache[key] = {
2143
2054
  entity: resource,
2144
2055
  destroyOnRender
@@ -2154,7 +2065,7 @@ var View = class _View {
2154
2065
  * If destroy=true, calls the resource's destroy() method.
2155
2066
  */
2156
2067
  release(key, destroy = true) {
2157
- return destroyResource(this.resources, key, destroy);
2068
+ return _View.destroyResource(this.resources, key, destroy);
2158
2069
  }
2159
2070
  // ============================================================
2160
2071
  // Leave tip
@@ -2164,24 +2075,16 @@ var View = class _View {
2164
2075
  */
2165
2076
  leaveTip(message, condition) {
2166
2077
  const changeListener = function(e) {
2167
- const isRouterChange = e.type === ROUTER_EVENTS.CHANGE;
2078
+ const isRouterChange = e.type === RouterEvents.CHANGE;
2168
2079
  const aKey = isRouterChange ? "a" : "b";
2169
2080
  const bKey = isRouterChange ? "b" : "a";
2170
2081
  if (changeListener[aKey]) {
2171
- if (typeof e.prevent === "function") {
2172
- e.prevent();
2173
- }
2174
- if (typeof e.reject === "function") {
2175
- e.reject();
2176
- }
2082
+ e.prevent?.();
2083
+ e.reject?.();
2177
2084
  } else if (condition()) {
2178
- if (typeof e.prevent === "function") {
2179
- e.prevent();
2180
- }
2085
+ e.prevent?.();
2181
2086
  changeListener[bKey] = 1;
2182
- if (typeof e.resolve === "function") {
2183
- e.resolve();
2184
- }
2087
+ e.resolve?.();
2185
2088
  }
2186
2089
  };
2187
2090
  const unloadListener = (e) => {
@@ -2189,21 +2092,284 @@ var View = class _View {
2189
2092
  e["msg"] = message;
2190
2093
  }
2191
2094
  };
2192
- const changeFn = changeListener;
2193
- const unloadFn = unloadListener;
2194
- Router.on(ROUTER_EVENTS.CHANGE, changeFn);
2195
- Router.on(ROUTER_EVENTS.PAGE_UNLOAD, unloadFn);
2196
- this.on("unload", changeFn);
2095
+ Router.on(RouterEvents.CHANGE, changeListener);
2096
+ Router.on(RouterEvents.PAGE_UNLOAD, unloadListener);
2097
+ this.on("unload", changeListener);
2197
2098
  this.on("destroy", () => {
2198
- Router.off(ROUTER_EVENTS.CHANGE, changeFn);
2199
- Router.off(ROUTER_EVENTS.PAGE_UNLOAD, unloadFn);
2099
+ Router.off(RouterEvents.CHANGE, changeListener);
2100
+ Router.off(RouterEvents.PAGE_UNLOAD, unloadListener);
2200
2101
  });
2201
2102
  }
2202
2103
  // ============================================================
2203
- // Static: extend and merge
2104
+ // Static public methods
2204
2105
  // ============================================================
2205
2106
  /** Collected ctors from mixins */
2206
2107
  static ctors;
2108
+ /**
2109
+ * Prepare a View subclass by scanning its prototype for event method patterns.
2110
+ * Pattern: `$?name<eventType1,eventType2>(&modifiers)`
2111
+ *
2112
+ * Only runs once per View subclass (guarded by ctors marker).
2113
+ * Called from Frame.mountView before creating the view instance.
2114
+ */
2115
+ static prepare(oView) {
2116
+ if (oView.ctors) {
2117
+ return oView.ctors;
2118
+ }
2119
+ const ctors = [];
2120
+ oView.ctors = ctors;
2121
+ const proto = oView.prototype;
2122
+ const eventsObject = {};
2123
+ const eventsList = [];
2124
+ const selectorObject = {};
2125
+ const mixins = proto["mixins"];
2126
+ if (mixins && Array.isArray(mixins)) {
2127
+ _View.mergeMixins(mixins, oView, ctors);
2128
+ }
2129
+ for (const p in proto) {
2130
+ if (!hasOwnProperty(proto, p)) continue;
2131
+ const currentFn = proto[p];
2132
+ if (typeof currentFn !== "function") continue;
2133
+ const matches = p.match(VIEW_EVENT_METHOD_REGEXP);
2134
+ if (!matches) continue;
2135
+ const isSelector = matches[1];
2136
+ const selectorOrCallback = matches[2];
2137
+ const events = matches[3];
2138
+ const modifiers = matches[4];
2139
+ const mod = {};
2140
+ if (modifiers) {
2141
+ for (const item of modifiers.split(",")) {
2142
+ mod[item] = true;
2143
+ }
2144
+ }
2145
+ const eventTypes = events.split(",");
2146
+ for (const item of eventTypes) {
2147
+ const globalNode = VIEW_GLOBALS[selectorOrCallback];
2148
+ let mask = 1;
2149
+ if (isSelector) {
2150
+ if (globalNode) {
2151
+ eventsList.push({
2152
+ handler: currentFn,
2153
+ element: globalNode,
2154
+ eventName: item,
2155
+ modifiers: mod
2156
+ });
2157
+ continue;
2158
+ }
2159
+ mask = 2;
2160
+ let selectorEntry = selectorObject[item];
2161
+ if (!selectorEntry) {
2162
+ selectorEntry = selectorObject[item] = {
2163
+ selectors: []
2164
+ };
2165
+ }
2166
+ if (!selectorEntry[selectorOrCallback]) {
2167
+ selectorEntry[selectorOrCallback] = 1;
2168
+ selectorEntry.selectors.push(selectorOrCallback);
2169
+ }
2170
+ }
2171
+ eventsObject[item] = (eventsObject[item] || 0) | mask;
2172
+ const combinedKey = selectorOrCallback + SPLITTER + item;
2173
+ const existingFn = proto[combinedKey];
2174
+ if (!existingFn) {
2175
+ proto[combinedKey] = currentFn;
2176
+ } else if (typeof existingFn === "function") {
2177
+ const mixinFn = currentFn;
2178
+ const existingMixin = existingFn;
2179
+ if (existingMixin.marker) {
2180
+ if (mixinFn.marker) {
2181
+ proto[combinedKey] = _View.processMixinsSameEvent(
2182
+ mixinFn,
2183
+ existingMixin
2184
+ );
2185
+ } else if (hasOwnProperty(proto, p)) {
2186
+ proto[combinedKey] = currentFn;
2187
+ }
2188
+ }
2189
+ }
2190
+ }
2191
+ }
2192
+ _View.wrapMethod(proto, "render", "$renderWrap");
2193
+ proto["$evtObjMap"] = eventsObject;
2194
+ proto["$globalEvtList"] = eventsList;
2195
+ proto["$selMap"] = selectorObject;
2196
+ proto["$assignFn"] = proto["assign"];
2197
+ return ctors;
2198
+ }
2199
+ /**
2200
+ * Bind or unbind event delegation for a view instance.
2201
+ * Called from Frame during mount/unmount.
2202
+ */
2203
+ static delegateEvents(view, destroy = false) {
2204
+ const eventsObject = view.eventObjectMap;
2205
+ const selectorObject = view.eventSelectorMap;
2206
+ const eventsList = view.globalEventList;
2207
+ for (const e in eventsObject) {
2208
+ if (hasOwnProperty(eventsObject, e)) {
2209
+ if (destroy) {
2210
+ EventDelegator.unbind(e, !!selectorObject[e]);
2211
+ } else {
2212
+ EventDelegator.bind(e, !!selectorObject[e]);
2213
+ }
2214
+ }
2215
+ }
2216
+ for (const entry of eventsList) {
2217
+ if (destroy) {
2218
+ entry.element.removeEventListener(
2219
+ entry.eventName,
2220
+ entry.boundHandler
2221
+ );
2222
+ } else {
2223
+ const handler = entry.handler;
2224
+ const element = entry.element;
2225
+ const modifiers = entry.modifiers;
2226
+ entry.boundHandler = function(domEvent) {
2227
+ const extendedEvent = domEvent;
2228
+ extendedEvent.eventTarget = element;
2229
+ if (modifiers) {
2230
+ const kbEvent = domEvent;
2231
+ if (modifiers["ctrl"] && !kbEvent.ctrlKey || modifiers["shift"] && !kbEvent.shiftKey || modifiers["alt"] && !kbEvent.altKey || modifiers["meta"] && !kbEvent.metaKey) {
2232
+ return;
2233
+ }
2234
+ }
2235
+ funcWithTry(handler, [domEvent], view, noop);
2236
+ };
2237
+ entry.element.addEventListener(
2238
+ entry.eventName,
2239
+ entry.boundHandler
2240
+ );
2241
+ }
2242
+ }
2243
+ }
2244
+ /**
2245
+ * Destroy all resources managed by a view.
2246
+ * If lastly=true, destroy ALL resources; otherwise only destroyOnRender ones.
2247
+ */
2248
+ static destroyAllResources(view, lastly) {
2249
+ const cache = view.resources;
2250
+ for (const p in cache) {
2251
+ if (hasOwnProperty(cache, p)) {
2252
+ const entry = cache[p];
2253
+ if (lastly || entry.destroyOnRender) {
2254
+ _View.destroyResource(cache, p, true);
2255
+ }
2256
+ }
2257
+ }
2258
+ }
2259
+ /**
2260
+ * Process deferred invoke calls on a frame.
2261
+ */
2262
+ static runInvokes(frame) {
2263
+ const list = frame.invokeList;
2264
+ if (!list) return;
2265
+ while (list.length) {
2266
+ const entry = list.shift();
2267
+ if (entry && !entry.removed) {
2268
+ frame.invoke(entry.name, entry.args);
2269
+ }
2270
+ }
2271
+ }
2272
+ // ============================================================
2273
+ // Static private methods
2274
+ // ============================================================
2275
+ /**
2276
+ * Wrap a method on the prototype to add signature checking and resource cleanup.
2277
+ */
2278
+ static wrapMethod(proto, fnName, shortKey) {
2279
+ const originalFn = proto[fnName];
2280
+ if (typeof originalFn !== "function") return;
2281
+ const originalAsFn = originalFn;
2282
+ const wrapped = function(...args) {
2283
+ if (this.signature > 0) {
2284
+ this.signature++;
2285
+ this.fire("render");
2286
+ _View.destroyAllResources(this, false);
2287
+ const lookup = this;
2288
+ const candidate = lookup[fnName];
2289
+ const instanceFn = typeof candidate === "function" ? candidate : originalAsFn;
2290
+ const fnToCall = instanceFn === wrapped ? originalAsFn : instanceFn;
2291
+ return funcWithTry(fnToCall, args, this, noop);
2292
+ }
2293
+ return void 0;
2294
+ };
2295
+ proto[fnName] = wrapped;
2296
+ proto[shortKey] = wrapped;
2297
+ }
2298
+ /**
2299
+ * When two mixins define the same event method, merge them into
2300
+ * a single function that calls both in sequence.
2301
+ */
2302
+ static processMixinsSameEvent(additional, exist) {
2303
+ let temp;
2304
+ if (exist.handlerList) {
2305
+ temp = exist;
2306
+ } else {
2307
+ const merged = function(...e) {
2308
+ funcWithTry(merged.handlerList ?? [], e, this, noop);
2309
+ };
2310
+ merged.handlerList = [exist];
2311
+ merged.marker = 1;
2312
+ temp = merged;
2313
+ }
2314
+ temp.handlerList = (temp.handlerList ?? []).concat(
2315
+ additional.handlerList ?? [additional]
2316
+ );
2317
+ return temp;
2318
+ }
2319
+ /**
2320
+ * Merge an array of mixin objects into the view prototype.
2321
+ */
2322
+ static mergeMixins(mixins, viewClass, ctors) {
2323
+ const proto = viewClass.prototype;
2324
+ const temp = {};
2325
+ for (const node of mixins) {
2326
+ for (const p in node) {
2327
+ if (!hasOwnProperty(node, p)) continue;
2328
+ const fn = node[p];
2329
+ if (typeof fn !== "function") continue;
2330
+ const mixinFn = fn;
2331
+ const exist = temp[p];
2332
+ if (p === "make") {
2333
+ ctors.push(mixinFn);
2334
+ continue;
2335
+ }
2336
+ if (VIEW_EVENT_METHOD_REGEXP.test(p)) {
2337
+ if (exist) {
2338
+ temp[p] = _View.processMixinsSameEvent(mixinFn, exist);
2339
+ } else {
2340
+ mixinFn.marker = 1;
2341
+ temp[p] = mixinFn;
2342
+ }
2343
+ } else if (!exist) {
2344
+ temp[p] = mixinFn;
2345
+ }
2346
+ }
2347
+ }
2348
+ for (const p in temp) {
2349
+ if (!hasOwnProperty(proto, p)) {
2350
+ proto[p] = temp[p];
2351
+ }
2352
+ }
2353
+ }
2354
+ /**
2355
+ * Destroy a single resource entry.
2356
+ */
2357
+ static destroyResource(cache, key, callDestroy, oldEntity) {
2358
+ const entry = cache[key];
2359
+ if (!entry || entry.entity === oldEntity) return void 0;
2360
+ const entity = entry.entity;
2361
+ if (entity && typeof entity === "object") {
2362
+ const destroyFn = entity["destroy"];
2363
+ if (typeof destroyFn === "function" && callDestroy) {
2364
+ funcWithTry(destroyFn, [], entity, noop);
2365
+ }
2366
+ }
2367
+ Reflect.deleteProperty(cache, key);
2368
+ return entity;
2369
+ }
2370
+ // ============================================================
2371
+ // Static: extend and merge
2372
+ // ============================================================
2207
2373
  /**
2208
2374
  * Extend View to create a new View subclass.
2209
2375
  *
@@ -2213,19 +2379,20 @@ var View = class _View {
2213
2379
  * - Event method patterns: `'name<click>'` etc.
2214
2380
  */
2215
2381
  static extend(props, statics) {
2216
- props = props || {};
2217
- const make = props["make"];
2382
+ const definedProps = props ?? {};
2383
+ const make = definedProps["make"];
2218
2384
  const ctors = [];
2219
- if (make) {
2385
+ if (typeof make === "function") {
2220
2386
  ctors.push(make);
2221
2387
  }
2222
2388
  const ParentView = this;
2223
2389
  const ChildView = class extends ParentView {
2224
2390
  constructor(nodeId, ownerFrame, initParams, node, mixinCtors) {
2225
2391
  super(nodeId, ownerFrame, initParams, node, []);
2226
- for (const key in props) {
2227
- if (has(props, key) && key !== "make") {
2228
- 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];
2229
2396
  }
2230
2397
  }
2231
2398
  this.id = nodeId;
@@ -2245,56 +2412,110 @@ var View = class _View {
2245
2412
  }
2246
2413
  };
2247
2414
  const proto = ChildView.prototype;
2248
- for (const key in props) {
2249
- if (has(props, key) && key !== "make") {
2250
- proto[key] = props[key];
2415
+ for (const key in definedProps) {
2416
+ if (hasOwnProperty(definedProps, key) && key !== "make") {
2417
+ proto[key] = definedProps[key];
2251
2418
  }
2252
2419
  }
2253
2420
  if (statics) {
2421
+ const staticTarget = ChildView;
2254
2422
  for (const key in statics) {
2255
- if (has(statics, key)) {
2256
- ChildView[key] = statics[key];
2423
+ if (hasOwnProperty(statics, key)) {
2424
+ staticTarget[key] = statics[key];
2257
2425
  }
2258
2426
  }
2259
2427
  }
2260
- ChildView.merge = viewMerge;
2261
- ChildView.extend = _View.extend;
2262
2428
  return ChildView;
2263
2429
  }
2264
2430
  /**
2265
2431
  * Merge mixins into View prototype.
2266
2432
  */
2267
2433
  static merge(...mixins) {
2268
- const self = this;
2269
- const existingCtors = self.ctors || [];
2270
- viewMergeMixins(mixins, self, existingCtors);
2271
- return self;
2434
+ const existingCtors = this.ctors || [];
2435
+ _View.mergeMixins(mixins, this, existingCtors);
2436
+ return this;
2272
2437
  }
2273
2438
  };
2274
- function viewMerge(...mixins) {
2275
- const self = this;
2276
- const existingCtors = self.ctors || [];
2277
- viewMergeMixins(mixins, self, existingCtors);
2278
- return self;
2439
+ function defineView(props, statics) {
2440
+ return View.extend(props, statics);
2279
2441
  }
2280
- function runInvokes(frame) {
2281
- const list = frame.invokeList;
2282
- if (!list) return;
2283
- while (list.length) {
2284
- const entry = list.shift();
2285
- if (entry && !entry.removed) {
2286
- frame.invoke(entry.name, entry.args);
2287
- }
2442
+
2443
+ // src/module-loader.ts
2444
+ var config = {
2445
+ rootId: "root",
2446
+ hashbang: "#!",
2447
+ error: (error) => {
2448
+ throw error;
2288
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;
2289
2510
  }
2290
2511
 
2291
2512
  // src/frame.ts
2292
2513
  var frameRegistry = /* @__PURE__ */ new Map();
2293
2514
  var rootFrame;
2294
2515
  var globalAlter;
2516
+ var MAX_FRAME_POOL = 64;
2295
2517
  var frameCache = [];
2296
2518
  var staticEmitter = new EventEmitter();
2297
- var viewClassRegistry = {};
2298
2519
  var Frame = class _Frame extends EventEmitter {
2299
2520
  /** Frame ID (same as owner DOM element ID) */
2300
2521
  id;
@@ -2309,8 +2530,8 @@ var Frame = class _Frame extends EventEmitter {
2309
2530
  childrenCount = 0;
2310
2531
  /** Ready count (children that have fired 'created') */
2311
2532
  readyCount = 0;
2312
- /** Ready map: id -> 1 */
2313
- readyMap = {};
2533
+ /** Set of child frame IDs that have fired 'created' */
2534
+ readyMap = /* @__PURE__ */ new Set();
2314
2535
  /** View instance */
2315
2536
  viewInstance;
2316
2537
  /** Get view instance (read-only) */
@@ -2381,31 +2602,42 @@ var Frame = class _Frame extends EventEmitter {
2381
2602
  this.viewPath = viewPath;
2382
2603
  const params = parsed["params"];
2383
2604
  translateQuery(pId || this.id, viewPath, params);
2605
+ const initParams = { ...params };
2384
2606
  if (viewInitParams) {
2385
- assign(params, viewInitParams);
2607
+ assign(initParams, viewInitParams);
2386
2608
  }
2387
2609
  const sign = this.signature;
2388
- if (viewClassRegistry[viewClassName]) {
2389
- this.doMountView(viewClassRegistry[viewClassName], params, node, sign);
2610
+ const registered = getViewClass(viewClassName);
2611
+ if (registered) {
2612
+ this.doMountView(registered, initParams, node, sign);
2613
+ return;
2390
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
+ });
2391
2629
  }
2392
2630
  /**
2393
2631
  * Internal: actually mount the view after class is loaded.
2394
2632
  */
2395
2633
  doMountView(ViewClass, params, node, sign) {
2396
2634
  if (sign !== this.signature) return;
2397
- const mixinCtors = viewPrepare(ViewClass);
2398
- const ViewConstructor = ViewClass;
2399
- const view = new ViewConstructor(
2400
- this.id,
2401
- this,
2402
- params,
2403
- node,
2404
- mixinCtors
2405
- );
2635
+ const mixinCtors = View.prepare(ViewClass);
2636
+ const Ctor = ViewClass;
2637
+ const view = new Ctor(this.id, this, params, node, mixinCtors);
2406
2638
  this.viewInstance = view;
2407
2639
  view.signature = 1;
2408
- viewDelegateEvents(view);
2640
+ View.delegateEvents(view);
2409
2641
  const initResult = funcWithTry(
2410
2642
  view.init,
2411
2643
  [params, { node, deep: !view.template }],
@@ -2416,13 +2648,10 @@ var Frame = class _Frame extends EventEmitter {
2416
2648
  Promise.resolve(initResult).then(() => {
2417
2649
  if (nextSign !== this.signature) return;
2418
2650
  if (view.template) {
2419
- const renderFn = view.$b;
2420
- if (renderFn) {
2421
- renderFn.call(view);
2422
- }
2651
+ view.render();
2423
2652
  } else {
2424
2653
  this.hasAltered = 0;
2425
- if (!view.$e) {
2654
+ if (!view.endUpdatePendingFlag) {
2426
2655
  view.endUpdate();
2427
2656
  }
2428
2657
  }
@@ -2486,7 +2715,9 @@ var Frame = class _Frame extends EventEmitter {
2486
2715
  frame.unmountView();
2487
2716
  removeFrame(targetId, wasCreated);
2488
2717
  reInitFrameForCache(frame);
2489
- frameCache.push(frame);
2718
+ if (frameCache.length < MAX_FRAME_POOL) {
2719
+ frameCache.push(frame);
2720
+ }
2490
2721
  const parent = frameRegistry.get(pId || "");
2491
2722
  if (parent && parent.childrenMap[targetId]) {
2492
2723
  Reflect.deleteProperty(parent.childrenMap, targetId);
@@ -2505,13 +2736,12 @@ var Frame = class _Frame extends EventEmitter {
2505
2736
  const viewElements = rootEl.querySelectorAll(`[${LARK_VIEW}]`);
2506
2737
  const frames = [];
2507
2738
  viewElements.forEach((el) => {
2508
- const htmlEl = el;
2509
- if (!htmlEl.frameBound) {
2510
- const elId = ensureElementId2(htmlEl);
2511
- htmlEl.frameBound = 1;
2512
- const viewPath = getAttribute(htmlEl, LARK_VIEW);
2513
- frames.push([elId, viewPath]);
2514
- }
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]);
2515
2745
  });
2516
2746
  for (const [frameId, viewPath] of frames) {
2517
2747
  this.mountFrame(frameId, viewPath);
@@ -2524,7 +2754,7 @@ var Frame = class _Frame extends EventEmitter {
2524
2754
  */
2525
2755
  unmountZone(zoneId, _inner) {
2526
2756
  for (const childId in this.childrenMap) {
2527
- if (has(this.childrenMap, childId)) {
2757
+ if (hasOwnProperty(this.childrenMap, childId)) {
2528
2758
  if (!zoneId || childId !== zoneId) {
2529
2759
  this.unmountFrame(childId);
2530
2760
  }
@@ -2538,7 +2768,7 @@ var Frame = class _Frame extends EventEmitter {
2538
2768
  children() {
2539
2769
  const result = [];
2540
2770
  for (const id in this.childrenMap) {
2541
- if (has(this.childrenMap, id)) {
2771
+ if (hasOwnProperty(this.childrenMap, id)) {
2542
2772
  result.push(id);
2543
2773
  }
2544
2774
  }
@@ -2565,14 +2795,10 @@ var Frame = class _Frame extends EventEmitter {
2565
2795
  let result;
2566
2796
  const view = this.view;
2567
2797
  if (view && view.rendered) {
2568
- const fn = view[name];
2798
+ const lookup = view;
2799
+ const fn = lookup[name];
2569
2800
  if (typeof fn === "function") {
2570
- result = funcWithTry(
2571
- fn,
2572
- args || [],
2573
- view,
2574
- noop
2575
- );
2801
+ result = funcWithTry(fn, args || [], view, noop);
2576
2802
  }
2577
2803
  } else {
2578
2804
  const key = SPLITTER + name;
@@ -2595,6 +2821,25 @@ var Frame = class _Frame extends EventEmitter {
2595
2821
  }
2596
2822
  return result;
2597
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
+ }
2598
2843
  // ============================================================
2599
2844
  // Static methods
2600
2845
  // ============================================================
@@ -2606,10 +2851,27 @@ var Frame = class _Frame extends EventEmitter {
2606
2851
  static getAll() {
2607
2852
  return frameRegistry;
2608
2853
  }
2609
- /** Get or create root frame */
2610
- 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) {
2611
2873
  if (!rootFrame) {
2612
- rootId = rootId || "lark-root";
2874
+ rootId = rootId || "root";
2613
2875
  let rootElement = document.getElementById(rootId);
2614
2876
  if (!rootElement) {
2615
2877
  rootElement = document.body;
@@ -2619,6 +2881,17 @@ var Frame = class _Frame extends EventEmitter {
2619
2881
  }
2620
2882
  return rootFrame;
2621
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
+ }
2622
2895
  /** Bind event listener (static) */
2623
2896
  static on(event, handler) {
2624
2897
  staticEmitter.on(event, handler);
@@ -2634,6 +2907,9 @@ var Frame = class _Frame extends EventEmitter {
2634
2907
  staticEmitter.fire(event, data);
2635
2908
  }
2636
2909
  };
2910
+ function htmlElIsBound(element) {
2911
+ return !!element.frameBound;
2912
+ }
2637
2913
  function ensureElementId2(element) {
2638
2914
  const id = element.getAttribute("id");
2639
2915
  if (id) return id;
@@ -2663,8 +2939,8 @@ function notifyCreated(frameInstance) {
2663
2939
  const pId = frameInstance.parentId;
2664
2940
  if (pId) {
2665
2941
  const parent = frameRegistry.get(pId);
2666
- if (parent && !parent.readyMap[frameInstance.id]) {
2667
- parent.readyMap[frameInstance.id] = 1;
2942
+ if (parent && !parent.readyMap.has(frameInstance.id)) {
2943
+ parent.readyMap.add(frameInstance.id);
2668
2944
  parent.readyCount++;
2669
2945
  notifyCreated(parent);
2670
2946
  }
@@ -2679,9 +2955,9 @@ function notifyAlter(frameInstance, data) {
2679
2955
  const pId = frameInstance.parentId;
2680
2956
  if (pId) {
2681
2957
  const parent = frameRegistry.get(pId);
2682
- if (parent && parent.readyMap[frameInstance.id]) {
2958
+ if (parent && parent.readyMap.has(frameInstance.id)) {
2683
2959
  parent.readyCount--;
2684
- Reflect.deleteProperty(parent.readyMap, frameInstance.id);
2960
+ parent.readyMap.delete(frameInstance.id);
2685
2961
  notifyAlter(parent, data);
2686
2962
  }
2687
2963
  }
@@ -2694,7 +2970,7 @@ function reInitFrame(frame, id, parentId) {
2694
2970
  frame["childrenCount"] = 0;
2695
2971
  frame["readyCount"] = 0;
2696
2972
  frame["signature"] = 1;
2697
- frame["readyMap"] = {};
2973
+ frame["readyMap"] = /* @__PURE__ */ new Set();
2698
2974
  frame["invokeList"] = [];
2699
2975
  frameRegistry.set(id, frame);
2700
2976
  }
@@ -2702,7 +2978,7 @@ function reInitFrameForCache(frame) {
2702
2978
  Reflect.set(frame, "id", "");
2703
2979
  frame["_parentId"] = void 0;
2704
2980
  frame["childrenMap"] = {};
2705
- frame["readyMap"] = {};
2981
+ frame["readyMap"] = /* @__PURE__ */ new Set();
2706
2982
  }
2707
2983
  function translateQuery(pId, src, params) {
2708
2984
  const parentFrame = frameRegistry.get(pId);
@@ -2712,37 +2988,153 @@ function translateQuery(pId, src, params) {
2712
2988
  if (!parentRefData) return;
2713
2989
  if (src.indexOf(SPLITTER) > 0) {
2714
2990
  translateData(parentRefData, params);
2715
- if (params[SPLITTER]) {
2716
- assign(
2717
- params,
2718
- params[SPLITTER]
2719
- );
2991
+ const paramsRec = params;
2992
+ const splitterValue = paramsRec[SPLITTER];
2993
+ if (splitterValue && typeof splitterValue === "object") {
2994
+ assign(params, splitterValue);
2720
2995
  Reflect.deleteProperty(params, SPLITTER);
2721
2996
  }
2722
2997
  }
2723
2998
  }
2724
- function registerViewClass(viewPath, ViewClass) {
2725
- const parsed = parseUri(viewPath);
2726
- const path = parsed.path;
2727
- if (path) {
2728
- 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
+ });
2729
3036
  }
3037
+ return preparePromises[projectName];
3038
+ }
3039
+ function resetProjectsMap() {
3040
+ projectsMap = null;
2730
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;
2731
3123
 
2732
3124
  // src/service.ts
2733
- var Bag = class {
2734
- /** Bag data */
3125
+ var Payload = class {
3126
+ /** Payload data */
2735
3127
  data;
2736
3128
  /** Internal cache info */
2737
3129
  cacheInfo;
2738
3130
  constructor(data = {}) {
2739
3131
  this.data = data;
2740
3132
  }
2741
- /** Get a value from bag data */
3133
+ /** Get a value from payload data */
2742
3134
  get(key) {
2743
3135
  return this.data[key];
2744
3136
  }
2745
- /** Set a value in bag data */
3137
+ /** Set a value in payload data */
2746
3138
  set(keyOrData, value) {
2747
3139
  if (typeof keyOrData === "string") {
2748
3140
  this.data[keyOrData] = value;
@@ -2754,297 +3146,398 @@ var Bag = class {
2754
3146
  };
2755
3147
  var FETCH_FLAGS_ALL = 1;
2756
3148
  var FETCH_FLAGS_ONE = 2;
2757
- function createServiceType(syncFn, cacheMax = 20, cacheBuffer = 5) {
2758
- const metas = {};
2759
- const bagCache = new Cache({
2760
- maxSize: cacheMax,
2761
- bufferSize: cacheBuffer
2762
- });
2763
- const pendingCacheKeys = {};
2764
- const staticEmitter2 = new EventEmitter();
2765
- const serviceType = {
2766
- add(attrs) {
2767
- if (!isArray(attrs)) {
2768
- attrs = [attrs];
2769
- }
2770
- for (const bag of attrs) {
2771
- if (bag) {
2772
- const name = bag.name;
2773
- const cache = bag.cache;
2774
- bag.cache = cache ? cache | 0 : 0;
2775
- metas[name] = bag;
2776
- }
2777
- }
2778
- },
2779
- meta(attrs) {
2780
- const name = typeof attrs === "string" ? attrs : attrs["name"];
2781
- return metas[name] || attrs;
2782
- },
2783
- create(attrs) {
2784
- const meta = serviceType.meta(attrs);
2785
- const cache = attrs["cache"] | 0 || meta.cache || 0;
2786
- const entity = new Bag();
2787
- entity.set(meta);
2788
- entity.cacheInfo = {
2789
- name: meta.name,
2790
- after: meta.after,
2791
- cleans: meta.cleans,
2792
- key: cache ? defaultCacheKey(meta, attrs) : "",
2793
- time: 0
2794
- };
2795
- if (typeof attrs === "object" && attrs !== null) {
2796
- entity.set(attrs);
2797
- }
2798
- const before = meta.before;
2799
- if (before) {
2800
- funcWithTry(before, [entity], entity, noop);
2801
- }
2802
- staticEmitter2.fire("begin", { bag: entity });
2803
- return entity;
2804
- },
2805
- get(attrs, createNew) {
2806
- let entity;
2807
- let needsUpdate = false;
2808
- if (!createNew) {
2809
- entity = serviceType.cached(attrs);
2810
- }
2811
- if (!entity) {
2812
- entity = serviceType.create(attrs);
2813
- needsUpdate = true;
2814
- }
2815
- return { entity, needsUpdate };
2816
- },
2817
- cached(attrs) {
2818
- const meta = serviceType.meta(attrs);
2819
- const cache = attrs["cache"] | 0 || meta.cache || 0;
2820
- let cacheKey = "";
2821
- if (cache) {
2822
- cacheKey = defaultCacheKey(meta, attrs);
2823
- }
2824
- if (cacheKey) {
2825
- const info = pendingCacheKeys[cacheKey];
2826
- if (info) {
2827
- return info.e;
2828
- }
2829
- const cached = bagCache.get(cacheKey);
2830
- if (cached && cached.cacheInfo) {
2831
- if (now() - cached.cacheInfo.time > cache) {
2832
- bagCache.del(cacheKey);
2833
- return void 0;
3149
+ var Service = class {
3150
+ /** Service instance ID */
3151
+ id = "";
3152
+ /** Whether service is busy (1 = busy) */
3153
+ busy = 0;
3154
+ /** Whether service is destroyed (1 = destroyed) */
3155
+ destroyed = 0;
3156
+ /** Task queue for sequential operations */
3157
+ taskQueue = [];
3158
+ /** Previous dequeue arguments */
3159
+ prevArgs = [];
3160
+ /** Instance event emitter */
3161
+ _emitter = new EventEmitter();
3162
+ constructor() {
3163
+ this.id = generateId("service");
3164
+ }
3165
+ // ============================================================
3166
+ // Instance accessors for type-level data
3167
+ // ============================================================
3168
+ /** Instance event emitter (public accessor) */
3169
+ get emitter() {
3170
+ return this._emitter;
3171
+ }
3172
+ /**
3173
+ * Get internals object for serviceSend compatibility.
3174
+ * References per-type static state from the current class.
3175
+ */
3176
+ get internals() {
3177
+ const ctor = this.constructor;
3178
+ return {
3179
+ metaList: ctor._metaList,
3180
+ payloadCache: ctor._payloadCache,
3181
+ pendingCacheKeys: ctor._pendingCacheKeys,
3182
+ syncFn: ctor._syncFn,
3183
+ staticEmitter: ctor._staticEmitter
3184
+ };
3185
+ }
3186
+ /**
3187
+ * Get type reference (the constructor) for serviceSend compatibility.
3188
+ * Static methods like get/create are accessible via the constructor.
3189
+ */
3190
+ get type() {
3191
+ return this.constructor;
3192
+ }
3193
+ // ============================================================
3194
+ // Instance methods
3195
+ // ============================================================
3196
+ /**
3197
+ * Fetch all endpoints, callback when all complete.
3198
+ * Uses cache when available.
3199
+ */
3200
+ all(attrs, done) {
3201
+ serviceSend(this, attrs, done, FETCH_FLAGS_ALL, false);
3202
+ return this;
3203
+ }
3204
+ /**
3205
+ * Fetch all endpoints, callback on each completion.
3206
+ */
3207
+ one(attrs, done) {
3208
+ serviceSend(this, attrs, done, FETCH_FLAGS_ONE, false);
3209
+ return this;
3210
+ }
3211
+ /**
3212
+ * Fetch all endpoints, skip cache (always request).
3213
+ */
3214
+ save(attrs, done) {
3215
+ serviceSend(this, attrs, done, FETCH_FLAGS_ALL, true);
3216
+ return this;
3217
+ }
3218
+ /**
3219
+ * Enqueue a task for sequential execution.
3220
+ */
3221
+ enqueue(callback) {
3222
+ if (!this.destroyed) {
3223
+ this.taskQueue.push(callback);
3224
+ this.dequeue(...this.prevArgs);
3225
+ }
3226
+ return this;
3227
+ }
3228
+ /**
3229
+ * Dequeue and execute the next task in queue.
3230
+ */
3231
+ dequeue(...args) {
3232
+ if (!this.busy && !this.destroyed) {
3233
+ this.busy = 1;
3234
+ setTimeout(() => {
3235
+ this.busy = 0;
3236
+ if (!this.destroyed) {
3237
+ const task2 = this.taskQueue.shift();
3238
+ if (task2) {
3239
+ this.prevArgs = args;
3240
+ funcWithTry(task2, args, this, noop);
2834
3241
  }
2835
- return cached;
2836
3242
  }
3243
+ }, 0);
3244
+ }
3245
+ }
3246
+ /**
3247
+ * Destroy the service instance.
3248
+ * After destruction, no new requests can be sent.
3249
+ */
3250
+ destroy() {
3251
+ this.destroyed = 1;
3252
+ this.taskQueue = [];
3253
+ }
3254
+ // Instance event methods (delegate to instance emitter)
3255
+ on(event, handler) {
3256
+ this._emitter.on(event, handler);
3257
+ return this;
3258
+ }
3259
+ off(event, handler) {
3260
+ this._emitter.off(event, handler);
3261
+ return this;
3262
+ }
3263
+ fire(event, data) {
3264
+ this._emitter.fire(event, data);
3265
+ return this;
3266
+ }
3267
+ // ============================================================
3268
+ // Per-type static state
3269
+ // ============================================================
3270
+ /** Per-type metadata registry */
3271
+ static _metaList = {};
3272
+ /** Per-type payload cache (LFU with frequency eviction) */
3273
+ static _payloadCache = new Cache({
3274
+ maxSize: 20,
3275
+ bufferSize: 5
3276
+ });
3277
+ /** Per-type pending cache keys for deduplication */
3278
+ static _pendingCacheKeys = {};
3279
+ /** Per-type sync function */
3280
+ static _syncFn = noop;
3281
+ /** Per-type static event emitter */
3282
+ static _staticEmitter = new EventEmitter();
3283
+ /** Per-type cache max size */
3284
+ static _cacheMax = 20;
3285
+ /** Per-type cache buffer size */
3286
+ static _cacheBuffer = 5;
3287
+ // ============================================================
3288
+ // Static methods (operate on per-type state via `this`)
3289
+ // ============================================================
3290
+ /**
3291
+ * Register API endpoint metadata.
3292
+ */
3293
+ static add(attrs) {
3294
+ if (!Array.isArray(attrs)) {
3295
+ attrs = [attrs];
3296
+ }
3297
+ for (const payload of attrs) {
3298
+ if (payload) {
3299
+ const name = payload.name;
3300
+ const cache = payload.cache;
3301
+ payload.cache = cache ? cache | 0 : 0;
3302
+ this._metaList[name] = payload;
2837
3303
  }
2838
- return void 0;
2839
- },
2840
- clear(names) {
2841
- const nameList = (typeof names === "string" ? names : names.join(",")).split(",");
2842
- const nameSet = {};
2843
- for (const n of nameList) {
2844
- nameSet[n] = 1;
2845
- }
2846
- const keysToDelete = [];
2847
- bagCache.forEach((bag) => {
2848
- if (bag?.cacheInfo && nameSet[bag.cacheInfo.name]) {
2849
- if (bag.cacheInfo.key) {
2850
- keysToDelete.push(bag.cacheInfo.key);
2851
- }
3304
+ }
3305
+ }
3306
+ /**
3307
+ * Get metadata for an API endpoint.
3308
+ */
3309
+ static meta(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;
3314
+ }
3315
+ /**
3316
+ * Create a Payload for an API request.
3317
+ */
3318
+ static create(attrs) {
3319
+ const meta = this.meta(attrs);
3320
+ const cache = toCacheValue(attrs["cache"]) || meta.cache || 0;
3321
+ const entity = new Payload();
3322
+ entity.set(meta);
3323
+ entity.cacheInfo = {
3324
+ name: meta.name,
3325
+ after: typeof meta.after === "function" ? meta.after : void 0,
3326
+ cleans: typeof meta.cleanKeys === "string" ? meta.cleanKeys : void 0,
3327
+ key: cache ? defaultCacheKey(meta, attrs) : "",
3328
+ time: 0
3329
+ };
3330
+ if (attrs !== null) {
3331
+ entity.set(attrs);
3332
+ }
3333
+ const before = meta.before;
3334
+ if (typeof before === "function") {
3335
+ funcWithTry(before, [entity], entity, noop);
3336
+ }
3337
+ this._staticEmitter.fire("begin", { payload: entity });
3338
+ return entity;
3339
+ }
3340
+ /**
3341
+ * Get or create a Payload for an API request.
3342
+ */
3343
+ static get(attrs, createNew) {
3344
+ let entity;
3345
+ let needsUpdate = false;
3346
+ if (!createNew) {
3347
+ entity = this.cached(attrs);
3348
+ }
3349
+ if (!entity) {
3350
+ entity = this.create(attrs);
3351
+ needsUpdate = true;
3352
+ }
3353
+ return { entity, needsUpdate };
3354
+ }
3355
+ /**
3356
+ * Get cached Payload if available and not expired.
3357
+ */
3358
+ static cached(attrs) {
3359
+ const meta = this.meta(attrs);
3360
+ const cache = toCacheValue(attrs["cache"]) || meta.cache || 0;
3361
+ let cacheKey = "";
3362
+ if (cache) {
3363
+ cacheKey = defaultCacheKey(meta, attrs);
3364
+ }
3365
+ if (cacheKey) {
3366
+ const info = this._pendingCacheKeys[cacheKey];
3367
+ if (info) {
3368
+ const entity = info.entity;
3369
+ return entity instanceof Payload ? entity : void 0;
3370
+ }
3371
+ const cached = this._payloadCache.get(cacheKey);
3372
+ if (cached && cached.cacheInfo) {
3373
+ if (now() - cached.cacheInfo.time > cache) {
3374
+ this._payloadCache.del(cacheKey);
3375
+ return void 0;
2852
3376
  }
2853
- });
2854
- for (const key of keysToDelete) {
2855
- bagCache.del(key);
3377
+ return cached;
2856
3378
  }
2857
- },
2858
- on(event, handler) {
2859
- staticEmitter2.on(event, handler);
2860
- },
2861
- off(event, handler) {
2862
- staticEmitter2.off(event, handler);
2863
- },
2864
- fire(event, data) {
2865
- staticEmitter2.fire(event, data);
2866
- },
2867
- extend(newSyncFn, newCacheMax, newCacheBuffer) {
2868
- return createServiceType(
2869
- newSyncFn,
2870
- newCacheMax || cacheMax,
2871
- newCacheBuffer || cacheBuffer
2872
- );
2873
3379
  }
2874
- };
2875
- const internals = {
2876
- metas,
2877
- bagCache,
2878
- pendingCacheKeys,
2879
- syncFn,
2880
- staticEmitter: staticEmitter2
2881
- };
2882
- function ServiceInstance() {
2883
- this.id = generateId("s");
2884
- this["$e"] = 0;
2885
- this["$d"] = 0;
2886
- this["$g"] = [];
2887
- this["$h"] = [];
2888
- this._emitter = new EventEmitter();
2889
- this._internals = internals;
2890
- this._type = serviceType;
2891
- }
2892
- ServiceInstance.prototype = {
2893
- all(attrs, done) {
2894
- serviceSend(this, attrs, done, FETCH_FLAGS_ALL, false);
2895
- return this;
2896
- },
2897
- one(attrs, done) {
2898
- serviceSend(this, attrs, done, FETCH_FLAGS_ONE, false);
2899
- return this;
2900
- },
2901
- save(attrs, done) {
2902
- serviceSend(this, attrs, done, FETCH_FLAGS_ALL, true);
2903
- return this;
2904
- },
2905
- enqueue(callback) {
2906
- if (!this["$d"]) {
2907
- this["$g"].push(callback);
2908
- this.dequeue(...this["$h"]);
2909
- }
2910
- return this;
2911
- },
2912
- dequeue(...args) {
2913
- if (!this["$e"] && !this["$d"]) {
2914
- this["$e"] = 1;
2915
- setTimeout(() => {
2916
- this["$e"] = 0;
2917
- if (!this["$d"]) {
2918
- const task2 = this["$g"].shift();
2919
- if (task2) {
2920
- this["$h"] = args;
2921
- funcWithTry(task2, args, this, noop);
2922
- }
2923
- }
2924
- }, 0);
3380
+ return void 0;
3381
+ }
3382
+ /**
3383
+ * Clear cached payloads by endpoint name.
3384
+ */
3385
+ static clear(names) {
3386
+ const nameSet = new Set(
3387
+ (typeof names === "string" ? names : names.join(",")).split(",")
3388
+ );
3389
+ const keysToDelete = [];
3390
+ this._payloadCache.forEach((payload) => {
3391
+ const info = payload?.cacheInfo;
3392
+ if (info && info.key && nameSet.has(info.name)) {
3393
+ keysToDelete.push(info.key);
2925
3394
  }
2926
- },
2927
- destroy() {
2928
- this["$d"] = 1;
2929
- this["$g"] = [];
2930
- },
2931
- on(event, handler) {
2932
- this._emitter.on(event, handler);
2933
- return this;
2934
- },
2935
- off(event, handler) {
2936
- this._emitter.off(event, handler);
2937
- return this;
2938
- },
2939
- fire(event, data) {
2940
- this._emitter.fire(event, data);
2941
- return this;
3395
+ });
3396
+ for (const key of keysToDelete) {
3397
+ this._payloadCache.del(key);
2942
3398
  }
2943
- };
2944
- const staticsMap = {
2945
- add: serviceType.add,
2946
- meta: serviceType.meta,
2947
- create: serviceType.create,
2948
- get: serviceType.get,
2949
- cached: serviceType.cached,
2950
- clear: serviceType.clear,
2951
- on: serviceType.on,
2952
- off: serviceType.off,
2953
- fire: serviceType.fire,
2954
- extend: serviceType.extend,
2955
- _internals: internals
2956
- };
2957
- const constructor = Object.assign(
2958
- ServiceInstance,
2959
- staticsMap
2960
- );
2961
- return constructor;
3399
+ }
3400
+ // Static event methods (operate on per-type emitter)
3401
+ static on(event, handler) {
3402
+ this._staticEmitter.on(event, handler);
3403
+ }
3404
+ static off(event, handler) {
3405
+ this._staticEmitter.off(event, handler);
3406
+ }
3407
+ static fire(event, data) {
3408
+ this._staticEmitter.fire(event, data);
3409
+ }
3410
+ /**
3411
+ * Create a new Service subclass with a custom sync function.
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.
3424
+ */
3425
+ static extend(newSyncFn, newCacheMax, newCacheBuffer) {
3426
+ const ParentService = this;
3427
+ class ChildService extends ParentService {
3428
+ // Intentionally per-subclass — see Service.extend doc.
3429
+ static _metaList = {};
3430
+ static _payloadCache = new Cache({
3431
+ maxSize: newCacheMax || ParentService._cacheMax,
3432
+ bufferSize: newCacheBuffer || ParentService._cacheBuffer
3433
+ });
3434
+ static _pendingCacheKeys = {};
3435
+ static _syncFn = newSyncFn;
3436
+ static _staticEmitter = new EventEmitter();
3437
+ static _cacheMax = newCacheMax || ParentService._cacheMax;
3438
+ static _cacheBuffer = newCacheBuffer || ParentService._cacheBuffer;
3439
+ }
3440
+ return ChildService;
3441
+ }
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;
2962
3451
  }
2963
- var Service = createServiceType(noop);
2964
3452
  function defaultCacheKey(meta, attrs) {
2965
- 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;
2966
3462
  }
2967
3463
  function serviceSend(service, attrs, done, flag, save) {
2968
- if (service["$d"]) return;
2969
- if (service["$e"]) {
2970
- service.enqueue(
2971
- serviceSend.bind(null, service, attrs, done, flag, save)
2972
- );
3464
+ if (service.destroyed) return;
3465
+ if (service.busy) {
3466
+ const queued = () => serviceSend(service, attrs, done, flag, save);
3467
+ service.enqueue(queued);
2973
3468
  return;
2974
3469
  }
2975
- service["$e"] = 1;
3470
+ service.busy = 1;
2976
3471
  let attrList;
2977
3472
  if (typeof attrs === "string") {
2978
3473
  attrList = [{ name: attrs }];
2979
- } else if (isArray(attrs)) {
3474
+ } else if (Array.isArray(attrs)) {
2980
3475
  attrList = attrs;
2981
3476
  } else {
2982
3477
  attrList = [attrs];
2983
3478
  }
2984
- const internals = service._internals;
3479
+ const internals = service.internals;
2985
3480
  const { syncFn, pendingCacheKeys, staticEmitter: staticEmitter2 } = internals;
2986
3481
  let requestCount = 0;
2987
3482
  const total = attrList.length;
2988
3483
  const doneArr = new Array(total + 1);
2989
3484
  const errorArgs = [];
2990
3485
  const remoteComplete = (idx, error) => {
2991
- const bag = doneArr[idx + 1];
2992
- let newBag = false;
3486
+ const payload = doneArr[idx + 1];
3487
+ let newPayload = false;
2993
3488
  if (error) {
2994
3489
  errorArgs[idx] = error;
2995
- staticEmitter2.fire("fail", { bag, error });
3490
+ staticEmitter2.fire("fail", { payload, error });
2996
3491
  } else {
2997
- newBag = true;
2998
- staticEmitter2.fire("done", { bag });
3492
+ newPayload = true;
3493
+ staticEmitter2.fire("done", { payload });
2999
3494
  }
3000
- if (!service["$d"]) {
3495
+ if (!service.destroyed) {
3001
3496
  const finish = requestCount === total;
3002
3497
  if (finish) {
3003
- service["$e"] = 0;
3498
+ service.busy = 0;
3004
3499
  if (flag === FETCH_FLAGS_ALL) {
3005
3500
  doneArr[0] = errorArgs;
3006
3501
  funcWithTry(done, doneArr, service, noop);
3007
3502
  }
3008
3503
  }
3009
3504
  if (flag === FETCH_FLAGS_ONE) {
3010
- funcWithTry(done, [error || null, bag, finish, idx], service, noop);
3505
+ funcWithTry(done, [error || null, payload, finish, idx], service, noop);
3011
3506
  }
3012
3507
  }
3013
- if (newBag) {
3014
- staticEmitter2.fire("end", { bag, error });
3508
+ if (newPayload) {
3509
+ staticEmitter2.fire("end", { payload, error });
3015
3510
  }
3016
3511
  };
3017
3512
  for (const attr of attrList) {
3018
3513
  if (!attr) continue;
3019
3514
  const attrObj = typeof attr === "string" ? { name: attr } : attr;
3020
- const bagInfo = service._type.get(attrObj, save);
3021
- const bagEntity = bagInfo.entity;
3022
- const cacheKey = bagEntity.cacheInfo?.key || "";
3515
+ const payloadInfo = service.type.get(attrObj, save);
3516
+ const payloadEntity = payloadInfo.entity;
3517
+ const cacheKey = payloadEntity.cacheInfo?.key || "";
3023
3518
  const complete = remoteComplete.bind(null, requestCount++);
3024
3519
  if (cacheKey && pendingCacheKeys[cacheKey]) {
3025
3520
  pendingCacheKeys[cacheKey].push(complete);
3026
- } else if (bagInfo.needsUpdate) {
3521
+ } else if (payloadInfo.needsUpdate) {
3027
3522
  if (cacheKey) {
3028
3523
  const cacheList = [complete];
3029
- cacheList.e = bagEntity;
3524
+ cacheList.entity = payloadEntity;
3030
3525
  pendingCacheKeys[cacheKey] = cacheList;
3031
3526
  const cacheComplete = () => {
3032
3527
  const list = pendingCacheKeys[cacheKey];
3033
- const entity = list.e;
3034
- if (entity.cacheInfo) {
3528
+ const entity = list.entity;
3529
+ if (entity instanceof Payload && entity.cacheInfo) {
3035
3530
  entity.cacheInfo.time = now();
3531
+ internals.payloadCache.set(cacheKey, entity);
3036
3532
  }
3037
- internals.bagCache.set(cacheKey, entity);
3038
3533
  Reflect.deleteProperty(pendingCacheKeys, cacheKey);
3039
3534
  for (const cb of list) {
3040
- if (typeof cb === "function") {
3041
- cb();
3042
- }
3535
+ if (typeof cb === "function") cb();
3043
3536
  }
3044
3537
  };
3045
- syncFn(bagEntity, cacheComplete);
3538
+ syncFn(payloadEntity, cacheComplete);
3046
3539
  } else {
3047
- syncFn(bagEntity, complete);
3540
+ syncFn(payloadEntity, complete);
3048
3541
  }
3049
3542
  } else {
3050
3543
  complete();
@@ -3052,12 +3545,14 @@ function serviceSend(service, attrs, done, flag, save) {
3052
3545
  }
3053
3546
  }
3054
3547
 
3055
- // src/frame-visualizer/index.ts
3056
- var MSG_PING = "LARK_VIS_PING";
3057
- var MSG_PONG = "LARK_VIS_PONG";
3058
- var MSG_REQUEST_TREE = "LARK_VIS_REQUEST_TREE";
3059
- var MSG_TREE = "LARK_VIS_TREE";
3060
- var MSG_TREE_DELTA = "LARK_VIS_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
+ };
3061
3556
  function serializeView(view) {
3062
3557
  return {
3063
3558
  id: view.id,
@@ -3097,7 +3592,10 @@ function serializeFrame(frameId) {
3097
3592
  };
3098
3593
  }
3099
3594
  function serializeFrameTree() {
3100
- const root = Frame.root();
3595
+ const root = Frame.getRoot();
3596
+ if (!root) {
3597
+ return { root: null, totalFrames: 0, timestamp: Date.now(), rootId: "" };
3598
+ }
3101
3599
  const rootNode = serializeFrame(root.id);
3102
3600
  let totalFrames = 0;
3103
3601
  const countFrames = (node) => {
@@ -3125,19 +3623,22 @@ function installFrameVisualizerBridge() {
3125
3623
  const data = event.data;
3126
3624
  if (!data || typeof data !== "object") return;
3127
3625
  const type = data.type;
3128
- if (type === MSG_PING) {
3626
+ if (type === FrameVisualBridge.MSG_PING) {
3129
3627
  const source = event.source;
3130
3628
  if (source) {
3131
- source.postMessage({ type: MSG_PONG }, { targetOrigin: "*" });
3629
+ source.postMessage(
3630
+ { type: FrameVisualBridge.MSG_PONG },
3631
+ { targetOrigin: "*" }
3632
+ );
3132
3633
  }
3133
3634
  return;
3134
3635
  }
3135
- if (type === MSG_REQUEST_TREE) {
3636
+ if (type === FrameVisualBridge.MSG_REQUEST_TREE) {
3136
3637
  const tree = serializeFrameTree();
3137
3638
  const source = event.source;
3138
3639
  if (source) {
3139
3640
  source.postMessage(
3140
- { type: MSG_TREE, data: tree },
3641
+ { type: FrameVisualBridge.MSG_TREE, data: tree },
3141
3642
  { targetOrigin: "*" }
3142
3643
  );
3143
3644
  }
@@ -3156,18 +3657,14 @@ function pushTreeUpdate() {
3156
3657
  const treeJson = JSON.stringify(tree);
3157
3658
  if (treeJson !== lastTreeJson) {
3158
3659
  lastTreeJson = treeJson;
3159
- window.parent.postMessage({ type: MSG_TREE_DELTA, data: tree }, "*");
3660
+ window.parent.postMessage(
3661
+ { type: FrameVisualBridge.MSG_TREE_DELTA, data: tree },
3662
+ "*"
3663
+ );
3160
3664
  }
3161
3665
  }
3162
3666
 
3163
3667
  // src/framework.ts
3164
- var config = {
3165
- rootId: "lark-root",
3166
- hashbang: "#!",
3167
- error: (error) => {
3168
- throw error;
3169
- }
3170
- };
3171
3668
  var booted3 = false;
3172
3669
  var taskList = [];
3173
3670
  var taskIndex = 0;
@@ -3216,6 +3713,9 @@ function task(fn, args, context) {
3216
3713
  }
3217
3714
  }
3218
3715
  var dispatcherUpdateTag = 0;
3716
+ function isThenable(value) {
3717
+ return !!value && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
3718
+ }
3219
3719
  function viewIsObserveChanged(view) {
3220
3720
  const loc = view.locationObserved;
3221
3721
  let result = false;
@@ -3229,7 +3729,7 @@ function viewIsObserveChanged(view) {
3229
3729
  const changedParams = lastChanged2?.params;
3230
3730
  if (changedParams) {
3231
3731
  for (const key of loc.keys) {
3232
- result = has(changedParams, key);
3732
+ result = hasOwnProperty(changedParams, key);
3233
3733
  if (result) break;
3234
3734
  }
3235
3735
  }
@@ -3241,49 +3741,65 @@ function stateIsObserveChanged(view, stateKeys) {
3241
3741
  const observedKeys = view.observedStateKeys;
3242
3742
  if (!observedKeys) return false;
3243
3743
  for (const key of observedKeys) {
3244
- if (has(stateKeys, key)) return true;
3744
+ if (stateKeys.has(key)) return true;
3245
3745
  }
3246
3746
  return false;
3247
3747
  }
3248
3748
  function dispatcherUpdate(frame, stateKeys) {
3249
- const frameInternal = frame;
3250
- const view = frame.view;
3251
- if (!view || frameInternal.dispatcherUpdateTag === dispatcherUpdateTag || view.signature <= 1) {
3252
- return;
3253
- }
3254
- frameInternal.dispatcherUpdateTag = dispatcherUpdateTag;
3255
- const isChanged = stateKeys ? stateIsObserveChanged(view, stateKeys) : viewIsObserveChanged(view);
3256
- let renderPromise;
3257
- if (isChanged) {
3258
- const renderResult = funcWithTry(view.$b ?? view.render, [], view, noop);
3259
- if (renderResult && typeof renderResult.then === "function") {
3260
- renderPromise = renderResult;
3261
- }
3262
- }
3263
- const children = frame.children();
3264
- const recurse = () => {
3265
- for (const childId of children) {
3266
- const childFrame = Frame.get(childId);
3267
- if (childFrame) {
3268
- 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
+ }
3269
3787
  }
3270
3788
  }
3271
3789
  };
3272
- if (renderPromise) {
3273
- renderPromise.then(recurse);
3274
- } else {
3275
- recurse();
3276
- }
3790
+ drain(stack);
3277
3791
  }
3278
3792
  function dispatcherNotifyChange(e) {
3279
- const rootFrame2 = Frame.root();
3280
- const view = e["view"];
3793
+ const rootFrame2 = Frame.getRoot();
3794
+ if (!rootFrame2) return;
3795
+ const routeEvent = e;
3796
+ const view = routeEvent.view;
3281
3797
  if (view) {
3282
3798
  const viewPath = typeof view === "object" && view !== null ? String(view.to || "") : String(view);
3283
3799
  rootFrame2.mountView(viewPath);
3284
3800
  } else {
3285
3801
  dispatcherUpdateTag++;
3286
- dispatcherUpdate(rootFrame2, e["keys"]);
3802
+ dispatcherUpdate(rootFrame2, e.keys);
3287
3803
  }
3288
3804
  }
3289
3805
  function dispatchEvent(target, eventType, eventInit) {
@@ -3294,23 +3810,8 @@ function dispatchEvent(target, eventType, eventInit) {
3294
3810
  });
3295
3811
  target.dispatchEvent(event);
3296
3812
  }
3297
- var Base = class extends EventEmitter {
3298
- };
3299
- function use(names, callback) {
3300
- if (!config.require) {
3301
- if (callback) callback();
3302
- return;
3303
- }
3304
- const nameList = typeof names === "string" ? [names] : names;
3305
- const result = config.require(nameList);
3306
- if (result && typeof result.then === "function") {
3307
- result.then((modules) => {
3308
- if (callback) callback(modules);
3309
- });
3310
- }
3311
- }
3312
3813
  var WAIT_OK = 1;
3313
- var WAIT_TIMEOUT_OR_UNFOUND = 0;
3814
+ var WAIT_TIMEOUT_OR_NOT_FOUND = 0;
3314
3815
  function waitZoneViewsRendered(viewId, timeout) {
3315
3816
  if (timeout == null) {
3316
3817
  timeout = 30 * 1e3;
@@ -3321,7 +3822,7 @@ function waitZoneViewsRendered(viewId, timeout) {
3321
3822
  const check = () => {
3322
3823
  const currentTime = now();
3323
3824
  if (currentTime > endTime || !checkFrame) {
3324
- resolve(WAIT_TIMEOUT_OR_UNFOUND);
3825
+ resolve(WAIT_TIMEOUT_OR_NOT_FOUND);
3325
3826
  } else if (checkFrame.childrenCount === checkFrame.readyCount) {
3326
3827
  resolve(WAIT_OK);
3327
3828
  } else {
@@ -3331,12 +3832,27 @@ function waitZoneViewsRendered(viewId, timeout) {
3331
3832
  setTimeout(check, 9);
3332
3833
  });
3333
3834
  }
3835
+ function getConfigImpl(key) {
3836
+ if (key === void 0) return config;
3837
+ return config[key];
3838
+ }
3334
3839
  var Framework = {
3335
3840
  // ============================================================
3336
3841
  // Lifecycle
3337
3842
  // ============================================================
3843
+ /** Read framework configuration. See `FrameworkInterface.getConfig`. */
3844
+ getConfig: getConfigImpl,
3845
+ /**
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
+ },
3338
3854
  /**
3339
- * Get or set framework configuration.
3855
+ * @deprecated Use `getConfig()` / `setConfig()`. Behavior unchanged.
3340
3856
  */
3341
3857
  config(cfg) {
3342
3858
  if (!cfg) {
@@ -3357,17 +3873,17 @@ var Framework = {
3357
3873
  }
3358
3874
  Router._setConfig(config);
3359
3875
  EventDelegator.setFrameGetter((id) => Frame.get(id));
3360
- Router.on(ROUTER_EVENTS.CHANGED, (data) => {
3361
- dispatcherNotifyChange(data);
3876
+ Router.on(RouterEvents.CHANGED, (data) => {
3877
+ if (data) dispatcherNotifyChange(data);
3362
3878
  });
3363
- State.on(ROUTER_EVENTS.CHANGED, (data) => {
3364
- dispatcherNotifyChange(data);
3879
+ State.on(RouterEvents.CHANGED, (data) => {
3880
+ if (data) dispatcherNotifyChange(data);
3365
3881
  });
3366
3882
  booted3 = true;
3367
3883
  markBooted();
3368
3884
  markRouterBooted();
3369
3885
  installFrameVisualizerBridge();
3370
- const rootFrame2 = Frame.root(config.rootId);
3886
+ const rootFrame2 = Frame.createRoot(config.rootId);
3371
3887
  Router._bind();
3372
3888
  const defaultView = config.defaultView || "";
3373
3889
  if (defaultView && !rootFrame2.view) {
@@ -3398,7 +3914,7 @@ var Framework = {
3398
3914
  /** Wait for zone views to be rendered */
3399
3915
  waitZoneViewsRendered,
3400
3916
  WAIT_OK,
3401
- WAIT_TIMEOUT_OR_UNFOUND,
3917
+ WAIT_TIMEOUT_OR_NOT_FOUND,
3402
3918
  /**
3403
3919
  * Convert array to hash map.
3404
3920
  */
@@ -3422,7 +3938,7 @@ var Framework = {
3422
3938
  /**
3423
3939
  * Check if object has own property.
3424
3940
  */
3425
- has,
3941
+ has: hasOwnProperty,
3426
3942
  /**
3427
3943
  * Get object keys.
3428
3944
  */
@@ -3463,7 +3979,7 @@ var Framework = {
3463
3979
  /**
3464
3980
  * Base class with EventEmitter.
3465
3981
  */
3466
- Base,
3982
+ Base: EventEmitter,
3467
3983
  // ============================================================
3468
3984
  // Module access
3469
3985
  // ============================================================
@@ -3481,10 +3997,14 @@ if (typeof window !== "undefined") {
3481
3997
  window.__lark_State = State;
3482
3998
  window.__lark_Router = Router;
3483
3999
  window.__lark_Frame = Frame;
4000
+ window.__lark_View = View;
4001
+ window.__lark_invalidateViewClass = invalidateViewClass;
4002
+ window.__lark_getViewClassRegistry = getViewClassRegistry;
4003
+ window.__lark_registerViewClass = registerViewClass;
3484
4004
  }
3485
4005
 
3486
4006
  // src/store.ts
3487
- var LARK_GLOBAL = "lark_global";
4007
+ var LARK_GLOBAL = "lark-global";
3488
4008
  var Platform = /* @__PURE__ */ ((Platform2) => {
3489
4009
  Platform2["Lark"] = "lark";
3490
4010
  Platform2["React"] = "react";
@@ -3494,28 +4014,38 @@ var Platform = /* @__PURE__ */ ((Platform2) => {
3494
4014
  var isFunction = (val) => typeof val === "function";
3495
4015
  var isObject = (val) => val !== null && typeof val === "object";
3496
4016
  var isPromise = (val) => isObject(val) && isFunction(val["then"]);
3497
- var hasOwnProperty = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
3498
- 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) => {
3499
4020
  if (!obj || !isObject(obj)) return {};
3500
- const newData = Array.isArray(obj) ? [] : Object.create(Object.getPrototypeOf(obj));
4021
+ const newData = Array.isArray(obj) ? [] : {};
3501
4022
  for (const key in obj) {
3502
- if (hasOwnProperty(obj, key)) {
4023
+ if (hasOwnProperty2(obj, key)) {
3503
4024
  const value = obj[key];
3504
- newData[key] = isObject(value) ? deepClone(value) : value;
4025
+ newData[key] = isObject(value) ? deepCloneFallback(value) : value;
3505
4026
  }
3506
4027
  }
3507
4028
  return newData;
3508
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
+ };
3509
4040
  var cloneData = (data) => isObject(data) ? deepClone(data) : data;
3510
4041
  var getDataByKey = (target, key) => {
3511
- let data;
3512
- const rec = target;
3513
- if (key.includes(".")) {
3514
- key.split(".").forEach((k, index) => {
3515
- data = index === 0 ? rec?.[k] : data?.[k];
3516
- });
3517
- } else {
3518
- 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];
3519
4049
  }
3520
4050
  return data;
3521
4051
  };
@@ -3527,8 +4057,10 @@ var Queue = class {
3527
4057
  const flushTickTask = () => {
3528
4058
  while (queue.length > 0) {
3529
4059
  const task2 = queue.shift();
3530
- pendingTasks.delete(task2);
3531
- runTask(task2);
4060
+ if (task2) {
4061
+ pendingTasks.delete(task2);
4062
+ runTask(task2);
4063
+ }
3532
4064
  }
3533
4065
  };
3534
4066
  Promise.resolve().then(flushTickTask);
@@ -3561,17 +4093,18 @@ var Queue = class {
3561
4093
  };
3562
4094
  var addParams2Callback = (cb, params) => {
3563
4095
  if (!cb || !params) return;
3564
- const cbObj = cb;
3565
- if (isObject(cb) && isObject(cbObj["params"])) {
3566
- Object.assign(cbObj["params"], params);
4096
+ const tagged = cb;
4097
+ const existing = tagged.params;
4098
+ if (isObject(existing)) {
4099
+ Object.assign(existing, params);
3567
4100
  } else {
3568
- cbObj["params"] = params;
4101
+ tagged.params = params;
3569
4102
  }
3570
4103
  };
3571
4104
  var runTask = (cb) => {
3572
- const cbObj = cb;
3573
- const params = cbObj["params"];
3574
- delete cbObj["params"];
4105
+ const tagged = cb;
4106
+ const params = tagged.params;
4107
+ delete tagged.params;
3575
4108
  try {
3576
4109
  cb(params);
3577
4110
  } catch {
@@ -3597,7 +4130,9 @@ var ArrMethods = {};
3597
4130
  if (res === -1 || res === false) {
3598
4131
  res = rawMethod.apply(
3599
4132
  this,
3600
- args.map((item) => ProxyCache.get(item) ?? item)
4133
+ args.map(
4134
+ (item) => isObject(item) ? ProxyCache.get(item) ?? item : item
4135
+ )
3601
4136
  );
3602
4137
  }
3603
4138
  return res;
@@ -3615,6 +4150,7 @@ var inArrUpdate = false;
3615
4150
  });
3616
4151
  function needKeepArrItem(target, newVal, key) {
3617
4152
  if (!Array.isArray(target) || !inArrUpdate) return false;
4153
+ if (!isObject(newVal)) return false;
3618
4154
  return getLinkKeys(newVal) === createLinkKeys(target, key);
3619
4155
  }
3620
4156
  var defStateConfig = {
@@ -3626,11 +4162,13 @@ var StateConfigMap = /* @__PURE__ */ new WeakMap();
3626
4162
  var setStateConfig = (target, config2) => {
3627
4163
  if (target && isObject(config2)) StateConfigMap.set(target, config2);
3628
4164
  };
3629
- var getStateConfig = (target, key) => {
3630
- if (!StateConfigMap.has(target)) return void 0;
4165
+ function getStateConfig(target, key) {
4166
+ if (!StateConfigMap.has(target)) {
4167
+ return void 0;
4168
+ }
3631
4169
  const config2 = StateConfigMap.get(target);
3632
- return key ? config2[key] : config2;
3633
- };
4170
+ return key ? config2?.[key] : config2;
4171
+ }
3634
4172
  var isState = (target) => isObject(target) && StateConfigMap.has(target);
3635
4173
  var createLinkKeys = (target, property) => {
3636
4174
  if (!hasLinkKeys(target)) return property;
@@ -3668,7 +4206,7 @@ var needKeep = (key) => {
3668
4206
  };
3669
4207
  var canSetNewVal = (params) => {
3670
4208
  const { property, newVal, oldVal } = params;
3671
- if (hasOwnProperty(params.target, property) && newVal === oldVal)
4209
+ if (hasOwnProperty2(params.target, property) && newVal === oldVal)
3672
4210
  return false;
3673
4211
  return true;
3674
4212
  };
@@ -3680,16 +4218,13 @@ var getNewVal = (params) => {
3680
4218
  if (config2?.shallow) return newVal;
3681
4219
  if (needKeepVal) return newVal;
3682
4220
  const linkKeys = createLinkKeys(target, property);
3683
- const newState = createState(newVal, {
3684
- ...config2,
3685
- linkKeys
3686
- });
4221
+ const newState = createState(newVal, { ...config2, linkKeys });
3687
4222
  if (isPromise(newVal)) handlePromise(newVal, target, property);
3688
4223
  return newState;
3689
4224
  };
3690
4225
  var genPayload = (params) => {
3691
4226
  const { target, property, newVal, oldVal } = params;
3692
- const config2 = getStateConfig(target) || defStateConfig;
4227
+ const config2 = getStateConfig(target) ?? defStateConfig;
3693
4228
  return {
3694
4229
  belong: config2.belong || LARK_GLOBAL,
3695
4230
  target,
@@ -3699,11 +4234,12 @@ var genPayload = (params) => {
3699
4234
  };
3700
4235
  };
3701
4236
  var handlePromise = (child, parent, key) => {
4237
+ const childObj = child;
3702
4238
  child.then((res) => {
3703
4239
  const parentProxy = ProxyCache.get(parent);
3704
4240
  if (parentProxy) {
3705
4241
  const oldVal = parentProxy[key];
3706
- if (ProxyCache.get(child) === oldVal) {
4242
+ if (ProxyCache.get(childObj) === oldVal) {
3707
4243
  parentProxy[key] = res;
3708
4244
  }
3709
4245
  }
@@ -3711,7 +4247,7 @@ var handlePromise = (child, parent, key) => {
3711
4247
  const parentProxy = ProxyCache.get(parent);
3712
4248
  if (parentProxy) {
3713
4249
  const oldVal = parentProxy[key];
3714
- if (ProxyCache.get(child) === oldVal) {
4250
+ if (ProxyCache.get(childObj) === oldVal) {
3715
4251
  parentProxy[key] = err;
3716
4252
  }
3717
4253
  }
@@ -3723,7 +4259,7 @@ var mark2 = (host, key) => {
3723
4259
  let sign;
3724
4260
  if (!host[_deleteKey]) {
3725
4261
  const markHost = host[_markObjKey] || (host[_markObjKey] = {});
3726
- if (!hasOwnProperty(markHost, key)) {
4262
+ if (!hasOwnProperty2(markHost, key)) {
3727
4263
  markHost[key] = 0;
3728
4264
  }
3729
4265
  sign = ++markHost[key];
@@ -3760,7 +4296,7 @@ function createState(initialData, config2) {
3760
4296
  oldVal
3761
4297
  });
3762
4298
  if (!canSet) return true;
3763
- const proxyTarget = receiver || state;
4299
+ const proxyTarget = isObject(receiver) && receiver !== null ? receiver : state;
3764
4300
  newVal = getNewVal({
3765
4301
  target: proxyTarget,
3766
4302
  property: strProp,
@@ -3780,7 +4316,7 @@ function createState(initialData, config2) {
3780
4316
  },
3781
4317
  deleteProperty(target, property) {
3782
4318
  const strProp = property;
3783
- if (!hasOwnProperty(target, strProp)) return true;
4319
+ if (!hasOwnProperty2(target, strProp)) return true;
3784
4320
  const oldVal = Reflect.get(target, property);
3785
4321
  Reflect.deleteProperty(target, property);
3786
4322
  const payload = genPayload({
@@ -3824,11 +4360,8 @@ function shallowSet(target, key, data) {
3824
4360
  keep(key);
3825
4361
  const config2 = getStateConfig(target);
3826
4362
  const linkKeys = createLinkKeys(target, key);
3827
- target[key] = createState(data, {
3828
- ...config2,
3829
- linkKeys,
3830
- shallow: true
3831
- });
4363
+ target[key] = createState(data, { ...config2, linkKeys, shallow: true });
4364
+ return target[key];
3832
4365
  }
3833
4366
  var GlobalDeps = /* @__PURE__ */ new Map();
3834
4367
  function track(payload) {
@@ -3909,8 +4442,22 @@ var _stateKeys = /* @__PURE__ */ Symbol("state-keys");
3909
4442
  var _storeState = /* @__PURE__ */ Symbol("store-state");
3910
4443
  var _storeDefScheduler = /* @__PURE__ */ Symbol("store-def-scheduler");
3911
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
+ }
3912
4458
  var BaseStore = class {
3913
4459
  [_storeStatus] = 0 /* BEFORE_CREATE */;
4460
+ [_computedKeys] = /* @__PURE__ */ new Set();
3914
4461
  [_storeDefScheduler] = getDefScheduler;
3915
4462
  [_storeBoot]() {
3916
4463
  this[_storeStatus] = 2 /* ACTIVE */;
@@ -3933,10 +4480,16 @@ var BaseStore = class {
3933
4480
  if (isObject(body)) {
3934
4481
  const state = {};
3935
4482
  const handlers = {};
4483
+ const computedDefs = {};
4484
+ const computedKeys = this[_computedKeys];
3936
4485
  Reflect.ownKeys(body).forEach((key) => {
3937
4486
  const strKey = key;
3938
4487
  const val = body[strKey];
3939
- 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)) {
3940
4493
  if (!excludeFns.includes(strKey)) handlers[strKey] = val;
3941
4494
  } else {
3942
4495
  state[strKey] = val;
@@ -3946,6 +4499,22 @@ var BaseStore = class {
3946
4499
  this[_originState] = cloneData(state);
3947
4500
  this[_stateKeys] = Object.keys(state);
3948
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
+ }
3949
4518
  }
3950
4519
  }
3951
4520
  constructor(name, config2) {
@@ -3967,18 +4536,21 @@ var BaseStore = class {
3967
4536
  }
3968
4537
  [_storeProxy](toOut = false) {
3969
4538
  const self = this;
3970
- return new Proxy(self, {
4539
+ const proxy = new Proxy(self, {
3971
4540
  get(target, property) {
3972
- if (self[_stateKeys].includes(property)) {
3973
- const val = self[_storeState][property];
4541
+ const strProp = property;
4542
+ if (self[_stateKeys].includes(strProp)) {
4543
+ const val = self[_storeState][strProp];
3974
4544
  return toOut ? cloneData(val) : val;
3975
4545
  }
3976
4546
  return Reflect.get(target, property);
3977
4547
  },
3978
4548
  set(_target, property, val) {
3979
4549
  if (toOut) return true;
3980
- if (self[_stateKeys].includes(property)) {
3981
- 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;
3982
4554
  }
3983
4555
  return true;
3984
4556
  },
@@ -3986,11 +4558,14 @@ var BaseStore = class {
3986
4558
  return Reflect.has(target, property) || self[_stateKeys].includes(property);
3987
4559
  }
3988
4560
  });
4561
+ return proxy;
3989
4562
  }
3990
4563
  };
3991
4564
  var LarkUtils = {
3992
4565
  isLarkView(instance) {
3993
- 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"]);
3994
4569
  },
3995
4570
  getRender(view) {
3996
4571
  return view.updater.digest.bind(view.updater);
@@ -4006,41 +4581,45 @@ var getLarkAdapter = (storeName) => ({
4006
4581
  Store: LarkStore,
4007
4582
  useStore: ((view) => {
4008
4583
  const store = getStore(storeName);
4584
+ if (!(store instanceof LarkStore)) return {};
4009
4585
  return store[_storeBoot](view);
4010
4586
  })
4011
4587
  });
4012
- var innerObserveFlags = /* @__PURE__ */ Symbol("store-inner-observe-flags");
4013
- var boundViews = /* @__PURE__ */ Symbol("bound-views");
4588
+ var _innerObserveFlags = /* @__PURE__ */ Symbol("store-inner-observe-flags");
4589
+ var _boundViews = /* @__PURE__ */ Symbol("store-bound-views");
4014
4590
  var LarkStore = class extends BaseStore {
4015
- [boundViews] = /* @__PURE__ */ new Set();
4016
- [innerObserveFlags] = /* @__PURE__ */ new Set();
4591
+ [_boundViews] = /* @__PURE__ */ new Set();
4592
+ [_innerObserveFlags] = /* @__PURE__ */ new Set();
4017
4593
  [_storeBoot](view) {
4018
- if (view && LarkUtils.isLarkView(view) && !this[boundViews].has(view)) {
4019
- this[boundViews].add(view);
4594
+ if (view && LarkUtils.isLarkView(view) && !this[_boundViews].has(view)) {
4595
+ this[_boundViews].add(view);
4020
4596
  LarkUtils.onDestroy(view, () => {
4021
- this[boundViews].delete(view);
4597
+ this[_boundViews].delete(view);
4022
4598
  });
4023
4599
  }
4024
4600
  return super[_storeBoot]();
4025
4601
  }
4026
4602
  [_storeDestroy]() {
4027
- this[boundViews].clear();
4028
- this[innerObserveFlags].clear();
4603
+ this[_boundViews].clear();
4604
+ this[_innerObserveFlags].clear();
4029
4605
  super[_storeDestroy]();
4030
4606
  }
4031
4607
  observe(view, keys2, defCallback) {
4032
4608
  if (this[_storeStatus] !== 2 /* ACTIVE */) return noop;
4033
- let observeKeys = keys2;
4609
+ let observeKeys = Array.isArray(keys2) ? keys2 : [];
4034
4610
  const _view = view;
4035
4611
  const renderFn = _view ? LarkUtils.getRender(_view) : noop;
4036
4612
  const dateSetterFn = _view ? LarkUtils.getDataSetter(_view) : noop;
4037
4613
  const isInnerObserve = !view;
4038
4614
  const innerFlags = /* @__PURE__ */ new Set();
4039
- const storeInnerObserveFlags = this[innerObserveFlags];
4615
+ const storeInnerObserveFlags = this[_innerObserveFlags];
4040
4616
  if (isFunction(keys2)) {
4041
4617
  const res = keys2();
4042
4618
  if (Array.isArray(res)) observeKeys = res;
4043
4619
  }
4620
+ if (keys2 === void 0 && _view && observeKeys.length === 0 && Array.isArray(this[_stateKeys])) {
4621
+ observeKeys = this[_stateKeys].slice();
4622
+ }
4044
4623
  const defSetter = (immediate, key, alias, transform) => () => {
4045
4624
  const stateVal = getDataByKey(this, key);
4046
4625
  let data = { [alias || key]: stateVal };
@@ -4095,11 +4674,12 @@ var getReactAdapter = (storeName) => ({
4095
4674
  Store: ReactStore,
4096
4675
  useStore: (() => {
4097
4676
  const store = getStore(storeName);
4677
+ if (!(store instanceof ReactStore)) return {};
4098
4678
  return store[_storeBoot]();
4099
4679
  })
4100
4680
  });
4101
- var observeSym = /* @__PURE__ */ Symbol("observe");
4102
- var getLastState = /* @__PURE__ */ Symbol("get-last-state");
4681
+ var _observe = /* @__PURE__ */ Symbol("store-observe");
4682
+ var _getLastState = /* @__PURE__ */ Symbol("store-get-last-state");
4103
4683
  var ReactStore = class extends BaseStore {
4104
4684
  stateChangeCount = 0;
4105
4685
  lastCount = 0;
@@ -4113,7 +4693,7 @@ var ReactStore = class extends BaseStore {
4113
4693
  }
4114
4694
  [_storeCreate](body) {
4115
4695
  super[_storeCreate](body);
4116
- this[observeSym](() => {
4696
+ this[_observe](() => {
4117
4697
  this.stateChangeCount += 1;
4118
4698
  });
4119
4699
  }
@@ -4123,7 +4703,7 @@ var ReactStore = class extends BaseStore {
4123
4703
  this.lastCount = currCount;
4124
4704
  return changed;
4125
4705
  }
4126
- [getLastState](handlers) {
4706
+ [_getLastState](handlers) {
4127
4707
  if (this.isStateChanged() || !this.lastState) {
4128
4708
  const state = this[_storeState];
4129
4709
  const immutableState = freezeData(state);
@@ -4131,7 +4711,7 @@ var ReactStore = class extends BaseStore {
4131
4711
  }
4132
4712
  return this.lastState;
4133
4713
  }
4134
- [observeSym](cb) {
4714
+ [_observe](cb) {
4135
4715
  const tasks = this[_stateKeys].map((key) => ({ key, cb }));
4136
4716
  track({ belong: this[_storeName], trackList: tasks });
4137
4717
  return () => clear({ belong: this[_storeName], clearList: tasks });
@@ -4158,6 +4738,7 @@ var getNodeAdapter = (storeName) => ({
4158
4738
  Store: NodeStore,
4159
4739
  useStore: (() => {
4160
4740
  const store = getStore(storeName);
4741
+ if (!(store instanceof NodeStore)) return {};
4161
4742
  return store[_storeBoot]();
4162
4743
  })
4163
4744
  });
@@ -4184,7 +4765,7 @@ var getPlatform = (comp) => {
4184
4765
  if (LarkUtils.isLarkView(comp)) return "lark" /* Lark */;
4185
4766
  return void 0;
4186
4767
  };
4187
- var extendApis = { lazySet, shallowSet };
4768
+ var extendApis = { lazySet, shallowSet, computed };
4188
4769
  var StoreCache = /* @__PURE__ */ new Map();
4189
4770
  function defineStore(name, creator, config2) {
4190
4771
  if (StoreCache.has(name)) {
@@ -4195,18 +4776,20 @@ function defineStore(name, creator, config2) {
4195
4776
  const StoreClass = adapter.Store;
4196
4777
  const useStore = adapter.useStore;
4197
4778
  const store = new StoreClass(name, config2);
4198
- store[_storeCreate](
4199
- creator(
4200
- store[_innerStore](),
4201
- extendApis
4202
- )
4203
- );
4779
+ const innerProxy = store[_innerStore]();
4780
+ const body = creator(innerProxy, extendApis);
4781
+ store[_storeCreate](body);
4204
4782
  Object.defineProperties(useStore, {
4205
4783
  $storeName: { value: name, configurable: true },
4206
- $del: { value: () => store[_storeDestroy](), configurable: true }
4784
+ $destroyFn: { value: () => store[_storeDestroy](), configurable: true }
4207
4785
  });
4208
4786
  if (!StoreCache.has(name)) {
4209
- StoreCache.set(name, { store, creator, config: config2, useStore });
4787
+ StoreCache.set(name, {
4788
+ store,
4789
+ creator,
4790
+ config: config2,
4791
+ useStore
4792
+ });
4210
4793
  }
4211
4794
  return useStore;
4212
4795
  }
@@ -4228,7 +4811,7 @@ function getUseStore(name) {
4228
4811
  return void 0;
4229
4812
  }
4230
4813
  function cloneStore(name, useStore, config2) {
4231
- const oldStoreName = useStore["$storeName"];
4814
+ const oldStoreName = useStore.$storeName ?? "";
4232
4815
  const cached = StoreCache.get(oldStoreName);
4233
4816
  const oldStoreCreator = cached?.creator;
4234
4817
  const oldConfig = cached?.config || {};
@@ -4249,16 +4832,12 @@ function isStoreActive(name) {
4249
4832
  }
4250
4833
  var cellCount = 0;
4251
4834
  function cell(data) {
4252
- return createState(data, {
4253
- belong: LARK_GLOBAL,
4254
- linkKeys: `${LARK_GLOBAL}_${cellCount++}`
4255
- });
4835
+ const linkKeys = `${LARK_GLOBAL}_${cellCount++}`;
4836
+ return createState(data, { belong: LARK_GLOBAL, linkKeys });
4256
4837
  }
4257
4838
  function observeCell(state, cb, immediate = true) {
4258
4839
  const linkKeys = getLinkKeys(state);
4259
- if (!linkKeys)
4260
- return () => {
4261
- };
4840
+ if (!linkKeys) return noop;
4262
4841
  const keys2 = linkKeys.split(".");
4263
4842
  const key = keys2[keys2.length - 1];
4264
4843
  const list = [{ key, cb }];
@@ -4267,49 +4846,50 @@ function observeCell(state, cb, immediate = true) {
4267
4846
  return () => clear({ belong: LARK_GLOBAL, clearList: list });
4268
4847
  }
4269
4848
  function multi(useStore) {
4270
- const storeName = useStore["$storeName"];
4271
- const flagSym = `lark_comp_${storeName}`;
4849
+ const storeName = useStore.$storeName ?? "";
4850
+ const flagSym = `lark-comp-${storeName}`;
4272
4851
  const map = /* @__PURE__ */ new Map();
4273
4852
  let rootViewPath;
4274
4853
  const getFlag = (viewContext) => {
4275
- const owner = viewContext["owner"];
4276
- const viewPath = owner?.["path"] || "";
4277
- const viewId = owner?.["id"] || "";
4854
+ const owner = viewContext.owner;
4855
+ const viewPath = owner?.path ?? "";
4856
+ const viewId = owner?.id ?? "";
4278
4857
  let flag;
4279
4858
  if (viewPath === rootViewPath) {
4280
- flag = `${flagSym}_${viewId}`;
4859
+ flag = `${flagSym}-${viewId}`;
4281
4860
  } else {
4282
- flag = owner?.["viewInitParams"]?.[flagSym];
4861
+ const initParams = owner?.viewInitParams;
4862
+ const candidate = initParams?.[flagSym];
4863
+ flag = typeof candidate === "string" ? candidate : void 0;
4283
4864
  }
4284
- if (owner && isFunction(owner["mountFrame"])) {
4285
- const rawMountFrame = owner["mountFrame"];
4286
- 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(
4287
4868
  owner,
4288
- vfId,
4869
+ frameId,
4289
4870
  viewPath2,
4290
4871
  Object.assign(viewInitParams, { [flagSym]: flag })
4291
4872
  );
4292
4873
  }
4293
- return flag;
4874
+ return flag ?? "";
4294
4875
  };
4295
4876
  const useFn = ((view) => {
4296
4877
  if (!view)
4297
- throw new Error("[lark-store] multi: cannot find the view instance");
4878
+ throw new Error(
4879
+ "[@lark.js/mvc error] multi: cannot find the view instance"
4880
+ );
4298
4881
  const viewCtx = view;
4299
- const flag = viewCtx[flagSym];
4882
+ const tag = viewCtx[flagSym];
4883
+ const flag = typeof tag === "string" ? tag : "";
4300
4884
  if (map.has(flag)) return map.get(flag);
4301
- const newFn = cloneStore(
4302
- flag,
4303
- useStore
4304
- );
4885
+ const newFn = cloneStore(flag, useStore);
4305
4886
  map.set(flag, newFn);
4306
4887
  return useFn(view);
4307
4888
  });
4308
4889
  const mixinObj = {
4309
4890
  make() {
4310
4891
  if (!rootViewPath) {
4311
- const owner = this["owner"];
4312
- rootViewPath = owner?.["path"] || "";
4892
+ rootViewPath = this.owner?.path ?? "";
4313
4893
  }
4314
4894
  this[flagSym] = getFlag(this);
4315
4895
  }
@@ -4439,7 +5019,7 @@ function convertArtSyntax(source, debug) {
4439
5019
  }
4440
5020
  if (blockStack.length > 0) {
4441
5021
  const unclosed = blockStack.map((b) => `"${b.ctrl}" at line ${b.line}`).join(", ");
4442
- throw new Error(`[@lark/mvc error] unclosed block(s): ${unclosed}`);
5022
+ throw new Error(`[@lark.js/mvc error] unclosed block(s): ${unclosed}`);
4443
5023
  }
4444
5024
  return result.join("");
4445
5025
  }
@@ -4516,7 +5096,7 @@ function convertArtExpression(code, debug, lineNo, blockStack = []) {
4516
5096
  return `${debugPrefix}<%for(${forExpr}){%>`;
4517
5097
  }
4518
5098
  const tokens = code.split(/\s+/);
4519
- const keyword = tokens.shift();
5099
+ const keyword = tokens.shift() ?? "";
4520
5100
  switch (keyword) {
4521
5101
  case "if": {
4522
5102
  blockStack.push({ ctrl: "if", line: lineNo });
@@ -4533,12 +5113,12 @@ function convertArtExpression(code, debug, lineNo, blockStack = []) {
4533
5113
  }
4534
5114
  return `${debugPrefix}<%}else{%>`;
4535
5115
  }
4536
- case "each": {
4537
- blockStack.push({ ctrl: "each", line: lineNo });
5116
+ case "forOf": {
5117
+ blockStack.push({ ctrl: "forOf", line: lineNo });
4538
5118
  const object = tokens[0];
4539
5119
  if (tokens.length > 1 && tokens[1] !== "as") {
4540
5120
  throw new Error(
4541
- `[@lark/mvc error] bad each syntax: {{${code}}}. Expected "as" keyword, got "${tokens[1]}". Usage: {{each list as item [index]}}`
5121
+ `[@lark.js/mvc error] bad forOf syntax: {{${code}}}. Expected "as" keyword, got "${tokens[1]}". Usage: {{forOf list as item [index]}}`
4542
5122
  );
4543
5123
  }
4544
5124
  const restTokens = tokens.slice(2);
@@ -4560,12 +5140,12 @@ function convertArtExpression(code, debug, lineNo, blockStack = []) {
4560
5140
  }
4561
5141
  return `${debugPrefix}<%for(let ${index}=0${refExpr},${refObjCount}=${refObj}.length${lastCount};${index}<${refObjCount};${index}++){${firstAndLast}${valueDecl}%>`;
4562
5142
  }
4563
- case "parse": {
4564
- blockStack.push({ ctrl: "parse", line: lineNo });
5143
+ case "forIn": {
5144
+ blockStack.push({ ctrl: "forIn", line: lineNo });
4565
5145
  const object = tokens[0];
4566
5146
  if (tokens.length > 1 && tokens[1] !== "as") {
4567
5147
  throw new Error(
4568
- `[@lark/mvc error] bad parse syntax: {{${code}}}. Expected "as" keyword, got "${tokens[1]}". Usage: {{for-in obj as val [key]}}`
5148
+ `[@lark.js/mvc error] bad forIn syntax: {{${code}}}. Expected "as" keyword, got "${tokens[1]}". Usage: {{for-in obj as val [key]}}`
4569
5149
  );
4570
5150
  }
4571
5151
  const restTokens2 = tokens.slice(2);
@@ -4585,19 +5165,19 @@ function convertArtExpression(code, debug, lineNo, blockStack = []) {
4585
5165
  case "set":
4586
5166
  return `${debugPrefix}<%let ${tokens.join(" ")};%>`;
4587
5167
  case "/if":
4588
- case "/each":
4589
- case "/parse":
5168
+ case "/forOf":
5169
+ case "/forIn":
4590
5170
  case "/for": {
4591
5171
  const expectedCtrl = keyword.substring(1);
4592
5172
  const last = blockStack.pop();
4593
5173
  if (!last) {
4594
5174
  throw new Error(
4595
- `[@lark/mvc error] unexpected {{${code}}}: no matching open block`
5175
+ `[@lark.js/mvc error] unexpected {{${code}}}: no matching open block`
4596
5176
  );
4597
5177
  }
4598
5178
  if (last.ctrl !== expectedCtrl) {
4599
5179
  throw new Error(
4600
- `[@lark/mvc error] unexpected {{${code}}}: expected {{/${last.ctrl}}} to close block opened at line ${last.line}`
5180
+ `[@lark.js/mvc error] unexpected {{${code}}}: expected {{/${last.ctrl}}} to close block opened at line ${last.line}`
4601
5181
  );
4602
5182
  }
4603
5183
  return `${debugPrefix}<%}%>`;
@@ -4661,7 +5241,7 @@ function parseAsExpr(expr) {
4661
5241
  function compileToFunction(source, debug, file) {
4662
5242
  const matcher = /<%([@=!:])?([\s\S]*?)%>|$/g;
4663
5243
  let index = 0;
4664
- let funcSource = `$p+='`;
5244
+ let funcSource = `$out+='`;
4665
5245
  let hasAtRule = false;
4666
5246
  const escapeSlashRegExp = /\\|'/g;
4667
5247
  const escapeBreakReturnRegExp = /\r|\n/g;
@@ -4687,17 +5267,17 @@ function compileToFunction(source, debug, file) {
4687
5267
  }
4688
5268
  if (operate === "@") {
4689
5269
  hasAtRule = true;
4690
- funcSource += `'+($expr='<%${operate + expr}%>',$i($$ref,${content}))+'`;
5270
+ funcSource += `'+($dbgExpr='<%${operate + expr}%>',$refFn($refAlt,${content}))+'`;
4691
5271
  } else if (operate === "=" || operate === ":") {
4692
- funcSource += `'+($expr='<%${operate + expr}%>',$e(${content}))+'`;
5272
+ funcSource += `'+($dbgExpr='<%${operate + expr}%>',$encHtml(${content}))+'`;
4693
5273
  } else if (operate === "!") {
4694
- if (!content.startsWith("$eu(") || !content.endsWith(")")) {
4695
- content = `$n(${content})`;
5274
+ if (!content.startsWith("$encUri(") || !content.endsWith(")")) {
5275
+ content = `$strSafe(${content})`;
4696
5276
  }
4697
- funcSource += `'+($expr='<%${operate + expr}%>',${content})+'`;
5277
+ funcSource += `'+($dbgExpr='<%${operate + expr}%>',${content})+'`;
4698
5278
  } else if (content) {
4699
5279
  if (line > -1) {
4700
- funcSource += `';$line=${line};$art='${art}';`;
5280
+ funcSource += `';$dbgLine=${line};$dbgArt='${art}';`;
4701
5281
  content = "";
4702
5282
  } else {
4703
5283
  funcSource += `';`;
@@ -4706,19 +5286,19 @@ function compileToFunction(source, debug, file) {
4706
5286
  funcSource = funcSource.substring(0, funcSource.length - 4) + ";";
4707
5287
  }
4708
5288
  if (expr) {
4709
- funcSource += `$expr='<%${expr}%>';`;
5289
+ funcSource += `$dbgExpr='<%${expr}%>';`;
4710
5290
  }
4711
- funcSource += content + `;$p+='`;
5291
+ funcSource += content + `;$out+='`;
4712
5292
  }
4713
5293
  } else {
4714
5294
  if (operate === "@") {
4715
5295
  hasAtRule = true;
4716
- funcSource += `'+$i($$ref,${content})+'`;
5296
+ funcSource += `'+$refFn($refAlt,${content})+'`;
4717
5297
  } else if (operate === "=" || operate === ":") {
4718
- funcSource += `'+$e(${content})+'`;
5298
+ funcSource += `'+$encHtml(${content})+'`;
4719
5299
  } else if (operate === "!") {
4720
- if (!content.startsWith("$eu(") || !content.endsWith(")")) {
4721
- content = `$n(${content})`;
5300
+ if (!content.startsWith("$encUri(") || !content.endsWith(")")) {
5301
+ content = `$strSafe(${content})`;
4722
5302
  }
4723
5303
  funcSource += `'+${content}+'`;
4724
5304
  } else if (content) {
@@ -4726,28 +5306,24 @@ function compileToFunction(source, debug, file) {
4726
5306
  if (funcSource.endsWith(`+'';`)) {
4727
5307
  funcSource = funcSource.substring(0, funcSource.length - 4) + ";";
4728
5308
  }
4729
- funcSource += `${content};$p+='`;
5309
+ funcSource += `${content};$out+='`;
4730
5310
  }
4731
5311
  }
4732
5312
  return match;
4733
5313
  });
4734
5314
  funcSource += `';`;
4735
- funcSource = funcSource.replace(/\$p\+='';/g, "");
4736
- funcSource = funcSource.replace(/\$p\+=''\+/g, "$p+=");
5315
+ funcSource = funcSource.replace(/\$out\+='';/g, "");
5316
+ funcSource = funcSource.replace(/\$out\+=''\+/g, "$out+=");
4737
5317
  if (debug) {
4738
5318
  const filePart = file ? `\\r\\n\\tat file:${file}` : "";
4739
- funcSource = `let $expr,$art,$line;try{${funcSource}}catch(ex){let msg='render view error:'+(ex.message||ex);if($art)msg+='\\r\\n\\tsrc art:{{'+$art+'}}\\r\\n\\tat line:'+$line;msg+='\\r\\n\\t'+($art?'translate to:':'expr:');msg+=$expr+'${filePart}';throw msg;}`;
5319
+ funcSource = `let $dbgExpr,$dbgArt,$dbgLine;try{${funcSource}}catch(ex){let msg='render view error:'+(ex.message||ex);if($dbgArt)msg+='\\r\\n\\tsrc art:{{'+$dbgArt+'}}\\r\\n\\tat line:'+$dbgLine;msg+='\\r\\n\\t'+($dbgArt?'translate to:':'expr:');msg+=$dbgExpr+'${filePart}';throw msg;}`;
4740
5320
  }
4741
5321
  const viewIdRegExp = new RegExp(String.fromCharCode(31), "g");
4742
5322
  funcSource = funcSource.replace(viewIdRegExp, `'+$viewId+'`);
4743
- const atRule = hasAtRule ? `if(!$i){$i=(ref,v,k,f)=>{for(f=ref[$g];--f;)if(ref[k=$g+f]===v)return k;ref[k=$g+ref[$g]++]=v;return k;}}` : "";
4744
- const encode = `if(!$n){let $em={'&':'amp','<':'lt','>':'gt','"':'#34','\\'':'#39','\`':'#96'},$er=/[&<>"'\`]/g,$ef=m=>'&'+$em[m]+';';$n=v=>''+(v==null?'':v);$e=v=>$n(v).replace($er,$ef)}`;
4745
- const encodeURIMore = `if(!$eu){let $um={'!':'%21','\\'':'%27','(':'%28',')':'%29','*':'%2A'},$uf=m=>$um[m],$uq=/[!')(*]/g;$eu=v=>encodeURIComponent($n(v)).replace($uq,$uf)}`;
4746
- const encodeQuote = `if(!$eq){let $qr=/['"\\\\]/g;$eq=v=>$n(v).replace($qr,'\\\\$&')}`;
4747
- const refFallback = "if(!$$ref)$$ref=$$;";
4748
- const fns = `${refFallback}${encode}${encodeURIMore}${encodeQuote}${atRule};`;
4749
- const fullSource = `${fns}let $g='\\x1e',$_temp,$p=''{{VARS}};${funcSource}return $p`;
4750
- return `($$,$viewId,$$ref,$e,$n,$eu,$i,$eq)=>{${fullSource}}`;
5323
+ void hasAtRule;
5324
+ const refFallback = "if(!$refAlt)$refAlt=$data;";
5325
+ const fullSource = `${refFallback}let $splitter='\\x1e',$tmp,$out=''{{VARS}};${funcSource}return $out`;
5326
+ return `($data,$viewId,$refAlt,$encHtml,$strSafe,$encUri,$refFn,$encQuote)=>{${fullSource}}`;
4751
5327
  }
4752
5328
  function compileTemplate(source, options = {}) {
4753
5329
  const { debug = false, globalVars = [], file } = options;
@@ -4756,17 +5332,14 @@ function compileTemplate(source, options = {}) {
4756
5332
  const viewEventProcessed = processViewEvents(converted);
4757
5333
  const finalSource = restoreComments(viewEventProcessed, comments);
4758
5334
  const funcBody = compileToFunction(finalSource, debug, file);
4759
- const varDeclarations = globalVars.map((key) => `,${key}=$$.${key}`).join("");
5335
+ const varDeclarations = globalVars.map((key) => `,${key}=$data.${key}`).join("");
4760
5336
  const funcWithVars = funcBody.replace("{{VARS}}", () => varDeclarations);
4761
- return `export default function(data, selfId, refData) {
4762
- let $$ = data || {},
4763
- $viewId = selfId || '';
4764
- return (${funcWithVars})($$, $viewId, refData,
4765
- /* $e */ v => String(v == null ? '' : v).replace(/[&<>"'\`]/g, m => '&' + ({'&':'amp','<':'lt','>':'gt','"':'#34',"'":'#39','\`':'#96'})[m] + ';'),
4766
- /* $n */ v => String(v == null ? '' : v),
4767
- /* $eu */ null,
4768
- /* $i */ null,
4769
- /* $eq */ null
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) {
5339
+ let $data = data || {},
5340
+ $viewId = viewId || '';
5341
+ return (${funcWithVars})($data, $viewId, refData,
5342
+ __larkEncHtml, __larkStrSafe, __larkEncUri, __larkRefFn, __larkEncQuote
4770
5343
  );
4771
5344
  }`;
4772
5345
  }
@@ -4875,7 +5448,7 @@ function fallbackExtractVariables(source) {
4875
5448
  while ((m = outputRegExp.exec(source)) !== null) {
4876
5449
  vars.add(m[1]);
4877
5450
  }
4878
- const eachRegExp = /\{\{each\s+([a-zA-Z_$][\w$]*)\s+as/g;
5451
+ const eachRegExp = /\{\{forOf\s+([a-zA-Z_$][\w$]*)\s+as/g;
4879
5452
  while ((m = eachRegExp.exec(source)) !== null) {
4880
5453
  vars.add(m[1]);
4881
5454
  }
@@ -4891,82 +5464,100 @@ function walkAst(ast, visitors) {
4891
5464
  if (visitors[type]) {
4892
5465
  visitors[type](node);
4893
5466
  }
5467
+ const bag = node;
4894
5468
  for (const key of Object.keys(node)) {
4895
5469
  if (key === "type" || key === "start" || key === "end" || key === "loc" || key === "range")
4896
5470
  continue;
4897
- if (type === "MemberExpression" && key === "property" && !node.computed)
4898
- continue;
4899
- if (type === "ObjectProperty" && key === "key" && !node.computed) {
4900
- continue;
5471
+ if (type === "MemberExpression" && key === "property") {
5472
+ const me = node;
5473
+ if (!me.computed) continue;
4901
5474
  }
4902
- if (type === "ObjectMethod" && key === "key" && !node.computed) {
4903
- continue;
5475
+ if (type === "ObjectProperty" && key === "key") {
5476
+ const op = node;
5477
+ if (!op.computed) continue;
4904
5478
  }
4905
- 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];
4906
5484
  if (Array.isArray(child)) {
4907
5485
  for (const item of child) {
4908
- if (item && typeof item === "object" && typeof item.type === "string") {
4909
- visit(item);
4910
- }
5486
+ if (isAstNode(item)) visit(item);
4911
5487
  }
4912
- } else if (child && typeof child === "object" && typeof child.type === "string") {
5488
+ } else if (isAstNode(child)) {
4913
5489
  visit(child);
4914
5490
  }
4915
5491
  }
4916
5492
  }
4917
5493
  visit(ast);
4918
5494
  }
5495
+ function isAstNode(v) {
5496
+ return !!v && typeof v === "object" && typeof v.type === "string";
5497
+ }
4919
5498
  var BUILTIN_GLOBALS = {
4920
5499
  // ─── Template runtime helpers (injected by compileToFunction) ───────
4921
5500
  //
4922
5501
  // These variables appear in the generated template function signature
4923
5502
  // or body. They must be excluded from extractGlobalVars() so that
4924
- // they are not mistaken for user data variables and destructured from $$.
5503
+ // they are not mistaken for user data variables and destructured from $data.
4925
5504
  // SPLITTER character constant (same as \x1e), used as namespace separator
4926
5505
  // for refData keys, event attribute encoding, and internal data structures.
4927
- // Declared as: let $g='\x1e'
4928
- $g: 1,
4929
- // refData — the data object passed from Updater to the template function.
4930
- // User variables are destructured from $$ at the top of the function:
4931
- // let {name, age} = $$;
5506
+ // Declared as: let $splitter='\x1e'
5507
+ $splitter: 1,
5508
+ // Data — the data object passed from Updater to the template function.
5509
+ // User variables are destructured from $data at the top of the function:
5510
+ // let {name, age} = $data;
4932
5511
  // This is the first parameter of the generated arrow function.
4933
- $$: 1,
5512
+ $data: 1,
4934
5513
  // Null-safe toString: v => '' + (v == null ? '' : v)
4935
5514
  // Converts null/undefined to empty string, otherwise calls toString().
4936
5515
  // Wraps every {{!raw}} output to prevent "null" / "undefined" rendering.
4937
- $n: 1,
4938
- // HTML entity encoder: v => $n(v).replace(/[&<>"'`]/g, entityMap)
5516
+ $strSafe: 1,
5517
+ // HTML entity encoder: v => $strSafe(v).replace(/[&<>"'`]/g, entityMap)
4939
5518
  // Encodes &, <, >, ", ', ` to HTML entities (&amp; &lt; etc.)
4940
5519
  // Applied to all {{=escaped}} and {{:binding}} outputs.
4941
- $e: 1,
4942
- // HTML entity map — internal object used by $e:
5520
+ $encHtml: 1,
5521
+ // HTML entity map — internal object used by $encHtml:
4943
5522
  // {'&':'amp','<':'gt','>':'gt','"':'#34','\'':'#39','`':'#96'}
4944
- // Not a standalone function; referenced inside $e's closure.
4945
- $em: 1,
4946
- // HTML entity RegExp — internal regexp used by $e:
5523
+ // Not a standalone function; referenced inside $encHtml's closure.
5524
+ $entMap: 1,
5525
+ // HTML entity RegExp — internal regexp used by $encHtml:
4947
5526
  // /[&<>"'`]/g
4948
- $er: 1,
4949
- // HTML entity replacer function — internal helper used by $e:
4950
- // m => '&' + $em[m] + ';'
4951
- // Maps each matched character to its entity string.
4952
- $ef: 1,
5527
+ $entReg: 1,
5528
+ // HTML entity replacer function — internal helper used by $encHtml:
5529
+ // m => '&' + $entMap[m] + ';'
5530
+ // Maps matched character to its entity string.
5531
+ $entFn: 1,
4953
5532
  // Output buffer — the string accumulator for rendered HTML.
4954
- // All template output is appended via $p += '...'.
4955
- // Declared as: let $p = ''
4956
- $p: 1,
5533
+ // All template output is appended via $out += '...'.
5534
+ // Declared as: let $out = ''
5535
+ $out: 1,
4957
5536
  // Reference lookup: (refData, value) => key
4958
5537
  // Finds or allocates a SPLITTER-prefixed key in refData for a given
4959
5538
  // object reference. Used by {{@ref}} operator for passing object
4960
5539
  // references to child views via v-lark attributes.
4961
- $i: 1,
4962
- // URI encoder: v => encodeURIComponent($n(v)).replace(/[!')(*]/g, extraMap)
5540
+ $refFn: 1,
5541
+ // URI encoder: v => encodeURIComponent($strSafe(v)).replace(/[!')(*]/g, extraMap)
4963
5542
  // Extends encodeURIComponent with encoding of ! ' ( ) *.
4964
5543
  // Applied to values in @event URL parameters and {{!uri}} contexts.
4965
- $eu: 1,
4966
- // Quote encoder: v => $n(v).replace(/['"\\]/g, '\\$&')
5544
+ $encUri: 1,
5545
+ // URI encode map internal object used by $encUri:
5546
+ // {'!':'%21','\'':'%27','(':'%28',')':'%29','*':'%2A'}
5547
+ $uriMap: 1,
5548
+ // URI encode replacer — internal helper used by $encUri:
5549
+ // m => $uriMap[m]
5550
+ $uriFn: 1,
5551
+ // URI encode regexp — internal regexp used by $encUri:
5552
+ // /[!')(*]/g
5553
+ $uriReg: 1,
5554
+ // Quote encoder: v => $strSafe(v).replace(/['"\\]/g, '\\$&')
4967
5555
  // Escapes quotes and backslashes for safe embedding in HTML attribute
4968
5556
  // values (e.g. data-json='...').
4969
- $eq: 1,
5557
+ $encQuote: 1,
5558
+ // Quote encode regexp — internal regexp used by $encQuote:
5559
+ // /['"\\]/g
5560
+ $qReg: 1,
4970
5561
  // View ID — the unique identifier of the owning View instance.
4971
5562
  // Injected into @event attribute values at render time so that
4972
5563
  // EventDelegator can dispatch events to the correct View handler.
@@ -4974,23 +5565,23 @@ var BUILTIN_GLOBALS = {
4974
5565
  $viewId: 1,
4975
5566
  // Debug: current expression text — stores the template expression being
4976
5567
  // evaluated, for error reporting. Only present in debug mode.
4977
- // e.g. $expr='<%=user.name%>'
4978
- $expr: 1,
5568
+ // e.g. $dbgExpr='<%=user.name%>'
5569
+ $dbgExpr: 1,
4979
5570
  // Debug: original art syntax — stores the {{}} template syntax before
4980
5571
  // conversion, for error reporting. Only present in debug mode.
4981
- // e.g. $art='{{=user.name}}'
4982
- $art: 1,
5572
+ // e.g. $dbgArt='{{=user.name}}'
5573
+ $dbgArt: 1,
4983
5574
  // Debug: source line number — tracks the current line in the template
4984
5575
  // source, for error reporting. Only present in debug mode.
4985
- $line: 1,
4986
- // refData alias — fallback reference lookup table.
4987
- // Defaults to $$ when no explicit $$ref is provided.
4988
- // Ensures $i() does not crash when @ operator is used without refData.
4989
- $$ref: 1,
5576
+ $dbgLine: 1,
5577
+ // RefData alias — fallback reference lookup table.
5578
+ // Defaults to $data when no explicit $refAlt is provided.
5579
+ // Ensures $refFn() does not crash when @ operator is used without refData.
5580
+ $refAlt: 1,
4990
5581
  // Temporary variable — used by the compiler for intermediate
4991
5582
  // expression results in generated code (e.g. loop variables,
4992
- // conditional branches). Declared as: let $_temp
4993
- $_temp: 1,
5583
+ // conditional branches). Declared as: let $tmp
5584
+ $tmp: 1,
4994
5585
  // JS literals
4995
5586
  undefined: 1,
4996
5587
  null: 1,
@@ -5055,17 +5646,19 @@ var BUILTIN_GLOBALS = {
5055
5646
  };
5056
5647
  var BUILTIN_GLOBAL_SET = new Set(Object.keys(BUILTIN_GLOBALS));
5057
5648
  export {
5058
- Bag,
5059
5649
  CALL_BREAK_TIME,
5060
5650
  Cache,
5651
+ cross_site_default as CrossSite,
5061
5652
  EVENT_METHOD_REGEXP,
5062
5653
  EventDelegator,
5063
5654
  EventEmitter,
5064
5655
  Frame,
5656
+ FrameVisualBridge,
5065
5657
  Framework,
5066
5658
  LARK_VIEW,
5659
+ Payload,
5067
5660
  Platform,
5068
- ROUTER_EVENTS,
5661
+ RouterEvents as ROUTER_EVENTS,
5069
5662
  Router,
5070
5663
  SPLITTER,
5071
5664
  Service,
@@ -5079,13 +5672,14 @@ export {
5079
5672
  applyVdomOps,
5080
5673
  assign,
5081
5674
  cell,
5082
- classExtend,
5083
5675
  cloneData,
5084
5676
  cloneStore,
5085
5677
  compileTemplate,
5678
+ computed,
5086
5679
  createState,
5087
5680
  createVdomRef,
5088
5681
  defineStore,
5682
+ defineView,
5089
5683
  delStore,
5090
5684
  encodeHTML,
5091
5685
  encodeQ,
@@ -5093,6 +5687,7 @@ export {
5093
5687
  encodeURIExtra,
5094
5688
  ensureElementId,
5095
5689
  extractGlobalVars,
5690
+ config as frameworkConfig,
5096
5691
  funcWithTry,
5097
5692
  generateId,
5098
5693
  getAttribute,
@@ -5100,9 +5695,9 @@ export {
5100
5695
  getPlatform,
5101
5696
  getStore,
5102
5697
  getUseStore,
5103
- has,
5698
+ hasOwnProperty,
5104
5699
  installFrameVisualizerBridge,
5105
- isArray,
5700
+ invalidateViewClass,
5106
5701
  isPlainObject,
5107
5702
  isPrimitive,
5108
5703
  isPrimitiveOrFunc,
@@ -5121,6 +5716,7 @@ export {
5121
5716
  observeCell,
5122
5717
  parseUri,
5123
5718
  registerViewClass,
5719
+ resetProjectsMap,
5124
5720
  safeguard,
5125
5721
  serializeFrameTree,
5126
5722
  setData,
@@ -5132,6 +5728,7 @@ export {
5132
5728
  toUri,
5133
5729
  translateData,
5134
5730
  unmark,
5731
+ use,
5135
5732
  vdomGetCompareKey,
5136
5733
  vdomGetNode,
5137
5734
  vdomSetAttributes,