@lark.js/mvc 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -20,17 +20,19 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
- Bag: () => Bag,
24
23
  CALL_BREAK_TIME: () => CALL_BREAK_TIME,
25
24
  Cache: () => Cache,
25
+ CrossSite: () => cross_site_default,
26
26
  EVENT_METHOD_REGEXP: () => EVENT_METHOD_REGEXP,
27
27
  EventDelegator: () => EventDelegator,
28
28
  EventEmitter: () => EventEmitter,
29
29
  Frame: () => Frame,
30
+ FrameVisualBridge: () => FrameVisualBridge,
30
31
  Framework: () => Framework,
31
32
  LARK_VIEW: () => LARK_VIEW,
33
+ Payload: () => Payload,
32
34
  Platform: () => Platform,
33
- ROUTER_EVENTS: () => ROUTER_EVENTS,
35
+ ROUTER_EVENTS: () => RouterEvents,
34
36
  Router: () => Router,
35
37
  SPLITTER: () => SPLITTER,
36
38
  Service: () => Service,
@@ -44,13 +46,14 @@ __export(index_exports, {
44
46
  applyVdomOps: () => applyVdomOps,
45
47
  assign: () => assign,
46
48
  cell: () => cell,
47
- classExtend: () => classExtend,
48
49
  cloneData: () => cloneData,
49
50
  cloneStore: () => cloneStore,
50
51
  compileTemplate: () => compileTemplate,
52
+ computed: () => computed,
51
53
  createState: () => createState,
52
54
  createVdomRef: () => createVdomRef,
53
55
  defineStore: () => defineStore,
56
+ defineView: () => defineView,
54
57
  delStore: () => delStore,
55
58
  encodeHTML: () => encodeHTML,
56
59
  encodeQ: () => encodeQ,
@@ -58,6 +61,7 @@ __export(index_exports, {
58
61
  encodeURIExtra: () => encodeURIExtra,
59
62
  ensureElementId: () => ensureElementId,
60
63
  extractGlobalVars: () => extractGlobalVars,
64
+ frameworkConfig: () => config,
61
65
  funcWithTry: () => funcWithTry,
62
66
  generateId: () => generateId,
63
67
  getAttribute: () => getAttribute,
@@ -65,9 +69,9 @@ __export(index_exports, {
65
69
  getPlatform: () => getPlatform,
66
70
  getStore: () => getStore,
67
71
  getUseStore: () => getUseStore,
68
- has: () => has,
72
+ hasOwnProperty: () => hasOwnProperty,
69
73
  installFrameVisualizerBridge: () => installFrameVisualizerBridge,
70
- isArray: () => isArray,
74
+ invalidateViewClass: () => invalidateViewClass,
71
75
  isPlainObject: () => isPlainObject,
72
76
  isPrimitive: () => isPrimitive,
73
77
  isPrimitiveOrFunc: () => isPrimitiveOrFunc,
@@ -86,6 +90,7 @@ __export(index_exports, {
86
90
  observeCell: () => observeCell,
87
91
  parseUri: () => parseUri,
88
92
  registerViewClass: () => registerViewClass,
93
+ resetProjectsMap: () => resetProjectsMap,
89
94
  safeguard: () => safeguard,
90
95
  serializeFrameTree: () => serializeFrameTree,
91
96
  setData: () => setData,
@@ -97,6 +102,7 @@ __export(index_exports, {
97
102
  toUri: () => toUri,
98
103
  translateData: () => translateData,
99
104
  unmark: () => unmark,
105
+ use: () => use,
100
106
  vdomGetCompareKey: () => vdomGetCompareKey,
101
107
  vdomGetNode: () => vdomGetNode,
102
108
  vdomSetAttributes: () => vdomSetAttributes,
@@ -110,12 +116,12 @@ module.exports = __toCommonJS(index_exports);
110
116
  // src/constants.ts
111
117
  var globalCounter = 0;
112
118
  var SPLITTER = "";
113
- var ROUTER_EVENTS = {
119
+ var RouterEvents = {
114
120
  CHANGE: "change",
115
121
  CHANGED: "changed",
116
122
  PAGE_UNLOAD: "page_unload"
117
123
  };
118
- var LARK_KEYS = {
124
+ var LarkInnerKeys = {
119
125
  /** Attribute name: ldk (static key for skipping VDOM diff) */
120
126
  DIFF_KEY: "ldk",
121
127
  /** Attribute name: lak (static attribute key) */
@@ -147,7 +153,6 @@ function isPlainObject(value) {
147
153
  if (proto === null) return true;
148
154
  return proto === Object.prototype || proto === null;
149
155
  }
150
- var isArray = Array.isArray;
151
156
  function isPrimitiveOrFunc(value) {
152
157
  return !value || typeof value !== "object" && typeof value !== "function";
153
158
  }
@@ -163,13 +168,13 @@ function syncCounter(val) {
163
168
  }
164
169
  function noop() {
165
170
  }
166
- function has(owner, prop) {
171
+ function hasOwnProperty(owner, prop) {
167
172
  return owner != null && Object.prototype.hasOwnProperty.call(owner, prop);
168
173
  }
169
174
  function keys(obj) {
170
175
  const result = [];
171
176
  for (const p in obj) {
172
- if (has(obj, p)) {
177
+ if (hasOwnProperty(obj, p)) {
173
178
  result.push(p);
174
179
  }
175
180
  }
@@ -179,7 +184,7 @@ function assign(target, ...sources) {
179
184
  for (const source of sources) {
180
185
  if (source) {
181
186
  for (const p in source) {
182
- if (has(source, p)) {
187
+ if (hasOwnProperty(source, p)) {
183
188
  target[p] = source[p];
184
189
  }
185
190
  }
@@ -188,25 +193,26 @@ function assign(target, ...sources) {
188
193
  return target;
189
194
  }
190
195
  function funcWithTry(fns, args, context, configError) {
191
- const fnArray = isArray(fns) ? fns : [fns];
196
+ const fnArray = Array.isArray(fns) ? fns : [fns];
192
197
  let ret;
193
198
  for (const fn of fnArray) {
194
199
  try {
195
- ret = Function.prototype.apply.call(fn, context, args);
200
+ ret = fn.apply(context, args);
196
201
  } catch (e) {
197
- configError(e);
202
+ configError?.(e);
198
203
  }
199
204
  }
200
205
  return ret;
201
206
  }
207
+ var EMPTY_STRING_SET = /* @__PURE__ */ new Set();
202
208
  function setData(newData, oldData, changedKeys2, excludes) {
203
209
  let changed = false;
204
210
  for (const p in newData) {
205
- if (has(newData, p)) {
211
+ if (hasOwnProperty(newData, p)) {
206
212
  const now2 = newData[p];
207
213
  const old = oldData[p];
208
214
  if ((!isPrimitiveOrFunc(now2) || old !== now2) && !excludes.has(p)) {
209
- changedKeys2[p] = 1;
215
+ changedKeys2.add(p);
210
216
  changed = true;
211
217
  }
212
218
  oldData[p] = now2;
@@ -214,17 +220,25 @@ function setData(newData, oldData, changedKeys2, excludes) {
214
220
  }
215
221
  return changed;
216
222
  }
223
+ function isRefToken(s) {
224
+ if (s.length < 2 || s[0] !== SPLITTER) return false;
225
+ for (let i = 1; i < s.length; i++) {
226
+ const c = s.charCodeAt(i);
227
+ if (c < 48 || c > 57) return false;
228
+ }
229
+ return true;
230
+ }
217
231
  function translateData(data, value) {
218
232
  if (isPrimitive(value)) {
219
233
  const prop = String(value);
220
- if (prop[0] === SPLITTER && has(data, prop)) {
234
+ if (isRefToken(prop) && hasOwnProperty(data, prop)) {
221
235
  return data[prop];
222
236
  }
223
237
  return value;
224
238
  }
225
- if (isPlainObject(value) || isArray(value)) {
239
+ if (isPlainObject(value) || Array.isArray(value)) {
226
240
  for (const p in value) {
227
- if (has(value, p)) {
241
+ if (hasOwnProperty(value, p)) {
228
242
  const val = value[p];
229
243
  const newVal = translateData(data, val);
230
244
  value[p] = newVal;
@@ -261,38 +275,33 @@ function nodeInside(a, b) {
261
275
  return false;
262
276
  }
263
277
  }
264
- var paramsTemp = {};
265
- function paramsReplacer(_match, name, value) {
266
- try {
267
- paramsTemp[name] = decodeURIComponent(value || "");
268
- } catch {
269
- paramsTemp[name] = value || "";
270
- }
271
- return "";
272
- }
273
278
  function parseUri(uri) {
274
- paramsTemp = {};
279
+ const params = {};
275
280
  const path = uri.replace(URL_QUERY_HASH_REGEXP, "");
276
281
  const pathname = path;
277
282
  const actualPath = uri === pathname && IS_URL_PARAMS.test(pathname) ? "" : pathname;
278
- uri.replace(actualPath, "").replace(URL_PARAM_REGEXP, paramsReplacer);
279
- return {
280
- path: actualPath,
281
- params: { ...paramsTemp }
282
- };
283
+ uri.replace(actualPath, "").replace(URL_PARAM_REGEXP, (_match, name, value) => {
284
+ try {
285
+ params[name] = decodeURIComponent(value || "");
286
+ } catch {
287
+ params[name] = value || "";
288
+ }
289
+ return "";
290
+ });
291
+ return { path: actualPath, params };
283
292
  }
284
293
  var IS_URL_PARAMS = {
285
294
  test(s) {
286
295
  return /(?!^)=|&/.test(s);
287
296
  }
288
297
  };
289
- function toUri(path, params, keepEmptyObject) {
298
+ function toUri(path, params, keepEmpty) {
290
299
  const pairs = [];
291
300
  let hasParams = false;
292
301
  for (const p in params) {
293
- if (has(params, p)) {
302
+ if (hasOwnProperty(params, p)) {
294
303
  const v = String(params[p] ?? "");
295
- if (!keepEmptyObject || v || has(keepEmptyObject, p)) {
304
+ if (!keepEmpty || v || keepEmpty.has(p)) {
296
305
  pairs.push(`${p}=${encodeURIComponent(v)}`);
297
306
  hasParams = true;
298
307
  }
@@ -315,14 +324,6 @@ function toMap(list, key) {
315
324
  function now() {
316
325
  return Date.now ? Date.now() : (/* @__PURE__ */ new Date()).getTime();
317
326
  }
318
- function classExtend(make, base, props, statics) {
319
- const baseProto = base["prototype"] ?? {};
320
- const cProto = Object.create(baseProto);
321
- assign(cProto, props);
322
- Object.assign(make, statics);
323
- cProto.constructor = make;
324
- make["prototype"] = cProto;
325
- }
326
327
 
327
328
  // src/apply-style.ts
328
329
  var injectedStyleIds = /* @__PURE__ */ new Set();
@@ -362,32 +363,40 @@ function applyStyle(styleIdOrPairs, css) {
362
363
  }
363
364
 
364
365
  // src/mark.ts
365
- var DELETED_KEY = SPLITTER + "$a";
366
- var MARK_OBJECT_KEY = SPLITTER + "$b";
366
+ var hostStore = /* @__PURE__ */ new WeakMap();
367
+ function getOrCreate(host) {
368
+ let record = hostStore.get(host);
369
+ if (!record) {
370
+ record = { signs: /* @__PURE__ */ new Map(), deleted: false };
371
+ hostStore.set(host, record);
372
+ }
373
+ return record;
374
+ }
367
375
  function mark(host, key) {
368
- let sign = 0;
369
- const hostRecord = host;
370
- if (!hostRecord[DELETED_KEY]) {
371
- const markHost = hostRecord[MARK_OBJECT_KEY] || (hostRecord[MARK_OBJECT_KEY] = {});
372
- if (!Object.prototype.hasOwnProperty.call(markHost, key)) {
373
- markHost[key] = 0;
374
- }
375
- sign = ++markHost[key];
376
+ const record = getOrCreate(host);
377
+ if (record.deleted) {
378
+ return () => false;
376
379
  }
380
+ const sign = (record.signs.get(key) ?? 0) + 1;
381
+ record.signs.set(key, sign);
377
382
  return () => {
378
- const temp = hostRecord[MARK_OBJECT_KEY];
379
- return !!(temp && sign === temp[key]);
383
+ const current = hostStore.get(host);
384
+ return !!current && !current.deleted && current.signs.get(key) === sign;
380
385
  };
381
386
  }
382
387
  function unmark(host) {
383
- const hostRecord = host;
384
- hostRecord[MARK_OBJECT_KEY] = 0;
385
- hostRecord[DELETED_KEY] = 1;
388
+ const record = hostStore.get(host);
389
+ if (record) {
390
+ record.deleted = true;
391
+ record.signs.clear();
392
+ } else {
393
+ hostStore.set(host, { signs: /* @__PURE__ */ new Map(), deleted: true });
394
+ }
386
395
  }
387
396
 
388
397
  // src/safeguard.ts
389
398
  var proxiesPool = /* @__PURE__ */ new Map();
390
- var SAFEGUARD_SENTINEL = "_sf_";
399
+ var SAFEGUARD_SENTINEL = "_safe_";
391
400
  function safeguard(data, getter, setter, isRoot) {
392
401
  if (typeof window.__lark_Debug === "undefined" || !window.__lark_Debug) {
393
402
  return data;
@@ -428,7 +437,7 @@ function safeguard(data, getter, setter, isRoot) {
428
437
  if (!prefix && getter) {
429
438
  getter(property);
430
439
  }
431
- if (!isRoot && has(target, property) && (isArray(out) || isPlainObject(out))) {
440
+ if (!isRoot && hasOwnProperty(target, property) && (Array.isArray(out) || isPlainObject(out))) {
432
441
  return build(prefix + property + ".", out);
433
442
  }
434
443
  return out;
@@ -517,15 +526,17 @@ var Cache = class {
517
526
  this.lookup.set(prefixedKey, entry);
518
527
  }
519
528
  /**
520
- * Delete a cached entry.
529
+ * Delete a cached entry. Removes it immediately from both the lookup map
530
+ * and the entries array so the GC can reclaim the value without waiting
531
+ * for the next eviction sweep.
521
532
  */
522
533
  del(key) {
523
534
  const prefixedKey = this.prefixKey(key);
524
535
  const entry = this.lookup.get(prefixedKey);
525
536
  if (!entry) return;
526
- entry.frequency = -1;
527
- entry.value = void 0;
528
537
  this.lookup.delete(prefixedKey);
538
+ const idx = this.entries.indexOf(entry);
539
+ if (idx !== -1) this.entries.splice(idx, 1);
529
540
  if (this.onRemove) {
530
541
  this.onRemove(key);
531
542
  }
@@ -550,20 +561,46 @@ var Cache = class {
550
561
  this.entries = [];
551
562
  this.lookup.clear();
552
563
  }
553
- /** Evict least-used entries from cache */
564
+ /**
565
+ * Evict the `bufferSize` worst entries from the cache.
566
+ *
567
+ * Uses single-pass partial selection (O(n·k)) instead of sorting the entire
568
+ * `entries` array (O(n log n)). For the typical `bufferSize = 5` this is
569
+ * effectively a linear scan with at most 5 in-bucket comparisons per
570
+ * iteration — and it avoids mutating the rest of `entries`.
571
+ */
554
572
  evictEntries() {
555
- this.entries.sort(this.comparator);
556
- let count = this.bufferSize;
557
- while (count-- > 0 && this.entries.length > 0) {
558
- const entry = this.entries.pop();
559
- if (entry && entry.frequency > 0) {
560
- this.lookup.delete(this.prefixKey(entry.originalKey));
561
- if (this.onRemove) {
562
- this.onRemove(entry.originalKey);
563
- }
573
+ const entries = this.entries;
574
+ const k = this.bufferSize;
575
+ if (k <= 0 || entries.length === 0) return;
576
+ if (entries.length <= k) {
577
+ for (const e of entries) {
578
+ this.lookup.delete(this.prefixKey(e.originalKey));
579
+ if (this.onRemove) this.onRemove(e.originalKey);
580
+ }
581
+ this.entries = [];
582
+ return;
583
+ }
584
+ const cmp = this.comparator;
585
+ const worst = [];
586
+ for (const entry of entries) {
587
+ if (worst.length < k) {
588
+ let i = worst.length;
589
+ while (i > 0 && cmp(entry, worst[i - 1]) > 0) i--;
590
+ worst.splice(i, 0, entry);
591
+ } else if (cmp(entry, worst[k - 1]) > 0) {
592
+ worst.pop();
593
+ let i = worst.length;
594
+ while (i > 0 && cmp(entry, worst[i - 1]) > 0) i--;
595
+ worst.splice(i, 0, entry);
564
596
  }
565
597
  }
566
- this.entries = this.entries.filter((e) => e.frequency !== -1);
598
+ const evictSet = new Set(worst);
599
+ for (const e of worst) {
600
+ this.lookup.delete(this.prefixKey(e.originalKey));
601
+ if (this.onRemove) this.onRemove(e.originalKey);
602
+ }
603
+ this.entries = entries.filter((e) => !evictSet.has(e));
567
604
  }
568
605
  };
569
606
 
@@ -571,6 +608,10 @@ var Cache = class {
571
608
  var EventEmitter = class {
572
609
  /** Event listeners: prefixed key -> listener array */
573
610
  listeners = /* @__PURE__ */ new Map();
611
+ /** Number of `fire()` calls currently on the stack (re-entrancy depth). */
612
+ firingDepth = 0;
613
+ /** Keys whose listener list needs compaction after firing settles. */
614
+ pendingCompaction;
574
615
  /**
575
616
  * Bind event listener.
576
617
  */
@@ -593,24 +634,37 @@ var EventEmitter = class {
593
634
  const key = SPLITTER + event;
594
635
  if (handler) {
595
636
  const list = this.listeners.get(key);
596
- if (list) {
637
+ if (!list) return this;
638
+ if (this.firingDepth > 0) {
597
639
  for (const listener of list) {
598
640
  if (listener.handler === handler) {
599
641
  listener.handler = noop;
642
+ (this.pendingCompaction ??= /* @__PURE__ */ new Set()).add(key);
643
+ break;
644
+ }
645
+ }
646
+ } else {
647
+ for (let i = 0; i < list.length; i++) {
648
+ if (list[i].handler === handler) {
649
+ list.splice(i, 1);
600
650
  break;
601
651
  }
602
652
  }
653
+ if (list.length === 0) this.listeners.delete(key);
603
654
  }
604
655
  } else {
605
656
  this.listeners.delete(key);
606
- Reflect.deleteProperty(this, `on${event}`);
657
+ Reflect.deleteProperty(
658
+ this,
659
+ `on${event[0].toUpperCase() + event.slice(1)}`
660
+ );
607
661
  }
608
662
  return this;
609
663
  }
610
664
  /**
611
- * Fire event, execute all bound handlers.
612
- * Supports executing state management: handlers removed during
613
- * execution are safely handled.
665
+ * Fire event, execute all bound handlers. Safe for re-entrant `off()` calls
666
+ * during dispatch: removed handlers are replaced with noop and compacted
667
+ * after the outermost fire returns.
614
668
  *
615
669
  * @param event - Event name
616
670
  * @param data - Event data (type property added automatically)
@@ -624,38 +678,41 @@ var EventEmitter = class {
624
678
  data = {};
625
679
  }
626
680
  data["type"] = event;
627
- if (list) {
628
- let end = list.length;
629
- const len = end - 1;
630
- while (end--) {
631
- const idx = lastToFirst ? end : len - end;
632
- const listener = list[idx];
633
- if (listener.handler !== noop) {
681
+ this.firingDepth++;
682
+ try {
683
+ if (list) {
684
+ const len = list.length;
685
+ for (let i = 0; i < len; i++) {
686
+ const idx = lastToFirst ? len - 1 - i : i;
687
+ const listener = list[idx];
688
+ if (!listener) continue;
689
+ if (listener.handler === noop) continue;
634
690
  listener.executing = 1;
635
- funcWithTry(
636
- [listener.handler],
637
- [data],
638
- this,
639
- noop
640
- );
691
+ funcWithTry([listener.handler], [data], this, noop);
641
692
  listener.executing = "";
642
- } else if (!listener.executing) {
643
- list.splice(idx, 1);
644
693
  }
645
694
  }
646
- }
647
- const onMethodName = `on${event}`;
648
- const onMethod = this[onMethodName];
649
- if (typeof onMethod === "function") {
650
- funcWithTry(
651
- [onMethod],
652
- [data],
653
- this,
654
- noop
655
- );
656
- }
657
- if (remove) {
658
- this.off(event);
695
+ const onMethodName = `on${event[0].toUpperCase() + event.slice(1)}`;
696
+ const onMethod = this[onMethodName];
697
+ if (typeof onMethod === "function") {
698
+ funcWithTry([onMethod], [data], this, noop);
699
+ }
700
+ if (remove) {
701
+ this.off(event);
702
+ }
703
+ } finally {
704
+ this.firingDepth--;
705
+ if (this.firingDepth === 0 && this.pendingCompaction) {
706
+ for (const k of this.pendingCompaction) {
707
+ const l = this.listeners.get(k);
708
+ if (!l) continue;
709
+ for (let i = l.length - 1; i >= 0; i--) {
710
+ if (l[i].handler === noop) l.splice(i, 1);
711
+ }
712
+ if (l.length === 0) this.listeners.delete(k);
713
+ }
714
+ this.pendingCompaction = void 0;
715
+ }
659
716
  }
660
717
  return this;
661
718
  }
@@ -664,8 +721,8 @@ var EventEmitter = class {
664
721
  // src/state.ts
665
722
  var appData = {};
666
723
  var keyRefCounts = {};
667
- var changedKeys = {};
668
- var stashedChangedKeys = {};
724
+ var changedKeys = /* @__PURE__ */ new Set();
725
+ var stashedChangedKeys = EMPTY_STRING_SET;
669
726
  var dataIsChanged = false;
670
727
  var dataWhereSet = {};
671
728
  var emitter = new EventEmitter();
@@ -676,7 +733,7 @@ function markBooted() {
676
733
  function setupKeysRef(keys2) {
677
734
  const keyList = keys2.split(",");
678
735
  for (const key of keyList) {
679
- if (has(keyRefCounts, key)) {
736
+ if (hasOwnProperty(keyRefCounts, key)) {
680
737
  keyRefCounts[key]++;
681
738
  } else {
682
739
  keyRefCounts[key] = 1;
@@ -686,7 +743,7 @@ function setupKeysRef(keys2) {
686
743
  }
687
744
  function teardownKeysRef(keyList) {
688
745
  for (const key of keyList) {
689
- if (has(keyRefCounts, key)) {
746
+ if (hasOwnProperty(keyRefCounts, key)) {
690
747
  const count = --keyRefCounts[key];
691
748
  if (count <= 0) {
692
749
  Reflect.deleteProperty(keyRefCounts, key);
@@ -698,35 +755,14 @@ function teardownKeysRef(keyList) {
698
755
  }
699
756
  }
700
757
  }
701
- var notifyStarted = 0;
702
- var notifyList = [];
703
- var notifyTimer;
758
+ var warnedKeys = /* @__PURE__ */ new Set();
704
759
  function clearNotify(key) {
705
- for (let i = notifyList.length; i--; ) {
706
- if (notifyList[i]?.key === key) {
707
- notifyList.splice(i, 1);
708
- }
709
- }
710
- }
711
- function doNotify() {
712
- const locker = {};
713
- for (const n of notifyList) {
714
- if (!locker[n.key]) {
715
- console.warn(n.message);
716
- locker[n.key] = 1;
717
- }
718
- }
719
- notifyList.length = 0;
720
- notifyStarted = 0;
760
+ warnedKeys.delete(key);
721
761
  }
722
762
  function delayNotify(key, message) {
723
- clearTimeout(notifyTimer);
724
- notifyStarted = 0;
725
- notifyList.push({ key, message });
726
- if (!notifyStarted) {
727
- notifyStarted = 1;
728
- notifyTimer = setTimeout(doNotify, 500);
729
- }
763
+ if (warnedKeys.has(key)) return;
764
+ warnedKeys.add(key);
765
+ console.warn(message);
730
766
  }
731
767
  var State = {
732
768
  /**
@@ -738,7 +774,7 @@ var State = {
738
774
  return safeguard(
739
775
  result,
740
776
  (dataKey) => {
741
- if (booted && has(dataWhereSet, dataKey) && dataWhereSet[dataKey] !== window.location.pathname) {
777
+ if (booted && hasOwnProperty(dataWhereSet, dataKey) && dataWhereSet[dataKey] !== window.location.pathname) {
742
778
  console.warn(
743
779
  `beware! You get state:"{State}.${dataKey}" where it set by page:${dataWhereSet[dataKey]}`
744
780
  );
@@ -759,7 +795,7 @@ var State = {
759
795
  * Set data to state.
760
796
  */
761
797
  set(data, excludes) {
762
- dataIsChanged = setData(data, appData, changedKeys, excludes || /* @__PURE__ */ new Set()) || dataIsChanged;
798
+ dataIsChanged = setData(data, appData, changedKeys, excludes || EMPTY_STRING_SET) || dataIsChanged;
763
799
  if (typeof window.__lark_Debug !== "undefined" && window.__lark_Debug && booted) {
764
800
  for (const p in data) {
765
801
  dataWhereSet[p] = window.location.pathname;
@@ -776,26 +812,19 @@ var State = {
776
812
  }
777
813
  if (dataIsChanged) {
778
814
  if (typeof window.__lark_Debug !== "undefined" && window.__lark_Debug) {
779
- for (const p in changedKeys) {
780
- if (has(changedKeys, p)) {
781
- clearNotify(p);
782
- }
815
+ for (const p of changedKeys) {
816
+ clearNotify(p);
783
817
  }
784
818
  }
785
819
  dataIsChanged = false;
786
- const keys2 = {};
787
- for (const k in changedKeys) {
788
- if (has(changedKeys, k)) {
789
- keys2[k] = 1;
790
- }
791
- }
820
+ const keys2 = changedKeys;
792
821
  stashedChangedKeys = keys2;
793
- changedKeys = {};
794
- emitter.fire(ROUTER_EVENTS.CHANGED, { keys: keys2 });
822
+ changedKeys = /* @__PURE__ */ new Set();
823
+ emitter.fire(RouterEvents.CHANGED, { keys: keys2 });
795
824
  }
796
825
  },
797
826
  /**
798
- * Get diff of what changed in last digest.
827
+ * Get the set of keys changed in the most recent digest.
799
828
  */
800
829
  diff() {
801
830
  return stashedChangedKeys;
@@ -835,6 +864,7 @@ var State = {
835
864
  emitter.fire(event, data, remove);
836
865
  return State;
837
866
  }
867
+ // onChanged: noop,
838
868
  };
839
869
 
840
870
  // src/router.ts
@@ -852,6 +882,7 @@ var cachedDefaultPath;
852
882
  var cachedRewrite;
853
883
  var defaultTitle;
854
884
  var frameworkConfig;
885
+ var beforeEachGuards = [];
855
886
  function createEmptyLocation() {
856
887
  return {
857
888
  href: "",
@@ -1006,7 +1037,7 @@ var Router = {
1006
1037
  document.title = defaultTitle || document.title;
1007
1038
  }
1008
1039
  emitter2.fire(
1009
- ROUTER_EVENTS.CHANGED,
1040
+ RouterEvents.CHANGED,
1010
1041
  lastChanged
1011
1042
  );
1012
1043
  }
@@ -1039,16 +1070,16 @@ var Router = {
1039
1070
  }
1040
1071
  const lPath = lastLocation["path"] || "";
1041
1072
  const lParams = lastLocation["params"];
1042
- const lQuery = {};
1073
+ const lQuery = /* @__PURE__ */ new Set();
1043
1074
  for (const k in lastLocation.query["params"]) {
1044
- if (has(lastLocation.query["params"], k)) {
1045
- lQuery[k] = 1;
1075
+ if (hasOwnProperty(lastLocation.query["params"], k)) {
1076
+ lQuery.add(k);
1046
1077
  }
1047
1078
  }
1048
1079
  if (tPath) {
1049
- if (!has(window, "history")) {
1050
- for (const qKey in lQuery) {
1051
- if (has(lQuery, qKey) && !has(tParams, qKey)) {
1080
+ if (!hasOwnProperty(window, "history")) {
1081
+ for (const qKey of lQuery) {
1082
+ if (!hasOwnProperty(tParams, qKey)) {
1052
1083
  tParams[qKey] = "";
1053
1084
  }
1054
1085
  }
@@ -1057,14 +1088,17 @@ var Router = {
1057
1088
  tPath = lPath;
1058
1089
  tParams = assign({}, lParams, tParams);
1059
1090
  }
1060
- updateUrl(
1061
- tPath,
1062
- tParams,
1063
- lastLocation,
1064
- replace,
1065
- silentFlag,
1066
- lQuery
1067
- );
1091
+ updateUrl(tPath, tParams, lastLocation, replace, silentFlag, lQuery);
1092
+ },
1093
+ /**
1094
+ * Register an async-friendly navigation guard. See `RouterInterface.beforeEach`.
1095
+ */
1096
+ beforeEach(guard) {
1097
+ beforeEachGuards.push(guard);
1098
+ return () => {
1099
+ const idx = beforeEachGuards.indexOf(guard);
1100
+ if (idx !== -1) beforeEachGuards.splice(idx, 1);
1101
+ };
1068
1102
  },
1069
1103
  /**
1070
1104
  * Join multiple path segments into a single path.
@@ -1128,21 +1162,48 @@ var Router = {
1128
1162
  }
1129
1163
  };
1130
1164
  Router.fire(
1131
- ROUTER_EVENTS.CHANGE,
1165
+ RouterEvents.CHANGE,
1132
1166
  changeEvent
1133
1167
  );
1134
- if (!suspend && !changeEvent.p) {
1168
+ if (suspend || changeEvent.p) {
1169
+ return;
1170
+ }
1171
+ if (beforeEachGuards.length === 0) {
1135
1172
  changeEvent.resolve();
1173
+ return;
1174
+ }
1175
+ const from = lastLocation;
1176
+ const to = loc;
1177
+ const guards = beforeEachGuards.slice();
1178
+ let chain = Promise.resolve(true);
1179
+ for (const guard of guards) {
1180
+ chain = chain.then((prev) => {
1181
+ if (prev === false) return false;
1182
+ return guard(to, from);
1183
+ });
1136
1184
  }
1185
+ chain.then(
1186
+ (result) => {
1187
+ if (changeEvent.p) return;
1188
+ if (result === false) {
1189
+ changeEvent.reject();
1190
+ } else {
1191
+ changeEvent.resolve();
1192
+ }
1193
+ },
1194
+ () => {
1195
+ if (!changeEvent.p) changeEvent.reject();
1196
+ }
1197
+ );
1137
1198
  }
1138
1199
  };
1139
1200
  Router.notify = watchChange;
1140
1201
  window.addEventListener("hashchange", watchChange);
1141
1202
  window.addEventListener("popstate", watchChange);
1142
1203
  window.addEventListener("beforeunload", (domEvent) => {
1143
- const te = {};
1144
- Router.fire(ROUTER_EVENTS.PAGE_UNLOAD, te);
1145
- const msg = te["msg"];
1204
+ const data = {};
1205
+ Router.fire(RouterEvents.PAGE_UNLOAD, data);
1206
+ const msg = data["msg"];
1146
1207
  if (msg) {
1147
1208
  domEvent.returnValue = msg;
1148
1209
  }
@@ -1174,26 +1235,30 @@ var frameGetter;
1174
1235
  function parseEventInfo(eventInfo) {
1175
1236
  const cached = eventInfoCache.get(eventInfo);
1176
1237
  if (cached) {
1177
- return assign({}, cached, { r: eventInfo });
1238
+ return assign({}, cached, { value: eventInfo });
1178
1239
  }
1179
1240
  const match = eventInfo.match(EVENT_METHOD_REGEXP) || [];
1180
1241
  const result = {
1181
- v: match[1] || "",
1182
- n: match[2] || "",
1183
- i: match[3] || ""
1242
+ id: match[1] || "",
1243
+ name: match[2] || "",
1244
+ params: match[3] || ""
1184
1245
  };
1185
1246
  eventInfoCache.set(eventInfo, result);
1186
- return assign({}, result, { r: eventInfo });
1247
+ return assign({}, result, { value: eventInfo });
1187
1248
  }
1188
1249
  function findFrameInfo(current, eventType) {
1189
1250
  const eventInfos = [];
1190
- let begin = current;
1191
1251
  const info = current.getAttribute(`@${eventType}`);
1252
+ const hasSelectorEvents = !!selectorEvents[eventType];
1253
+ if (!info && !hasSelectorEvents) {
1254
+ return eventInfos;
1255
+ }
1256
+ let begin = current;
1192
1257
  let match;
1193
1258
  if (info) {
1194
1259
  match = parseEventInfo(info);
1195
1260
  }
1196
- if (match && !match.v || selectorEvents[eventType]) {
1261
+ if (match && !match.id || hasSelectorEvents) {
1197
1262
  let selectorFrameId = "#";
1198
1263
  let backtrace = 0;
1199
1264
  while (begin && begin !== document.body) {
@@ -1219,10 +1284,10 @@ function findFrameInfo(current, eventType) {
1219
1284
  if (selectorEntry) {
1220
1285
  for (const selectorName of selectorEntry.selectors) {
1221
1286
  const entry = {
1222
- r: selectorName,
1223
- v: frameId,
1224
- n: selectorName,
1225
- i: ""
1287
+ value: selectorName,
1288
+ id: frameId,
1289
+ name: selectorName,
1290
+ params: ""
1226
1291
  };
1227
1292
  if (selectorName) {
1228
1293
  if (!backtrace && elementMatchesSelector(current, selectorName)) {
@@ -1234,8 +1299,8 @@ function findFrameInfo(current, eventType) {
1234
1299
  }
1235
1300
  }
1236
1301
  if (view.template && !backtrace) {
1237
- if (match && !match.v) {
1238
- match.v = frameId;
1302
+ if (match && !match.id) {
1303
+ match.id = frameId;
1239
1304
  }
1240
1305
  break;
1241
1306
  }
@@ -1251,10 +1316,10 @@ function findFrameInfo(current, eventType) {
1251
1316
  }
1252
1317
  if (match) {
1253
1318
  eventInfos.push({
1254
- v: match.v,
1255
- r: match.r,
1256
- n: match.n,
1257
- i: match.i
1319
+ id: match.id,
1320
+ value: match.value,
1321
+ name: match.name,
1322
+ params: match.params
1258
1323
  });
1259
1324
  }
1260
1325
  return eventInfos;
@@ -1275,7 +1340,7 @@ function domEventProcessor(domEvent) {
1275
1340
  const eventInfos = findFrameInfo(current, eventType);
1276
1341
  if (eventInfos.length) {
1277
1342
  for (const info of eventInfos) {
1278
- const { v: frameId, n: handlerName, i: params } = info;
1343
+ const { id: frameId, name: handlerName, params } = info;
1279
1344
  if (lastFrameId !== frameId) {
1280
1345
  if (lastFrameId && domEvent.isPropagationStopped?.()) {
1281
1346
  break;
@@ -1366,7 +1431,7 @@ var EventDelegator = {
1366
1431
  };
1367
1432
 
1368
1433
  // src/vdom.ts
1369
- var WrapMeta = {
1434
+ var wrapMeta = {
1370
1435
  option: [1, "<select multiple>"],
1371
1436
  thead: [1, "<table>"],
1372
1437
  col: [2, "<table><colgroup>"],
@@ -1374,13 +1439,13 @@ var WrapMeta = {
1374
1439
  td: [3, "<table><tbody><tr>"],
1375
1440
  area: [1, "<map>"],
1376
1441
  param: [1, "<object>"],
1377
- g: [1, '<svg xmlns="' + SVG_NS + '">'],
1378
- m: [1, '<math xmlns="' + MATH_NS + '">'],
1442
+ svg: [1, '<svg xmlns="' + SVG_NS + '">'],
1443
+ math: [1, '<math xmlns="' + MATH_NS + '">'],
1379
1444
  _: [0, ""]
1380
1445
  };
1381
- WrapMeta["optgroup"] = WrapMeta["option"];
1382
- WrapMeta["tbody"] = WrapMeta["tfoot"] = WrapMeta["colgroup"] = WrapMeta["caption"] = WrapMeta["thead"];
1383
- WrapMeta["th"] = WrapMeta["td"];
1446
+ wrapMeta["optgroup"] = wrapMeta["option"];
1447
+ wrapMeta["tbody"] = wrapMeta["tfoot"] = wrapMeta["colgroup"] = wrapMeta["caption"] = wrapMeta["thead"];
1448
+ wrapMeta["th"] = wrapMeta["td"];
1384
1449
  var VDoc = document.implementation.createHTMLDocument("");
1385
1450
  var VBase = VDoc.createElement("base");
1386
1451
  VBase.href = document.location.href;
@@ -1391,16 +1456,12 @@ var VDomSpecials = {
1391
1456
  OPTION: ["selected"]
1392
1457
  };
1393
1458
  function vdomUnmountFrames(frame, node) {
1394
- if (node.nodeType === 1) {
1395
- const el = node;
1396
- const id = el.getAttribute("id");
1397
- if (id) {
1398
- frame.unmountZone(id);
1399
- const children = frame.children();
1400
- if (children.includes(id)) {
1401
- frame.unmountFrame(id);
1402
- }
1403
- }
1459
+ if (!(node instanceof Element)) return;
1460
+ const id = node.getAttribute("id");
1461
+ if (!id) return;
1462
+ frame.unmountZone(id);
1463
+ if (frame.children().includes(id)) {
1464
+ frame.unmountFrame(id);
1404
1465
  }
1405
1466
  }
1406
1467
  function vdomGetNode(html, refNode) {
@@ -1408,14 +1469,14 @@ function vdomGetNode(html, refNode) {
1408
1469
  const ns = refNode.namespaceURI;
1409
1470
  let tag;
1410
1471
  if (ns === SVG_NS) {
1411
- tag = "g";
1472
+ tag = "svg";
1412
1473
  } else if (ns === MATH_NS) {
1413
- tag = "m";
1474
+ tag = "math";
1414
1475
  } else {
1415
1476
  const match = TAG_NAME_REGEXP.exec(html);
1416
1477
  tag = match ? match[1] : "";
1417
1478
  }
1418
- const wrap = WrapMeta[tag] || WrapMeta["_"];
1479
+ const wrap = wrapMeta[tag] || wrapMeta["_"];
1419
1480
  tmp.innerHTML = wrap[1] + html;
1420
1481
  let j = wrap[0];
1421
1482
  while (j--) {
@@ -1432,7 +1493,7 @@ function vdomGetCompareKey(node) {
1432
1493
  }
1433
1494
  let key = el.autoId ? "" : el.getAttribute("id") || void 0;
1434
1495
  if (!key) {
1435
- key = el.getAttribute(LARK_KEYS.DIFF_KEY) || void 0;
1496
+ key = el.getAttribute(LarkInnerKeys.DIFF_KEY) || void 0;
1436
1497
  }
1437
1498
  if (!key) {
1438
1499
  const larkView = el.getAttribute(LARK_VIEW);
@@ -1445,8 +1506,7 @@ function vdomGetCompareKey(node) {
1445
1506
  return key;
1446
1507
  }
1447
1508
  function vdomSpecialDiff(oldNode, newNode) {
1448
- const nodeName = oldNode.nodeName;
1449
- const specials = VDomSpecials[nodeName];
1509
+ const specials = VDomSpecials[oldNode.nodeName];
1450
1510
  if (!specials) return 0;
1451
1511
  const oldEl = oldNode;
1452
1512
  const newEl = newNode;
@@ -1495,13 +1555,17 @@ function vdomSetChildNodes(oldParent, newParent, ref, frame, keys_) {
1495
1555
  let oldNode = oldParent.lastChild;
1496
1556
  let newNode = newParent.firstChild;
1497
1557
  let extra = 0;
1498
- const keyedNodes = {};
1499
- const newKeyedNodes = {};
1558
+ const keyedNodes = /* @__PURE__ */ new Map();
1559
+ const newKeyedNodes = /* @__PURE__ */ new Map();
1500
1560
  while (oldNode) {
1501
1561
  extra++;
1502
1562
  const nodeKey = vdomGetCompareKey(oldNode);
1503
1563
  if (nodeKey) {
1504
- const bucket = keyedNodes[nodeKey] || (keyedNodes[nodeKey] = []);
1564
+ let bucket = keyedNodes.get(nodeKey);
1565
+ if (!bucket) {
1566
+ bucket = [];
1567
+ keyedNodes.set(nodeKey, bucket);
1568
+ }
1505
1569
  bucket.push(oldNode);
1506
1570
  }
1507
1571
  oldNode = oldNode.previousSibling;
@@ -1509,7 +1573,7 @@ function vdomSetChildNodes(oldParent, newParent, ref, frame, keys_) {
1509
1573
  while (newNode) {
1510
1574
  const nodeKey = vdomGetCompareKey(newNode);
1511
1575
  if (nodeKey) {
1512
- newKeyedNodes[nodeKey] = (newKeyedNodes[nodeKey] || 0) + 1;
1576
+ newKeyedNodes.set(nodeKey, (newKeyedNodes.get(nodeKey) ?? 0) + 1);
1513
1577
  }
1514
1578
  newNode = newNode.nextSibling;
1515
1579
  }
@@ -1520,23 +1584,25 @@ function vdomSetChildNodes(oldParent, newParent, ref, frame, keys_) {
1520
1584
  const tempNew = newNode;
1521
1585
  newNode = newNode.nextSibling;
1522
1586
  const nodeKey = vdomGetCompareKey(tempNew);
1523
- let foundNode = nodeKey ? keyedNodes[nodeKey] : void 0;
1587
+ let foundNode = nodeKey ? keyedNodes.get(nodeKey) : void 0;
1524
1588
  if (foundNode && (foundNode = foundNode.slice()) && foundNode.length) {
1525
1589
  const matched = foundNode.pop();
1526
1590
  while (matched !== oldNode) {
1527
- const next = oldNode?.nextSibling ?? null;
1591
+ if (!oldNode) break;
1592
+ const next = oldNode.nextSibling;
1528
1593
  oldParent.appendChild(oldNode);
1529
1594
  oldNode = next;
1530
1595
  }
1531
1596
  oldNode = matched.nextSibling;
1532
- if (nodeKey && newKeyedNodes[nodeKey]) {
1533
- newKeyedNodes[nodeKey]--;
1597
+ if (nodeKey) {
1598
+ const c = newKeyedNodes.get(nodeKey);
1599
+ if (c) newKeyedNodes.set(nodeKey, c - 1);
1534
1600
  }
1535
1601
  vdomSetNode(matched, tempNew, oldParent, ref, frame, keys_);
1536
1602
  } else if (oldNode) {
1537
1603
  const tempOld2 = oldNode;
1538
1604
  const oldKey = vdomGetCompareKey(tempOld2);
1539
- if (oldKey && keyedNodes[oldKey] && newKeyedNodes[oldKey]) {
1605
+ if (oldKey && keyedNodes.has(oldKey) && newKeyedNodes.get(oldKey)) {
1540
1606
  extra++;
1541
1607
  ref.hasChanged = 1;
1542
1608
  ref.domOps.push([8, oldParent, tempNew, tempOld2]);
@@ -1560,17 +1626,21 @@ function vdomSetChildNodes(oldParent, newParent, ref, frame, keys_) {
1560
1626
  }
1561
1627
  }
1562
1628
  function vdomSetNode(oldNode, newNode, oldParent, ref, frame, keys_) {
1563
- if (vdomSpecialDiff(oldNode, newNode) || oldNode.nodeType === 1 && oldNode.hasAttribute(LARK_KEYS.VIEW_KEY) || !(oldNode.isEqualNode && oldNode.isEqualNode(newNode))) {
1629
+ const oldAsEl = oldNode instanceof Element ? oldNode : null;
1630
+ const newAsEl = newNode instanceof Element ? newNode : null;
1631
+ const hasViewKey = !!oldAsEl?.hasAttribute(LarkInnerKeys.VIEW_KEY);
1632
+ const equalAsNodes = oldAsEl !== null && newAsEl !== null && oldAsEl.isEqualNode && oldAsEl.isEqualNode(newAsEl);
1633
+ if (vdomSpecialDiff(oldNode, newNode) || hasViewKey || !equalAsNodes) {
1564
1634
  if (oldNode.nodeType === newNode.nodeType && oldNode.nodeName === newNode.nodeName) {
1565
- if (oldNode.nodeType === 1) {
1566
- const oldEl = oldNode;
1567
- const newEl = newNode;
1568
- const staticKey = newEl.getAttribute(LARK_KEYS.DIFF_KEY);
1569
- if (staticKey && staticKey === oldEl.getAttribute(LARK_KEYS.DIFF_KEY)) {
1635
+ if (oldAsEl !== null && newAsEl !== null) {
1636
+ const oldEl = oldAsEl;
1637
+ const newEl = newAsEl;
1638
+ const staticKey = newEl.getAttribute(LarkInnerKeys.DIFF_KEY);
1639
+ if (staticKey && staticKey === oldEl.getAttribute(LarkInnerKeys.DIFF_KEY)) {
1570
1640
  return;
1571
1641
  }
1572
1642
  const newLarkView = newEl.getAttribute(LARK_VIEW);
1573
- const updateAttribute = !newEl.getAttribute(LARK_KEYS.ATTR_KEY) || newEl.getAttribute(LARK_KEYS.ATTR_KEY) !== oldEl.getAttribute(LARK_KEYS.ATTR_KEY);
1643
+ const updateAttribute = !newEl.getAttribute(LarkInnerKeys.ATTR_KEY) || newEl.getAttribute(LarkInnerKeys.ATTR_KEY) !== oldEl.getAttribute(LarkInnerKeys.ATTR_KEY);
1574
1644
  let updateChildren = true;
1575
1645
  if (newLarkView) {
1576
1646
  const oldFrameId = oldEl.getAttribute("id") || "";
@@ -1671,7 +1741,8 @@ function encodeQ(v) {
1671
1741
  }
1672
1742
 
1673
1743
  // src/updater.ts
1674
- function updaterRef(refData, value, key) {
1744
+ function updaterRef(refDataIn, value, key) {
1745
+ const refData = refDataIn;
1675
1746
  const counter = refData[SPLITTER];
1676
1747
  for (let i = counter; --i; ) {
1677
1748
  key = SPLITTER + i;
@@ -1691,13 +1762,19 @@ var Updater = class {
1691
1762
  /** Ref data for template rendering */
1692
1763
  refData;
1693
1764
  /** Changed keys in current digest cycle */
1694
- changedKeys = {};
1765
+ changedKeys = /* @__PURE__ */ new Set();
1695
1766
  /** Whether data has changed since last digest */
1696
1767
  hasChangedFlag = 0;
1697
- /** Digesting queue: supports re-digest during digest */
1768
+ /**
1769
+ * Digesting queue: supports re-digest during digest.
1770
+ * Holds pending callbacks; `null` is used as a sentinel marking the start
1771
+ * of an active digest cycle, so `runDigest` can detect re-entrant calls.
1772
+ */
1698
1773
  digestingQueue = [];
1699
- /** Snapshot JSON string for altered() detection */
1700
- snapshotJson;
1774
+ /** Monotonically increasing version, bumped each time data actually changes. */
1775
+ version = 0;
1776
+ /** Snapshot of `version` taken by `snapshot()`, used by `altered()`. */
1777
+ snapshotVersion;
1701
1778
  constructor(viewId) {
1702
1779
  this.viewId = viewId;
1703
1780
  this.data = { vId: viewId };
@@ -1725,7 +1802,16 @@ var Updater = class {
1725
1802
  * Returns this for chaining.
1726
1803
  */
1727
1804
  set(data, excludes) {
1728
- this.hasChangedFlag = setData(data, this.data, this.changedKeys, excludes || /* @__PURE__ */ new Set()) || this.hasChangedFlag ? 1 : 0;
1805
+ const changed = setData(
1806
+ data,
1807
+ this.data,
1808
+ this.changedKeys,
1809
+ excludes || EMPTY_STRING_SET
1810
+ );
1811
+ if (changed) {
1812
+ this.version++;
1813
+ this.hasChangedFlag = 1;
1814
+ }
1729
1815
  return this;
1730
1816
  }
1731
1817
  /**
@@ -1761,13 +1847,13 @@ var Updater = class {
1761
1847
  const keys2 = this.changedKeys;
1762
1848
  const changed = this.hasChangedFlag;
1763
1849
  this.hasChangedFlag = 0;
1764
- this.changedKeys = {};
1850
+ this.changedKeys = /* @__PURE__ */ new Set();
1765
1851
  const frame = Frame.get(this.viewId);
1766
1852
  const view = frame?.view;
1767
1853
  const node = getById(this.viewId);
1768
- if (changed && view && node && view.signature > 0) {
1854
+ if (changed && view && node && view.signature > 0 && frame) {
1769
1855
  const template = view.template;
1770
- if (template && typeof template === "function") {
1856
+ if (typeof template === "function") {
1771
1857
  const html = template(
1772
1858
  this.data,
1773
1859
  this.viewId,
@@ -1804,43 +1890,65 @@ var Updater = class {
1804
1890
  }
1805
1891
  }
1806
1892
  /**
1807
- * Save a snapshot of current data for altered() detection.
1893
+ * Save a snapshot of the current data version for `altered()` detection.
1894
+ * Cheap O(1) — records the current monotonic version, no serialization.
1808
1895
  */
1809
1896
  snapshot() {
1810
- this.snapshotJson = JSON.stringify(this.data);
1897
+ this.snapshotVersion = this.version;
1811
1898
  return this;
1812
1899
  }
1813
1900
  /**
1814
- * Check if data has changed since last snapshot.
1901
+ * Check whether data has changed since the last snapshot.
1902
+ * Returns undefined when no snapshot has been taken yet.
1815
1903
  */
1816
1904
  altered() {
1817
- if (this.snapshotJson) {
1818
- return this.snapshotJson !== JSON.stringify(this.data);
1819
- }
1820
- return void 0;
1905
+ if (this.snapshotVersion === void 0) return void 0;
1906
+ return this.version !== this.snapshotVersion;
1821
1907
  }
1822
1908
  /**
1823
- * Translate data references (SPLITTER-prefixed values).
1909
+ * Translate a refData reference back to its original value.
1910
+ *
1911
+ * The ref protocol is `SPLITTER` + ascii decimal digits — the exact format
1912
+ * emitted by `updaterRef`. We require that exact shape so a user-supplied
1913
+ * string that merely begins with SPLITTER is never accidentally resolved
1914
+ * (or mishandled as a "missing ref").
1824
1915
  */
1825
1916
  translate(data) {
1826
- if (typeof data === "string" && data[0] === SPLITTER) {
1827
- return has(this.refData, data) ? this.refData[data] : data;
1917
+ if (typeof data !== "string") return data;
1918
+ if (data.length < 2 || data[0] !== SPLITTER) return data;
1919
+ for (let i = 1; i < data.length; i++) {
1920
+ const c = data.charCodeAt(i);
1921
+ if (c < 48 || c > 57) return data;
1828
1922
  }
1829
- return data;
1923
+ return hasOwnProperty(this.refData, data) ? this.refData[data] : data;
1830
1924
  }
1831
1925
  /**
1832
- * Parse expression with data context.
1926
+ * Resolve a dotted property path against refData.
1927
+ *
1928
+ * Only safe property-path syntax is supported: `a`, `a.b`, `a.b.c`.
1929
+ * Numeric literals (e.g. `1`, `1.5`) are returned as numbers. Anything else
1930
+ * returns `undefined` — we no longer evaluate arbitrary JavaScript via
1931
+ * `new Function`, so the method is CSP-safe and cannot be used as an
1932
+ * injection vector.
1833
1933
  */
1834
1934
  parse(expr) {
1835
- try {
1836
- const fn = new Function("data", `with(data) { return ${expr}; }`);
1837
- return fn(this.refData);
1838
- } catch {
1935
+ const trimmed = expr.trim();
1936
+ if (!trimmed) return void 0;
1937
+ if (/^-?\d+(?:\.\d+)?$/.test(trimmed)) {
1938
+ return Number(trimmed);
1939
+ }
1940
+ if (!/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*$/.test(trimmed)) {
1839
1941
  return void 0;
1840
1942
  }
1943
+ let cur = this.refData;
1944
+ for (const segment of trimmed.split(".")) {
1945
+ if (cur == null || typeof cur !== "object") return void 0;
1946
+ cur = cur[segment];
1947
+ }
1948
+ return cur;
1841
1949
  }
1842
1950
  /**
1843
- * Get changed keys (for external inspection).
1951
+ * Get the set of keys changed since the last digest (for external inspection).
1844
1952
  */
1845
1953
  getChangedKeys() {
1846
1954
  return this.changedKeys;
@@ -1855,221 +1963,6 @@ if (typeof window !== "undefined") {
1855
1963
  if (typeof document !== "undefined") {
1856
1964
  VIEW_GLOBALS["document"] = document;
1857
1965
  }
1858
- function viewPrepare(oView) {
1859
- if (oView.ctors) {
1860
- return oView.ctors;
1861
- }
1862
- const ctors = [];
1863
- oView.ctors = ctors;
1864
- const proto = oView.prototype;
1865
- const eventsObject = {};
1866
- const eventsList = [];
1867
- const selectorObject = {};
1868
- const mixins = proto["mixins"];
1869
- if (mixins && Array.isArray(mixins)) {
1870
- viewMergeMixins(mixins, oView, ctors);
1871
- }
1872
- for (const p in proto) {
1873
- if (!has(proto, p)) continue;
1874
- const currentFn = proto[p];
1875
- if (typeof currentFn !== "function") continue;
1876
- const matches = p.match(VIEW_EVENT_METHOD_REGEXP);
1877
- if (!matches) continue;
1878
- const isSelector = matches[1];
1879
- const selectorOrCallback = matches[2];
1880
- const events = matches[3];
1881
- const modifiers = matches[4];
1882
- const mod = {};
1883
- if (modifiers) {
1884
- for (const item of modifiers.split(",")) {
1885
- mod[item] = true;
1886
- }
1887
- }
1888
- const eventTypes = events.split(",");
1889
- for (const item of eventTypes) {
1890
- const globalNode = VIEW_GLOBALS[selectorOrCallback];
1891
- let mask = 1;
1892
- if (isSelector) {
1893
- if (globalNode) {
1894
- eventsList.push({
1895
- handler: currentFn,
1896
- element: globalNode,
1897
- eventName: item,
1898
- modifiers: mod
1899
- });
1900
- continue;
1901
- }
1902
- mask = 2;
1903
- let selectorEntry = selectorObject[item];
1904
- if (!selectorEntry) {
1905
- selectorEntry = selectorObject[item] = {
1906
- selectors: []
1907
- };
1908
- }
1909
- if (!selectorEntry[selectorOrCallback]) {
1910
- selectorEntry[selectorOrCallback] = 1;
1911
- selectorEntry.selectors.push(selectorOrCallback);
1912
- }
1913
- }
1914
- eventsObject[item] = (eventsObject[item] || 0) | mask;
1915
- const combinedKey = selectorOrCallback + SPLITTER + item;
1916
- const existingFn = proto[combinedKey];
1917
- if (!existingFn) {
1918
- proto[combinedKey] = currentFn;
1919
- } else {
1920
- const mixinFn = currentFn;
1921
- const existingMixin = existingFn;
1922
- if (existingMixin.b) {
1923
- if (mixinFn.b) {
1924
- proto[combinedKey] = processMixinsSameEvent(
1925
- currentFn,
1926
- existingFn
1927
- );
1928
- } else if (has(proto, p)) {
1929
- proto[combinedKey] = currentFn;
1930
- }
1931
- }
1932
- }
1933
- }
1934
- }
1935
- viewWrapMethod(proto, "render", "$b");
1936
- proto["$eo"] = eventsObject;
1937
- proto["$el"] = eventsList;
1938
- proto["$so"] = selectorObject;
1939
- proto["$f"] = proto["assign"];
1940
- return ctors;
1941
- }
1942
- function viewWrapMethod(proto, fnName, shortKey) {
1943
- const originalFn = proto[fnName];
1944
- if (typeof originalFn !== "function") return;
1945
- const wrapped = function(...args) {
1946
- if (this.signature > 0) {
1947
- this.signature++;
1948
- this.fire("rendercall");
1949
- destroyAllResources(this, false);
1950
- const instanceFn = typeof this[fnName] === "function" ? this[fnName] : originalFn;
1951
- const fnToCall = instanceFn === wrapped ? originalFn : instanceFn;
1952
- return funcWithTry(fnToCall, args, this, noop);
1953
- }
1954
- return void 0;
1955
- };
1956
- proto[fnName] = wrapped;
1957
- proto[shortKey] = wrapped;
1958
- }
1959
- function processMixinsSameEvent(additional, exist) {
1960
- let temp;
1961
- const existMixin = exist;
1962
- if (existMixin.a) {
1963
- temp = existMixin;
1964
- } else {
1965
- temp = function(...e) {
1966
- funcWithTry(temp.a ?? [], e, this, noop);
1967
- };
1968
- temp.a = [exist];
1969
- temp.b = 1;
1970
- }
1971
- const additionalMixin = additional;
1972
- temp.a = (temp.a ?? []).concat(additionalMixin.a ?? [additional]);
1973
- return temp;
1974
- }
1975
- function viewMergeMixins(mixins, viewClass, ctors) {
1976
- const proto = viewClass.prototype;
1977
- const temp = {};
1978
- for (const node of mixins) {
1979
- for (const p in node) {
1980
- if (!has(node, p)) continue;
1981
- const fn = node[p];
1982
- const exist = temp[p];
1983
- if (p === "make") {
1984
- ctors.push(fn);
1985
- continue;
1986
- }
1987
- if (VIEW_EVENT_METHOD_REGEXP.test(p)) {
1988
- if (exist) {
1989
- temp[p] = processMixinsSameEvent(fn, exist);
1990
- } else {
1991
- fn.b = 1;
1992
- temp[p] = fn;
1993
- }
1994
- } else {
1995
- if (!exist) {
1996
- temp[p] = fn;
1997
- }
1998
- }
1999
- }
2000
- }
2001
- for (const p in temp) {
2002
- if (!has(proto, p)) {
2003
- proto[p] = temp[p];
2004
- }
2005
- }
2006
- }
2007
- function viewDelegateEvents(view, destroy = false) {
2008
- const proto = Object.getPrototypeOf(view) ?? {};
2009
- const eventsObject = proto["$eo"] || view.eventObjectMap;
2010
- const selectorObject = proto["$so"] || view.eventSelectorMap;
2011
- const eventsList = proto["$el"] || view.globalEventList;
2012
- for (const e in eventsObject) {
2013
- if (has(eventsObject, e)) {
2014
- if (destroy) {
2015
- EventDelegator.unbind(e, !!selectorObject[e]);
2016
- } else {
2017
- EventDelegator.bind(e, !!selectorObject[e]);
2018
- }
2019
- }
2020
- }
2021
- for (const entry of eventsList) {
2022
- if (destroy) {
2023
- entry.element.removeEventListener(
2024
- entry.eventName,
2025
- entry.boundHandler
2026
- );
2027
- } else {
2028
- const handler = entry.handler;
2029
- const element = entry.element;
2030
- const modifiers = entry.modifiers;
2031
- entry.boundHandler = function(domEvent) {
2032
- const extendedEvent = domEvent;
2033
- extendedEvent.eventTarget = element;
2034
- if (modifiers) {
2035
- const kbEvent = domEvent;
2036
- if (modifiers["ctrl"] && !kbEvent.ctrlKey || modifiers["shift"] && !kbEvent.shiftKey || modifiers["alt"] && !kbEvent.altKey || modifiers["meta"] && !kbEvent.metaKey) {
2037
- return;
2038
- }
2039
- }
2040
- funcWithTry(handler, [domEvent], view, noop);
2041
- };
2042
- entry.element.addEventListener(
2043
- entry.eventName,
2044
- entry.boundHandler
2045
- );
2046
- }
2047
- }
2048
- }
2049
- function destroyAllResources(view, lastly) {
2050
- const cache = view.resources;
2051
- for (const p in cache) {
2052
- if (has(cache, p)) {
2053
- const entry = cache[p];
2054
- if (lastly || entry.destroyOnRender) {
2055
- destroyResource(cache, p, true);
2056
- }
2057
- }
2058
- }
2059
- }
2060
- function destroyResource(cache, key, callDestroy, oldEntity) {
2061
- const entry = cache[key];
2062
- if (!entry || entry.entity === oldEntity) return void 0;
2063
- const entity = entry.entity;
2064
- if (entity && typeof entity === "object") {
2065
- const destroyFn = entity["destroy"];
2066
- if (typeof destroyFn === "function" && callDestroy) {
2067
- funcWithTry(destroyFn, [], entity, noop);
2068
- }
2069
- }
2070
- Reflect.deleteProperty(cache, key);
2071
- return entity;
2072
- }
2073
1966
  var View = class _View {
2074
1967
  /** View ID (same as owner frame ID) */
2075
1968
  id = "";
@@ -2093,18 +1986,44 @@ var View = class _View {
2093
1986
  observedStateKeys;
2094
1987
  /** Resource map */
2095
1988
  resources = {};
2096
- /** Selector event map: eventType -> handler name list */
2097
- eventSelectorMap = {};
2098
- /** Event object map: eventType -> bitmask */
2099
- eventObjectMap = {};
2100
- /** Global event list */
2101
- globalEventList = [];
2102
1989
  /** Assign method reference */
2103
1990
  assignMethod;
2104
1991
  /** Whether endUpdate pending */
2105
1992
  endUpdatePending;
2106
1993
  /** Internal event storage */
2107
1994
  _events = new EventEmitter();
1995
+ // ============================================================
1996
+ // Getters for prototype-stored event maps
1997
+ // ============================================================
1998
+ /** Prototype-stored event maps shape (set by View.prepare). */
1999
+ get protoEventState() {
2000
+ return Object.getPrototypeOf(this);
2001
+ }
2002
+ /**
2003
+ * Event bitmask map: eventType -> bitmask (1=root, 2=selector).
2004
+ * Read from prototype ($evtObjMap) set by View.prepare.
2005
+ * Using a getter avoids ES6 class field shadowing the prototype value.
2006
+ */
2007
+ get eventObjectMap() {
2008
+ return this.protoEventState.$evtObjMap ?? {};
2009
+ }
2010
+ /**
2011
+ * Selector event map: eventType -> selector list.
2012
+ * Read from prototype ($selMap) set by View.prepare.
2013
+ */
2014
+ get eventSelectorMap() {
2015
+ return this.protoEventState.$selMap ?? {};
2016
+ }
2017
+ /**
2018
+ * Global event list: [{handler, element, eventName, modifiers}].
2019
+ * Read from prototype ($globalEvtList) set by View.prepare.
2020
+ */
2021
+ get globalEventList() {
2022
+ return this.protoEventState.$globalEvtList ?? [];
2023
+ }
2024
+ // ============================================================
2025
+ // Instance lifecycle methods
2026
+ // ============================================================
2108
2027
  /**
2109
2028
  * Initialize view (called by Frame when mounting).
2110
2029
  */
@@ -2112,14 +2031,7 @@ var View = class _View {
2112
2031
  }
2113
2032
  /**
2114
2033
  * Render view template (called by Frame after init).
2115
- * Wrapped by View_WrapMethod to manage signature + resources.
2116
- *
2117
- * Default implementation calls updater.digest() which:
2118
- * 1. Executes the template function with current data
2119
- * 2. Runs VDOM diff against previous DOM
2120
- * 3. Applies DOM operations
2121
- * 4. Calls endUpdate to mount child frames
2122
- *
2034
+ * Wrapped by View.wrapMethod to manage signature + resources.
2123
2035
  */
2124
2036
  render() {
2125
2037
  this.updater.digest();
@@ -2142,13 +2054,17 @@ var View = class _View {
2142
2054
  // ============================================================
2143
2055
  // Update methods
2144
2056
  // ============================================================
2057
+ /** Get the owning frame, asserting it has been bound. */
2058
+ get ownerFrame() {
2059
+ return this.owner;
2060
+ }
2145
2061
  /**
2146
2062
  * Notify view that HTML update is about to begin.
2147
2063
  * Unmounts child frames in the update zone.
2148
2064
  */
2149
2065
  beginUpdate(id) {
2150
2066
  if (this.signature > 0 && this.endUpdatePending !== void 0) {
2151
- this.owner.unmountZone(id, true);
2067
+ this.ownerFrame.unmountZone(id, true);
2152
2068
  }
2153
2069
  }
2154
2070
  /**
@@ -2166,12 +2082,12 @@ var View = class _View {
2166
2082
  this.endUpdatePending = 1;
2167
2083
  this.rendered = true;
2168
2084
  }
2169
- const ownerFrame = this.owner;
2085
+ const ownerFrame = this.ownerFrame;
2170
2086
  ownerFrame.mountZone(updateId, inner);
2171
2087
  if (!flag) {
2172
2088
  setTimeout(
2173
2089
  this.wrapAsync(() => {
2174
- runInvokes(ownerFrame);
2090
+ _View.runInvokes(ownerFrame);
2175
2091
  }),
2176
2092
  0
2177
2093
  );
@@ -2205,11 +2121,12 @@ var View = class _View {
2205
2121
  const loc = this.locationObserved;
2206
2122
  loc.flag = 1;
2207
2123
  if (typeof params === "object" && !Array.isArray(params)) {
2208
- if (params["path"]) {
2124
+ const opts = params;
2125
+ if (opts["path"]) {
2209
2126
  observePath = true;
2210
2127
  }
2211
- const paramKeys = params["params"];
2212
- if (paramKeys) {
2128
+ const paramKeys = opts["params"];
2129
+ if (typeof paramKeys === "string" || Array.isArray(paramKeys)) {
2213
2130
  params = paramKeys;
2214
2131
  }
2215
2132
  }
@@ -2247,7 +2164,7 @@ var View = class _View {
2247
2164
  capture(key, resource, destroyOnRender = false) {
2248
2165
  const cache = this.resources;
2249
2166
  if (resource) {
2250
- destroyResource(cache, key, true, resource);
2167
+ _View.destroyResource(cache, key, true, resource);
2251
2168
  cache[key] = {
2252
2169
  entity: resource,
2253
2170
  destroyOnRender
@@ -2263,7 +2180,7 @@ var View = class _View {
2263
2180
  * If destroy=true, calls the resource's destroy() method.
2264
2181
  */
2265
2182
  release(key, destroy = true) {
2266
- return destroyResource(this.resources, key, destroy);
2183
+ return _View.destroyResource(this.resources, key, destroy);
2267
2184
  }
2268
2185
  // ============================================================
2269
2186
  // Leave tip
@@ -2273,24 +2190,16 @@ var View = class _View {
2273
2190
  */
2274
2191
  leaveTip(message, condition) {
2275
2192
  const changeListener = function(e) {
2276
- const isRouterChange = e.type === ROUTER_EVENTS.CHANGE;
2193
+ const isRouterChange = e.type === RouterEvents.CHANGE;
2277
2194
  const aKey = isRouterChange ? "a" : "b";
2278
2195
  const bKey = isRouterChange ? "b" : "a";
2279
2196
  if (changeListener[aKey]) {
2280
- if (typeof e.prevent === "function") {
2281
- e.prevent();
2282
- }
2283
- if (typeof e.reject === "function") {
2284
- e.reject();
2285
- }
2197
+ e.prevent?.();
2198
+ e.reject?.();
2286
2199
  } else if (condition()) {
2287
- if (typeof e.prevent === "function") {
2288
- e.prevent();
2289
- }
2200
+ e.prevent?.();
2290
2201
  changeListener[bKey] = 1;
2291
- if (typeof e.resolve === "function") {
2292
- e.resolve();
2293
- }
2202
+ e.resolve?.();
2294
2203
  }
2295
2204
  };
2296
2205
  const unloadListener = (e) => {
@@ -2298,21 +2207,284 @@ var View = class _View {
2298
2207
  e["msg"] = message;
2299
2208
  }
2300
2209
  };
2301
- const changeFn = changeListener;
2302
- const unloadFn = unloadListener;
2303
- Router.on(ROUTER_EVENTS.CHANGE, changeFn);
2304
- Router.on(ROUTER_EVENTS.PAGE_UNLOAD, unloadFn);
2305
- this.on("unload", changeFn);
2210
+ Router.on(RouterEvents.CHANGE, changeListener);
2211
+ Router.on(RouterEvents.PAGE_UNLOAD, unloadListener);
2212
+ this.on("unload", changeListener);
2306
2213
  this.on("destroy", () => {
2307
- Router.off(ROUTER_EVENTS.CHANGE, changeFn);
2308
- Router.off(ROUTER_EVENTS.PAGE_UNLOAD, unloadFn);
2214
+ Router.off(RouterEvents.CHANGE, changeListener);
2215
+ Router.off(RouterEvents.PAGE_UNLOAD, unloadListener);
2309
2216
  });
2310
2217
  }
2311
2218
  // ============================================================
2312
- // Static: extend and merge
2219
+ // Static public methods
2313
2220
  // ============================================================
2314
2221
  /** Collected ctors from mixins */
2315
2222
  static ctors;
2223
+ /**
2224
+ * Prepare a View subclass by scanning its prototype for event method patterns.
2225
+ * Pattern: `$?name<eventType1,eventType2>(&modifiers)`
2226
+ *
2227
+ * Only runs once per View subclass (guarded by ctors marker).
2228
+ * Called from Frame.mountView before creating the view instance.
2229
+ */
2230
+ static prepare(oView) {
2231
+ if (oView.ctors) {
2232
+ return oView.ctors;
2233
+ }
2234
+ const ctors = [];
2235
+ oView.ctors = ctors;
2236
+ const proto = oView.prototype;
2237
+ const eventsObject = {};
2238
+ const eventsList = [];
2239
+ const selectorObject = {};
2240
+ const mixins = proto["mixins"];
2241
+ if (mixins && Array.isArray(mixins)) {
2242
+ _View.mergeMixins(mixins, oView, ctors);
2243
+ }
2244
+ for (const p in proto) {
2245
+ if (!hasOwnProperty(proto, p)) continue;
2246
+ const currentFn = proto[p];
2247
+ if (typeof currentFn !== "function") continue;
2248
+ const matches = p.match(VIEW_EVENT_METHOD_REGEXP);
2249
+ if (!matches) continue;
2250
+ const isSelector = matches[1];
2251
+ const selectorOrCallback = matches[2];
2252
+ const events = matches[3];
2253
+ const modifiers = matches[4];
2254
+ const mod = {};
2255
+ if (modifiers) {
2256
+ for (const item of modifiers.split(",")) {
2257
+ mod[item] = true;
2258
+ }
2259
+ }
2260
+ const eventTypes = events.split(",");
2261
+ for (const item of eventTypes) {
2262
+ const globalNode = VIEW_GLOBALS[selectorOrCallback];
2263
+ let mask = 1;
2264
+ if (isSelector) {
2265
+ if (globalNode) {
2266
+ eventsList.push({
2267
+ handler: currentFn,
2268
+ element: globalNode,
2269
+ eventName: item,
2270
+ modifiers: mod
2271
+ });
2272
+ continue;
2273
+ }
2274
+ mask = 2;
2275
+ let selectorEntry = selectorObject[item];
2276
+ if (!selectorEntry) {
2277
+ selectorEntry = selectorObject[item] = {
2278
+ selectors: []
2279
+ };
2280
+ }
2281
+ if (!selectorEntry[selectorOrCallback]) {
2282
+ selectorEntry[selectorOrCallback] = 1;
2283
+ selectorEntry.selectors.push(selectorOrCallback);
2284
+ }
2285
+ }
2286
+ eventsObject[item] = (eventsObject[item] || 0) | mask;
2287
+ const combinedKey = selectorOrCallback + SPLITTER + item;
2288
+ const existingFn = proto[combinedKey];
2289
+ if (!existingFn) {
2290
+ proto[combinedKey] = currentFn;
2291
+ } else if (typeof existingFn === "function") {
2292
+ const mixinFn = currentFn;
2293
+ const existingMixin = existingFn;
2294
+ if (existingMixin.marker) {
2295
+ if (mixinFn.marker) {
2296
+ proto[combinedKey] = _View.processMixinsSameEvent(
2297
+ mixinFn,
2298
+ existingMixin
2299
+ );
2300
+ } else if (hasOwnProperty(proto, p)) {
2301
+ proto[combinedKey] = currentFn;
2302
+ }
2303
+ }
2304
+ }
2305
+ }
2306
+ }
2307
+ _View.wrapMethod(proto, "render", "$renderWrap");
2308
+ proto["$evtObjMap"] = eventsObject;
2309
+ proto["$globalEvtList"] = eventsList;
2310
+ proto["$selMap"] = selectorObject;
2311
+ proto["$assignFn"] = proto["assign"];
2312
+ return ctors;
2313
+ }
2314
+ /**
2315
+ * Bind or unbind event delegation for a view instance.
2316
+ * Called from Frame during mount/unmount.
2317
+ */
2318
+ static delegateEvents(view, destroy = false) {
2319
+ const eventsObject = view.eventObjectMap;
2320
+ const selectorObject = view.eventSelectorMap;
2321
+ const eventsList = view.globalEventList;
2322
+ for (const e in eventsObject) {
2323
+ if (hasOwnProperty(eventsObject, e)) {
2324
+ if (destroy) {
2325
+ EventDelegator.unbind(e, !!selectorObject[e]);
2326
+ } else {
2327
+ EventDelegator.bind(e, !!selectorObject[e]);
2328
+ }
2329
+ }
2330
+ }
2331
+ for (const entry of eventsList) {
2332
+ if (destroy) {
2333
+ entry.element.removeEventListener(
2334
+ entry.eventName,
2335
+ entry.boundHandler
2336
+ );
2337
+ } else {
2338
+ const handler = entry.handler;
2339
+ const element = entry.element;
2340
+ const modifiers = entry.modifiers;
2341
+ entry.boundHandler = function(domEvent) {
2342
+ const extendedEvent = domEvent;
2343
+ extendedEvent.eventTarget = element;
2344
+ if (modifiers) {
2345
+ const kbEvent = domEvent;
2346
+ if (modifiers["ctrl"] && !kbEvent.ctrlKey || modifiers["shift"] && !kbEvent.shiftKey || modifiers["alt"] && !kbEvent.altKey || modifiers["meta"] && !kbEvent.metaKey) {
2347
+ return;
2348
+ }
2349
+ }
2350
+ funcWithTry(handler, [domEvent], view, noop);
2351
+ };
2352
+ entry.element.addEventListener(
2353
+ entry.eventName,
2354
+ entry.boundHandler
2355
+ );
2356
+ }
2357
+ }
2358
+ }
2359
+ /**
2360
+ * Destroy all resources managed by a view.
2361
+ * If lastly=true, destroy ALL resources; otherwise only destroyOnRender ones.
2362
+ */
2363
+ static destroyAllResources(view, lastly) {
2364
+ const cache = view.resources;
2365
+ for (const p in cache) {
2366
+ if (hasOwnProperty(cache, p)) {
2367
+ const entry = cache[p];
2368
+ if (lastly || entry.destroyOnRender) {
2369
+ _View.destroyResource(cache, p, true);
2370
+ }
2371
+ }
2372
+ }
2373
+ }
2374
+ /**
2375
+ * Process deferred invoke calls on a frame.
2376
+ */
2377
+ static runInvokes(frame) {
2378
+ const list = frame.invokeList;
2379
+ if (!list) return;
2380
+ while (list.length) {
2381
+ const entry = list.shift();
2382
+ if (entry && !entry.removed) {
2383
+ frame.invoke(entry.name, entry.args);
2384
+ }
2385
+ }
2386
+ }
2387
+ // ============================================================
2388
+ // Static private methods
2389
+ // ============================================================
2390
+ /**
2391
+ * Wrap a method on the prototype to add signature checking and resource cleanup.
2392
+ */
2393
+ static wrapMethod(proto, fnName, shortKey) {
2394
+ const originalFn = proto[fnName];
2395
+ if (typeof originalFn !== "function") return;
2396
+ const originalAsFn = originalFn;
2397
+ const wrapped = function(...args) {
2398
+ if (this.signature > 0) {
2399
+ this.signature++;
2400
+ this.fire("render");
2401
+ _View.destroyAllResources(this, false);
2402
+ const lookup = this;
2403
+ const candidate = lookup[fnName];
2404
+ const instanceFn = typeof candidate === "function" ? candidate : originalAsFn;
2405
+ const fnToCall = instanceFn === wrapped ? originalAsFn : instanceFn;
2406
+ return funcWithTry(fnToCall, args, this, noop);
2407
+ }
2408
+ return void 0;
2409
+ };
2410
+ proto[fnName] = wrapped;
2411
+ proto[shortKey] = wrapped;
2412
+ }
2413
+ /**
2414
+ * When two mixins define the same event method, merge them into
2415
+ * a single function that calls both in sequence.
2416
+ */
2417
+ static processMixinsSameEvent(additional, exist) {
2418
+ let temp;
2419
+ if (exist.handlerList) {
2420
+ temp = exist;
2421
+ } else {
2422
+ const merged = function(...e) {
2423
+ funcWithTry(merged.handlerList ?? [], e, this, noop);
2424
+ };
2425
+ merged.handlerList = [exist];
2426
+ merged.marker = 1;
2427
+ temp = merged;
2428
+ }
2429
+ temp.handlerList = (temp.handlerList ?? []).concat(
2430
+ additional.handlerList ?? [additional]
2431
+ );
2432
+ return temp;
2433
+ }
2434
+ /**
2435
+ * Merge an array of mixin objects into the view prototype.
2436
+ */
2437
+ static mergeMixins(mixins, viewClass, ctors) {
2438
+ const proto = viewClass.prototype;
2439
+ const temp = {};
2440
+ for (const node of mixins) {
2441
+ for (const p in node) {
2442
+ if (!hasOwnProperty(node, p)) continue;
2443
+ const fn = node[p];
2444
+ if (typeof fn !== "function") continue;
2445
+ const mixinFn = fn;
2446
+ const exist = temp[p];
2447
+ if (p === "make") {
2448
+ ctors.push(mixinFn);
2449
+ continue;
2450
+ }
2451
+ if (VIEW_EVENT_METHOD_REGEXP.test(p)) {
2452
+ if (exist) {
2453
+ temp[p] = _View.processMixinsSameEvent(mixinFn, exist);
2454
+ } else {
2455
+ mixinFn.marker = 1;
2456
+ temp[p] = mixinFn;
2457
+ }
2458
+ } else if (!exist) {
2459
+ temp[p] = mixinFn;
2460
+ }
2461
+ }
2462
+ }
2463
+ for (const p in temp) {
2464
+ if (!hasOwnProperty(proto, p)) {
2465
+ proto[p] = temp[p];
2466
+ }
2467
+ }
2468
+ }
2469
+ /**
2470
+ * Destroy a single resource entry.
2471
+ */
2472
+ static destroyResource(cache, key, callDestroy, oldEntity) {
2473
+ const entry = cache[key];
2474
+ if (!entry || entry.entity === oldEntity) return void 0;
2475
+ const entity = entry.entity;
2476
+ if (entity && typeof entity === "object") {
2477
+ const destroyFn = entity["destroy"];
2478
+ if (typeof destroyFn === "function" && callDestroy) {
2479
+ funcWithTry(destroyFn, [], entity, noop);
2480
+ }
2481
+ }
2482
+ Reflect.deleteProperty(cache, key);
2483
+ return entity;
2484
+ }
2485
+ // ============================================================
2486
+ // Static: extend and merge
2487
+ // ============================================================
2316
2488
  /**
2317
2489
  * Extend View to create a new View subclass.
2318
2490
  *
@@ -2322,19 +2494,20 @@ var View = class _View {
2322
2494
  * - Event method patterns: `'name<click>'` etc.
2323
2495
  */
2324
2496
  static extend(props, statics) {
2325
- props = props || {};
2326
- const make = props["make"];
2497
+ const definedProps = props ?? {};
2498
+ const make = definedProps["make"];
2327
2499
  const ctors = [];
2328
- if (make) {
2500
+ if (typeof make === "function") {
2329
2501
  ctors.push(make);
2330
2502
  }
2331
2503
  const ParentView = this;
2332
2504
  const ChildView = class extends ParentView {
2333
2505
  constructor(nodeId, ownerFrame, initParams, node, mixinCtors) {
2334
2506
  super(nodeId, ownerFrame, initParams, node, []);
2335
- for (const key in props) {
2336
- if (has(props, key) && key !== "make") {
2337
- this[key] = props[key];
2507
+ const instanceProps = this;
2508
+ for (const key in definedProps) {
2509
+ if (hasOwnProperty(definedProps, key) && key !== "make" && key !== "render") {
2510
+ instanceProps[key] = definedProps[key];
2338
2511
  }
2339
2512
  }
2340
2513
  this.id = nodeId;
@@ -2354,56 +2527,110 @@ var View = class _View {
2354
2527
  }
2355
2528
  };
2356
2529
  const proto = ChildView.prototype;
2357
- for (const key in props) {
2358
- if (has(props, key) && key !== "make") {
2359
- proto[key] = props[key];
2530
+ for (const key in definedProps) {
2531
+ if (hasOwnProperty(definedProps, key) && key !== "make") {
2532
+ proto[key] = definedProps[key];
2360
2533
  }
2361
2534
  }
2362
2535
  if (statics) {
2536
+ const staticTarget = ChildView;
2363
2537
  for (const key in statics) {
2364
- if (has(statics, key)) {
2365
- ChildView[key] = statics[key];
2538
+ if (hasOwnProperty(statics, key)) {
2539
+ staticTarget[key] = statics[key];
2366
2540
  }
2367
2541
  }
2368
2542
  }
2369
- ChildView.merge = viewMerge;
2370
- ChildView.extend = _View.extend;
2371
2543
  return ChildView;
2372
2544
  }
2373
2545
  /**
2374
2546
  * Merge mixins into View prototype.
2375
2547
  */
2376
2548
  static merge(...mixins) {
2377
- const self = this;
2378
- const existingCtors = self.ctors || [];
2379
- viewMergeMixins(mixins, self, existingCtors);
2380
- return self;
2549
+ const existingCtors = this.ctors || [];
2550
+ _View.mergeMixins(mixins, this, existingCtors);
2551
+ return this;
2381
2552
  }
2382
2553
  };
2383
- function viewMerge(...mixins) {
2384
- const self = this;
2385
- const existingCtors = self.ctors || [];
2386
- viewMergeMixins(mixins, self, existingCtors);
2387
- return self;
2554
+ function defineView(props, statics) {
2555
+ return View.extend(props, statics);
2388
2556
  }
2389
- function runInvokes(frame) {
2390
- const list = frame.invokeList;
2391
- if (!list) return;
2392
- while (list.length) {
2393
- const entry = list.shift();
2394
- if (entry && !entry.removed) {
2395
- frame.invoke(entry.name, entry.args);
2396
- }
2557
+
2558
+ // src/module-loader.ts
2559
+ var config = {
2560
+ rootId: "root",
2561
+ hashbang: "#!",
2562
+ error: (error) => {
2563
+ throw error;
2397
2564
  }
2565
+ };
2566
+ function use(names, callback) {
2567
+ const nameList = typeof names === "string" ? [names] : names;
2568
+ const loadPromise = (() => {
2569
+ if (config.require) {
2570
+ const result = config.require(nameList);
2571
+ if (result && typeof result.then === "function") {
2572
+ return result;
2573
+ }
2574
+ return Promise.resolve([]);
2575
+ }
2576
+ return Promise.all(
2577
+ nameList.map((name) => {
2578
+ const importPath = name.startsWith(".") || name.startsWith("/") ? name : `./${name}`;
2579
+ return import(
2580
+ /* @vite-ignore */
2581
+ /* webpackIgnore: true */
2582
+ importPath
2583
+ ).then((mod) => {
2584
+ return mod && (mod["__esModule"] || // For Webpack
2585
+ typeof mod["default"] === "function") ? mod["default"] : mod;
2586
+ }).catch((err) => {
2587
+ const errorHandler = config.error;
2588
+ if (errorHandler) {
2589
+ errorHandler(err instanceof Error ? err : new Error(String(err)));
2590
+ }
2591
+ return void 0;
2592
+ });
2593
+ })
2594
+ );
2595
+ })();
2596
+ if (callback) {
2597
+ loadPromise.then((modules) => {
2598
+ callback(...modules);
2599
+ });
2600
+ }
2601
+ return loadPromise;
2602
+ }
2603
+
2604
+ // src/view-registry.ts
2605
+ var viewClassRegistry = {};
2606
+ function getViewClass(path) {
2607
+ return viewClassRegistry[path];
2608
+ }
2609
+ function registerViewClass(viewPath, ViewClass) {
2610
+ const parsed = parseUri(viewPath);
2611
+ const path = parsed.path;
2612
+ if (path) {
2613
+ viewClassRegistry[path] = ViewClass;
2614
+ }
2615
+ }
2616
+ function invalidateViewClass(viewPath) {
2617
+ const parsed = parseUri(viewPath);
2618
+ const path = parsed.path;
2619
+ if (path) {
2620
+ Reflect.deleteProperty(viewClassRegistry, path);
2621
+ }
2622
+ }
2623
+ function getViewClassRegistry() {
2624
+ return viewClassRegistry;
2398
2625
  }
2399
2626
 
2400
2627
  // src/frame.ts
2401
2628
  var frameRegistry = /* @__PURE__ */ new Map();
2402
2629
  var rootFrame;
2403
2630
  var globalAlter;
2631
+ var MAX_FRAME_POOL = 64;
2404
2632
  var frameCache = [];
2405
2633
  var staticEmitter = new EventEmitter();
2406
- var viewClassRegistry = {};
2407
2634
  var Frame = class _Frame extends EventEmitter {
2408
2635
  /** Frame ID (same as owner DOM element ID) */
2409
2636
  id;
@@ -2418,8 +2645,8 @@ var Frame = class _Frame extends EventEmitter {
2418
2645
  childrenCount = 0;
2419
2646
  /** Ready count (children that have fired 'created') */
2420
2647
  readyCount = 0;
2421
- /** Ready map: id -> 1 */
2422
- readyMap = {};
2648
+ /** Set of child frame IDs that have fired 'created' */
2649
+ readyMap = /* @__PURE__ */ new Set();
2423
2650
  /** View instance */
2424
2651
  viewInstance;
2425
2652
  /** Get view instance (read-only) */
@@ -2490,31 +2717,42 @@ var Frame = class _Frame extends EventEmitter {
2490
2717
  this.viewPath = viewPath;
2491
2718
  const params = parsed["params"];
2492
2719
  translateQuery(pId || this.id, viewPath, params);
2720
+ const initParams = { ...params };
2493
2721
  if (viewInitParams) {
2494
- assign(params, viewInitParams);
2722
+ assign(initParams, viewInitParams);
2495
2723
  }
2496
2724
  const sign = this.signature;
2497
- if (viewClassRegistry[viewClassName]) {
2498
- this.doMountView(viewClassRegistry[viewClassName], params, node, sign);
2725
+ const registered = getViewClass(viewClassName);
2726
+ if (registered) {
2727
+ this.doMountView(registered, initParams, node, sign);
2728
+ return;
2499
2729
  }
2730
+ use(viewClassName, (ViewClass) => {
2731
+ if (sign !== this.signature) return;
2732
+ if (typeof ViewClass === "function") {
2733
+ const ViewClassTyped = ViewClass;
2734
+ registerViewClass(viewClassName, ViewClassTyped);
2735
+ this.doMountView(ViewClassTyped, initParams, node, sign);
2736
+ } else {
2737
+ const error = new Error(`Cannot load view: ${viewClassName}`);
2738
+ const errorHandler = config.error;
2739
+ if (errorHandler) {
2740
+ errorHandler(error);
2741
+ }
2742
+ }
2743
+ });
2500
2744
  }
2501
2745
  /**
2502
2746
  * Internal: actually mount the view after class is loaded.
2503
2747
  */
2504
2748
  doMountView(ViewClass, params, node, sign) {
2505
2749
  if (sign !== this.signature) return;
2506
- const mixinCtors = viewPrepare(ViewClass);
2507
- const ViewConstructor = ViewClass;
2508
- const view = new ViewConstructor(
2509
- this.id,
2510
- this,
2511
- params,
2512
- node,
2513
- mixinCtors
2514
- );
2750
+ const mixinCtors = View.prepare(ViewClass);
2751
+ const Ctor = ViewClass;
2752
+ const view = new Ctor(this.id, this, params, node, mixinCtors);
2515
2753
  this.viewInstance = view;
2516
2754
  view.signature = 1;
2517
- viewDelegateEvents(view);
2755
+ View.delegateEvents(view);
2518
2756
  const initResult = funcWithTry(
2519
2757
  view.init,
2520
2758
  [params, { node, deep: !view.template }],
@@ -2525,13 +2763,10 @@ var Frame = class _Frame extends EventEmitter {
2525
2763
  Promise.resolve(initResult).then(() => {
2526
2764
  if (nextSign !== this.signature) return;
2527
2765
  if (view.template) {
2528
- const renderFn = view.$b;
2529
- if (renderFn) {
2530
- renderFn.call(view);
2531
- }
2766
+ view.render();
2532
2767
  } else {
2533
2768
  this.hasAltered = 0;
2534
- if (!view.$e) {
2769
+ if (!view.endUpdatePendingFlag) {
2535
2770
  view.endUpdate();
2536
2771
  }
2537
2772
  }
@@ -2595,7 +2830,9 @@ var Frame = class _Frame extends EventEmitter {
2595
2830
  frame.unmountView();
2596
2831
  removeFrame(targetId, wasCreated);
2597
2832
  reInitFrameForCache(frame);
2598
- frameCache.push(frame);
2833
+ if (frameCache.length < MAX_FRAME_POOL) {
2834
+ frameCache.push(frame);
2835
+ }
2599
2836
  const parent = frameRegistry.get(pId || "");
2600
2837
  if (parent && parent.childrenMap[targetId]) {
2601
2838
  Reflect.deleteProperty(parent.childrenMap, targetId);
@@ -2614,13 +2851,12 @@ var Frame = class _Frame extends EventEmitter {
2614
2851
  const viewElements = rootEl.querySelectorAll(`[${LARK_VIEW}]`);
2615
2852
  const frames = [];
2616
2853
  viewElements.forEach((el) => {
2617
- const htmlEl = el;
2618
- if (!htmlEl.frameBound) {
2619
- const elId = ensureElementId2(htmlEl);
2620
- htmlEl.frameBound = 1;
2621
- const viewPath = getAttribute(htmlEl, LARK_VIEW);
2622
- frames.push([elId, viewPath]);
2623
- }
2854
+ if (!(el instanceof HTMLElement)) return;
2855
+ if (htmlElIsBound(el)) return;
2856
+ const elId = ensureElementId2(el);
2857
+ el.frameBound = 1;
2858
+ const viewPath = getAttribute(el, LARK_VIEW);
2859
+ frames.push([elId, viewPath]);
2624
2860
  });
2625
2861
  for (const [frameId, viewPath] of frames) {
2626
2862
  this.mountFrame(frameId, viewPath);
@@ -2633,7 +2869,7 @@ var Frame = class _Frame extends EventEmitter {
2633
2869
  */
2634
2870
  unmountZone(zoneId, _inner) {
2635
2871
  for (const childId in this.childrenMap) {
2636
- if (has(this.childrenMap, childId)) {
2872
+ if (hasOwnProperty(this.childrenMap, childId)) {
2637
2873
  if (!zoneId || childId !== zoneId) {
2638
2874
  this.unmountFrame(childId);
2639
2875
  }
@@ -2647,7 +2883,7 @@ var Frame = class _Frame extends EventEmitter {
2647
2883
  children() {
2648
2884
  const result = [];
2649
2885
  for (const id in this.childrenMap) {
2650
- if (has(this.childrenMap, id)) {
2886
+ if (hasOwnProperty(this.childrenMap, id)) {
2651
2887
  result.push(id);
2652
2888
  }
2653
2889
  }
@@ -2674,14 +2910,10 @@ var Frame = class _Frame extends EventEmitter {
2674
2910
  let result;
2675
2911
  const view = this.view;
2676
2912
  if (view && view.rendered) {
2677
- const fn = view[name];
2913
+ const lookup = view;
2914
+ const fn = lookup[name];
2678
2915
  if (typeof fn === "function") {
2679
- result = funcWithTry(
2680
- fn,
2681
- args || [],
2682
- view,
2683
- noop
2684
- );
2916
+ result = funcWithTry(fn, args || [], view, noop);
2685
2917
  }
2686
2918
  } else {
2687
2919
  const key = SPLITTER + name;
@@ -2704,6 +2936,25 @@ var Frame = class _Frame extends EventEmitter {
2704
2936
  }
2705
2937
  return result;
2706
2938
  }
2939
+ /**
2940
+ * Type-safe variant of `invoke`.
2941
+ *
2942
+ * `invoke()` accepts any string and any args, which silently hides
2943
+ * mismatched call sites when a method gets renamed. `invokeTyped` carries
2944
+ * the view's method signature through TypeScript so the compiler catches
2945
+ * those mistakes:
2946
+ *
2947
+ * ```ts
2948
+ * type Home = View & { loadData(id: string): Promise<void> };
2949
+ * frame.invokeTyped<Home, "loadData">("loadData", ["user-1"]);
2950
+ * ```
2951
+ *
2952
+ * Behavior is identical to `invoke` at runtime — same defer / direct-call
2953
+ * paths — so it's a drop-in safer overload.
2954
+ */
2955
+ invokeTyped(name, args) {
2956
+ return this.invoke(name, args);
2957
+ }
2707
2958
  // ============================================================
2708
2959
  // Static methods
2709
2960
  // ============================================================
@@ -2715,10 +2966,27 @@ var Frame = class _Frame extends EventEmitter {
2715
2966
  static getAll() {
2716
2967
  return frameRegistry;
2717
2968
  }
2718
- /** Get or create root frame */
2719
- static root(rootId) {
2969
+ /**
2970
+ * Returns the existing root frame, or `undefined` if none has been created.
2971
+ * Pure getter — never creates a Frame, never touches the DOM.
2972
+ *
2973
+ * Use `Frame.createRoot(id)` to create the root explicitly during framework
2974
+ * boot. For Micro-Frontend hosts that own multiple independent containers,
2975
+ * use `new Frame(containerId)` directly so each MF mount has its own root.
2976
+ */
2977
+ static getRoot() {
2978
+ return rootFrame;
2979
+ }
2980
+ /**
2981
+ * Create (or return) the singleton root frame for this app.
2982
+ *
2983
+ * Idempotent: subsequent calls always return the original root regardless
2984
+ * of `rootId` — so passing a different id later is silently ignored.
2985
+ * `Framework.boot()` is the canonical caller; user code rarely needs this.
2986
+ */
2987
+ static createRoot(rootId) {
2720
2988
  if (!rootFrame) {
2721
- rootId = rootId || "lark-root";
2989
+ rootId = rootId || "root";
2722
2990
  let rootElement = document.getElementById(rootId);
2723
2991
  if (!rootElement) {
2724
2992
  rootElement = document.body;
@@ -2728,6 +2996,17 @@ var Frame = class _Frame extends EventEmitter {
2728
2996
  }
2729
2997
  return rootFrame;
2730
2998
  }
2999
+ /**
3000
+ * @deprecated Use `Frame.getRoot()` for read-only access or
3001
+ * `Frame.createRoot(id)` to create the root explicitly. The single-method
3002
+ * `root()` blurred the distinction and was a common source of bugs in
3003
+ * Micro-Frontend hosts.
3004
+ *
3005
+ * Kept for backward compatibility — behavior unchanged.
3006
+ */
3007
+ static root(rootId) {
3008
+ return _Frame.createRoot(rootId);
3009
+ }
2731
3010
  /** Bind event listener (static) */
2732
3011
  static on(event, handler) {
2733
3012
  staticEmitter.on(event, handler);
@@ -2743,6 +3022,9 @@ var Frame = class _Frame extends EventEmitter {
2743
3022
  staticEmitter.fire(event, data);
2744
3023
  }
2745
3024
  };
3025
+ function htmlElIsBound(element) {
3026
+ return !!element.frameBound;
3027
+ }
2746
3028
  function ensureElementId2(element) {
2747
3029
  const id = element.getAttribute("id");
2748
3030
  if (id) return id;
@@ -2772,8 +3054,8 @@ function notifyCreated(frameInstance) {
2772
3054
  const pId = frameInstance.parentId;
2773
3055
  if (pId) {
2774
3056
  const parent = frameRegistry.get(pId);
2775
- if (parent && !parent.readyMap[frameInstance.id]) {
2776
- parent.readyMap[frameInstance.id] = 1;
3057
+ if (parent && !parent.readyMap.has(frameInstance.id)) {
3058
+ parent.readyMap.add(frameInstance.id);
2777
3059
  parent.readyCount++;
2778
3060
  notifyCreated(parent);
2779
3061
  }
@@ -2788,9 +3070,9 @@ function notifyAlter(frameInstance, data) {
2788
3070
  const pId = frameInstance.parentId;
2789
3071
  if (pId) {
2790
3072
  const parent = frameRegistry.get(pId);
2791
- if (parent && parent.readyMap[frameInstance.id]) {
3073
+ if (parent && parent.readyMap.has(frameInstance.id)) {
2792
3074
  parent.readyCount--;
2793
- Reflect.deleteProperty(parent.readyMap, frameInstance.id);
3075
+ parent.readyMap.delete(frameInstance.id);
2794
3076
  notifyAlter(parent, data);
2795
3077
  }
2796
3078
  }
@@ -2803,7 +3085,7 @@ function reInitFrame(frame, id, parentId) {
2803
3085
  frame["childrenCount"] = 0;
2804
3086
  frame["readyCount"] = 0;
2805
3087
  frame["signature"] = 1;
2806
- frame["readyMap"] = {};
3088
+ frame["readyMap"] = /* @__PURE__ */ new Set();
2807
3089
  frame["invokeList"] = [];
2808
3090
  frameRegistry.set(id, frame);
2809
3091
  }
@@ -2811,7 +3093,7 @@ function reInitFrameForCache(frame) {
2811
3093
  Reflect.set(frame, "id", "");
2812
3094
  frame["_parentId"] = void 0;
2813
3095
  frame["childrenMap"] = {};
2814
- frame["readyMap"] = {};
3096
+ frame["readyMap"] = /* @__PURE__ */ new Set();
2815
3097
  }
2816
3098
  function translateQuery(pId, src, params) {
2817
3099
  const parentFrame = frameRegistry.get(pId);
@@ -2821,37 +3103,153 @@ function translateQuery(pId, src, params) {
2821
3103
  if (!parentRefData) return;
2822
3104
  if (src.indexOf(SPLITTER) > 0) {
2823
3105
  translateData(parentRefData, params);
2824
- if (params[SPLITTER]) {
2825
- assign(
2826
- params,
2827
- params[SPLITTER]
2828
- );
3106
+ const paramsRec = params;
3107
+ const splitterValue = paramsRec[SPLITTER];
3108
+ if (splitterValue && typeof splitterValue === "object") {
3109
+ assign(params, splitterValue);
2829
3110
  Reflect.deleteProperty(params, SPLITTER);
2830
3111
  }
2831
3112
  }
2832
3113
  }
2833
- function registerViewClass(viewPath, ViewClass) {
2834
- const parsed = parseUri(viewPath);
2835
- const path = parsed.path;
2836
- if (path) {
2837
- viewClassRegistry[path] = ViewClass;
3114
+
3115
+ // src/cross-site.ts
3116
+ var preparePromises = {};
3117
+ var projectsMap = null;
3118
+ function loadRemoteView(viewPath, bizCode) {
3119
+ const crossConfigs = config.crossConfigs || window.crossConfigs;
3120
+ const currentName = config.projectName || "";
3121
+ const slashIndex = viewPath.indexOf("/");
3122
+ const projectName = slashIndex > -1 ? viewPath.substring(0, slashIndex) : viewPath;
3123
+ if (projectName === currentName) return Promise.resolve();
3124
+ if (!preparePromises[projectName]) {
3125
+ if (!projectsMap) {
3126
+ const map = toMap(crossConfigs || [], "projectName");
3127
+ projectsMap = map;
3128
+ }
3129
+ const info = projectsMap[projectName];
3130
+ if (!info) {
3131
+ return Promise.reject(
3132
+ new Error(`Cannot find ${projectName} from crossConfigs`)
3133
+ );
3134
+ }
3135
+ preparePromises[projectName] = use(`${projectName}/prepare`).then((modules) => {
3136
+ let mod = modules[0];
3137
+ if (mod && typeof mod === "object" && mod !== null) {
3138
+ const rec = mod;
3139
+ if (rec["__esModule"]) {
3140
+ mod = rec["default"];
3141
+ }
3142
+ }
3143
+ if (typeof mod === "function") {
3144
+ return mod({ bizCode });
3145
+ }
3146
+ return void 0;
3147
+ }).catch((err) => {
3148
+ Reflect.deleteProperty(preparePromises, projectName);
3149
+ throw err;
3150
+ });
2838
3151
  }
3152
+ return preparePromises[projectName];
3153
+ }
3154
+ function resetProjectsMap() {
3155
+ projectsMap = null;
2839
3156
  }
3157
+ var skeletonTemplate = (data, viewId) => {
3158
+ let skeletonHtml = "<div>Loading...</div>";
3159
+ if (data && typeof data === "object") {
3160
+ const candidate = data.skeleton;
3161
+ if (typeof candidate === "string") skeletonHtml = candidate;
3162
+ }
3163
+ return `<div id="mf_${viewId}">${skeletonHtml}</div>`;
3164
+ };
3165
+ var CrossSite = View.extend({
3166
+ /** Skeleton template renders loading state + child container */
3167
+ template: skeletonTemplate,
3168
+ init(params) {
3169
+ this.$sign = 0;
3170
+ this.on("destroy", () => {
3171
+ this.$sign = -1;
3172
+ });
3173
+ this.assign?.(params);
3174
+ },
3175
+ assign(data) {
3176
+ this.$view = typeof data["view"] === "string" ? data["view"] : "";
3177
+ const nested = data["params"];
3178
+ const nestedParams = nested && typeof nested === "object" ? nested : {};
3179
+ this.$params = { ...data, ...nestedParams };
3180
+ this.updater.set({
3181
+ skeleton: data["skeleton"],
3182
+ skeletonParams: data["skeletonParams"] || {},
3183
+ bizCode: data["bizCode"]
3184
+ });
3185
+ if (this.$sign > 0) {
3186
+ this.updateView();
3187
+ }
3188
+ return false;
3189
+ },
3190
+ async updateView() {
3191
+ const sign = ++this.$sign;
3192
+ const stored = this.updater.get();
3193
+ const bizCode = stored.bizCode;
3194
+ try {
3195
+ await loadRemoteView(this.$view, bizCode);
3196
+ } catch (ex) {
3197
+ const node = document.getElementById("mf_" + this.id);
3198
+ if (node) {
3199
+ const err = ex instanceof Error ? ex : new Error(String(ex));
3200
+ node.innerHTML = err.message || String(err);
3201
+ }
3202
+ }
3203
+ if (this.$sign !== sign) return;
3204
+ const mf = Frame.get("mf_" + this.id);
3205
+ const parsedNew = parseUri(this.$view);
3206
+ const newPath = parsedNew.path;
3207
+ const oldPath = mf?.viewPath ? parseUri(mf.viewPath).path : "";
3208
+ const view = mf?.view;
3209
+ if (newPath === oldPath && view && typeof view.assign === "function") {
3210
+ const result = funcWithTry(view.assign, [this.$params], view, noop);
3211
+ if (result) {
3212
+ view.render();
3213
+ }
3214
+ return;
3215
+ }
3216
+ const owner = this.owner;
3217
+ if (owner && typeof owner !== "number") {
3218
+ owner.mountFrame("mf_" + this.id, this.$view, this.$params);
3219
+ }
3220
+ },
3221
+ render() {
3222
+ const params = this.$params;
3223
+ this.updater.digest({
3224
+ skeleton: params?.["skeleton"]
3225
+ });
3226
+ this.updateView();
3227
+ },
3228
+ /**
3229
+ * Invoke a method on the remote view.
3230
+ * Usage: mf.invoke('callView', methodName, ...args)
3231
+ */
3232
+ callView(name, ...args) {
3233
+ const mf = Frame.get("mf_" + this.id);
3234
+ return mf?.invoke(name, args);
3235
+ }
3236
+ });
3237
+ var cross_site_default = CrossSite;
2840
3238
 
2841
3239
  // src/service.ts
2842
- var Bag = class {
2843
- /** Bag data */
3240
+ var Payload = class {
3241
+ /** Payload data */
2844
3242
  data;
2845
3243
  /** Internal cache info */
2846
3244
  cacheInfo;
2847
3245
  constructor(data = {}) {
2848
3246
  this.data = data;
2849
3247
  }
2850
- /** Get a value from bag data */
3248
+ /** Get a value from payload data */
2851
3249
  get(key) {
2852
3250
  return this.data[key];
2853
3251
  }
2854
- /** Set a value in bag data */
3252
+ /** Set a value in payload data */
2855
3253
  set(keyOrData, value) {
2856
3254
  if (typeof keyOrData === "string") {
2857
3255
  this.data[keyOrData] = value;
@@ -2863,297 +3261,398 @@ var Bag = class {
2863
3261
  };
2864
3262
  var FETCH_FLAGS_ALL = 1;
2865
3263
  var FETCH_FLAGS_ONE = 2;
2866
- function createServiceType(syncFn, cacheMax = 20, cacheBuffer = 5) {
2867
- const metas = {};
2868
- const bagCache = new Cache({
2869
- maxSize: cacheMax,
2870
- bufferSize: cacheBuffer
2871
- });
2872
- const pendingCacheKeys = {};
2873
- const staticEmitter2 = new EventEmitter();
2874
- const serviceType = {
2875
- add(attrs) {
2876
- if (!isArray(attrs)) {
2877
- attrs = [attrs];
2878
- }
2879
- for (const bag of attrs) {
2880
- if (bag) {
2881
- const name = bag.name;
2882
- const cache = bag.cache;
2883
- bag.cache = cache ? cache | 0 : 0;
2884
- metas[name] = bag;
2885
- }
2886
- }
2887
- },
2888
- meta(attrs) {
2889
- const name = typeof attrs === "string" ? attrs : attrs["name"];
2890
- return metas[name] || attrs;
2891
- },
2892
- create(attrs) {
2893
- const meta = serviceType.meta(attrs);
2894
- const cache = attrs["cache"] | 0 || meta.cache || 0;
2895
- const entity = new Bag();
2896
- entity.set(meta);
2897
- entity.cacheInfo = {
2898
- name: meta.name,
2899
- after: meta.after,
2900
- cleans: meta.cleans,
2901
- key: cache ? defaultCacheKey(meta, attrs) : "",
2902
- time: 0
2903
- };
2904
- if (typeof attrs === "object" && attrs !== null) {
2905
- entity.set(attrs);
2906
- }
2907
- const before = meta.before;
2908
- if (before) {
2909
- funcWithTry(before, [entity], entity, noop);
2910
- }
2911
- staticEmitter2.fire("begin", { bag: entity });
2912
- return entity;
2913
- },
2914
- get(attrs, createNew) {
2915
- let entity;
2916
- let needsUpdate = false;
2917
- if (!createNew) {
2918
- entity = serviceType.cached(attrs);
2919
- }
2920
- if (!entity) {
2921
- entity = serviceType.create(attrs);
2922
- needsUpdate = true;
2923
- }
2924
- return { entity, needsUpdate };
2925
- },
2926
- cached(attrs) {
2927
- const meta = serviceType.meta(attrs);
2928
- const cache = attrs["cache"] | 0 || meta.cache || 0;
2929
- let cacheKey = "";
2930
- if (cache) {
2931
- cacheKey = defaultCacheKey(meta, attrs);
2932
- }
2933
- if (cacheKey) {
2934
- const info = pendingCacheKeys[cacheKey];
2935
- if (info) {
2936
- return info.e;
2937
- }
2938
- const cached = bagCache.get(cacheKey);
2939
- if (cached && cached.cacheInfo) {
2940
- if (now() - cached.cacheInfo.time > cache) {
2941
- bagCache.del(cacheKey);
2942
- return void 0;
3264
+ var Service = class {
3265
+ /** Service instance ID */
3266
+ id = "";
3267
+ /** Whether service is busy (1 = busy) */
3268
+ busy = 0;
3269
+ /** Whether service is destroyed (1 = destroyed) */
3270
+ destroyed = 0;
3271
+ /** Task queue for sequential operations */
3272
+ taskQueue = [];
3273
+ /** Previous dequeue arguments */
3274
+ prevArgs = [];
3275
+ /** Instance event emitter */
3276
+ _emitter = new EventEmitter();
3277
+ constructor() {
3278
+ this.id = generateId("service");
3279
+ }
3280
+ // ============================================================
3281
+ // Instance accessors for type-level data
3282
+ // ============================================================
3283
+ /** Instance event emitter (public accessor) */
3284
+ get emitter() {
3285
+ return this._emitter;
3286
+ }
3287
+ /**
3288
+ * Get internals object for serviceSend compatibility.
3289
+ * References per-type static state from the current class.
3290
+ */
3291
+ get internals() {
3292
+ const ctor = this.constructor;
3293
+ return {
3294
+ metaList: ctor._metaList,
3295
+ payloadCache: ctor._payloadCache,
3296
+ pendingCacheKeys: ctor._pendingCacheKeys,
3297
+ syncFn: ctor._syncFn,
3298
+ staticEmitter: ctor._staticEmitter
3299
+ };
3300
+ }
3301
+ /**
3302
+ * Get type reference (the constructor) for serviceSend compatibility.
3303
+ * Static methods like get/create are accessible via the constructor.
3304
+ */
3305
+ get type() {
3306
+ return this.constructor;
3307
+ }
3308
+ // ============================================================
3309
+ // Instance methods
3310
+ // ============================================================
3311
+ /**
3312
+ * Fetch all endpoints, callback when all complete.
3313
+ * Uses cache when available.
3314
+ */
3315
+ all(attrs, done) {
3316
+ serviceSend(this, attrs, done, FETCH_FLAGS_ALL, false);
3317
+ return this;
3318
+ }
3319
+ /**
3320
+ * Fetch all endpoints, callback on each completion.
3321
+ */
3322
+ one(attrs, done) {
3323
+ serviceSend(this, attrs, done, FETCH_FLAGS_ONE, false);
3324
+ return this;
3325
+ }
3326
+ /**
3327
+ * Fetch all endpoints, skip cache (always request).
3328
+ */
3329
+ save(attrs, done) {
3330
+ serviceSend(this, attrs, done, FETCH_FLAGS_ALL, true);
3331
+ return this;
3332
+ }
3333
+ /**
3334
+ * Enqueue a task for sequential execution.
3335
+ */
3336
+ enqueue(callback) {
3337
+ if (!this.destroyed) {
3338
+ this.taskQueue.push(callback);
3339
+ this.dequeue(...this.prevArgs);
3340
+ }
3341
+ return this;
3342
+ }
3343
+ /**
3344
+ * Dequeue and execute the next task in queue.
3345
+ */
3346
+ dequeue(...args) {
3347
+ if (!this.busy && !this.destroyed) {
3348
+ this.busy = 1;
3349
+ setTimeout(() => {
3350
+ this.busy = 0;
3351
+ if (!this.destroyed) {
3352
+ const task2 = this.taskQueue.shift();
3353
+ if (task2) {
3354
+ this.prevArgs = args;
3355
+ funcWithTry(task2, args, this, noop);
2943
3356
  }
2944
- return cached;
2945
3357
  }
3358
+ }, 0);
3359
+ }
3360
+ }
3361
+ /**
3362
+ * Destroy the service instance.
3363
+ * After destruction, no new requests can be sent.
3364
+ */
3365
+ destroy() {
3366
+ this.destroyed = 1;
3367
+ this.taskQueue = [];
3368
+ }
3369
+ // Instance event methods (delegate to instance emitter)
3370
+ on(event, handler) {
3371
+ this._emitter.on(event, handler);
3372
+ return this;
3373
+ }
3374
+ off(event, handler) {
3375
+ this._emitter.off(event, handler);
3376
+ return this;
3377
+ }
3378
+ fire(event, data) {
3379
+ this._emitter.fire(event, data);
3380
+ return this;
3381
+ }
3382
+ // ============================================================
3383
+ // Per-type static state
3384
+ // ============================================================
3385
+ /** Per-type metadata registry */
3386
+ static _metaList = {};
3387
+ /** Per-type payload cache (LFU with frequency eviction) */
3388
+ static _payloadCache = new Cache({
3389
+ maxSize: 20,
3390
+ bufferSize: 5
3391
+ });
3392
+ /** Per-type pending cache keys for deduplication */
3393
+ static _pendingCacheKeys = {};
3394
+ /** Per-type sync function */
3395
+ static _syncFn = noop;
3396
+ /** Per-type static event emitter */
3397
+ static _staticEmitter = new EventEmitter();
3398
+ /** Per-type cache max size */
3399
+ static _cacheMax = 20;
3400
+ /** Per-type cache buffer size */
3401
+ static _cacheBuffer = 5;
3402
+ // ============================================================
3403
+ // Static methods (operate on per-type state via `this`)
3404
+ // ============================================================
3405
+ /**
3406
+ * Register API endpoint metadata.
3407
+ */
3408
+ static add(attrs) {
3409
+ if (!Array.isArray(attrs)) {
3410
+ attrs = [attrs];
3411
+ }
3412
+ for (const payload of attrs) {
3413
+ if (payload) {
3414
+ const name = payload.name;
3415
+ const cache = payload.cache;
3416
+ payload.cache = cache ? cache | 0 : 0;
3417
+ this._metaList[name] = payload;
2946
3418
  }
2947
- return void 0;
2948
- },
2949
- clear(names) {
2950
- const nameList = (typeof names === "string" ? names : names.join(",")).split(",");
2951
- const nameSet = {};
2952
- for (const n of nameList) {
2953
- nameSet[n] = 1;
2954
- }
2955
- const keysToDelete = [];
2956
- bagCache.forEach((bag) => {
2957
- if (bag?.cacheInfo && nameSet[bag.cacheInfo.name]) {
2958
- if (bag.cacheInfo.key) {
2959
- keysToDelete.push(bag.cacheInfo.key);
2960
- }
3419
+ }
3420
+ }
3421
+ /**
3422
+ * Get metadata for an API endpoint.
3423
+ */
3424
+ static meta(attrs) {
3425
+ const name = typeof attrs === "string" ? attrs : String(attrs["name"] ?? "");
3426
+ const known = this._metaList[name];
3427
+ if (known) return known;
3428
+ return attrs;
3429
+ }
3430
+ /**
3431
+ * Create a Payload for an API request.
3432
+ */
3433
+ static create(attrs) {
3434
+ const meta = this.meta(attrs);
3435
+ const cache = toCacheValue(attrs["cache"]) || meta.cache || 0;
3436
+ const entity = new Payload();
3437
+ entity.set(meta);
3438
+ entity.cacheInfo = {
3439
+ name: meta.name,
3440
+ after: typeof meta.after === "function" ? meta.after : void 0,
3441
+ cleans: typeof meta.cleanKeys === "string" ? meta.cleanKeys : void 0,
3442
+ key: cache ? defaultCacheKey(meta, attrs) : "",
3443
+ time: 0
3444
+ };
3445
+ if (attrs !== null) {
3446
+ entity.set(attrs);
3447
+ }
3448
+ const before = meta.before;
3449
+ if (typeof before === "function") {
3450
+ funcWithTry(before, [entity], entity, noop);
3451
+ }
3452
+ this._staticEmitter.fire("begin", { payload: entity });
3453
+ return entity;
3454
+ }
3455
+ /**
3456
+ * Get or create a Payload for an API request.
3457
+ */
3458
+ static get(attrs, createNew) {
3459
+ let entity;
3460
+ let needsUpdate = false;
3461
+ if (!createNew) {
3462
+ entity = this.cached(attrs);
3463
+ }
3464
+ if (!entity) {
3465
+ entity = this.create(attrs);
3466
+ needsUpdate = true;
3467
+ }
3468
+ return { entity, needsUpdate };
3469
+ }
3470
+ /**
3471
+ * Get cached Payload if available and not expired.
3472
+ */
3473
+ static cached(attrs) {
3474
+ const meta = this.meta(attrs);
3475
+ const cache = toCacheValue(attrs["cache"]) || meta.cache || 0;
3476
+ let cacheKey = "";
3477
+ if (cache) {
3478
+ cacheKey = defaultCacheKey(meta, attrs);
3479
+ }
3480
+ if (cacheKey) {
3481
+ const info = this._pendingCacheKeys[cacheKey];
3482
+ if (info) {
3483
+ const entity = info.entity;
3484
+ return entity instanceof Payload ? entity : void 0;
3485
+ }
3486
+ const cached = this._payloadCache.get(cacheKey);
3487
+ if (cached && cached.cacheInfo) {
3488
+ if (now() - cached.cacheInfo.time > cache) {
3489
+ this._payloadCache.del(cacheKey);
3490
+ return void 0;
2961
3491
  }
2962
- });
2963
- for (const key of keysToDelete) {
2964
- bagCache.del(key);
3492
+ return cached;
2965
3493
  }
2966
- },
2967
- on(event, handler) {
2968
- staticEmitter2.on(event, handler);
2969
- },
2970
- off(event, handler) {
2971
- staticEmitter2.off(event, handler);
2972
- },
2973
- fire(event, data) {
2974
- staticEmitter2.fire(event, data);
2975
- },
2976
- extend(newSyncFn, newCacheMax, newCacheBuffer) {
2977
- return createServiceType(
2978
- newSyncFn,
2979
- newCacheMax || cacheMax,
2980
- newCacheBuffer || cacheBuffer
2981
- );
2982
3494
  }
2983
- };
2984
- const internals = {
2985
- metas,
2986
- bagCache,
2987
- pendingCacheKeys,
2988
- syncFn,
2989
- staticEmitter: staticEmitter2
2990
- };
2991
- function ServiceInstance() {
2992
- this.id = generateId("s");
2993
- this["$e"] = 0;
2994
- this["$d"] = 0;
2995
- this["$g"] = [];
2996
- this["$h"] = [];
2997
- this._emitter = new EventEmitter();
2998
- this._internals = internals;
2999
- this._type = serviceType;
3000
- }
3001
- ServiceInstance.prototype = {
3002
- all(attrs, done) {
3003
- serviceSend(this, attrs, done, FETCH_FLAGS_ALL, false);
3004
- return this;
3005
- },
3006
- one(attrs, done) {
3007
- serviceSend(this, attrs, done, FETCH_FLAGS_ONE, false);
3008
- return this;
3009
- },
3010
- save(attrs, done) {
3011
- serviceSend(this, attrs, done, FETCH_FLAGS_ALL, true);
3012
- return this;
3013
- },
3014
- enqueue(callback) {
3015
- if (!this["$d"]) {
3016
- this["$g"].push(callback);
3017
- this.dequeue(...this["$h"]);
3018
- }
3019
- return this;
3020
- },
3021
- dequeue(...args) {
3022
- if (!this["$e"] && !this["$d"]) {
3023
- this["$e"] = 1;
3024
- setTimeout(() => {
3025
- this["$e"] = 0;
3026
- if (!this["$d"]) {
3027
- const task2 = this["$g"].shift();
3028
- if (task2) {
3029
- this["$h"] = args;
3030
- funcWithTry(task2, args, this, noop);
3031
- }
3032
- }
3033
- }, 0);
3495
+ return void 0;
3496
+ }
3497
+ /**
3498
+ * Clear cached payloads by endpoint name.
3499
+ */
3500
+ static clear(names) {
3501
+ const nameSet = new Set(
3502
+ (typeof names === "string" ? names : names.join(",")).split(",")
3503
+ );
3504
+ const keysToDelete = [];
3505
+ this._payloadCache.forEach((payload) => {
3506
+ const info = payload?.cacheInfo;
3507
+ if (info && info.key && nameSet.has(info.name)) {
3508
+ keysToDelete.push(info.key);
3034
3509
  }
3035
- },
3036
- destroy() {
3037
- this["$d"] = 1;
3038
- this["$g"] = [];
3039
- },
3040
- on(event, handler) {
3041
- this._emitter.on(event, handler);
3042
- return this;
3043
- },
3044
- off(event, handler) {
3045
- this._emitter.off(event, handler);
3046
- return this;
3047
- },
3048
- fire(event, data) {
3049
- this._emitter.fire(event, data);
3050
- return this;
3510
+ });
3511
+ for (const key of keysToDelete) {
3512
+ this._payloadCache.del(key);
3051
3513
  }
3052
- };
3053
- const staticsMap = {
3054
- add: serviceType.add,
3055
- meta: serviceType.meta,
3056
- create: serviceType.create,
3057
- get: serviceType.get,
3058
- cached: serviceType.cached,
3059
- clear: serviceType.clear,
3060
- on: serviceType.on,
3061
- off: serviceType.off,
3062
- fire: serviceType.fire,
3063
- extend: serviceType.extend,
3064
- _internals: internals
3065
- };
3066
- const constructor = Object.assign(
3067
- ServiceInstance,
3068
- staticsMap
3069
- );
3070
- return constructor;
3514
+ }
3515
+ // Static event methods (operate on per-type emitter)
3516
+ static on(event, handler) {
3517
+ this._staticEmitter.on(event, handler);
3518
+ }
3519
+ static off(event, handler) {
3520
+ this._staticEmitter.off(event, handler);
3521
+ }
3522
+ static fire(event, data) {
3523
+ this._staticEmitter.fire(event, data);
3524
+ }
3525
+ /**
3526
+ * Create a new Service subclass with a custom sync function.
3527
+ *
3528
+ * Each subclass gets its OWN copies of every per-type static field
3529
+ * (`_metaList`, `_payloadCache`, `_pendingCacheKeys`, `_syncFn`,
3530
+ * `_staticEmitter`, `_cacheMax`, `_cacheBuffer`) via `static override`.
3531
+ * This is intentional: it ensures that endpoint metadata, cache state,
3532
+ * in-flight dedup keys, and event subscribers are fully isolated between
3533
+ * different Service types, even when one extends another.
3534
+ *
3535
+ * **Do not refactor these `static override` declarations away** — sharing
3536
+ * them through prototype inheritance would let endpoints registered on one
3537
+ * subclass leak into another, and the LFU cache evictions of one type
3538
+ * would race with those of another.
3539
+ */
3540
+ static extend(newSyncFn, newCacheMax, newCacheBuffer) {
3541
+ const ParentService = this;
3542
+ class ChildService extends ParentService {
3543
+ // Intentionally per-subclass — see Service.extend doc.
3544
+ static _metaList = {};
3545
+ static _payloadCache = new Cache({
3546
+ maxSize: newCacheMax || ParentService._cacheMax,
3547
+ bufferSize: newCacheBuffer || ParentService._cacheBuffer
3548
+ });
3549
+ static _pendingCacheKeys = {};
3550
+ static _syncFn = newSyncFn;
3551
+ static _staticEmitter = new EventEmitter();
3552
+ static _cacheMax = newCacheMax || ParentService._cacheMax;
3553
+ static _cacheBuffer = newCacheBuffer || ParentService._cacheBuffer;
3554
+ }
3555
+ return ChildService;
3556
+ }
3557
+ };
3558
+ var metaJsonCache = /* @__PURE__ */ new WeakMap();
3559
+ function getMetaJson(meta) {
3560
+ let cached = metaJsonCache.get(meta);
3561
+ if (cached === void 0) {
3562
+ cached = JSON.stringify(meta);
3563
+ metaJsonCache.set(meta, cached);
3564
+ }
3565
+ return cached;
3071
3566
  }
3072
- var Service = createServiceType(noop);
3073
3567
  function defaultCacheKey(meta, attrs) {
3074
- return JSON.stringify(attrs) + SPLITTER + JSON.stringify(meta);
3568
+ return JSON.stringify(attrs) + SPLITTER + getMetaJson(meta);
3569
+ }
3570
+ function toCacheValue(v) {
3571
+ if (typeof v === "number") return v | 0;
3572
+ if (typeof v === "string") {
3573
+ const n = Number(v);
3574
+ return Number.isFinite(n) ? n | 0 : 0;
3575
+ }
3576
+ return 0;
3075
3577
  }
3076
3578
  function serviceSend(service, attrs, done, flag, save) {
3077
- if (service["$d"]) return;
3078
- if (service["$e"]) {
3079
- service.enqueue(
3080
- serviceSend.bind(null, service, attrs, done, flag, save)
3081
- );
3579
+ if (service.destroyed) return;
3580
+ if (service.busy) {
3581
+ const queued = () => serviceSend(service, attrs, done, flag, save);
3582
+ service.enqueue(queued);
3082
3583
  return;
3083
3584
  }
3084
- service["$e"] = 1;
3585
+ service.busy = 1;
3085
3586
  let attrList;
3086
3587
  if (typeof attrs === "string") {
3087
3588
  attrList = [{ name: attrs }];
3088
- } else if (isArray(attrs)) {
3589
+ } else if (Array.isArray(attrs)) {
3089
3590
  attrList = attrs;
3090
3591
  } else {
3091
3592
  attrList = [attrs];
3092
3593
  }
3093
- const internals = service._internals;
3594
+ const internals = service.internals;
3094
3595
  const { syncFn, pendingCacheKeys, staticEmitter: staticEmitter2 } = internals;
3095
3596
  let requestCount = 0;
3096
3597
  const total = attrList.length;
3097
3598
  const doneArr = new Array(total + 1);
3098
3599
  const errorArgs = [];
3099
3600
  const remoteComplete = (idx, error) => {
3100
- const bag = doneArr[idx + 1];
3101
- let newBag = false;
3601
+ const payload = doneArr[idx + 1];
3602
+ let newPayload = false;
3102
3603
  if (error) {
3103
3604
  errorArgs[idx] = error;
3104
- staticEmitter2.fire("fail", { bag, error });
3605
+ staticEmitter2.fire("fail", { payload, error });
3105
3606
  } else {
3106
- newBag = true;
3107
- staticEmitter2.fire("done", { bag });
3607
+ newPayload = true;
3608
+ staticEmitter2.fire("done", { payload });
3108
3609
  }
3109
- if (!service["$d"]) {
3610
+ if (!service.destroyed) {
3110
3611
  const finish = requestCount === total;
3111
3612
  if (finish) {
3112
- service["$e"] = 0;
3613
+ service.busy = 0;
3113
3614
  if (flag === FETCH_FLAGS_ALL) {
3114
3615
  doneArr[0] = errorArgs;
3115
3616
  funcWithTry(done, doneArr, service, noop);
3116
3617
  }
3117
3618
  }
3118
3619
  if (flag === FETCH_FLAGS_ONE) {
3119
- funcWithTry(done, [error || null, bag, finish, idx], service, noop);
3620
+ funcWithTry(done, [error || null, payload, finish, idx], service, noop);
3120
3621
  }
3121
3622
  }
3122
- if (newBag) {
3123
- staticEmitter2.fire("end", { bag, error });
3623
+ if (newPayload) {
3624
+ staticEmitter2.fire("end", { payload, error });
3124
3625
  }
3125
3626
  };
3126
3627
  for (const attr of attrList) {
3127
3628
  if (!attr) continue;
3128
3629
  const attrObj = typeof attr === "string" ? { name: attr } : attr;
3129
- const bagInfo = service._type.get(attrObj, save);
3130
- const bagEntity = bagInfo.entity;
3131
- const cacheKey = bagEntity.cacheInfo?.key || "";
3630
+ const payloadInfo = service.type.get(attrObj, save);
3631
+ const payloadEntity = payloadInfo.entity;
3632
+ const cacheKey = payloadEntity.cacheInfo?.key || "";
3132
3633
  const complete = remoteComplete.bind(null, requestCount++);
3133
3634
  if (cacheKey && pendingCacheKeys[cacheKey]) {
3134
3635
  pendingCacheKeys[cacheKey].push(complete);
3135
- } else if (bagInfo.needsUpdate) {
3636
+ } else if (payloadInfo.needsUpdate) {
3136
3637
  if (cacheKey) {
3137
3638
  const cacheList = [complete];
3138
- cacheList.e = bagEntity;
3639
+ cacheList.entity = payloadEntity;
3139
3640
  pendingCacheKeys[cacheKey] = cacheList;
3140
3641
  const cacheComplete = () => {
3141
3642
  const list = pendingCacheKeys[cacheKey];
3142
- const entity = list.e;
3143
- if (entity.cacheInfo) {
3643
+ const entity = list.entity;
3644
+ if (entity instanceof Payload && entity.cacheInfo) {
3144
3645
  entity.cacheInfo.time = now();
3646
+ internals.payloadCache.set(cacheKey, entity);
3145
3647
  }
3146
- internals.bagCache.set(cacheKey, entity);
3147
3648
  Reflect.deleteProperty(pendingCacheKeys, cacheKey);
3148
3649
  for (const cb of list) {
3149
- if (typeof cb === "function") {
3150
- cb();
3151
- }
3650
+ if (typeof cb === "function") cb();
3152
3651
  }
3153
3652
  };
3154
- syncFn(bagEntity, cacheComplete);
3653
+ syncFn(payloadEntity, cacheComplete);
3155
3654
  } else {
3156
- syncFn(bagEntity, complete);
3655
+ syncFn(payloadEntity, complete);
3157
3656
  }
3158
3657
  } else {
3159
3658
  complete();
@@ -3161,12 +3660,14 @@ function serviceSend(service, attrs, done, flag, save) {
3161
3660
  }
3162
3661
  }
3163
3662
 
3164
- // src/frame-visualizer/index.ts
3165
- var MSG_PING = "LARK_VIS_PING";
3166
- var MSG_PONG = "LARK_VIS_PONG";
3167
- var MSG_REQUEST_TREE = "LARK_VIS_REQUEST_TREE";
3168
- var MSG_TREE = "LARK_VIS_TREE";
3169
- var MSG_TREE_DELTA = "LARK_VIS_TREE_DELTA";
3663
+ // src/frame-visual.ts
3664
+ var FrameVisualBridge = {
3665
+ MSG_PING: "LARK_VIS_PING",
3666
+ MSG_PONG: "LARK_VIS_PONG",
3667
+ MSG_REQUEST_TREE: "LARK_VIS_REQUEST_TREE",
3668
+ MSG_TREE: "LARK_VIS_TREE",
3669
+ MSG_TREE_DELTA: "LARK_VIS_TREE_DELTA"
3670
+ };
3170
3671
  function serializeView(view) {
3171
3672
  return {
3172
3673
  id: view.id,
@@ -3206,7 +3707,10 @@ function serializeFrame(frameId) {
3206
3707
  };
3207
3708
  }
3208
3709
  function serializeFrameTree() {
3209
- const root = Frame.root();
3710
+ const root = Frame.getRoot();
3711
+ if (!root) {
3712
+ return { root: null, totalFrames: 0, timestamp: Date.now(), rootId: "" };
3713
+ }
3210
3714
  const rootNode = serializeFrame(root.id);
3211
3715
  let totalFrames = 0;
3212
3716
  const countFrames = (node) => {
@@ -3234,19 +3738,22 @@ function installFrameVisualizerBridge() {
3234
3738
  const data = event.data;
3235
3739
  if (!data || typeof data !== "object") return;
3236
3740
  const type = data.type;
3237
- if (type === MSG_PING) {
3741
+ if (type === FrameVisualBridge.MSG_PING) {
3238
3742
  const source = event.source;
3239
3743
  if (source) {
3240
- source.postMessage({ type: MSG_PONG }, { targetOrigin: "*" });
3744
+ source.postMessage(
3745
+ { type: FrameVisualBridge.MSG_PONG },
3746
+ { targetOrigin: "*" }
3747
+ );
3241
3748
  }
3242
3749
  return;
3243
3750
  }
3244
- if (type === MSG_REQUEST_TREE) {
3751
+ if (type === FrameVisualBridge.MSG_REQUEST_TREE) {
3245
3752
  const tree = serializeFrameTree();
3246
3753
  const source = event.source;
3247
3754
  if (source) {
3248
3755
  source.postMessage(
3249
- { type: MSG_TREE, data: tree },
3756
+ { type: FrameVisualBridge.MSG_TREE, data: tree },
3250
3757
  { targetOrigin: "*" }
3251
3758
  );
3252
3759
  }
@@ -3265,18 +3772,14 @@ function pushTreeUpdate() {
3265
3772
  const treeJson = JSON.stringify(tree);
3266
3773
  if (treeJson !== lastTreeJson) {
3267
3774
  lastTreeJson = treeJson;
3268
- window.parent.postMessage({ type: MSG_TREE_DELTA, data: tree }, "*");
3775
+ window.parent.postMessage(
3776
+ { type: FrameVisualBridge.MSG_TREE_DELTA, data: tree },
3777
+ "*"
3778
+ );
3269
3779
  }
3270
3780
  }
3271
3781
 
3272
3782
  // src/framework.ts
3273
- var config = {
3274
- rootId: "lark-root",
3275
- hashbang: "#!",
3276
- error: (error) => {
3277
- throw error;
3278
- }
3279
- };
3280
3783
  var booted3 = false;
3281
3784
  var taskList = [];
3282
3785
  var taskIndex = 0;
@@ -3325,6 +3828,9 @@ function task(fn, args, context) {
3325
3828
  }
3326
3829
  }
3327
3830
  var dispatcherUpdateTag = 0;
3831
+ function isThenable(value) {
3832
+ return !!value && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
3833
+ }
3328
3834
  function viewIsObserveChanged(view) {
3329
3835
  const loc = view.locationObserved;
3330
3836
  let result = false;
@@ -3338,7 +3844,7 @@ function viewIsObserveChanged(view) {
3338
3844
  const changedParams = lastChanged2?.params;
3339
3845
  if (changedParams) {
3340
3846
  for (const key of loc.keys) {
3341
- result = has(changedParams, key);
3847
+ result = hasOwnProperty(changedParams, key);
3342
3848
  if (result) break;
3343
3849
  }
3344
3850
  }
@@ -3350,49 +3856,65 @@ function stateIsObserveChanged(view, stateKeys) {
3350
3856
  const observedKeys = view.observedStateKeys;
3351
3857
  if (!observedKeys) return false;
3352
3858
  for (const key of observedKeys) {
3353
- if (has(stateKeys, key)) return true;
3859
+ if (stateKeys.has(key)) return true;
3354
3860
  }
3355
3861
  return false;
3356
3862
  }
3357
3863
  function dispatcherUpdate(frame, stateKeys) {
3358
- const frameInternal = frame;
3359
- const view = frame.view;
3360
- if (!view || frameInternal.dispatcherUpdateTag === dispatcherUpdateTag || view.signature <= 1) {
3361
- return;
3362
- }
3363
- frameInternal.dispatcherUpdateTag = dispatcherUpdateTag;
3364
- const isChanged = stateKeys ? stateIsObserveChanged(view, stateKeys) : viewIsObserveChanged(view);
3365
- let renderPromise;
3366
- if (isChanged) {
3367
- const renderResult = funcWithTry(view.$b ?? view.render, [], view, noop);
3368
- if (renderResult && typeof renderResult.then === "function") {
3369
- renderPromise = renderResult;
3370
- }
3371
- }
3372
- const children = frame.children();
3373
- const recurse = () => {
3374
- for (const childId of children) {
3375
- const childFrame = Frame.get(childId);
3376
- if (childFrame) {
3377
- dispatcherUpdate(childFrame, stateKeys);
3864
+ const stack = [frame];
3865
+ const drain = (s) => {
3866
+ while (s.length > 0) {
3867
+ const current = s.pop();
3868
+ const tagged = current;
3869
+ const view = current.view;
3870
+ if (!view || tagged.dispatcherUpdateTag === dispatcherUpdateTag || view.signature <= 1) {
3871
+ continue;
3872
+ }
3873
+ tagged.dispatcherUpdateTag = dispatcherUpdateTag;
3874
+ const isChanged = stateKeys ? stateIsObserveChanged(view, stateKeys) : viewIsObserveChanged(view);
3875
+ let renderPromise;
3876
+ if (isChanged) {
3877
+ const renderResult = funcWithTry(
3878
+ view.renderMethod ?? view.render,
3879
+ [],
3880
+ view,
3881
+ noop
3882
+ );
3883
+ if (isThenable(renderResult)) {
3884
+ renderPromise = renderResult;
3885
+ }
3886
+ }
3887
+ const children = current.children();
3888
+ if (renderPromise) {
3889
+ renderPromise.then(() => {
3890
+ const subStack = [];
3891
+ for (let i = children.length - 1; i >= 0; i--) {
3892
+ const child = Frame.get(children[i]);
3893
+ if (child) subStack.push(child);
3894
+ }
3895
+ drain(subStack);
3896
+ });
3897
+ } else {
3898
+ for (let i = children.length - 1; i >= 0; i--) {
3899
+ const child = Frame.get(children[i]);
3900
+ if (child) s.push(child);
3901
+ }
3378
3902
  }
3379
3903
  }
3380
3904
  };
3381
- if (renderPromise) {
3382
- renderPromise.then(recurse);
3383
- } else {
3384
- recurse();
3385
- }
3905
+ drain(stack);
3386
3906
  }
3387
3907
  function dispatcherNotifyChange(e) {
3388
- const rootFrame2 = Frame.root();
3389
- const view = e["view"];
3908
+ const rootFrame2 = Frame.getRoot();
3909
+ if (!rootFrame2) return;
3910
+ const routeEvent = e;
3911
+ const view = routeEvent.view;
3390
3912
  if (view) {
3391
3913
  const viewPath = typeof view === "object" && view !== null ? String(view.to || "") : String(view);
3392
3914
  rootFrame2.mountView(viewPath);
3393
3915
  } else {
3394
3916
  dispatcherUpdateTag++;
3395
- dispatcherUpdate(rootFrame2, e["keys"]);
3917
+ dispatcherUpdate(rootFrame2, e.keys);
3396
3918
  }
3397
3919
  }
3398
3920
  function dispatchEvent(target, eventType, eventInit) {
@@ -3403,23 +3925,8 @@ function dispatchEvent(target, eventType, eventInit) {
3403
3925
  });
3404
3926
  target.dispatchEvent(event);
3405
3927
  }
3406
- var Base = class extends EventEmitter {
3407
- };
3408
- function use(names, callback) {
3409
- if (!config.require) {
3410
- if (callback) callback();
3411
- return;
3412
- }
3413
- const nameList = typeof names === "string" ? [names] : names;
3414
- const result = config.require(nameList);
3415
- if (result && typeof result.then === "function") {
3416
- result.then((modules) => {
3417
- if (callback) callback(modules);
3418
- });
3419
- }
3420
- }
3421
3928
  var WAIT_OK = 1;
3422
- var WAIT_TIMEOUT_OR_UNFOUND = 0;
3929
+ var WAIT_TIMEOUT_OR_NOT_FOUND = 0;
3423
3930
  function waitZoneViewsRendered(viewId, timeout) {
3424
3931
  if (timeout == null) {
3425
3932
  timeout = 30 * 1e3;
@@ -3430,7 +3937,7 @@ function waitZoneViewsRendered(viewId, timeout) {
3430
3937
  const check = () => {
3431
3938
  const currentTime = now();
3432
3939
  if (currentTime > endTime || !checkFrame) {
3433
- resolve(WAIT_TIMEOUT_OR_UNFOUND);
3940
+ resolve(WAIT_TIMEOUT_OR_NOT_FOUND);
3434
3941
  } else if (checkFrame.childrenCount === checkFrame.readyCount) {
3435
3942
  resolve(WAIT_OK);
3436
3943
  } else {
@@ -3440,12 +3947,27 @@ function waitZoneViewsRendered(viewId, timeout) {
3440
3947
  setTimeout(check, 9);
3441
3948
  });
3442
3949
  }
3950
+ function getConfigImpl(key) {
3951
+ if (key === void 0) return config;
3952
+ return config[key];
3953
+ }
3443
3954
  var Framework = {
3444
3955
  // ============================================================
3445
3956
  // Lifecycle
3446
3957
  // ============================================================
3958
+ /** Read framework configuration. See `FrameworkInterface.getConfig`. */
3959
+ getConfig: getConfigImpl,
3960
+ /**
3961
+ * Merge a patch into framework configuration. See `FrameworkInterface.setConfig`.
3962
+ */
3963
+ setConfig(patch) {
3964
+ if (patch && typeof patch === "object") {
3965
+ assign(config, patch);
3966
+ }
3967
+ return config;
3968
+ },
3447
3969
  /**
3448
- * Get or set framework configuration.
3970
+ * @deprecated Use `getConfig()` / `setConfig()`. Behavior unchanged.
3449
3971
  */
3450
3972
  config(cfg) {
3451
3973
  if (!cfg) {
@@ -3466,17 +3988,17 @@ var Framework = {
3466
3988
  }
3467
3989
  Router._setConfig(config);
3468
3990
  EventDelegator.setFrameGetter((id) => Frame.get(id));
3469
- Router.on(ROUTER_EVENTS.CHANGED, (data) => {
3470
- dispatcherNotifyChange(data);
3991
+ Router.on(RouterEvents.CHANGED, (data) => {
3992
+ if (data) dispatcherNotifyChange(data);
3471
3993
  });
3472
- State.on(ROUTER_EVENTS.CHANGED, (data) => {
3473
- dispatcherNotifyChange(data);
3994
+ State.on(RouterEvents.CHANGED, (data) => {
3995
+ if (data) dispatcherNotifyChange(data);
3474
3996
  });
3475
3997
  booted3 = true;
3476
3998
  markBooted();
3477
3999
  markRouterBooted();
3478
4000
  installFrameVisualizerBridge();
3479
- const rootFrame2 = Frame.root(config.rootId);
4001
+ const rootFrame2 = Frame.createRoot(config.rootId);
3480
4002
  Router._bind();
3481
4003
  const defaultView = config.defaultView || "";
3482
4004
  if (defaultView && !rootFrame2.view) {
@@ -3507,7 +4029,7 @@ var Framework = {
3507
4029
  /** Wait for zone views to be rendered */
3508
4030
  waitZoneViewsRendered,
3509
4031
  WAIT_OK,
3510
- WAIT_TIMEOUT_OR_UNFOUND,
4032
+ WAIT_TIMEOUT_OR_NOT_FOUND,
3511
4033
  /**
3512
4034
  * Convert array to hash map.
3513
4035
  */
@@ -3531,7 +4053,7 @@ var Framework = {
3531
4053
  /**
3532
4054
  * Check if object has own property.
3533
4055
  */
3534
- has,
4056
+ has: hasOwnProperty,
3535
4057
  /**
3536
4058
  * Get object keys.
3537
4059
  */
@@ -3572,7 +4094,7 @@ var Framework = {
3572
4094
  /**
3573
4095
  * Base class with EventEmitter.
3574
4096
  */
3575
- Base,
4097
+ Base: EventEmitter,
3576
4098
  // ============================================================
3577
4099
  // Module access
3578
4100
  // ============================================================
@@ -3590,10 +4112,14 @@ if (typeof window !== "undefined") {
3590
4112
  window.__lark_State = State;
3591
4113
  window.__lark_Router = Router;
3592
4114
  window.__lark_Frame = Frame;
4115
+ window.__lark_View = View;
4116
+ window.__lark_invalidateViewClass = invalidateViewClass;
4117
+ window.__lark_getViewClassRegistry = getViewClassRegistry;
4118
+ window.__lark_registerViewClass = registerViewClass;
3593
4119
  }
3594
4120
 
3595
4121
  // src/store.ts
3596
- var LARK_GLOBAL = "lark_global";
4122
+ var LARK_GLOBAL = "lark-global";
3597
4123
  var Platform = /* @__PURE__ */ ((Platform2) => {
3598
4124
  Platform2["Lark"] = "lark";
3599
4125
  Platform2["React"] = "react";
@@ -3603,28 +4129,38 @@ var Platform = /* @__PURE__ */ ((Platform2) => {
3603
4129
  var isFunction = (val) => typeof val === "function";
3604
4130
  var isObject = (val) => val !== null && typeof val === "object";
3605
4131
  var isPromise = (val) => isObject(val) && isFunction(val["then"]);
3606
- var hasOwnProperty = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
3607
- var deepClone = (obj) => {
4132
+ var hasOwnProperty2 = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
4133
+ var hasStructuredClone = typeof globalThis !== "undefined" && typeof globalThis.structuredClone === "function";
4134
+ var deepCloneFallback = (obj) => {
3608
4135
  if (!obj || !isObject(obj)) return {};
3609
- const newData = Array.isArray(obj) ? [] : Object.create(Object.getPrototypeOf(obj));
4136
+ const newData = Array.isArray(obj) ? [] : {};
3610
4137
  for (const key in obj) {
3611
- if (hasOwnProperty(obj, key)) {
4138
+ if (hasOwnProperty2(obj, key)) {
3612
4139
  const value = obj[key];
3613
- newData[key] = isObject(value) ? deepClone(value) : value;
4140
+ newData[key] = isObject(value) ? deepCloneFallback(value) : value;
3614
4141
  }
3615
4142
  }
3616
4143
  return newData;
3617
4144
  };
4145
+ var deepClone = (obj) => {
4146
+ if (hasStructuredClone) {
4147
+ try {
4148
+ return structuredClone(obj);
4149
+ } catch {
4150
+ return deepCloneFallback(obj);
4151
+ }
4152
+ }
4153
+ return deepCloneFallback(obj);
4154
+ };
3618
4155
  var cloneData = (data) => isObject(data) ? deepClone(data) : data;
3619
4156
  var getDataByKey = (target, key) => {
3620
- let data;
3621
- const rec = target;
3622
- if (key.includes(".")) {
3623
- key.split(".").forEach((k, index) => {
3624
- data = index === 0 ? rec?.[k] : data?.[k];
3625
- });
3626
- } else {
3627
- data = rec?.[key];
4157
+ if (!key.includes(".")) {
4158
+ return target[key];
4159
+ }
4160
+ let data = target;
4161
+ for (const k of key.split(".")) {
4162
+ if (!isObject(data)) return void 0;
4163
+ data = data[k];
3628
4164
  }
3629
4165
  return data;
3630
4166
  };
@@ -3636,8 +4172,10 @@ var Queue = class {
3636
4172
  const flushTickTask = () => {
3637
4173
  while (queue.length > 0) {
3638
4174
  const task2 = queue.shift();
3639
- pendingTasks.delete(task2);
3640
- runTask(task2);
4175
+ if (task2) {
4176
+ pendingTasks.delete(task2);
4177
+ runTask(task2);
4178
+ }
3641
4179
  }
3642
4180
  };
3643
4181
  Promise.resolve().then(flushTickTask);
@@ -3670,17 +4208,18 @@ var Queue = class {
3670
4208
  };
3671
4209
  var addParams2Callback = (cb, params) => {
3672
4210
  if (!cb || !params) return;
3673
- const cbObj = cb;
3674
- if (isObject(cb) && isObject(cbObj["params"])) {
3675
- Object.assign(cbObj["params"], params);
4211
+ const tagged = cb;
4212
+ const existing = tagged.params;
4213
+ if (isObject(existing)) {
4214
+ Object.assign(existing, params);
3676
4215
  } else {
3677
- cbObj["params"] = params;
4216
+ tagged.params = params;
3678
4217
  }
3679
4218
  };
3680
4219
  var runTask = (cb) => {
3681
- const cbObj = cb;
3682
- const params = cbObj["params"];
3683
- delete cbObj["params"];
4220
+ const tagged = cb;
4221
+ const params = tagged.params;
4222
+ delete tagged.params;
3684
4223
  try {
3685
4224
  cb(params);
3686
4225
  } catch {
@@ -3706,7 +4245,9 @@ var ArrMethods = {};
3706
4245
  if (res === -1 || res === false) {
3707
4246
  res = rawMethod.apply(
3708
4247
  this,
3709
- args.map((item) => ProxyCache.get(item) ?? item)
4248
+ args.map(
4249
+ (item) => isObject(item) ? ProxyCache.get(item) ?? item : item
4250
+ )
3710
4251
  );
3711
4252
  }
3712
4253
  return res;
@@ -3724,6 +4265,7 @@ var inArrUpdate = false;
3724
4265
  });
3725
4266
  function needKeepArrItem(target, newVal, key) {
3726
4267
  if (!Array.isArray(target) || !inArrUpdate) return false;
4268
+ if (!isObject(newVal)) return false;
3727
4269
  return getLinkKeys(newVal) === createLinkKeys(target, key);
3728
4270
  }
3729
4271
  var defStateConfig = {
@@ -3735,11 +4277,13 @@ var StateConfigMap = /* @__PURE__ */ new WeakMap();
3735
4277
  var setStateConfig = (target, config2) => {
3736
4278
  if (target && isObject(config2)) StateConfigMap.set(target, config2);
3737
4279
  };
3738
- var getStateConfig = (target, key) => {
3739
- if (!StateConfigMap.has(target)) return void 0;
4280
+ function getStateConfig(target, key) {
4281
+ if (!StateConfigMap.has(target)) {
4282
+ return void 0;
4283
+ }
3740
4284
  const config2 = StateConfigMap.get(target);
3741
- return key ? config2[key] : config2;
3742
- };
4285
+ return key ? config2?.[key] : config2;
4286
+ }
3743
4287
  var isState = (target) => isObject(target) && StateConfigMap.has(target);
3744
4288
  var createLinkKeys = (target, property) => {
3745
4289
  if (!hasLinkKeys(target)) return property;
@@ -3777,7 +4321,7 @@ var needKeep = (key) => {
3777
4321
  };
3778
4322
  var canSetNewVal = (params) => {
3779
4323
  const { property, newVal, oldVal } = params;
3780
- if (hasOwnProperty(params.target, property) && newVal === oldVal)
4324
+ if (hasOwnProperty2(params.target, property) && newVal === oldVal)
3781
4325
  return false;
3782
4326
  return true;
3783
4327
  };
@@ -3789,16 +4333,13 @@ var getNewVal = (params) => {
3789
4333
  if (config2?.shallow) return newVal;
3790
4334
  if (needKeepVal) return newVal;
3791
4335
  const linkKeys = createLinkKeys(target, property);
3792
- const newState = createState(newVal, {
3793
- ...config2,
3794
- linkKeys
3795
- });
4336
+ const newState = createState(newVal, { ...config2, linkKeys });
3796
4337
  if (isPromise(newVal)) handlePromise(newVal, target, property);
3797
4338
  return newState;
3798
4339
  };
3799
4340
  var genPayload = (params) => {
3800
4341
  const { target, property, newVal, oldVal } = params;
3801
- const config2 = getStateConfig(target) || defStateConfig;
4342
+ const config2 = getStateConfig(target) ?? defStateConfig;
3802
4343
  return {
3803
4344
  belong: config2.belong || LARK_GLOBAL,
3804
4345
  target,
@@ -3808,11 +4349,12 @@ var genPayload = (params) => {
3808
4349
  };
3809
4350
  };
3810
4351
  var handlePromise = (child, parent, key) => {
4352
+ const childObj = child;
3811
4353
  child.then((res) => {
3812
4354
  const parentProxy = ProxyCache.get(parent);
3813
4355
  if (parentProxy) {
3814
4356
  const oldVal = parentProxy[key];
3815
- if (ProxyCache.get(child) === oldVal) {
4357
+ if (ProxyCache.get(childObj) === oldVal) {
3816
4358
  parentProxy[key] = res;
3817
4359
  }
3818
4360
  }
@@ -3820,7 +4362,7 @@ var handlePromise = (child, parent, key) => {
3820
4362
  const parentProxy = ProxyCache.get(parent);
3821
4363
  if (parentProxy) {
3822
4364
  const oldVal = parentProxy[key];
3823
- if (ProxyCache.get(child) === oldVal) {
4365
+ if (ProxyCache.get(childObj) === oldVal) {
3824
4366
  parentProxy[key] = err;
3825
4367
  }
3826
4368
  }
@@ -3832,7 +4374,7 @@ var mark2 = (host, key) => {
3832
4374
  let sign;
3833
4375
  if (!host[_deleteKey]) {
3834
4376
  const markHost = host[_markObjKey] || (host[_markObjKey] = {});
3835
- if (!hasOwnProperty(markHost, key)) {
4377
+ if (!hasOwnProperty2(markHost, key)) {
3836
4378
  markHost[key] = 0;
3837
4379
  }
3838
4380
  sign = ++markHost[key];
@@ -3869,7 +4411,7 @@ function createState(initialData, config2) {
3869
4411
  oldVal
3870
4412
  });
3871
4413
  if (!canSet) return true;
3872
- const proxyTarget = receiver || state;
4414
+ const proxyTarget = isObject(receiver) && receiver !== null ? receiver : state;
3873
4415
  newVal = getNewVal({
3874
4416
  target: proxyTarget,
3875
4417
  property: strProp,
@@ -3889,7 +4431,7 @@ function createState(initialData, config2) {
3889
4431
  },
3890
4432
  deleteProperty(target, property) {
3891
4433
  const strProp = property;
3892
- if (!hasOwnProperty(target, strProp)) return true;
4434
+ if (!hasOwnProperty2(target, strProp)) return true;
3893
4435
  const oldVal = Reflect.get(target, property);
3894
4436
  Reflect.deleteProperty(target, property);
3895
4437
  const payload = genPayload({
@@ -3933,11 +4475,8 @@ function shallowSet(target, key, data) {
3933
4475
  keep(key);
3934
4476
  const config2 = getStateConfig(target);
3935
4477
  const linkKeys = createLinkKeys(target, key);
3936
- target[key] = createState(data, {
3937
- ...config2,
3938
- linkKeys,
3939
- shallow: true
3940
- });
4478
+ target[key] = createState(data, { ...config2, linkKeys, shallow: true });
4479
+ return target[key];
3941
4480
  }
3942
4481
  var GlobalDeps = /* @__PURE__ */ new Map();
3943
4482
  function track(payload) {
@@ -4018,8 +4557,22 @@ var _stateKeys = /* @__PURE__ */ Symbol("state-keys");
4018
4557
  var _storeState = /* @__PURE__ */ Symbol("store-state");
4019
4558
  var _storeDefScheduler = /* @__PURE__ */ Symbol("store-def-scheduler");
4020
4559
  var _storeProxy = /* @__PURE__ */ Symbol("fn:store-proxy");
4560
+ var _computedKeys = /* @__PURE__ */ Symbol("store-computed-keys");
4561
+ var COMPUTED_BRAND = /* @__PURE__ */ Symbol("store-computed-brand");
4562
+ function computed(deps, fn) {
4563
+ const marker = {
4564
+ [COMPUTED_BRAND]: true,
4565
+ deps,
4566
+ fn
4567
+ };
4568
+ return marker;
4569
+ }
4570
+ function isComputedMarker(val) {
4571
+ return isObject(val) && val[COMPUTED_BRAND] === true;
4572
+ }
4021
4573
  var BaseStore = class {
4022
4574
  [_storeStatus] = 0 /* BEFORE_CREATE */;
4575
+ [_computedKeys] = /* @__PURE__ */ new Set();
4023
4576
  [_storeDefScheduler] = getDefScheduler;
4024
4577
  [_storeBoot]() {
4025
4578
  this[_storeStatus] = 2 /* ACTIVE */;
@@ -4042,10 +4595,16 @@ var BaseStore = class {
4042
4595
  if (isObject(body)) {
4043
4596
  const state = {};
4044
4597
  const handlers = {};
4598
+ const computedDefs = {};
4599
+ const computedKeys = this[_computedKeys];
4045
4600
  Reflect.ownKeys(body).forEach((key) => {
4046
4601
  const strKey = key;
4047
4602
  const val = body[strKey];
4048
- if (isFunction(val)) {
4603
+ if (isComputedMarker(val)) {
4604
+ computedDefs[strKey] = val;
4605
+ state[strKey] = void 0;
4606
+ computedKeys.add(strKey);
4607
+ } else if (isFunction(val)) {
4049
4608
  if (!excludeFns.includes(strKey)) handlers[strKey] = val;
4050
4609
  } else {
4051
4610
  state[strKey] = val;
@@ -4055,6 +4614,22 @@ var BaseStore = class {
4055
4614
  this[_originState] = cloneData(state);
4056
4615
  this[_stateKeys] = Object.keys(state);
4057
4616
  this[_storeState] = createState(state, { belong: this[_storeName] });
4617
+ if (Object.keys(computedDefs).length > 0) {
4618
+ const belong = this[_storeName];
4619
+ const storeState = this[_storeState];
4620
+ for (const key of Object.keys(computedDefs)) {
4621
+ const def = computedDefs[key];
4622
+ const recompute = () => {
4623
+ storeState[key] = def.fn();
4624
+ };
4625
+ recompute();
4626
+ const trackList = def.deps.map((depKey) => ({
4627
+ key: depKey,
4628
+ cb: recompute
4629
+ }));
4630
+ if (trackList.length > 0) track({ belong, trackList });
4631
+ }
4632
+ }
4058
4633
  }
4059
4634
  }
4060
4635
  constructor(name, config2) {
@@ -4076,18 +4651,21 @@ var BaseStore = class {
4076
4651
  }
4077
4652
  [_storeProxy](toOut = false) {
4078
4653
  const self = this;
4079
- return new Proxy(self, {
4654
+ const proxy = new Proxy(self, {
4080
4655
  get(target, property) {
4081
- if (self[_stateKeys].includes(property)) {
4082
- const val = self[_storeState][property];
4656
+ const strProp = property;
4657
+ if (self[_stateKeys].includes(strProp)) {
4658
+ const val = self[_storeState][strProp];
4083
4659
  return toOut ? cloneData(val) : val;
4084
4660
  }
4085
4661
  return Reflect.get(target, property);
4086
4662
  },
4087
4663
  set(_target, property, val) {
4088
4664
  if (toOut) return true;
4089
- if (self[_stateKeys].includes(property)) {
4090
- self[_storeState][property] = val;
4665
+ const strProp = property;
4666
+ if (self[_computedKeys].has(strProp)) return true;
4667
+ if (self[_stateKeys].includes(strProp)) {
4668
+ self[_storeState][strProp] = val;
4091
4669
  }
4092
4670
  return true;
4093
4671
  },
@@ -4095,11 +4673,14 @@ var BaseStore = class {
4095
4673
  return Reflect.has(target, property) || self[_stateKeys].includes(property);
4096
4674
  }
4097
4675
  });
4676
+ return proxy;
4098
4677
  }
4099
4678
  };
4100
4679
  var LarkUtils = {
4101
4680
  isLarkView(instance) {
4102
- return isObject(instance) && !!instance.updater;
4681
+ if (!isObject(instance)) return false;
4682
+ const updater = instance["updater"];
4683
+ return isObject(updater) && isFunction(updater["set"]) && isFunction(updater["digest"]);
4103
4684
  },
4104
4685
  getRender(view) {
4105
4686
  return view.updater.digest.bind(view.updater);
@@ -4115,41 +4696,45 @@ var getLarkAdapter = (storeName) => ({
4115
4696
  Store: LarkStore,
4116
4697
  useStore: ((view) => {
4117
4698
  const store = getStore(storeName);
4699
+ if (!(store instanceof LarkStore)) return {};
4118
4700
  return store[_storeBoot](view);
4119
4701
  })
4120
4702
  });
4121
- var innerObserveFlags = /* @__PURE__ */ Symbol("store-inner-observe-flags");
4122
- var boundViews = /* @__PURE__ */ Symbol("bound-views");
4703
+ var _innerObserveFlags = /* @__PURE__ */ Symbol("store-inner-observe-flags");
4704
+ var _boundViews = /* @__PURE__ */ Symbol("store-bound-views");
4123
4705
  var LarkStore = class extends BaseStore {
4124
- [boundViews] = /* @__PURE__ */ new Set();
4125
- [innerObserveFlags] = /* @__PURE__ */ new Set();
4706
+ [_boundViews] = /* @__PURE__ */ new Set();
4707
+ [_innerObserveFlags] = /* @__PURE__ */ new Set();
4126
4708
  [_storeBoot](view) {
4127
- if (view && LarkUtils.isLarkView(view) && !this[boundViews].has(view)) {
4128
- this[boundViews].add(view);
4709
+ if (view && LarkUtils.isLarkView(view) && !this[_boundViews].has(view)) {
4710
+ this[_boundViews].add(view);
4129
4711
  LarkUtils.onDestroy(view, () => {
4130
- this[boundViews].delete(view);
4712
+ this[_boundViews].delete(view);
4131
4713
  });
4132
4714
  }
4133
4715
  return super[_storeBoot]();
4134
4716
  }
4135
4717
  [_storeDestroy]() {
4136
- this[boundViews].clear();
4137
- this[innerObserveFlags].clear();
4718
+ this[_boundViews].clear();
4719
+ this[_innerObserveFlags].clear();
4138
4720
  super[_storeDestroy]();
4139
4721
  }
4140
4722
  observe(view, keys2, defCallback) {
4141
4723
  if (this[_storeStatus] !== 2 /* ACTIVE */) return noop;
4142
- let observeKeys = keys2;
4724
+ let observeKeys = Array.isArray(keys2) ? keys2 : [];
4143
4725
  const _view = view;
4144
4726
  const renderFn = _view ? LarkUtils.getRender(_view) : noop;
4145
4727
  const dateSetterFn = _view ? LarkUtils.getDataSetter(_view) : noop;
4146
4728
  const isInnerObserve = !view;
4147
4729
  const innerFlags = /* @__PURE__ */ new Set();
4148
- const storeInnerObserveFlags = this[innerObserveFlags];
4730
+ const storeInnerObserveFlags = this[_innerObserveFlags];
4149
4731
  if (isFunction(keys2)) {
4150
4732
  const res = keys2();
4151
4733
  if (Array.isArray(res)) observeKeys = res;
4152
4734
  }
4735
+ if (keys2 === void 0 && _view && observeKeys.length === 0 && Array.isArray(this[_stateKeys])) {
4736
+ observeKeys = this[_stateKeys].slice();
4737
+ }
4153
4738
  const defSetter = (immediate, key, alias, transform) => () => {
4154
4739
  const stateVal = getDataByKey(this, key);
4155
4740
  let data = { [alias || key]: stateVal };
@@ -4204,11 +4789,12 @@ var getReactAdapter = (storeName) => ({
4204
4789
  Store: ReactStore,
4205
4790
  useStore: (() => {
4206
4791
  const store = getStore(storeName);
4792
+ if (!(store instanceof ReactStore)) return {};
4207
4793
  return store[_storeBoot]();
4208
4794
  })
4209
4795
  });
4210
- var observeSym = /* @__PURE__ */ Symbol("observe");
4211
- var getLastState = /* @__PURE__ */ Symbol("get-last-state");
4796
+ var _observe = /* @__PURE__ */ Symbol("store-observe");
4797
+ var _getLastState = /* @__PURE__ */ Symbol("store-get-last-state");
4212
4798
  var ReactStore = class extends BaseStore {
4213
4799
  stateChangeCount = 0;
4214
4800
  lastCount = 0;
@@ -4222,7 +4808,7 @@ var ReactStore = class extends BaseStore {
4222
4808
  }
4223
4809
  [_storeCreate](body) {
4224
4810
  super[_storeCreate](body);
4225
- this[observeSym](() => {
4811
+ this[_observe](() => {
4226
4812
  this.stateChangeCount += 1;
4227
4813
  });
4228
4814
  }
@@ -4232,7 +4818,7 @@ var ReactStore = class extends BaseStore {
4232
4818
  this.lastCount = currCount;
4233
4819
  return changed;
4234
4820
  }
4235
- [getLastState](handlers) {
4821
+ [_getLastState](handlers) {
4236
4822
  if (this.isStateChanged() || !this.lastState) {
4237
4823
  const state = this[_storeState];
4238
4824
  const immutableState = freezeData(state);
@@ -4240,7 +4826,7 @@ var ReactStore = class extends BaseStore {
4240
4826
  }
4241
4827
  return this.lastState;
4242
4828
  }
4243
- [observeSym](cb) {
4829
+ [_observe](cb) {
4244
4830
  const tasks = this[_stateKeys].map((key) => ({ key, cb }));
4245
4831
  track({ belong: this[_storeName], trackList: tasks });
4246
4832
  return () => clear({ belong: this[_storeName], clearList: tasks });
@@ -4267,6 +4853,7 @@ var getNodeAdapter = (storeName) => ({
4267
4853
  Store: NodeStore,
4268
4854
  useStore: (() => {
4269
4855
  const store = getStore(storeName);
4856
+ if (!(store instanceof NodeStore)) return {};
4270
4857
  return store[_storeBoot]();
4271
4858
  })
4272
4859
  });
@@ -4293,7 +4880,7 @@ var getPlatform = (comp) => {
4293
4880
  if (LarkUtils.isLarkView(comp)) return "lark" /* Lark */;
4294
4881
  return void 0;
4295
4882
  };
4296
- var extendApis = { lazySet, shallowSet };
4883
+ var extendApis = { lazySet, shallowSet, computed };
4297
4884
  var StoreCache = /* @__PURE__ */ new Map();
4298
4885
  function defineStore(name, creator, config2) {
4299
4886
  if (StoreCache.has(name)) {
@@ -4304,18 +4891,20 @@ function defineStore(name, creator, config2) {
4304
4891
  const StoreClass = adapter.Store;
4305
4892
  const useStore = adapter.useStore;
4306
4893
  const store = new StoreClass(name, config2);
4307
- store[_storeCreate](
4308
- creator(
4309
- store[_innerStore](),
4310
- extendApis
4311
- )
4312
- );
4894
+ const innerProxy = store[_innerStore]();
4895
+ const body = creator(innerProxy, extendApis);
4896
+ store[_storeCreate](body);
4313
4897
  Object.defineProperties(useStore, {
4314
4898
  $storeName: { value: name, configurable: true },
4315
- $del: { value: () => store[_storeDestroy](), configurable: true }
4899
+ $destroyFn: { value: () => store[_storeDestroy](), configurable: true }
4316
4900
  });
4317
4901
  if (!StoreCache.has(name)) {
4318
- StoreCache.set(name, { store, creator, config: config2, useStore });
4902
+ StoreCache.set(name, {
4903
+ store,
4904
+ creator,
4905
+ config: config2,
4906
+ useStore
4907
+ });
4319
4908
  }
4320
4909
  return useStore;
4321
4910
  }
@@ -4337,7 +4926,7 @@ function getUseStore(name) {
4337
4926
  return void 0;
4338
4927
  }
4339
4928
  function cloneStore(name, useStore, config2) {
4340
- const oldStoreName = useStore["$storeName"];
4929
+ const oldStoreName = useStore.$storeName ?? "";
4341
4930
  const cached = StoreCache.get(oldStoreName);
4342
4931
  const oldStoreCreator = cached?.creator;
4343
4932
  const oldConfig = cached?.config || {};
@@ -4358,16 +4947,12 @@ function isStoreActive(name) {
4358
4947
  }
4359
4948
  var cellCount = 0;
4360
4949
  function cell(data) {
4361
- return createState(data, {
4362
- belong: LARK_GLOBAL,
4363
- linkKeys: `${LARK_GLOBAL}_${cellCount++}`
4364
- });
4950
+ const linkKeys = `${LARK_GLOBAL}_${cellCount++}`;
4951
+ return createState(data, { belong: LARK_GLOBAL, linkKeys });
4365
4952
  }
4366
4953
  function observeCell(state, cb, immediate = true) {
4367
4954
  const linkKeys = getLinkKeys(state);
4368
- if (!linkKeys)
4369
- return () => {
4370
- };
4955
+ if (!linkKeys) return noop;
4371
4956
  const keys2 = linkKeys.split(".");
4372
4957
  const key = keys2[keys2.length - 1];
4373
4958
  const list = [{ key, cb }];
@@ -4376,49 +4961,50 @@ function observeCell(state, cb, immediate = true) {
4376
4961
  return () => clear({ belong: LARK_GLOBAL, clearList: list });
4377
4962
  }
4378
4963
  function multi(useStore) {
4379
- const storeName = useStore["$storeName"];
4380
- const flagSym = `lark_comp_${storeName}`;
4964
+ const storeName = useStore.$storeName ?? "";
4965
+ const flagSym = `lark-comp-${storeName}`;
4381
4966
  const map = /* @__PURE__ */ new Map();
4382
4967
  let rootViewPath;
4383
4968
  const getFlag = (viewContext) => {
4384
- const owner = viewContext["owner"];
4385
- const viewPath = owner?.["path"] || "";
4386
- const viewId = owner?.["id"] || "";
4969
+ const owner = viewContext.owner;
4970
+ const viewPath = owner?.path ?? "";
4971
+ const viewId = owner?.id ?? "";
4387
4972
  let flag;
4388
4973
  if (viewPath === rootViewPath) {
4389
- flag = `${flagSym}_${viewId}`;
4974
+ flag = `${flagSym}-${viewId}`;
4390
4975
  } else {
4391
- flag = owner?.["viewInitParams"]?.[flagSym];
4976
+ const initParams = owner?.viewInitParams;
4977
+ const candidate = initParams?.[flagSym];
4978
+ flag = typeof candidate === "string" ? candidate : void 0;
4392
4979
  }
4393
- if (owner && isFunction(owner["mountFrame"])) {
4394
- const rawMountFrame = owner["mountFrame"];
4395
- owner["mountFrame"] = (vfId, viewPath2, viewInitParams = {}) => rawMountFrame.call(
4980
+ if (owner && isFunction(owner.mountFrame)) {
4981
+ const rawMountFrame = owner.mountFrame;
4982
+ owner.mountFrame = (frameId, viewPath2, viewInitParams = {}) => rawMountFrame.call(
4396
4983
  owner,
4397
- vfId,
4984
+ frameId,
4398
4985
  viewPath2,
4399
4986
  Object.assign(viewInitParams, { [flagSym]: flag })
4400
4987
  );
4401
4988
  }
4402
- return flag;
4989
+ return flag ?? "";
4403
4990
  };
4404
4991
  const useFn = ((view) => {
4405
4992
  if (!view)
4406
- throw new Error("[lark-store] multi: cannot find the view instance");
4993
+ throw new Error(
4994
+ "[@lark.js/mvc error] multi: cannot find the view instance"
4995
+ );
4407
4996
  const viewCtx = view;
4408
- const flag = viewCtx[flagSym];
4997
+ const tag = viewCtx[flagSym];
4998
+ const flag = typeof tag === "string" ? tag : "";
4409
4999
  if (map.has(flag)) return map.get(flag);
4410
- const newFn = cloneStore(
4411
- flag,
4412
- useStore
4413
- );
5000
+ const newFn = cloneStore(flag, useStore);
4414
5001
  map.set(flag, newFn);
4415
5002
  return useFn(view);
4416
5003
  });
4417
5004
  const mixinObj = {
4418
5005
  make() {
4419
5006
  if (!rootViewPath) {
4420
- const owner = this["owner"];
4421
- rootViewPath = owner?.["path"] || "";
5007
+ rootViewPath = this.owner?.path ?? "";
4422
5008
  }
4423
5009
  this[flagSym] = getFlag(this);
4424
5010
  }
@@ -4548,7 +5134,7 @@ function convertArtSyntax(source, debug) {
4548
5134
  }
4549
5135
  if (blockStack.length > 0) {
4550
5136
  const unclosed = blockStack.map((b) => `"${b.ctrl}" at line ${b.line}`).join(", ");
4551
- throw new Error(`[@lark/mvc error] unclosed block(s): ${unclosed}`);
5137
+ throw new Error(`[@lark.js/mvc error] unclosed block(s): ${unclosed}`);
4552
5138
  }
4553
5139
  return result.join("");
4554
5140
  }
@@ -4625,7 +5211,7 @@ function convertArtExpression(code, debug, lineNo, blockStack = []) {
4625
5211
  return `${debugPrefix}<%for(${forExpr}){%>`;
4626
5212
  }
4627
5213
  const tokens = code.split(/\s+/);
4628
- const keyword = tokens.shift();
5214
+ const keyword = tokens.shift() ?? "";
4629
5215
  switch (keyword) {
4630
5216
  case "if": {
4631
5217
  blockStack.push({ ctrl: "if", line: lineNo });
@@ -4642,12 +5228,12 @@ function convertArtExpression(code, debug, lineNo, blockStack = []) {
4642
5228
  }
4643
5229
  return `${debugPrefix}<%}else{%>`;
4644
5230
  }
4645
- case "each": {
4646
- blockStack.push({ ctrl: "each", line: lineNo });
5231
+ case "forOf": {
5232
+ blockStack.push({ ctrl: "forOf", line: lineNo });
4647
5233
  const object = tokens[0];
4648
5234
  if (tokens.length > 1 && tokens[1] !== "as") {
4649
5235
  throw new Error(
4650
- `[@lark/mvc error] bad each syntax: {{${code}}}. Expected "as" keyword, got "${tokens[1]}". Usage: {{each list as item [index]}}`
5236
+ `[@lark.js/mvc error] bad forOf syntax: {{${code}}}. Expected "as" keyword, got "${tokens[1]}". Usage: {{forOf list as item [index]}}`
4651
5237
  );
4652
5238
  }
4653
5239
  const restTokens = tokens.slice(2);
@@ -4669,12 +5255,12 @@ function convertArtExpression(code, debug, lineNo, blockStack = []) {
4669
5255
  }
4670
5256
  return `${debugPrefix}<%for(let ${index}=0${refExpr},${refObjCount}=${refObj}.length${lastCount};${index}<${refObjCount};${index}++){${firstAndLast}${valueDecl}%>`;
4671
5257
  }
4672
- case "parse": {
4673
- blockStack.push({ ctrl: "parse", line: lineNo });
5258
+ case "forIn": {
5259
+ blockStack.push({ ctrl: "forIn", line: lineNo });
4674
5260
  const object = tokens[0];
4675
5261
  if (tokens.length > 1 && tokens[1] !== "as") {
4676
5262
  throw new Error(
4677
- `[@lark/mvc error] bad parse syntax: {{${code}}}. Expected "as" keyword, got "${tokens[1]}". Usage: {{for-in obj as val [key]}}`
5263
+ `[@lark.js/mvc error] bad forIn syntax: {{${code}}}. Expected "as" keyword, got "${tokens[1]}". Usage: {{for-in obj as val [key]}}`
4678
5264
  );
4679
5265
  }
4680
5266
  const restTokens2 = tokens.slice(2);
@@ -4694,19 +5280,19 @@ function convertArtExpression(code, debug, lineNo, blockStack = []) {
4694
5280
  case "set":
4695
5281
  return `${debugPrefix}<%let ${tokens.join(" ")};%>`;
4696
5282
  case "/if":
4697
- case "/each":
4698
- case "/parse":
5283
+ case "/forOf":
5284
+ case "/forIn":
4699
5285
  case "/for": {
4700
5286
  const expectedCtrl = keyword.substring(1);
4701
5287
  const last = blockStack.pop();
4702
5288
  if (!last) {
4703
5289
  throw new Error(
4704
- `[@lark/mvc error] unexpected {{${code}}}: no matching open block`
5290
+ `[@lark.js/mvc error] unexpected {{${code}}}: no matching open block`
4705
5291
  );
4706
5292
  }
4707
5293
  if (last.ctrl !== expectedCtrl) {
4708
5294
  throw new Error(
4709
- `[@lark/mvc error] unexpected {{${code}}}: expected {{/${last.ctrl}}} to close block opened at line ${last.line}`
5295
+ `[@lark.js/mvc error] unexpected {{${code}}}: expected {{/${last.ctrl}}} to close block opened at line ${last.line}`
4710
5296
  );
4711
5297
  }
4712
5298
  return `${debugPrefix}<%}%>`;
@@ -4770,7 +5356,7 @@ function parseAsExpr(expr) {
4770
5356
  function compileToFunction(source, debug, file) {
4771
5357
  const matcher = /<%([@=!:])?([\s\S]*?)%>|$/g;
4772
5358
  let index = 0;
4773
- let funcSource = `$p+='`;
5359
+ let funcSource = `$out+='`;
4774
5360
  let hasAtRule = false;
4775
5361
  const escapeSlashRegExp = /\\|'/g;
4776
5362
  const escapeBreakReturnRegExp = /\r|\n/g;
@@ -4796,17 +5382,17 @@ function compileToFunction(source, debug, file) {
4796
5382
  }
4797
5383
  if (operate === "@") {
4798
5384
  hasAtRule = true;
4799
- funcSource += `'+($expr='<%${operate + expr}%>',$i($$ref,${content}))+'`;
5385
+ funcSource += `'+($dbgExpr='<%${operate + expr}%>',$refFn($refAlt,${content}))+'`;
4800
5386
  } else if (operate === "=" || operate === ":") {
4801
- funcSource += `'+($expr='<%${operate + expr}%>',$e(${content}))+'`;
5387
+ funcSource += `'+($dbgExpr='<%${operate + expr}%>',$encHtml(${content}))+'`;
4802
5388
  } else if (operate === "!") {
4803
- if (!content.startsWith("$eu(") || !content.endsWith(")")) {
4804
- content = `$n(${content})`;
5389
+ if (!content.startsWith("$encUri(") || !content.endsWith(")")) {
5390
+ content = `$strSafe(${content})`;
4805
5391
  }
4806
- funcSource += `'+($expr='<%${operate + expr}%>',${content})+'`;
5392
+ funcSource += `'+($dbgExpr='<%${operate + expr}%>',${content})+'`;
4807
5393
  } else if (content) {
4808
5394
  if (line > -1) {
4809
- funcSource += `';$line=${line};$art='${art}';`;
5395
+ funcSource += `';$dbgLine=${line};$dbgArt='${art}';`;
4810
5396
  content = "";
4811
5397
  } else {
4812
5398
  funcSource += `';`;
@@ -4815,19 +5401,19 @@ function compileToFunction(source, debug, file) {
4815
5401
  funcSource = funcSource.substring(0, funcSource.length - 4) + ";";
4816
5402
  }
4817
5403
  if (expr) {
4818
- funcSource += `$expr='<%${expr}%>';`;
5404
+ funcSource += `$dbgExpr='<%${expr}%>';`;
4819
5405
  }
4820
- funcSource += content + `;$p+='`;
5406
+ funcSource += content + `;$out+='`;
4821
5407
  }
4822
5408
  } else {
4823
5409
  if (operate === "@") {
4824
5410
  hasAtRule = true;
4825
- funcSource += `'+$i($$ref,${content})+'`;
5411
+ funcSource += `'+$refFn($refAlt,${content})+'`;
4826
5412
  } else if (operate === "=" || operate === ":") {
4827
- funcSource += `'+$e(${content})+'`;
5413
+ funcSource += `'+$encHtml(${content})+'`;
4828
5414
  } else if (operate === "!") {
4829
- if (!content.startsWith("$eu(") || !content.endsWith(")")) {
4830
- content = `$n(${content})`;
5415
+ if (!content.startsWith("$encUri(") || !content.endsWith(")")) {
5416
+ content = `$strSafe(${content})`;
4831
5417
  }
4832
5418
  funcSource += `'+${content}+'`;
4833
5419
  } else if (content) {
@@ -4835,28 +5421,24 @@ function compileToFunction(source, debug, file) {
4835
5421
  if (funcSource.endsWith(`+'';`)) {
4836
5422
  funcSource = funcSource.substring(0, funcSource.length - 4) + ";";
4837
5423
  }
4838
- funcSource += `${content};$p+='`;
5424
+ funcSource += `${content};$out+='`;
4839
5425
  }
4840
5426
  }
4841
5427
  return match;
4842
5428
  });
4843
5429
  funcSource += `';`;
4844
- funcSource = funcSource.replace(/\$p\+='';/g, "");
4845
- funcSource = funcSource.replace(/\$p\+=''\+/g, "$p+=");
5430
+ funcSource = funcSource.replace(/\$out\+='';/g, "");
5431
+ funcSource = funcSource.replace(/\$out\+=''\+/g, "$out+=");
4846
5432
  if (debug) {
4847
5433
  const filePart = file ? `\\r\\n\\tat file:${file}` : "";
4848
- funcSource = `let $expr,$art,$line;try{${funcSource}}catch(ex){let msg='render view error:'+(ex.message||ex);if($art)msg+='\\r\\n\\tsrc art:{{'+$art+'}}\\r\\n\\tat line:'+$line;msg+='\\r\\n\\t'+($art?'translate to:':'expr:');msg+=$expr+'${filePart}';throw msg;}`;
5434
+ funcSource = `let $dbgExpr,$dbgArt,$dbgLine;try{${funcSource}}catch(ex){let msg='render view error:'+(ex.message||ex);if($dbgArt)msg+='\\r\\n\\tsrc art:{{'+$dbgArt+'}}\\r\\n\\tat line:'+$dbgLine;msg+='\\r\\n\\t'+($dbgArt?'translate to:':'expr:');msg+=$dbgExpr+'${filePart}';throw msg;}`;
4849
5435
  }
4850
5436
  const viewIdRegExp = new RegExp(String.fromCharCode(31), "g");
4851
5437
  funcSource = funcSource.replace(viewIdRegExp, `'+$viewId+'`);
4852
- const atRule = hasAtRule ? `if(!$i){$i=(ref,v,k,f)=>{for(f=ref[$g];--f;)if(ref[k=$g+f]===v)return k;ref[k=$g+ref[$g]++]=v;return k;}}` : "";
4853
- const encode = `if(!$n){let $em={'&':'amp','<':'lt','>':'gt','"':'#34','\\'':'#39','\`':'#96'},$er=/[&<>"'\`]/g,$ef=m=>'&'+$em[m]+';';$n=v=>''+(v==null?'':v);$e=v=>$n(v).replace($er,$ef)}`;
4854
- const encodeURIMore = `if(!$eu){let $um={'!':'%21','\\'':'%27','(':'%28',')':'%29','*':'%2A'},$uf=m=>$um[m],$uq=/[!')(*]/g;$eu=v=>encodeURIComponent($n(v)).replace($uq,$uf)}`;
4855
- const encodeQuote = `if(!$eq){let $qr=/['"\\\\]/g;$eq=v=>$n(v).replace($qr,'\\\\$&')}`;
4856
- const refFallback = "if(!$$ref)$$ref=$$;";
4857
- const fns = `${refFallback}${encode}${encodeURIMore}${encodeQuote}${atRule};`;
4858
- const fullSource = `${fns}let $g='\\x1e',$_temp,$p=''{{VARS}};${funcSource}return $p`;
4859
- return `($$,$viewId,$$ref,$e,$n,$eu,$i,$eq)=>{${fullSource}}`;
5438
+ void hasAtRule;
5439
+ const refFallback = "if(!$refAlt)$refAlt=$data;";
5440
+ const fullSource = `${refFallback}let $splitter='\\x1e',$tmp,$out=''{{VARS}};${funcSource}return $out`;
5441
+ return `($data,$viewId,$refAlt,$encHtml,$strSafe,$encUri,$refFn,$encQuote)=>{${fullSource}}`;
4860
5442
  }
4861
5443
  function compileTemplate(source, options = {}) {
4862
5444
  const { debug = false, globalVars = [], file } = options;
@@ -4865,17 +5447,14 @@ function compileTemplate(source, options = {}) {
4865
5447
  const viewEventProcessed = processViewEvents(converted);
4866
5448
  const finalSource = restoreComments(viewEventProcessed, comments);
4867
5449
  const funcBody = compileToFunction(finalSource, debug, file);
4868
- const varDeclarations = globalVars.map((key) => `,${key}=$$.${key}`).join("");
5450
+ const varDeclarations = globalVars.map((key) => `,${key}=$data.${key}`).join("");
4869
5451
  const funcWithVars = funcBody.replace("{{VARS}}", () => varDeclarations);
4870
- return `export default function(data, selfId, refData) {
4871
- let $$ = data || {},
4872
- $viewId = selfId || '';
4873
- return (${funcWithVars})($$, $viewId, refData,
4874
- /* $e */ v => String(v == null ? '' : v).replace(/[&<>"'\`]/g, m => '&' + ({'&':'amp','<':'lt','>':'gt','"':'#34',"'":'#39','\`':'#96'})[m] + ';'),
4875
- /* $n */ v => String(v == null ? '' : v),
4876
- /* $eu */ null,
4877
- /* $i */ null,
4878
- /* $eq */ null
5452
+ return `import { encHtml as __larkEncHtml, strSafe as __larkStrSafe, encUri as __larkEncUri, encQuote as __larkEncQuote, refFn as __larkRefFn } from "@lark.js/mvc/runtime";
5453
+ export default function(data, viewId, refData) {
5454
+ let $data = data || {},
5455
+ $viewId = viewId || '';
5456
+ return (${funcWithVars})($data, $viewId, refData,
5457
+ __larkEncHtml, __larkStrSafe, __larkEncUri, __larkRefFn, __larkEncQuote
4879
5458
  );
4880
5459
  }`;
4881
5460
  }
@@ -4984,7 +5563,7 @@ function fallbackExtractVariables(source) {
4984
5563
  while ((m = outputRegExp.exec(source)) !== null) {
4985
5564
  vars.add(m[1]);
4986
5565
  }
4987
- const eachRegExp = /\{\{each\s+([a-zA-Z_$][\w$]*)\s+as/g;
5566
+ const eachRegExp = /\{\{forOf\s+([a-zA-Z_$][\w$]*)\s+as/g;
4988
5567
  while ((m = eachRegExp.exec(source)) !== null) {
4989
5568
  vars.add(m[1]);
4990
5569
  }
@@ -5000,82 +5579,100 @@ function walkAst(ast, visitors) {
5000
5579
  if (visitors[type]) {
5001
5580
  visitors[type](node);
5002
5581
  }
5582
+ const bag = node;
5003
5583
  for (const key of Object.keys(node)) {
5004
5584
  if (key === "type" || key === "start" || key === "end" || key === "loc" || key === "range")
5005
5585
  continue;
5006
- if (type === "MemberExpression" && key === "property" && !node.computed)
5007
- continue;
5008
- if (type === "ObjectProperty" && key === "key" && !node.computed) {
5009
- continue;
5586
+ if (type === "MemberExpression" && key === "property") {
5587
+ const me = node;
5588
+ if (!me.computed) continue;
5010
5589
  }
5011
- if (type === "ObjectMethod" && key === "key" && !node.computed) {
5012
- continue;
5590
+ if (type === "ObjectProperty" && key === "key") {
5591
+ const op = node;
5592
+ if (!op.computed) continue;
5013
5593
  }
5014
- const child = node[key];
5594
+ if (type === "ObjectMethod" && key === "key") {
5595
+ const om = node;
5596
+ if (!om.computed) continue;
5597
+ }
5598
+ const child = bag[key];
5015
5599
  if (Array.isArray(child)) {
5016
5600
  for (const item of child) {
5017
- if (item && typeof item === "object" && typeof item.type === "string") {
5018
- visit(item);
5019
- }
5601
+ if (isAstNode(item)) visit(item);
5020
5602
  }
5021
- } else if (child && typeof child === "object" && typeof child.type === "string") {
5603
+ } else if (isAstNode(child)) {
5022
5604
  visit(child);
5023
5605
  }
5024
5606
  }
5025
5607
  }
5026
5608
  visit(ast);
5027
5609
  }
5610
+ function isAstNode(v) {
5611
+ return !!v && typeof v === "object" && typeof v.type === "string";
5612
+ }
5028
5613
  var BUILTIN_GLOBALS = {
5029
5614
  // ─── Template runtime helpers (injected by compileToFunction) ───────
5030
5615
  //
5031
5616
  // These variables appear in the generated template function signature
5032
5617
  // or body. They must be excluded from extractGlobalVars() so that
5033
- // they are not mistaken for user data variables and destructured from $$.
5618
+ // they are not mistaken for user data variables and destructured from $data.
5034
5619
  // SPLITTER character constant (same as \x1e), used as namespace separator
5035
5620
  // for refData keys, event attribute encoding, and internal data structures.
5036
- // Declared as: let $g='\x1e'
5037
- $g: 1,
5038
- // refData — the data object passed from Updater to the template function.
5039
- // User variables are destructured from $$ at the top of the function:
5040
- // let {name, age} = $$;
5621
+ // Declared as: let $splitter='\x1e'
5622
+ $splitter: 1,
5623
+ // Data — the data object passed from Updater to the template function.
5624
+ // User variables are destructured from $data at the top of the function:
5625
+ // let {name, age} = $data;
5041
5626
  // This is the first parameter of the generated arrow function.
5042
- $$: 1,
5627
+ $data: 1,
5043
5628
  // Null-safe toString: v => '' + (v == null ? '' : v)
5044
5629
  // Converts null/undefined to empty string, otherwise calls toString().
5045
5630
  // Wraps every {{!raw}} output to prevent "null" / "undefined" rendering.
5046
- $n: 1,
5047
- // HTML entity encoder: v => $n(v).replace(/[&<>"'`]/g, entityMap)
5631
+ $strSafe: 1,
5632
+ // HTML entity encoder: v => $strSafe(v).replace(/[&<>"'`]/g, entityMap)
5048
5633
  // Encodes &, <, >, ", ', ` to HTML entities (&amp; &lt; etc.)
5049
5634
  // Applied to all {{=escaped}} and {{:binding}} outputs.
5050
- $e: 1,
5051
- // HTML entity map — internal object used by $e:
5635
+ $encHtml: 1,
5636
+ // HTML entity map — internal object used by $encHtml:
5052
5637
  // {'&':'amp','<':'gt','>':'gt','"':'#34','\'':'#39','`':'#96'}
5053
- // Not a standalone function; referenced inside $e's closure.
5054
- $em: 1,
5055
- // HTML entity RegExp — internal regexp used by $e:
5638
+ // Not a standalone function; referenced inside $encHtml's closure.
5639
+ $entMap: 1,
5640
+ // HTML entity RegExp — internal regexp used by $encHtml:
5056
5641
  // /[&<>"'`]/g
5057
- $er: 1,
5058
- // HTML entity replacer function — internal helper used by $e:
5059
- // m => '&' + $em[m] + ';'
5060
- // Maps each matched character to its entity string.
5061
- $ef: 1,
5642
+ $entReg: 1,
5643
+ // HTML entity replacer function — internal helper used by $encHtml:
5644
+ // m => '&' + $entMap[m] + ';'
5645
+ // Maps matched character to its entity string.
5646
+ $entFn: 1,
5062
5647
  // Output buffer — the string accumulator for rendered HTML.
5063
- // All template output is appended via $p += '...'.
5064
- // Declared as: let $p = ''
5065
- $p: 1,
5648
+ // All template output is appended via $out += '...'.
5649
+ // Declared as: let $out = ''
5650
+ $out: 1,
5066
5651
  // Reference lookup: (refData, value) => key
5067
5652
  // Finds or allocates a SPLITTER-prefixed key in refData for a given
5068
5653
  // object reference. Used by {{@ref}} operator for passing object
5069
5654
  // references to child views via v-lark attributes.
5070
- $i: 1,
5071
- // URI encoder: v => encodeURIComponent($n(v)).replace(/[!')(*]/g, extraMap)
5655
+ $refFn: 1,
5656
+ // URI encoder: v => encodeURIComponent($strSafe(v)).replace(/[!')(*]/g, extraMap)
5072
5657
  // Extends encodeURIComponent with encoding of ! ' ( ) *.
5073
5658
  // Applied to values in @event URL parameters and {{!uri}} contexts.
5074
- $eu: 1,
5075
- // Quote encoder: v => $n(v).replace(/['"\\]/g, '\\$&')
5659
+ $encUri: 1,
5660
+ // URI encode map internal object used by $encUri:
5661
+ // {'!':'%21','\'':'%27','(':'%28',')':'%29','*':'%2A'}
5662
+ $uriMap: 1,
5663
+ // URI encode replacer — internal helper used by $encUri:
5664
+ // m => $uriMap[m]
5665
+ $uriFn: 1,
5666
+ // URI encode regexp — internal regexp used by $encUri:
5667
+ // /[!')(*]/g
5668
+ $uriReg: 1,
5669
+ // Quote encoder: v => $strSafe(v).replace(/['"\\]/g, '\\$&')
5076
5670
  // Escapes quotes and backslashes for safe embedding in HTML attribute
5077
5671
  // values (e.g. data-json='...').
5078
- $eq: 1,
5672
+ $encQuote: 1,
5673
+ // Quote encode regexp — internal regexp used by $encQuote:
5674
+ // /['"\\]/g
5675
+ $qReg: 1,
5079
5676
  // View ID — the unique identifier of the owning View instance.
5080
5677
  // Injected into @event attribute values at render time so that
5081
5678
  // EventDelegator can dispatch events to the correct View handler.
@@ -5083,23 +5680,23 @@ var BUILTIN_GLOBALS = {
5083
5680
  $viewId: 1,
5084
5681
  // Debug: current expression text — stores the template expression being
5085
5682
  // evaluated, for error reporting. Only present in debug mode.
5086
- // e.g. $expr='<%=user.name%>'
5087
- $expr: 1,
5683
+ // e.g. $dbgExpr='<%=user.name%>'
5684
+ $dbgExpr: 1,
5088
5685
  // Debug: original art syntax — stores the {{}} template syntax before
5089
5686
  // conversion, for error reporting. Only present in debug mode.
5090
- // e.g. $art='{{=user.name}}'
5091
- $art: 1,
5687
+ // e.g. $dbgArt='{{=user.name}}'
5688
+ $dbgArt: 1,
5092
5689
  // Debug: source line number — tracks the current line in the template
5093
5690
  // source, for error reporting. Only present in debug mode.
5094
- $line: 1,
5095
- // refData alias — fallback reference lookup table.
5096
- // Defaults to $$ when no explicit $$ref is provided.
5097
- // Ensures $i() does not crash when @ operator is used without refData.
5098
- $$ref: 1,
5691
+ $dbgLine: 1,
5692
+ // RefData alias — fallback reference lookup table.
5693
+ // Defaults to $data when no explicit $refAlt is provided.
5694
+ // Ensures $refFn() does not crash when @ operator is used without refData.
5695
+ $refAlt: 1,
5099
5696
  // Temporary variable — used by the compiler for intermediate
5100
5697
  // expression results in generated code (e.g. loop variables,
5101
- // conditional branches). Declared as: let $_temp
5102
- $_temp: 1,
5698
+ // conditional branches). Declared as: let $tmp
5699
+ $tmp: 1,
5103
5700
  // JS literals
5104
5701
  undefined: 1,
5105
5702
  null: 1,
@@ -5165,15 +5762,17 @@ var BUILTIN_GLOBALS = {
5165
5762
  var BUILTIN_GLOBAL_SET = new Set(Object.keys(BUILTIN_GLOBALS));
5166
5763
  // Annotate the CommonJS export names for ESM import in node:
5167
5764
  0 && (module.exports = {
5168
- Bag,
5169
5765
  CALL_BREAK_TIME,
5170
5766
  Cache,
5767
+ CrossSite,
5171
5768
  EVENT_METHOD_REGEXP,
5172
5769
  EventDelegator,
5173
5770
  EventEmitter,
5174
5771
  Frame,
5772
+ FrameVisualBridge,
5175
5773
  Framework,
5176
5774
  LARK_VIEW,
5775
+ Payload,
5177
5776
  Platform,
5178
5777
  ROUTER_EVENTS,
5179
5778
  Router,
@@ -5189,13 +5788,14 @@ var BUILTIN_GLOBAL_SET = new Set(Object.keys(BUILTIN_GLOBALS));
5189
5788
  applyVdomOps,
5190
5789
  assign,
5191
5790
  cell,
5192
- classExtend,
5193
5791
  cloneData,
5194
5792
  cloneStore,
5195
5793
  compileTemplate,
5794
+ computed,
5196
5795
  createState,
5197
5796
  createVdomRef,
5198
5797
  defineStore,
5798
+ defineView,
5199
5799
  delStore,
5200
5800
  encodeHTML,
5201
5801
  encodeQ,
@@ -5203,6 +5803,7 @@ var BUILTIN_GLOBAL_SET = new Set(Object.keys(BUILTIN_GLOBALS));
5203
5803
  encodeURIExtra,
5204
5804
  ensureElementId,
5205
5805
  extractGlobalVars,
5806
+ frameworkConfig,
5206
5807
  funcWithTry,
5207
5808
  generateId,
5208
5809
  getAttribute,
@@ -5210,9 +5811,9 @@ var BUILTIN_GLOBAL_SET = new Set(Object.keys(BUILTIN_GLOBALS));
5210
5811
  getPlatform,
5211
5812
  getStore,
5212
5813
  getUseStore,
5213
- has,
5814
+ hasOwnProperty,
5214
5815
  installFrameVisualizerBridge,
5215
- isArray,
5816
+ invalidateViewClass,
5216
5817
  isPlainObject,
5217
5818
  isPrimitive,
5218
5819
  isPrimitiveOrFunc,
@@ -5231,6 +5832,7 @@ var BUILTIN_GLOBAL_SET = new Set(Object.keys(BUILTIN_GLOBALS));
5231
5832
  observeCell,
5232
5833
  parseUri,
5233
5834
  registerViewClass,
5835
+ resetProjectsMap,
5234
5836
  safeguard,
5235
5837
  serializeFrameTree,
5236
5838
  setData,
@@ -5242,6 +5844,7 @@ var BUILTIN_GLOBAL_SET = new Set(Object.keys(BUILTIN_GLOBALS));
5242
5844
  toUri,
5243
5845
  translateData,
5244
5846
  unmark,
5847
+ use,
5245
5848
  vdomGetCompareKey,
5246
5849
  vdomGetNode,
5247
5850
  vdomSetAttributes,