@lark.js/mvc 0.0.14 → 0.0.16

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/devtool.js CHANGED
@@ -21,21 +21,26 @@ import {
21
21
  nextCounter,
22
22
  refFn,
23
23
  strSafe
24
- } from "./chunk-66OZBBSP.js";
24
+ } from "./chunk-OGPBFCIK.js";
25
25
 
26
26
  // src/utils.ts
27
27
  var CALL_BREAK_TIME = 9;
28
28
  var callQueue = [];
29
29
  var callScheduled = false;
30
- var schedulerYield = (() => {
30
+ function getSchedulerYield() {
31
31
  try {
32
- if (typeof globalThis?.scheduler?.yield === "function") {
33
- return globalThis.scheduler.yield.bind(globalThis.scheduler);
32
+ const scheduler = Reflect.get(globalThis, "scheduler");
33
+ if (scheduler && typeof scheduler === "object" && typeof Reflect.get(scheduler, "yield") === "function") {
34
+ const yieldFn = Reflect.get(scheduler, "yield");
35
+ if (typeof yieldFn === "function") {
36
+ return yieldFn.bind(scheduler);
37
+ }
34
38
  }
35
39
  } catch {
36
40
  }
37
41
  return void 0;
38
- })();
42
+ }
43
+ var schedulerYield = getSchedulerYield();
39
44
  async function startCall() {
40
45
  callScheduled = false;
41
46
  const startTime = performance.now();
@@ -61,7 +66,9 @@ function scheduleNextChunk() {
61
66
  callScheduled = true;
62
67
  }
63
68
  function callFunction(fn, args) {
64
- callQueue.push(() => fn(...args));
69
+ callQueue.push(() => {
70
+ fn(...args);
71
+ });
65
72
  if (!callScheduled) {
66
73
  scheduleNextChunk();
67
74
  }
@@ -78,7 +85,6 @@ function asRecord(value) {
78
85
  if (isRecord(value)) {
79
86
  return value;
80
87
  }
81
- console.error("fallback to Object.fromEntries, even an empty object {}.");
82
88
  if (Array.isArray(value)) {
83
89
  return Object.fromEntries(value.entries());
84
90
  }
@@ -104,7 +110,7 @@ function assign(target, ...sources) {
104
110
  if (source) {
105
111
  for (const p in source) {
106
112
  if (hasOwnProperty(source, p)) {
107
- target[p] = source[p];
113
+ Reflect.set(target, p, source[p]);
108
114
  }
109
115
  }
110
116
  }
@@ -143,16 +149,16 @@ function translateData(data, value) {
143
149
  if (isPrimitive(value)) {
144
150
  const prop = String(value);
145
151
  if (isRefToken(prop) && hasOwnProperty(data, prop)) {
146
- return data[prop];
152
+ return Reflect.get(data, prop);
147
153
  }
148
154
  return value;
149
155
  }
150
156
  if (isPlainObject(value) || Array.isArray(value)) {
151
157
  for (const p in value) {
152
158
  if (hasOwnProperty(value, p)) {
153
- const val = value[p];
159
+ const val = Reflect.get(value, p);
154
160
  const newVal = translateData(data, val);
155
- value[p] = newVal;
161
+ Reflect.set(value, p, newVal);
156
162
  }
157
163
  }
158
164
  return value;
@@ -209,41 +215,30 @@ function toUri(path, params, keepEmpty) {
209
215
  }
210
216
 
211
217
  // src/event-emitter.ts
212
- var EventEmitter = class {
213
- /** Event listeners: prefixed key -> listener array */
214
- listeners = /* @__PURE__ */ new Map();
215
- /** Number of `fire()` calls currently on the stack (re-entrancy depth). */
216
- firingDepth = 0;
217
- /** Keys whose listener list needs compaction after firing settles. */
218
- pendingCompaction;
219
- /**
220
- * Bind event listener.
221
- */
222
- on(event, handler) {
218
+ function createEmitter() {
219
+ const listeners = /* @__PURE__ */ new Map();
220
+ let firingDepth = 0;
221
+ let pendingCompaction;
222
+ function on(event, handler) {
223
223
  const key = SPLITTER + event;
224
- let list = this.listeners.get(key);
224
+ let list = listeners.get(key);
225
225
  if (!list) {
226
226
  list = [];
227
- this.listeners.set(key, list);
227
+ listeners.set(key, list);
228
228
  }
229
229
  list.push({ handler, executing: 0 });
230
- return this;
230
+ return api;
231
231
  }
232
- /**
233
- * Unbind event listener.
234
- * If handler is provided, removes only that handler.
235
- * If no handler, removes all handlers for the event.
236
- */
237
- off(event, handler) {
232
+ function off(event, handler) {
238
233
  const key = SPLITTER + event;
239
234
  if (handler) {
240
- const list = this.listeners.get(key);
241
- if (!list) return this;
242
- if (this.firingDepth > 0) {
235
+ const list = listeners.get(key);
236
+ if (!list) return api;
237
+ if (firingDepth > 0) {
243
238
  for (const listener of list) {
244
239
  if (listener.handler === handler) {
245
240
  listener.handler = noop;
246
- (this.pendingCompaction ??= /* @__PURE__ */ new Set()).add(key);
241
+ (pendingCompaction ??= /* @__PURE__ */ new Set()).add(key);
247
242
  break;
248
243
  }
249
244
  }
@@ -254,35 +249,20 @@ var EventEmitter = class {
254
249
  break;
255
250
  }
256
251
  }
257
- if (list.length === 0) this.listeners.delete(key);
252
+ if (list.length === 0) listeners.delete(key);
258
253
  }
259
254
  } else {
260
- this.listeners.delete(key);
261
- Reflect.deleteProperty(
262
- this,
263
- `on${event[0].toUpperCase() + event.slice(1)}`
264
- );
255
+ listeners.delete(key);
256
+ Reflect.deleteProperty(internal, onMethodName(event));
265
257
  }
266
- return this;
258
+ return api;
267
259
  }
268
- /**
269
- * Fire event, execute all bound handlers. Safe for re-entrant `off()` calls
270
- * during dispatch: removed handlers are replaced with noop and compacted
271
- * after the outermost fire returns.
272
- *
273
- * @param event - Event name
274
- * @param data - Event data (type property added automatically)
275
- * @param remove - Whether to remove all handlers after firing
276
- * @param lastToFirst - Whether to execute handlers in reverse order
277
- */
278
- fire(event, data, remove, lastToFirst) {
260
+ function fire(event, data, remove, lastToFirst) {
279
261
  const key = SPLITTER + event;
280
- const list = this.listeners.get(key);
281
- if (!data) {
282
- data = {};
283
- }
284
- data["type"] = event;
285
- this.firingDepth++;
262
+ const list = listeners.get(key);
263
+ const eventData = data ?? {};
264
+ eventData["type"] = event;
265
+ firingDepth++;
286
266
  try {
287
267
  if (list) {
288
268
  const len = list.length;
@@ -292,35 +272,45 @@ var EventEmitter = class {
292
272
  if (!listener) continue;
293
273
  if (listener.handler === noop) continue;
294
274
  listener.executing = 1;
295
- funcWithTry([listener.handler], [data], this, noop);
275
+ funcWithTry([listener.handler], [eventData], null, noop);
296
276
  listener.executing = "";
297
277
  }
298
278
  }
299
- const onMethodName = `on${event[0].toUpperCase() + event.slice(1)}`;
300
- const onMethod = this[onMethodName];
279
+ const onMethod = internal[onMethodName(event)];
301
280
  if (typeof onMethod === "function") {
302
- funcWithTry([onMethod], [data], this, noop);
281
+ funcWithTry([onMethod], [eventData], null, noop);
303
282
  }
304
283
  if (remove) {
305
- this.off(event);
284
+ off(event);
306
285
  }
307
286
  } finally {
308
- this.firingDepth--;
309
- if (this.firingDepth === 0 && this.pendingCompaction) {
310
- for (const k of this.pendingCompaction) {
311
- const l = this.listeners.get(k);
287
+ firingDepth--;
288
+ if (firingDepth === 0 && pendingCompaction) {
289
+ for (const k of pendingCompaction) {
290
+ const l = listeners.get(k);
312
291
  if (!l) continue;
313
292
  for (let i = l.length - 1; i >= 0; i--) {
314
293
  if (l[i].handler === noop) l.splice(i, 1);
315
294
  }
316
- if (l.length === 0) this.listeners.delete(k);
295
+ if (l.length === 0) listeners.delete(k);
317
296
  }
318
- this.pendingCompaction = void 0;
297
+ pendingCompaction = void 0;
319
298
  }
320
299
  }
321
- return this;
300
+ return api;
322
301
  }
323
- };
302
+ function onMethodName(event) {
303
+ return "on" + event[0].toUpperCase() + event.slice(1);
304
+ }
305
+ const internal = {
306
+ on,
307
+ off,
308
+ fire,
309
+ listeners
310
+ };
311
+ const api = internal;
312
+ return api;
313
+ }
324
314
 
325
315
  // src/mark.ts
326
316
  var hostStore = /* @__PURE__ */ new WeakMap();
@@ -338,68 +328,41 @@ function unmark(host) {
338
328
  function sortCacheEntries(a, b) {
339
329
  return b.frequency - a.frequency || b.lastTimestamp - a.lastTimestamp;
340
330
  }
341
- var Cache = class {
342
- /** Cache entries array */
343
- entries = [];
344
- /** Fast lookup: prefixed key -> entry */
345
- lookup = /* @__PURE__ */ new Map();
346
- /** Buffer size for eviction */
347
- bufferSize;
348
- /** Maximum cache size */
349
- maxSize;
350
- /** Total capacity (maxSize + bufferSize) */
351
- capacity;
352
- /** Callback when entry is removed */
353
- onRemove;
354
- /** Sort comparator */
355
- comparator;
356
- constructor(options = {}) {
357
- this.maxSize = options.maxSize ?? 20;
358
- this.bufferSize = options.bufferSize ?? 5;
359
- this.capacity = this.maxSize + this.bufferSize;
360
- this.onRemove = options.onRemove;
361
- this.comparator = options.sortComparator ?? sortCacheEntries;
362
- }
363
- /** Prefix a key with SPLITTER for namespace isolation */
364
- prefixKey(key) {
331
+ function createCache(options = {}) {
332
+ let entries = [];
333
+ const lookup = /* @__PURE__ */ new Map();
334
+ const maxSize = options.maxSize ?? 20;
335
+ const bufferSize = options.bufferSize ?? 5;
336
+ const capacity = maxSize + bufferSize;
337
+ const onRemove = options.onRemove;
338
+ const comparator = options.sortComparator ?? sortCacheEntries;
339
+ function prefixKey(key) {
365
340
  return SPLITTER + key;
366
341
  }
367
- /**
368
- * Get a cached value by key.
369
- * Updates frequency and timestamp for cache sorting.
370
- */
371
- get(key) {
372
- const prefixedKey = this.prefixKey(key);
373
- const entry = this.lookup.get(prefixedKey);
342
+ function get(key) {
343
+ const prefixedKey = prefixKey(key);
344
+ const entry = lookup.get(prefixedKey);
374
345
  if (!entry) return void 0;
375
346
  entry.frequency++;
376
347
  entry.lastTimestamp = nextCounter();
377
348
  return entry.value;
378
349
  }
379
- /**
380
- * Iterate all cached values.
381
- */
382
- forEach(callback) {
383
- for (const entry of this.entries) {
350
+ function forEach(callback) {
351
+ for (const entry of entries) {
384
352
  callback(entry.value);
385
353
  }
386
354
  }
387
- /**
388
- * Set or update a cached value.
389
- * If key already exists, updates value and increments frequency.
390
- * If cache exceeds capacity, triggers eviction.
391
- */
392
- set(key, value) {
393
- const prefixedKey = this.prefixKey(key);
394
- const existing = this.lookup.get(prefixedKey);
355
+ function set(key, value) {
356
+ const prefixedKey = prefixKey(key);
357
+ const existing = lookup.get(prefixedKey);
395
358
  if (existing) {
396
359
  existing.value = value;
397
360
  existing.frequency++;
398
361
  existing.lastTimestamp = nextCounter();
399
362
  return;
400
363
  }
401
- if (this.entries.length >= this.capacity) {
402
- this.evictEntries();
364
+ if (entries.length >= capacity) {
365
+ evictEntries();
403
366
  }
404
367
  const entry = {
405
368
  originalKey: key,
@@ -407,87 +370,76 @@ var Cache = class {
407
370
  frequency: 1,
408
371
  lastTimestamp: nextCounter()
409
372
  };
410
- this.entries.push(entry);
411
- this.lookup.set(prefixedKey, entry);
373
+ entries.push(entry);
374
+ lookup.set(prefixedKey, entry);
412
375
  }
413
- /**
414
- * Delete a cached entry. Removes it immediately from both the lookup map
415
- * and the entries array so the GC can reclaim the value without waiting
416
- * for the next eviction sweep.
417
- */
418
- del(key) {
419
- const prefixedKey = this.prefixKey(key);
420
- const entry = this.lookup.get(prefixedKey);
376
+ function del(key) {
377
+ const prefixedKey = prefixKey(key);
378
+ const entry = lookup.get(prefixedKey);
421
379
  if (!entry) return;
422
- this.lookup.delete(prefixedKey);
423
- const idx = this.entries.indexOf(entry);
424
- if (idx !== -1) this.entries.splice(idx, 1);
425
- if (this.onRemove) {
426
- this.onRemove(key);
380
+ lookup.delete(prefixedKey);
381
+ const idx = entries.indexOf(entry);
382
+ if (idx !== -1) entries.splice(idx, 1);
383
+ if (onRemove) {
384
+ onRemove(key);
427
385
  }
428
386
  }
429
- /**
430
- * Check if a key exists in cache.
431
- */
432
- has(key) {
433
- return this.lookup.has(this.prefixKey(key));
434
- }
435
- /** Get current cache size */
436
- get size() {
437
- return this.entries.length;
387
+ function has(key) {
388
+ return lookup.has(prefixKey(key));
438
389
  }
439
- /** Clear all entries */
440
- clear() {
441
- if (this.onRemove) {
442
- for (const entry of this.entries) {
443
- this.onRemove(entry.originalKey);
390
+ function clear() {
391
+ if (onRemove) {
392
+ for (const entry of entries) {
393
+ onRemove(entry.originalKey);
444
394
  }
445
395
  }
446
- this.entries = [];
447
- this.lookup.clear();
396
+ entries = [];
397
+ lookup.clear();
448
398
  }
449
- /**
450
- * Evict the `bufferSize` worst entries from the cache.
451
- *
452
- * Uses single-pass partial selection (O(n·k)) instead of sorting the entire
453
- * `entries` array (O(n log n)). For the typical `bufferSize = 5` this is
454
- * effectively a linear scan with at most 5 in-bucket comparisons per
455
- * iteration — and it avoids mutating the rest of `entries`.
456
- */
457
- evictEntries() {
458
- const entries = this.entries;
459
- const k = this.bufferSize;
460
- if (k <= 0 || entries.length === 0) return;
461
- if (entries.length <= k) {
399
+ function evictEntries() {
400
+ if (bufferSize <= 0 || entries.length === 0) return;
401
+ if (entries.length <= bufferSize) {
462
402
  for (const e of entries) {
463
- this.lookup.delete(this.prefixKey(e.originalKey));
464
- if (this.onRemove) this.onRemove(e.originalKey);
403
+ lookup.delete(prefixKey(e.originalKey));
404
+ if (onRemove) onRemove(e.originalKey);
465
405
  }
466
- this.entries = [];
406
+ entries = [];
467
407
  return;
468
408
  }
469
- const cmp = this.comparator;
470
409
  const worst = [];
471
410
  for (const entry of entries) {
472
- if (worst.length < k) {
411
+ if (worst.length < bufferSize) {
473
412
  let i = worst.length;
474
- while (i > 0 && cmp(entry, worst[i - 1]) > 0) i--;
413
+ while (i > 0 && comparator(entry, worst[i - 1]) > 0) i--;
475
414
  worst.splice(i, 0, entry);
476
- } else if (cmp(entry, worst[k - 1]) > 0) {
415
+ } else if (comparator(entry, worst[bufferSize - 1]) > 0) {
477
416
  worst.pop();
478
417
  let i = worst.length;
479
- while (i > 0 && cmp(entry, worst[i - 1]) > 0) i--;
418
+ while (i > 0 && comparator(entry, worst[i - 1]) > 0) i--;
480
419
  worst.splice(i, 0, entry);
481
420
  }
482
421
  }
483
422
  const evictSet = new Set(worst);
484
423
  for (const e of worst) {
485
- this.lookup.delete(this.prefixKey(e.originalKey));
486
- if (this.onRemove) this.onRemove(e.originalKey);
487
- }
488
- this.entries = entries.filter((e) => !evictSet.has(e));
489
- }
490
- };
424
+ lookup.delete(prefixKey(e.originalKey));
425
+ if (onRemove) onRemove(e.originalKey);
426
+ }
427
+ entries = entries.filter((e) => !evictSet.has(e));
428
+ }
429
+ function getSize() {
430
+ return entries.length;
431
+ }
432
+ const api = {
433
+ get,
434
+ set,
435
+ del,
436
+ has,
437
+ clear,
438
+ forEach,
439
+ getSize
440
+ };
441
+ return api;
442
+ }
491
443
 
492
444
  // src/event-delegator.ts
493
445
  var rootEvents = {};
@@ -495,7 +447,7 @@ var selectorEvents = {};
495
447
  var rangeEvents = {};
496
448
  var rangeFrames = {};
497
449
  var elementGuid = 0;
498
- var eventInfoCache = new Cache({
450
+ var eventInfoCache = createCache({
499
451
  maxSize: 30,
500
452
  bufferSize: 10
501
453
  });
@@ -548,7 +500,7 @@ function findFrameInfo(current, eventType) {
548
500
  if (frame) {
549
501
  const view = frame.view;
550
502
  if (view) {
551
- const selectorEntry = view.eventSelectorMap[eventType];
503
+ const selectorEntry = { selectors: [] };
552
504
  if (selectorEntry) {
553
505
  for (const selectorName of selectorEntry.selectors) {
554
506
  const entry = {
@@ -566,7 +518,7 @@ function findFrameInfo(current, eventType) {
566
518
  }
567
519
  }
568
520
  }
569
- if (view.template && !backtrace) {
521
+ if (view.getTemplate() && !backtrace) {
570
522
  if (match && !match.id) {
571
523
  match.id = frameId;
572
524
  }
@@ -618,8 +570,9 @@ function domEventProcessor(domEvent) {
618
570
  const frame = frameId ? frameGetter?.(frameId) : void 0;
619
571
  const view = frame?.view;
620
572
  if (view) {
621
- const eventName = handlerName + SPLITTER + eventType;
622
- const fn = Reflect.get(view, eventName);
573
+ const eventKey = handlerName + "<" + eventType + ">";
574
+ const events = typeof view.getEvents === "function" ? view.getEvents() : void 0;
575
+ const fn = events?.[eventKey];
623
576
  if (fn) {
624
577
  const extendedEvent = domEvent;
625
578
  extendedEvent.eventTarget = target;
@@ -698,68 +651,6 @@ var EventDelegator = {
698
651
  }
699
652
  };
700
653
 
701
- // src/module-loader.ts
702
- var config = {
703
- rootId: "root",
704
- routeMode: "history",
705
- hashbang: "#!",
706
- error: (error) => {
707
- throw error;
708
- }
709
- };
710
- function use(names, callback) {
711
- const nameList = typeof names === "string" ? [names] : names;
712
- const loadPromise = (() => {
713
- if (config.require) {
714
- const result = config.require(nameList);
715
- if (result && typeof result.then === "function") {
716
- return result;
717
- }
718
- return Promise.resolve([]);
719
- }
720
- return Promise.all(
721
- nameList.map((name) => {
722
- const importPath = name.startsWith(".") || name.startsWith("/") ? name : `./${name}`;
723
- return import(
724
- /* @vite-ignore */
725
- /* webpackIgnore: true */
726
- importPath
727
- ).then((mod) => {
728
- return mod && (mod["__esModule"] || // For Webpack
729
- typeof mod["default"] === "function") ? mod["default"] : mod;
730
- }).catch((err) => {
731
- const errorHandler = config.error;
732
- if (errorHandler) {
733
- errorHandler(err instanceof Error ? err : new Error(String(err)));
734
- }
735
- return void 0;
736
- });
737
- })
738
- );
739
- })();
740
- if (callback) {
741
- loadPromise.then((modules) => {
742
- callback(...modules);
743
- });
744
- }
745
- return loadPromise;
746
- }
747
-
748
- // src/frame-registry.ts
749
- var frameRegistry = /* @__PURE__ */ new Map();
750
- function getFrame(id) {
751
- return frameRegistry.get(id);
752
- }
753
- function getAllFrames() {
754
- return frameRegistry;
755
- }
756
- function registerFrame(id, frame) {
757
- frameRegistry.set(id, frame);
758
- }
759
- function removeFrame(id) {
760
- frameRegistry.delete(id);
761
- }
762
-
763
654
  // src/dom.ts
764
655
  var wrapMeta = {
765
656
  option: [1, "<select multiple>"],
@@ -1035,6 +926,11 @@ function vdomCreateNode(vnode, owner, ref) {
1035
926
  if (tag === V_TEXT_NODE) {
1036
927
  return document.createTextNode(vnode.html);
1037
928
  }
929
+ if (tag === SPLITTER) {
930
+ const template = document.createElement("template");
931
+ template.innerHTML = vnode.html;
932
+ return template.content.firstChild || document.createTextNode("");
933
+ }
1038
934
  const sTag = typeof tag === "string" ? tag : tag.toString();
1039
935
  const ns = VDOM_NS_MAP[sTag] || owner.namespaceURI;
1040
936
  const el = document.createElementNS(ns, sTag);
@@ -1118,6 +1014,14 @@ function vdomSetNode(realNode, oldParent, lastVDom, newVDom, ref, frame, keys, r
1118
1014
  return;
1119
1015
  }
1120
1016
  if (lastTag === newTag) {
1017
+ if (newTag === SPLITTER) {
1018
+ if (lastVDom.html !== newVDom.html) {
1019
+ ref.changed = 1;
1020
+ domUnmountFrames(frame, realNode);
1021
+ oldParent.replaceChild(vdomCreateNode(newVDom, oldParent, ref), realNode);
1022
+ }
1023
+ return;
1024
+ }
1121
1025
  if (lastVDom.attrs === newVDom.attrs && lastVDom.html === newVDom.html) {
1122
1026
  if (newVDom.hasSpecials) {
1123
1027
  vdomSyncFormState(realNode, newVDom);
@@ -1126,12 +1030,7 @@ function vdomSetNode(realNode, oldParent, lastVDom, newVDom, ref, frame, keys, r
1126
1030
  }
1127
1031
  let attrChanged = 0;
1128
1032
  if (lastVDom.attrs !== newVDom.attrs || newVDom.hasSpecials) {
1129
- attrChanged = vdomSetAttributes(
1130
- realNode,
1131
- newVDom,
1132
- ref,
1133
- lastVDom
1134
- );
1033
+ attrChanged = vdomSetAttributes(realNode, newVDom, ref, lastVDom);
1135
1034
  if (attrChanged) ref.changed = 1;
1136
1035
  }
1137
1036
  let updateChildren = true;
@@ -1145,16 +1044,7 @@ function vdomSetNode(realNode, oldParent, lastVDom, newVDom, ref, frame, keys, r
1145
1044
  }
1146
1045
  vdomSyncFormState(realNode, newVDom);
1147
1046
  if (updateChildren && !newVDom.selfClose) {
1148
- vdomSetChildNodes(
1149
- realNode,
1150
- lastVDom,
1151
- newVDom,
1152
- ref,
1153
- frame,
1154
- keys,
1155
- rootView,
1156
- ready
1157
- );
1047
+ vdomSetChildNodes(realNode, lastVDom, newVDom, ref, frame, keys, rootView, ready);
1158
1048
  }
1159
1049
  } else {
1160
1050
  ref.changed = 1;
@@ -1224,17 +1114,7 @@ function vdomSetChildNodes(realNode, lastVDom, newVDom, ref, frame, keys, view,
1224
1114
  const nc = newChildren[newHead];
1225
1115
  if (!isSameVDomNode(nc, oc)) break;
1226
1116
  if (nc.tag === SPLITTER || oc.tag === SPLITTER) break;
1227
- vdomSetNode(
1228
- oldDomNodes[headIdx],
1229
- realNode,
1230
- oc,
1231
- nc,
1232
- ref,
1233
- frame,
1234
- keys,
1235
- view,
1236
- ready
1237
- );
1117
+ vdomSetNode(oldDomNodes[headIdx], realNode, oc, nc, ref, frame, keys, view, ready);
1238
1118
  usedOldDomNodes.add(oldDomNodes[headIdx]);
1239
1119
  headIdx++;
1240
1120
  newHead++;
@@ -1244,17 +1124,7 @@ function vdomSetChildNodes(realNode, lastVDom, newVDom, ref, frame, keys, view,
1244
1124
  const nc = newChildren[newTail];
1245
1125
  if (!isSameVDomNode(nc, oc)) break;
1246
1126
  if (nc.tag === SPLITTER || oc.tag === SPLITTER) break;
1247
- vdomSetNode(
1248
- oldDomNodes[tailIdx],
1249
- realNode,
1250
- oc,
1251
- nc,
1252
- ref,
1253
- frame,
1254
- keys,
1255
- view,
1256
- ready
1257
- );
1127
+ vdomSetNode(oldDomNodes[tailIdx], realNode, oc, nc, ref, frame, keys, view, ready);
1258
1128
  usedOldDomNodes.add(oldDomNodes[tailIdx]);
1259
1129
  tailIdx--;
1260
1130
  newTail--;
@@ -1377,113 +1247,88 @@ function createVDomRef(viewId) {
1377
1247
  }
1378
1248
 
1379
1249
  // src/updater.ts
1380
- var Updater = class {
1381
- /** View ID (same as owner frame ID) */
1382
- viewId;
1383
- /** Current data object */
1384
- data;
1385
- /** Ref data for template rendering */
1386
- refData;
1387
- /** Changed keys in current digest cycle */
1388
- changedKeys = /* @__PURE__ */ new Set();
1389
- /** Whether data has changed since last digest */
1390
- hasChangedFlag = 0;
1391
- /**
1392
- * Digesting queue: supports re-digest during digest.
1393
- * Holds pending callbacks; `null` is used as a sentinel marking the start
1394
- * of an active digest cycle, so `runDigest` can detect re-entrant calls.
1395
- */
1396
- digestingQueue = [];
1397
- /** Monotonically increasing version, bumped each time data actually changes. */
1398
- version = 0;
1399
- /** Snapshot of `version` taken by `snapshot()`, used by `altered()`. */
1400
- snapshotVersion;
1401
- /** Last rendered VDOM tree (only used when virtualDom is enabled) */
1402
- vdom;
1403
- constructor(viewId) {
1404
- this.viewId = viewId;
1405
- this.data = { vId: viewId };
1406
- const refCounter = {};
1407
- refCounter[SPLITTER] = 1;
1408
- this.refData = refCounter;
1409
- this.hasChangedFlag = 1;
1410
- }
1411
- /**
1412
- * Get data by key.
1413
- * Returns entire data object if key is omitted.
1414
- */
1415
- get(key) {
1416
- let result = this.data;
1250
+ function createUpdater(viewId) {
1251
+ let data = { vId: viewId };
1252
+ const refData = {};
1253
+ refData[SPLITTER] = 1;
1254
+ let changedKeys = /* @__PURE__ */ new Set();
1255
+ let hasChangedFlag = 0;
1256
+ const digestingQueue = [];
1257
+ let version = 0;
1258
+ let snapshotVersion;
1259
+ let vdom;
1260
+ hasChangedFlag = 1;
1261
+ function get(key) {
1262
+ let result = data;
1417
1263
  if (key) {
1418
- result = this.data[key];
1264
+ result = data[key];
1419
1265
  }
1420
1266
  return result;
1421
1267
  }
1422
- /**
1423
- * Set data, tracking changed keys.
1424
- * Returns this for chaining.
1425
- */
1426
- set(data, excludes) {
1427
- const changed = setData(
1428
- data,
1429
- this.data,
1430
- this.changedKeys,
1431
- excludes || EMPTY_STRING_SET
1432
- );
1268
+ function set(newData, excludes) {
1269
+ const changed = setData(newData, data, changedKeys, excludes || EMPTY_STRING_SET);
1433
1270
  if (changed) {
1434
- this.version++;
1435
- this.hasChangedFlag = 1;
1271
+ version++;
1272
+ hasChangedFlag = 1;
1436
1273
  }
1437
- return this;
1274
+ return api;
1438
1275
  }
1439
- /**
1440
- * Detect changes and trigger DOM re-render.
1441
- *
1442
- * The core rendering pipeline:
1443
- * 1. Set data if provided
1444
- * 2. If changed, run DOM diff (template → new DOM → diff against old DOM)
1445
- * 3. Apply DOM operations
1446
- * 4. Apply ID updates
1447
- * 5. Call endUpdate on views that need re-rendering
1448
- * 6. Support re-digest during digest via queue
1449
- */
1450
- digest(data, excludes, callback) {
1451
- if (data) {
1452
- this.set(data, excludes);
1276
+ function digest(newData, excludes, callback) {
1277
+ if (newData) {
1278
+ set(newData, excludes);
1453
1279
  }
1454
- const digesting = this.digestingQueue;
1455
1280
  if (callback) {
1456
- digesting.push(callback);
1281
+ digestingQueue.push(callback);
1457
1282
  }
1458
- if (digesting.length > 0 && digesting[0] === null) {
1283
+ if (digestingQueue.length > 0 && digestingQueue[0] === null) {
1459
1284
  return;
1460
1285
  }
1461
- this.runDigest(digesting);
1286
+ runDigest(digestingQueue);
1462
1287
  }
1463
- /**
1464
- * Core digest execution.
1465
- */
1466
- runDigest(digesting) {
1288
+ function runDigest(digesting) {
1467
1289
  const startIndex = digesting.length;
1468
1290
  digesting.push(null);
1469
- const keys = this.changedKeys;
1470
- const changed = this.hasChangedFlag;
1471
- this.hasChangedFlag = 0;
1472
- this.changedKeys = /* @__PURE__ */ new Set();
1473
- const frame = getFrame(this.viewId);
1291
+ const keys = changedKeys;
1292
+ const changed = hasChangedFlag;
1293
+ const frame = Frame.get(viewId);
1474
1294
  const view = frame?.view;
1475
- const node = getById(this.viewId);
1476
- if (changed && view && node && view.signature > 0 && frame) {
1477
- const template = view.template;
1295
+ const node = getById(viewId);
1296
+ if (changed && view && node && view.signature.value > 0 && frame) {
1297
+ hasChangedFlag = 0;
1298
+ changedKeys = /* @__PURE__ */ new Set();
1299
+ const template = view.getTemplate();
1478
1300
  if (typeof template === "function") {
1479
- if (config.virtualDom) {
1480
- const vdomTemplate = template;
1481
- const newVDom = vdomTemplate(this.data, this.viewId, this.refData);
1482
- const ref = createVDomRef(this.viewId);
1301
+ const result = template(
1302
+ data,
1303
+ viewId,
1304
+ refData,
1305
+ encodeHTML,
1306
+ strSafe,
1307
+ encodeURIExtra,
1308
+ refFn,
1309
+ encodeQuote
1310
+ );
1311
+ if (typeof result === "string") {
1312
+ const newDom = domGetNode(result, node);
1313
+ const ref = createDomRef();
1314
+ domSetChildNodes(node, newDom, ref, frame, keys);
1315
+ applyIdUpdates(ref.idUpdates);
1316
+ applyDomOps(ref.domOps);
1317
+ for (const v of ref.views) {
1318
+ if (v.render) {
1319
+ funcWithTry(v.render, [], v, noop);
1320
+ }
1321
+ }
1322
+ if (ref.hasChanged || !view.rendered.value) {
1323
+ view.endUpdate(viewId);
1324
+ }
1325
+ } else {
1326
+ const newVDom = result;
1327
+ const ref = createVDomRef(viewId);
1483
1328
  const ready = () => {
1484
- this.vdom = newVDom;
1485
- if (ref.changed || !view.rendered) {
1486
- view.endUpdate(this.viewId);
1329
+ vdom = newVDom;
1330
+ if (ref.changed || !view.rendered.value) {
1331
+ view.endUpdate(viewId);
1487
1332
  }
1488
1333
  for (const [el, prop, val] of ref.nodeProps) {
1489
1334
  Reflect.set(el, prop, val);
@@ -1494,45 +1339,14 @@ var Updater = class {
1494
1339
  }
1495
1340
  }
1496
1341
  };
1497
- vdomSetChildNodes(
1498
- node,
1499
- this.vdom,
1500
- newVDom,
1501
- ref,
1502
- frame,
1503
- keys,
1504
- view,
1505
- ready
1506
- );
1507
- } else {
1508
- const html = template(
1509
- this.data,
1510
- this.viewId,
1511
- this.refData,
1512
- encodeHTML,
1513
- strSafe,
1514
- encodeURIExtra,
1515
- refFn,
1516
- encodeQuote
1517
- );
1518
- const newDom = domGetNode(html, node);
1519
- const ref = createDomRef();
1520
- domSetChildNodes(node, newDom, ref, frame, keys);
1521
- applyIdUpdates(ref.idUpdates);
1522
- applyDomOps(ref.domOps);
1523
- for (const v of ref.views) {
1524
- if (v.render) {
1525
- funcWithTry(v.render, [], v, noop);
1526
- }
1527
- }
1528
- if (ref.hasChanged || !view.rendered) {
1529
- view.endUpdate(this.viewId);
1530
- }
1342
+ vdomSetChildNodes(node, vdom, newVDom, ref, frame, keys, view, ready);
1531
1343
  }
1532
1344
  }
1345
+ } else {
1346
+ changedKeys = /* @__PURE__ */ new Set();
1533
1347
  }
1534
1348
  if (digesting.length > startIndex + 1) {
1535
- this.runDigest(digesting);
1349
+ runDigest(digesting);
1536
1350
  } else {
1537
1351
  const callbacks = digesting.slice();
1538
1352
  digesting.length = 0;
@@ -1541,44 +1355,19 @@ var Updater = class {
1541
1355
  }
1542
1356
  }
1543
1357
  }
1544
- /**
1545
- * Save a snapshot of the current data version for `altered()` detection.
1546
- * Cheap O(1) — records the current monotonic version, no serialization.
1547
- */
1548
- snapshot() {
1549
- this.snapshotVersion = this.version;
1550
- return this;
1358
+ function snapshot() {
1359
+ snapshotVersion = version;
1360
+ return api;
1551
1361
  }
1552
- /**
1553
- * Check whether data has changed since the last snapshot.
1554
- * Returns undefined when no snapshot has been taken yet.
1555
- */
1556
- altered() {
1557
- if (this.snapshotVersion === void 0) return void 0;
1558
- return this.version !== this.snapshotVersion;
1362
+ function altered() {
1363
+ if (snapshotVersion === void 0) return void 0;
1364
+ return version !== snapshotVersion;
1559
1365
  }
1560
- /**
1561
- * Translate a refData reference back to its original value.
1562
- *
1563
- * The ref protocol is `SPLITTER` + ascii decimal digits — the exact format
1564
- * emitted by `refFn`. We require that exact shape so a user-supplied
1565
- * string that merely begins with SPLITTER is never accidentally resolved
1566
- * (or mishandled as a "missing ref").
1567
- */
1568
- translate(data) {
1569
- if (typeof data !== "string" || !isRefToken(data)) return data;
1570
- return hasOwnProperty(this.refData, data) ? this.refData[data] : data;
1366
+ function translate(dataVal) {
1367
+ if (typeof dataVal !== "string" || !isRefToken(dataVal)) return dataVal;
1368
+ return hasOwnProperty(refData, dataVal) ? refData[dataVal] : dataVal;
1571
1369
  }
1572
- /**
1573
- * Resolve a dotted property path against refData.
1574
- *
1575
- * Only safe property-path syntax is supported: `a`, `a.b`, `a.b.c`.
1576
- * Numeric literals (e.g. `1`, `1.5`) are returned as numbers. Anything else
1577
- * returns `undefined` — we no longer evaluate arbitrary JavaScript via
1578
- * `new Function`, so the method is CSP-safe and cannot be used as an
1579
- * injection vector.
1580
- */
1581
- parse(expr) {
1370
+ function parse(expr) {
1582
1371
  const trimmed = expr.trim();
1583
1372
  if (!trimmed) return void 0;
1584
1373
  if (/^-?\d+(?:\.\d+)?$/.test(trimmed)) {
@@ -1587,25 +1376,40 @@ var Updater = class {
1587
1376
  if (!/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*$/.test(trimmed)) {
1588
1377
  return void 0;
1589
1378
  }
1590
- let cur = this.refData;
1379
+ let cur = refData;
1591
1380
  for (const segment of trimmed.split(".")) {
1592
1381
  if (cur == null || typeof cur !== "object") return void 0;
1593
- cur = cur[segment];
1382
+ cur = Reflect.get(cur, segment);
1594
1383
  }
1595
1384
  return cur;
1596
1385
  }
1597
- /**
1598
- * Get the set of keys changed since the last digest (for external inspection).
1599
- */
1600
- getChangedKeys() {
1601
- return this.changedKeys;
1602
- }
1603
- };
1386
+ function getChangedKeys() {
1387
+ return changedKeys;
1388
+ }
1389
+ function forceDigest() {
1390
+ hasChangedFlag = 1;
1391
+ changedKeys = new Set(Object.keys(data));
1392
+ digest();
1393
+ }
1394
+ const api = {
1395
+ get,
1396
+ set,
1397
+ digest,
1398
+ forceDigest,
1399
+ snapshot,
1400
+ altered,
1401
+ refData,
1402
+ translate,
1403
+ parse,
1404
+ getChangedKeys
1405
+ };
1406
+ return api;
1407
+ }
1604
1408
 
1605
1409
  // src/router.ts
1606
- var emitter = new EventEmitter();
1607
- var hrefCache = new Cache();
1608
- var changedCache = new Cache();
1410
+ var emitter = createEmitter();
1411
+ var hrefCache = createCache();
1412
+ var changedCache = createCache();
1609
1413
  var lastLocation = createEmptyLocation();
1610
1414
  var lastChanged;
1611
1415
  var silent = 0;
@@ -1649,11 +1453,7 @@ function attachViewAndPath(loc) {
1649
1453
  path = cachedDefaultPath;
1650
1454
  }
1651
1455
  if (cachedRewrite) {
1652
- path = cachedRewrite(
1653
- path,
1654
- loc["params"],
1655
- cachedRoutes
1656
- );
1456
+ path = cachedRewrite(path, loc["params"], cachedRoutes);
1657
1457
  }
1658
1458
  const viewEntry = cachedRoutes[path] || cachedUnmatchedView || cachedDefaultView;
1659
1459
  loc["path"] = path;
@@ -1856,7 +1656,7 @@ var Router = {
1856
1656
  updateUrl(tPath, tParams, lastLocation, replace, silentFlag, lQuery);
1857
1657
  },
1858
1658
  /**
1859
- * Register an async-friendly navigation guard. See `RouterInterface.beforeEach`.
1659
+ * Register an async-friendly navigation guard. See `RouterApi.beforeEach`.
1860
1660
  */
1861
1661
  beforeEach(guard) {
1862
1662
  beforeEachGuards.push(guard);
@@ -1994,62 +1794,10 @@ var Router = {
1994
1794
  }
1995
1795
  };
1996
1796
 
1997
- // src/view-registry.ts
1998
- var viewClassRegistry = {};
1999
- function getViewClass(path) {
2000
- return viewClassRegistry[path];
2001
- }
2002
- function registerViewClass(viewPath, ViewClass) {
2003
- const parsed = parseUri(viewPath);
2004
- const path = parsed.path;
2005
- if (path) {
2006
- viewClassRegistry[path] = ViewClass;
2007
- }
2008
- }
2009
- function invalidateViewClass(viewPath) {
2010
- const parsed = parseUri(viewPath);
2011
- const path = parsed.path;
2012
- if (path) {
2013
- Reflect.deleteProperty(viewClassRegistry, path);
2014
- }
2015
- }
2016
-
2017
- // src/hmr.ts
2018
- function reloadViews(viewPath) {
2019
- const allFrames = getAllFrames();
2020
- const toReload = [];
2021
- for (const [, frame] of allFrames) {
2022
- if (frame.viewPath) {
2023
- const parsed = parseUri(frame.viewPath);
2024
- if (parsed.path === viewPath) {
2025
- toReload.push({ frame, fullPath: frame.viewPath });
2026
- }
2027
- }
2028
- }
2029
- for (const { frame, fullPath } of toReload) {
2030
- frame.mountView(fullPath);
2031
- }
2032
- }
2033
- function acceptView(hot, viewPath) {
2034
- hot.accept((newModule) => {
2035
- const candidate = newModule?.default ?? newModule;
2036
- if (typeof candidate === "function") {
2037
- const NewViewClass = candidate;
2038
- registerViewClass(viewPath, NewViewClass);
2039
- reloadViews(viewPath);
2040
- return;
2041
- }
2042
- if (getViewClass(viewPath)) {
2043
- reloadViews(viewPath);
2044
- return;
2045
- }
2046
- hot.invalidate();
2047
- });
2048
- }
2049
- function disposeView(hot, viewPath) {
2050
- hot.dispose(() => {
2051
- invalidateViewClass(viewPath);
2052
- });
1797
+ // src/hooks.ts
1798
+ var currentCtx = null;
1799
+ function setCurrentCtx(ctx) {
1800
+ currentCtx = ctx;
2053
1801
  }
2054
1802
 
2055
1803
  // src/view.ts
@@ -2060,161 +1808,99 @@ if (typeof window !== "undefined") {
2060
1808
  if (typeof document !== "undefined") {
2061
1809
  VIEW_GLOBALS["document"] = document;
2062
1810
  }
2063
- var View = class _View {
2064
- /** View ID (same as owner frame ID) */
2065
- id = "";
2066
- /** Owner frame */
2067
- owner = 0;
2068
- /** Updater instance */
2069
- updater;
2070
- /** Signature: > 0 means active, incremented on render, 0 = destroyed */
2071
- signature = 0;
2072
- /** Whether rendered at least once */
2073
- rendered;
2074
- /** Whether view has template */
2075
- template;
2076
- /** Location observation config */
2077
- locationObserved = {
1811
+ function createCtx(frame) {
1812
+ const id = frame.id;
1813
+ const updater = createUpdater(id);
1814
+ const emitter2 = createEmitter();
1815
+ const signature = { value: 0 };
1816
+ const rendered = { value: false };
1817
+ const resources = {};
1818
+ const locationObserved = {
2078
1819
  flag: 0,
2079
1820
  keys: [],
2080
1821
  observePath: false
2081
1822
  };
2082
- /** Observed state keys */
2083
- observedStateKeys;
2084
- /** Resource map */
2085
- resources = {};
2086
- /** Whether endUpdate pending */
2087
- endUpdatePending;
2088
- /** Internal event storage */
2089
- _events = new EventEmitter();
2090
- // ============================================================
2091
- // Getters for prototype-stored event maps
2092
- // ============================================================
2093
- /** Prototype-stored event maps shape (set by View.prepare). */
2094
- get protoEventState() {
2095
- return Object.getPrototypeOf(this);
2096
- }
2097
- /**
2098
- * Event bitmask map: eventType -> bitmask (1=root, 2=selector).
2099
- * Read from prototype ($evtObjMap) set by View.prepare.
2100
- * Using a getter avoids ES6 class field shadowing the prototype value.
2101
- */
2102
- get eventObjectMap() {
2103
- return this.protoEventState.$evtObjMap ?? {};
2104
- }
2105
- /**
2106
- * Selector event map: eventType -> selector list.
2107
- * Read from prototype ($selMap) set by View.prepare.
2108
- */
2109
- get eventSelectorMap() {
2110
- return this.protoEventState.$selMap ?? {};
1823
+ const mutable = {
1824
+ observedStateKeys: void 0,
1825
+ endUpdatePending: void 0,
1826
+ template: void 0,
1827
+ events: void 0,
1828
+ assignFn: void 0
1829
+ };
1830
+ const cleanups = [];
1831
+ function on(event, handler) {
1832
+ emitter2.on(event, handler);
1833
+ return () => emitter2.off(event, handler);
2111
1834
  }
2112
- /**
2113
- * Global event list: [{handler, element, eventName, modifiers}].
2114
- * Read from prototype ($globalEvtList) set by View.prepare.
2115
- */
2116
- get globalEventList() {
2117
- return this.protoEventState.$globalEvtList ?? [];
1835
+ function off(event, handler) {
1836
+ emitter2.off(event, handler);
2118
1837
  }
2119
- // ============================================================
2120
- // Instance lifecycle methods
2121
- // ============================================================
2122
- /**
2123
- * Initialize view (called by Frame when mounting).
2124
- */
2125
- init() {
1838
+ function fire(event, data, remove, lastToFirst) {
1839
+ emitter2.fire(event, data, remove, lastToFirst);
2126
1840
  }
2127
- /**
2128
- * Render view template (called by Frame after init).
2129
- * Wrapped by View.wrapMethod to manage signature + resources.
2130
- */
2131
- render() {
2132
- this.updater.digest();
2133
- }
2134
- // ============================================================
2135
- // Event methods (delegate to internal EventEmitter)
2136
- // ============================================================
2137
- on(event, handler) {
2138
- this._events.on(event, handler);
2139
- return this;
2140
- }
2141
- off(event, handler) {
2142
- this._events.off(event, handler);
2143
- return this;
1841
+ function capture(key, resource, destroyOnRender = false) {
1842
+ if (resource !== void 0) {
1843
+ destroyResource(resources, key, true, resource);
1844
+ resources[key] = { entity: resource, destroyOnRender };
1845
+ } else {
1846
+ const entry = resources[key];
1847
+ return entry ? entry.entity : void 0;
1848
+ }
1849
+ return resource;
2144
1850
  }
2145
- fire(event, data, remove, lastToFirst) {
2146
- this._events.fire(event, data, remove, lastToFirst);
2147
- return this;
1851
+ function release(key, destroy = true) {
1852
+ return destroyResource(resources, key, destroy);
2148
1853
  }
2149
- // ============================================================
2150
- // Update methods
2151
- // ============================================================
2152
- /** Get the owning frame, asserting it has been bound. */
2153
- get ownerFrame() {
2154
- return this.owner;
1854
+ function render() {
1855
+ if (signature.value > 0) {
1856
+ signature.value++;
1857
+ fire("render");
1858
+ destroyAllResources(ctx, false);
1859
+ if (typeof ctx.renderMethod === "function") {
1860
+ funcWithTry(ctx.renderMethod, [], ctx, noop);
1861
+ } else {
1862
+ updater.digest();
1863
+ }
1864
+ }
2155
1865
  }
2156
- /**
2157
- * Notify view that HTML update is about to begin.
2158
- * Unmounts child frames in the update zone.
2159
- */
2160
- beginUpdate(id) {
2161
- if (this.signature > 0 && this.endUpdatePending !== void 0) {
2162
- this.ownerFrame.unmountZone(id);
1866
+ function beginUpdate(zoneId) {
1867
+ if (signature.value > 0 && mutable.endUpdatePending !== void 0) {
1868
+ frame.unmountZone(zoneId);
2163
1869
  }
2164
1870
  }
2165
- /**
2166
- * Notify view that HTML update has ended.
2167
- * Mounts child frames in the update zone and runs deferred invokes.
2168
- */
2169
- endUpdate(id, inner) {
2170
- if (this.signature > 0) {
2171
- const updateId = id || this.id;
1871
+ function endUpdate(zoneId, inner) {
1872
+ if (signature.value > 0) {
1873
+ const updateId = zoneId ?? id;
2172
1874
  let flag;
2173
1875
  if (inner) {
2174
1876
  flag = inner;
2175
1877
  } else {
2176
- flag = this.endUpdatePending;
2177
- this.endUpdatePending = 1;
2178
- this.rendered = true;
1878
+ flag = mutable.endUpdatePending;
1879
+ mutable.endUpdatePending = 1;
1880
+ rendered.value = true;
2179
1881
  }
2180
- const ownerFrame = this.ownerFrame;
2181
- ownerFrame.mountZone(updateId);
1882
+ frame.mountZone(updateId);
2182
1883
  if (!flag) {
2183
1884
  setTimeout(
2184
- this.wrapAsync(() => {
2185
- _View.runInvokes(ownerFrame);
1885
+ wrapAsync(() => {
1886
+ runInvokes(frame);
2186
1887
  }),
2187
1888
  0
2188
1889
  );
2189
1890
  }
2190
1891
  }
2191
1892
  }
2192
- // ============================================================
2193
- // Async wrapper
2194
- // ============================================================
2195
- /**
2196
- * Wrap an async callback to check view signature before executing.
2197
- * If the view has been re-rendered or destroyed, the callback is skipped.
2198
- */
2199
- wrapAsync(fn, context) {
2200
- const currentSignature = this.signature;
1893
+ function wrapAsync(fn, context) {
1894
+ const currentSignature = signature.value;
2201
1895
  return (...args) => {
2202
- if (currentSignature > 0 && currentSignature === this.signature) {
2203
- return fn.apply(context || this, args);
1896
+ if (currentSignature > 0 && currentSignature === signature.value) {
1897
+ return fn.apply(context ?? ctx, args);
2204
1898
  }
2205
1899
  return void 0;
2206
1900
  };
2207
1901
  }
2208
- // ============================================================
2209
- // Location observation
2210
- // ============================================================
2211
- /**
2212
- * Observe location parameters or path changes.
2213
- * When observed keys change, render() is called automatically.
2214
- */
2215
- observeLocation(params, observePath = false) {
2216
- const loc = this.locationObserved;
2217
- loc.flag = 1;
1902
+ function observeLocation(params, observePath = false) {
1903
+ locationObserved.flag = 1;
2218
1904
  if (typeof params === "object" && !Array.isArray(params)) {
2219
1905
  const opts = params;
2220
1906
  if (opts["path"]) {
@@ -2225,880 +1911,634 @@ var View = class _View {
2225
1911
  params = paramKeys;
2226
1912
  }
2227
1913
  }
2228
- loc.observePath = observePath;
1914
+ locationObserved.observePath = observePath;
2229
1915
  if (params) {
2230
1916
  if (typeof params === "string") {
2231
- loc.keys = params.split(",");
1917
+ locationObserved.keys = params.split(",");
2232
1918
  } else if (Array.isArray(params)) {
2233
- loc.keys = params;
1919
+ locationObserved.keys = params;
2234
1920
  }
2235
1921
  }
2236
1922
  }
2237
- // ============================================================
2238
- // State observation
2239
- // ============================================================
2240
- /**
2241
- * Observe State data keys for changes.
2242
- * When observed keys change via State.digest(), render() is called.
2243
- */
2244
- observeState(observedKeys) {
2245
- if (typeof observedKeys === "string") {
2246
- this.observedStateKeys = observedKeys.split(",");
1923
+ function observeState(keys) {
1924
+ if (typeof keys === "string") {
1925
+ mutable.observedStateKeys = keys.split(",");
2247
1926
  } else {
2248
- this.observedStateKeys = observedKeys;
1927
+ mutable.observedStateKeys = keys;
2249
1928
  }
2250
1929
  }
2251
- // ============================================================
2252
- // Resource management
2253
- // ============================================================
2254
- /**
2255
- * Capture (register) a resource under a key.
2256
- * If a resource already exists at that key, it's destroyed first.
2257
- * When destroyOnRender=true, the resource is destroyed on next render call.
2258
- */
2259
- capture(key, resource, destroyOnRender = false) {
2260
- const cache = this.resources;
2261
- if (resource) {
2262
- _View.destroyResource(cache, key, true, resource);
2263
- cache[key] = {
2264
- entity: resource,
2265
- destroyOnRender
2266
- };
2267
- } else {
2268
- const entry = cache[key];
2269
- return entry ? entry.entity : void 0;
1930
+ function leaveTip(message, condition) {
1931
+ const changeState = { a: 0, b: 0 };
1932
+ function isRouteChange(e) {
1933
+ return "prevent" in e && "reject" in e && "resolve" in e;
2270
1934
  }
2271
- return resource;
2272
- }
2273
- /**
2274
- * Release a captured resource.
2275
- * If destroy=true, calls the resource's destroy() method.
2276
- */
2277
- release(key, destroy = true) {
2278
- return _View.destroyResource(this.resources, key, destroy);
2279
- }
2280
- // ============================================================
2281
- // Leave tip
2282
- // ============================================================
2283
- /**
2284
- * Set up a leave confirmation for route changes and page unload.
2285
- */
2286
- leaveTip(message, condition) {
2287
- const changeListener = function(e) {
1935
+ const changeListener = (e) => {
1936
+ if (!e) return;
2288
1937
  const isRouterChange = e.type === RouterEvents.CHANGE;
2289
1938
  const aKey = isRouterChange ? "a" : "b";
2290
1939
  const bKey = isRouterChange ? "b" : "a";
2291
- if (changeListener[aKey]) {
2292
- e.prevent?.();
2293
- e.reject?.();
1940
+ if (changeState[aKey]) {
1941
+ if (isRouteChange(e)) {
1942
+ e.prevent();
1943
+ e.reject();
1944
+ }
2294
1945
  } else if (condition()) {
2295
- e.prevent?.();
2296
- changeListener[bKey] = 1;
2297
- e.resolve?.();
1946
+ if (isRouteChange(e)) {
1947
+ e.prevent();
1948
+ changeState[bKey] = 1;
1949
+ e.resolve();
1950
+ }
2298
1951
  }
2299
1952
  };
2300
1953
  const unloadListener = (e) => {
2301
- if (condition()) {
2302
- e["msg"] = message;
1954
+ if (e && condition()) {
1955
+ Reflect.set(e, "msg", message);
2303
1956
  }
2304
1957
  };
2305
1958
  Router.on(RouterEvents.CHANGE, changeListener);
2306
1959
  Router.on(RouterEvents.PAGE_UNLOAD, unloadListener);
2307
- this.on("unload", changeListener);
2308
- this.on("destroy", () => {
1960
+ on("unload", changeListener);
1961
+ on("destroy", () => {
2309
1962
  Router.off(RouterEvents.CHANGE, changeListener);
2310
1963
  Router.off(RouterEvents.PAGE_UNLOAD, unloadListener);
2311
1964
  });
2312
1965
  }
2313
- // ============================================================
2314
- // Static public methods
2315
- // ============================================================
2316
- /** Collected makes from mixins */
2317
- static makes;
2318
- /**
2319
- * Prepare a View subclass by scanning its prototype for event method patterns.
2320
- * Pattern: `$?name<eventType1,eventType2>(&modifiers)`
2321
- *
2322
- * Only runs once per View subclass (guarded by makes marker).
2323
- * Called from Frame.mountView before creating the view instance.
2324
- */
2325
- static prepare(oView) {
2326
- if (oView.makes) {
2327
- return oView.makes;
2328
- }
2329
- const makes = [];
2330
- oView.makes = makes;
2331
- const eventsObject = {};
2332
- const eventsList = [];
2333
- const selectorObject = {};
2334
- const mixins = Reflect.get(oView.prototype, "mixins");
2335
- if (mixins && Array.isArray(mixins)) {
2336
- _View.mergeMixins(mixins, oView, makes);
2337
- }
2338
- for (const p in oView.prototype) {
2339
- if (!hasOwnProperty(oView.prototype, p)) continue;
2340
- const currentFn = Reflect.get(oView.prototype, p);
2341
- if (typeof currentFn !== "function") continue;
2342
- const matches = p.match(VIEW_EVENT_METHOD_REGEXP);
2343
- if (!matches) continue;
2344
- const isSelector = matches[1];
2345
- const selectorOrCallback = matches[2];
2346
- const events = matches[3];
2347
- const modifiers = matches[4];
2348
- const mod = {};
2349
- if (modifiers) {
2350
- for (const item of modifiers.split(",")) {
2351
- mod[item] = true;
2352
- }
2353
- }
2354
- const eventTypes = events.split(",");
2355
- for (const item of eventTypes) {
2356
- const globalNode = VIEW_GLOBALS[selectorOrCallback];
2357
- let mask = 1;
2358
- if (isSelector) {
2359
- if (globalNode) {
2360
- eventsList.push({
2361
- handler: currentFn,
2362
- element: globalNode,
2363
- eventName: item,
2364
- modifiers: mod
2365
- });
2366
- continue;
2367
- }
2368
- mask = 2;
2369
- let selectorEntry = selectorObject[item];
2370
- if (!selectorEntry) {
2371
- selectorEntry = selectorObject[item] = {
2372
- selectors: []
2373
- };
2374
- }
2375
- if (!selectorEntry[selectorOrCallback]) {
2376
- selectorEntry[selectorOrCallback] = 1;
2377
- selectorEntry.selectors.push(selectorOrCallback);
2378
- }
2379
- }
2380
- eventsObject[item] = (eventsObject[item] || 0) | mask;
2381
- const combinedKey = selectorOrCallback + SPLITTER + item;
2382
- const existingFn = Reflect.get(oView.prototype, combinedKey);
2383
- if (!existingFn) {
2384
- Reflect.set(oView.prototype, combinedKey, currentFn);
2385
- } else if (typeof existingFn === "function") {
2386
- const mixinFn = currentFn;
2387
- const existingMixin = existingFn;
2388
- if (existingMixin.marker) {
2389
- if (mixinFn.marker) {
2390
- Reflect.set(
2391
- oView.prototype,
2392
- combinedKey,
2393
- _View.processMixinsSameEvent(mixinFn, existingMixin)
2394
- );
2395
- } else if (hasOwnProperty(oView.prototype, p)) {
2396
- Reflect.set(oView.prototype, combinedKey, currentFn);
2397
- }
2398
- }
2399
- }
1966
+ function init(_params) {
1967
+ }
1968
+ function getTemplate() {
1969
+ return mutable.template;
1970
+ }
1971
+ function setTemplate(v) {
1972
+ mutable.template = v;
1973
+ }
1974
+ function getObservedStateKeys() {
1975
+ return mutable.observedStateKeys;
1976
+ }
1977
+ function setObservedStateKeys(v) {
1978
+ mutable.observedStateKeys = v;
1979
+ }
1980
+ function getEndUpdatePending() {
1981
+ return mutable.endUpdatePending;
1982
+ }
1983
+ function setEndUpdatePending(v) {
1984
+ mutable.endUpdatePending = v;
1985
+ }
1986
+ function getEvents() {
1987
+ return mutable.events;
1988
+ }
1989
+ function setEvents(v) {
1990
+ mutable.events = v;
1991
+ }
1992
+ function getAssign() {
1993
+ return mutable.assignFn;
1994
+ }
1995
+ function setAssign(v) {
1996
+ mutable.assignFn = v;
1997
+ }
1998
+ const ctx = {
1999
+ id,
2000
+ owner: frame,
2001
+ updater,
2002
+ signature,
2003
+ rendered,
2004
+ getTemplate,
2005
+ setTemplate,
2006
+ locationObserved,
2007
+ getObservedStateKeys,
2008
+ setObservedStateKeys,
2009
+ resources,
2010
+ emitter: emitter2,
2011
+ getEndUpdatePending,
2012
+ setEndUpdatePending,
2013
+ getEvents,
2014
+ setEvents,
2015
+ cleanups,
2016
+ getAssign,
2017
+ setAssign,
2018
+ render,
2019
+ init,
2020
+ beginUpdate,
2021
+ endUpdate,
2022
+ wrapAsync,
2023
+ observeLocation,
2024
+ observeState,
2025
+ capture,
2026
+ release,
2027
+ leaveTip,
2028
+ fire,
2029
+ on,
2030
+ off
2031
+ };
2032
+ return ctx;
2033
+ }
2034
+ function registerEvents(ctx) {
2035
+ const events = ctx.getEvents();
2036
+ if (!events) return;
2037
+ for (const key of Object.keys(events)) {
2038
+ if (!hasOwnProperty(events, key)) continue;
2039
+ const handler = events[key];
2040
+ if (typeof handler !== "function") continue;
2041
+ const matches = key.match(VIEW_EVENT_METHOD_REGEXP);
2042
+ if (!matches) continue;
2043
+ const isSelector = matches[1];
2044
+ const selectorOrCallback = matches[2];
2045
+ const eventTypes = matches[3];
2046
+ const modifiers = matches[4];
2047
+ const mod = {};
2048
+ if (modifiers) {
2049
+ for (const item of modifiers.split(",")) {
2050
+ mod[item] = true;
2051
+ }
2052
+ }
2053
+ for (const eventType of eventTypes.split(",")) {
2054
+ const globalNode = VIEW_GLOBALS[selectorOrCallback];
2055
+ if (isSelector && globalNode) {
2056
+ registerGlobalEvent(ctx, globalNode, eventType, handler, mod, key);
2057
+ } else if (isSelector) {
2058
+ EventDelegator.bind(eventType, true);
2059
+ } else {
2060
+ EventDelegator.bind(eventType, false);
2061
+ }
2062
+ }
2063
+ }
2064
+ }
2065
+ function unregisterEvents(ctx) {
2066
+ const events = ctx.getEvents();
2067
+ if (!events) return;
2068
+ for (const key of Object.keys(events)) {
2069
+ if (!hasOwnProperty(events, key)) continue;
2070
+ const matches = key.match(VIEW_EVENT_METHOD_REGEXP);
2071
+ if (!matches) continue;
2072
+ const isSelector = matches[1];
2073
+ const selectorOrCallback = matches[2];
2074
+ const eventTypes = matches[3];
2075
+ for (const eventType of eventTypes.split(",")) {
2076
+ const globalNode = VIEW_GLOBALS[selectorOrCallback];
2077
+ if (isSelector && globalNode) {
2078
+ } else if (isSelector) {
2079
+ EventDelegator.unbind(eventType, true);
2080
+ } else {
2081
+ EventDelegator.unbind(eventType, false);
2400
2082
  }
2401
2083
  }
2402
- _View.wrapMethod(asRecord(oView.prototype), "render", "$renderWrap");
2403
- Reflect.set(oView.prototype, "$evtObjMap", eventsObject);
2404
- Reflect.set(oView.prototype, "$globalEvtList", eventsList);
2405
- Reflect.set(oView.prototype, "$selMap", selectorObject);
2406
- return makes;
2407
2084
  }
2408
- /**
2409
- * Bind or unbind event delegation for a view instance.
2410
- * Called from Frame during mount/unmount.
2411
- */
2412
- static delegateEvents(view, destroy = false) {
2413
- const eventsObject = view.eventObjectMap;
2414
- const selectorObject = view.eventSelectorMap;
2415
- const eventsList = view.globalEventList;
2416
- for (const e in eventsObject) {
2417
- if (hasOwnProperty(eventsObject, e)) {
2418
- if (destroy) {
2419
- EventDelegator.unbind(e, !!selectorObject[e]);
2420
- } else {
2421
- EventDelegator.bind(e, !!selectorObject[e]);
2085
+ EventDelegator.clearRangeEvents(ctx.id);
2086
+ }
2087
+ function registerGlobalEvent(ctx, element, eventName, handler, modifiers, _key) {
2088
+ const listener = {
2089
+ handleEvent(domEvent) {
2090
+ Reflect.set(domEvent, "eventTarget", element);
2091
+ if (modifiers) {
2092
+ const ctrlKey = Reflect.get(domEvent, "ctrlKey");
2093
+ const shiftKey = Reflect.get(domEvent, "shiftKey");
2094
+ const altKey = Reflect.get(domEvent, "altKey");
2095
+ const metaKey = Reflect.get(domEvent, "metaKey");
2096
+ if (modifiers["ctrl"] && !ctrlKey || modifiers["shift"] && !shiftKey || modifiers["alt"] && !altKey || modifiers["meta"] && !metaKey) {
2097
+ return;
2422
2098
  }
2423
2099
  }
2100
+ funcWithTry(handler, [domEvent], ctx, noop);
2424
2101
  }
2425
- for (const entry of eventsList) {
2426
- if (destroy) {
2427
- entry.element.removeEventListener(
2428
- entry.eventName,
2429
- entry.boundHandler
2430
- );
2431
- } else {
2432
- const handler = entry.handler;
2433
- const element = entry.element;
2434
- const modifiers = entry.modifiers;
2435
- entry.boundHandler = function(domEvent) {
2436
- const extendedEvent = domEvent;
2437
- extendedEvent.eventTarget = element;
2438
- if (modifiers) {
2439
- const kbEvent = domEvent;
2440
- if (modifiers["ctrl"] && !kbEvent.ctrlKey || modifiers["shift"] && !kbEvent.shiftKey || modifiers["alt"] && !kbEvent.altKey || modifiers["meta"] && !kbEvent.metaKey) {
2441
- return;
2442
- }
2443
- }
2444
- funcWithTry(handler, [domEvent], view, noop);
2445
- };
2446
- entry.element.addEventListener(
2447
- entry.eventName,
2448
- entry.boundHandler
2449
- );
2102
+ };
2103
+ element.addEventListener(eventName, listener);
2104
+ ctx.on("destroy", () => {
2105
+ element.removeEventListener(eventName, listener);
2106
+ });
2107
+ }
2108
+ function destroyAllResources(ctx, lastly) {
2109
+ const cache = ctx.resources;
2110
+ for (const p in cache) {
2111
+ if (hasOwnProperty(cache, p)) {
2112
+ const entry = cache[p];
2113
+ if (lastly || entry.destroyOnRender) {
2114
+ destroyResource(cache, p, true);
2450
2115
  }
2451
2116
  }
2452
2117
  }
2453
- /**
2454
- * Destroy all resources managed by a view.
2455
- * If lastly=true, destroy ALL resources; otherwise only destroyOnRender ones.
2456
- */
2457
- static destroyAllResources(view, lastly) {
2458
- const cache = view.resources;
2459
- for (const p in cache) {
2460
- if (hasOwnProperty(cache, p)) {
2461
- const entry = cache[p];
2462
- if (lastly || entry.destroyOnRender) {
2463
- _View.destroyResource(cache, p, true);
2464
- }
2465
- }
2118
+ }
2119
+ function destroyResource(cache, key, callDestroy, oldEntity) {
2120
+ const entry = cache[key];
2121
+ if (!entry || entry.entity === oldEntity) return void 0;
2122
+ const entity = entry.entity;
2123
+ if (entity && typeof entity === "object") {
2124
+ const destroyFn = Reflect.get(entity, "destroy");
2125
+ if (typeof destroyFn === "function" && callDestroy) {
2126
+ funcWithTry(destroyFn, [], entity, noop);
2466
2127
  }
2467
2128
  }
2468
- /**
2469
- * Process deferred invoke calls on a frame.
2470
- */
2471
- static runInvokes(frame) {
2472
- const list = frame.invokeList;
2473
- if (!list) return;
2474
- while (list.length) {
2475
- const entry = list.shift();
2476
- if (entry && !entry.removed) {
2477
- frame.invoke(entry.name, entry.args);
2478
- }
2129
+ Reflect.deleteProperty(cache, key);
2130
+ return entity;
2131
+ }
2132
+ function runInvokes(frame) {
2133
+ const list = frame.invokeList;
2134
+ if (!list) return;
2135
+ while (list.length) {
2136
+ const entry = list.shift();
2137
+ if (entry && !entry.removed) {
2138
+ frame.invoke(entry.name, entry.args);
2479
2139
  }
2480
2140
  }
2481
- // ============================================================
2482
- // Static private methods
2483
- // ============================================================
2484
- /**
2485
- * Wrap a method on the prototype to add signature checking and resource cleanup.
2486
- */
2487
- static wrapMethod(proto, fnName, shortKey) {
2488
- const originalFn = proto[fnName];
2489
- if (typeof originalFn !== "function") return;
2490
- const originalAsFn = originalFn;
2491
- const wrapped = function(...args) {
2492
- if (this.signature > 0) {
2493
- this.signature++;
2494
- this.fire("render");
2495
- _View.destroyAllResources(this, false);
2496
- const lookup = asRecord(this);
2497
- const candidate = lookup[fnName];
2498
- const instanceFn = typeof candidate === "function" ? candidate : originalAsFn;
2499
- const fnToCall = instanceFn === wrapped ? originalAsFn : instanceFn;
2500
- return funcWithTry(fnToCall, args, this, noop);
2501
- }
2502
- return void 0;
2503
- };
2504
- proto[fnName] = wrapped;
2505
- proto[shortKey] = wrapped;
2141
+ }
2142
+ function mountCtx(frame, setup, params) {
2143
+ const ctx = createCtx(frame);
2144
+ setCurrentCtx(ctx);
2145
+ let descriptor;
2146
+ try {
2147
+ descriptor = setup(ctx, params);
2148
+ } finally {
2149
+ setCurrentCtx(null);
2150
+ }
2151
+ ctx.setTemplate(descriptor.template);
2152
+ ctx.setEvents(descriptor.events);
2153
+ if (descriptor.assign) {
2154
+ ctx.setAssign(descriptor.assign);
2155
+ }
2156
+ ctx.signature.value = 1;
2157
+ frame.view = ctx;
2158
+ registerEvents(ctx);
2159
+ if (ctx.getTemplate()) {
2160
+ ctx.render();
2161
+ } else {
2162
+ ctx.endUpdate();
2506
2163
  }
2507
- /**
2508
- * When two mixins define the same event method, merge them into
2509
- * a single function that calls both in sequence.
2510
- */
2511
- static processMixinsSameEvent(additional, exist) {
2512
- let temp;
2513
- if (exist.handlerList) {
2514
- temp = exist;
2515
- } else {
2516
- const merged = function(...e) {
2517
- funcWithTry(merged.handlerList ?? [], e, this, noop);
2518
- };
2519
- merged.handlerList = [exist];
2520
- merged.marker = 1;
2521
- temp = merged;
2522
- }
2523
- temp.handlerList = (temp.handlerList ?? []).concat(
2524
- additional.handlerList ?? [additional]
2525
- );
2526
- return temp;
2164
+ return ctx;
2165
+ }
2166
+ function unmountCtx(ctx) {
2167
+ for (let i = ctx.cleanups.length - 1; i >= 0; i--) {
2168
+ const cleanup = ctx.cleanups[i];
2169
+ funcWithTry(cleanup, [], null, noop);
2527
2170
  }
2528
- /**
2529
- * Merge an array of mixin objects into the view prototype.
2530
- */
2531
- static mergeMixins(mixins, viewClass, makes) {
2532
- const proto = asRecord(viewClass.prototype);
2533
- const temp = {};
2534
- for (const node of mixins) {
2535
- for (const p in node) {
2536
- if (!hasOwnProperty(node, p)) continue;
2537
- const fn = node[p];
2538
- if (typeof fn !== "function") continue;
2539
- const mixinFn = fn;
2540
- const exist = temp[p];
2541
- if (p === "make") {
2542
- makes.push(mixinFn);
2543
- continue;
2544
- }
2545
- if (VIEW_EVENT_METHOD_REGEXP.test(p)) {
2546
- if (exist) {
2547
- temp[p] = _View.processMixinsSameEvent(mixinFn, exist);
2548
- } else {
2549
- mixinFn.marker = 1;
2550
- temp[p] = mixinFn;
2551
- }
2552
- } else if (!exist) {
2553
- temp[p] = mixinFn;
2554
- }
2555
- }
2556
- }
2557
- for (const p in temp) {
2558
- if (!hasOwnProperty(proto, p)) {
2559
- proto[p] = temp[p];
2560
- }
2561
- }
2171
+ ctx.cleanups.length = 0;
2172
+ unregisterEvents(ctx);
2173
+ destroyAllResources(ctx, true);
2174
+ if (ctx.signature.value > 0) {
2175
+ ctx.fire("destroy", void 0, true, true);
2562
2176
  }
2563
- /**
2564
- * Destroy a single resource entry.
2565
- */
2566
- static destroyResource(cache, key, callDestroy, oldEntity) {
2567
- const entry = cache[key];
2568
- if (!entry || entry.entity === oldEntity) return void 0;
2569
- const entity = entry.entity;
2570
- if (entity && typeof entity === "object") {
2571
- const destroyFn = entity["destroy"];
2572
- if (typeof destroyFn === "function" && callDestroy) {
2573
- funcWithTry(destroyFn, [], entity, noop);
2574
- }
2575
- }
2576
- Reflect.deleteProperty(cache, key);
2577
- return entity;
2578
- }
2579
- // ============================================================
2580
- // Static: extend and merge
2581
- // ============================================================
2582
- /**
2583
- * Extend View to create a new View subclass.
2584
- *
2585
- * Supports:
2586
- * - props.make: constructor-like init (called with initParams + {node, deep})
2587
- * - props.mixins: array of mixin objects
2588
- * - Event method patterns: `'name<click>'` etc.
2589
- */
2590
- static extend(props, statics) {
2591
- const definedProps = props ?? {};
2592
- const make = definedProps["make"];
2593
- const makes = [];
2594
- if (typeof make === "function") {
2595
- makes.push(make);
2596
- }
2597
- const ParentView = this;
2598
- const ChildView = class extends ParentView {
2599
- constructor(nodeId, ownerFrame, initParams, node, mixinCtors) {
2600
- super(nodeId, ownerFrame, initParams, node, []);
2601
- const instanceProps = this;
2602
- for (const key in definedProps) {
2603
- if (hasOwnProperty(definedProps, key) && key !== "make" && key !== "render") {
2604
- instanceProps[key] = definedProps[key];
2605
- }
2606
- }
2607
- this.id = nodeId;
2608
- this.owner = ownerFrame;
2609
- this.updater = new Updater(nodeId);
2610
- const params = [
2611
- initParams,
2612
- {
2613
- node,
2614
- deep: !this.template
2615
- }
2616
- ];
2617
- const concatCtors = makes.concat(mixinCtors || []);
2618
- if (concatCtors.length) {
2619
- funcWithTry(concatCtors, params, this, noop);
2620
- }
2621
- }
2622
- };
2623
- for (const key in definedProps) {
2624
- if (hasOwnProperty(definedProps, key) && key !== "make") {
2625
- Reflect.set(ChildView.prototype, key, definedProps[key]);
2626
- }
2627
- }
2628
- if (statics) {
2629
- for (const key in statics) {
2630
- if (hasOwnProperty(statics, key)) {
2631
- Reflect.set(ChildView, key, statics[key]);
2632
- }
2177
+ EventDelegator.clearRangeEvents(ctx.id);
2178
+ ctx.signature.value = 0;
2179
+ }
2180
+
2181
+ // src/module-loader.ts
2182
+ var config = {
2183
+ rootId: "root",
2184
+ routeMode: "history",
2185
+ hashbang: "#!",
2186
+ error: (error) => {
2187
+ throw error;
2188
+ }
2189
+ };
2190
+ function use(names, callback) {
2191
+ const nameList = typeof names === "string" ? [names] : names;
2192
+ const loadPromise = (() => {
2193
+ if (config.require) {
2194
+ const result = config.require(nameList);
2195
+ if (result && typeof result.then === "function") {
2196
+ return result;
2633
2197
  }
2198
+ return Promise.resolve([]);
2634
2199
  }
2635
- return ChildView;
2636
- }
2637
- /**
2638
- * Merge mixins into View prototype.
2639
- */
2640
- static merge(...mixins) {
2641
- const existingCtors = this.makes || [];
2642
- _View.mergeMixins(mixins, this, existingCtors);
2643
- return this;
2644
- }
2645
- // ============================================================
2646
- // HMR support (static accept / dispose)
2647
- // ============================================================
2648
- /**
2649
- * Set up HMR accept handler for this view module.
2650
- *
2651
- * When the module is hot-replaced, the new View class is extracted from
2652
- * the new module, registered in the view registry, and all currently
2653
- * mounted frames using this viewPath are re-mounted.
2654
- *
2655
- * No-op when `hot` is undefined (production / non-HMR environment).
2656
- *
2657
- * ```ts
2658
- * if (import.meta.hot) {
2659
- * HomeView.accept(import.meta.hot, 'home');
2660
- * }
2661
- * ```
2662
- */
2663
- static accept(hot, viewPath) {
2664
- if (!hot) return;
2665
- acceptView(hot, viewPath);
2200
+ return Promise.all(
2201
+ nameList.map((name) => {
2202
+ const importPath = name.startsWith(".") || name.startsWith("/") ? name : `./${name}`;
2203
+ return import(
2204
+ /* @vite-ignore */
2205
+ /* webpackIgnore: true */
2206
+ importPath
2207
+ ).then((mod) => {
2208
+ return mod && (mod["__esModule"] || // For Webpack
2209
+ typeof mod["default"] === "function") ? mod["default"] : mod;
2210
+ }).catch((err) => {
2211
+ const errorHandler = config.error;
2212
+ if (errorHandler) {
2213
+ errorHandler(err instanceof Error ? err : new Error(String(err)));
2214
+ }
2215
+ return void 0;
2216
+ });
2217
+ })
2218
+ );
2219
+ })();
2220
+ if (callback) {
2221
+ loadPromise.then((modules) => {
2222
+ callback(...modules);
2223
+ });
2666
2224
  }
2667
- /**
2668
- * Set up HMR dispose handler for this view module.
2669
- *
2670
- * When the module is about to be replaced, the old View class is removed
2671
- * from the registry so subsequent lookups don't return the stale class.
2672
- *
2673
- * No-op when `hot` is undefined (production / non-HMR environment).
2674
- *
2675
- * ```ts
2676
- * if (import.meta.hot) {
2677
- * HomeView.dispose(import.meta.hot, 'home');
2678
- * }
2679
- * ```
2680
- */
2681
- static dispose(hot, viewPath) {
2682
- if (!hot) return;
2683
- disposeView(hot, viewPath);
2225
+ return loadPromise;
2226
+ }
2227
+
2228
+ // src/view-registry.ts
2229
+ var viewSetupRegistry = {};
2230
+ function getViewClass(path) {
2231
+ return viewSetupRegistry[path];
2232
+ }
2233
+ function registerViewClass(viewPath, setup) {
2234
+ const parsed = parseUri(viewPath);
2235
+ const path = parsed.path;
2236
+ if (path) {
2237
+ viewSetupRegistry[path] = setup;
2684
2238
  }
2685
- };
2239
+ }
2686
2240
 
2687
2241
  // src/frame.ts
2242
+ var frameRegistry = /* @__PURE__ */ new Map();
2688
2243
  var rootFrame;
2689
2244
  var globalAlter;
2690
- var MAX_FRAME_POOL = 64;
2691
- var frameCache = [];
2692
- var staticEmitter = new EventEmitter();
2693
- var Frame = class _Frame extends EventEmitter {
2694
- /** Frame ID (same as owner DOM element ID) */
2695
- id;
2696
- /** Parent Frame ID */
2697
- _parentId = void 0;
2698
- get parentId() {
2699
- return this._parentId;
2700
- }
2701
- /** Children map: id -> id */
2702
- childrenMap = {};
2703
- /** Children count */
2704
- childrenCount = 0;
2705
- /** Ready count (children that have fired 'created') */
2706
- readyCount = 0;
2707
- /** Set of child frame IDs that have fired 'created' */
2708
- readyMap = /* @__PURE__ */ new Set();
2709
- /** View instance */
2710
- viewInstance;
2711
- /** Get view instance (read-only) */
2712
- get view() {
2713
- return this.viewInstance;
2714
- }
2715
- /** Invoke list for deferred method calls */
2716
- invokeList = [];
2717
- /** Signature for async operation tracking */
2718
- signature = 1;
2719
- /** Whether view has altered */
2720
- hasAltered = 0;
2721
- /** Whether view is destroyed */
2722
- destroyed = 0;
2723
- /** View path (v-lark attribute value) */
2724
- viewPath;
2725
- /** Original template before mount */
2726
- originalTemplate;
2727
- /** Hold fire created flag */
2728
- holdFireCreated = 0;
2729
- /** Children created flag */
2730
- childrenCreated = 0;
2731
- /** Children alter flag */
2732
- childrenAlter = 0;
2733
- constructor(id, parentId) {
2734
- super();
2735
- this.id = id;
2736
- if (parentId) {
2737
- this._parentId = parentId;
2738
- }
2739
- registerFrame(id, this);
2740
- const element = document.getElementById(id);
2741
- if (element) {
2742
- element.frame = this;
2743
- element.frameBound = 1;
2744
- }
2745
- _Frame.fire("add", { frame: this });
2746
- }
2747
- // ============================================================
2748
- // Instance methods
2749
- // ============================================================
2750
- /**
2751
- * Mount a view to this frame.
2752
- *
2753
- * Complete flow:
2754
- * 1. Parse viewPath, translate query params from parent
2755
- * 2. Unmount current view
2756
- * 3. Load View class (via require or provided ViewClass)
2757
- * 4. View_Prepare (scan event methods)
2758
- * 5. Create View instance
2759
- * 6. View_DelegateEvents (bind DOM events)
2760
- * 7. Call view.init()
2761
- * 8. If view has template, call render via Updater
2762
- * 9. If no template, call endUpdate directly
2763
- */
2764
- mountView(viewPath, viewInitParams) {
2765
- const node = document.getElementById(this.id);
2766
- const pId = this.parentId;
2767
- if (!this.hasAltered && node) {
2768
- this.hasAltered = 1;
2769
- this.originalTemplate = node.innerHTML;
2770
- }
2771
- this.unmountView();
2772
- this.destroyed = 0;
2773
- const parsed = parseUri(viewPath || "");
2774
- const viewClassName = parsed.path;
2775
- if (!node || !viewClassName) return;
2776
- this.viewPath = viewPath;
2777
- const params = parsed["params"];
2778
- translateQuery(pId || this.id, viewPath, params);
2779
- const initParams = { ...params };
2780
- if (viewInitParams) {
2781
- assign(initParams, viewInitParams);
2782
- }
2783
- const sign = this.signature;
2784
- const registered = getViewClass(viewClassName);
2785
- if (registered) {
2786
- this.doMountView(registered, initParams, node, sign);
2787
- return;
2788
- }
2789
- use(viewClassName, (ViewClass) => {
2790
- if (sign !== this.signature) return;
2791
- if (typeof ViewClass === "function") {
2792
- const ViewClassTyped = ViewClass;
2793
- registerViewClass(viewClassName, ViewClassTyped);
2794
- this.doMountView(ViewClassTyped, initParams, node, sign);
2795
- } else {
2796
- const error = new Error(`Cannot load view: ${viewClassName}`);
2797
- const errorHandler = config.error;
2798
- if (errorHandler) {
2799
- errorHandler(error);
2800
- }
2245
+ var staticEmitter = createEmitter();
2246
+ function isViewSetup(fn) {
2247
+ return typeof fn === "function";
2248
+ }
2249
+ function createFrame(id, parentId) {
2250
+ const emitter2 = createEmitter();
2251
+ const invokeList = [];
2252
+ const childrenMap = {};
2253
+ const readyMap = /* @__PURE__ */ new Set();
2254
+ let viewPath;
2255
+ function getViewPath() {
2256
+ return viewPath;
2257
+ }
2258
+ const frame = {
2259
+ id,
2260
+ getViewPath,
2261
+ parentId,
2262
+ view: void 0,
2263
+ invokeList,
2264
+ signature: 1,
2265
+ destroyed: 0,
2266
+ hasAltered: 0,
2267
+ originalTemplate: void 0,
2268
+ holdFireCreated: 0,
2269
+ childrenCreated: 0,
2270
+ childrenAlter: 0,
2271
+ childrenMap,
2272
+ childrenCount: 0,
2273
+ readyCount: 0,
2274
+ readyMap,
2275
+ emitter: emitter2,
2276
+ mountView(viewPathArg, viewInitParams) {
2277
+ const node = document.getElementById(frame.id);
2278
+ const pId = frame.parentId;
2279
+ if (!frame.hasAltered && node) {
2280
+ frame.hasAltered = 1;
2281
+ frame.originalTemplate = node.innerHTML;
2282
+ }
2283
+ frame.unmountView();
2284
+ frame.destroyed = 0;
2285
+ const parsed = parseUri(viewPathArg || "");
2286
+ const viewClassName = parsed.path;
2287
+ if (!node || !viewClassName) return;
2288
+ viewPath = viewPathArg;
2289
+ const params = parsed.params;
2290
+ translateQuery(pId ?? frame.id, viewPathArg, params);
2291
+ const initParams = { ...params };
2292
+ if (viewInitParams) {
2293
+ assign(initParams, viewInitParams);
2294
+ }
2295
+ const sign = frame.signature;
2296
+ const registered = getViewClass(viewClassName);
2297
+ if (registered) {
2298
+ doMountView(registered, initParams, node, sign);
2299
+ return;
2801
2300
  }
2802
- });
2803
- }
2804
- /**
2805
- * Internal: actually mount the view after class is loaded.
2806
- */
2807
- doMountView(ViewClass, params, node, sign) {
2808
- if (sign !== this.signature) return;
2809
- const mixinConstructors = View.prepare(ViewClass);
2810
- const Constructor = ViewClass;
2811
- const view = new Constructor(
2812
- this.id,
2813
- this,
2814
- params,
2815
- node,
2816
- mixinConstructors
2817
- );
2818
- this.viewInstance = view;
2819
- view.signature = 1;
2820
- View.delegateEvents(view);
2821
- const initResult = funcWithTry(
2822
- view.init,
2823
- [params, { node, deep: !view.template }],
2824
- view,
2825
- noop
2826
- );
2827
- const nextSign = ++this.signature;
2828
- Promise.resolve(initResult).then(() => {
2829
- if (nextSign !== this.signature) return;
2830
- if (view.template) {
2831
- view.render();
2832
- } else {
2833
- this.hasAltered = 0;
2834
- if (!view.endUpdatePendingFlag) {
2835
- view.endUpdate();
2301
+ use(viewClassName, (loadedModule) => {
2302
+ if (sign !== frame.signature) return;
2303
+ if (isViewSetup(loadedModule)) {
2304
+ registerViewClass(viewClassName, loadedModule);
2305
+ doMountView(loadedModule, initParams, node, sign);
2306
+ } else {
2307
+ const error = new Error(`Cannot load view: ${viewClassName}`);
2308
+ const errorHandler = config.error;
2309
+ if (errorHandler) {
2310
+ errorHandler(error);
2311
+ }
2836
2312
  }
2313
+ });
2314
+ },
2315
+ unmountView() {
2316
+ const currentView = frame.view;
2317
+ frame.invokeList.length = 0;
2318
+ if (!currentView) return;
2319
+ if (!globalAlter) {
2320
+ globalAlter = { id: frame.id };
2321
+ }
2322
+ frame.destroyed = 1;
2323
+ frame.unmountZone();
2324
+ notifyAlter(frame, globalAlter);
2325
+ unmountCtx(currentView);
2326
+ frame.view = void 0;
2327
+ const node = document.getElementById(frame.id);
2328
+ if (node && frame.originalTemplate) {
2329
+ node.innerHTML = frame.originalTemplate;
2330
+ }
2331
+ globalAlter = void 0;
2332
+ unmark(currentView);
2333
+ },
2334
+ mountFrame(frameId, viewPathArg, viewInitParams) {
2335
+ notifyAlter(frame, { id: frameId });
2336
+ let childFrame = frameRegistry.get(frameId);
2337
+ if (!childFrame) {
2338
+ if (!frame.childrenMap[frameId]) {
2339
+ frame.childrenCount++;
2340
+ }
2341
+ frame.childrenMap[frameId] = frameId;
2342
+ childFrame = createFrame(frameId, frame.id);
2837
2343
  }
2838
- });
2839
- }
2840
- /**
2841
- * Unmount current view.
2842
- */
2843
- unmountView() {
2844
- const view = this.view;
2845
- this.invokeList = [];
2846
- if (!view) return;
2847
- if (!globalAlter) {
2848
- globalAlter = { id: this.id };
2849
- }
2850
- this.destroyed = 1;
2851
- this.unmountZone();
2852
- notifyAlter(this, globalAlter);
2853
- if (view.signature > 0) {
2854
- view.fire("destroy", void 0, true, true);
2855
- }
2856
- EventDelegator.clearRangeEvents(this.id);
2857
- delete this["viewInstance"];
2858
- const node = document.getElementById(this.id);
2859
- if (node && this.originalTemplate) {
2860
- node.innerHTML = this.originalTemplate;
2861
- }
2862
- globalAlter = void 0;
2863
- unmark(view);
2864
- }
2865
- /**
2866
- * Mount a child frame.
2867
- */
2868
- mountFrame(frameId, viewPath, viewInitParams) {
2869
- notifyAlter(this, { id: frameId });
2870
- let childFrame = getFrame(frameId);
2871
- if (!childFrame) {
2872
- if (!this.childrenMap[frameId]) {
2873
- this.childrenCount++;
2874
- }
2875
- this.childrenMap[frameId] = frameId;
2876
- childFrame = frameCache.pop();
2877
- if (childFrame) {
2878
- reInitFrame(childFrame, frameId, this.id);
2879
- } else {
2880
- childFrame = new _Frame(frameId, this.id);
2344
+ childFrame.mountView(viewPathArg, viewInitParams);
2345
+ return childFrame;
2346
+ },
2347
+ unmountFrame(id2) {
2348
+ const targetId = id2 ? frame.childrenMap[id2] : frame.id;
2349
+ const targetFrame = frameRegistry.get(targetId);
2350
+ if (!targetFrame) return;
2351
+ const wasCreated = targetFrame.readyCount > 0;
2352
+ const pId = targetFrame.parentId;
2353
+ targetFrame.unmountView();
2354
+ removeFrame(targetId, wasCreated);
2355
+ const parent = frameRegistry.get(pId ?? "");
2356
+ if (parent && parent.childrenMap[targetId]) {
2357
+ Reflect.deleteProperty(parent.childrenMap, targetId);
2358
+ parent.childrenCount--;
2359
+ notifyCreated(parent);
2881
2360
  }
2882
- }
2883
- childFrame.mountView(viewPath, viewInitParams);
2884
- return childFrame;
2885
- }
2886
- /**
2887
- * Unmount a child frame.
2888
- */
2889
- unmountFrame(id) {
2890
- const targetId = id ? this.childrenMap[id] : this.id;
2891
- const frame = getFrame(targetId);
2892
- if (!frame) return;
2893
- const wasCreated = frame.readyCount > 0;
2894
- const pId = frame.parentId;
2895
- frame.unmountView();
2896
- removeFrame2(targetId, wasCreated);
2897
- reInitFrameForCache(frame);
2898
- if (frameCache.length < MAX_FRAME_POOL) {
2899
- frameCache.push(frame);
2900
- }
2901
- const parent = getFrame(pId || "");
2902
- if (parent && parent.childrenMap[targetId]) {
2903
- Reflect.deleteProperty(parent.childrenMap, targetId);
2904
- parent.childrenCount--;
2905
- notifyCreated(parent);
2906
- }
2907
- }
2908
- /**
2909
- * Mount all views in a zone.
2910
- */
2911
- mountZone(zoneId) {
2912
- const targetZone = zoneId || this.id;
2913
- this.holdFireCreated = 1;
2914
- const rootEl = document.getElementById(targetZone);
2915
- if (!rootEl) return;
2916
- const viewElements = rootEl.querySelectorAll(`[${LARK_VIEW}]`);
2917
- const frames = [];
2918
- viewElements.forEach((el) => {
2919
- if (!(el instanceof HTMLElement)) return;
2920
- if (htmlElIsBound(el)) return;
2921
- const elId = ensureElementId(el, "frame_");
2922
- el.frameBound = 1;
2923
- const viewPath = getAttribute(el, LARK_VIEW);
2924
- frames.push([elId, viewPath]);
2925
- });
2926
- for (const [frameId, viewPath] of frames) {
2927
- this.mountFrame(frameId, viewPath);
2928
- }
2929
- this.holdFireCreated = 0;
2930
- notifyCreated(this);
2931
- }
2932
- /**
2933
- * Unmount all views in a zone.
2934
- */
2935
- unmountZone(zoneId) {
2936
- for (const childId in this.childrenMap) {
2937
- if (hasOwnProperty(this.childrenMap, childId)) {
2938
- if (!zoneId || childId !== zoneId) {
2939
- this.unmountFrame(childId);
2361
+ },
2362
+ mountZone(zoneId) {
2363
+ const targetZone = zoneId ?? frame.id;
2364
+ frame.holdFireCreated = 1;
2365
+ const rootEl = document.getElementById(targetZone);
2366
+ if (!rootEl) return;
2367
+ const viewElements = rootEl.querySelectorAll(`[${LARK_VIEW}]`);
2368
+ const frames = [];
2369
+ viewElements.forEach((el) => {
2370
+ if (!(el instanceof HTMLElement)) return;
2371
+ if (htmlElIsBound(el)) return;
2372
+ const elId = ensureElementId(el, "frame_");
2373
+ Reflect.set(el, "frameBound", 1);
2374
+ const viewPathArg = getAttribute(el, LARK_VIEW);
2375
+ if (viewPathArg) {
2376
+ frames.push([elId, viewPathArg]);
2940
2377
  }
2378
+ });
2379
+ for (const [frameId, viewPathArg] of frames) {
2380
+ frame.mountFrame(frameId, viewPathArg);
2941
2381
  }
2942
- }
2943
- notifyCreated(this);
2944
- }
2945
- /**
2946
- * Get all child frame IDs.
2947
- */
2948
- children() {
2949
- const result = [];
2950
- for (const id in this.childrenMap) {
2951
- if (hasOwnProperty(this.childrenMap, id)) {
2952
- result.push(id);
2953
- }
2954
- }
2955
- return result;
2956
- }
2957
- /**
2958
- * Get parent frame at given level.
2959
- * @param level - How many levels up (default 1)
2960
- */
2961
- parent(level = 1) {
2962
- let frame = void 0;
2963
- let currentPid = this.parentId;
2964
- let n = level >>> 0 || 1;
2965
- while (currentPid && n--) {
2966
- frame = getFrame(currentPid);
2967
- currentPid = frame?.parentId;
2968
- }
2969
- return frame;
2970
- }
2971
- /**
2972
- * Invoke a method on the view.
2973
- */
2974
- invoke(name, args) {
2975
- let result;
2976
- const view = this.view;
2977
- if (view && view.rendered) {
2978
- const fn = Reflect.get(view, name);
2979
- if (typeof fn === "function") {
2980
- result = funcWithTry(fn, args || [], view, noop);
2382
+ frame.holdFireCreated = 0;
2383
+ notifyCreated(frame);
2384
+ },
2385
+ unmountZone(zoneId) {
2386
+ for (const childId in frame.childrenMap) {
2387
+ if (hasOwnProperty(frame.childrenMap, childId)) {
2388
+ if (!zoneId || childId !== zoneId) {
2389
+ frame.unmountFrame(childId);
2390
+ }
2391
+ }
2981
2392
  }
2982
- } else {
2983
- const key = SPLITTER + name;
2984
- let existingEntry;
2985
- for (const entry of this.invokeList) {
2986
- if (entry.key === key) {
2987
- existingEntry = entry;
2988
- break;
2393
+ notifyCreated(frame);
2394
+ },
2395
+ parent(level = 1) {
2396
+ let result = void 0;
2397
+ let currentPid = frame.parentId;
2398
+ let n = level >>> 0 || 1;
2399
+ while (currentPid && n--) {
2400
+ result = frameRegistry.get(currentPid);
2401
+ currentPid = result?.parentId;
2402
+ }
2403
+ return result;
2404
+ },
2405
+ invoke(name, args) {
2406
+ let result;
2407
+ const currentView = frame.view;
2408
+ if (currentView && currentView.rendered.value) {
2409
+ const fn = Reflect.get(currentView, name);
2410
+ if (typeof fn === "function") {
2411
+ result = funcWithTry(fn, args ?? [], currentView, noop);
2412
+ }
2413
+ } else {
2414
+ const key = SPLITTER + name;
2415
+ let existingEntry;
2416
+ for (const entry of frame.invokeList) {
2417
+ if (entry.key === key) {
2418
+ existingEntry = entry;
2419
+ break;
2420
+ }
2989
2421
  }
2422
+ if (existingEntry) {
2423
+ existingEntry.removed = args === existingEntry.args;
2424
+ }
2425
+ const newEntry = {
2426
+ name,
2427
+ args: args ?? [],
2428
+ key
2429
+ };
2430
+ frame.invokeList.push(newEntry);
2990
2431
  }
2991
- if (existingEntry) {
2992
- existingEntry.removed = args === existingEntry.args;
2432
+ return result;
2433
+ },
2434
+ children() {
2435
+ const result = [];
2436
+ for (const id2 in frame.childrenMap) {
2437
+ if (hasOwnProperty(frame.childrenMap, id2)) {
2438
+ result.push(id2);
2439
+ }
2993
2440
  }
2994
- const newEntry = {
2995
- name,
2996
- args: args || [],
2997
- key
2998
- };
2999
- this.invokeList.push(newEntry);
2441
+ return result;
2442
+ },
2443
+ on(event, handler) {
2444
+ emitter2.on(event, handler);
2445
+ return frame;
2446
+ },
2447
+ off(event, handler) {
2448
+ emitter2.off(event, handler);
2449
+ return frame;
2450
+ },
2451
+ fire(event, data) {
2452
+ emitter2.fire(event, data);
2453
+ return frame;
3000
2454
  }
3001
- return result;
3002
- }
3003
- /**
3004
- * Type-safe variant of `invoke`.
3005
- *
3006
- * `invoke()` accepts any string and any args, which silently hides
3007
- * mismatched call sites when a method gets renamed. `invokeTyped` carries
3008
- * the view's method signature through TypeScript so the compiler catches
3009
- * those mistakes:
3010
- *
3011
- * ```ts
3012
- * type Home = View & { loadData(id: string): Promise<void> };
3013
- * frame.invokeTyped<Home, "loadData">("loadData", ["user-1"]);
3014
- * ```
3015
- *
3016
- * Behavior is identical to `invoke` at runtime — same defer / direct-call
3017
- * paths — so it's a drop-in safer overload.
3018
- */
3019
- invokeTyped(name, args) {
3020
- return this.invoke(name, args);
3021
- }
3022
- // ============================================================
3023
- // Static methods
3024
- // ============================================================
2455
+ };
2456
+ frameRegistry.set(id, frame);
2457
+ const element = document.getElementById(id);
2458
+ if (element) {
2459
+ Reflect.set(element, "frame", frame);
2460
+ Reflect.set(element, "frameBound", 1);
2461
+ }
2462
+ staticEmitter.fire("add", { frame });
2463
+ return frame;
2464
+ }
2465
+ function doMountView(setup, params, node, sign) {
2466
+ const frameId = node.id;
2467
+ const frame = frameRegistry.get(frameId);
2468
+ if (!frame) return;
2469
+ if (sign !== frame.signature) return;
2470
+ const ctx = mountCtx(frame, setup, params);
2471
+ frame.view = ctx;
2472
+ runInvokes(frame);
2473
+ }
2474
+ var Frame = {
3025
2475
  /** Get frame by ID */
3026
- static get(id) {
3027
- return getFrame(id);
3028
- }
2476
+ get(id) {
2477
+ return frameRegistry.get(id);
2478
+ },
3029
2479
  /** Get all frames */
3030
- static getAll() {
3031
- return getAllFrames();
3032
- }
2480
+ getAll() {
2481
+ return frameRegistry;
2482
+ },
3033
2483
  /**
3034
- * Returns the existing root frame, or `undefined` if none has been created.
3035
- * Pure getter — never creates a Frame, never touches the DOM.
3036
- *
3037
- * Use `Frame.createRoot(id)` to create the root explicitly during framework
3038
- * boot. For Micro-Frontend hosts that own multiple independent containers,
3039
- * use `new Frame(containerId)` directly so each MF mount has its own root.
2484
+ * Returns the existing root frame, or undefined if none has been created.
3040
2485
  */
3041
- static getRoot() {
2486
+ getRoot() {
3042
2487
  return rootFrame;
3043
- }
2488
+ },
3044
2489
  /**
3045
- * Create (or return) the singleton root frame for this app.
3046
- *
3047
- * Idempotent: subsequent calls always return the original root regardless
3048
- * of `rootId` — so passing a different id later is silently ignored.
3049
- * `Framework.boot()` is the canonical caller; user code rarely needs this.
2490
+ * Create (or return) the singleton root frame.
2491
+ * Idempotent: subsequent calls always return the original root.
3050
2492
  */
3051
- static createRoot(rootId) {
2493
+ createRoot(rootId) {
3052
2494
  if (!rootFrame) {
3053
- rootId = rootId || "root";
3054
- let rootElement = document.getElementById(rootId);
2495
+ const id = rootId ?? "root";
2496
+ let rootElement = document.getElementById(id);
3055
2497
  if (!rootElement) {
3056
2498
  rootElement = document.body;
3057
- rootElement.id = rootId;
2499
+ rootElement.id = id;
3058
2500
  }
3059
- rootFrame = new _Frame(rootId);
2501
+ rootFrame = createFrame(id);
3060
2502
  }
3061
2503
  return rootFrame;
3062
- }
2504
+ },
3063
2505
  /** Bind event listener (static) */
3064
- static on(event, handler) {
2506
+ on(event, handler) {
3065
2507
  staticEmitter.on(event, handler);
3066
- return _Frame;
3067
- }
2508
+ return Frame;
2509
+ },
3068
2510
  /** Unbind event listener (static) */
3069
- static off(event, handler) {
2511
+ off(event, handler) {
3070
2512
  staticEmitter.off(event, handler);
3071
- return _Frame;
3072
- }
2513
+ return Frame;
2514
+ },
3073
2515
  /** Fire event (static) */
3074
- static fire(event, data) {
2516
+ fire(event, data) {
3075
2517
  staticEmitter.fire(event, data);
3076
2518
  }
3077
2519
  };
3078
2520
  function htmlElIsBound(element) {
3079
- return !!element.frameBound;
2521
+ return !!Reflect.get(element, "frameBound");
3080
2522
  }
3081
- function removeFrame2(id, wasCreated) {
3082
- const frameInstance = getFrame(id);
2523
+ function removeFrame(id, wasCreated) {
2524
+ const frameInstance = frameRegistry.get(id);
3083
2525
  if (!frameInstance) return;
3084
- removeFrame(id);
3085
- Frame.fire("remove", { frame: frameInstance, fcc: wasCreated });
2526
+ frameRegistry.delete(id);
2527
+ staticEmitter.fire("remove", { frame: frameInstance, fcc: wasCreated });
3086
2528
  const element = document.getElementById(id);
3087
2529
  if (element) {
3088
- element.frameBound = 0;
2530
+ Reflect.set(element, "frameBound", 0);
3089
2531
  Reflect.deleteProperty(element, "frame");
3090
2532
  }
3091
2533
  }
3092
2534
  function notifyCreated(frameInstance) {
3093
- if (!frameInstance["childrenCreated"] && !frameInstance["holdFireCreated"] && frameInstance["childrenCount"] === frameInstance["readyCount"]) {
3094
- if (!frameInstance["childrenCreated"]) {
3095
- frameInstance["childrenCreated"] = 1;
3096
- frameInstance["childrenAlter"] = 0;
3097
- frameInstance.fire("created");
3098
- }
2535
+ if (!frameInstance.childrenCreated && !frameInstance.holdFireCreated && frameInstance.childrenCount === frameInstance.readyCount) {
2536
+ frameInstance.childrenCreated = 1;
2537
+ frameInstance.childrenAlter = 0;
2538
+ frameInstance.emitter.fire("created");
3099
2539
  const pId = frameInstance.parentId;
3100
2540
  if (pId) {
3101
- const parent = getFrame(pId);
2541
+ const parent = frameRegistry.get(pId);
3102
2542
  if (parent && !parent.readyMap.has(frameInstance.id)) {
3103
2543
  parent.readyMap.add(frameInstance.id);
3104
2544
  parent.readyCount++;
@@ -3108,13 +2548,13 @@ function notifyCreated(frameInstance) {
3108
2548
  }
3109
2549
  }
3110
2550
  function notifyAlter(frameInstance, data) {
3111
- if (!frameInstance["childrenAlter"] && frameInstance["childrenCreated"]) {
3112
- frameInstance["childrenCreated"] = 0;
3113
- frameInstance["childrenAlter"] = 1;
3114
- frameInstance.fire("alter", data);
2551
+ if (!frameInstance.childrenAlter && frameInstance.childrenCreated) {
2552
+ frameInstance.childrenCreated = 0;
2553
+ frameInstance.childrenAlter = 1;
2554
+ frameInstance.emitter.fire("alter", data);
3115
2555
  const pId = frameInstance.parentId;
3116
2556
  if (pId) {
3117
- const parent = getFrame(pId);
2557
+ const parent = frameRegistry.get(pId);
3118
2558
  if (parent && parent.readyMap.has(frameInstance.id)) {
3119
2559
  parent.readyCount--;
3120
2560
  parent.readyMap.delete(frameInstance.id);
@@ -3123,35 +2563,24 @@ function notifyAlter(frameInstance, data) {
3123
2563
  }
3124
2564
  }
3125
2565
  }
3126
- function reInitFrame(frame, id, parentId) {
3127
- Reflect.set(frame, "id", id);
3128
- frame["_parentId"] = parentId;
3129
- frame["childrenMap"] = {};
3130
- frame["childrenCount"] = 0;
3131
- frame["readyCount"] = 0;
3132
- frame["signature"] = 1;
3133
- frame["readyMap"] = /* @__PURE__ */ new Set();
3134
- frame["invokeList"] = [];
3135
- registerFrame(id, frame);
3136
- }
3137
- function reInitFrameForCache(frame) {
3138
- Reflect.set(frame, "id", "");
3139
- frame["_parentId"] = void 0;
3140
- frame["childrenMap"] = {};
3141
- frame["readyMap"] = /* @__PURE__ */ new Set();
3142
- }
3143
2566
  function translateQuery(pId, src, params) {
3144
- const parentFrame = getFrame(pId);
2567
+ const parentFrame = frameRegistry.get(pId);
3145
2568
  const parentView = parentFrame?.view;
3146
2569
  if (!parentView) return;
3147
2570
  const parentRefData = parentView.updater.refData;
3148
2571
  if (!parentRefData) return;
3149
2572
  if (src.indexOf(SPLITTER) > 0) {
3150
2573
  translateData(parentRefData, params);
3151
- const paramsRec = params;
3152
- const splitterValue = paramsRec[SPLITTER];
3153
- if (splitterValue && typeof splitterValue === "object") {
3154
- assign(params, splitterValue);
2574
+ const splitterValue = Reflect.get(params, SPLITTER);
2575
+ if (isRecord(splitterValue)) {
2576
+ for (const k in splitterValue) {
2577
+ if (hasOwnProperty(splitterValue, k)) {
2578
+ const v = splitterValue[k];
2579
+ if (typeof v === "string") {
2580
+ params[k] = v;
2581
+ }
2582
+ }
2583
+ }
3155
2584
  Reflect.deleteProperty(params, SPLITTER);
3156
2585
  }
3157
2586
  }
@@ -3166,10 +2595,10 @@ var FrameDevtoolBridge = {
3166
2595
  MSG_TREE_DELTA: "LARK_DEVTOOL_TREE_DELTA"
3167
2596
  };
3168
2597
  function serializeView(view) {
3169
- const evtMap = view.eventObjectMap;
2598
+ const evtMap = {};
3170
2599
  const eventMethodKeys = evtMap ? Object.keys(evtMap) : [];
3171
2600
  const resourceKeys = view.resources ? Object.keys(view.resources) : [];
3172
- const hasAssign = typeof view["assign"] === "function";
2601
+ const hasAssign = typeof view.getAssign() === "function";
3173
2602
  let updaterData = null;
3174
2603
  try {
3175
2604
  const ref = view.updater?.refData;
@@ -3185,14 +2614,14 @@ function serializeView(view) {
3185
2614
  return {
3186
2615
  id: view.id,
3187
2616
  rendered: !!view.rendered,
3188
- signature: view.signature,
3189
- observedStateKeys: view.observedStateKeys ?? null,
2617
+ signature: view.signature.value,
2618
+ observedStateKeys: view.getObservedStateKeys() ?? null,
3190
2619
  locationObserved: {
3191
2620
  flag: view.locationObserved.flag,
3192
2621
  keys: view.locationObserved.keys,
3193
2622
  observePath: view.locationObserved.observePath
3194
2623
  },
3195
- hasTemplate: !!view.template,
2624
+ hasTemplate: !!view.getTemplate(),
3196
2625
  eventMethodKeys,
3197
2626
  resourceKeys,
3198
2627
  hasAssign,
@@ -3213,7 +2642,7 @@ function serializeFrame(frameId) {
3213
2642
  return {
3214
2643
  id: frame.id,
3215
2644
  parentId: frame.parentId ?? null,
3216
- viewPath: frame.viewPath ?? null,
2645
+ viewPath: frame.getViewPath() ?? null,
3217
2646
  childrenCount: frame.childrenCount,
3218
2647
  readyCount: frame.readyCount,
3219
2648
  childrenCreated: frame.childrenCreated,
@@ -3258,10 +2687,7 @@ function installFrameDevtoolBridge() {
3258
2687
  if (type === FrameDevtoolBridge.MSG_PING) {
3259
2688
  const source = event.source;
3260
2689
  if (source) {
3261
- source.postMessage(
3262
- { type: FrameDevtoolBridge.MSG_PONG },
3263
- { targetOrigin: "*" }
3264
- );
2690
+ source.postMessage({ type: FrameDevtoolBridge.MSG_PONG }, { targetOrigin: "*" });
3265
2691
  }
3266
2692
  return;
3267
2693
  }
@@ -3289,10 +2715,7 @@ function pushTreeUpdate() {
3289
2715
  const treeJson = JSON.stringify(tree);
3290
2716
  if (treeJson !== lastTreeJson) {
3291
2717
  lastTreeJson = treeJson;
3292
- window.parent.postMessage(
3293
- { type: FrameDevtoolBridge.MSG_TREE_DELTA, data: tree },
3294
- "*"
3295
- );
2718
+ window.parent.postMessage({ type: FrameDevtoolBridge.MSG_TREE_DELTA, data: tree }, "*");
3296
2719
  }
3297
2720
  }
3298
2721
  export {