@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.cjs CHANGED
@@ -82,10 +82,7 @@ var URI_ENT_MAP = {
82
82
  };
83
83
  var URI_ENT_REGEXP = /[!')(*]/g;
84
84
  function encodeURIExtra(v) {
85
- return encodeURIComponent(strSafe(v)).replace(
86
- URI_ENT_REGEXP,
87
- (m) => URI_ENT_MAP[m]
88
- );
85
+ return encodeURIComponent(strSafe(v)).replace(URI_ENT_REGEXP, (m) => URI_ENT_MAP[m]);
89
86
  }
90
87
  var QUOTE_ENT_REGEXP = /['"\\]/g;
91
88
  function encodeQuote(v) {
@@ -114,15 +111,20 @@ function isRefToken(s) {
114
111
  var CALL_BREAK_TIME = 9;
115
112
  var callQueue = [];
116
113
  var callScheduled = false;
117
- var schedulerYield = (() => {
114
+ function getSchedulerYield() {
118
115
  try {
119
- if (typeof globalThis?.scheduler?.yield === "function") {
120
- return globalThis.scheduler.yield.bind(globalThis.scheduler);
116
+ const scheduler = Reflect.get(globalThis, "scheduler");
117
+ if (scheduler && typeof scheduler === "object" && typeof Reflect.get(scheduler, "yield") === "function") {
118
+ const yieldFn = Reflect.get(scheduler, "yield");
119
+ if (typeof yieldFn === "function") {
120
+ return yieldFn.bind(scheduler);
121
+ }
121
122
  }
122
123
  } catch {
123
124
  }
124
125
  return void 0;
125
- })();
126
+ }
127
+ var schedulerYield = getSchedulerYield();
126
128
  async function startCall() {
127
129
  callScheduled = false;
128
130
  const startTime = performance.now();
@@ -148,7 +150,9 @@ function scheduleNextChunk() {
148
150
  callScheduled = true;
149
151
  }
150
152
  function callFunction(fn, args) {
151
- callQueue.push(() => fn(...args));
153
+ callQueue.push(() => {
154
+ fn(...args);
155
+ });
152
156
  if (!callScheduled) {
153
157
  scheduleNextChunk();
154
158
  }
@@ -165,7 +169,6 @@ function asRecord(value) {
165
169
  if (isRecord(value)) {
166
170
  return value;
167
171
  }
168
- console.error("fallback to Object.fromEntries, even an empty object {}.");
169
172
  if (Array.isArray(value)) {
170
173
  return Object.fromEntries(value.entries());
171
174
  }
@@ -191,7 +194,7 @@ function assign(target, ...sources) {
191
194
  if (source) {
192
195
  for (const p in source) {
193
196
  if (hasOwnProperty(source, p)) {
194
- target[p] = source[p];
197
+ Reflect.set(target, p, source[p]);
195
198
  }
196
199
  }
197
200
  }
@@ -230,16 +233,16 @@ function translateData(data, value) {
230
233
  if (isPrimitive(value)) {
231
234
  const prop = String(value);
232
235
  if (isRefToken(prop) && hasOwnProperty(data, prop)) {
233
- return data[prop];
236
+ return Reflect.get(data, prop);
234
237
  }
235
238
  return value;
236
239
  }
237
240
  if (isPlainObject(value) || Array.isArray(value)) {
238
241
  for (const p in value) {
239
242
  if (hasOwnProperty(value, p)) {
240
- const val = value[p];
243
+ const val = Reflect.get(value, p);
241
244
  const newVal = translateData(data, val);
242
- value[p] = newVal;
245
+ Reflect.set(value, p, newVal);
243
246
  }
244
247
  }
245
248
  return value;
@@ -296,41 +299,30 @@ function toUri(path, params, keepEmpty) {
296
299
  }
297
300
 
298
301
  // src/event-emitter.ts
299
- var EventEmitter = class {
300
- /** Event listeners: prefixed key -> listener array */
301
- listeners = /* @__PURE__ */ new Map();
302
- /** Number of `fire()` calls currently on the stack (re-entrancy depth). */
303
- firingDepth = 0;
304
- /** Keys whose listener list needs compaction after firing settles. */
305
- pendingCompaction;
306
- /**
307
- * Bind event listener.
308
- */
309
- on(event, handler) {
302
+ function createEmitter() {
303
+ const listeners = /* @__PURE__ */ new Map();
304
+ let firingDepth = 0;
305
+ let pendingCompaction;
306
+ function on(event, handler) {
310
307
  const key = SPLITTER + event;
311
- let list = this.listeners.get(key);
308
+ let list = listeners.get(key);
312
309
  if (!list) {
313
310
  list = [];
314
- this.listeners.set(key, list);
311
+ listeners.set(key, list);
315
312
  }
316
313
  list.push({ handler, executing: 0 });
317
- return this;
314
+ return api;
318
315
  }
319
- /**
320
- * Unbind event listener.
321
- * If handler is provided, removes only that handler.
322
- * If no handler, removes all handlers for the event.
323
- */
324
- off(event, handler) {
316
+ function off(event, handler) {
325
317
  const key = SPLITTER + event;
326
318
  if (handler) {
327
- const list = this.listeners.get(key);
328
- if (!list) return this;
329
- if (this.firingDepth > 0) {
319
+ const list = listeners.get(key);
320
+ if (!list) return api;
321
+ if (firingDepth > 0) {
330
322
  for (const listener of list) {
331
323
  if (listener.handler === handler) {
332
324
  listener.handler = noop;
333
- (this.pendingCompaction ??= /* @__PURE__ */ new Set()).add(key);
325
+ (pendingCompaction ??= /* @__PURE__ */ new Set()).add(key);
334
326
  break;
335
327
  }
336
328
  }
@@ -341,35 +333,20 @@ var EventEmitter = class {
341
333
  break;
342
334
  }
343
335
  }
344
- if (list.length === 0) this.listeners.delete(key);
336
+ if (list.length === 0) listeners.delete(key);
345
337
  }
346
338
  } else {
347
- this.listeners.delete(key);
348
- Reflect.deleteProperty(
349
- this,
350
- `on${event[0].toUpperCase() + event.slice(1)}`
351
- );
339
+ listeners.delete(key);
340
+ Reflect.deleteProperty(internal, onMethodName(event));
352
341
  }
353
- return this;
342
+ return api;
354
343
  }
355
- /**
356
- * Fire event, execute all bound handlers. Safe for re-entrant `off()` calls
357
- * during dispatch: removed handlers are replaced with noop and compacted
358
- * after the outermost fire returns.
359
- *
360
- * @param event - Event name
361
- * @param data - Event data (type property added automatically)
362
- * @param remove - Whether to remove all handlers after firing
363
- * @param lastToFirst - Whether to execute handlers in reverse order
364
- */
365
- fire(event, data, remove, lastToFirst) {
344
+ function fire(event, data, remove, lastToFirst) {
366
345
  const key = SPLITTER + event;
367
- const list = this.listeners.get(key);
368
- if (!data) {
369
- data = {};
370
- }
371
- data["type"] = event;
372
- this.firingDepth++;
346
+ const list = listeners.get(key);
347
+ const eventData = data ?? {};
348
+ eventData["type"] = event;
349
+ firingDepth++;
373
350
  try {
374
351
  if (list) {
375
352
  const len = list.length;
@@ -379,35 +356,45 @@ var EventEmitter = class {
379
356
  if (!listener) continue;
380
357
  if (listener.handler === noop) continue;
381
358
  listener.executing = 1;
382
- funcWithTry([listener.handler], [data], this, noop);
359
+ funcWithTry([listener.handler], [eventData], null, noop);
383
360
  listener.executing = "";
384
361
  }
385
362
  }
386
- const onMethodName = `on${event[0].toUpperCase() + event.slice(1)}`;
387
- const onMethod = this[onMethodName];
363
+ const onMethod = internal[onMethodName(event)];
388
364
  if (typeof onMethod === "function") {
389
- funcWithTry([onMethod], [data], this, noop);
365
+ funcWithTry([onMethod], [eventData], null, noop);
390
366
  }
391
367
  if (remove) {
392
- this.off(event);
368
+ off(event);
393
369
  }
394
370
  } finally {
395
- this.firingDepth--;
396
- if (this.firingDepth === 0 && this.pendingCompaction) {
397
- for (const k of this.pendingCompaction) {
398
- const l = this.listeners.get(k);
371
+ firingDepth--;
372
+ if (firingDepth === 0 && pendingCompaction) {
373
+ for (const k of pendingCompaction) {
374
+ const l = listeners.get(k);
399
375
  if (!l) continue;
400
376
  for (let i = l.length - 1; i >= 0; i--) {
401
377
  if (l[i].handler === noop) l.splice(i, 1);
402
378
  }
403
- if (l.length === 0) this.listeners.delete(k);
379
+ if (l.length === 0) listeners.delete(k);
404
380
  }
405
- this.pendingCompaction = void 0;
381
+ pendingCompaction = void 0;
406
382
  }
407
383
  }
408
- return this;
384
+ return api;
409
385
  }
410
- };
386
+ function onMethodName(event) {
387
+ return "on" + event[0].toUpperCase() + event.slice(1);
388
+ }
389
+ const internal = {
390
+ on,
391
+ off,
392
+ fire,
393
+ listeners
394
+ };
395
+ const api = internal;
396
+ return api;
397
+ }
411
398
 
412
399
  // src/mark.ts
413
400
  var hostStore = /* @__PURE__ */ new WeakMap();
@@ -425,68 +412,41 @@ function unmark(host) {
425
412
  function sortCacheEntries(a, b) {
426
413
  return b.frequency - a.frequency || b.lastTimestamp - a.lastTimestamp;
427
414
  }
428
- var Cache = class {
429
- /** Cache entries array */
430
- entries = [];
431
- /** Fast lookup: prefixed key -> entry */
432
- lookup = /* @__PURE__ */ new Map();
433
- /** Buffer size for eviction */
434
- bufferSize;
435
- /** Maximum cache size */
436
- maxSize;
437
- /** Total capacity (maxSize + bufferSize) */
438
- capacity;
439
- /** Callback when entry is removed */
440
- onRemove;
441
- /** Sort comparator */
442
- comparator;
443
- constructor(options = {}) {
444
- this.maxSize = options.maxSize ?? 20;
445
- this.bufferSize = options.bufferSize ?? 5;
446
- this.capacity = this.maxSize + this.bufferSize;
447
- this.onRemove = options.onRemove;
448
- this.comparator = options.sortComparator ?? sortCacheEntries;
449
- }
450
- /** Prefix a key with SPLITTER for namespace isolation */
451
- prefixKey(key) {
415
+ function createCache(options = {}) {
416
+ let entries = [];
417
+ const lookup = /* @__PURE__ */ new Map();
418
+ const maxSize = options.maxSize ?? 20;
419
+ const bufferSize = options.bufferSize ?? 5;
420
+ const capacity = maxSize + bufferSize;
421
+ const onRemove = options.onRemove;
422
+ const comparator = options.sortComparator ?? sortCacheEntries;
423
+ function prefixKey(key) {
452
424
  return SPLITTER + key;
453
425
  }
454
- /**
455
- * Get a cached value by key.
456
- * Updates frequency and timestamp for cache sorting.
457
- */
458
- get(key) {
459
- const prefixedKey = this.prefixKey(key);
460
- const entry = this.lookup.get(prefixedKey);
426
+ function get(key) {
427
+ const prefixedKey = prefixKey(key);
428
+ const entry = lookup.get(prefixedKey);
461
429
  if (!entry) return void 0;
462
430
  entry.frequency++;
463
431
  entry.lastTimestamp = nextCounter();
464
432
  return entry.value;
465
433
  }
466
- /**
467
- * Iterate all cached values.
468
- */
469
- forEach(callback) {
470
- for (const entry of this.entries) {
434
+ function forEach(callback) {
435
+ for (const entry of entries) {
471
436
  callback(entry.value);
472
437
  }
473
438
  }
474
- /**
475
- * Set or update a cached value.
476
- * If key already exists, updates value and increments frequency.
477
- * If cache exceeds capacity, triggers eviction.
478
- */
479
- set(key, value) {
480
- const prefixedKey = this.prefixKey(key);
481
- const existing = this.lookup.get(prefixedKey);
439
+ function set(key, value) {
440
+ const prefixedKey = prefixKey(key);
441
+ const existing = lookup.get(prefixedKey);
482
442
  if (existing) {
483
443
  existing.value = value;
484
444
  existing.frequency++;
485
445
  existing.lastTimestamp = nextCounter();
486
446
  return;
487
447
  }
488
- if (this.entries.length >= this.capacity) {
489
- this.evictEntries();
448
+ if (entries.length >= capacity) {
449
+ evictEntries();
490
450
  }
491
451
  const entry = {
492
452
  originalKey: key,
@@ -494,87 +454,76 @@ var Cache = class {
494
454
  frequency: 1,
495
455
  lastTimestamp: nextCounter()
496
456
  };
497
- this.entries.push(entry);
498
- this.lookup.set(prefixedKey, entry);
457
+ entries.push(entry);
458
+ lookup.set(prefixedKey, entry);
499
459
  }
500
- /**
501
- * Delete a cached entry. Removes it immediately from both the lookup map
502
- * and the entries array so the GC can reclaim the value without waiting
503
- * for the next eviction sweep.
504
- */
505
- del(key) {
506
- const prefixedKey = this.prefixKey(key);
507
- const entry = this.lookup.get(prefixedKey);
460
+ function del(key) {
461
+ const prefixedKey = prefixKey(key);
462
+ const entry = lookup.get(prefixedKey);
508
463
  if (!entry) return;
509
- this.lookup.delete(prefixedKey);
510
- const idx = this.entries.indexOf(entry);
511
- if (idx !== -1) this.entries.splice(idx, 1);
512
- if (this.onRemove) {
513
- this.onRemove(key);
464
+ lookup.delete(prefixedKey);
465
+ const idx = entries.indexOf(entry);
466
+ if (idx !== -1) entries.splice(idx, 1);
467
+ if (onRemove) {
468
+ onRemove(key);
514
469
  }
515
470
  }
516
- /**
517
- * Check if a key exists in cache.
518
- */
519
- has(key) {
520
- return this.lookup.has(this.prefixKey(key));
471
+ function has(key) {
472
+ return lookup.has(prefixKey(key));
521
473
  }
522
- /** Get current cache size */
523
- get size() {
524
- return this.entries.length;
525
- }
526
- /** Clear all entries */
527
- clear() {
528
- if (this.onRemove) {
529
- for (const entry of this.entries) {
530
- this.onRemove(entry.originalKey);
474
+ function clear() {
475
+ if (onRemove) {
476
+ for (const entry of entries) {
477
+ onRemove(entry.originalKey);
531
478
  }
532
479
  }
533
- this.entries = [];
534
- this.lookup.clear();
480
+ entries = [];
481
+ lookup.clear();
535
482
  }
536
- /**
537
- * Evict the `bufferSize` worst entries from the cache.
538
- *
539
- * Uses single-pass partial selection (O(n·k)) instead of sorting the entire
540
- * `entries` array (O(n log n)). For the typical `bufferSize = 5` this is
541
- * effectively a linear scan with at most 5 in-bucket comparisons per
542
- * iteration — and it avoids mutating the rest of `entries`.
543
- */
544
- evictEntries() {
545
- const entries = this.entries;
546
- const k = this.bufferSize;
547
- if (k <= 0 || entries.length === 0) return;
548
- if (entries.length <= k) {
483
+ function evictEntries() {
484
+ if (bufferSize <= 0 || entries.length === 0) return;
485
+ if (entries.length <= bufferSize) {
549
486
  for (const e of entries) {
550
- this.lookup.delete(this.prefixKey(e.originalKey));
551
- if (this.onRemove) this.onRemove(e.originalKey);
487
+ lookup.delete(prefixKey(e.originalKey));
488
+ if (onRemove) onRemove(e.originalKey);
552
489
  }
553
- this.entries = [];
490
+ entries = [];
554
491
  return;
555
492
  }
556
- const cmp = this.comparator;
557
493
  const worst = [];
558
494
  for (const entry of entries) {
559
- if (worst.length < k) {
495
+ if (worst.length < bufferSize) {
560
496
  let i = worst.length;
561
- while (i > 0 && cmp(entry, worst[i - 1]) > 0) i--;
497
+ while (i > 0 && comparator(entry, worst[i - 1]) > 0) i--;
562
498
  worst.splice(i, 0, entry);
563
- } else if (cmp(entry, worst[k - 1]) > 0) {
499
+ } else if (comparator(entry, worst[bufferSize - 1]) > 0) {
564
500
  worst.pop();
565
501
  let i = worst.length;
566
- while (i > 0 && cmp(entry, worst[i - 1]) > 0) i--;
502
+ while (i > 0 && comparator(entry, worst[i - 1]) > 0) i--;
567
503
  worst.splice(i, 0, entry);
568
504
  }
569
505
  }
570
506
  const evictSet = new Set(worst);
571
507
  for (const e of worst) {
572
- this.lookup.delete(this.prefixKey(e.originalKey));
573
- if (this.onRemove) this.onRemove(e.originalKey);
574
- }
575
- this.entries = entries.filter((e) => !evictSet.has(e));
576
- }
577
- };
508
+ lookup.delete(prefixKey(e.originalKey));
509
+ if (onRemove) onRemove(e.originalKey);
510
+ }
511
+ entries = entries.filter((e) => !evictSet.has(e));
512
+ }
513
+ function getSize() {
514
+ return entries.length;
515
+ }
516
+ const api = {
517
+ get,
518
+ set,
519
+ del,
520
+ has,
521
+ clear,
522
+ forEach,
523
+ getSize
524
+ };
525
+ return api;
526
+ }
578
527
 
579
528
  // src/event-delegator.ts
580
529
  var rootEvents = {};
@@ -582,7 +531,7 @@ var selectorEvents = {};
582
531
  var rangeEvents = {};
583
532
  var rangeFrames = {};
584
533
  var elementGuid = 0;
585
- var eventInfoCache = new Cache({
534
+ var eventInfoCache = createCache({
586
535
  maxSize: 30,
587
536
  bufferSize: 10
588
537
  });
@@ -635,7 +584,7 @@ function findFrameInfo(current, eventType) {
635
584
  if (frame) {
636
585
  const view = frame.view;
637
586
  if (view) {
638
- const selectorEntry = view.eventSelectorMap[eventType];
587
+ const selectorEntry = { selectors: [] };
639
588
  if (selectorEntry) {
640
589
  for (const selectorName of selectorEntry.selectors) {
641
590
  const entry = {
@@ -653,7 +602,7 @@ function findFrameInfo(current, eventType) {
653
602
  }
654
603
  }
655
604
  }
656
- if (view.template && !backtrace) {
605
+ if (view.getTemplate() && !backtrace) {
657
606
  if (match && !match.id) {
658
607
  match.id = frameId;
659
608
  }
@@ -705,8 +654,9 @@ function domEventProcessor(domEvent) {
705
654
  const frame = frameId ? frameGetter?.(frameId) : void 0;
706
655
  const view = frame?.view;
707
656
  if (view) {
708
- const eventName = handlerName + SPLITTER + eventType;
709
- const fn = Reflect.get(view, eventName);
657
+ const eventKey = handlerName + "<" + eventType + ">";
658
+ const events = typeof view.getEvents === "function" ? view.getEvents() : void 0;
659
+ const fn = events?.[eventKey];
710
660
  if (fn) {
711
661
  const extendedEvent = domEvent;
712
662
  extendedEvent.eventTarget = target;
@@ -785,68 +735,6 @@ var EventDelegator = {
785
735
  }
786
736
  };
787
737
 
788
- // src/module-loader.ts
789
- var config = {
790
- rootId: "root",
791
- routeMode: "history",
792
- hashbang: "#!",
793
- error: (error) => {
794
- throw error;
795
- }
796
- };
797
- function use(names, callback) {
798
- const nameList = typeof names === "string" ? [names] : names;
799
- const loadPromise = (() => {
800
- if (config.require) {
801
- const result = config.require(nameList);
802
- if (result && typeof result.then === "function") {
803
- return result;
804
- }
805
- return Promise.resolve([]);
806
- }
807
- return Promise.all(
808
- nameList.map((name) => {
809
- const importPath = name.startsWith(".") || name.startsWith("/") ? name : `./${name}`;
810
- return import(
811
- /* @vite-ignore */
812
- /* webpackIgnore: true */
813
- importPath
814
- ).then((mod) => {
815
- return mod && (mod["__esModule"] || // For Webpack
816
- typeof mod["default"] === "function") ? mod["default"] : mod;
817
- }).catch((err) => {
818
- const errorHandler = config.error;
819
- if (errorHandler) {
820
- errorHandler(err instanceof Error ? err : new Error(String(err)));
821
- }
822
- return void 0;
823
- });
824
- })
825
- );
826
- })();
827
- if (callback) {
828
- loadPromise.then((modules) => {
829
- callback(...modules);
830
- });
831
- }
832
- return loadPromise;
833
- }
834
-
835
- // src/frame-registry.ts
836
- var frameRegistry = /* @__PURE__ */ new Map();
837
- function getFrame(id) {
838
- return frameRegistry.get(id);
839
- }
840
- function getAllFrames() {
841
- return frameRegistry;
842
- }
843
- function registerFrame(id, frame) {
844
- frameRegistry.set(id, frame);
845
- }
846
- function removeFrame(id) {
847
- frameRegistry.delete(id);
848
- }
849
-
850
738
  // src/dom.ts
851
739
  var wrapMeta = {
852
740
  option: [1, "<select multiple>"],
@@ -1122,6 +1010,11 @@ function vdomCreateNode(vnode, owner, ref) {
1122
1010
  if (tag === V_TEXT_NODE) {
1123
1011
  return document.createTextNode(vnode.html);
1124
1012
  }
1013
+ if (tag === SPLITTER) {
1014
+ const template = document.createElement("template");
1015
+ template.innerHTML = vnode.html;
1016
+ return template.content.firstChild || document.createTextNode("");
1017
+ }
1125
1018
  const sTag = typeof tag === "string" ? tag : tag.toString();
1126
1019
  const ns = VDOM_NS_MAP[sTag] || owner.namespaceURI;
1127
1020
  const el = document.createElementNS(ns, sTag);
@@ -1205,6 +1098,14 @@ function vdomSetNode(realNode, oldParent, lastVDom, newVDom, ref, frame, keys, r
1205
1098
  return;
1206
1099
  }
1207
1100
  if (lastTag === newTag) {
1101
+ if (newTag === SPLITTER) {
1102
+ if (lastVDom.html !== newVDom.html) {
1103
+ ref.changed = 1;
1104
+ domUnmountFrames(frame, realNode);
1105
+ oldParent.replaceChild(vdomCreateNode(newVDom, oldParent, ref), realNode);
1106
+ }
1107
+ return;
1108
+ }
1208
1109
  if (lastVDom.attrs === newVDom.attrs && lastVDom.html === newVDom.html) {
1209
1110
  if (newVDom.hasSpecials) {
1210
1111
  vdomSyncFormState(realNode, newVDom);
@@ -1213,12 +1114,7 @@ function vdomSetNode(realNode, oldParent, lastVDom, newVDom, ref, frame, keys, r
1213
1114
  }
1214
1115
  let attrChanged = 0;
1215
1116
  if (lastVDom.attrs !== newVDom.attrs || newVDom.hasSpecials) {
1216
- attrChanged = vdomSetAttributes(
1217
- realNode,
1218
- newVDom,
1219
- ref,
1220
- lastVDom
1221
- );
1117
+ attrChanged = vdomSetAttributes(realNode, newVDom, ref, lastVDom);
1222
1118
  if (attrChanged) ref.changed = 1;
1223
1119
  }
1224
1120
  let updateChildren = true;
@@ -1232,16 +1128,7 @@ function vdomSetNode(realNode, oldParent, lastVDom, newVDom, ref, frame, keys, r
1232
1128
  }
1233
1129
  vdomSyncFormState(realNode, newVDom);
1234
1130
  if (updateChildren && !newVDom.selfClose) {
1235
- vdomSetChildNodes(
1236
- realNode,
1237
- lastVDom,
1238
- newVDom,
1239
- ref,
1240
- frame,
1241
- keys,
1242
- rootView,
1243
- ready
1244
- );
1131
+ vdomSetChildNodes(realNode, lastVDom, newVDom, ref, frame, keys, rootView, ready);
1245
1132
  }
1246
1133
  } else {
1247
1134
  ref.changed = 1;
@@ -1311,17 +1198,7 @@ function vdomSetChildNodes(realNode, lastVDom, newVDom, ref, frame, keys, view,
1311
1198
  const nc = newChildren[newHead];
1312
1199
  if (!isSameVDomNode(nc, oc)) break;
1313
1200
  if (nc.tag === SPLITTER || oc.tag === SPLITTER) break;
1314
- vdomSetNode(
1315
- oldDomNodes[headIdx],
1316
- realNode,
1317
- oc,
1318
- nc,
1319
- ref,
1320
- frame,
1321
- keys,
1322
- view,
1323
- ready
1324
- );
1201
+ vdomSetNode(oldDomNodes[headIdx], realNode, oc, nc, ref, frame, keys, view, ready);
1325
1202
  usedOldDomNodes.add(oldDomNodes[headIdx]);
1326
1203
  headIdx++;
1327
1204
  newHead++;
@@ -1331,17 +1208,7 @@ function vdomSetChildNodes(realNode, lastVDom, newVDom, ref, frame, keys, view,
1331
1208
  const nc = newChildren[newTail];
1332
1209
  if (!isSameVDomNode(nc, oc)) break;
1333
1210
  if (nc.tag === SPLITTER || oc.tag === SPLITTER) break;
1334
- vdomSetNode(
1335
- oldDomNodes[tailIdx],
1336
- realNode,
1337
- oc,
1338
- nc,
1339
- ref,
1340
- frame,
1341
- keys,
1342
- view,
1343
- ready
1344
- );
1211
+ vdomSetNode(oldDomNodes[tailIdx], realNode, oc, nc, ref, frame, keys, view, ready);
1345
1212
  usedOldDomNodes.add(oldDomNodes[tailIdx]);
1346
1213
  tailIdx--;
1347
1214
  newTail--;
@@ -1464,113 +1331,88 @@ function createVDomRef(viewId) {
1464
1331
  }
1465
1332
 
1466
1333
  // src/updater.ts
1467
- var Updater = class {
1468
- /** View ID (same as owner frame ID) */
1469
- viewId;
1470
- /** Current data object */
1471
- data;
1472
- /** Ref data for template rendering */
1473
- refData;
1474
- /** Changed keys in current digest cycle */
1475
- changedKeys = /* @__PURE__ */ new Set();
1476
- /** Whether data has changed since last digest */
1477
- hasChangedFlag = 0;
1478
- /**
1479
- * Digesting queue: supports re-digest during digest.
1480
- * Holds pending callbacks; `null` is used as a sentinel marking the start
1481
- * of an active digest cycle, so `runDigest` can detect re-entrant calls.
1482
- */
1483
- digestingQueue = [];
1484
- /** Monotonically increasing version, bumped each time data actually changes. */
1485
- version = 0;
1486
- /** Snapshot of `version` taken by `snapshot()`, used by `altered()`. */
1487
- snapshotVersion;
1488
- /** Last rendered VDOM tree (only used when virtualDom is enabled) */
1489
- vdom;
1490
- constructor(viewId) {
1491
- this.viewId = viewId;
1492
- this.data = { vId: viewId };
1493
- const refCounter = {};
1494
- refCounter[SPLITTER] = 1;
1495
- this.refData = refCounter;
1496
- this.hasChangedFlag = 1;
1497
- }
1498
- /**
1499
- * Get data by key.
1500
- * Returns entire data object if key is omitted.
1501
- */
1502
- get(key) {
1503
- let result = this.data;
1334
+ function createUpdater(viewId) {
1335
+ let data = { vId: viewId };
1336
+ const refData = {};
1337
+ refData[SPLITTER] = 1;
1338
+ let changedKeys = /* @__PURE__ */ new Set();
1339
+ let hasChangedFlag = 0;
1340
+ const digestingQueue = [];
1341
+ let version = 0;
1342
+ let snapshotVersion;
1343
+ let vdom;
1344
+ hasChangedFlag = 1;
1345
+ function get(key) {
1346
+ let result = data;
1504
1347
  if (key) {
1505
- result = this.data[key];
1348
+ result = data[key];
1506
1349
  }
1507
1350
  return result;
1508
1351
  }
1509
- /**
1510
- * Set data, tracking changed keys.
1511
- * Returns this for chaining.
1512
- */
1513
- set(data, excludes) {
1514
- const changed = setData(
1515
- data,
1516
- this.data,
1517
- this.changedKeys,
1518
- excludes || EMPTY_STRING_SET
1519
- );
1352
+ function set(newData, excludes) {
1353
+ const changed = setData(newData, data, changedKeys, excludes || EMPTY_STRING_SET);
1520
1354
  if (changed) {
1521
- this.version++;
1522
- this.hasChangedFlag = 1;
1355
+ version++;
1356
+ hasChangedFlag = 1;
1523
1357
  }
1524
- return this;
1358
+ return api;
1525
1359
  }
1526
- /**
1527
- * Detect changes and trigger DOM re-render.
1528
- *
1529
- * The core rendering pipeline:
1530
- * 1. Set data if provided
1531
- * 2. If changed, run DOM diff (template → new DOM → diff against old DOM)
1532
- * 3. Apply DOM operations
1533
- * 4. Apply ID updates
1534
- * 5. Call endUpdate on views that need re-rendering
1535
- * 6. Support re-digest during digest via queue
1536
- */
1537
- digest(data, excludes, callback) {
1538
- if (data) {
1539
- this.set(data, excludes);
1360
+ function digest(newData, excludes, callback) {
1361
+ if (newData) {
1362
+ set(newData, excludes);
1540
1363
  }
1541
- const digesting = this.digestingQueue;
1542
1364
  if (callback) {
1543
- digesting.push(callback);
1365
+ digestingQueue.push(callback);
1544
1366
  }
1545
- if (digesting.length > 0 && digesting[0] === null) {
1367
+ if (digestingQueue.length > 0 && digestingQueue[0] === null) {
1546
1368
  return;
1547
1369
  }
1548
- this.runDigest(digesting);
1370
+ runDigest(digestingQueue);
1549
1371
  }
1550
- /**
1551
- * Core digest execution.
1552
- */
1553
- runDigest(digesting) {
1372
+ function runDigest(digesting) {
1554
1373
  const startIndex = digesting.length;
1555
1374
  digesting.push(null);
1556
- const keys = this.changedKeys;
1557
- const changed = this.hasChangedFlag;
1558
- this.hasChangedFlag = 0;
1559
- this.changedKeys = /* @__PURE__ */ new Set();
1560
- const frame = getFrame(this.viewId);
1375
+ const keys = changedKeys;
1376
+ const changed = hasChangedFlag;
1377
+ const frame = Frame.get(viewId);
1561
1378
  const view = frame?.view;
1562
- const node = getById(this.viewId);
1563
- if (changed && view && node && view.signature > 0 && frame) {
1564
- const template = view.template;
1379
+ const node = getById(viewId);
1380
+ if (changed && view && node && view.signature.value > 0 && frame) {
1381
+ hasChangedFlag = 0;
1382
+ changedKeys = /* @__PURE__ */ new Set();
1383
+ const template = view.getTemplate();
1565
1384
  if (typeof template === "function") {
1566
- if (config.virtualDom) {
1567
- const vdomTemplate = template;
1568
- const newVDom = vdomTemplate(this.data, this.viewId, this.refData);
1569
- const ref = createVDomRef(this.viewId);
1385
+ const result = template(
1386
+ data,
1387
+ viewId,
1388
+ refData,
1389
+ encodeHTML,
1390
+ strSafe,
1391
+ encodeURIExtra,
1392
+ refFn,
1393
+ encodeQuote
1394
+ );
1395
+ if (typeof result === "string") {
1396
+ const newDom = domGetNode(result, node);
1397
+ const ref = createDomRef();
1398
+ domSetChildNodes(node, newDom, ref, frame, keys);
1399
+ applyIdUpdates(ref.idUpdates);
1400
+ applyDomOps(ref.domOps);
1401
+ for (const v of ref.views) {
1402
+ if (v.render) {
1403
+ funcWithTry(v.render, [], v, noop);
1404
+ }
1405
+ }
1406
+ if (ref.hasChanged || !view.rendered.value) {
1407
+ view.endUpdate(viewId);
1408
+ }
1409
+ } else {
1410
+ const newVDom = result;
1411
+ const ref = createVDomRef(viewId);
1570
1412
  const ready = () => {
1571
- this.vdom = newVDom;
1572
- if (ref.changed || !view.rendered) {
1573
- view.endUpdate(this.viewId);
1413
+ vdom = newVDom;
1414
+ if (ref.changed || !view.rendered.value) {
1415
+ view.endUpdate(viewId);
1574
1416
  }
1575
1417
  for (const [el, prop, val] of ref.nodeProps) {
1576
1418
  Reflect.set(el, prop, val);
@@ -1581,45 +1423,14 @@ var Updater = class {
1581
1423
  }
1582
1424
  }
1583
1425
  };
1584
- vdomSetChildNodes(
1585
- node,
1586
- this.vdom,
1587
- newVDom,
1588
- ref,
1589
- frame,
1590
- keys,
1591
- view,
1592
- ready
1593
- );
1594
- } else {
1595
- const html = template(
1596
- this.data,
1597
- this.viewId,
1598
- this.refData,
1599
- encodeHTML,
1600
- strSafe,
1601
- encodeURIExtra,
1602
- refFn,
1603
- encodeQuote
1604
- );
1605
- const newDom = domGetNode(html, node);
1606
- const ref = createDomRef();
1607
- domSetChildNodes(node, newDom, ref, frame, keys);
1608
- applyIdUpdates(ref.idUpdates);
1609
- applyDomOps(ref.domOps);
1610
- for (const v of ref.views) {
1611
- if (v.render) {
1612
- funcWithTry(v.render, [], v, noop);
1613
- }
1614
- }
1615
- if (ref.hasChanged || !view.rendered) {
1616
- view.endUpdate(this.viewId);
1617
- }
1426
+ vdomSetChildNodes(node, vdom, newVDom, ref, frame, keys, view, ready);
1618
1427
  }
1619
1428
  }
1429
+ } else {
1430
+ changedKeys = /* @__PURE__ */ new Set();
1620
1431
  }
1621
1432
  if (digesting.length > startIndex + 1) {
1622
- this.runDigest(digesting);
1433
+ runDigest(digesting);
1623
1434
  } else {
1624
1435
  const callbacks = digesting.slice();
1625
1436
  digesting.length = 0;
@@ -1628,44 +1439,19 @@ var Updater = class {
1628
1439
  }
1629
1440
  }
1630
1441
  }
1631
- /**
1632
- * Save a snapshot of the current data version for `altered()` detection.
1633
- * Cheap O(1) — records the current monotonic version, no serialization.
1634
- */
1635
- snapshot() {
1636
- this.snapshotVersion = this.version;
1637
- return this;
1442
+ function snapshot() {
1443
+ snapshotVersion = version;
1444
+ return api;
1638
1445
  }
1639
- /**
1640
- * Check whether data has changed since the last snapshot.
1641
- * Returns undefined when no snapshot has been taken yet.
1642
- */
1643
- altered() {
1644
- if (this.snapshotVersion === void 0) return void 0;
1645
- return this.version !== this.snapshotVersion;
1446
+ function altered() {
1447
+ if (snapshotVersion === void 0) return void 0;
1448
+ return version !== snapshotVersion;
1646
1449
  }
1647
- /**
1648
- * Translate a refData reference back to its original value.
1649
- *
1650
- * The ref protocol is `SPLITTER` + ascii decimal digits — the exact format
1651
- * emitted by `refFn`. We require that exact shape so a user-supplied
1652
- * string that merely begins with SPLITTER is never accidentally resolved
1653
- * (or mishandled as a "missing ref").
1654
- */
1655
- translate(data) {
1656
- if (typeof data !== "string" || !isRefToken(data)) return data;
1657
- return hasOwnProperty(this.refData, data) ? this.refData[data] : data;
1450
+ function translate(dataVal) {
1451
+ if (typeof dataVal !== "string" || !isRefToken(dataVal)) return dataVal;
1452
+ return hasOwnProperty(refData, dataVal) ? refData[dataVal] : dataVal;
1658
1453
  }
1659
- /**
1660
- * Resolve a dotted property path against refData.
1661
- *
1662
- * Only safe property-path syntax is supported: `a`, `a.b`, `a.b.c`.
1663
- * Numeric literals (e.g. `1`, `1.5`) are returned as numbers. Anything else
1664
- * returns `undefined` — we no longer evaluate arbitrary JavaScript via
1665
- * `new Function`, so the method is CSP-safe and cannot be used as an
1666
- * injection vector.
1667
- */
1668
- parse(expr) {
1454
+ function parse(expr) {
1669
1455
  const trimmed = expr.trim();
1670
1456
  if (!trimmed) return void 0;
1671
1457
  if (/^-?\d+(?:\.\d+)?$/.test(trimmed)) {
@@ -1674,25 +1460,40 @@ var Updater = class {
1674
1460
  if (!/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*$/.test(trimmed)) {
1675
1461
  return void 0;
1676
1462
  }
1677
- let cur = this.refData;
1463
+ let cur = refData;
1678
1464
  for (const segment of trimmed.split(".")) {
1679
1465
  if (cur == null || typeof cur !== "object") return void 0;
1680
- cur = cur[segment];
1466
+ cur = Reflect.get(cur, segment);
1681
1467
  }
1682
1468
  return cur;
1683
1469
  }
1684
- /**
1685
- * Get the set of keys changed since the last digest (for external inspection).
1686
- */
1687
- getChangedKeys() {
1688
- return this.changedKeys;
1689
- }
1690
- };
1470
+ function getChangedKeys() {
1471
+ return changedKeys;
1472
+ }
1473
+ function forceDigest() {
1474
+ hasChangedFlag = 1;
1475
+ changedKeys = new Set(Object.keys(data));
1476
+ digest();
1477
+ }
1478
+ const api = {
1479
+ get,
1480
+ set,
1481
+ digest,
1482
+ forceDigest,
1483
+ snapshot,
1484
+ altered,
1485
+ refData,
1486
+ translate,
1487
+ parse,
1488
+ getChangedKeys
1489
+ };
1490
+ return api;
1491
+ }
1691
1492
 
1692
1493
  // src/router.ts
1693
- var emitter = new EventEmitter();
1694
- var hrefCache = new Cache();
1695
- var changedCache = new Cache();
1494
+ var emitter = createEmitter();
1495
+ var hrefCache = createCache();
1496
+ var changedCache = createCache();
1696
1497
  var lastLocation = createEmptyLocation();
1697
1498
  var lastChanged;
1698
1499
  var silent = 0;
@@ -1736,11 +1537,7 @@ function attachViewAndPath(loc) {
1736
1537
  path = cachedDefaultPath;
1737
1538
  }
1738
1539
  if (cachedRewrite) {
1739
- path = cachedRewrite(
1740
- path,
1741
- loc["params"],
1742
- cachedRoutes
1743
- );
1540
+ path = cachedRewrite(path, loc["params"], cachedRoutes);
1744
1541
  }
1745
1542
  const viewEntry = cachedRoutes[path] || cachedUnmatchedView || cachedDefaultView;
1746
1543
  loc["path"] = path;
@@ -1943,7 +1740,7 @@ var Router = {
1943
1740
  updateUrl(tPath, tParams, lastLocation, replace, silentFlag, lQuery);
1944
1741
  },
1945
1742
  /**
1946
- * Register an async-friendly navigation guard. See `RouterInterface.beforeEach`.
1743
+ * Register an async-friendly navigation guard. See `RouterApi.beforeEach`.
1947
1744
  */
1948
1745
  beforeEach(guard) {
1949
1746
  beforeEachGuards.push(guard);
@@ -2081,62 +1878,10 @@ var Router = {
2081
1878
  }
2082
1879
  };
2083
1880
 
2084
- // src/view-registry.ts
2085
- var viewClassRegistry = {};
2086
- function getViewClass(path) {
2087
- return viewClassRegistry[path];
2088
- }
2089
- function registerViewClass(viewPath, ViewClass) {
2090
- const parsed = parseUri(viewPath);
2091
- const path = parsed.path;
2092
- if (path) {
2093
- viewClassRegistry[path] = ViewClass;
2094
- }
2095
- }
2096
- function invalidateViewClass(viewPath) {
2097
- const parsed = parseUri(viewPath);
2098
- const path = parsed.path;
2099
- if (path) {
2100
- Reflect.deleteProperty(viewClassRegistry, path);
2101
- }
2102
- }
2103
-
2104
- // src/hmr.ts
2105
- function reloadViews(viewPath) {
2106
- const allFrames = getAllFrames();
2107
- const toReload = [];
2108
- for (const [, frame] of allFrames) {
2109
- if (frame.viewPath) {
2110
- const parsed = parseUri(frame.viewPath);
2111
- if (parsed.path === viewPath) {
2112
- toReload.push({ frame, fullPath: frame.viewPath });
2113
- }
2114
- }
2115
- }
2116
- for (const { frame, fullPath } of toReload) {
2117
- frame.mountView(fullPath);
2118
- }
2119
- }
2120
- function acceptView(hot, viewPath) {
2121
- hot.accept((newModule) => {
2122
- const candidate = newModule?.default ?? newModule;
2123
- if (typeof candidate === "function") {
2124
- const NewViewClass = candidate;
2125
- registerViewClass(viewPath, NewViewClass);
2126
- reloadViews(viewPath);
2127
- return;
2128
- }
2129
- if (getViewClass(viewPath)) {
2130
- reloadViews(viewPath);
2131
- return;
2132
- }
2133
- hot.invalidate();
2134
- });
2135
- }
2136
- function disposeView(hot, viewPath) {
2137
- hot.dispose(() => {
2138
- invalidateViewClass(viewPath);
2139
- });
1881
+ // src/hooks.ts
1882
+ var currentCtx = null;
1883
+ function setCurrentCtx(ctx) {
1884
+ currentCtx = ctx;
2140
1885
  }
2141
1886
 
2142
1887
  // src/view.ts
@@ -2147,161 +1892,99 @@ if (typeof window !== "undefined") {
2147
1892
  if (typeof document !== "undefined") {
2148
1893
  VIEW_GLOBALS["document"] = document;
2149
1894
  }
2150
- var View = class _View {
2151
- /** View ID (same as owner frame ID) */
2152
- id = "";
2153
- /** Owner frame */
2154
- owner = 0;
2155
- /** Updater instance */
2156
- updater;
2157
- /** Signature: > 0 means active, incremented on render, 0 = destroyed */
2158
- signature = 0;
2159
- /** Whether rendered at least once */
2160
- rendered;
2161
- /** Whether view has template */
2162
- template;
2163
- /** Location observation config */
2164
- locationObserved = {
1895
+ function createCtx(frame) {
1896
+ const id = frame.id;
1897
+ const updater = createUpdater(id);
1898
+ const emitter2 = createEmitter();
1899
+ const signature = { value: 0 };
1900
+ const rendered = { value: false };
1901
+ const resources = {};
1902
+ const locationObserved = {
2165
1903
  flag: 0,
2166
1904
  keys: [],
2167
1905
  observePath: false
2168
1906
  };
2169
- /** Observed state keys */
2170
- observedStateKeys;
2171
- /** Resource map */
2172
- resources = {};
2173
- /** Whether endUpdate pending */
2174
- endUpdatePending;
2175
- /** Internal event storage */
2176
- _events = new EventEmitter();
2177
- // ============================================================
2178
- // Getters for prototype-stored event maps
2179
- // ============================================================
2180
- /** Prototype-stored event maps shape (set by View.prepare). */
2181
- get protoEventState() {
2182
- return Object.getPrototypeOf(this);
2183
- }
2184
- /**
2185
- * Event bitmask map: eventType -> bitmask (1=root, 2=selector).
2186
- * Read from prototype ($evtObjMap) set by View.prepare.
2187
- * Using a getter avoids ES6 class field shadowing the prototype value.
2188
- */
2189
- get eventObjectMap() {
2190
- return this.protoEventState.$evtObjMap ?? {};
2191
- }
2192
- /**
2193
- * Selector event map: eventType -> selector list.
2194
- * Read from prototype ($selMap) set by View.prepare.
2195
- */
2196
- get eventSelectorMap() {
2197
- return this.protoEventState.$selMap ?? {};
1907
+ const mutable = {
1908
+ observedStateKeys: void 0,
1909
+ endUpdatePending: void 0,
1910
+ template: void 0,
1911
+ events: void 0,
1912
+ assignFn: void 0
1913
+ };
1914
+ const cleanups = [];
1915
+ function on(event, handler) {
1916
+ emitter2.on(event, handler);
1917
+ return () => emitter2.off(event, handler);
2198
1918
  }
2199
- /**
2200
- * Global event list: [{handler, element, eventName, modifiers}].
2201
- * Read from prototype ($globalEvtList) set by View.prepare.
2202
- */
2203
- get globalEventList() {
2204
- return this.protoEventState.$globalEvtList ?? [];
1919
+ function off(event, handler) {
1920
+ emitter2.off(event, handler);
2205
1921
  }
2206
- // ============================================================
2207
- // Instance lifecycle methods
2208
- // ============================================================
2209
- /**
2210
- * Initialize view (called by Frame when mounting).
2211
- */
2212
- init() {
1922
+ function fire(event, data, remove, lastToFirst) {
1923
+ emitter2.fire(event, data, remove, lastToFirst);
2213
1924
  }
2214
- /**
2215
- * Render view template (called by Frame after init).
2216
- * Wrapped by View.wrapMethod to manage signature + resources.
2217
- */
2218
- render() {
2219
- this.updater.digest();
2220
- }
2221
- // ============================================================
2222
- // Event methods (delegate to internal EventEmitter)
2223
- // ============================================================
2224
- on(event, handler) {
2225
- this._events.on(event, handler);
2226
- return this;
2227
- }
2228
- off(event, handler) {
2229
- this._events.off(event, handler);
2230
- return this;
1925
+ function capture(key, resource, destroyOnRender = false) {
1926
+ if (resource !== void 0) {
1927
+ destroyResource(resources, key, true, resource);
1928
+ resources[key] = { entity: resource, destroyOnRender };
1929
+ } else {
1930
+ const entry = resources[key];
1931
+ return entry ? entry.entity : void 0;
1932
+ }
1933
+ return resource;
2231
1934
  }
2232
- fire(event, data, remove, lastToFirst) {
2233
- this._events.fire(event, data, remove, lastToFirst);
2234
- return this;
1935
+ function release(key, destroy = true) {
1936
+ return destroyResource(resources, key, destroy);
2235
1937
  }
2236
- // ============================================================
2237
- // Update methods
2238
- // ============================================================
2239
- /** Get the owning frame, asserting it has been bound. */
2240
- get ownerFrame() {
2241
- return this.owner;
1938
+ function render() {
1939
+ if (signature.value > 0) {
1940
+ signature.value++;
1941
+ fire("render");
1942
+ destroyAllResources(ctx, false);
1943
+ if (typeof ctx.renderMethod === "function") {
1944
+ funcWithTry(ctx.renderMethod, [], ctx, noop);
1945
+ } else {
1946
+ updater.digest();
1947
+ }
1948
+ }
2242
1949
  }
2243
- /**
2244
- * Notify view that HTML update is about to begin.
2245
- * Unmounts child frames in the update zone.
2246
- */
2247
- beginUpdate(id) {
2248
- if (this.signature > 0 && this.endUpdatePending !== void 0) {
2249
- this.ownerFrame.unmountZone(id);
1950
+ function beginUpdate(zoneId) {
1951
+ if (signature.value > 0 && mutable.endUpdatePending !== void 0) {
1952
+ frame.unmountZone(zoneId);
2250
1953
  }
2251
1954
  }
2252
- /**
2253
- * Notify view that HTML update has ended.
2254
- * Mounts child frames in the update zone and runs deferred invokes.
2255
- */
2256
- endUpdate(id, inner) {
2257
- if (this.signature > 0) {
2258
- const updateId = id || this.id;
1955
+ function endUpdate(zoneId, inner) {
1956
+ if (signature.value > 0) {
1957
+ const updateId = zoneId ?? id;
2259
1958
  let flag;
2260
1959
  if (inner) {
2261
1960
  flag = inner;
2262
1961
  } else {
2263
- flag = this.endUpdatePending;
2264
- this.endUpdatePending = 1;
2265
- this.rendered = true;
1962
+ flag = mutable.endUpdatePending;
1963
+ mutable.endUpdatePending = 1;
1964
+ rendered.value = true;
2266
1965
  }
2267
- const ownerFrame = this.ownerFrame;
2268
- ownerFrame.mountZone(updateId);
1966
+ frame.mountZone(updateId);
2269
1967
  if (!flag) {
2270
1968
  setTimeout(
2271
- this.wrapAsync(() => {
2272
- _View.runInvokes(ownerFrame);
1969
+ wrapAsync(() => {
1970
+ runInvokes(frame);
2273
1971
  }),
2274
1972
  0
2275
1973
  );
2276
1974
  }
2277
1975
  }
2278
1976
  }
2279
- // ============================================================
2280
- // Async wrapper
2281
- // ============================================================
2282
- /**
2283
- * Wrap an async callback to check view signature before executing.
2284
- * If the view has been re-rendered or destroyed, the callback is skipped.
2285
- */
2286
- wrapAsync(fn, context) {
2287
- const currentSignature = this.signature;
1977
+ function wrapAsync(fn, context) {
1978
+ const currentSignature = signature.value;
2288
1979
  return (...args) => {
2289
- if (currentSignature > 0 && currentSignature === this.signature) {
2290
- return fn.apply(context || this, args);
1980
+ if (currentSignature > 0 && currentSignature === signature.value) {
1981
+ return fn.apply(context ?? ctx, args);
2291
1982
  }
2292
1983
  return void 0;
2293
1984
  };
2294
1985
  }
2295
- // ============================================================
2296
- // Location observation
2297
- // ============================================================
2298
- /**
2299
- * Observe location parameters or path changes.
2300
- * When observed keys change, render() is called automatically.
2301
- */
2302
- observeLocation(params, observePath = false) {
2303
- const loc = this.locationObserved;
2304
- loc.flag = 1;
1986
+ function observeLocation(params, observePath = false) {
1987
+ locationObserved.flag = 1;
2305
1988
  if (typeof params === "object" && !Array.isArray(params)) {
2306
1989
  const opts = params;
2307
1990
  if (opts["path"]) {
@@ -2312,880 +1995,634 @@ var View = class _View {
2312
1995
  params = paramKeys;
2313
1996
  }
2314
1997
  }
2315
- loc.observePath = observePath;
1998
+ locationObserved.observePath = observePath;
2316
1999
  if (params) {
2317
2000
  if (typeof params === "string") {
2318
- loc.keys = params.split(",");
2001
+ locationObserved.keys = params.split(",");
2319
2002
  } else if (Array.isArray(params)) {
2320
- loc.keys = params;
2003
+ locationObserved.keys = params;
2321
2004
  }
2322
2005
  }
2323
2006
  }
2324
- // ============================================================
2325
- // State observation
2326
- // ============================================================
2327
- /**
2328
- * Observe State data keys for changes.
2329
- * When observed keys change via State.digest(), render() is called.
2330
- */
2331
- observeState(observedKeys) {
2332
- if (typeof observedKeys === "string") {
2333
- this.observedStateKeys = observedKeys.split(",");
2007
+ function observeState(keys) {
2008
+ if (typeof keys === "string") {
2009
+ mutable.observedStateKeys = keys.split(",");
2334
2010
  } else {
2335
- this.observedStateKeys = observedKeys;
2011
+ mutable.observedStateKeys = keys;
2336
2012
  }
2337
2013
  }
2338
- // ============================================================
2339
- // Resource management
2340
- // ============================================================
2341
- /**
2342
- * Capture (register) a resource under a key.
2343
- * If a resource already exists at that key, it's destroyed first.
2344
- * When destroyOnRender=true, the resource is destroyed on next render call.
2345
- */
2346
- capture(key, resource, destroyOnRender = false) {
2347
- const cache = this.resources;
2348
- if (resource) {
2349
- _View.destroyResource(cache, key, true, resource);
2350
- cache[key] = {
2351
- entity: resource,
2352
- destroyOnRender
2353
- };
2354
- } else {
2355
- const entry = cache[key];
2356
- return entry ? entry.entity : void 0;
2014
+ function leaveTip(message, condition) {
2015
+ const changeState = { a: 0, b: 0 };
2016
+ function isRouteChange(e) {
2017
+ return "prevent" in e && "reject" in e && "resolve" in e;
2357
2018
  }
2358
- return resource;
2359
- }
2360
- /**
2361
- * Release a captured resource.
2362
- * If destroy=true, calls the resource's destroy() method.
2363
- */
2364
- release(key, destroy = true) {
2365
- return _View.destroyResource(this.resources, key, destroy);
2366
- }
2367
- // ============================================================
2368
- // Leave tip
2369
- // ============================================================
2370
- /**
2371
- * Set up a leave confirmation for route changes and page unload.
2372
- */
2373
- leaveTip(message, condition) {
2374
- const changeListener = function(e) {
2019
+ const changeListener = (e) => {
2020
+ if (!e) return;
2375
2021
  const isRouterChange = e.type === RouterEvents.CHANGE;
2376
2022
  const aKey = isRouterChange ? "a" : "b";
2377
2023
  const bKey = isRouterChange ? "b" : "a";
2378
- if (changeListener[aKey]) {
2379
- e.prevent?.();
2380
- e.reject?.();
2024
+ if (changeState[aKey]) {
2025
+ if (isRouteChange(e)) {
2026
+ e.prevent();
2027
+ e.reject();
2028
+ }
2381
2029
  } else if (condition()) {
2382
- e.prevent?.();
2383
- changeListener[bKey] = 1;
2384
- e.resolve?.();
2030
+ if (isRouteChange(e)) {
2031
+ e.prevent();
2032
+ changeState[bKey] = 1;
2033
+ e.resolve();
2034
+ }
2385
2035
  }
2386
2036
  };
2387
2037
  const unloadListener = (e) => {
2388
- if (condition()) {
2389
- e["msg"] = message;
2038
+ if (e && condition()) {
2039
+ Reflect.set(e, "msg", message);
2390
2040
  }
2391
2041
  };
2392
2042
  Router.on(RouterEvents.CHANGE, changeListener);
2393
2043
  Router.on(RouterEvents.PAGE_UNLOAD, unloadListener);
2394
- this.on("unload", changeListener);
2395
- this.on("destroy", () => {
2044
+ on("unload", changeListener);
2045
+ on("destroy", () => {
2396
2046
  Router.off(RouterEvents.CHANGE, changeListener);
2397
2047
  Router.off(RouterEvents.PAGE_UNLOAD, unloadListener);
2398
2048
  });
2399
2049
  }
2400
- // ============================================================
2401
- // Static public methods
2402
- // ============================================================
2403
- /** Collected makes from mixins */
2404
- static makes;
2405
- /**
2406
- * Prepare a View subclass by scanning its prototype for event method patterns.
2407
- * Pattern: `$?name<eventType1,eventType2>(&modifiers)`
2408
- *
2409
- * Only runs once per View subclass (guarded by makes marker).
2410
- * Called from Frame.mountView before creating the view instance.
2411
- */
2412
- static prepare(oView) {
2413
- if (oView.makes) {
2414
- return oView.makes;
2415
- }
2416
- const makes = [];
2417
- oView.makes = makes;
2418
- const eventsObject = {};
2419
- const eventsList = [];
2420
- const selectorObject = {};
2421
- const mixins = Reflect.get(oView.prototype, "mixins");
2422
- if (mixins && Array.isArray(mixins)) {
2423
- _View.mergeMixins(mixins, oView, makes);
2424
- }
2425
- for (const p in oView.prototype) {
2426
- if (!hasOwnProperty(oView.prototype, p)) continue;
2427
- const currentFn = Reflect.get(oView.prototype, p);
2428
- if (typeof currentFn !== "function") continue;
2429
- const matches = p.match(VIEW_EVENT_METHOD_REGEXP);
2430
- if (!matches) continue;
2431
- const isSelector = matches[1];
2432
- const selectorOrCallback = matches[2];
2433
- const events = matches[3];
2434
- const modifiers = matches[4];
2435
- const mod = {};
2436
- if (modifiers) {
2437
- for (const item of modifiers.split(",")) {
2438
- mod[item] = true;
2439
- }
2440
- }
2441
- const eventTypes = events.split(",");
2442
- for (const item of eventTypes) {
2443
- const globalNode = VIEW_GLOBALS[selectorOrCallback];
2444
- let mask = 1;
2445
- if (isSelector) {
2446
- if (globalNode) {
2447
- eventsList.push({
2448
- handler: currentFn,
2449
- element: globalNode,
2450
- eventName: item,
2451
- modifiers: mod
2452
- });
2453
- continue;
2454
- }
2455
- mask = 2;
2456
- let selectorEntry = selectorObject[item];
2457
- if (!selectorEntry) {
2458
- selectorEntry = selectorObject[item] = {
2459
- selectors: []
2460
- };
2461
- }
2462
- if (!selectorEntry[selectorOrCallback]) {
2463
- selectorEntry[selectorOrCallback] = 1;
2464
- selectorEntry.selectors.push(selectorOrCallback);
2465
- }
2466
- }
2467
- eventsObject[item] = (eventsObject[item] || 0) | mask;
2468
- const combinedKey = selectorOrCallback + SPLITTER + item;
2469
- const existingFn = Reflect.get(oView.prototype, combinedKey);
2470
- if (!existingFn) {
2471
- Reflect.set(oView.prototype, combinedKey, currentFn);
2472
- } else if (typeof existingFn === "function") {
2473
- const mixinFn = currentFn;
2474
- const existingMixin = existingFn;
2475
- if (existingMixin.marker) {
2476
- if (mixinFn.marker) {
2477
- Reflect.set(
2478
- oView.prototype,
2479
- combinedKey,
2480
- _View.processMixinsSameEvent(mixinFn, existingMixin)
2481
- );
2482
- } else if (hasOwnProperty(oView.prototype, p)) {
2483
- Reflect.set(oView.prototype, combinedKey, currentFn);
2484
- }
2485
- }
2486
- }
2050
+ function init(_params) {
2051
+ }
2052
+ function getTemplate() {
2053
+ return mutable.template;
2054
+ }
2055
+ function setTemplate(v) {
2056
+ mutable.template = v;
2057
+ }
2058
+ function getObservedStateKeys() {
2059
+ return mutable.observedStateKeys;
2060
+ }
2061
+ function setObservedStateKeys(v) {
2062
+ mutable.observedStateKeys = v;
2063
+ }
2064
+ function getEndUpdatePending() {
2065
+ return mutable.endUpdatePending;
2066
+ }
2067
+ function setEndUpdatePending(v) {
2068
+ mutable.endUpdatePending = v;
2069
+ }
2070
+ function getEvents() {
2071
+ return mutable.events;
2072
+ }
2073
+ function setEvents(v) {
2074
+ mutable.events = v;
2075
+ }
2076
+ function getAssign() {
2077
+ return mutable.assignFn;
2078
+ }
2079
+ function setAssign(v) {
2080
+ mutable.assignFn = v;
2081
+ }
2082
+ const ctx = {
2083
+ id,
2084
+ owner: frame,
2085
+ updater,
2086
+ signature,
2087
+ rendered,
2088
+ getTemplate,
2089
+ setTemplate,
2090
+ locationObserved,
2091
+ getObservedStateKeys,
2092
+ setObservedStateKeys,
2093
+ resources,
2094
+ emitter: emitter2,
2095
+ getEndUpdatePending,
2096
+ setEndUpdatePending,
2097
+ getEvents,
2098
+ setEvents,
2099
+ cleanups,
2100
+ getAssign,
2101
+ setAssign,
2102
+ render,
2103
+ init,
2104
+ beginUpdate,
2105
+ endUpdate,
2106
+ wrapAsync,
2107
+ observeLocation,
2108
+ observeState,
2109
+ capture,
2110
+ release,
2111
+ leaveTip,
2112
+ fire,
2113
+ on,
2114
+ off
2115
+ };
2116
+ return ctx;
2117
+ }
2118
+ function registerEvents(ctx) {
2119
+ const events = ctx.getEvents();
2120
+ if (!events) return;
2121
+ for (const key of Object.keys(events)) {
2122
+ if (!hasOwnProperty(events, key)) continue;
2123
+ const handler = events[key];
2124
+ if (typeof handler !== "function") continue;
2125
+ const matches = key.match(VIEW_EVENT_METHOD_REGEXP);
2126
+ if (!matches) continue;
2127
+ const isSelector = matches[1];
2128
+ const selectorOrCallback = matches[2];
2129
+ const eventTypes = matches[3];
2130
+ const modifiers = matches[4];
2131
+ const mod = {};
2132
+ if (modifiers) {
2133
+ for (const item of modifiers.split(",")) {
2134
+ mod[item] = true;
2135
+ }
2136
+ }
2137
+ for (const eventType of eventTypes.split(",")) {
2138
+ const globalNode = VIEW_GLOBALS[selectorOrCallback];
2139
+ if (isSelector && globalNode) {
2140
+ registerGlobalEvent(ctx, globalNode, eventType, handler, mod, key);
2141
+ } else if (isSelector) {
2142
+ EventDelegator.bind(eventType, true);
2143
+ } else {
2144
+ EventDelegator.bind(eventType, false);
2145
+ }
2146
+ }
2147
+ }
2148
+ }
2149
+ function unregisterEvents(ctx) {
2150
+ const events = ctx.getEvents();
2151
+ if (!events) return;
2152
+ for (const key of Object.keys(events)) {
2153
+ if (!hasOwnProperty(events, key)) continue;
2154
+ const matches = key.match(VIEW_EVENT_METHOD_REGEXP);
2155
+ if (!matches) continue;
2156
+ const isSelector = matches[1];
2157
+ const selectorOrCallback = matches[2];
2158
+ const eventTypes = matches[3];
2159
+ for (const eventType of eventTypes.split(",")) {
2160
+ const globalNode = VIEW_GLOBALS[selectorOrCallback];
2161
+ if (isSelector && globalNode) {
2162
+ } else if (isSelector) {
2163
+ EventDelegator.unbind(eventType, true);
2164
+ } else {
2165
+ EventDelegator.unbind(eventType, false);
2487
2166
  }
2488
2167
  }
2489
- _View.wrapMethod(asRecord(oView.prototype), "render", "$renderWrap");
2490
- Reflect.set(oView.prototype, "$evtObjMap", eventsObject);
2491
- Reflect.set(oView.prototype, "$globalEvtList", eventsList);
2492
- Reflect.set(oView.prototype, "$selMap", selectorObject);
2493
- return makes;
2494
2168
  }
2495
- /**
2496
- * Bind or unbind event delegation for a view instance.
2497
- * Called from Frame during mount/unmount.
2498
- */
2499
- static delegateEvents(view, destroy = false) {
2500
- const eventsObject = view.eventObjectMap;
2501
- const selectorObject = view.eventSelectorMap;
2502
- const eventsList = view.globalEventList;
2503
- for (const e in eventsObject) {
2504
- if (hasOwnProperty(eventsObject, e)) {
2505
- if (destroy) {
2506
- EventDelegator.unbind(e, !!selectorObject[e]);
2507
- } else {
2508
- EventDelegator.bind(e, !!selectorObject[e]);
2169
+ EventDelegator.clearRangeEvents(ctx.id);
2170
+ }
2171
+ function registerGlobalEvent(ctx, element, eventName, handler, modifiers, _key) {
2172
+ const listener = {
2173
+ handleEvent(domEvent) {
2174
+ Reflect.set(domEvent, "eventTarget", element);
2175
+ if (modifiers) {
2176
+ const ctrlKey = Reflect.get(domEvent, "ctrlKey");
2177
+ const shiftKey = Reflect.get(domEvent, "shiftKey");
2178
+ const altKey = Reflect.get(domEvent, "altKey");
2179
+ const metaKey = Reflect.get(domEvent, "metaKey");
2180
+ if (modifiers["ctrl"] && !ctrlKey || modifiers["shift"] && !shiftKey || modifiers["alt"] && !altKey || modifiers["meta"] && !metaKey) {
2181
+ return;
2509
2182
  }
2510
2183
  }
2184
+ funcWithTry(handler, [domEvent], ctx, noop);
2511
2185
  }
2512
- for (const entry of eventsList) {
2513
- if (destroy) {
2514
- entry.element.removeEventListener(
2515
- entry.eventName,
2516
- entry.boundHandler
2517
- );
2518
- } else {
2519
- const handler = entry.handler;
2520
- const element = entry.element;
2521
- const modifiers = entry.modifiers;
2522
- entry.boundHandler = function(domEvent) {
2523
- const extendedEvent = domEvent;
2524
- extendedEvent.eventTarget = element;
2525
- if (modifiers) {
2526
- const kbEvent = domEvent;
2527
- if (modifiers["ctrl"] && !kbEvent.ctrlKey || modifiers["shift"] && !kbEvent.shiftKey || modifiers["alt"] && !kbEvent.altKey || modifiers["meta"] && !kbEvent.metaKey) {
2528
- return;
2529
- }
2530
- }
2531
- funcWithTry(handler, [domEvent], view, noop);
2532
- };
2533
- entry.element.addEventListener(
2534
- entry.eventName,
2535
- entry.boundHandler
2536
- );
2186
+ };
2187
+ element.addEventListener(eventName, listener);
2188
+ ctx.on("destroy", () => {
2189
+ element.removeEventListener(eventName, listener);
2190
+ });
2191
+ }
2192
+ function destroyAllResources(ctx, lastly) {
2193
+ const cache = ctx.resources;
2194
+ for (const p in cache) {
2195
+ if (hasOwnProperty(cache, p)) {
2196
+ const entry = cache[p];
2197
+ if (lastly || entry.destroyOnRender) {
2198
+ destroyResource(cache, p, true);
2537
2199
  }
2538
2200
  }
2539
2201
  }
2540
- /**
2541
- * Destroy all resources managed by a view.
2542
- * If lastly=true, destroy ALL resources; otherwise only destroyOnRender ones.
2543
- */
2544
- static destroyAllResources(view, lastly) {
2545
- const cache = view.resources;
2546
- for (const p in cache) {
2547
- if (hasOwnProperty(cache, p)) {
2548
- const entry = cache[p];
2549
- if (lastly || entry.destroyOnRender) {
2550
- _View.destroyResource(cache, p, true);
2551
- }
2552
- }
2202
+ }
2203
+ function destroyResource(cache, key, callDestroy, oldEntity) {
2204
+ const entry = cache[key];
2205
+ if (!entry || entry.entity === oldEntity) return void 0;
2206
+ const entity = entry.entity;
2207
+ if (entity && typeof entity === "object") {
2208
+ const destroyFn = Reflect.get(entity, "destroy");
2209
+ if (typeof destroyFn === "function" && callDestroy) {
2210
+ funcWithTry(destroyFn, [], entity, noop);
2553
2211
  }
2554
2212
  }
2555
- /**
2556
- * Process deferred invoke calls on a frame.
2557
- */
2558
- static runInvokes(frame) {
2559
- const list = frame.invokeList;
2560
- if (!list) return;
2561
- while (list.length) {
2562
- const entry = list.shift();
2563
- if (entry && !entry.removed) {
2564
- frame.invoke(entry.name, entry.args);
2565
- }
2213
+ Reflect.deleteProperty(cache, key);
2214
+ return entity;
2215
+ }
2216
+ function runInvokes(frame) {
2217
+ const list = frame.invokeList;
2218
+ if (!list) return;
2219
+ while (list.length) {
2220
+ const entry = list.shift();
2221
+ if (entry && !entry.removed) {
2222
+ frame.invoke(entry.name, entry.args);
2566
2223
  }
2567
2224
  }
2568
- // ============================================================
2569
- // Static private methods
2570
- // ============================================================
2571
- /**
2572
- * Wrap a method on the prototype to add signature checking and resource cleanup.
2573
- */
2574
- static wrapMethod(proto, fnName, shortKey) {
2575
- const originalFn = proto[fnName];
2576
- if (typeof originalFn !== "function") return;
2577
- const originalAsFn = originalFn;
2578
- const wrapped = function(...args) {
2579
- if (this.signature > 0) {
2580
- this.signature++;
2581
- this.fire("render");
2582
- _View.destroyAllResources(this, false);
2583
- const lookup = asRecord(this);
2584
- const candidate = lookup[fnName];
2585
- const instanceFn = typeof candidate === "function" ? candidate : originalAsFn;
2586
- const fnToCall = instanceFn === wrapped ? originalAsFn : instanceFn;
2587
- return funcWithTry(fnToCall, args, this, noop);
2588
- }
2589
- return void 0;
2590
- };
2591
- proto[fnName] = wrapped;
2592
- proto[shortKey] = wrapped;
2225
+ }
2226
+ function mountCtx(frame, setup, params) {
2227
+ const ctx = createCtx(frame);
2228
+ setCurrentCtx(ctx);
2229
+ let descriptor;
2230
+ try {
2231
+ descriptor = setup(ctx, params);
2232
+ } finally {
2233
+ setCurrentCtx(null);
2234
+ }
2235
+ ctx.setTemplate(descriptor.template);
2236
+ ctx.setEvents(descriptor.events);
2237
+ if (descriptor.assign) {
2238
+ ctx.setAssign(descriptor.assign);
2239
+ }
2240
+ ctx.signature.value = 1;
2241
+ frame.view = ctx;
2242
+ registerEvents(ctx);
2243
+ if (ctx.getTemplate()) {
2244
+ ctx.render();
2245
+ } else {
2246
+ ctx.endUpdate();
2593
2247
  }
2594
- /**
2595
- * When two mixins define the same event method, merge them into
2596
- * a single function that calls both in sequence.
2597
- */
2598
- static processMixinsSameEvent(additional, exist) {
2599
- let temp;
2600
- if (exist.handlerList) {
2601
- temp = exist;
2602
- } else {
2603
- const merged = function(...e) {
2604
- funcWithTry(merged.handlerList ?? [], e, this, noop);
2605
- };
2606
- merged.handlerList = [exist];
2607
- merged.marker = 1;
2608
- temp = merged;
2609
- }
2610
- temp.handlerList = (temp.handlerList ?? []).concat(
2611
- additional.handlerList ?? [additional]
2612
- );
2613
- return temp;
2248
+ return ctx;
2249
+ }
2250
+ function unmountCtx(ctx) {
2251
+ for (let i = ctx.cleanups.length - 1; i >= 0; i--) {
2252
+ const cleanup = ctx.cleanups[i];
2253
+ funcWithTry(cleanup, [], null, noop);
2614
2254
  }
2615
- /**
2616
- * Merge an array of mixin objects into the view prototype.
2617
- */
2618
- static mergeMixins(mixins, viewClass, makes) {
2619
- const proto = asRecord(viewClass.prototype);
2620
- const temp = {};
2621
- for (const node of mixins) {
2622
- for (const p in node) {
2623
- if (!hasOwnProperty(node, p)) continue;
2624
- const fn = node[p];
2625
- if (typeof fn !== "function") continue;
2626
- const mixinFn = fn;
2627
- const exist = temp[p];
2628
- if (p === "make") {
2629
- makes.push(mixinFn);
2630
- continue;
2631
- }
2632
- if (VIEW_EVENT_METHOD_REGEXP.test(p)) {
2633
- if (exist) {
2634
- temp[p] = _View.processMixinsSameEvent(mixinFn, exist);
2635
- } else {
2636
- mixinFn.marker = 1;
2637
- temp[p] = mixinFn;
2638
- }
2639
- } else if (!exist) {
2640
- temp[p] = mixinFn;
2641
- }
2642
- }
2643
- }
2644
- for (const p in temp) {
2645
- if (!hasOwnProperty(proto, p)) {
2646
- proto[p] = temp[p];
2647
- }
2648
- }
2255
+ ctx.cleanups.length = 0;
2256
+ unregisterEvents(ctx);
2257
+ destroyAllResources(ctx, true);
2258
+ if (ctx.signature.value > 0) {
2259
+ ctx.fire("destroy", void 0, true, true);
2649
2260
  }
2650
- /**
2651
- * Destroy a single resource entry.
2652
- */
2653
- static destroyResource(cache, key, callDestroy, oldEntity) {
2654
- const entry = cache[key];
2655
- if (!entry || entry.entity === oldEntity) return void 0;
2656
- const entity = entry.entity;
2657
- if (entity && typeof entity === "object") {
2658
- const destroyFn = entity["destroy"];
2659
- if (typeof destroyFn === "function" && callDestroy) {
2660
- funcWithTry(destroyFn, [], entity, noop);
2661
- }
2662
- }
2663
- Reflect.deleteProperty(cache, key);
2664
- return entity;
2665
- }
2666
- // ============================================================
2667
- // Static: extend and merge
2668
- // ============================================================
2669
- /**
2670
- * Extend View to create a new View subclass.
2671
- *
2672
- * Supports:
2673
- * - props.make: constructor-like init (called with initParams + {node, deep})
2674
- * - props.mixins: array of mixin objects
2675
- * - Event method patterns: `'name<click>'` etc.
2676
- */
2677
- static extend(props, statics) {
2678
- const definedProps = props ?? {};
2679
- const make = definedProps["make"];
2680
- const makes = [];
2681
- if (typeof make === "function") {
2682
- makes.push(make);
2683
- }
2684
- const ParentView = this;
2685
- const ChildView = class extends ParentView {
2686
- constructor(nodeId, ownerFrame, initParams, node, mixinCtors) {
2687
- super(nodeId, ownerFrame, initParams, node, []);
2688
- const instanceProps = this;
2689
- for (const key in definedProps) {
2690
- if (hasOwnProperty(definedProps, key) && key !== "make" && key !== "render") {
2691
- instanceProps[key] = definedProps[key];
2692
- }
2693
- }
2694
- this.id = nodeId;
2695
- this.owner = ownerFrame;
2696
- this.updater = new Updater(nodeId);
2697
- const params = [
2698
- initParams,
2699
- {
2700
- node,
2701
- deep: !this.template
2702
- }
2703
- ];
2704
- const concatCtors = makes.concat(mixinCtors || []);
2705
- if (concatCtors.length) {
2706
- funcWithTry(concatCtors, params, this, noop);
2707
- }
2708
- }
2709
- };
2710
- for (const key in definedProps) {
2711
- if (hasOwnProperty(definedProps, key) && key !== "make") {
2712
- Reflect.set(ChildView.prototype, key, definedProps[key]);
2713
- }
2714
- }
2715
- if (statics) {
2716
- for (const key in statics) {
2717
- if (hasOwnProperty(statics, key)) {
2718
- Reflect.set(ChildView, key, statics[key]);
2719
- }
2261
+ EventDelegator.clearRangeEvents(ctx.id);
2262
+ ctx.signature.value = 0;
2263
+ }
2264
+
2265
+ // src/module-loader.ts
2266
+ var config = {
2267
+ rootId: "root",
2268
+ routeMode: "history",
2269
+ hashbang: "#!",
2270
+ error: (error) => {
2271
+ throw error;
2272
+ }
2273
+ };
2274
+ function use(names, callback) {
2275
+ const nameList = typeof names === "string" ? [names] : names;
2276
+ const loadPromise = (() => {
2277
+ if (config.require) {
2278
+ const result = config.require(nameList);
2279
+ if (result && typeof result.then === "function") {
2280
+ return result;
2720
2281
  }
2282
+ return Promise.resolve([]);
2721
2283
  }
2722
- return ChildView;
2723
- }
2724
- /**
2725
- * Merge mixins into View prototype.
2726
- */
2727
- static merge(...mixins) {
2728
- const existingCtors = this.makes || [];
2729
- _View.mergeMixins(mixins, this, existingCtors);
2730
- return this;
2731
- }
2732
- // ============================================================
2733
- // HMR support (static accept / dispose)
2734
- // ============================================================
2735
- /**
2736
- * Set up HMR accept handler for this view module.
2737
- *
2738
- * When the module is hot-replaced, the new View class is extracted from
2739
- * the new module, registered in the view registry, and all currently
2740
- * mounted frames using this viewPath are re-mounted.
2741
- *
2742
- * No-op when `hot` is undefined (production / non-HMR environment).
2743
- *
2744
- * ```ts
2745
- * if (import.meta.hot) {
2746
- * HomeView.accept(import.meta.hot, 'home');
2747
- * }
2748
- * ```
2749
- */
2750
- static accept(hot, viewPath) {
2751
- if (!hot) return;
2752
- acceptView(hot, viewPath);
2284
+ return Promise.all(
2285
+ nameList.map((name) => {
2286
+ const importPath = name.startsWith(".") || name.startsWith("/") ? name : `./${name}`;
2287
+ return import(
2288
+ /* @vite-ignore */
2289
+ /* webpackIgnore: true */
2290
+ importPath
2291
+ ).then((mod) => {
2292
+ return mod && (mod["__esModule"] || // For Webpack
2293
+ typeof mod["default"] === "function") ? mod["default"] : mod;
2294
+ }).catch((err) => {
2295
+ const errorHandler = config.error;
2296
+ if (errorHandler) {
2297
+ errorHandler(err instanceof Error ? err : new Error(String(err)));
2298
+ }
2299
+ return void 0;
2300
+ });
2301
+ })
2302
+ );
2303
+ })();
2304
+ if (callback) {
2305
+ loadPromise.then((modules) => {
2306
+ callback(...modules);
2307
+ });
2753
2308
  }
2754
- /**
2755
- * Set up HMR dispose handler for this view module.
2756
- *
2757
- * When the module is about to be replaced, the old View class is removed
2758
- * from the registry so subsequent lookups don't return the stale class.
2759
- *
2760
- * No-op when `hot` is undefined (production / non-HMR environment).
2761
- *
2762
- * ```ts
2763
- * if (import.meta.hot) {
2764
- * HomeView.dispose(import.meta.hot, 'home');
2765
- * }
2766
- * ```
2767
- */
2768
- static dispose(hot, viewPath) {
2769
- if (!hot) return;
2770
- disposeView(hot, viewPath);
2309
+ return loadPromise;
2310
+ }
2311
+
2312
+ // src/view-registry.ts
2313
+ var viewSetupRegistry = {};
2314
+ function getViewClass(path) {
2315
+ return viewSetupRegistry[path];
2316
+ }
2317
+ function registerViewClass(viewPath, setup) {
2318
+ const parsed = parseUri(viewPath);
2319
+ const path = parsed.path;
2320
+ if (path) {
2321
+ viewSetupRegistry[path] = setup;
2771
2322
  }
2772
- };
2323
+ }
2773
2324
 
2774
2325
  // src/frame.ts
2326
+ var frameRegistry = /* @__PURE__ */ new Map();
2775
2327
  var rootFrame;
2776
2328
  var globalAlter;
2777
- var MAX_FRAME_POOL = 64;
2778
- var frameCache = [];
2779
- var staticEmitter = new EventEmitter();
2780
- var Frame = class _Frame extends EventEmitter {
2781
- /** Frame ID (same as owner DOM element ID) */
2782
- id;
2783
- /** Parent Frame ID */
2784
- _parentId = void 0;
2785
- get parentId() {
2786
- return this._parentId;
2787
- }
2788
- /** Children map: id -> id */
2789
- childrenMap = {};
2790
- /** Children count */
2791
- childrenCount = 0;
2792
- /** Ready count (children that have fired 'created') */
2793
- readyCount = 0;
2794
- /** Set of child frame IDs that have fired 'created' */
2795
- readyMap = /* @__PURE__ */ new Set();
2796
- /** View instance */
2797
- viewInstance;
2798
- /** Get view instance (read-only) */
2799
- get view() {
2800
- return this.viewInstance;
2801
- }
2802
- /** Invoke list for deferred method calls */
2803
- invokeList = [];
2804
- /** Signature for async operation tracking */
2805
- signature = 1;
2806
- /** Whether view has altered */
2807
- hasAltered = 0;
2808
- /** Whether view is destroyed */
2809
- destroyed = 0;
2810
- /** View path (v-lark attribute value) */
2811
- viewPath;
2812
- /** Original template before mount */
2813
- originalTemplate;
2814
- /** Hold fire created flag */
2815
- holdFireCreated = 0;
2816
- /** Children created flag */
2817
- childrenCreated = 0;
2818
- /** Children alter flag */
2819
- childrenAlter = 0;
2820
- constructor(id, parentId) {
2821
- super();
2822
- this.id = id;
2823
- if (parentId) {
2824
- this._parentId = parentId;
2825
- }
2826
- registerFrame(id, this);
2827
- const element = document.getElementById(id);
2828
- if (element) {
2829
- element.frame = this;
2830
- element.frameBound = 1;
2831
- }
2832
- _Frame.fire("add", { frame: this });
2833
- }
2834
- // ============================================================
2835
- // Instance methods
2836
- // ============================================================
2837
- /**
2838
- * Mount a view to this frame.
2839
- *
2840
- * Complete flow:
2841
- * 1. Parse viewPath, translate query params from parent
2842
- * 2. Unmount current view
2843
- * 3. Load View class (via require or provided ViewClass)
2844
- * 4. View_Prepare (scan event methods)
2845
- * 5. Create View instance
2846
- * 6. View_DelegateEvents (bind DOM events)
2847
- * 7. Call view.init()
2848
- * 8. If view has template, call render via Updater
2849
- * 9. If no template, call endUpdate directly
2850
- */
2851
- mountView(viewPath, viewInitParams) {
2852
- const node = document.getElementById(this.id);
2853
- const pId = this.parentId;
2854
- if (!this.hasAltered && node) {
2855
- this.hasAltered = 1;
2856
- this.originalTemplate = node.innerHTML;
2857
- }
2858
- this.unmountView();
2859
- this.destroyed = 0;
2860
- const parsed = parseUri(viewPath || "");
2861
- const viewClassName = parsed.path;
2862
- if (!node || !viewClassName) return;
2863
- this.viewPath = viewPath;
2864
- const params = parsed["params"];
2865
- translateQuery(pId || this.id, viewPath, params);
2866
- const initParams = { ...params };
2867
- if (viewInitParams) {
2868
- assign(initParams, viewInitParams);
2869
- }
2870
- const sign = this.signature;
2871
- const registered = getViewClass(viewClassName);
2872
- if (registered) {
2873
- this.doMountView(registered, initParams, node, sign);
2874
- return;
2875
- }
2876
- use(viewClassName, (ViewClass) => {
2877
- if (sign !== this.signature) return;
2878
- if (typeof ViewClass === "function") {
2879
- const ViewClassTyped = ViewClass;
2880
- registerViewClass(viewClassName, ViewClassTyped);
2881
- this.doMountView(ViewClassTyped, initParams, node, sign);
2882
- } else {
2883
- const error = new Error(`Cannot load view: ${viewClassName}`);
2884
- const errorHandler = config.error;
2885
- if (errorHandler) {
2886
- errorHandler(error);
2887
- }
2329
+ var staticEmitter = createEmitter();
2330
+ function isViewSetup(fn) {
2331
+ return typeof fn === "function";
2332
+ }
2333
+ function createFrame(id, parentId) {
2334
+ const emitter2 = createEmitter();
2335
+ const invokeList = [];
2336
+ const childrenMap = {};
2337
+ const readyMap = /* @__PURE__ */ new Set();
2338
+ let viewPath;
2339
+ function getViewPath() {
2340
+ return viewPath;
2341
+ }
2342
+ const frame = {
2343
+ id,
2344
+ getViewPath,
2345
+ parentId,
2346
+ view: void 0,
2347
+ invokeList,
2348
+ signature: 1,
2349
+ destroyed: 0,
2350
+ hasAltered: 0,
2351
+ originalTemplate: void 0,
2352
+ holdFireCreated: 0,
2353
+ childrenCreated: 0,
2354
+ childrenAlter: 0,
2355
+ childrenMap,
2356
+ childrenCount: 0,
2357
+ readyCount: 0,
2358
+ readyMap,
2359
+ emitter: emitter2,
2360
+ mountView(viewPathArg, viewInitParams) {
2361
+ const node = document.getElementById(frame.id);
2362
+ const pId = frame.parentId;
2363
+ if (!frame.hasAltered && node) {
2364
+ frame.hasAltered = 1;
2365
+ frame.originalTemplate = node.innerHTML;
2366
+ }
2367
+ frame.unmountView();
2368
+ frame.destroyed = 0;
2369
+ const parsed = parseUri(viewPathArg || "");
2370
+ const viewClassName = parsed.path;
2371
+ if (!node || !viewClassName) return;
2372
+ viewPath = viewPathArg;
2373
+ const params = parsed.params;
2374
+ translateQuery(pId ?? frame.id, viewPathArg, params);
2375
+ const initParams = { ...params };
2376
+ if (viewInitParams) {
2377
+ assign(initParams, viewInitParams);
2378
+ }
2379
+ const sign = frame.signature;
2380
+ const registered = getViewClass(viewClassName);
2381
+ if (registered) {
2382
+ doMountView(registered, initParams, node, sign);
2383
+ return;
2888
2384
  }
2889
- });
2890
- }
2891
- /**
2892
- * Internal: actually mount the view after class is loaded.
2893
- */
2894
- doMountView(ViewClass, params, node, sign) {
2895
- if (sign !== this.signature) return;
2896
- const mixinConstructors = View.prepare(ViewClass);
2897
- const Constructor = ViewClass;
2898
- const view = new Constructor(
2899
- this.id,
2900
- this,
2901
- params,
2902
- node,
2903
- mixinConstructors
2904
- );
2905
- this.viewInstance = view;
2906
- view.signature = 1;
2907
- View.delegateEvents(view);
2908
- const initResult = funcWithTry(
2909
- view.init,
2910
- [params, { node, deep: !view.template }],
2911
- view,
2912
- noop
2913
- );
2914
- const nextSign = ++this.signature;
2915
- Promise.resolve(initResult).then(() => {
2916
- if (nextSign !== this.signature) return;
2917
- if (view.template) {
2918
- view.render();
2919
- } else {
2920
- this.hasAltered = 0;
2921
- if (!view.endUpdatePendingFlag) {
2922
- view.endUpdate();
2385
+ use(viewClassName, (loadedModule) => {
2386
+ if (sign !== frame.signature) return;
2387
+ if (isViewSetup(loadedModule)) {
2388
+ registerViewClass(viewClassName, loadedModule);
2389
+ doMountView(loadedModule, initParams, node, sign);
2390
+ } else {
2391
+ const error = new Error(`Cannot load view: ${viewClassName}`);
2392
+ const errorHandler = config.error;
2393
+ if (errorHandler) {
2394
+ errorHandler(error);
2395
+ }
2923
2396
  }
2397
+ });
2398
+ },
2399
+ unmountView() {
2400
+ const currentView = frame.view;
2401
+ frame.invokeList.length = 0;
2402
+ if (!currentView) return;
2403
+ if (!globalAlter) {
2404
+ globalAlter = { id: frame.id };
2405
+ }
2406
+ frame.destroyed = 1;
2407
+ frame.unmountZone();
2408
+ notifyAlter(frame, globalAlter);
2409
+ unmountCtx(currentView);
2410
+ frame.view = void 0;
2411
+ const node = document.getElementById(frame.id);
2412
+ if (node && frame.originalTemplate) {
2413
+ node.innerHTML = frame.originalTemplate;
2414
+ }
2415
+ globalAlter = void 0;
2416
+ unmark(currentView);
2417
+ },
2418
+ mountFrame(frameId, viewPathArg, viewInitParams) {
2419
+ notifyAlter(frame, { id: frameId });
2420
+ let childFrame = frameRegistry.get(frameId);
2421
+ if (!childFrame) {
2422
+ if (!frame.childrenMap[frameId]) {
2423
+ frame.childrenCount++;
2424
+ }
2425
+ frame.childrenMap[frameId] = frameId;
2426
+ childFrame = createFrame(frameId, frame.id);
2924
2427
  }
2925
- });
2926
- }
2927
- /**
2928
- * Unmount current view.
2929
- */
2930
- unmountView() {
2931
- const view = this.view;
2932
- this.invokeList = [];
2933
- if (!view) return;
2934
- if (!globalAlter) {
2935
- globalAlter = { id: this.id };
2936
- }
2937
- this.destroyed = 1;
2938
- this.unmountZone();
2939
- notifyAlter(this, globalAlter);
2940
- if (view.signature > 0) {
2941
- view.fire("destroy", void 0, true, true);
2942
- }
2943
- EventDelegator.clearRangeEvents(this.id);
2944
- delete this["viewInstance"];
2945
- const node = document.getElementById(this.id);
2946
- if (node && this.originalTemplate) {
2947
- node.innerHTML = this.originalTemplate;
2948
- }
2949
- globalAlter = void 0;
2950
- unmark(view);
2951
- }
2952
- /**
2953
- * Mount a child frame.
2954
- */
2955
- mountFrame(frameId, viewPath, viewInitParams) {
2956
- notifyAlter(this, { id: frameId });
2957
- let childFrame = getFrame(frameId);
2958
- if (!childFrame) {
2959
- if (!this.childrenMap[frameId]) {
2960
- this.childrenCount++;
2961
- }
2962
- this.childrenMap[frameId] = frameId;
2963
- childFrame = frameCache.pop();
2964
- if (childFrame) {
2965
- reInitFrame(childFrame, frameId, this.id);
2966
- } else {
2967
- childFrame = new _Frame(frameId, this.id);
2428
+ childFrame.mountView(viewPathArg, viewInitParams);
2429
+ return childFrame;
2430
+ },
2431
+ unmountFrame(id2) {
2432
+ const targetId = id2 ? frame.childrenMap[id2] : frame.id;
2433
+ const targetFrame = frameRegistry.get(targetId);
2434
+ if (!targetFrame) return;
2435
+ const wasCreated = targetFrame.readyCount > 0;
2436
+ const pId = targetFrame.parentId;
2437
+ targetFrame.unmountView();
2438
+ removeFrame(targetId, wasCreated);
2439
+ const parent = frameRegistry.get(pId ?? "");
2440
+ if (parent && parent.childrenMap[targetId]) {
2441
+ Reflect.deleteProperty(parent.childrenMap, targetId);
2442
+ parent.childrenCount--;
2443
+ notifyCreated(parent);
2968
2444
  }
2969
- }
2970
- childFrame.mountView(viewPath, viewInitParams);
2971
- return childFrame;
2972
- }
2973
- /**
2974
- * Unmount a child frame.
2975
- */
2976
- unmountFrame(id) {
2977
- const targetId = id ? this.childrenMap[id] : this.id;
2978
- const frame = getFrame(targetId);
2979
- if (!frame) return;
2980
- const wasCreated = frame.readyCount > 0;
2981
- const pId = frame.parentId;
2982
- frame.unmountView();
2983
- removeFrame2(targetId, wasCreated);
2984
- reInitFrameForCache(frame);
2985
- if (frameCache.length < MAX_FRAME_POOL) {
2986
- frameCache.push(frame);
2987
- }
2988
- const parent = getFrame(pId || "");
2989
- if (parent && parent.childrenMap[targetId]) {
2990
- Reflect.deleteProperty(parent.childrenMap, targetId);
2991
- parent.childrenCount--;
2992
- notifyCreated(parent);
2993
- }
2994
- }
2995
- /**
2996
- * Mount all views in a zone.
2997
- */
2998
- mountZone(zoneId) {
2999
- const targetZone = zoneId || this.id;
3000
- this.holdFireCreated = 1;
3001
- const rootEl = document.getElementById(targetZone);
3002
- if (!rootEl) return;
3003
- const viewElements = rootEl.querySelectorAll(`[${LARK_VIEW}]`);
3004
- const frames = [];
3005
- viewElements.forEach((el) => {
3006
- if (!(el instanceof HTMLElement)) return;
3007
- if (htmlElIsBound(el)) return;
3008
- const elId = ensureElementId(el, "frame_");
3009
- el.frameBound = 1;
3010
- const viewPath = getAttribute(el, LARK_VIEW);
3011
- frames.push([elId, viewPath]);
3012
- });
3013
- for (const [frameId, viewPath] of frames) {
3014
- this.mountFrame(frameId, viewPath);
3015
- }
3016
- this.holdFireCreated = 0;
3017
- notifyCreated(this);
3018
- }
3019
- /**
3020
- * Unmount all views in a zone.
3021
- */
3022
- unmountZone(zoneId) {
3023
- for (const childId in this.childrenMap) {
3024
- if (hasOwnProperty(this.childrenMap, childId)) {
3025
- if (!zoneId || childId !== zoneId) {
3026
- this.unmountFrame(childId);
2445
+ },
2446
+ mountZone(zoneId) {
2447
+ const targetZone = zoneId ?? frame.id;
2448
+ frame.holdFireCreated = 1;
2449
+ const rootEl = document.getElementById(targetZone);
2450
+ if (!rootEl) return;
2451
+ const viewElements = rootEl.querySelectorAll(`[${LARK_VIEW}]`);
2452
+ const frames = [];
2453
+ viewElements.forEach((el) => {
2454
+ if (!(el instanceof HTMLElement)) return;
2455
+ if (htmlElIsBound(el)) return;
2456
+ const elId = ensureElementId(el, "frame_");
2457
+ Reflect.set(el, "frameBound", 1);
2458
+ const viewPathArg = getAttribute(el, LARK_VIEW);
2459
+ if (viewPathArg) {
2460
+ frames.push([elId, viewPathArg]);
3027
2461
  }
2462
+ });
2463
+ for (const [frameId, viewPathArg] of frames) {
2464
+ frame.mountFrame(frameId, viewPathArg);
3028
2465
  }
3029
- }
3030
- notifyCreated(this);
3031
- }
3032
- /**
3033
- * Get all child frame IDs.
3034
- */
3035
- children() {
3036
- const result = [];
3037
- for (const id in this.childrenMap) {
3038
- if (hasOwnProperty(this.childrenMap, id)) {
3039
- result.push(id);
3040
- }
3041
- }
3042
- return result;
3043
- }
3044
- /**
3045
- * Get parent frame at given level.
3046
- * @param level - How many levels up (default 1)
3047
- */
3048
- parent(level = 1) {
3049
- let frame = void 0;
3050
- let currentPid = this.parentId;
3051
- let n = level >>> 0 || 1;
3052
- while (currentPid && n--) {
3053
- frame = getFrame(currentPid);
3054
- currentPid = frame?.parentId;
3055
- }
3056
- return frame;
3057
- }
3058
- /**
3059
- * Invoke a method on the view.
3060
- */
3061
- invoke(name, args) {
3062
- let result;
3063
- const view = this.view;
3064
- if (view && view.rendered) {
3065
- const fn = Reflect.get(view, name);
3066
- if (typeof fn === "function") {
3067
- result = funcWithTry(fn, args || [], view, noop);
2466
+ frame.holdFireCreated = 0;
2467
+ notifyCreated(frame);
2468
+ },
2469
+ unmountZone(zoneId) {
2470
+ for (const childId in frame.childrenMap) {
2471
+ if (hasOwnProperty(frame.childrenMap, childId)) {
2472
+ if (!zoneId || childId !== zoneId) {
2473
+ frame.unmountFrame(childId);
2474
+ }
2475
+ }
3068
2476
  }
3069
- } else {
3070
- const key = SPLITTER + name;
3071
- let existingEntry;
3072
- for (const entry of this.invokeList) {
3073
- if (entry.key === key) {
3074
- existingEntry = entry;
3075
- break;
2477
+ notifyCreated(frame);
2478
+ },
2479
+ parent(level = 1) {
2480
+ let result = void 0;
2481
+ let currentPid = frame.parentId;
2482
+ let n = level >>> 0 || 1;
2483
+ while (currentPid && n--) {
2484
+ result = frameRegistry.get(currentPid);
2485
+ currentPid = result?.parentId;
2486
+ }
2487
+ return result;
2488
+ },
2489
+ invoke(name, args) {
2490
+ let result;
2491
+ const currentView = frame.view;
2492
+ if (currentView && currentView.rendered.value) {
2493
+ const fn = Reflect.get(currentView, name);
2494
+ if (typeof fn === "function") {
2495
+ result = funcWithTry(fn, args ?? [], currentView, noop);
2496
+ }
2497
+ } else {
2498
+ const key = SPLITTER + name;
2499
+ let existingEntry;
2500
+ for (const entry of frame.invokeList) {
2501
+ if (entry.key === key) {
2502
+ existingEntry = entry;
2503
+ break;
2504
+ }
3076
2505
  }
2506
+ if (existingEntry) {
2507
+ existingEntry.removed = args === existingEntry.args;
2508
+ }
2509
+ const newEntry = {
2510
+ name,
2511
+ args: args ?? [],
2512
+ key
2513
+ };
2514
+ frame.invokeList.push(newEntry);
3077
2515
  }
3078
- if (existingEntry) {
3079
- existingEntry.removed = args === existingEntry.args;
2516
+ return result;
2517
+ },
2518
+ children() {
2519
+ const result = [];
2520
+ for (const id2 in frame.childrenMap) {
2521
+ if (hasOwnProperty(frame.childrenMap, id2)) {
2522
+ result.push(id2);
2523
+ }
3080
2524
  }
3081
- const newEntry = {
3082
- name,
3083
- args: args || [],
3084
- key
3085
- };
3086
- this.invokeList.push(newEntry);
2525
+ return result;
2526
+ },
2527
+ on(event, handler) {
2528
+ emitter2.on(event, handler);
2529
+ return frame;
2530
+ },
2531
+ off(event, handler) {
2532
+ emitter2.off(event, handler);
2533
+ return frame;
2534
+ },
2535
+ fire(event, data) {
2536
+ emitter2.fire(event, data);
2537
+ return frame;
3087
2538
  }
3088
- return result;
3089
- }
3090
- /**
3091
- * Type-safe variant of `invoke`.
3092
- *
3093
- * `invoke()` accepts any string and any args, which silently hides
3094
- * mismatched call sites when a method gets renamed. `invokeTyped` carries
3095
- * the view's method signature through TypeScript so the compiler catches
3096
- * those mistakes:
3097
- *
3098
- * ```ts
3099
- * type Home = View & { loadData(id: string): Promise<void> };
3100
- * frame.invokeTyped<Home, "loadData">("loadData", ["user-1"]);
3101
- * ```
3102
- *
3103
- * Behavior is identical to `invoke` at runtime — same defer / direct-call
3104
- * paths — so it's a drop-in safer overload.
3105
- */
3106
- invokeTyped(name, args) {
3107
- return this.invoke(name, args);
3108
- }
3109
- // ============================================================
3110
- // Static methods
3111
- // ============================================================
2539
+ };
2540
+ frameRegistry.set(id, frame);
2541
+ const element = document.getElementById(id);
2542
+ if (element) {
2543
+ Reflect.set(element, "frame", frame);
2544
+ Reflect.set(element, "frameBound", 1);
2545
+ }
2546
+ staticEmitter.fire("add", { frame });
2547
+ return frame;
2548
+ }
2549
+ function doMountView(setup, params, node, sign) {
2550
+ const frameId = node.id;
2551
+ const frame = frameRegistry.get(frameId);
2552
+ if (!frame) return;
2553
+ if (sign !== frame.signature) return;
2554
+ const ctx = mountCtx(frame, setup, params);
2555
+ frame.view = ctx;
2556
+ runInvokes(frame);
2557
+ }
2558
+ var Frame = {
3112
2559
  /** Get frame by ID */
3113
- static get(id) {
3114
- return getFrame(id);
3115
- }
2560
+ get(id) {
2561
+ return frameRegistry.get(id);
2562
+ },
3116
2563
  /** Get all frames */
3117
- static getAll() {
3118
- return getAllFrames();
3119
- }
2564
+ getAll() {
2565
+ return frameRegistry;
2566
+ },
3120
2567
  /**
3121
- * Returns the existing root frame, or `undefined` if none has been created.
3122
- * Pure getter — never creates a Frame, never touches the DOM.
3123
- *
3124
- * Use `Frame.createRoot(id)` to create the root explicitly during framework
3125
- * boot. For Micro-Frontend hosts that own multiple independent containers,
3126
- * use `new Frame(containerId)` directly so each MF mount has its own root.
2568
+ * Returns the existing root frame, or undefined if none has been created.
3127
2569
  */
3128
- static getRoot() {
2570
+ getRoot() {
3129
2571
  return rootFrame;
3130
- }
2572
+ },
3131
2573
  /**
3132
- * Create (or return) the singleton root frame for this app.
3133
- *
3134
- * Idempotent: subsequent calls always return the original root regardless
3135
- * of `rootId` — so passing a different id later is silently ignored.
3136
- * `Framework.boot()` is the canonical caller; user code rarely needs this.
2574
+ * Create (or return) the singleton root frame.
2575
+ * Idempotent: subsequent calls always return the original root.
3137
2576
  */
3138
- static createRoot(rootId) {
2577
+ createRoot(rootId) {
3139
2578
  if (!rootFrame) {
3140
- rootId = rootId || "root";
3141
- let rootElement = document.getElementById(rootId);
2579
+ const id = rootId ?? "root";
2580
+ let rootElement = document.getElementById(id);
3142
2581
  if (!rootElement) {
3143
2582
  rootElement = document.body;
3144
- rootElement.id = rootId;
2583
+ rootElement.id = id;
3145
2584
  }
3146
- rootFrame = new _Frame(rootId);
2585
+ rootFrame = createFrame(id);
3147
2586
  }
3148
2587
  return rootFrame;
3149
- }
2588
+ },
3150
2589
  /** Bind event listener (static) */
3151
- static on(event, handler) {
2590
+ on(event, handler) {
3152
2591
  staticEmitter.on(event, handler);
3153
- return _Frame;
3154
- }
2592
+ return Frame;
2593
+ },
3155
2594
  /** Unbind event listener (static) */
3156
- static off(event, handler) {
2595
+ off(event, handler) {
3157
2596
  staticEmitter.off(event, handler);
3158
- return _Frame;
3159
- }
2597
+ return Frame;
2598
+ },
3160
2599
  /** Fire event (static) */
3161
- static fire(event, data) {
2600
+ fire(event, data) {
3162
2601
  staticEmitter.fire(event, data);
3163
2602
  }
3164
2603
  };
3165
2604
  function htmlElIsBound(element) {
3166
- return !!element.frameBound;
2605
+ return !!Reflect.get(element, "frameBound");
3167
2606
  }
3168
- function removeFrame2(id, wasCreated) {
3169
- const frameInstance = getFrame(id);
2607
+ function removeFrame(id, wasCreated) {
2608
+ const frameInstance = frameRegistry.get(id);
3170
2609
  if (!frameInstance) return;
3171
- removeFrame(id);
3172
- Frame.fire("remove", { frame: frameInstance, fcc: wasCreated });
2610
+ frameRegistry.delete(id);
2611
+ staticEmitter.fire("remove", { frame: frameInstance, fcc: wasCreated });
3173
2612
  const element = document.getElementById(id);
3174
2613
  if (element) {
3175
- element.frameBound = 0;
2614
+ Reflect.set(element, "frameBound", 0);
3176
2615
  Reflect.deleteProperty(element, "frame");
3177
2616
  }
3178
2617
  }
3179
2618
  function notifyCreated(frameInstance) {
3180
- if (!frameInstance["childrenCreated"] && !frameInstance["holdFireCreated"] && frameInstance["childrenCount"] === frameInstance["readyCount"]) {
3181
- if (!frameInstance["childrenCreated"]) {
3182
- frameInstance["childrenCreated"] = 1;
3183
- frameInstance["childrenAlter"] = 0;
3184
- frameInstance.fire("created");
3185
- }
2619
+ if (!frameInstance.childrenCreated && !frameInstance.holdFireCreated && frameInstance.childrenCount === frameInstance.readyCount) {
2620
+ frameInstance.childrenCreated = 1;
2621
+ frameInstance.childrenAlter = 0;
2622
+ frameInstance.emitter.fire("created");
3186
2623
  const pId = frameInstance.parentId;
3187
2624
  if (pId) {
3188
- const parent = getFrame(pId);
2625
+ const parent = frameRegistry.get(pId);
3189
2626
  if (parent && !parent.readyMap.has(frameInstance.id)) {
3190
2627
  parent.readyMap.add(frameInstance.id);
3191
2628
  parent.readyCount++;
@@ -3195,13 +2632,13 @@ function notifyCreated(frameInstance) {
3195
2632
  }
3196
2633
  }
3197
2634
  function notifyAlter(frameInstance, data) {
3198
- if (!frameInstance["childrenAlter"] && frameInstance["childrenCreated"]) {
3199
- frameInstance["childrenCreated"] = 0;
3200
- frameInstance["childrenAlter"] = 1;
3201
- frameInstance.fire("alter", data);
2635
+ if (!frameInstance.childrenAlter && frameInstance.childrenCreated) {
2636
+ frameInstance.childrenCreated = 0;
2637
+ frameInstance.childrenAlter = 1;
2638
+ frameInstance.emitter.fire("alter", data);
3202
2639
  const pId = frameInstance.parentId;
3203
2640
  if (pId) {
3204
- const parent = getFrame(pId);
2641
+ const parent = frameRegistry.get(pId);
3205
2642
  if (parent && parent.readyMap.has(frameInstance.id)) {
3206
2643
  parent.readyCount--;
3207
2644
  parent.readyMap.delete(frameInstance.id);
@@ -3210,35 +2647,24 @@ function notifyAlter(frameInstance, data) {
3210
2647
  }
3211
2648
  }
3212
2649
  }
3213
- function reInitFrame(frame, id, parentId) {
3214
- Reflect.set(frame, "id", id);
3215
- frame["_parentId"] = parentId;
3216
- frame["childrenMap"] = {};
3217
- frame["childrenCount"] = 0;
3218
- frame["readyCount"] = 0;
3219
- frame["signature"] = 1;
3220
- frame["readyMap"] = /* @__PURE__ */ new Set();
3221
- frame["invokeList"] = [];
3222
- registerFrame(id, frame);
3223
- }
3224
- function reInitFrameForCache(frame) {
3225
- Reflect.set(frame, "id", "");
3226
- frame["_parentId"] = void 0;
3227
- frame["childrenMap"] = {};
3228
- frame["readyMap"] = /* @__PURE__ */ new Set();
3229
- }
3230
2650
  function translateQuery(pId, src, params) {
3231
- const parentFrame = getFrame(pId);
2651
+ const parentFrame = frameRegistry.get(pId);
3232
2652
  const parentView = parentFrame?.view;
3233
2653
  if (!parentView) return;
3234
2654
  const parentRefData = parentView.updater.refData;
3235
2655
  if (!parentRefData) return;
3236
2656
  if (src.indexOf(SPLITTER) > 0) {
3237
2657
  translateData(parentRefData, params);
3238
- const paramsRec = params;
3239
- const splitterValue = paramsRec[SPLITTER];
3240
- if (splitterValue && typeof splitterValue === "object") {
3241
- assign(params, splitterValue);
2658
+ const splitterValue = Reflect.get(params, SPLITTER);
2659
+ if (isRecord(splitterValue)) {
2660
+ for (const k in splitterValue) {
2661
+ if (hasOwnProperty(splitterValue, k)) {
2662
+ const v = splitterValue[k];
2663
+ if (typeof v === "string") {
2664
+ params[k] = v;
2665
+ }
2666
+ }
2667
+ }
3242
2668
  Reflect.deleteProperty(params, SPLITTER);
3243
2669
  }
3244
2670
  }
@@ -3253,10 +2679,10 @@ var FrameDevtoolBridge = {
3253
2679
  MSG_TREE_DELTA: "LARK_DEVTOOL_TREE_DELTA"
3254
2680
  };
3255
2681
  function serializeView(view) {
3256
- const evtMap = view.eventObjectMap;
2682
+ const evtMap = {};
3257
2683
  const eventMethodKeys = evtMap ? Object.keys(evtMap) : [];
3258
2684
  const resourceKeys = view.resources ? Object.keys(view.resources) : [];
3259
- const hasAssign = typeof view["assign"] === "function";
2685
+ const hasAssign = typeof view.getAssign() === "function";
3260
2686
  let updaterData = null;
3261
2687
  try {
3262
2688
  const ref = view.updater?.refData;
@@ -3272,14 +2698,14 @@ function serializeView(view) {
3272
2698
  return {
3273
2699
  id: view.id,
3274
2700
  rendered: !!view.rendered,
3275
- signature: view.signature,
3276
- observedStateKeys: view.observedStateKeys ?? null,
2701
+ signature: view.signature.value,
2702
+ observedStateKeys: view.getObservedStateKeys() ?? null,
3277
2703
  locationObserved: {
3278
2704
  flag: view.locationObserved.flag,
3279
2705
  keys: view.locationObserved.keys,
3280
2706
  observePath: view.locationObserved.observePath
3281
2707
  },
3282
- hasTemplate: !!view.template,
2708
+ hasTemplate: !!view.getTemplate(),
3283
2709
  eventMethodKeys,
3284
2710
  resourceKeys,
3285
2711
  hasAssign,
@@ -3300,7 +2726,7 @@ function serializeFrame(frameId) {
3300
2726
  return {
3301
2727
  id: frame.id,
3302
2728
  parentId: frame.parentId ?? null,
3303
- viewPath: frame.viewPath ?? null,
2729
+ viewPath: frame.getViewPath() ?? null,
3304
2730
  childrenCount: frame.childrenCount,
3305
2731
  readyCount: frame.readyCount,
3306
2732
  childrenCreated: frame.childrenCreated,
@@ -3345,10 +2771,7 @@ function installFrameDevtoolBridge() {
3345
2771
  if (type === FrameDevtoolBridge.MSG_PING) {
3346
2772
  const source = event.source;
3347
2773
  if (source) {
3348
- source.postMessage(
3349
- { type: FrameDevtoolBridge.MSG_PONG },
3350
- { targetOrigin: "*" }
3351
- );
2774
+ source.postMessage({ type: FrameDevtoolBridge.MSG_PONG }, { targetOrigin: "*" });
3352
2775
  }
3353
2776
  return;
3354
2777
  }
@@ -3376,10 +2799,7 @@ function pushTreeUpdate() {
3376
2799
  const treeJson = JSON.stringify(tree);
3377
2800
  if (treeJson !== lastTreeJson) {
3378
2801
  lastTreeJson = treeJson;
3379
- window.parent.postMessage(
3380
- { type: FrameDevtoolBridge.MSG_TREE_DELTA, data: tree },
3381
- "*"
3382
- );
2802
+ window.parent.postMessage({ type: FrameDevtoolBridge.MSG_TREE_DELTA, data: tree }, "*");
3383
2803
  }
3384
2804
  }
3385
2805
  // Annotate the CommonJS export names for ESM import in node: