@lark.js/mvc 0.0.4 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  // src/constants.ts
2
2
  var globalCounter = 0;
3
- var SPLITTER = "";
4
- var ROUTER_EVENTS = {
3
+ var SPLITTER = String.fromCharCode(30);
4
+ var RouterEvents = {
5
5
  CHANGE: "change",
6
6
  CHANGED: "changed",
7
7
  PAGE_UNLOAD: "page_unload"
8
8
  };
9
- var LARK_KEYS = {
9
+ var LarkInnerKeys = {
10
10
  /** Attribute name: ldk (static key for skipping VDOM diff) */
11
11
  DIFF_KEY: "ldk",
12
12
  /** Attribute name: lak (static attribute key) */
@@ -53,13 +53,13 @@ function syncCounter(val) {
53
53
  }
54
54
  function noop() {
55
55
  }
56
- function has(owner, prop) {
56
+ function hasOwnProperty(owner, prop) {
57
57
  return owner != null && Object.prototype.hasOwnProperty.call(owner, prop);
58
58
  }
59
59
  function keys(obj) {
60
60
  const result = [];
61
61
  for (const p in obj) {
62
- if (has(obj, p)) {
62
+ if (hasOwnProperty(obj, p)) {
63
63
  result.push(p);
64
64
  }
65
65
  }
@@ -69,7 +69,7 @@ function assign(target, ...sources) {
69
69
  for (const source of sources) {
70
70
  if (source) {
71
71
  for (const p in source) {
72
- if (has(source, p)) {
72
+ if (hasOwnProperty(source, p)) {
73
73
  target[p] = source[p];
74
74
  }
75
75
  }
@@ -82,21 +82,22 @@ function funcWithTry(fns, args, context, configError) {
82
82
  let ret;
83
83
  for (const fn of fnArray) {
84
84
  try {
85
- ret = Function.prototype.apply.call(fn, context, args);
85
+ ret = fn.apply(context, args);
86
86
  } catch (e) {
87
87
  configError?.(e);
88
88
  }
89
89
  }
90
90
  return ret;
91
91
  }
92
+ var EMPTY_STRING_SET = /* @__PURE__ */ new Set();
92
93
  function setData(newData, oldData, changedKeys2, excludes) {
93
94
  let changed = false;
94
95
  for (const p in newData) {
95
- if (has(newData, p)) {
96
+ if (hasOwnProperty(newData, p)) {
96
97
  const now2 = newData[p];
97
98
  const old = oldData[p];
98
99
  if ((!isPrimitiveOrFunc(now2) || old !== now2) && !excludes.has(p)) {
99
- changedKeys2[p] = 1;
100
+ changedKeys2.add(p);
100
101
  changed = true;
101
102
  }
102
103
  oldData[p] = now2;
@@ -104,17 +105,25 @@ function setData(newData, oldData, changedKeys2, excludes) {
104
105
  }
105
106
  return changed;
106
107
  }
108
+ function isRefToken(s) {
109
+ if (s.length < 2 || s[0] !== SPLITTER) return false;
110
+ for (let i = 1; i < s.length; i++) {
111
+ const c = s.charCodeAt(i);
112
+ if (c < 48 || c > 57) return false;
113
+ }
114
+ return true;
115
+ }
107
116
  function translateData(data, value) {
108
117
  if (isPrimitive(value)) {
109
118
  const prop = String(value);
110
- if (prop[0] === SPLITTER && has(data, prop)) {
119
+ if (isRefToken(prop) && hasOwnProperty(data, prop)) {
111
120
  return data[prop];
112
121
  }
113
122
  return value;
114
123
  }
115
124
  if (isPlainObject(value) || Array.isArray(value)) {
116
125
  for (const p in value) {
117
- if (has(value, p)) {
126
+ if (hasOwnProperty(value, p)) {
118
127
  const val = value[p];
119
128
  const newVal = translateData(data, val);
120
129
  value[p] = newVal;
@@ -151,38 +160,33 @@ function nodeInside(a, b) {
151
160
  return false;
152
161
  }
153
162
  }
154
- var paramsTemp = {};
155
- function paramsReplacer(_match, name, value) {
156
- try {
157
- paramsTemp[name] = decodeURIComponent(value || "");
158
- } catch {
159
- paramsTemp[name] = value || "";
160
- }
161
- return "";
162
- }
163
163
  function parseUri(uri) {
164
- paramsTemp = {};
164
+ const params = {};
165
165
  const path = uri.replace(URL_QUERY_HASH_REGEXP, "");
166
166
  const pathname = path;
167
167
  const actualPath = uri === pathname && IS_URL_PARAMS.test(pathname) ? "" : pathname;
168
- uri.replace(actualPath, "").replace(URL_PARAM_REGEXP, paramsReplacer);
169
- return {
170
- path: actualPath,
171
- params: { ...paramsTemp }
172
- };
168
+ uri.replace(actualPath, "").replace(URL_PARAM_REGEXP, (_match, name, value) => {
169
+ try {
170
+ params[name] = decodeURIComponent(value || "");
171
+ } catch {
172
+ params[name] = value || "";
173
+ }
174
+ return "";
175
+ });
176
+ return { path: actualPath, params };
173
177
  }
174
178
  var IS_URL_PARAMS = {
175
179
  test(s) {
176
180
  return /(?!^)=|&/.test(s);
177
181
  }
178
182
  };
179
- function toUri(path, params, keepEmptyObject) {
183
+ function toUri(path, params, keepEmpty) {
180
184
  const pairs = [];
181
185
  let hasParams = false;
182
186
  for (const p in params) {
183
- if (has(params, p)) {
187
+ if (hasOwnProperty(params, p)) {
184
188
  const v = String(params[p] ?? "");
185
- if (!keepEmptyObject || v || has(keepEmptyObject, p)) {
189
+ if (!keepEmpty || v || keepEmpty.has(p)) {
186
190
  pairs.push(`${p}=${encodeURIComponent(v)}`);
187
191
  hasParams = true;
188
192
  }
@@ -205,14 +209,6 @@ function toMap(list, key) {
205
209
  function now() {
206
210
  return Date.now ? Date.now() : (/* @__PURE__ */ new Date()).getTime();
207
211
  }
208
- function classExtend(make, base, props, statics) {
209
- const baseProto = base["prototype"] ?? {};
210
- const classProto = Object.create(baseProto);
211
- assign(classProto, props);
212
- Object.assign(make, statics);
213
- classProto.constructor = make;
214
- make["prototype"] = classProto;
215
- }
216
212
 
217
213
  // src/apply-style.ts
218
214
  var injectedStyleIds = /* @__PURE__ */ new Set();
@@ -252,27 +248,35 @@ function applyStyle(styleIdOrPairs, css) {
252
248
  }
253
249
 
254
250
  // src/mark.ts
255
- var DELETED_KEY = SPLITTER + "$delFlag";
256
- var MARK_OBJECT_KEY = SPLITTER + "$markKey";
251
+ var hostStore = /* @__PURE__ */ new WeakMap();
252
+ function getOrCreate(host) {
253
+ let record = hostStore.get(host);
254
+ if (!record) {
255
+ record = { signs: /* @__PURE__ */ new Map(), deleted: false };
256
+ hostStore.set(host, record);
257
+ }
258
+ return record;
259
+ }
257
260
  function mark(host, key) {
258
- let sign = 0;
259
- const hostRecord = host;
260
- if (!hostRecord[DELETED_KEY]) {
261
- const markHost = hostRecord[MARK_OBJECT_KEY] || (hostRecord[MARK_OBJECT_KEY] = {});
262
- if (!Object.prototype.hasOwnProperty.call(markHost, key)) {
263
- markHost[key] = 0;
264
- }
265
- sign = ++markHost[key];
261
+ const record = getOrCreate(host);
262
+ if (record.deleted) {
263
+ return () => false;
266
264
  }
265
+ const sign = (record.signs.get(key) ?? 0) + 1;
266
+ record.signs.set(key, sign);
267
267
  return () => {
268
- const temp = hostRecord[MARK_OBJECT_KEY];
269
- return !!(temp && sign === temp[key]);
268
+ const current = hostStore.get(host);
269
+ return !!current && !current.deleted && current.signs.get(key) === sign;
270
270
  };
271
271
  }
272
272
  function unmark(host) {
273
- const hostRecord = host;
274
- hostRecord[MARK_OBJECT_KEY] = 0;
275
- hostRecord[DELETED_KEY] = 1;
273
+ const record = hostStore.get(host);
274
+ if (record) {
275
+ record.deleted = true;
276
+ record.signs.clear();
277
+ } else {
278
+ hostStore.set(host, { signs: /* @__PURE__ */ new Map(), deleted: true });
279
+ }
276
280
  }
277
281
 
278
282
  // src/safeguard.ts
@@ -301,7 +305,7 @@ function safeguard(data, getter, setter, isRoot) {
301
305
  set(target, property, value) {
302
306
  if (!setter && !prefix) {
303
307
  throw new Error(
304
- "Avoid write back, key: " + prefix + property + " value:" + value + " more: https://github.com/hangtiancheng/h"
308
+ "Avoid write back, key: " + prefix + property + " value:" + value + " more: https://github.com/hangtiancheng/lark"
305
309
  );
306
310
  }
307
311
  Reflect.set(target, property, value);
@@ -318,7 +322,7 @@ function safeguard(data, getter, setter, isRoot) {
318
322
  if (!prefix && getter) {
319
323
  getter(property);
320
324
  }
321
- if (!isRoot && has(target, property) && (Array.isArray(out) || isPlainObject(out))) {
325
+ if (!isRoot && hasOwnProperty(target, property) && (Array.isArray(out) || isPlainObject(out))) {
322
326
  return build(prefix + property + ".", out);
323
327
  }
324
328
  return out;
@@ -407,15 +411,17 @@ var Cache = class {
407
411
  this.lookup.set(prefixedKey, entry);
408
412
  }
409
413
  /**
410
- * Delete a cached entry.
414
+ * Delete a cached entry. Removes it immediately from both the lookup map
415
+ * and the entries array so the GC can reclaim the value without waiting
416
+ * for the next eviction sweep.
411
417
  */
412
418
  del(key) {
413
419
  const prefixedKey = this.prefixKey(key);
414
420
  const entry = this.lookup.get(prefixedKey);
415
421
  if (!entry) return;
416
- entry.frequency = -1;
417
- entry.value = void 0;
418
422
  this.lookup.delete(prefixedKey);
423
+ const idx = this.entries.indexOf(entry);
424
+ if (idx !== -1) this.entries.splice(idx, 1);
419
425
  if (this.onRemove) {
420
426
  this.onRemove(key);
421
427
  }
@@ -440,20 +446,46 @@ var Cache = class {
440
446
  this.entries = [];
441
447
  this.lookup.clear();
442
448
  }
443
- /** Evict least-used entries from cache */
449
+ /**
450
+ * Evict the `bufferSize` worst entries from the cache.
451
+ *
452
+ * Uses single-pass partial selection (O(n·k)) instead of sorting the entire
453
+ * `entries` array (O(n log n)). For the typical `bufferSize = 5` this is
454
+ * effectively a linear scan with at most 5 in-bucket comparisons per
455
+ * iteration — and it avoids mutating the rest of `entries`.
456
+ */
444
457
  evictEntries() {
445
- this.entries.sort(this.comparator);
446
- let count = this.bufferSize;
447
- while (count-- > 0 && this.entries.length > 0) {
448
- const entry = this.entries.pop();
449
- if (entry && entry.frequency > 0) {
450
- this.lookup.delete(this.prefixKey(entry.originalKey));
451
- if (this.onRemove) {
452
- this.onRemove(entry.originalKey);
453
- }
458
+ const entries = this.entries;
459
+ const k = this.bufferSize;
460
+ if (k <= 0 || entries.length === 0) return;
461
+ if (entries.length <= k) {
462
+ for (const e of entries) {
463
+ this.lookup.delete(this.prefixKey(e.originalKey));
464
+ if (this.onRemove) this.onRemove(e.originalKey);
454
465
  }
466
+ this.entries = [];
467
+ return;
455
468
  }
456
- this.entries = this.entries.filter((e) => e.frequency !== -1);
469
+ const cmp = this.comparator;
470
+ const worst = [];
471
+ for (const entry of entries) {
472
+ if (worst.length < k) {
473
+ let i = worst.length;
474
+ while (i > 0 && cmp(entry, worst[i - 1]) > 0) i--;
475
+ worst.splice(i, 0, entry);
476
+ } else if (cmp(entry, worst[k - 1]) > 0) {
477
+ worst.pop();
478
+ let i = worst.length;
479
+ while (i > 0 && cmp(entry, worst[i - 1]) > 0) i--;
480
+ worst.splice(i, 0, entry);
481
+ }
482
+ }
483
+ const evictSet = new Set(worst);
484
+ for (const e of worst) {
485
+ this.lookup.delete(this.prefixKey(e.originalKey));
486
+ if (this.onRemove) this.onRemove(e.originalKey);
487
+ }
488
+ this.entries = entries.filter((e) => !evictSet.has(e));
457
489
  }
458
490
  };
459
491
 
@@ -461,6 +493,10 @@ var Cache = class {
461
493
  var EventEmitter = class {
462
494
  /** Event listeners: prefixed key -> listener array */
463
495
  listeners = /* @__PURE__ */ new Map();
496
+ /** Number of `fire()` calls currently on the stack (re-entrancy depth). */
497
+ firingDepth = 0;
498
+ /** Keys whose listener list needs compaction after firing settles. */
499
+ pendingCompaction;
464
500
  /**
465
501
  * Bind event listener.
466
502
  */
@@ -483,13 +519,23 @@ var EventEmitter = class {
483
519
  const key = SPLITTER + event;
484
520
  if (handler) {
485
521
  const list = this.listeners.get(key);
486
- if (list) {
522
+ if (!list) return this;
523
+ if (this.firingDepth > 0) {
487
524
  for (const listener of list) {
488
525
  if (listener.handler === handler) {
489
526
  listener.handler = noop;
527
+ (this.pendingCompaction ??= /* @__PURE__ */ new Set()).add(key);
490
528
  break;
491
529
  }
492
530
  }
531
+ } else {
532
+ for (let i = 0; i < list.length; i++) {
533
+ if (list[i].handler === handler) {
534
+ list.splice(i, 1);
535
+ break;
536
+ }
537
+ }
538
+ if (list.length === 0) this.listeners.delete(key);
493
539
  }
494
540
  } else {
495
541
  this.listeners.delete(key);
@@ -501,9 +547,9 @@ var EventEmitter = class {
501
547
  return this;
502
548
  }
503
549
  /**
504
- * Fire event, execute all bound handlers.
505
- * Supports executing state management: handlers removed during
506
- * execution are safely handled.
550
+ * Fire event, execute all bound handlers. Safe for re-entrant `off()` calls
551
+ * during dispatch: removed handlers are replaced with noop and compacted
552
+ * after the outermost fire returns.
507
553
  *
508
554
  * @param event - Event name
509
555
  * @param data - Event data (type property added automatically)
@@ -517,38 +563,41 @@ var EventEmitter = class {
517
563
  data = {};
518
564
  }
519
565
  data["type"] = event;
520
- if (list) {
521
- let end = list.length;
522
- const len = end - 1;
523
- while (end--) {
524
- const idx = lastToFirst ? end : len - end;
525
- const listener = list[idx];
526
- if (listener.handler !== noop) {
566
+ this.firingDepth++;
567
+ try {
568
+ if (list) {
569
+ const len = list.length;
570
+ for (let i = 0; i < len; i++) {
571
+ const idx = lastToFirst ? len - 1 - i : i;
572
+ const listener = list[idx];
573
+ if (!listener) continue;
574
+ if (listener.handler === noop) continue;
527
575
  listener.executing = 1;
528
- funcWithTry(
529
- [listener.handler],
530
- [data],
531
- this,
532
- noop
533
- );
576
+ funcWithTry([listener.handler], [data], this, noop);
534
577
  listener.executing = "";
535
- } else if (!listener.executing) {
536
- list.splice(idx, 1);
537
578
  }
538
579
  }
539
- }
540
- const onMethodName = `on${event[0].toUpperCase() + event.slice(1)}`;
541
- const onMethod = this[onMethodName];
542
- if (typeof onMethod === "function") {
543
- funcWithTry(
544
- [onMethod],
545
- [data],
546
- this,
547
- noop
548
- );
549
- }
550
- if (remove) {
551
- this.off(event);
580
+ const onMethodName = `on${event[0].toUpperCase() + event.slice(1)}`;
581
+ const onMethod = this[onMethodName];
582
+ if (typeof onMethod === "function") {
583
+ funcWithTry([onMethod], [data], this, noop);
584
+ }
585
+ if (remove) {
586
+ this.off(event);
587
+ }
588
+ } finally {
589
+ this.firingDepth--;
590
+ if (this.firingDepth === 0 && this.pendingCompaction) {
591
+ for (const k of this.pendingCompaction) {
592
+ const l = this.listeners.get(k);
593
+ if (!l) continue;
594
+ for (let i = l.length - 1; i >= 0; i--) {
595
+ if (l[i].handler === noop) l.splice(i, 1);
596
+ }
597
+ if (l.length === 0) this.listeners.delete(k);
598
+ }
599
+ this.pendingCompaction = void 0;
600
+ }
552
601
  }
553
602
  return this;
554
603
  }
@@ -557,8 +606,8 @@ var EventEmitter = class {
557
606
  // src/state.ts
558
607
  var appData = {};
559
608
  var keyRefCounts = {};
560
- var changedKeys = {};
561
- var stashedChangedKeys = {};
609
+ var changedKeys = /* @__PURE__ */ new Set();
610
+ var stashedChangedKeys = EMPTY_STRING_SET;
562
611
  var dataIsChanged = false;
563
612
  var dataWhereSet = {};
564
613
  var emitter = new EventEmitter();
@@ -569,7 +618,7 @@ function markBooted() {
569
618
  function setupKeysRef(keys2) {
570
619
  const keyList = keys2.split(",");
571
620
  for (const key of keyList) {
572
- if (has(keyRefCounts, key)) {
621
+ if (hasOwnProperty(keyRefCounts, key)) {
573
622
  keyRefCounts[key]++;
574
623
  } else {
575
624
  keyRefCounts[key] = 1;
@@ -579,7 +628,7 @@ function setupKeysRef(keys2) {
579
628
  }
580
629
  function teardownKeysRef(keyList) {
581
630
  for (const key of keyList) {
582
- if (has(keyRefCounts, key)) {
631
+ if (hasOwnProperty(keyRefCounts, key)) {
583
632
  const count = --keyRefCounts[key];
584
633
  if (count <= 0) {
585
634
  Reflect.deleteProperty(keyRefCounts, key);
@@ -591,35 +640,14 @@ function teardownKeysRef(keyList) {
591
640
  }
592
641
  }
593
642
  }
594
- var notifyStarted = 0;
595
- var notifyList = [];
596
- var notifyTimer;
643
+ var warnedKeys = /* @__PURE__ */ new Set();
597
644
  function clearNotify(key) {
598
- for (let i = notifyList.length; i--; ) {
599
- if (notifyList[i]?.key === key) {
600
- notifyList.splice(i, 1);
601
- }
602
- }
603
- }
604
- function doNotify() {
605
- const locker = {};
606
- for (const n of notifyList) {
607
- if (!locker[n.key]) {
608
- console.warn(n.message);
609
- locker[n.key] = 1;
610
- }
611
- }
612
- notifyList.length = 0;
613
- notifyStarted = 0;
645
+ warnedKeys.delete(key);
614
646
  }
615
647
  function delayNotify(key, message) {
616
- clearTimeout(notifyTimer);
617
- notifyStarted = 0;
618
- notifyList.push({ key, message });
619
- if (!notifyStarted) {
620
- notifyStarted = 1;
621
- notifyTimer = setTimeout(doNotify, 500);
622
- }
648
+ if (warnedKeys.has(key)) return;
649
+ warnedKeys.add(key);
650
+ console.warn(message);
623
651
  }
624
652
  var State = {
625
653
  /**
@@ -631,7 +659,7 @@ var State = {
631
659
  return safeguard(
632
660
  result,
633
661
  (dataKey) => {
634
- if (booted && has(dataWhereSet, dataKey) && dataWhereSet[dataKey] !== window.location.pathname) {
662
+ if (booted && hasOwnProperty(dataWhereSet, dataKey) && dataWhereSet[dataKey] !== window.location.pathname) {
635
663
  console.warn(
636
664
  `beware! You get state:"{State}.${dataKey}" where it set by page:${dataWhereSet[dataKey]}`
637
665
  );
@@ -652,7 +680,7 @@ var State = {
652
680
  * Set data to state.
653
681
  */
654
682
  set(data, excludes) {
655
- dataIsChanged = setData(data, appData, changedKeys, excludes || /* @__PURE__ */ new Set()) || dataIsChanged;
683
+ dataIsChanged = setData(data, appData, changedKeys, excludes || EMPTY_STRING_SET) || dataIsChanged;
656
684
  if (typeof window.__lark_Debug !== "undefined" && window.__lark_Debug && booted) {
657
685
  for (const p in data) {
658
686
  dataWhereSet[p] = window.location.pathname;
@@ -669,26 +697,19 @@ var State = {
669
697
  }
670
698
  if (dataIsChanged) {
671
699
  if (typeof window.__lark_Debug !== "undefined" && window.__lark_Debug) {
672
- for (const p in changedKeys) {
673
- if (has(changedKeys, p)) {
674
- clearNotify(p);
675
- }
700
+ for (const p of changedKeys) {
701
+ clearNotify(p);
676
702
  }
677
703
  }
678
704
  dataIsChanged = false;
679
- const keys2 = {};
680
- for (const k in changedKeys) {
681
- if (has(changedKeys, k)) {
682
- keys2[k] = 1;
683
- }
684
- }
705
+ const keys2 = changedKeys;
685
706
  stashedChangedKeys = keys2;
686
- changedKeys = {};
687
- emitter.fire(ROUTER_EVENTS.CHANGED, { keys: keys2 });
707
+ changedKeys = /* @__PURE__ */ new Set();
708
+ emitter.fire(RouterEvents.CHANGED, { keys: keys2 });
688
709
  }
689
710
  },
690
711
  /**
691
- * Get diff of what changed in last digest.
712
+ * Get the set of keys changed in the most recent digest.
692
713
  */
693
714
  diff() {
694
715
  return stashedChangedKeys;
@@ -746,6 +767,8 @@ var cachedDefaultPath;
746
767
  var cachedRewrite;
747
768
  var defaultTitle;
748
769
  var frameworkConfig;
770
+ var routeMode = "history";
771
+ var beforeEachGuards = [];
749
772
  function createEmptyLocation() {
750
773
  return {
751
774
  href: "",
@@ -770,7 +793,8 @@ function attachViewAndPath(loc) {
770
793
  cachedRewrite = frameworkConfig.rewrite;
771
794
  }
772
795
  if (!loc.view) {
773
- let path = loc.hash["path"] || cachedDefaultPath || "/";
796
+ const rawPath = routeMode === "history" ? loc.query["path"] || loc.hash["path"] : loc.hash["path"];
797
+ let path = rawPath || cachedDefaultPath || "/";
774
798
  if (cachedRewrite) {
775
799
  path = cachedRewrite(
776
800
  path,
@@ -836,7 +860,16 @@ function getChanged(oldLoc, newLoc) {
836
860
  changedCache.set(tKey, finalResult);
837
861
  return finalResult;
838
862
  }
839
- function updateHash(path, replace) {
863
+ function updateBrowserUrl(path, replace) {
864
+ if (routeMode === "history") {
865
+ const url = path || "/";
866
+ if (replace) {
867
+ window.history.replaceState(null, "", url);
868
+ } else {
869
+ window.history.pushState(null, "", url);
870
+ }
871
+ return;
872
+ }
840
873
  const hashbang = frameworkConfig?.hashbang || "#!";
841
874
  const fullPath = path === "" ? "" : hashbang + path;
842
875
  if (replace) {
@@ -847,9 +880,10 @@ function updateHash(path, replace) {
847
880
  }
848
881
  function updateUrl(path, params, loc, replace, silentFlag, lQuery) {
849
882
  path = toUri(path, params, lQuery);
850
- if (path !== loc.srcHash) {
883
+ const currentSrc = routeMode === "history" ? loc.srcQuery : loc.srcHash;
884
+ if (path !== currentSrc) {
851
885
  silent = silentFlag ? 1 : 0;
852
- updateHash(path, replace);
886
+ updateBrowserUrl(path, replace);
853
887
  }
854
888
  }
855
889
  var Router = {
@@ -863,10 +897,29 @@ var Router = {
863
897
  if (cached) {
864
898
  return cached;
865
899
  }
866
- const srcQuery = href.replace(URL_TRIM_HASH_REGEXP, "");
867
- const srcHash = href.replace(URL_TRIM_QUERY_REGEXP, "");
868
- const query = parseUri(srcQuery);
869
- const hash = parseUri(srcHash);
900
+ let srcQuery;
901
+ let srcHash;
902
+ let query;
903
+ let hash;
904
+ if (routeMode === "history") {
905
+ try {
906
+ const urlObj = new URL(href, window.location.origin);
907
+ srcQuery = urlObj.pathname + urlObj.search;
908
+ srcHash = urlObj.hash ? urlObj.hash.replace(/^#!?/, "") : "";
909
+ query = parseUri(srcQuery);
910
+ hash = srcHash ? parseUri(srcHash) : { path: "", params: {} };
911
+ } catch {
912
+ srcQuery = href.replace(URL_TRIM_HASH_REGEXP, "");
913
+ srcHash = href.replace(URL_TRIM_QUERY_REGEXP, "");
914
+ query = parseUri(srcQuery);
915
+ hash = parseUri(srcHash);
916
+ }
917
+ } else {
918
+ srcQuery = href.replace(URL_TRIM_HASH_REGEXP, "");
919
+ srcHash = href.replace(URL_TRIM_QUERY_REGEXP, "");
920
+ query = parseUri(srcQuery);
921
+ hash = parseUri(srcHash);
922
+ }
870
923
  const params = assign({}, query["params"], hash["params"]);
871
924
  const location = {
872
925
  href,
@@ -900,7 +953,7 @@ var Router = {
900
953
  document.title = defaultTitle || document.title;
901
954
  }
902
955
  emitter2.fire(
903
- ROUTER_EVENTS.CHANGED,
956
+ RouterEvents.CHANGED,
904
957
  lastChanged
905
958
  );
906
959
  }
@@ -933,16 +986,16 @@ var Router = {
933
986
  }
934
987
  const lPath = lastLocation["path"] || "";
935
988
  const lParams = lastLocation["params"];
936
- const lQuery = {};
989
+ const lQuery = /* @__PURE__ */ new Set();
937
990
  for (const k in lastLocation.query["params"]) {
938
- if (has(lastLocation.query["params"], k)) {
939
- lQuery[k] = 1;
991
+ if (hasOwnProperty(lastLocation.query["params"], k)) {
992
+ lQuery.add(k);
940
993
  }
941
994
  }
942
995
  if (tPath) {
943
- if (!has(window, "history")) {
944
- for (const qKey in lQuery) {
945
- if (has(lQuery, qKey) && !has(tParams, qKey)) {
996
+ if (routeMode === "hash" && !hasOwnProperty(window, "history")) {
997
+ for (const qKey of lQuery) {
998
+ if (!hasOwnProperty(tParams, qKey)) {
946
999
  tParams[qKey] = "";
947
1000
  }
948
1001
  }
@@ -951,14 +1004,17 @@ var Router = {
951
1004
  tPath = lPath;
952
1005
  tParams = assign({}, lParams, tParams);
953
1006
  }
954
- updateUrl(
955
- tPath,
956
- tParams,
957
- lastLocation,
958
- replace,
959
- silentFlag,
960
- lQuery
961
- );
1007
+ updateUrl(tPath, tParams, lastLocation, replace, silentFlag, lQuery);
1008
+ },
1009
+ /**
1010
+ * Register an async-friendly navigation guard. See `RouterInterface.beforeEach`.
1011
+ */
1012
+ beforeEach(guard) {
1013
+ beforeEachGuards.push(guard);
1014
+ return () => {
1015
+ const idx = beforeEachGuards.indexOf(guard);
1016
+ if (idx !== -1) beforeEachGuards.splice(idx, 1);
1017
+ };
962
1018
  },
963
1019
  /**
964
1020
  * Join multiple path segments into a single path.
@@ -989,32 +1045,41 @@ var Router = {
989
1045
  return Router;
990
1046
  },
991
1047
  /**
992
- * Internal: bind hashchange and beforeunload events.
1048
+ * Internal: bind routing events and beforeunload.
993
1049
  * Called by Framework.boot().
1050
+ * In hash mode, listens to hashchange + popstate.
1051
+ * In history mode, listens to popstate only.
994
1052
  */
995
1053
  _bind() {
996
1054
  defaultTitle = document.title;
997
- let lastHash = Router.parse().srcHash;
1055
+ const getLocationKey = () => {
1056
+ if (routeMode === "history") {
1057
+ return window.location.pathname + window.location.search;
1058
+ }
1059
+ return Router.parse().srcHash;
1060
+ };
1061
+ let lastKey = getLocationKey();
998
1062
  let suspend;
999
1063
  const watchChange = () => {
1000
1064
  if (suspend) {
1001
1065
  return;
1002
1066
  }
1067
+ hrefCache.clear();
1003
1068
  const loc = Router.parse();
1004
- const newHash = loc.srcHash;
1005
- if (newHash !== lastHash) {
1069
+ const newKey = routeMode === "history" ? loc.srcQuery : loc.srcHash;
1070
+ if (newKey !== lastKey) {
1006
1071
  const changeEvent = {
1007
1072
  p: 0,
1008
1073
  reject: () => {
1009
1074
  changeEvent.p = 1;
1010
1075
  suspend = "";
1011
- updateHash(lastHash);
1076
+ updateBrowserUrl(lastKey);
1012
1077
  },
1013
1078
  resolve: () => {
1014
1079
  changeEvent.p = 1;
1015
- lastHash = newHash;
1080
+ lastKey = newKey;
1016
1081
  suspend = "";
1017
- updateHash(newHash);
1082
+ updateBrowserUrl(newKey);
1018
1083
  Router.diff();
1019
1084
  },
1020
1085
  prevent: () => {
@@ -1022,20 +1087,51 @@ var Router = {
1022
1087
  }
1023
1088
  };
1024
1089
  Router.fire(
1025
- ROUTER_EVENTS.CHANGE,
1090
+ RouterEvents.CHANGE,
1026
1091
  changeEvent
1027
1092
  );
1028
- if (!suspend && !changeEvent.p) {
1093
+ if (suspend || changeEvent.p) {
1094
+ return;
1095
+ }
1096
+ if (beforeEachGuards.length === 0) {
1029
1097
  changeEvent.resolve();
1098
+ return;
1099
+ }
1100
+ const from = lastLocation;
1101
+ const to = loc;
1102
+ const guards = beforeEachGuards.slice();
1103
+ let chain = Promise.resolve(true);
1104
+ for (const guard of guards) {
1105
+ chain = chain.then((prev) => {
1106
+ if (prev === false) return false;
1107
+ return guard(to, from);
1108
+ });
1030
1109
  }
1110
+ chain.then(
1111
+ (result) => {
1112
+ if (changeEvent.p) return;
1113
+ if (result === false) {
1114
+ changeEvent.reject();
1115
+ } else {
1116
+ changeEvent.resolve();
1117
+ }
1118
+ },
1119
+ () => {
1120
+ if (!changeEvent.p) changeEvent.reject();
1121
+ }
1122
+ );
1031
1123
  }
1032
1124
  };
1033
1125
  Router.notify = watchChange;
1034
- window.addEventListener("hashchange", watchChange);
1035
- window.addEventListener("popstate", watchChange);
1126
+ if (routeMode === "history") {
1127
+ window.addEventListener("popstate", watchChange);
1128
+ } else {
1129
+ window.addEventListener("hashchange", watchChange);
1130
+ window.addEventListener("popstate", watchChange);
1131
+ }
1036
1132
  window.addEventListener("beforeunload", (domEvent) => {
1037
1133
  const data = {};
1038
- Router.fire(ROUTER_EVENTS.PAGE_UNLOAD, data);
1134
+ Router.fire(RouterEvents.PAGE_UNLOAD, data);
1039
1135
  const msg = data["msg"];
1040
1136
  if (msg) {
1041
1137
  domEvent.returnValue = msg;
@@ -1048,11 +1144,15 @@ var Router = {
1048
1144
  */
1049
1145
  _setConfig(cfg) {
1050
1146
  frameworkConfig = cfg;
1147
+ routeMode = cfg.routeMode || "history";
1051
1148
  }
1052
1149
  };
1053
1150
  function markRouterBooted() {
1054
1151
  booted2 = true;
1055
1152
  }
1153
+ function getRouteMode() {
1154
+ return routeMode;
1155
+ }
1056
1156
 
1057
1157
  // src/event-delegator.ts
1058
1158
  var rootEvents = {};
@@ -1081,13 +1181,17 @@ function parseEventInfo(eventInfo) {
1081
1181
  }
1082
1182
  function findFrameInfo(current, eventType) {
1083
1183
  const eventInfos = [];
1084
- let begin = current;
1085
1184
  const info = current.getAttribute(`@${eventType}`);
1185
+ const hasSelectorEvents = !!selectorEvents[eventType];
1186
+ if (!info && !hasSelectorEvents) {
1187
+ return eventInfos;
1188
+ }
1189
+ let begin = current;
1086
1190
  let match;
1087
1191
  if (info) {
1088
1192
  match = parseEventInfo(info);
1089
1193
  }
1090
- if (match && !match.id || selectorEvents[eventType]) {
1194
+ if (match && !match.id || hasSelectorEvents) {
1091
1195
  let selectorFrameId = "#";
1092
1196
  let backtrace = 0;
1093
1197
  while (begin && begin !== document.body) {
@@ -1260,7 +1364,7 @@ var EventDelegator = {
1260
1364
  };
1261
1365
 
1262
1366
  // src/vdom.ts
1263
- var WrapMeta = {
1367
+ var wrapMeta = {
1264
1368
  option: [1, "<select multiple>"],
1265
1369
  thead: [1, "<table>"],
1266
1370
  col: [2, "<table><colgroup>"],
@@ -1272,9 +1376,9 @@ var WrapMeta = {
1272
1376
  math: [1, '<math xmlns="' + MATH_NS + '">'],
1273
1377
  _: [0, ""]
1274
1378
  };
1275
- WrapMeta["optgroup"] = WrapMeta["option"];
1276
- WrapMeta["tbody"] = WrapMeta["tfoot"] = WrapMeta["colgroup"] = WrapMeta["caption"] = WrapMeta["thead"];
1277
- WrapMeta["th"] = WrapMeta["td"];
1379
+ wrapMeta["optgroup"] = wrapMeta["option"];
1380
+ wrapMeta["tbody"] = wrapMeta["tfoot"] = wrapMeta["colgroup"] = wrapMeta["caption"] = wrapMeta["thead"];
1381
+ wrapMeta["th"] = wrapMeta["td"];
1278
1382
  var VDoc = document.implementation.createHTMLDocument("");
1279
1383
  var VBase = VDoc.createElement("base");
1280
1384
  VBase.href = document.location.href;
@@ -1285,16 +1389,12 @@ var VDomSpecials = {
1285
1389
  OPTION: ["selected"]
1286
1390
  };
1287
1391
  function vdomUnmountFrames(frame, node) {
1288
- if (node.nodeType === 1) {
1289
- const el = node;
1290
- const id = el.getAttribute("id");
1291
- if (id) {
1292
- frame.unmountZone(id);
1293
- const children = frame.children();
1294
- if (children.includes(id)) {
1295
- frame.unmountFrame(id);
1296
- }
1297
- }
1392
+ if (!(node instanceof Element)) return;
1393
+ const id = node.getAttribute("id");
1394
+ if (!id) return;
1395
+ frame.unmountZone(id);
1396
+ if (frame.children().includes(id)) {
1397
+ frame.unmountFrame(id);
1298
1398
  }
1299
1399
  }
1300
1400
  function vdomGetNode(html, refNode) {
@@ -1309,7 +1409,7 @@ function vdomGetNode(html, refNode) {
1309
1409
  const match = TAG_NAME_REGEXP.exec(html);
1310
1410
  tag = match ? match[1] : "";
1311
1411
  }
1312
- const wrap = WrapMeta[tag] || WrapMeta["_"];
1412
+ const wrap = wrapMeta[tag] || wrapMeta["_"];
1313
1413
  tmp.innerHTML = wrap[1] + html;
1314
1414
  let j = wrap[0];
1315
1415
  while (j--) {
@@ -1326,7 +1426,7 @@ function vdomGetCompareKey(node) {
1326
1426
  }
1327
1427
  let key = el.autoId ? "" : el.getAttribute("id") || void 0;
1328
1428
  if (!key) {
1329
- key = el.getAttribute(LARK_KEYS.DIFF_KEY) || void 0;
1429
+ key = el.getAttribute(LarkInnerKeys.DIFF_KEY) || void 0;
1330
1430
  }
1331
1431
  if (!key) {
1332
1432
  const larkView = el.getAttribute(LARK_VIEW);
@@ -1339,8 +1439,7 @@ function vdomGetCompareKey(node) {
1339
1439
  return key;
1340
1440
  }
1341
1441
  function vdomSpecialDiff(oldNode, newNode) {
1342
- const nodeName = oldNode.nodeName;
1343
- const specials = VDomSpecials[nodeName];
1442
+ const specials = VDomSpecials[oldNode.nodeName];
1344
1443
  if (!specials) return 0;
1345
1444
  const oldEl = oldNode;
1346
1445
  const newEl = newNode;
@@ -1389,13 +1488,17 @@ function vdomSetChildNodes(oldParent, newParent, ref, frame, keys_) {
1389
1488
  let oldNode = oldParent.lastChild;
1390
1489
  let newNode = newParent.firstChild;
1391
1490
  let extra = 0;
1392
- const keyedNodes = {};
1393
- const newKeyedNodes = {};
1491
+ const keyedNodes = /* @__PURE__ */ new Map();
1492
+ const newKeyedNodes = /* @__PURE__ */ new Map();
1394
1493
  while (oldNode) {
1395
1494
  extra++;
1396
1495
  const nodeKey = vdomGetCompareKey(oldNode);
1397
1496
  if (nodeKey) {
1398
- const bucket = keyedNodes[nodeKey] || (keyedNodes[nodeKey] = []);
1497
+ let bucket = keyedNodes.get(nodeKey);
1498
+ if (!bucket) {
1499
+ bucket = [];
1500
+ keyedNodes.set(nodeKey, bucket);
1501
+ }
1399
1502
  bucket.push(oldNode);
1400
1503
  }
1401
1504
  oldNode = oldNode.previousSibling;
@@ -1403,7 +1506,7 @@ function vdomSetChildNodes(oldParent, newParent, ref, frame, keys_) {
1403
1506
  while (newNode) {
1404
1507
  const nodeKey = vdomGetCompareKey(newNode);
1405
1508
  if (nodeKey) {
1406
- newKeyedNodes[nodeKey] = (newKeyedNodes[nodeKey] || 0) + 1;
1509
+ newKeyedNodes.set(nodeKey, (newKeyedNodes.get(nodeKey) ?? 0) + 1);
1407
1510
  }
1408
1511
  newNode = newNode.nextSibling;
1409
1512
  }
@@ -1414,23 +1517,25 @@ function vdomSetChildNodes(oldParent, newParent, ref, frame, keys_) {
1414
1517
  const tempNew = newNode;
1415
1518
  newNode = newNode.nextSibling;
1416
1519
  const nodeKey = vdomGetCompareKey(tempNew);
1417
- let foundNode = nodeKey ? keyedNodes[nodeKey] : void 0;
1520
+ let foundNode = nodeKey ? keyedNodes.get(nodeKey) : void 0;
1418
1521
  if (foundNode && (foundNode = foundNode.slice()) && foundNode.length) {
1419
1522
  const matched = foundNode.pop();
1420
1523
  while (matched !== oldNode) {
1421
- const next = oldNode?.nextSibling ?? null;
1524
+ if (!oldNode) break;
1525
+ const next = oldNode.nextSibling;
1422
1526
  oldParent.appendChild(oldNode);
1423
1527
  oldNode = next;
1424
1528
  }
1425
1529
  oldNode = matched.nextSibling;
1426
- if (nodeKey && newKeyedNodes[nodeKey]) {
1427
- newKeyedNodes[nodeKey]--;
1530
+ if (nodeKey) {
1531
+ const c = newKeyedNodes.get(nodeKey);
1532
+ if (c) newKeyedNodes.set(nodeKey, c - 1);
1428
1533
  }
1429
1534
  vdomSetNode(matched, tempNew, oldParent, ref, frame, keys_);
1430
1535
  } else if (oldNode) {
1431
1536
  const tempOld2 = oldNode;
1432
1537
  const oldKey = vdomGetCompareKey(tempOld2);
1433
- if (oldKey && keyedNodes[oldKey] && newKeyedNodes[oldKey]) {
1538
+ if (oldKey && keyedNodes.has(oldKey) && newKeyedNodes.get(oldKey)) {
1434
1539
  extra++;
1435
1540
  ref.hasChanged = 1;
1436
1541
  ref.domOps.push([8, oldParent, tempNew, tempOld2]);
@@ -1454,17 +1559,21 @@ function vdomSetChildNodes(oldParent, newParent, ref, frame, keys_) {
1454
1559
  }
1455
1560
  }
1456
1561
  function vdomSetNode(oldNode, newNode, oldParent, ref, frame, keys_) {
1457
- if (vdomSpecialDiff(oldNode, newNode) || oldNode.nodeType === 1 && oldNode.hasAttribute(LARK_KEYS.VIEW_KEY) || !(oldNode.isEqualNode && oldNode.isEqualNode(newNode))) {
1562
+ const oldAsEl = oldNode instanceof Element ? oldNode : null;
1563
+ const newAsEl = newNode instanceof Element ? newNode : null;
1564
+ const hasViewKey = !!oldAsEl?.hasAttribute(LarkInnerKeys.VIEW_KEY);
1565
+ const equalAsNodes = oldAsEl !== null && newAsEl !== null && oldAsEl.isEqualNode && oldAsEl.isEqualNode(newAsEl);
1566
+ if (vdomSpecialDiff(oldNode, newNode) || hasViewKey || !equalAsNodes) {
1458
1567
  if (oldNode.nodeType === newNode.nodeType && oldNode.nodeName === newNode.nodeName) {
1459
- if (oldNode.nodeType === 1) {
1460
- const oldEl = oldNode;
1461
- const newEl = newNode;
1462
- const staticKey = newEl.getAttribute(LARK_KEYS.DIFF_KEY);
1463
- if (staticKey && staticKey === oldEl.getAttribute(LARK_KEYS.DIFF_KEY)) {
1568
+ if (oldAsEl !== null && newAsEl !== null) {
1569
+ const oldEl = oldAsEl;
1570
+ const newEl = newAsEl;
1571
+ const staticKey = newEl.getAttribute(LarkInnerKeys.DIFF_KEY);
1572
+ if (staticKey && staticKey === oldEl.getAttribute(LarkInnerKeys.DIFF_KEY)) {
1464
1573
  return;
1465
1574
  }
1466
1575
  const newLarkView = newEl.getAttribute(LARK_VIEW);
1467
- const updateAttribute = !newEl.getAttribute(LARK_KEYS.ATTR_KEY) || newEl.getAttribute(LARK_KEYS.ATTR_KEY) !== oldEl.getAttribute(LARK_KEYS.ATTR_KEY);
1576
+ const updateAttribute = !newEl.getAttribute(LarkInnerKeys.ATTR_KEY) || newEl.getAttribute(LarkInnerKeys.ATTR_KEY) !== oldEl.getAttribute(LarkInnerKeys.ATTR_KEY);
1468
1577
  let updateChildren = true;
1469
1578
  if (newLarkView) {
1470
1579
  const oldFrameId = oldEl.getAttribute("id") || "";
@@ -1565,7 +1674,8 @@ function encodeQ(v) {
1565
1674
  }
1566
1675
 
1567
1676
  // src/updater.ts
1568
- function updaterRef(refData, value, key) {
1677
+ function updaterRef(refDataIn, value, key) {
1678
+ const refData = refDataIn;
1569
1679
  const counter = refData[SPLITTER];
1570
1680
  for (let i = counter; --i; ) {
1571
1681
  key = SPLITTER + i;
@@ -1585,13 +1695,19 @@ var Updater = class {
1585
1695
  /** Ref data for template rendering */
1586
1696
  refData;
1587
1697
  /** Changed keys in current digest cycle */
1588
- changedKeys = {};
1698
+ changedKeys = /* @__PURE__ */ new Set();
1589
1699
  /** Whether data has changed since last digest */
1590
1700
  hasChangedFlag = 0;
1591
- /** Digesting queue: supports re-digest during digest */
1701
+ /**
1702
+ * Digesting queue: supports re-digest during digest.
1703
+ * Holds pending callbacks; `null` is used as a sentinel marking the start
1704
+ * of an active digest cycle, so `runDigest` can detect re-entrant calls.
1705
+ */
1592
1706
  digestingQueue = [];
1593
- /** Snapshot JSON string for altered() detection */
1594
- snapshotJson;
1707
+ /** Monotonically increasing version, bumped each time data actually changes. */
1708
+ version = 0;
1709
+ /** Snapshot of `version` taken by `snapshot()`, used by `altered()`. */
1710
+ snapshotVersion;
1595
1711
  constructor(viewId) {
1596
1712
  this.viewId = viewId;
1597
1713
  this.data = { vId: viewId };
@@ -1619,7 +1735,16 @@ var Updater = class {
1619
1735
  * Returns this for chaining.
1620
1736
  */
1621
1737
  set(data, excludes) {
1622
- this.hasChangedFlag = setData(data, this.data, this.changedKeys, excludes || /* @__PURE__ */ new Set()) || this.hasChangedFlag ? 1 : 0;
1738
+ const changed = setData(
1739
+ data,
1740
+ this.data,
1741
+ this.changedKeys,
1742
+ excludes || EMPTY_STRING_SET
1743
+ );
1744
+ if (changed) {
1745
+ this.version++;
1746
+ this.hasChangedFlag = 1;
1747
+ }
1623
1748
  return this;
1624
1749
  }
1625
1750
  /**
@@ -1655,13 +1780,13 @@ var Updater = class {
1655
1780
  const keys2 = this.changedKeys;
1656
1781
  const changed = this.hasChangedFlag;
1657
1782
  this.hasChangedFlag = 0;
1658
- this.changedKeys = {};
1783
+ this.changedKeys = /* @__PURE__ */ new Set();
1659
1784
  const frame = Frame.get(this.viewId);
1660
1785
  const view = frame?.view;
1661
1786
  const node = getById(this.viewId);
1662
- if (changed && view && node && view.signature > 0) {
1787
+ if (changed && view && node && view.signature > 0 && frame) {
1663
1788
  const template = view.template;
1664
- if (template && typeof template === "function") {
1789
+ if (typeof template === "function") {
1665
1790
  const html = template(
1666
1791
  this.data,
1667
1792
  this.viewId,
@@ -1698,43 +1823,65 @@ var Updater = class {
1698
1823
  }
1699
1824
  }
1700
1825
  /**
1701
- * Save a snapshot of current data for altered() detection.
1826
+ * Save a snapshot of the current data version for `altered()` detection.
1827
+ * Cheap O(1) — records the current monotonic version, no serialization.
1702
1828
  */
1703
1829
  snapshot() {
1704
- this.snapshotJson = JSON.stringify(this.data);
1830
+ this.snapshotVersion = this.version;
1705
1831
  return this;
1706
1832
  }
1707
1833
  /**
1708
- * Check if data has changed since last snapshot.
1834
+ * Check whether data has changed since the last snapshot.
1835
+ * Returns undefined when no snapshot has been taken yet.
1709
1836
  */
1710
1837
  altered() {
1711
- if (this.snapshotJson) {
1712
- return this.snapshotJson !== JSON.stringify(this.data);
1713
- }
1714
- return void 0;
1838
+ if (this.snapshotVersion === void 0) return void 0;
1839
+ return this.version !== this.snapshotVersion;
1715
1840
  }
1716
1841
  /**
1717
- * Translate data references (SPLITTER-prefixed values).
1842
+ * Translate a refData reference back to its original value.
1843
+ *
1844
+ * The ref protocol is `SPLITTER` + ascii decimal digits — the exact format
1845
+ * emitted by `updaterRef`. We require that exact shape so a user-supplied
1846
+ * string that merely begins with SPLITTER is never accidentally resolved
1847
+ * (or mishandled as a "missing ref").
1718
1848
  */
1719
1849
  translate(data) {
1720
- if (typeof data === "string" && data[0] === SPLITTER) {
1721
- return has(this.refData, data) ? this.refData[data] : data;
1850
+ if (typeof data !== "string") return data;
1851
+ if (data.length < 2 || data[0] !== SPLITTER) return data;
1852
+ for (let i = 1; i < data.length; i++) {
1853
+ const c = data.charCodeAt(i);
1854
+ if (c < 48 || c > 57) return data;
1722
1855
  }
1723
- return data;
1856
+ return hasOwnProperty(this.refData, data) ? this.refData[data] : data;
1724
1857
  }
1725
1858
  /**
1726
- * Parse expression with data context.
1859
+ * Resolve a dotted property path against refData.
1860
+ *
1861
+ * Only safe property-path syntax is supported: `a`, `a.b`, `a.b.c`.
1862
+ * Numeric literals (e.g. `1`, `1.5`) are returned as numbers. Anything else
1863
+ * returns `undefined` — we no longer evaluate arbitrary JavaScript via
1864
+ * `new Function`, so the method is CSP-safe and cannot be used as an
1865
+ * injection vector.
1727
1866
  */
1728
1867
  parse(expr) {
1729
- try {
1730
- const fn = new Function("data", `with(data) { return ${expr}; }`);
1731
- return fn(this.refData);
1732
- } catch {
1868
+ const trimmed = expr.trim();
1869
+ if (!trimmed) return void 0;
1870
+ if (/^-?\d+(?:\.\d+)?$/.test(trimmed)) {
1871
+ return Number(trimmed);
1872
+ }
1873
+ if (!/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*$/.test(trimmed)) {
1733
1874
  return void 0;
1734
1875
  }
1876
+ let cur = this.refData;
1877
+ for (const segment of trimmed.split(".")) {
1878
+ if (cur == null || typeof cur !== "object") return void 0;
1879
+ cur = cur[segment];
1880
+ }
1881
+ return cur;
1735
1882
  }
1736
1883
  /**
1737
- * Get changed keys (for external inspection).
1884
+ * Get the set of keys changed since the last digest (for external inspection).
1738
1885
  */
1739
1886
  getChangedKeys() {
1740
1887
  return this.changedKeys;
@@ -1781,30 +1928,31 @@ var View = class _View {
1781
1928
  // ============================================================
1782
1929
  // Getters for prototype-stored event maps
1783
1930
  // ============================================================
1931
+ /** Prototype-stored event maps shape (set by View.prepare). */
1932
+ get protoEventState() {
1933
+ return Object.getPrototypeOf(this);
1934
+ }
1784
1935
  /**
1785
1936
  * Event bitmask map: eventType -> bitmask (1=root, 2=selector).
1786
1937
  * Read from prototype ($evtObjMap) set by View.prepare.
1787
1938
  * Using a getter avoids ES6 class field shadowing the prototype value.
1788
1939
  */
1789
1940
  get eventObjectMap() {
1790
- const proto = Object.getPrototypeOf(this);
1791
- return proto["$evtObjMap"] || {};
1941
+ return this.protoEventState.$evtObjMap ?? {};
1792
1942
  }
1793
1943
  /**
1794
1944
  * Selector event map: eventType -> selector list.
1795
1945
  * Read from prototype ($selMap) set by View.prepare.
1796
1946
  */
1797
1947
  get eventSelectorMap() {
1798
- const proto = Object.getPrototypeOf(this);
1799
- return proto["$selMap"] || {};
1948
+ return this.protoEventState.$selMap ?? {};
1800
1949
  }
1801
1950
  /**
1802
1951
  * Global event list: [{handler, element, eventName, modifiers}].
1803
1952
  * Read from prototype ($globalEvtList) set by View.prepare.
1804
1953
  */
1805
1954
  get globalEventList() {
1806
- const proto = Object.getPrototypeOf(this);
1807
- return proto["$globalEvtList"] || [];
1955
+ return this.protoEventState.$globalEvtList ?? [];
1808
1956
  }
1809
1957
  // ============================================================
1810
1958
  // Instance lifecycle methods
@@ -1839,13 +1987,17 @@ var View = class _View {
1839
1987
  // ============================================================
1840
1988
  // Update methods
1841
1989
  // ============================================================
1990
+ /** Get the owning frame, asserting it has been bound. */
1991
+ get ownerFrame() {
1992
+ return this.owner;
1993
+ }
1842
1994
  /**
1843
1995
  * Notify view that HTML update is about to begin.
1844
1996
  * Unmounts child frames in the update zone.
1845
1997
  */
1846
1998
  beginUpdate(id) {
1847
1999
  if (this.signature > 0 && this.endUpdatePending !== void 0) {
1848
- this.owner.unmountZone(id, true);
2000
+ this.ownerFrame.unmountZone(id, true);
1849
2001
  }
1850
2002
  }
1851
2003
  /**
@@ -1863,7 +2015,7 @@ var View = class _View {
1863
2015
  this.endUpdatePending = 1;
1864
2016
  this.rendered = true;
1865
2017
  }
1866
- const ownerFrame = this.owner;
2018
+ const ownerFrame = this.ownerFrame;
1867
2019
  ownerFrame.mountZone(updateId, inner);
1868
2020
  if (!flag) {
1869
2021
  setTimeout(
@@ -1902,11 +2054,12 @@ var View = class _View {
1902
2054
  const loc = this.locationObserved;
1903
2055
  loc.flag = 1;
1904
2056
  if (typeof params === "object" && !Array.isArray(params)) {
1905
- if (params["path"]) {
2057
+ const opts = params;
2058
+ if (opts["path"]) {
1906
2059
  observePath = true;
1907
2060
  }
1908
- const paramKeys = params["params"];
1909
- if (paramKeys) {
2061
+ const paramKeys = opts["params"];
2062
+ if (typeof paramKeys === "string" || Array.isArray(paramKeys)) {
1910
2063
  params = paramKeys;
1911
2064
  }
1912
2065
  }
@@ -1970,24 +2123,16 @@ var View = class _View {
1970
2123
  */
1971
2124
  leaveTip(message, condition) {
1972
2125
  const changeListener = function(e) {
1973
- const isRouterChange = e.type === ROUTER_EVENTS.CHANGE;
2126
+ const isRouterChange = e.type === RouterEvents.CHANGE;
1974
2127
  const aKey = isRouterChange ? "a" : "b";
1975
2128
  const bKey = isRouterChange ? "b" : "a";
1976
2129
  if (changeListener[aKey]) {
1977
- if (typeof e.prevent === "function") {
1978
- e.prevent();
1979
- }
1980
- if (typeof e.reject === "function") {
1981
- e.reject();
1982
- }
2130
+ e.prevent?.();
2131
+ e.reject?.();
1983
2132
  } else if (condition()) {
1984
- if (typeof e.prevent === "function") {
1985
- e.prevent();
1986
- }
2133
+ e.prevent?.();
1987
2134
  changeListener[bKey] = 1;
1988
- if (typeof e.resolve === "function") {
1989
- e.resolve();
1990
- }
2135
+ e.resolve?.();
1991
2136
  }
1992
2137
  };
1993
2138
  const unloadListener = (e) => {
@@ -1995,14 +2140,12 @@ var View = class _View {
1995
2140
  e["msg"] = message;
1996
2141
  }
1997
2142
  };
1998
- const changeFn = changeListener;
1999
- const unloadFn = unloadListener;
2000
- Router.on(ROUTER_EVENTS.CHANGE, changeFn);
2001
- Router.on(ROUTER_EVENTS.PAGE_UNLOAD, unloadFn);
2002
- this.on("unload", changeFn);
2143
+ Router.on(RouterEvents.CHANGE, changeListener);
2144
+ Router.on(RouterEvents.PAGE_UNLOAD, unloadListener);
2145
+ this.on("unload", changeListener);
2003
2146
  this.on("destroy", () => {
2004
- Router.off(ROUTER_EVENTS.CHANGE, changeFn);
2005
- Router.off(ROUTER_EVENTS.PAGE_UNLOAD, unloadFn);
2147
+ Router.off(RouterEvents.CHANGE, changeListener);
2148
+ Router.off(RouterEvents.PAGE_UNLOAD, unloadListener);
2006
2149
  });
2007
2150
  }
2008
2151
  // ============================================================
@@ -2032,7 +2175,7 @@ var View = class _View {
2032
2175
  _View.mergeMixins(mixins, oView, ctors);
2033
2176
  }
2034
2177
  for (const p in proto) {
2035
- if (!has(proto, p)) continue;
2178
+ if (!hasOwnProperty(proto, p)) continue;
2036
2179
  const currentFn = proto[p];
2037
2180
  if (typeof currentFn !== "function") continue;
2038
2181
  const matches = p.match(VIEW_EVENT_METHOD_REGEXP);
@@ -2078,16 +2221,16 @@ var View = class _View {
2078
2221
  const existingFn = proto[combinedKey];
2079
2222
  if (!existingFn) {
2080
2223
  proto[combinedKey] = currentFn;
2081
- } else {
2224
+ } else if (typeof existingFn === "function") {
2082
2225
  const mixinFn = currentFn;
2083
2226
  const existingMixin = existingFn;
2084
2227
  if (existingMixin.marker) {
2085
2228
  if (mixinFn.marker) {
2086
2229
  proto[combinedKey] = _View.processMixinsSameEvent(
2087
- currentFn,
2088
- existingFn
2230
+ mixinFn,
2231
+ existingMixin
2089
2232
  );
2090
- } else if (has(proto, p)) {
2233
+ } else if (hasOwnProperty(proto, p)) {
2091
2234
  proto[combinedKey] = currentFn;
2092
2235
  }
2093
2236
  }
@@ -2110,7 +2253,7 @@ var View = class _View {
2110
2253
  const selectorObject = view.eventSelectorMap;
2111
2254
  const eventsList = view.globalEventList;
2112
2255
  for (const e in eventsObject) {
2113
- if (has(eventsObject, e)) {
2256
+ if (hasOwnProperty(eventsObject, e)) {
2114
2257
  if (destroy) {
2115
2258
  EventDelegator.unbind(e, !!selectorObject[e]);
2116
2259
  } else {
@@ -2153,7 +2296,7 @@ var View = class _View {
2153
2296
  static destroyAllResources(view, lastly) {
2154
2297
  const cache = view.resources;
2155
2298
  for (const p in cache) {
2156
- if (has(cache, p)) {
2299
+ if (hasOwnProperty(cache, p)) {
2157
2300
  const entry = cache[p];
2158
2301
  if (lastly || entry.destroyOnRender) {
2159
2302
  _View.destroyResource(cache, p, true);
@@ -2183,13 +2326,16 @@ var View = class _View {
2183
2326
  static wrapMethod(proto, fnName, shortKey) {
2184
2327
  const originalFn = proto[fnName];
2185
2328
  if (typeof originalFn !== "function") return;
2329
+ const originalAsFn = originalFn;
2186
2330
  const wrapped = function(...args) {
2187
2331
  if (this.signature > 0) {
2188
2332
  this.signature++;
2189
2333
  this.fire("render");
2190
2334
  _View.destroyAllResources(this, false);
2191
- const instanceFn = typeof this[fnName] === "function" ? this[fnName] : originalFn;
2192
- const fnToCall = instanceFn === wrapped ? originalFn : instanceFn;
2335
+ const lookup = this;
2336
+ const candidate = lookup[fnName];
2337
+ const instanceFn = typeof candidate === "function" ? candidate : originalAsFn;
2338
+ const fnToCall = instanceFn === wrapped ? originalAsFn : instanceFn;
2193
2339
  return funcWithTry(fnToCall, args, this, noop);
2194
2340
  }
2195
2341
  return void 0;
@@ -2203,19 +2349,18 @@ var View = class _View {
2203
2349
  */
2204
2350
  static processMixinsSameEvent(additional, exist) {
2205
2351
  let temp;
2206
- const existMixin = exist;
2207
- if (existMixin.handlerList) {
2208
- temp = existMixin;
2352
+ if (exist.handlerList) {
2353
+ temp = exist;
2209
2354
  } else {
2210
- temp = function(...e) {
2211
- funcWithTry(temp.handlerList ?? [], e, this, noop);
2355
+ const merged = function(...e) {
2356
+ funcWithTry(merged.handlerList ?? [], e, this, noop);
2212
2357
  };
2213
- temp.handlerList = [exist];
2214
- temp.marker = 1;
2358
+ merged.handlerList = [exist];
2359
+ merged.marker = 1;
2360
+ temp = merged;
2215
2361
  }
2216
- const additionalMixin = additional;
2217
2362
  temp.handlerList = (temp.handlerList ?? []).concat(
2218
- additionalMixin.handlerList ?? [additional]
2363
+ additional.handlerList ?? [additional]
2219
2364
  );
2220
2365
  return temp;
2221
2366
  }
@@ -2227,29 +2372,29 @@ var View = class _View {
2227
2372
  const temp = {};
2228
2373
  for (const node of mixins) {
2229
2374
  for (const p in node) {
2230
- if (!has(node, p)) continue;
2375
+ if (!hasOwnProperty(node, p)) continue;
2231
2376
  const fn = node[p];
2377
+ if (typeof fn !== "function") continue;
2378
+ const mixinFn = fn;
2232
2379
  const exist = temp[p];
2233
2380
  if (p === "make") {
2234
- ctors.push(fn);
2381
+ ctors.push(mixinFn);
2235
2382
  continue;
2236
2383
  }
2237
2384
  if (VIEW_EVENT_METHOD_REGEXP.test(p)) {
2238
2385
  if (exist) {
2239
- temp[p] = _View.processMixinsSameEvent(fn, exist);
2386
+ temp[p] = _View.processMixinsSameEvent(mixinFn, exist);
2240
2387
  } else {
2241
- fn.marker = 1;
2242
- temp[p] = fn;
2243
- }
2244
- } else {
2245
- if (!exist) {
2246
- temp[p] = fn;
2388
+ mixinFn.marker = 1;
2389
+ temp[p] = mixinFn;
2247
2390
  }
2391
+ } else if (!exist) {
2392
+ temp[p] = mixinFn;
2248
2393
  }
2249
2394
  }
2250
2395
  }
2251
2396
  for (const p in temp) {
2252
- if (!has(proto, p)) {
2397
+ if (!hasOwnProperty(proto, p)) {
2253
2398
  proto[p] = temp[p];
2254
2399
  }
2255
2400
  }
@@ -2282,19 +2427,20 @@ var View = class _View {
2282
2427
  * - Event method patterns: `'name<click>'` etc.
2283
2428
  */
2284
2429
  static extend(props, statics) {
2285
- props = props || {};
2286
- const make = props["make"];
2430
+ const definedProps = props ?? {};
2431
+ const make = definedProps["make"];
2287
2432
  const ctors = [];
2288
- if (make) {
2433
+ if (typeof make === "function") {
2289
2434
  ctors.push(make);
2290
2435
  }
2291
2436
  const ParentView = this;
2292
2437
  const ChildView = class extends ParentView {
2293
2438
  constructor(nodeId, ownerFrame, initParams, node, mixinCtors) {
2294
2439
  super(nodeId, ownerFrame, initParams, node, []);
2295
- for (const key in props) {
2296
- if (has(props, key) && key !== "make" && key !== "render") {
2297
- this[key] = props[key];
2440
+ const instanceProps = this;
2441
+ for (const key in definedProps) {
2442
+ if (hasOwnProperty(definedProps, key) && key !== "make" && key !== "render") {
2443
+ instanceProps[key] = definedProps[key];
2298
2444
  }
2299
2445
  }
2300
2446
  this.id = nodeId;
@@ -2314,15 +2460,16 @@ var View = class _View {
2314
2460
  }
2315
2461
  };
2316
2462
  const proto = ChildView.prototype;
2317
- for (const key in props) {
2318
- if (has(props, key) && key !== "make") {
2319
- proto[key] = props[key];
2463
+ for (const key in definedProps) {
2464
+ if (hasOwnProperty(definedProps, key) && key !== "make") {
2465
+ proto[key] = definedProps[key];
2320
2466
  }
2321
2467
  }
2322
2468
  if (statics) {
2469
+ const staticTarget = ChildView;
2323
2470
  for (const key in statics) {
2324
- if (has(statics, key)) {
2325
- ChildView[key] = statics[key];
2471
+ if (hasOwnProperty(statics, key)) {
2472
+ staticTarget[key] = statics[key];
2326
2473
  }
2327
2474
  }
2328
2475
  }
@@ -2337,14 +2484,87 @@ var View = class _View {
2337
2484
  return this;
2338
2485
  }
2339
2486
  };
2487
+ function defineView(props, statics) {
2488
+ return View.extend(props, statics);
2489
+ }
2490
+
2491
+ // src/module-loader.ts
2492
+ var config = {
2493
+ rootId: "root",
2494
+ routeMode: "history",
2495
+ hashbang: "#!",
2496
+ error: (error) => {
2497
+ throw error;
2498
+ }
2499
+ };
2500
+ function use(names, callback) {
2501
+ const nameList = typeof names === "string" ? [names] : names;
2502
+ const loadPromise = (() => {
2503
+ if (config.require) {
2504
+ const result = config.require(nameList);
2505
+ if (result && typeof result.then === "function") {
2506
+ return result;
2507
+ }
2508
+ return Promise.resolve([]);
2509
+ }
2510
+ return Promise.all(
2511
+ nameList.map((name) => {
2512
+ const importPath = name.startsWith(".") || name.startsWith("/") ? name : `./${name}`;
2513
+ return import(
2514
+ /* @vite-ignore */
2515
+ /* webpackIgnore: true */
2516
+ importPath
2517
+ ).then((mod) => {
2518
+ return mod && (mod["__esModule"] || // For Webpack
2519
+ typeof mod["default"] === "function") ? mod["default"] : mod;
2520
+ }).catch((err) => {
2521
+ const errorHandler = config.error;
2522
+ if (errorHandler) {
2523
+ errorHandler(err instanceof Error ? err : new Error(String(err)));
2524
+ }
2525
+ return void 0;
2526
+ });
2527
+ })
2528
+ );
2529
+ })();
2530
+ if (callback) {
2531
+ loadPromise.then((modules) => {
2532
+ callback(...modules);
2533
+ });
2534
+ }
2535
+ return loadPromise;
2536
+ }
2537
+
2538
+ // src/view-registry.ts
2539
+ var viewClassRegistry = {};
2540
+ function getViewClass(path) {
2541
+ return viewClassRegistry[path];
2542
+ }
2543
+ function registerViewClass(viewPath, ViewClass) {
2544
+ const parsed = parseUri(viewPath);
2545
+ const path = parsed.path;
2546
+ if (path) {
2547
+ viewClassRegistry[path] = ViewClass;
2548
+ }
2549
+ }
2550
+ function invalidateViewClass(viewPath) {
2551
+ const parsed = parseUri(viewPath);
2552
+ const path = parsed.path;
2553
+ if (path) {
2554
+ Reflect.deleteProperty(viewClassRegistry, path);
2555
+ }
2556
+ }
2557
+ function getViewClassRegistry() {
2558
+ return viewClassRegistry;
2559
+ }
2340
2560
 
2341
2561
  // src/frame.ts
2342
2562
  var frameRegistry = /* @__PURE__ */ new Map();
2343
2563
  var rootFrame;
2344
2564
  var globalAlter;
2565
+ var MAX_FRAME_POOL = 64;
2345
2566
  var frameCache = [];
2346
2567
  var staticEmitter = new EventEmitter();
2347
- var viewClassRegistry = {};
2348
2568
  var Frame = class _Frame extends EventEmitter {
2349
2569
  /** Frame ID (same as owner DOM element ID) */
2350
2570
  id;
@@ -2359,8 +2579,8 @@ var Frame = class _Frame extends EventEmitter {
2359
2579
  childrenCount = 0;
2360
2580
  /** Ready count (children that have fired 'created') */
2361
2581
  readyCount = 0;
2362
- /** Ready map: id -> 1 */
2363
- readyMap = {};
2582
+ /** Set of child frame IDs that have fired 'created' */
2583
+ readyMap = /* @__PURE__ */ new Set();
2364
2584
  /** View instance */
2365
2585
  viewInstance;
2366
2586
  /** Get view instance (read-only) */
@@ -2431,13 +2651,30 @@ var Frame = class _Frame extends EventEmitter {
2431
2651
  this.viewPath = viewPath;
2432
2652
  const params = parsed["params"];
2433
2653
  translateQuery(pId || this.id, viewPath, params);
2654
+ const initParams = { ...params };
2434
2655
  if (viewInitParams) {
2435
- assign(params, viewInitParams);
2656
+ assign(initParams, viewInitParams);
2436
2657
  }
2437
2658
  const sign = this.signature;
2438
- if (viewClassRegistry[viewClassName]) {
2439
- this.doMountView(viewClassRegistry[viewClassName], params, node, sign);
2659
+ const registered = getViewClass(viewClassName);
2660
+ if (registered) {
2661
+ this.doMountView(registered, initParams, node, sign);
2662
+ return;
2440
2663
  }
2664
+ use(viewClassName, (ViewClass) => {
2665
+ if (sign !== this.signature) return;
2666
+ if (typeof ViewClass === "function") {
2667
+ const ViewClassTyped = ViewClass;
2668
+ registerViewClass(viewClassName, ViewClassTyped);
2669
+ this.doMountView(ViewClassTyped, initParams, node, sign);
2670
+ } else {
2671
+ const error = new Error(`Cannot load view: ${viewClassName}`);
2672
+ const errorHandler = config.error;
2673
+ if (errorHandler) {
2674
+ errorHandler(error);
2675
+ }
2676
+ }
2677
+ });
2441
2678
  }
2442
2679
  /**
2443
2680
  * Internal: actually mount the view after class is loaded.
@@ -2445,14 +2682,8 @@ var Frame = class _Frame extends EventEmitter {
2445
2682
  doMountView(ViewClass, params, node, sign) {
2446
2683
  if (sign !== this.signature) return;
2447
2684
  const mixinCtors = View.prepare(ViewClass);
2448
- const ViewConstructor = ViewClass;
2449
- const view = new ViewConstructor(
2450
- this.id,
2451
- this,
2452
- params,
2453
- node,
2454
- mixinCtors
2455
- );
2685
+ const Ctor = ViewClass;
2686
+ const view = new Ctor(this.id, this, params, node, mixinCtors);
2456
2687
  this.viewInstance = view;
2457
2688
  view.signature = 1;
2458
2689
  View.delegateEvents(view);
@@ -2533,7 +2764,9 @@ var Frame = class _Frame extends EventEmitter {
2533
2764
  frame.unmountView();
2534
2765
  removeFrame(targetId, wasCreated);
2535
2766
  reInitFrameForCache(frame);
2536
- frameCache.push(frame);
2767
+ if (frameCache.length < MAX_FRAME_POOL) {
2768
+ frameCache.push(frame);
2769
+ }
2537
2770
  const parent = frameRegistry.get(pId || "");
2538
2771
  if (parent && parent.childrenMap[targetId]) {
2539
2772
  Reflect.deleteProperty(parent.childrenMap, targetId);
@@ -2552,13 +2785,12 @@ var Frame = class _Frame extends EventEmitter {
2552
2785
  const viewElements = rootEl.querySelectorAll(`[${LARK_VIEW}]`);
2553
2786
  const frames = [];
2554
2787
  viewElements.forEach((el) => {
2555
- const htmlEl = el;
2556
- if (!htmlEl.frameBound) {
2557
- const elId = ensureElementId2(htmlEl);
2558
- htmlEl.frameBound = 1;
2559
- const viewPath = getAttribute(htmlEl, LARK_VIEW);
2560
- frames.push([elId, viewPath]);
2561
- }
2788
+ if (!(el instanceof HTMLElement)) return;
2789
+ if (htmlElIsBound(el)) return;
2790
+ const elId = ensureElementId2(el);
2791
+ el.frameBound = 1;
2792
+ const viewPath = getAttribute(el, LARK_VIEW);
2793
+ frames.push([elId, viewPath]);
2562
2794
  });
2563
2795
  for (const [frameId, viewPath] of frames) {
2564
2796
  this.mountFrame(frameId, viewPath);
@@ -2571,7 +2803,7 @@ var Frame = class _Frame extends EventEmitter {
2571
2803
  */
2572
2804
  unmountZone(zoneId, _inner) {
2573
2805
  for (const childId in this.childrenMap) {
2574
- if (has(this.childrenMap, childId)) {
2806
+ if (hasOwnProperty(this.childrenMap, childId)) {
2575
2807
  if (!zoneId || childId !== zoneId) {
2576
2808
  this.unmountFrame(childId);
2577
2809
  }
@@ -2585,7 +2817,7 @@ var Frame = class _Frame extends EventEmitter {
2585
2817
  children() {
2586
2818
  const result = [];
2587
2819
  for (const id in this.childrenMap) {
2588
- if (has(this.childrenMap, id)) {
2820
+ if (hasOwnProperty(this.childrenMap, id)) {
2589
2821
  result.push(id);
2590
2822
  }
2591
2823
  }
@@ -2612,14 +2844,10 @@ var Frame = class _Frame extends EventEmitter {
2612
2844
  let result;
2613
2845
  const view = this.view;
2614
2846
  if (view && view.rendered) {
2615
- const fn = view[name];
2847
+ const lookup = view;
2848
+ const fn = lookup[name];
2616
2849
  if (typeof fn === "function") {
2617
- result = funcWithTry(
2618
- fn,
2619
- args || [],
2620
- view,
2621
- noop
2622
- );
2850
+ result = funcWithTry(fn, args || [], view, noop);
2623
2851
  }
2624
2852
  } else {
2625
2853
  const key = SPLITTER + name;
@@ -2642,6 +2870,25 @@ var Frame = class _Frame extends EventEmitter {
2642
2870
  }
2643
2871
  return result;
2644
2872
  }
2873
+ /**
2874
+ * Type-safe variant of `invoke`.
2875
+ *
2876
+ * `invoke()` accepts any string and any args, which silently hides
2877
+ * mismatched call sites when a method gets renamed. `invokeTyped` carries
2878
+ * the view's method signature through TypeScript so the compiler catches
2879
+ * those mistakes:
2880
+ *
2881
+ * ```ts
2882
+ * type Home = View & { loadData(id: string): Promise<void> };
2883
+ * frame.invokeTyped<Home, "loadData">("loadData", ["user-1"]);
2884
+ * ```
2885
+ *
2886
+ * Behavior is identical to `invoke` at runtime — same defer / direct-call
2887
+ * paths — so it's a drop-in safer overload.
2888
+ */
2889
+ invokeTyped(name, args) {
2890
+ return this.invoke(name, args);
2891
+ }
2645
2892
  // ============================================================
2646
2893
  // Static methods
2647
2894
  // ============================================================
@@ -2653,8 +2900,25 @@ var Frame = class _Frame extends EventEmitter {
2653
2900
  static getAll() {
2654
2901
  return frameRegistry;
2655
2902
  }
2656
- /** Get or create root frame */
2657
- static root(rootId) {
2903
+ /**
2904
+ * Returns the existing root frame, or `undefined` if none has been created.
2905
+ * Pure getter — never creates a Frame, never touches the DOM.
2906
+ *
2907
+ * Use `Frame.createRoot(id)` to create the root explicitly during framework
2908
+ * boot. For Micro-Frontend hosts that own multiple independent containers,
2909
+ * use `new Frame(containerId)` directly so each MF mount has its own root.
2910
+ */
2911
+ static getRoot() {
2912
+ return rootFrame;
2913
+ }
2914
+ /**
2915
+ * Create (or return) the singleton root frame for this app.
2916
+ *
2917
+ * Idempotent: subsequent calls always return the original root regardless
2918
+ * of `rootId` — so passing a different id later is silently ignored.
2919
+ * `Framework.boot()` is the canonical caller; user code rarely needs this.
2920
+ */
2921
+ static createRoot(rootId) {
2658
2922
  if (!rootFrame) {
2659
2923
  rootId = rootId || "root";
2660
2924
  let rootElement = document.getElementById(rootId);
@@ -2666,6 +2930,17 @@ var Frame = class _Frame extends EventEmitter {
2666
2930
  }
2667
2931
  return rootFrame;
2668
2932
  }
2933
+ /**
2934
+ * @deprecated Use `Frame.getRoot()` for read-only access or
2935
+ * `Frame.createRoot(id)` to create the root explicitly. The single-method
2936
+ * `root()` blurred the distinction and was a common source of bugs in
2937
+ * Micro-Frontend hosts.
2938
+ *
2939
+ * Kept for backward compatibility — behavior unchanged.
2940
+ */
2941
+ static root(rootId) {
2942
+ return _Frame.createRoot(rootId);
2943
+ }
2669
2944
  /** Bind event listener (static) */
2670
2945
  static on(event, handler) {
2671
2946
  staticEmitter.on(event, handler);
@@ -2681,6 +2956,9 @@ var Frame = class _Frame extends EventEmitter {
2681
2956
  staticEmitter.fire(event, data);
2682
2957
  }
2683
2958
  };
2959
+ function htmlElIsBound(element) {
2960
+ return !!element.frameBound;
2961
+ }
2684
2962
  function ensureElementId2(element) {
2685
2963
  const id = element.getAttribute("id");
2686
2964
  if (id) return id;
@@ -2710,8 +2988,8 @@ function notifyCreated(frameInstance) {
2710
2988
  const pId = frameInstance.parentId;
2711
2989
  if (pId) {
2712
2990
  const parent = frameRegistry.get(pId);
2713
- if (parent && !parent.readyMap[frameInstance.id]) {
2714
- parent.readyMap[frameInstance.id] = 1;
2991
+ if (parent && !parent.readyMap.has(frameInstance.id)) {
2992
+ parent.readyMap.add(frameInstance.id);
2715
2993
  parent.readyCount++;
2716
2994
  notifyCreated(parent);
2717
2995
  }
@@ -2726,9 +3004,9 @@ function notifyAlter(frameInstance, data) {
2726
3004
  const pId = frameInstance.parentId;
2727
3005
  if (pId) {
2728
3006
  const parent = frameRegistry.get(pId);
2729
- if (parent && parent.readyMap[frameInstance.id]) {
3007
+ if (parent && parent.readyMap.has(frameInstance.id)) {
2730
3008
  parent.readyCount--;
2731
- Reflect.deleteProperty(parent.readyMap, frameInstance.id);
3009
+ parent.readyMap.delete(frameInstance.id);
2732
3010
  notifyAlter(parent, data);
2733
3011
  }
2734
3012
  }
@@ -2741,7 +3019,7 @@ function reInitFrame(frame, id, parentId) {
2741
3019
  frame["childrenCount"] = 0;
2742
3020
  frame["readyCount"] = 0;
2743
3021
  frame["signature"] = 1;
2744
- frame["readyMap"] = {};
3022
+ frame["readyMap"] = /* @__PURE__ */ new Set();
2745
3023
  frame["invokeList"] = [];
2746
3024
  frameRegistry.set(id, frame);
2747
3025
  }
@@ -2749,7 +3027,7 @@ function reInitFrameForCache(frame) {
2749
3027
  Reflect.set(frame, "id", "");
2750
3028
  frame["_parentId"] = void 0;
2751
3029
  frame["childrenMap"] = {};
2752
- frame["readyMap"] = {};
3030
+ frame["readyMap"] = /* @__PURE__ */ new Set();
2753
3031
  }
2754
3032
  function translateQuery(pId, src, params) {
2755
3033
  const parentFrame = frameRegistry.get(pId);
@@ -2759,22 +3037,138 @@ function translateQuery(pId, src, params) {
2759
3037
  if (!parentRefData) return;
2760
3038
  if (src.indexOf(SPLITTER) > 0) {
2761
3039
  translateData(parentRefData, params);
2762
- if (params[SPLITTER]) {
2763
- assign(
2764
- params,
2765
- params[SPLITTER]
2766
- );
3040
+ const paramsRec = params;
3041
+ const splitterValue = paramsRec[SPLITTER];
3042
+ if (splitterValue && typeof splitterValue === "object") {
3043
+ assign(params, splitterValue);
2767
3044
  Reflect.deleteProperty(params, SPLITTER);
2768
3045
  }
2769
3046
  }
2770
3047
  }
2771
- function registerViewClass(viewPath, ViewClass) {
2772
- const parsed = parseUri(viewPath);
2773
- const path = parsed.path;
2774
- if (path) {
2775
- viewClassRegistry[path] = ViewClass;
3048
+
3049
+ // src/cross-site.ts
3050
+ var preparePromises = {};
3051
+ var projectsMap = null;
3052
+ function loadRemoteView(viewPath, bizCode) {
3053
+ const crossConfigs = config.crossConfigs || window.crossConfigs;
3054
+ const currentName = config.projectName || "";
3055
+ const slashIndex = viewPath.indexOf("/");
3056
+ const projectName = slashIndex > -1 ? viewPath.substring(0, slashIndex) : viewPath;
3057
+ if (projectName === currentName) return Promise.resolve();
3058
+ if (!preparePromises[projectName]) {
3059
+ if (!projectsMap) {
3060
+ const map = toMap(crossConfigs || [], "projectName");
3061
+ projectsMap = map;
3062
+ }
3063
+ const info = projectsMap[projectName];
3064
+ if (!info) {
3065
+ return Promise.reject(
3066
+ new Error(`Cannot find ${projectName} from crossConfigs`)
3067
+ );
3068
+ }
3069
+ preparePromises[projectName] = use(`${projectName}/prepare`).then((modules) => {
3070
+ let mod = modules[0];
3071
+ if (mod && typeof mod === "object" && mod !== null) {
3072
+ const rec = mod;
3073
+ if (rec["__esModule"]) {
3074
+ mod = rec["default"];
3075
+ }
3076
+ }
3077
+ if (typeof mod === "function") {
3078
+ return mod({ bizCode });
3079
+ }
3080
+ return void 0;
3081
+ }).catch((err) => {
3082
+ Reflect.deleteProperty(preparePromises, projectName);
3083
+ throw err;
3084
+ });
2776
3085
  }
3086
+ return preparePromises[projectName];
2777
3087
  }
3088
+ function resetProjectsMap() {
3089
+ projectsMap = null;
3090
+ }
3091
+ var skeletonTemplate = (data, viewId) => {
3092
+ let skeletonHtml = "<div>Loading...</div>";
3093
+ if (data && typeof data === "object") {
3094
+ const candidate = data.skeleton;
3095
+ if (typeof candidate === "string") skeletonHtml = candidate;
3096
+ }
3097
+ return `<div id="mf_${viewId}">${skeletonHtml}</div>`;
3098
+ };
3099
+ var CrossSite = View.extend({
3100
+ /** Skeleton template renders loading state + child container */
3101
+ template: skeletonTemplate,
3102
+ init(params) {
3103
+ this.$sign = 0;
3104
+ this.on("destroy", () => {
3105
+ this.$sign = -1;
3106
+ });
3107
+ this.assign?.(params);
3108
+ },
3109
+ assign(data) {
3110
+ this.$view = typeof data["view"] === "string" ? data["view"] : "";
3111
+ const nested = data["params"];
3112
+ const nestedParams = nested && typeof nested === "object" ? nested : {};
3113
+ this.$params = { ...data, ...nestedParams };
3114
+ this.updater.set({
3115
+ skeleton: data["skeleton"],
3116
+ skeletonParams: data["skeletonParams"] || {},
3117
+ bizCode: data["bizCode"]
3118
+ });
3119
+ if (this.$sign > 0) {
3120
+ this.updateView();
3121
+ }
3122
+ return false;
3123
+ },
3124
+ async updateView() {
3125
+ const sign = ++this.$sign;
3126
+ const stored = this.updater.get();
3127
+ const bizCode = stored.bizCode;
3128
+ try {
3129
+ await loadRemoteView(this.$view, bizCode);
3130
+ } catch (ex) {
3131
+ const node = document.getElementById("mf_" + this.id);
3132
+ if (node) {
3133
+ const err = ex instanceof Error ? ex : new Error(String(ex));
3134
+ node.innerHTML = err.message || String(err);
3135
+ }
3136
+ }
3137
+ if (this.$sign !== sign) return;
3138
+ const mf = Frame.get("mf_" + this.id);
3139
+ const parsedNew = parseUri(this.$view);
3140
+ const newPath = parsedNew.path;
3141
+ const oldPath = mf?.viewPath ? parseUri(mf.viewPath).path : "";
3142
+ const view = mf?.view;
3143
+ if (newPath === oldPath && view && typeof view.assign === "function") {
3144
+ const result = funcWithTry(view.assign, [this.$params], view, noop);
3145
+ if (result) {
3146
+ view.render();
3147
+ }
3148
+ return;
3149
+ }
3150
+ const owner = this.owner;
3151
+ if (owner && typeof owner !== "number") {
3152
+ owner.mountFrame("mf_" + this.id, this.$view, this.$params);
3153
+ }
3154
+ },
3155
+ render() {
3156
+ const params = this.$params;
3157
+ this.updater.digest({
3158
+ skeleton: params?.["skeleton"]
3159
+ });
3160
+ this.updateView();
3161
+ },
3162
+ /**
3163
+ * Invoke a method on the remote view.
3164
+ * Usage: mf.invoke('callView', methodName, ...args)
3165
+ */
3166
+ callView(name, ...args) {
3167
+ const mf = Frame.get("mf_" + this.id);
3168
+ return mf?.invoke(name, args);
3169
+ }
3170
+ });
3171
+ var cross_site_default = CrossSite;
2778
3172
 
2779
3173
  // src/service.ts
2780
3174
  var Payload = class {
@@ -2962,29 +3356,31 @@ var Service = class {
2962
3356
  * Get metadata for an API endpoint.
2963
3357
  */
2964
3358
  static meta(attrs) {
2965
- const name = typeof attrs === "string" ? attrs : attrs["name"];
2966
- return this._metaList[name] || attrs;
3359
+ const name = typeof attrs === "string" ? attrs : String(attrs["name"] ?? "");
3360
+ const known = this._metaList[name];
3361
+ if (known) return known;
3362
+ return attrs;
2967
3363
  }
2968
3364
  /**
2969
3365
  * Create a Payload for an API request.
2970
3366
  */
2971
3367
  static create(attrs) {
2972
3368
  const meta = this.meta(attrs);
2973
- const cache = attrs["cache"] | 0 || meta.cache || 0;
3369
+ const cache = toCacheValue(attrs["cache"]) || meta.cache || 0;
2974
3370
  const entity = new Payload();
2975
3371
  entity.set(meta);
2976
3372
  entity.cacheInfo = {
2977
3373
  name: meta.name,
2978
- after: meta.after,
2979
- cleans: meta.cleanKeys,
3374
+ after: typeof meta.after === "function" ? meta.after : void 0,
3375
+ cleans: typeof meta.cleanKeys === "string" ? meta.cleanKeys : void 0,
2980
3376
  key: cache ? defaultCacheKey(meta, attrs) : "",
2981
3377
  time: 0
2982
3378
  };
2983
- if (typeof attrs === "object" && attrs !== null) {
3379
+ if (attrs !== null) {
2984
3380
  entity.set(attrs);
2985
3381
  }
2986
3382
  const before = meta.before;
2987
- if (before) {
3383
+ if (typeof before === "function") {
2988
3384
  funcWithTry(before, [entity], entity, noop);
2989
3385
  }
2990
3386
  this._staticEmitter.fire("begin", { payload: entity });
@@ -3010,7 +3406,7 @@ var Service = class {
3010
3406
  */
3011
3407
  static cached(attrs) {
3012
3408
  const meta = this.meta(attrs);
3013
- const cache = attrs["cache"] | 0 || meta.cache || 0;
3409
+ const cache = toCacheValue(attrs["cache"]) || meta.cache || 0;
3014
3410
  let cacheKey = "";
3015
3411
  if (cache) {
3016
3412
  cacheKey = defaultCacheKey(meta, attrs);
@@ -3018,7 +3414,8 @@ var Service = class {
3018
3414
  if (cacheKey) {
3019
3415
  const info = this._pendingCacheKeys[cacheKey];
3020
3416
  if (info) {
3021
- return info.entity;
3417
+ const entity = info.entity;
3418
+ return entity instanceof Payload ? entity : void 0;
3022
3419
  }
3023
3420
  const cached = this._payloadCache.get(cacheKey);
3024
3421
  if (cached && cached.cacheInfo) {
@@ -3035,17 +3432,14 @@ var Service = class {
3035
3432
  * Clear cached payloads by endpoint name.
3036
3433
  */
3037
3434
  static clear(names) {
3038
- const nameList = (typeof names === "string" ? names : names.join(",")).split(",");
3039
- const nameSet = {};
3040
- for (const n of nameList) {
3041
- nameSet[n] = 1;
3042
- }
3435
+ const nameSet = new Set(
3436
+ (typeof names === "string" ? names : names.join(",")).split(",")
3437
+ );
3043
3438
  const keysToDelete = [];
3044
3439
  this._payloadCache.forEach((payload) => {
3045
- if (payload?.cacheInfo && nameSet[payload.cacheInfo.name]) {
3046
- if (payload.cacheInfo.key) {
3047
- keysToDelete.push(payload.cacheInfo.key);
3048
- }
3440
+ const info = payload?.cacheInfo;
3441
+ if (info && info.key && nameSet.has(info.name)) {
3442
+ keysToDelete.push(info.key);
3049
3443
  }
3050
3444
  });
3051
3445
  for (const key of keysToDelete) {
@@ -3064,12 +3458,23 @@ var Service = class {
3064
3458
  }
3065
3459
  /**
3066
3460
  * Create a new Service subclass with a custom sync function.
3067
- * Each subclass gets its own per-type state (metaList, cache, etc.)
3068
- * to ensure isolation between different Service types.
3461
+ *
3462
+ * Each subclass gets its OWN copies of every per-type static field
3463
+ * (`_metaList`, `_payloadCache`, `_pendingCacheKeys`, `_syncFn`,
3464
+ * `_staticEmitter`, `_cacheMax`, `_cacheBuffer`) via `static override`.
3465
+ * This is intentional: it ensures that endpoint metadata, cache state,
3466
+ * in-flight dedup keys, and event subscribers are fully isolated between
3467
+ * different Service types, even when one extends another.
3468
+ *
3469
+ * **Do not refactor these `static override` declarations away** — sharing
3470
+ * them through prototype inheritance would let endpoints registered on one
3471
+ * subclass leak into another, and the LFU cache evictions of one type
3472
+ * would race with those of another.
3069
3473
  */
3070
3474
  static extend(newSyncFn, newCacheMax, newCacheBuffer) {
3071
3475
  const ParentService = this;
3072
3476
  class ChildService extends ParentService {
3477
+ // Intentionally per-subclass — see Service.extend doc.
3073
3478
  static _metaList = {};
3074
3479
  static _payloadCache = new Cache({
3075
3480
  maxSize: newCacheMax || ParentService._cacheMax,
@@ -3084,18 +3489,34 @@ var Service = class {
3084
3489
  return ChildService;
3085
3490
  }
3086
3491
  };
3492
+ var metaJsonCache = /* @__PURE__ */ new WeakMap();
3493
+ function getMetaJson(meta) {
3494
+ let cached = metaJsonCache.get(meta);
3495
+ if (cached === void 0) {
3496
+ cached = JSON.stringify(meta);
3497
+ metaJsonCache.set(meta, cached);
3498
+ }
3499
+ return cached;
3500
+ }
3087
3501
  function defaultCacheKey(meta, attrs) {
3088
- return JSON.stringify(attrs) + SPLITTER + JSON.stringify(meta);
3502
+ return JSON.stringify(attrs) + SPLITTER + getMetaJson(meta);
3503
+ }
3504
+ function toCacheValue(v) {
3505
+ if (typeof v === "number") return v | 0;
3506
+ if (typeof v === "string") {
3507
+ const n = Number(v);
3508
+ return Number.isFinite(n) ? n | 0 : 0;
3509
+ }
3510
+ return 0;
3089
3511
  }
3090
3512
  function serviceSend(service, attrs, done, flag, save) {
3091
- if (service["destroyed"]) return;
3092
- if (service["busy"]) {
3093
- service.enqueue(
3094
- serviceSend.bind(null, service, attrs, done, flag, save)
3095
- );
3513
+ if (service.destroyed) return;
3514
+ if (service.busy) {
3515
+ const queued = () => serviceSend(service, attrs, done, flag, save);
3516
+ service.enqueue(queued);
3096
3517
  return;
3097
3518
  }
3098
- service["busy"] = 1;
3519
+ service.busy = 1;
3099
3520
  let attrList;
3100
3521
  if (typeof attrs === "string") {
3101
3522
  attrList = [{ name: attrs }];
@@ -3120,10 +3541,10 @@ function serviceSend(service, attrs, done, flag, save) {
3120
3541
  newPayload = true;
3121
3542
  staticEmitter2.fire("done", { payload });
3122
3543
  }
3123
- if (!service["destroyed"]) {
3544
+ if (!service.destroyed) {
3124
3545
  const finish = requestCount === total;
3125
3546
  if (finish) {
3126
- service["busy"] = 0;
3547
+ service.busy = 0;
3127
3548
  if (flag === FETCH_FLAGS_ALL) {
3128
3549
  doneArr[0] = errorArgs;
3129
3550
  funcWithTry(done, doneArr, service, noop);
@@ -3140,10 +3561,7 @@ function serviceSend(service, attrs, done, flag, save) {
3140
3561
  for (const attr of attrList) {
3141
3562
  if (!attr) continue;
3142
3563
  const attrObj = typeof attr === "string" ? { name: attr } : attr;
3143
- const payloadInfo = service.type.get(
3144
- attrObj,
3145
- save
3146
- );
3564
+ const payloadInfo = service.type.get(attrObj, save);
3147
3565
  const payloadEntity = payloadInfo.entity;
3148
3566
  const cacheKey = payloadEntity.cacheInfo?.key || "";
3149
3567
  const complete = remoteComplete.bind(null, requestCount++);
@@ -3157,15 +3575,13 @@ function serviceSend(service, attrs, done, flag, save) {
3157
3575
  const cacheComplete = () => {
3158
3576
  const list = pendingCacheKeys[cacheKey];
3159
3577
  const entity = list.entity;
3160
- if (entity.cacheInfo) {
3578
+ if (entity instanceof Payload && entity.cacheInfo) {
3161
3579
  entity.cacheInfo.time = now();
3580
+ internals.payloadCache.set(cacheKey, entity);
3162
3581
  }
3163
- internals.payloadCache.set(cacheKey, entity);
3164
3582
  Reflect.deleteProperty(pendingCacheKeys, cacheKey);
3165
3583
  for (const cb of list) {
3166
- if (typeof cb === "function") {
3167
- cb();
3168
- }
3584
+ if (typeof cb === "function") cb();
3169
3585
  }
3170
3586
  };
3171
3587
  syncFn(payloadEntity, cacheComplete);
@@ -3178,13 +3594,32 @@ function serviceSend(service, attrs, done, flag, save) {
3178
3594
  }
3179
3595
  }
3180
3596
 
3181
- // src/frame-visualizer.ts
3182
- var MSG_PING = "LARK_VISUALIZER_PING";
3183
- var MSG_PONG = "LARK_VISUALIZER_PONG";
3184
- var MSG_REQUEST_TREE = "LARK_VISUALIZER_REQUEST_TREE";
3185
- var MSG_TREE = "LARK_VISUALIZER_TREE";
3186
- var MSG_TREE_DELTA = "LARK_VISUALIZER_TREE_DELTA";
3597
+ // src/frame-visual.ts
3598
+ var FrameVisualBridge = {
3599
+ MSG_PING: "LARK_VIS_PING",
3600
+ MSG_PONG: "LARK_VIS_PONG",
3601
+ MSG_REQUEST_TREE: "LARK_VIS_REQUEST_TREE",
3602
+ MSG_TREE: "LARK_VIS_TREE",
3603
+ MSG_TREE_DELTA: "LARK_VIS_TREE_DELTA"
3604
+ };
3187
3605
  function serializeView(view) {
3606
+ const evtMap = view.eventObjectMap;
3607
+ const eventMethodKeys = evtMap ? Object.keys(evtMap) : [];
3608
+ const resourceKeys = view.resources ? Object.keys(view.resources) : [];
3609
+ const lookup = view;
3610
+ const hasAssign = typeof lookup["assign"] === "function";
3611
+ let updaterData = null;
3612
+ try {
3613
+ const ref = view.updater?.refData;
3614
+ if (ref && typeof ref === "object") {
3615
+ updaterData = {};
3616
+ for (const k of Object.keys(ref)) {
3617
+ const v = ref[k];
3618
+ updaterData[k] = v === null || typeof v !== "object" ? v : `[${typeof v}]`;
3619
+ }
3620
+ }
3621
+ } catch {
3622
+ }
3188
3623
  return {
3189
3624
  id: view.id,
3190
3625
  rendered: !!view.rendered,
@@ -3195,7 +3630,11 @@ function serializeView(view) {
3195
3630
  keys: view.locationObserved.keys,
3196
3631
  observePath: view.locationObserved.observePath
3197
3632
  },
3198
- hasTemplate: !!view.template
3633
+ hasTemplate: !!view.template,
3634
+ eventMethodKeys,
3635
+ resourceKeys,
3636
+ hasAssign,
3637
+ updaterData
3199
3638
  };
3200
3639
  }
3201
3640
  function serializeFrame(frameId) {
@@ -3223,7 +3662,10 @@ function serializeFrame(frameId) {
3223
3662
  };
3224
3663
  }
3225
3664
  function serializeFrameTree() {
3226
- const root = Frame.root();
3665
+ const root = Frame.getRoot();
3666
+ if (!root) {
3667
+ return { root: null, totalFrames: 0, timestamp: Date.now(), rootId: "" };
3668
+ }
3227
3669
  const rootNode = serializeFrame(root.id);
3228
3670
  let totalFrames = 0;
3229
3671
  const countFrames = (node) => {
@@ -3251,19 +3693,22 @@ function installFrameVisualizerBridge() {
3251
3693
  const data = event.data;
3252
3694
  if (!data || typeof data !== "object") return;
3253
3695
  const type = data.type;
3254
- if (type === MSG_PING) {
3696
+ if (type === FrameVisualBridge.MSG_PING) {
3255
3697
  const source = event.source;
3256
3698
  if (source) {
3257
- source.postMessage({ type: MSG_PONG }, { targetOrigin: "*" });
3699
+ source.postMessage(
3700
+ { type: FrameVisualBridge.MSG_PONG },
3701
+ { targetOrigin: "*" }
3702
+ );
3258
3703
  }
3259
3704
  return;
3260
3705
  }
3261
- if (type === MSG_REQUEST_TREE) {
3706
+ if (type === FrameVisualBridge.MSG_REQUEST_TREE) {
3262
3707
  const tree = serializeFrameTree();
3263
3708
  const source = event.source;
3264
3709
  if (source) {
3265
3710
  source.postMessage(
3266
- { type: MSG_TREE, data: tree },
3711
+ { type: FrameVisualBridge.MSG_TREE, data: tree },
3267
3712
  { targetOrigin: "*" }
3268
3713
  );
3269
3714
  }
@@ -3282,18 +3727,14 @@ function pushTreeUpdate() {
3282
3727
  const treeJson = JSON.stringify(tree);
3283
3728
  if (treeJson !== lastTreeJson) {
3284
3729
  lastTreeJson = treeJson;
3285
- window.parent.postMessage({ type: MSG_TREE_DELTA, data: tree }, "*");
3730
+ window.parent.postMessage(
3731
+ { type: FrameVisualBridge.MSG_TREE_DELTA, data: tree },
3732
+ "*"
3733
+ );
3286
3734
  }
3287
3735
  }
3288
3736
 
3289
3737
  // src/framework.ts
3290
- var config = {
3291
- rootId: "root",
3292
- hashbang: "#!",
3293
- error: (error) => {
3294
- throw error;
3295
- }
3296
- };
3297
3738
  var booted3 = false;
3298
3739
  var taskList = [];
3299
3740
  var taskIndex = 0;
@@ -3342,6 +3783,9 @@ function task(fn, args, context) {
3342
3783
  }
3343
3784
  }
3344
3785
  var dispatcherUpdateTag = 0;
3786
+ function isThenable(value) {
3787
+ return !!value && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
3788
+ }
3345
3789
  function viewIsObserveChanged(view) {
3346
3790
  const loc = view.locationObserved;
3347
3791
  let result = false;
@@ -3355,7 +3799,7 @@ function viewIsObserveChanged(view) {
3355
3799
  const changedParams = lastChanged2?.params;
3356
3800
  if (changedParams) {
3357
3801
  for (const key of loc.keys) {
3358
- result = has(changedParams, key);
3802
+ result = hasOwnProperty(changedParams, key);
3359
3803
  if (result) break;
3360
3804
  }
3361
3805
  }
@@ -3367,48 +3811,59 @@ function stateIsObserveChanged(view, stateKeys) {
3367
3811
  const observedKeys = view.observedStateKeys;
3368
3812
  if (!observedKeys) return false;
3369
3813
  for (const key of observedKeys) {
3370
- if (has(stateKeys, key)) return true;
3814
+ if (stateKeys.has(key)) return true;
3371
3815
  }
3372
3816
  return false;
3373
3817
  }
3374
3818
  function dispatcherUpdate(frame, stateKeys) {
3375
- const frameInternal = frame;
3376
- const view = frame.view;
3377
- if (!view || frameInternal.dispatcherUpdateTag === dispatcherUpdateTag || view.signature <= 1) {
3378
- return;
3379
- }
3380
- frameInternal.dispatcherUpdateTag = dispatcherUpdateTag;
3381
- const isChanged = stateKeys ? stateIsObserveChanged(view, stateKeys) : viewIsObserveChanged(view);
3382
- let renderPromise;
3383
- if (isChanged) {
3384
- const renderResult = funcWithTry(
3385
- view.renderMethod ?? view.render,
3386
- [],
3387
- view,
3388
- noop
3389
- );
3390
- if (renderResult && typeof renderResult.then === "function") {
3391
- renderPromise = renderResult;
3392
- }
3393
- }
3394
- const children = frame.children();
3395
- const recurse = () => {
3396
- for (const childId of children) {
3397
- const childFrame = Frame.get(childId);
3398
- if (childFrame) {
3399
- dispatcherUpdate(childFrame, stateKeys);
3819
+ const stack = [frame];
3820
+ const drain = (s) => {
3821
+ while (s.length > 0) {
3822
+ const current = s.pop();
3823
+ const tagged = current;
3824
+ const view = current.view;
3825
+ if (!view || tagged.dispatcherUpdateTag === dispatcherUpdateTag || view.signature <= 1) {
3826
+ continue;
3827
+ }
3828
+ tagged.dispatcherUpdateTag = dispatcherUpdateTag;
3829
+ const isChanged = stateKeys ? stateIsObserveChanged(view, stateKeys) : viewIsObserveChanged(view);
3830
+ let renderPromise;
3831
+ if (isChanged) {
3832
+ const renderResult = funcWithTry(
3833
+ view.renderMethod ?? view.render,
3834
+ [],
3835
+ view,
3836
+ noop
3837
+ );
3838
+ if (isThenable(renderResult)) {
3839
+ renderPromise = renderResult;
3840
+ }
3841
+ }
3842
+ const children = current.children();
3843
+ if (renderPromise) {
3844
+ renderPromise.then(() => {
3845
+ const subStack = [];
3846
+ for (let i = children.length - 1; i >= 0; i--) {
3847
+ const child = Frame.get(children[i]);
3848
+ if (child) subStack.push(child);
3849
+ }
3850
+ drain(subStack);
3851
+ });
3852
+ } else {
3853
+ for (let i = children.length - 1; i >= 0; i--) {
3854
+ const child = Frame.get(children[i]);
3855
+ if (child) s.push(child);
3856
+ }
3400
3857
  }
3401
3858
  }
3402
3859
  };
3403
- if (renderPromise) {
3404
- renderPromise.then(recurse);
3405
- } else {
3406
- recurse();
3407
- }
3860
+ drain(stack);
3408
3861
  }
3409
3862
  function dispatcherNotifyChange(e) {
3410
- const rootFrame2 = Frame.root();
3411
- const view = e.view;
3863
+ const rootFrame2 = Frame.getRoot();
3864
+ if (!rootFrame2) return;
3865
+ const routeEvent = e;
3866
+ const view = routeEvent.view;
3412
3867
  if (view) {
3413
3868
  const viewPath = typeof view === "object" && view !== null ? String(view.to || "") : String(view);
3414
3869
  rootFrame2.mountView(viewPath);
@@ -3425,19 +3880,6 @@ function dispatchEvent(target, eventType, eventInit) {
3425
3880
  });
3426
3881
  target.dispatchEvent(event);
3427
3882
  }
3428
- function use(names, callback) {
3429
- if (!config.require) {
3430
- if (callback) callback();
3431
- return;
3432
- }
3433
- const nameList = typeof names === "string" ? [names] : names;
3434
- const result = config.require(nameList);
3435
- if (result && typeof result.then === "function") {
3436
- result.then((modules) => {
3437
- if (callback) callback(modules);
3438
- });
3439
- }
3440
- }
3441
3883
  var WAIT_OK = 1;
3442
3884
  var WAIT_TIMEOUT_OR_NOT_FOUND = 0;
3443
3885
  function waitZoneViewsRendered(viewId, timeout) {
@@ -3460,12 +3902,27 @@ function waitZoneViewsRendered(viewId, timeout) {
3460
3902
  setTimeout(check, 9);
3461
3903
  });
3462
3904
  }
3905
+ function getConfigImpl(key) {
3906
+ if (key === void 0) return config;
3907
+ return config[key];
3908
+ }
3463
3909
  var Framework = {
3464
3910
  // ============================================================
3465
3911
  // Lifecycle
3466
3912
  // ============================================================
3913
+ /** Read framework configuration. See `FrameworkInterface.getConfig`. */
3914
+ getConfig: getConfigImpl,
3915
+ /**
3916
+ * Merge a patch into framework configuration. See `FrameworkInterface.setConfig`.
3917
+ */
3918
+ setConfig(patch) {
3919
+ if (patch && typeof patch === "object") {
3920
+ assign(config, patch);
3921
+ }
3922
+ return config;
3923
+ },
3467
3924
  /**
3468
- * Get or set framework configuration.
3925
+ * @deprecated Use `getConfig()` / `setConfig()`. Behavior unchanged.
3469
3926
  */
3470
3927
  config(cfg) {
3471
3928
  if (!cfg) {
@@ -3486,17 +3943,17 @@ var Framework = {
3486
3943
  }
3487
3944
  Router._setConfig(config);
3488
3945
  EventDelegator.setFrameGetter((id) => Frame.get(id));
3489
- Router.on(ROUTER_EVENTS.CHANGED, (data) => {
3946
+ Router.on(RouterEvents.CHANGED, (data) => {
3490
3947
  if (data) dispatcherNotifyChange(data);
3491
3948
  });
3492
- State.on(ROUTER_EVENTS.CHANGED, (data) => {
3949
+ State.on(RouterEvents.CHANGED, (data) => {
3493
3950
  if (data) dispatcherNotifyChange(data);
3494
3951
  });
3495
3952
  booted3 = true;
3496
3953
  markBooted();
3497
3954
  markRouterBooted();
3498
3955
  installFrameVisualizerBridge();
3499
- const rootFrame2 = Frame.root(config.rootId);
3956
+ const rootFrame2 = Frame.createRoot(config.rootId);
3500
3957
  Router._bind();
3501
3958
  const defaultView = config.defaultView || "";
3502
3959
  if (defaultView && !rootFrame2.view) {
@@ -3551,7 +4008,7 @@ var Framework = {
3551
4008
  /**
3552
4009
  * Check if object has own property.
3553
4010
  */
3554
- has,
4011
+ has: hasOwnProperty,
3555
4012
  /**
3556
4013
  * Get object keys.
3557
4014
  */
@@ -3611,846 +4068,166 @@ if (typeof window !== "undefined") {
3611
4068
  window.__lark_Router = Router;
3612
4069
  window.__lark_Frame = Frame;
3613
4070
  window.__lark_View = View;
4071
+ window.__lark_invalidateViewClass = invalidateViewClass;
4072
+ window.__lark_getViewClassRegistry = getViewClassRegistry;
4073
+ window.__lark_registerViewClass = registerViewClass;
3614
4074
  }
3615
4075
 
3616
- // src/store.ts
3617
- var LARK_GLOBAL = "lark-global";
3618
- var Platform = /* @__PURE__ */ ((Platform2) => {
3619
- Platform2["Lark"] = "lark";
3620
- Platform2["React"] = "react";
3621
- Platform2["Node"] = "node";
3622
- return Platform2;
3623
- })(Platform || {});
3624
- var isFunction = (val) => typeof val === "function";
3625
- var isObject = (val) => val !== null && typeof val === "object";
3626
- var isPromise = (val) => isObject(val) && isFunction(val["then"]);
3627
- var hasOwnProperty = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
3628
- var deepClone = (obj) => {
3629
- if (!obj || !isObject(obj)) return {};
3630
- const newData = Array.isArray(obj) ? [] : Object.create(Object.getPrototypeOf(obj));
3631
- for (const key in obj) {
3632
- if (hasOwnProperty(obj, key)) {
3633
- const value = obj[key];
3634
- newData[key] = isObject(value) ? deepClone(value) : value;
3635
- }
3636
- }
3637
- return newData;
3638
- };
3639
- var cloneData = (data) => isObject(data) ? deepClone(data) : data;
3640
- var getDataByKey = (target, key) => {
3641
- let data;
3642
- const rec = target;
3643
- if (key.includes(".")) {
3644
- key.split(".").forEach((k, index) => {
3645
- data = index === 0 ? rec?.[k] : data?.[k];
3646
- });
3647
- } else {
3648
- data = rec?.[key];
3649
- }
3650
- return data;
3651
- };
3652
- var Queue = class {
3653
- pendingTasks = /* @__PURE__ */ new Set();
3654
- queue = [];
3655
- flushTasks() {
3656
- const { pendingTasks, queue } = this;
3657
- const flushTickTask = () => {
3658
- while (queue.length > 0) {
3659
- const task2 = queue.shift();
3660
- if (task2) {
3661
- pendingTasks.delete(task2);
3662
- runTask(task2);
3663
- }
3664
- }
3665
- };
3666
- Promise.resolve().then(flushTickTask);
3667
- }
3668
- add(tasks) {
3669
- const isQueueEmpty = this.queue.length === 0;
3670
- for (const { cb, params } of tasks) {
3671
- addParams2Callback(cb, params);
3672
- if (!this.pendingTasks.has(cb)) {
3673
- this.queue.push(cb);
3674
- this.pendingTasks.add(cb);
3675
- }
4076
+ // src/url-state.ts
4077
+ function useUrlState(view, initialState) {
4078
+ const keys2 = initialState ? Object.keys(initialState) : [];
4079
+ if (keys2.length > 0) {
4080
+ view.observeLocation(keys2);
4081
+ }
4082
+ const getState = () => {
4083
+ const loc = Router.parse();
4084
+ const result = { ...initialState || {} };
4085
+ for (const key of keys2) {
4086
+ const val = loc.get(key);
4087
+ if (val) result[key] = val;
3676
4088
  }
3677
- if (isQueueEmpty) this.flushTasks();
3678
- }
3679
- delete(tasks) {
3680
- if (this.pendingTasks.size === 0) return;
3681
- for (const { cb } of tasks) {
3682
- if (this.pendingTasks.has(cb)) {
3683
- this.pendingTasks.delete(cb);
3684
- const index = this.queue.findIndex((item) => item === cb);
3685
- if (index !== -1) this.queue.splice(index, 1);
3686
- }
3687
- }
3688
- }
3689
- clear() {
3690
- this.queue = [];
3691
- this.pendingTasks.clear();
3692
- }
3693
- };
3694
- var addParams2Callback = (cb, params) => {
3695
- if (!cb || !params) return;
3696
- const cbObj = cb;
3697
- if (isObject(cb) && isObject(cbObj["params"])) {
3698
- Object.assign(cbObj["params"], params);
3699
- } else {
3700
- cbObj["params"] = params;
3701
- }
3702
- };
3703
- var runTask = (cb) => {
3704
- const cbObj = cb;
3705
- const params = cbObj["params"];
3706
- delete cbObj["params"];
3707
- try {
3708
- cb(params);
3709
- } catch {
3710
- }
3711
- };
3712
- var getDefScheduler = () => new Queue();
3713
- var run = (tasks, scheduler) => {
3714
- if (scheduler) {
3715
- if (isFunction(scheduler)) {
3716
- for (const { cb, params } of tasks) scheduler(cb, params);
3717
- } else {
3718
- scheduler.add(tasks);
3719
- }
3720
- } else {
3721
- for (const { cb, params } of tasks) cb(params);
3722
- }
3723
- };
3724
- var ArrMethods = {};
3725
- ["indexOf", "lastIndexOf", "includes"].forEach((key) => {
3726
- const rawMethod = Array.prototype[key];
3727
- ArrMethods[key] = function(...args) {
3728
- let res = rawMethod.apply(this, args);
3729
- if (res === -1 || res === false) {
3730
- res = rawMethod.apply(
3731
- this,
3732
- args.map((item) => ProxyCache.get(item) ?? item)
3733
- );
3734
- }
3735
- return res;
3736
- };
3737
- });
3738
- var inArrUpdate = false;
3739
- ["unshift", "shift", "push", "pop", "splice"].forEach((key) => {
3740
- const rawMethod = Array.prototype[key];
3741
- ArrMethods[key] = function(...args) {
3742
- inArrUpdate = true;
3743
- const result = rawMethod.apply(this, args);
3744
- inArrUpdate = false;
3745
4089
  return result;
3746
4090
  };
3747
- });
3748
- function needKeepArrItem(target, newVal, key) {
3749
- if (!Array.isArray(target) || !inArrUpdate) return false;
3750
- return getLinkKeys(newVal) === createLinkKeys(target, key);
3751
- }
3752
- var defStateConfig = {
3753
- belong: LARK_GLOBAL,
3754
- linkKeys: "",
3755
- shallow: false
3756
- };
3757
- var StateConfigMap = /* @__PURE__ */ new WeakMap();
3758
- var setStateConfig = (target, config2) => {
3759
- if (target && isObject(config2)) StateConfigMap.set(target, config2);
3760
- };
3761
- var getStateConfig = (target, key) => {
3762
- if (!StateConfigMap.has(target)) {
3763
- return void 0;
3764
- }
3765
- const config2 = StateConfigMap.get(target);
3766
- return key ? config2?.[key] : config2;
3767
- };
3768
- var isState = (target) => isObject(target) && StateConfigMap.has(target);
3769
- var createLinkKeys = (target, property) => {
3770
- if (!hasLinkKeys(target)) return property;
3771
- if (Array.isArray(target)) return getLinkKeys(target) ?? property;
3772
- return `${getLinkKeys(target)}.${property}`;
3773
- };
3774
- var formatLinkKeys = (target, key) => {
3775
- const originKeysStr = getLinkKeys(target);
3776
- if (!originKeysStr) return null;
3777
- const linkKeys = [];
3778
- const originKeys = originKeysStr.split(".");
3779
- let pre = "";
3780
- for (const k of originKeys) {
3781
- const curr = pre ? `${pre}.${k}` : k;
3782
- linkKeys.push(curr);
3783
- pre = curr;
3784
- }
3785
- linkKeys.push(`${originKeysStr}.${key}`);
3786
- return linkKeys;
3787
- };
3788
- var getLinkKeys = (target) => getStateConfig(target, "linkKeys");
3789
- var hasLinkKeys = (target) => !!getLinkKeys(target);
3790
- var ProxyCache = /* @__PURE__ */ new WeakMap();
3791
- var keepKey = null;
3792
- var keep = (key) => {
3793
- if (keepKey) throw new Error("[lark-store] keepKey is not null");
3794
- keepKey = key;
3795
- };
3796
- var needKeep = (key) => {
3797
- if (keepKey === key) {
3798
- keepKey = null;
3799
- return true;
3800
- }
3801
- return false;
3802
- };
3803
- var canSetNewVal = (params) => {
3804
- const { property, newVal, oldVal } = params;
3805
- if (hasOwnProperty(params.target, property) && newVal === oldVal)
3806
- return false;
3807
- return true;
3808
- };
3809
- var getNewVal = (params) => {
3810
- const { target, newVal, property, needKeepVal = false } = params;
3811
- const config2 = getStateConfig(target);
3812
- if (!isObject(newVal)) return newVal;
3813
- if (needKeepArrItem(target, newVal, property)) return newVal;
3814
- if (config2?.shallow) return newVal;
3815
- if (needKeepVal) return newVal;
3816
- const linkKeys = createLinkKeys(target, property);
3817
- const newState = createState(newVal, {
3818
- ...config2,
3819
- linkKeys
3820
- });
3821
- if (isPromise(newVal)) handlePromise(newVal, target, property);
3822
- return newState;
3823
- };
3824
- var genPayload = (params) => {
3825
- const { target, property, newVal, oldVal } = params;
3826
- const config2 = getStateConfig(target) || defStateConfig;
3827
- return {
3828
- belong: config2.belong || LARK_GLOBAL,
3829
- target,
3830
- keys: formatLinkKeys(target, property) || [property],
3831
- newVal,
3832
- oldVal
3833
- };
3834
- };
3835
- var handlePromise = (child, parent, key) => {
3836
- child.then((res) => {
3837
- const parentProxy = ProxyCache.get(parent);
3838
- if (parentProxy) {
3839
- const oldVal = parentProxy[key];
3840
- if (ProxyCache.get(child) === oldVal) {
3841
- parentProxy[key] = res;
3842
- }
3843
- }
3844
- }).catch((err) => {
3845
- const parentProxy = ProxyCache.get(parent);
3846
- if (parentProxy) {
3847
- const oldVal = parentProxy[key];
3848
- if (ProxyCache.get(child) === oldVal) {
3849
- parentProxy[key] = err;
3850
- }
3851
- }
3852
- });
3853
- };
3854
- var _deleteKey = "_delete";
3855
- var _markObjKey = "_markObj";
3856
- var mark2 = (host, key) => {
3857
- let sign;
3858
- if (!host[_deleteKey]) {
3859
- const markHost = host[_markObjKey] || (host[_markObjKey] = {});
3860
- if (!hasOwnProperty(markHost, key)) {
3861
- markHost[key] = 0;
3862
- }
3863
- sign = ++markHost[key];
3864
- }
3865
- return () => {
3866
- const temp = host[_markObjKey];
3867
- return !!temp && sign === temp[key];
4091
+ const setState = (patch) => {
4092
+ const current = getState();
4093
+ const resolved = typeof patch === "function" ? patch(current) : patch;
4094
+ Router.to(resolved);
3868
4095
  };
3869
- };
3870
- var unmark2 = (host) => {
3871
- host[_deleteKey] = 1;
3872
- host[_markObjKey] = 0;
3873
- };
3874
- function createState(initialData, config2) {
3875
- if (!isObject(initialData)) return initialData;
3876
- const mergedConfig = Object.assign({ ...defStateConfig }, config2);
3877
- const data = Array.isArray(initialData) ? [] : Object.create(Object.getPrototypeOf(initialData));
3878
- const handler = {
3879
- get(target, property, _receiver) {
3880
- if (Array.isArray(target) && property in ArrMethods) {
3881
- return ArrMethods[property];
3882
- }
3883
- return Reflect.get(target, property);
3884
- },
3885
- set(target, property, newVal, receiver) {
3886
- const strProp = property;
3887
- const oldVal = Reflect.get(target, property);
3888
- const preventTrigger = isLazySet(strProp);
3889
- const needKeepVal = needKeep(strProp);
3890
- const canSet = canSetNewVal({
3891
- target,
3892
- property: strProp,
3893
- newVal,
3894
- oldVal
3895
- });
3896
- if (!canSet) return true;
3897
- const proxyTarget = receiver || state;
3898
- newVal = getNewVal({
3899
- target: proxyTarget,
3900
- property: strProp,
3901
- newVal,
3902
- needKeepVal
3903
- });
3904
- Reflect.set(target, property, newVal, receiver);
3905
- if (preventTrigger) return true;
3906
- const payload = genPayload({
3907
- target: proxyTarget,
3908
- property: strProp,
3909
- newVal,
3910
- oldVal
3911
- });
3912
- trigger(payload);
3913
- return true;
3914
- },
3915
- deleteProperty(target, property) {
3916
- const strProp = property;
3917
- if (!hasOwnProperty(target, strProp)) return true;
3918
- const oldVal = Reflect.get(target, property);
3919
- Reflect.deleteProperty(target, property);
3920
- const payload = genPayload({
3921
- target: state,
3922
- property: strProp,
3923
- newVal: null,
3924
- oldVal
3925
- });
3926
- trigger(payload);
3927
- return true;
3928
- }
3929
- };
3930
- const state = new Proxy(data, handler);
3931
- ProxyCache.set(initialData, state);
3932
- setStateConfig(state, mergedConfig);
3933
- lazySet(state, initialData);
3934
- return state;
3935
- }
3936
- var curLazySetKey = null;
3937
- function lazySet(target, data) {
3938
- if (isObject(data)) {
3939
- Reflect.ownKeys(data).forEach((key) => {
3940
- const strKey = key;
3941
- if (curLazySetKey) throw new Error("[lark-store] lazy set key conflict");
3942
- curLazySetKey = strKey;
3943
- target[strKey] = data[strKey];
3944
- });
3945
- }
4096
+ return [getState(), setState];
3946
4097
  }
3947
- function isLazySet(property) {
3948
- if (curLazySetKey === property) {
3949
- curLazySetKey = "";
3950
- return true;
3951
- }
3952
- return false;
4098
+
4099
+ // src/store.ts
4100
+ var COMPUTED_BRAND = /* @__PURE__ */ Symbol("lark-store-computed");
4101
+ function isComputedMarker(val) {
4102
+ return val !== null && typeof val === "object" && val[COMPUTED_BRAND] === true;
3953
4103
  }
3954
- function shallowSet(target, key, data) {
3955
- if (!isState(target))
3956
- throw new Error("[lark-store] shallowSet only supports state objects");
3957
- if (!isObject(data)) return target[key] = data;
3958
- keep(key);
3959
- const config2 = getStateConfig(target);
3960
- const linkKeys = createLinkKeys(target, key);
3961
- target[key] = createState(data, {
3962
- ...config2,
3963
- linkKeys,
3964
- shallow: true
3965
- });
4104
+ function computed(deps, fn) {
4105
+ return { [COMPUTED_BRAND]: true, deps, fn };
3966
4106
  }
3967
- var GlobalDeps = /* @__PURE__ */ new Map();
3968
- function track(payload) {
3969
- const { belong, trackList = [] } = payload;
3970
- let deps = GlobalDeps.get(belong);
3971
- if (!deps) GlobalDeps.set(belong, deps = /* @__PURE__ */ new Map());
3972
- for (const { key, cb } of trackList) {
3973
- if (isFunction(cb)) {
3974
- let callbacks = deps.get(key);
3975
- if (!callbacks) deps.set(key, callbacks = /* @__PURE__ */ new Set());
3976
- callbacks.add(cb);
3977
- }
3978
- }
3979
- }
3980
- function trigger(payload) {
3981
- const { belong, keys: keys2 } = payload;
3982
- const store = getStore(belong);
3983
- if (store && store.status !== 2 /* ACTIVE */ && belong !== LARK_GLOBAL)
3984
- return;
3985
- const deps = GlobalDeps.get(belong);
3986
- if (!deps) return;
3987
- const tasks = /* @__PURE__ */ new Set();
3988
- for (const key of keys2) {
3989
- const callbacks = deps.get(key);
3990
- if (callbacks) {
3991
- for (const cb of callbacks) {
3992
- tasks.add({ cb, params: { [key]: true } });
3993
- }
3994
- }
3995
- }
3996
- if (tasks.size === 0) return;
3997
- const scheduler = store?.scheduler;
3998
- run(Array.from(tasks), scheduler);
3999
- }
4000
- function clear(payload) {
4001
- if (!payload) {
4002
- GlobalDeps.clear();
4003
- return;
4004
- }
4005
- const { belong, clearList } = payload;
4006
- const deps = GlobalDeps.get(belong);
4007
- if (!deps) return;
4008
- const store = getStore(belong);
4009
- const scheduler = store?.scheduler;
4010
- if (clearList) {
4011
- for (const { key, cb } of clearList) {
4012
- const callbacks = deps.get(key);
4013
- if (callbacks) {
4014
- if (!cb) {
4015
- deps.delete(key);
4016
- } else if (callbacks.has(cb)) {
4017
- callbacks.delete(cb);
4018
- if (scheduler && !isFunction(scheduler)) {
4019
- scheduler.delete([{ cb }]);
4020
- }
4021
- if (callbacks.size === 0) deps.delete(key);
4107
+ var storeRegistry = /* @__PURE__ */ new Map();
4108
+ function create(name, creator) {
4109
+ const listeners = /* @__PURE__ */ new Set();
4110
+ const computedDefs = /* @__PURE__ */ new Map();
4111
+ const computedKeys = /* @__PURE__ */ new Set();
4112
+ const actionKeys = /* @__PURE__ */ new Set();
4113
+ let state;
4114
+ let destroyed = false;
4115
+ const getState = () => state;
4116
+ const setState = (partial) => {
4117
+ if (destroyed) return;
4118
+ const prevState = state;
4119
+ const resolved = typeof partial === "function" ? partial(prevState) : partial;
4120
+ const nextState = { ...prevState };
4121
+ let changed = false;
4122
+ for (const key in resolved) {
4123
+ if (Object.prototype.hasOwnProperty.call(resolved, key) && !computedKeys.has(key) && !actionKeys.has(key)) {
4124
+ const newVal = resolved[key];
4125
+ if (!Object.is(prevState[key], newVal)) {
4126
+ nextState[key] = newVal;
4127
+ changed = true;
4022
4128
  }
4023
4129
  }
4024
4130
  }
4025
- } else {
4026
- deps.clear();
4027
- GlobalDeps.delete(belong);
4028
- if (scheduler && !isFunction(scheduler)) {
4029
- scheduler.clear();
4030
- }
4031
- }
4032
- }
4033
- var _storeName = /* @__PURE__ */ Symbol("store-name");
4034
- var _storeStatus = /* @__PURE__ */ Symbol("store-status");
4035
- var _storeScheduler = /* @__PURE__ */ Symbol("store-scheduler");
4036
- var _storeCreate = /* @__PURE__ */ Symbol("fn:store-create");
4037
- var _storeBoot = /* @__PURE__ */ Symbol("fn:store-boot");
4038
- var _storeDestroy = /* @__PURE__ */ Symbol("fn:store-destroy");
4039
- var _innerStore = /* @__PURE__ */ Symbol("inner-store");
4040
- var _outerStore = /* @__PURE__ */ Symbol("outer-store");
4041
- var _originState = /* @__PURE__ */ Symbol("origin-state");
4042
- var _stateKeys = /* @__PURE__ */ Symbol("state-keys");
4043
- var _storeState = /* @__PURE__ */ Symbol("store-state");
4044
- var _storeDefScheduler = /* @__PURE__ */ Symbol("store-def-scheduler");
4045
- var _storeProxy = /* @__PURE__ */ Symbol("fn:store-proxy");
4046
- var BaseStore = class {
4047
- [_storeStatus] = 0 /* BEFORE_CREATE */;
4048
- [_storeDefScheduler] = getDefScheduler;
4049
- [_storeBoot]() {
4050
- this[_storeStatus] = 2 /* ACTIVE */;
4051
- return this[_storeProxy](true);
4052
- }
4053
- [_storeDestroy]() {
4054
- clear({ belong: this[_storeName] });
4055
- this[_storeState] = createState(this[_originState], {
4056
- belong: this[_storeName]
4057
- });
4058
- this[_storeStatus] = 3 /* DESTROYED */;
4059
- }
4060
- /**
4061
- *
4062
- * @param body - The object returned by the creator function
4063
- * @param excludeFns - Function keys to exclude from handlers (e.g. ['observe'])
4064
- */
4065
- [_storeCreate](body, excludeFns = ["observe"]) {
4066
- this[_storeStatus] = 1 /* CREATED */;
4067
- if (isObject(body)) {
4068
- const state = {};
4069
- const handlers = {};
4070
- Reflect.ownKeys(body).forEach((key) => {
4071
- const strKey = key;
4072
- const val = body[strKey];
4073
- if (isFunction(val)) {
4074
- if (!excludeFns.includes(strKey)) handlers[strKey] = val;
4075
- } else {
4076
- state[strKey] = val;
4077
- }
4078
- });
4079
- Object.assign(this, handlers);
4080
- this[_originState] = cloneData(state);
4081
- this[_stateKeys] = Object.keys(state);
4082
- this[_storeState] = createState(state, { belong: this[_storeName] });
4131
+ if (!changed) return;
4132
+ state = nextState;
4133
+ recomputeIfNeeded(prevState);
4134
+ for (const listener of listeners) {
4135
+ listener(state, prevState);
4083
4136
  }
4084
- }
4085
- constructor(name, config2) {
4086
- this[_storeName] = name;
4087
- this[_storeScheduler] = config2?.scheduler || this[_storeDefScheduler]();
4088
- this[_outerStore] = this[_storeProxy](true);
4089
- }
4090
- [_innerStore]() {
4091
- return this[_storeProxy]();
4092
- }
4093
- get status() {
4094
- return this[_storeStatus];
4095
- }
4096
- get storeName() {
4097
- return this[_storeName];
4098
- }
4099
- get scheduler() {
4100
- return this[_storeScheduler];
4101
- }
4102
- [_storeProxy](toOut = false) {
4103
- const self = this;
4104
- return new Proxy(self, {
4105
- get(target, property) {
4106
- if (self[_stateKeys].includes(property)) {
4107
- const val = self[_storeState][property];
4108
- return toOut ? cloneData(val) : val;
4109
- }
4110
- return Reflect.get(target, property);
4111
- },
4112
- set(_target, property, val) {
4113
- if (toOut) return true;
4114
- if (self[_stateKeys].includes(property)) {
4115
- self[_storeState][property] = val;
4116
- }
4117
- return true;
4118
- },
4119
- has(target, property) {
4120
- return Reflect.has(target, property) || self[_stateKeys].includes(property);
4137
+ };
4138
+ const recomputeIfNeeded = (prevState) => {
4139
+ if (computedDefs.size === 0) return;
4140
+ const changedKeys2 = /* @__PURE__ */ new Set();
4141
+ for (const key of Object.keys(state)) {
4142
+ if (!Object.is(
4143
+ state[key],
4144
+ prevState[key]
4145
+ )) {
4146
+ changedKeys2.add(key);
4121
4147
  }
4122
- });
4123
- }
4124
- };
4125
- var LarkUtils = {
4126
- isLarkView(instance) {
4127
- return isObject(instance) && !!instance.updater;
4128
- },
4129
- getRender(view) {
4130
- return view.updater.digest.bind(view.updater);
4131
- },
4132
- getDataSetter(view) {
4133
- return view.updater.set.bind(view.updater);
4134
- },
4135
- onDestroy(view, cb) {
4136
- view.on("destroy", cb);
4137
- }
4138
- };
4139
- var getLarkAdapter = (storeName) => ({
4140
- Store: LarkStore,
4141
- useStore: ((view) => {
4142
- const store = getStore(storeName);
4143
- return store[_storeBoot](view);
4144
- })
4145
- });
4146
- var innerObserveFlags = /* @__PURE__ */ Symbol("store-inner-observe-flags");
4147
- var boundViews = /* @__PURE__ */ Symbol("bound-views");
4148
- var LarkStore = class extends BaseStore {
4149
- [boundViews] = /* @__PURE__ */ new Set();
4150
- [innerObserveFlags] = /* @__PURE__ */ new Set();
4151
- [_storeBoot](view) {
4152
- if (view && LarkUtils.isLarkView(view) && !this[boundViews].has(view)) {
4153
- this[boundViews].add(view);
4154
- LarkUtils.onDestroy(view, () => {
4155
- this[boundViews].delete(view);
4156
- });
4157
4148
  }
4158
- return super[_storeBoot]();
4159
- }
4160
- [_storeDestroy]() {
4161
- this[boundViews].clear();
4162
- this[innerObserveFlags].clear();
4163
- super[_storeDestroy]();
4164
- }
4165
- observe(view, keys2, defCallback) {
4166
- if (this[_storeStatus] !== 2 /* ACTIVE */) return noop;
4167
- let observeKeys = keys2;
4168
- const _view = view;
4169
- const renderFn = _view ? LarkUtils.getRender(_view) : noop;
4170
- const dateSetterFn = _view ? LarkUtils.getDataSetter(_view) : noop;
4171
- const isInnerObserve = !view;
4172
- const innerFlags = /* @__PURE__ */ new Set();
4173
- const storeInnerObserveFlags = this[innerObserveFlags];
4174
- if (isFunction(keys2)) {
4175
- const res = keys2();
4176
- if (Array.isArray(res)) observeKeys = res;
4177
- }
4178
- const defSetter = (immediate, key, alias, transform) => () => {
4179
- const stateVal = getDataByKey(this, key);
4180
- let data = { [alias || key]: stateVal };
4181
- if (transform && isFunction(transform)) {
4182
- const newData = transform(stateVal);
4183
- if (isObject(newData)) data = newData;
4184
- }
4185
- if (immediate) dateSetterFn(data);
4186
- else renderFn(data);
4187
- };
4188
- const getList = (immediate = false) => {
4189
- const list = [];
4190
- for (const item of observeKeys) {
4191
- if (!item) continue;
4192
- const payload = typeof item === "string" ? { key: item } : item;
4193
- const { cb: cbDefault = defCallback, key } = payload;
4194
- let cb = cbDefault;
4195
- const { alias, lazy = true, transform } = payload;
4196
- if (!key) continue;
4197
- const c1 = !immediate;
4198
- const c2 = !cb && !!_view;
4199
- const c3 = !!cb && String(lazy) === "false";
4200
- if (!(c1 || c2 || c3)) continue;
4201
- if (isInnerObserve && cb) {
4202
- const flag = `storeInner_${key}_${observeKeys.join("-")}_${cb.toString()}`;
4203
- if (!storeInnerObserveFlags.has(flag)) {
4204
- storeInnerObserveFlags.add(flag);
4205
- innerFlags.add(flag);
4206
- }
4149
+ let recomputed = false;
4150
+ for (const [key, def] of computedDefs) {
4151
+ if (def.deps.some((dep) => changedKeys2.has(dep))) {
4152
+ const newVal = def.fn();
4153
+ if (!Object.is(state[key], newVal)) {
4154
+ state[key] = newVal;
4155
+ recomputed = true;
4207
4156
  }
4208
- if (c2) cb = defSetter(immediate, key, alias, transform);
4209
- if (cb) list.push({ key, cb });
4210
4157
  }
4211
- return list;
4212
- };
4213
- const trackList = getList();
4214
- track({ belong: this[_storeName], trackList });
4215
- if (_view) {
4216
- LarkUtils.onDestroy(
4217
- _view,
4218
- () => clear({ belong: this[_storeName], clearList: trackList })
4219
- );
4220
4158
  }
4221
- if (!isInnerObserve) run(getList(true));
4159
+ if (recomputed) {
4160
+ }
4161
+ };
4162
+ const subscribe = (listener) => {
4163
+ listeners.add(listener);
4222
4164
  return () => {
4223
- clear({ belong: this[_storeName], clearList: trackList });
4224
- innerFlags.forEach((flag) => storeInnerObserveFlags.delete(flag));
4165
+ listeners.delete(listener);
4225
4166
  };
4226
- }
4227
- };
4228
- var getReactAdapter = (storeName) => ({
4229
- Store: ReactStore,
4230
- useStore: (() => {
4231
- const store = getStore(storeName);
4232
- return store[_storeBoot]();
4233
- })
4234
- });
4235
- var observeSym = /* @__PURE__ */ Symbol("observe");
4236
- var getLastState = /* @__PURE__ */ Symbol("get-last-state");
4237
- var ReactStore = class extends BaseStore {
4238
- stateChangeCount = 0;
4239
- lastCount = 0;
4240
- lastState = null;
4241
- constructor(name, config2) {
4242
- const effectiveConfig = config2 && !config2.scheduler ? {
4243
- ...config2,
4244
- scheduler: (cb, params) => cb(params)
4245
- } : config2;
4246
- super(name, effectiveConfig);
4247
- }
4248
- [_storeCreate](body) {
4249
- super[_storeCreate](body);
4250
- this[observeSym](() => {
4251
- this.stateChangeCount += 1;
4252
- });
4253
- }
4254
- isStateChanged() {
4255
- const currCount = this.stateChangeCount;
4256
- const changed = this.lastCount !== currCount;
4257
- this.lastCount = currCount;
4258
- return changed;
4259
- }
4260
- [getLastState](handlers) {
4261
- if (this.isStateChanged() || !this.lastState) {
4262
- const state = this[_storeState];
4263
- const immutableState = freezeData(state);
4264
- this.lastState = Object.assign({}, handlers, immutableState);
4265
- }
4266
- return this.lastState;
4267
- }
4268
- [observeSym](cb) {
4269
- const tasks = this[_stateKeys].map((key) => ({ key, cb }));
4270
- track({ belong: this[_storeName], trackList: tasks });
4271
- return () => clear({ belong: this[_storeName], clearList: tasks });
4272
- }
4273
- };
4274
- var freezeData = (target) => {
4275
- const data = {};
4276
- const keys2 = Object.keys(target);
4277
- for (const key of keys2) {
4278
- const value = target[key];
4279
- if (Array.isArray(value)) {
4280
- data[key] = value.map(
4281
- (item) => isObject(item) ? freezeData(item) : item
4282
- );
4283
- } else if (isObject(value)) {
4284
- data[key] = freezeData(value);
4167
+ };
4168
+ const destroy = () => {
4169
+ destroyed = true;
4170
+ listeners.clear();
4171
+ storeRegistry.delete(name);
4172
+ };
4173
+ const api = { getState, setState, subscribe, destroy };
4174
+ const body = creator(setState, getState);
4175
+ const initialState = {};
4176
+ const actions = {};
4177
+ for (const key of Object.keys(body)) {
4178
+ const val = body[key];
4179
+ if (isComputedMarker(val)) {
4180
+ computedDefs.set(key, val);
4181
+ computedKeys.add(key);
4182
+ initialState[key] = void 0;
4183
+ } else if (typeof val === "function") {
4184
+ actions[key] = val;
4185
+ actionKeys.add(key);
4285
4186
  } else {
4286
- data[key] = value;
4187
+ initialState[key] = val;
4287
4188
  }
4288
4189
  }
4289
- return data;
4290
- };
4291
- var getNodeAdapter = (storeName) => ({
4292
- Store: NodeStore,
4293
- useStore: (() => {
4294
- const store = getStore(storeName);
4295
- return store[_storeBoot]();
4296
- })
4297
- });
4298
- var NodeStore = class extends BaseStore {
4299
- observe(key, callback, immediate = true) {
4300
- const tasks = [{ key, cb: callback }];
4301
- track({ belong: this[_storeName], trackList: tasks });
4302
- if (immediate) run(tasks);
4303
- return () => clear({ belong: this[_storeName], clearList: tasks });
4304
- }
4305
- };
4306
- var getAdapter = (platform, storeName) => {
4307
- switch (platform) {
4308
- case "react" /* React */:
4309
- return getReactAdapter(storeName);
4310
- case "node" /* Node */:
4311
- return getNodeAdapter(storeName);
4312
- case "lark" /* Lark */:
4313
- default:
4314
- return getLarkAdapter(storeName);
4315
- }
4316
- };
4317
- var getPlatform = (comp) => {
4318
- if (LarkUtils.isLarkView(comp)) return "lark" /* Lark */;
4319
- return void 0;
4320
- };
4321
- var extendApis = { lazySet, shallowSet };
4322
- var StoreCache = /* @__PURE__ */ new Map();
4323
- function defineStore(name, creator, config2) {
4324
- if (StoreCache.has(name)) {
4325
- name = name + "_copy";
4326
- }
4327
- const { platform = "lark" /* Lark */ } = config2 || {};
4328
- const adapter = getAdapter(platform, name);
4329
- const StoreClass = adapter.Store;
4330
- const useStore = adapter.useStore;
4331
- const store = new StoreClass(name, config2);
4332
- store[_storeCreate](
4333
- creator(store[_innerStore](), extendApis)
4334
- );
4335
- Object.defineProperties(useStore, {
4336
- $storeName: { value: name, configurable: true },
4337
- $destroyFn: { value: () => store[_storeDestroy](), configurable: true }
4338
- });
4339
- if (!StoreCache.has(name)) {
4340
- StoreCache.set(name, { store, creator, config: config2, useStore });
4341
- }
4342
- return useStore;
4343
- }
4344
- function getStore(name) {
4345
- if (name && StoreCache.has(name)) {
4346
- const entry = StoreCache.get(name);
4347
- return entry ? entry.store : void 0;
4348
- }
4349
- return void 0;
4350
- }
4351
- function delStore(name) {
4352
- if (name && StoreCache.has(name)) StoreCache.delete(name);
4353
- }
4354
- function getUseStore(name) {
4355
- if (name && StoreCache.has(name)) {
4356
- const entry = StoreCache.get(name);
4357
- return entry ? entry.useStore : void 0;
4190
+ state = { ...initialState, ...actions };
4191
+ for (const [key, def] of computedDefs) {
4192
+ state[key] = def.fn();
4358
4193
  }
4359
- return void 0;
4360
- }
4361
- function cloneStore(name, useStore, config2) {
4362
- const oldStoreName = useStore["$storeName"];
4363
- const cached = StoreCache.get(oldStoreName);
4364
- const oldStoreCreator = cached?.creator;
4365
- const oldConfig = cached?.config || {};
4366
- if (!name || !oldStoreCreator) return;
4367
- const mergedConfig = { ...oldConfig, ...config2 || {} };
4368
- return defineStore(
4369
- name,
4370
- oldStoreCreator,
4371
- mergedConfig
4372
- );
4194
+ storeRegistry.set(name, api);
4195
+ return api;
4373
4196
  }
4374
- function isStoreActive(name) {
4375
- if (name && StoreCache.has(name)) {
4376
- const entry = StoreCache.get(name);
4377
- return entry ? entry.store.status === 2 /* ACTIVE */ : false;
4378
- }
4379
- return false;
4380
- }
4381
- var cellCount = 0;
4382
- function cell(data) {
4383
- return createState(data, {
4384
- belong: LARK_GLOBAL,
4385
- linkKeys: `${LARK_GLOBAL}_${cellCount++}`
4386
- });
4387
- }
4388
- function observeCell(state, cb, immediate = true) {
4389
- const linkKeys = getLinkKeys(state);
4390
- if (!linkKeys)
4391
- return () => {
4392
- };
4393
- const keys2 = linkKeys.split(".");
4394
- const key = keys2[keys2.length - 1];
4395
- const list = [{ key, cb }];
4396
- track({ belong: LARK_GLOBAL, trackList: list });
4397
- if (immediate) cb();
4398
- return () => clear({ belong: LARK_GLOBAL, clearList: list });
4197
+ function isLarkView(instance) {
4198
+ if (!instance || typeof instance !== "object") return false;
4199
+ const obj = instance;
4200
+ const updater = obj["updater"];
4201
+ return updater !== null && typeof updater === "object" && typeof updater["set"] === "function" && typeof updater["digest"] === "function";
4399
4202
  }
4400
- function multi(useStore) {
4401
- const storeName = useStore["$storeName"];
4402
- const flagSym = `lark-comp-${storeName}`;
4403
- const map = /* @__PURE__ */ new Map();
4404
- let rootViewPath;
4405
- const getFlag = (viewContext) => {
4406
- const owner = viewContext["owner"];
4407
- const viewPath = owner?.["path"] || "";
4408
- const viewId = owner?.["id"] || "";
4409
- let flag;
4410
- if (viewPath === rootViewPath) {
4411
- flag = `${flagSym}-${viewId}`;
4412
- } else {
4413
- flag = owner?.["viewInitParams"]?.[flagSym];
4414
- }
4415
- if (owner && isFunction(owner["mountFrame"])) {
4416
- const rawMountFrame = owner["mountFrame"];
4417
- owner["mountFrame"] = (vfId, viewPath2, viewInitParams = {}) => rawMountFrame.call(
4418
- owner,
4419
- vfId,
4420
- viewPath2,
4421
- Object.assign(viewInitParams, { [flagSym]: flag })
4422
- );
4423
- }
4424
- return flag;
4203
+ function bindStore(view, store, selector) {
4204
+ if (!isLarkView(view)) return () => {
4425
4205
  };
4426
- const useFn = ((view) => {
4427
- if (!view)
4428
- throw new Error(
4429
- "[@lark.js/mvc error] multi: cannot find the view instance"
4430
- );
4431
- const viewCtx = view;
4432
- const flag = viewCtx[flagSym];
4433
- if (map.has(flag)) return map.get(flag);
4434
- const newFn = cloneStore(flag, useStore);
4435
- map.set(flag, newFn);
4436
- return useFn(view);
4437
- });
4438
- const mixinObj = {
4439
- make() {
4440
- if (!rootViewPath) {
4441
- const owner = this["owner"];
4442
- rootViewPath = owner?.["path"] || "";
4206
+ const extract = (s) => {
4207
+ if (selector) return selector(s);
4208
+ const result = {};
4209
+ for (const key in s) {
4210
+ if (Object.prototype.hasOwnProperty.call(s, key) && typeof s[key] !== "function") {
4211
+ result[key] = s[key];
4443
4212
  }
4444
- this[flagSym] = getFlag(this);
4445
4213
  }
4214
+ return result;
4446
4215
  };
4447
- return [useFn, mixinObj];
4216
+ view.updater.set(extract(store.getState()));
4217
+ view.updater.digest();
4218
+ const off = store.subscribe((state) => {
4219
+ view.updater.set(extract(state));
4220
+ view.updater.digest();
4221
+ });
4222
+ view.on("destroy", off);
4223
+ return off;
4448
4224
  }
4225
+ var defineStore = create;
4449
4226
 
4450
4227
  // src/compiler.ts
4451
4228
  import { parse as babelParse } from "@babel/parser";
4452
- var SPLITTER2 = "";
4453
- var VIEW_ID_PLACEHOLDER = "";
4229
+ var SPLITTER2 = String.fromCharCode(30);
4230
+ var VIEW_ID_PLACEHOLDER = String.fromCharCode(31);
4454
4231
  function jsObjectToUrlParams(paramsStr) {
4455
4232
  const trimmed = paramsStr.trim();
4456
4233
  if (!/^[{[]/.test(trimmed) && /=/.test(trimmed)) {
@@ -4510,7 +4287,7 @@ function addLineMarkers(source) {
4510
4287
  if (parts.length > 1) {
4511
4288
  const reconstructed = parts.map((part, i) => {
4512
4289
  if (i === 0) return part;
4513
- return openTag + SPLITTER2 + ++lineNo;
4290
+ return openTag + SPLITTER2 + ++lineNo + part;
4514
4291
  }).join("");
4515
4292
  result.push(reconstructed);
4516
4293
  } else {
@@ -4646,7 +4423,7 @@ function convertArtExpression(code, debug, lineNo, blockStack = []) {
4646
4423
  return `${debugPrefix}<%for(${forExpr}){%>`;
4647
4424
  }
4648
4425
  const tokens = code.split(/\s+/);
4649
- const keyword = tokens.shift();
4426
+ const keyword = tokens.shift() ?? "";
4650
4427
  switch (keyword) {
4651
4428
  case "if": {
4652
4429
  blockStack.push({ ctrl: "if", line: lineNo });
@@ -4870,13 +4647,9 @@ function compileToFunction(source, debug, file) {
4870
4647
  }
4871
4648
  const viewIdRegExp = new RegExp(String.fromCharCode(31), "g");
4872
4649
  funcSource = funcSource.replace(viewIdRegExp, `'+$viewId+'`);
4873
- const atRule = hasAtRule ? `if(!$refFn){$refFn=(ref,v,k,f)=>{for(f=ref[$splitter];--f;)if(ref[k=$splitter+f]===v)return k;ref[k=$splitter+ref[$splitter]++]=v;return k;}}` : "";
4874
- const encode = `if(!$strSafe){let $entMap={'&':'amp','<':'lt','>':'gt','"':'#34','\\'':'#39','\`':'#96'},$entReg=/[&<>"'\`]/g,$entFn=m=>'&'+$entMap[m]+';';$strSafe=v=>''+(v==null?'':v);$encHtml=v=>$strSafe(v).replace($entReg,$entFn)}`;
4875
- const encodeURIMore = `if(!$encUri){let $uriMap={'!':'%21','\\'':'%27','(':'%28',')':'%29','*':'%2A'},$uriFn=m=>$uriMap[m],$uriReg=/[!')(*]/g;$encUri=v=>encodeURIComponent($strSafe(v)).replace($uriReg,$uriFn)}`;
4876
- const encodeQuote = `if(!$encQuote){let $qReg=/['"\\\\]/g;$encQuote=v=>$strSafe(v).replace($qReg,'\\\\$&')}`;
4650
+ void hasAtRule;
4877
4651
  const refFallback = "if(!$refAlt)$refAlt=$data;";
4878
- const fns = `${refFallback}${encode}${encodeURIMore}${encodeQuote}${atRule};`;
4879
- const fullSource = `${fns}let $splitter='\\x1e',$tmp,$out=''{{VARS}};${funcSource}return $out`;
4652
+ const fullSource = `${refFallback}let $splitter='\\x1e',$tmp,$out=''{{VARS}};${funcSource}return $out`;
4880
4653
  return `($data,$viewId,$refAlt,$encHtml,$strSafe,$encUri,$refFn,$encQuote)=>{${fullSource}}`;
4881
4654
  }
4882
4655
  function compileTemplate(source, options = {}) {
@@ -4888,15 +4661,12 @@ function compileTemplate(source, options = {}) {
4888
4661
  const funcBody = compileToFunction(finalSource, debug, file);
4889
4662
  const varDeclarations = globalVars.map((key) => `,${key}=$data.${key}`).join("");
4890
4663
  const funcWithVars = funcBody.replace("{{VARS}}", () => varDeclarations);
4891
- return `export default function(data, selfId, refData) {
4664
+ return `import { encHtml as __larkEncHtml, strSafe as __larkStrSafe, encUri as __larkEncUri, encQuote as __larkEncQuote, refFn as __larkRefFn } from "@lark.js/mvc/runtime";
4665
+ export default function(data, viewId, refData) {
4892
4666
  let $data = data || {},
4893
- $viewId = selfId || '';
4667
+ $viewId = viewId || '';
4894
4668
  return (${funcWithVars})($data, $viewId, refData,
4895
- /* $encHtml */ v => String(v == null ? '' : v).replace(/[&<>"'\`]/g, m => '&' + ({'&':'amp','<':'lt','>':'gt','"':'#34',"'":'#39','\`':'#96'})[m] + ';'),
4896
- /* $strSafe */ v => String(v == null ? '' : v),
4897
- /* $encUri */ null,
4898
- /* $refFn */ null,
4899
- /* $encQuote */ null
4669
+ __larkEncHtml, __larkStrSafe, __larkEncUri, __larkRefFn, __larkEncQuote
4900
4670
  );
4901
4671
  }`;
4902
4672
  }
@@ -4910,7 +4680,7 @@ function extractGlobalVars(source) {
4910
4680
  const htmlStore = {};
4911
4681
  let htmlIndex = 0;
4912
4682
  let lastIndex = 0;
4913
- const htmlKey = "";
4683
+ const htmlKey = String.fromCharCode(5);
4914
4684
  template.replace(
4915
4685
  templateCmdRegExp,
4916
4686
  (match, operate, content, offset) => {
@@ -5021,31 +4791,37 @@ function walkAst(ast, visitors) {
5021
4791
  if (visitors[type]) {
5022
4792
  visitors[type](node);
5023
4793
  }
4794
+ const bag = node;
5024
4795
  for (const key of Object.keys(node)) {
5025
4796
  if (key === "type" || key === "start" || key === "end" || key === "loc" || key === "range")
5026
4797
  continue;
5027
- if (type === "MemberExpression" && key === "property" && !node.computed)
5028
- continue;
5029
- if (type === "ObjectProperty" && key === "key" && !node.computed) {
5030
- continue;
4798
+ if (type === "MemberExpression" && key === "property") {
4799
+ const me = node;
4800
+ if (!me.computed) continue;
5031
4801
  }
5032
- if (type === "ObjectMethod" && key === "key" && !node.computed) {
5033
- continue;
4802
+ if (type === "ObjectProperty" && key === "key") {
4803
+ const op = node;
4804
+ if (!op.computed) continue;
4805
+ }
4806
+ if (type === "ObjectMethod" && key === "key") {
4807
+ const om = node;
4808
+ if (!om.computed) continue;
5034
4809
  }
5035
- const child = node[key];
4810
+ const child = bag[key];
5036
4811
  if (Array.isArray(child)) {
5037
4812
  for (const item of child) {
5038
- if (item && typeof item === "object" && typeof item.type === "string") {
5039
- visit(item);
5040
- }
4813
+ if (isAstNode(item)) visit(item);
5041
4814
  }
5042
- } else if (child && typeof child === "object" && typeof child.type === "string") {
4815
+ } else if (isAstNode(child)) {
5043
4816
  visit(child);
5044
4817
  }
5045
4818
  }
5046
4819
  }
5047
4820
  visit(ast);
5048
4821
  }
4822
+ function isAstNode(v) {
4823
+ return !!v && typeof v === "object" && typeof v.type === "string";
4824
+ }
5049
4825
  var BUILTIN_GLOBALS = {
5050
4826
  // ─── Template runtime helpers (injected by compileToFunction) ───────
5051
4827
  //
@@ -5199,15 +4975,16 @@ var BUILTIN_GLOBAL_SET = new Set(Object.keys(BUILTIN_GLOBALS));
5199
4975
  export {
5200
4976
  CALL_BREAK_TIME,
5201
4977
  Cache,
4978
+ cross_site_default as CrossSite,
5202
4979
  EVENT_METHOD_REGEXP,
5203
4980
  EventDelegator,
5204
4981
  EventEmitter,
5205
4982
  Frame,
4983
+ FrameVisualBridge,
5206
4984
  Framework,
5207
4985
  LARK_VIEW,
5208
4986
  Payload,
5209
- Platform,
5210
- ROUTER_EVENTS,
4987
+ RouterEvents as ROUTER_EVENTS,
5211
4988
  Router,
5212
4989
  SPLITTER,
5213
4990
  Service,
@@ -5220,59 +4997,52 @@ export {
5220
4997
  applyStyle,
5221
4998
  applyVdomOps,
5222
4999
  assign,
5223
- cell,
5224
- classExtend,
5225
- cloneData,
5226
- cloneStore,
5000
+ bindStore,
5227
5001
  compileTemplate,
5228
- createState,
5002
+ computed,
5003
+ create,
5229
5004
  createVdomRef,
5230
5005
  defineStore,
5231
- delStore,
5006
+ defineView,
5232
5007
  encodeHTML,
5233
5008
  encodeQ,
5234
5009
  encodeSafe,
5235
5010
  encodeURIExtra,
5236
5011
  ensureElementId,
5237
5012
  extractGlobalVars,
5013
+ config as frameworkConfig,
5238
5014
  funcWithTry,
5239
5015
  generateId,
5240
5016
  getAttribute,
5241
5017
  getById,
5242
- getPlatform,
5243
- getStore,
5244
- getUseStore,
5245
- has,
5018
+ getRouteMode,
5019
+ hasOwnProperty,
5246
5020
  installFrameVisualizerBridge,
5021
+ invalidateViewClass,
5247
5022
  isPlainObject,
5248
5023
  isPrimitive,
5249
5024
  isPrimitiveOrFunc,
5250
- isState,
5251
- isStoreActive,
5252
5025
  keys,
5253
- lazySet,
5254
5026
  mark,
5255
5027
  markBooted,
5256
5028
  markRouterBooted,
5257
- multi,
5258
5029
  nextCounter,
5259
5030
  nodeInside,
5260
5031
  noop,
5261
5032
  now,
5262
- observeCell,
5263
5033
  parseUri,
5264
5034
  registerViewClass,
5035
+ resetProjectsMap,
5265
5036
  safeguard,
5266
5037
  serializeFrameTree,
5267
5038
  setData,
5268
- shallowSet,
5269
- mark2 as storeMark,
5270
- unmark2 as storeUnmark,
5271
5039
  syncCounter,
5272
5040
  toMap,
5273
5041
  toUri,
5274
5042
  translateData,
5275
5043
  unmark,
5044
+ use,
5045
+ useUrlState,
5276
5046
  vdomGetCompareKey,
5277
5047
  vdomGetNode,
5278
5048
  vdomSetAttributes,