@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/index.js CHANGED
@@ -55,10 +55,7 @@ var URI_ENT_MAP = {
55
55
  };
56
56
  var URI_ENT_REGEXP = /[!')(*]/g;
57
57
  function encodeURIExtra(v) {
58
- return encodeURIComponent(strSafe(v)).replace(
59
- URI_ENT_REGEXP,
60
- (m) => URI_ENT_MAP[m]
61
- );
58
+ return encodeURIComponent(strSafe(v)).replace(URI_ENT_REGEXP, (m) => URI_ENT_MAP[m]);
62
59
  }
63
60
  var QUOTE_ENT_REGEXP = /['"\\]/g;
64
61
  function encodeQuote(v) {
@@ -83,19 +80,173 @@ function isRefToken(s) {
83
80
  return true;
84
81
  }
85
82
 
83
+ // src/mark.ts
84
+ var hostStore = /* @__PURE__ */ new WeakMap();
85
+ function getOrCreate(host) {
86
+ let record = hostStore.get(host);
87
+ if (!record) {
88
+ record = { signs: /* @__PURE__ */ new Map(), deleted: false };
89
+ hostStore.set(host, record);
90
+ }
91
+ return record;
92
+ }
93
+ function mark(host, key) {
94
+ const record = getOrCreate(host);
95
+ if (record.deleted) {
96
+ return () => false;
97
+ }
98
+ const sign = (record.signs.get(key) ?? 0) + 1;
99
+ record.signs.set(key, sign);
100
+ return () => {
101
+ const current = hostStore.get(host);
102
+ return !!current && !current.deleted && current.signs.get(key) === sign;
103
+ };
104
+ }
105
+ function unmark(host) {
106
+ const record = hostStore.get(host);
107
+ if (record) {
108
+ record.deleted = true;
109
+ record.signs.clear();
110
+ } else {
111
+ hostStore.set(host, { signs: /* @__PURE__ */ new Map(), deleted: true });
112
+ }
113
+ }
114
+
115
+ // src/cache.ts
116
+ function sortCacheEntries(a, b) {
117
+ return b.frequency - a.frequency || b.lastTimestamp - a.lastTimestamp;
118
+ }
119
+ function createCache(options = {}) {
120
+ let entries = [];
121
+ const lookup = /* @__PURE__ */ new Map();
122
+ const maxSize = options.maxSize ?? 20;
123
+ const bufferSize = options.bufferSize ?? 5;
124
+ const capacity = maxSize + bufferSize;
125
+ const onRemove = options.onRemove;
126
+ const comparator = options.sortComparator ?? sortCacheEntries;
127
+ function prefixKey(key) {
128
+ return SPLITTER + key;
129
+ }
130
+ function get(key) {
131
+ const prefixedKey = prefixKey(key);
132
+ const entry = lookup.get(prefixedKey);
133
+ if (!entry) return void 0;
134
+ entry.frequency++;
135
+ entry.lastTimestamp = nextCounter();
136
+ return entry.value;
137
+ }
138
+ function forEach(callback) {
139
+ for (const entry of entries) {
140
+ callback(entry.value);
141
+ }
142
+ }
143
+ function set(key, value) {
144
+ const prefixedKey = prefixKey(key);
145
+ const existing = lookup.get(prefixedKey);
146
+ if (existing) {
147
+ existing.value = value;
148
+ existing.frequency++;
149
+ existing.lastTimestamp = nextCounter();
150
+ return;
151
+ }
152
+ if (entries.length >= capacity) {
153
+ evictEntries();
154
+ }
155
+ const entry = {
156
+ originalKey: key,
157
+ value,
158
+ frequency: 1,
159
+ lastTimestamp: nextCounter()
160
+ };
161
+ entries.push(entry);
162
+ lookup.set(prefixedKey, entry);
163
+ }
164
+ function del(key) {
165
+ const prefixedKey = prefixKey(key);
166
+ const entry = lookup.get(prefixedKey);
167
+ if (!entry) return;
168
+ lookup.delete(prefixedKey);
169
+ const idx = entries.indexOf(entry);
170
+ if (idx !== -1) entries.splice(idx, 1);
171
+ if (onRemove) {
172
+ onRemove(key);
173
+ }
174
+ }
175
+ function has(key) {
176
+ return lookup.has(prefixKey(key));
177
+ }
178
+ function clear() {
179
+ if (onRemove) {
180
+ for (const entry of entries) {
181
+ onRemove(entry.originalKey);
182
+ }
183
+ }
184
+ entries = [];
185
+ lookup.clear();
186
+ }
187
+ function evictEntries() {
188
+ if (bufferSize <= 0 || entries.length === 0) return;
189
+ if (entries.length <= bufferSize) {
190
+ for (const e of entries) {
191
+ lookup.delete(prefixKey(e.originalKey));
192
+ if (onRemove) onRemove(e.originalKey);
193
+ }
194
+ entries = [];
195
+ return;
196
+ }
197
+ const worst = [];
198
+ for (const entry of entries) {
199
+ if (worst.length < bufferSize) {
200
+ let i = worst.length;
201
+ while (i > 0 && comparator(entry, worst[i - 1]) > 0) i--;
202
+ worst.splice(i, 0, entry);
203
+ } else if (comparator(entry, worst[bufferSize - 1]) > 0) {
204
+ worst.pop();
205
+ let i = worst.length;
206
+ while (i > 0 && comparator(entry, worst[i - 1]) > 0) i--;
207
+ worst.splice(i, 0, entry);
208
+ }
209
+ }
210
+ const evictSet = new Set(worst);
211
+ for (const e of worst) {
212
+ lookup.delete(prefixKey(e.originalKey));
213
+ if (onRemove) onRemove(e.originalKey);
214
+ }
215
+ entries = entries.filter((e) => !evictSet.has(e));
216
+ }
217
+ function getSize() {
218
+ return entries.length;
219
+ }
220
+ const api = {
221
+ get,
222
+ set,
223
+ del,
224
+ has,
225
+ clear,
226
+ forEach,
227
+ getSize
228
+ };
229
+ return api;
230
+ }
231
+
86
232
  // src/utils.ts
87
233
  var CALL_BREAK_TIME2 = 9;
88
234
  var callQueue = [];
89
235
  var callScheduled = false;
90
- var schedulerYield = (() => {
236
+ function getSchedulerYield() {
91
237
  try {
92
- if (typeof globalThis?.scheduler?.yield === "function") {
93
- return globalThis.scheduler.yield.bind(globalThis.scheduler);
238
+ const scheduler = Reflect.get(globalThis, "scheduler");
239
+ if (scheduler && typeof scheduler === "object" && typeof Reflect.get(scheduler, "yield") === "function") {
240
+ const yieldFn = Reflect.get(scheduler, "yield");
241
+ if (typeof yieldFn === "function") {
242
+ return yieldFn.bind(scheduler);
243
+ }
94
244
  }
95
245
  } catch {
96
246
  }
97
247
  return void 0;
98
- })();
248
+ }
249
+ var schedulerYield = getSchedulerYield();
99
250
  async function startCall() {
100
251
  callScheduled = false;
101
252
  const startTime = performance.now();
@@ -121,7 +272,9 @@ function scheduleNextChunk() {
121
272
  callScheduled = true;
122
273
  }
123
274
  function callFunction(fn, args) {
124
- callQueue.push(() => fn(...args));
275
+ callQueue.push(() => {
276
+ fn(...args);
277
+ });
125
278
  if (!callScheduled) {
126
279
  scheduleNextChunk();
127
280
  }
@@ -138,7 +291,6 @@ function asRecord(value) {
138
291
  if (isRecord(value)) {
139
292
  return value;
140
293
  }
141
- console.error("fallback to Object.fromEntries, even an empty object {}.");
142
294
  if (Array.isArray(value)) {
143
295
  return Object.fromEntries(value.entries());
144
296
  }
@@ -173,7 +325,7 @@ function assign(target, ...sources) {
173
325
  if (source) {
174
326
  for (const p in source) {
175
327
  if (hasOwnProperty(source, p)) {
176
- target[p] = source[p];
328
+ Reflect.set(target, p, source[p]);
177
329
  }
178
330
  }
179
331
  }
@@ -212,16 +364,16 @@ function translateData(data, value) {
212
364
  if (isPrimitive(value)) {
213
365
  const prop = String(value);
214
366
  if (isRefToken(prop) && hasOwnProperty(data, prop)) {
215
- return data[prop];
367
+ return Reflect.get(data, prop);
216
368
  }
217
369
  return value;
218
370
  }
219
371
  if (isPlainObject(value) || Array.isArray(value)) {
220
372
  for (const p in value) {
221
373
  if (hasOwnProperty(value, p)) {
222
- const val = value[p];
374
+ const val = Reflect.get(value, p);
223
375
  const newVal = translateData(data, val);
224
- value[p] = newVal;
376
+ Reflect.set(value, p, newVal);
225
377
  }
226
378
  }
227
379
  return value;
@@ -287,276 +439,32 @@ function toUri(path, params, keepEmpty) {
287
439
  }
288
440
  return path;
289
441
  }
290
- function toMap(list, key) {
291
- const map = {};
292
- if (!list) return map;
293
- for (const item of list) {
294
- const mapKey = key ? String(item[key]) : String(item);
295
- map[mapKey] = key ? item : (map[mapKey] || 0) + 1;
296
- }
297
- return map;
298
- }
299
-
300
- // src/apply-style.ts
301
- var injectedStyleIds = /* @__PURE__ */ new Set();
302
- function applyStyle(styleIdOrPairs, css) {
303
- if (Array.isArray(styleIdOrPairs)) {
304
- if (styleIdOrPairs.length % 2 !== 0) {
305
- throw new Error("Invalid array of [id, content] pairs");
306
- }
307
- const cleanupCallbacks = [];
308
- for (let i = 0; i < styleIdOrPairs.length; i += 2) {
309
- const styleId = styleIdOrPairs[i];
310
- const styleContent = styleIdOrPairs[i + 1];
311
- const cleanup = applyStyle(styleId, styleContent);
312
- if (cleanup !== noop) {
313
- cleanupCallbacks.push(cleanup);
314
- }
315
- }
316
- return () => {
317
- for (const cleanup of cleanupCallbacks) {
318
- cleanup();
319
- }
320
- };
321
- }
322
- if (css && !injectedStyleIds.has(styleIdOrPairs)) {
323
- injectedStyleIds.add(styleIdOrPairs);
324
- const styleElement = document.createElement("style");
325
- styleElement.setAttribute("from", "lark");
326
- styleElement.id = styleIdOrPairs;
327
- styleElement.textContent = css;
328
- document.head.append(styleElement);
329
- return () => {
330
- injectedStyleIds.delete(styleIdOrPairs);
331
- styleElement.remove();
332
- };
333
- }
334
- return noop;
335
- }
336
-
337
- // src/mark.ts
338
- var hostStore = /* @__PURE__ */ new WeakMap();
339
- function getOrCreate(host) {
340
- let record = hostStore.get(host);
341
- if (!record) {
342
- record = { signs: /* @__PURE__ */ new Map(), deleted: false };
343
- hostStore.set(host, record);
344
- }
345
- return record;
346
- }
347
- function mark(host, key) {
348
- const record = getOrCreate(host);
349
- if (record.deleted) {
350
- return () => false;
351
- }
352
- const sign = (record.signs.get(key) ?? 0) + 1;
353
- record.signs.set(key, sign);
354
- return () => {
355
- const current = hostStore.get(host);
356
- return !!current && !current.deleted && current.signs.get(key) === sign;
357
- };
358
- }
359
- function unmark(host) {
360
- const record = hostStore.get(host);
361
- if (record) {
362
- record.deleted = true;
363
- record.signs.clear();
364
- } else {
365
- hostStore.set(host, { signs: /* @__PURE__ */ new Map(), deleted: true });
366
- }
367
- }
368
-
369
- // src/cache.ts
370
- function sortCacheEntries(a, b) {
371
- return b.frequency - a.frequency || b.lastTimestamp - a.lastTimestamp;
372
- }
373
- var Cache = class {
374
- /** Cache entries array */
375
- entries = [];
376
- /** Fast lookup: prefixed key -> entry */
377
- lookup = /* @__PURE__ */ new Map();
378
- /** Buffer size for eviction */
379
- bufferSize;
380
- /** Maximum cache size */
381
- maxSize;
382
- /** Total capacity (maxSize + bufferSize) */
383
- capacity;
384
- /** Callback when entry is removed */
385
- onRemove;
386
- /** Sort comparator */
387
- comparator;
388
- constructor(options = {}) {
389
- this.maxSize = options.maxSize ?? 20;
390
- this.bufferSize = options.bufferSize ?? 5;
391
- this.capacity = this.maxSize + this.bufferSize;
392
- this.onRemove = options.onRemove;
393
- this.comparator = options.sortComparator ?? sortCacheEntries;
394
- }
395
- /** Prefix a key with SPLITTER for namespace isolation */
396
- prefixKey(key) {
397
- return SPLITTER + key;
398
- }
399
- /**
400
- * Get a cached value by key.
401
- * Updates frequency and timestamp for cache sorting.
402
- */
403
- get(key) {
404
- const prefixedKey = this.prefixKey(key);
405
- const entry = this.lookup.get(prefixedKey);
406
- if (!entry) return void 0;
407
- entry.frequency++;
408
- entry.lastTimestamp = nextCounter();
409
- return entry.value;
410
- }
411
- /**
412
- * Iterate all cached values.
413
- */
414
- forEach(callback) {
415
- for (const entry of this.entries) {
416
- callback(entry.value);
417
- }
418
- }
419
- /**
420
- * Set or update a cached value.
421
- * If key already exists, updates value and increments frequency.
422
- * If cache exceeds capacity, triggers eviction.
423
- */
424
- set(key, value) {
425
- const prefixedKey = this.prefixKey(key);
426
- const existing = this.lookup.get(prefixedKey);
427
- if (existing) {
428
- existing.value = value;
429
- existing.frequency++;
430
- existing.lastTimestamp = nextCounter();
431
- return;
432
- }
433
- if (this.entries.length >= this.capacity) {
434
- this.evictEntries();
435
- }
436
- const entry = {
437
- originalKey: key,
438
- value,
439
- frequency: 1,
440
- lastTimestamp: nextCounter()
441
- };
442
- this.entries.push(entry);
443
- this.lookup.set(prefixedKey, entry);
444
- }
445
- /**
446
- * Delete a cached entry. Removes it immediately from both the lookup map
447
- * and the entries array so the GC can reclaim the value without waiting
448
- * for the next eviction sweep.
449
- */
450
- del(key) {
451
- const prefixedKey = this.prefixKey(key);
452
- const entry = this.lookup.get(prefixedKey);
453
- if (!entry) return;
454
- this.lookup.delete(prefixedKey);
455
- const idx = this.entries.indexOf(entry);
456
- if (idx !== -1) this.entries.splice(idx, 1);
457
- if (this.onRemove) {
458
- this.onRemove(key);
459
- }
460
- }
461
- /**
462
- * Check if a key exists in cache.
463
- */
464
- has(key) {
465
- return this.lookup.has(this.prefixKey(key));
466
- }
467
- /** Get current cache size */
468
- get size() {
469
- return this.entries.length;
470
- }
471
- /** Clear all entries */
472
- clear() {
473
- if (this.onRemove) {
474
- for (const entry of this.entries) {
475
- this.onRemove(entry.originalKey);
476
- }
477
- }
478
- this.entries = [];
479
- this.lookup.clear();
480
- }
481
- /**
482
- * Evict the `bufferSize` worst entries from the cache.
483
- *
484
- * Uses single-pass partial selection (O(n·k)) instead of sorting the entire
485
- * `entries` array (O(n log n)). For the typical `bufferSize = 5` this is
486
- * effectively a linear scan with at most 5 in-bucket comparisons per
487
- * iteration — and it avoids mutating the rest of `entries`.
488
- */
489
- evictEntries() {
490
- const entries = this.entries;
491
- const k = this.bufferSize;
492
- if (k <= 0 || entries.length === 0) return;
493
- if (entries.length <= k) {
494
- for (const e of entries) {
495
- this.lookup.delete(this.prefixKey(e.originalKey));
496
- if (this.onRemove) this.onRemove(e.originalKey);
497
- }
498
- this.entries = [];
499
- return;
500
- }
501
- const cmp = this.comparator;
502
- const worst = [];
503
- for (const entry of entries) {
504
- if (worst.length < k) {
505
- let i = worst.length;
506
- while (i > 0 && cmp(entry, worst[i - 1]) > 0) i--;
507
- worst.splice(i, 0, entry);
508
- } else if (cmp(entry, worst[k - 1]) > 0) {
509
- worst.pop();
510
- let i = worst.length;
511
- while (i > 0 && cmp(entry, worst[i - 1]) > 0) i--;
512
- worst.splice(i, 0, entry);
513
- }
514
- }
515
- const evictSet = new Set(worst);
516
- for (const e of worst) {
517
- this.lookup.delete(this.prefixKey(e.originalKey));
518
- if (this.onRemove) this.onRemove(e.originalKey);
519
- }
520
- this.entries = entries.filter((e) => !evictSet.has(e));
521
- }
522
- };
523
442
 
524
443
  // src/event-emitter.ts
525
- var EventEmitter = class {
526
- /** Event listeners: prefixed key -> listener array */
527
- listeners = /* @__PURE__ */ new Map();
528
- /** Number of `fire()` calls currently on the stack (re-entrancy depth). */
529
- firingDepth = 0;
530
- /** Keys whose listener list needs compaction after firing settles. */
531
- pendingCompaction;
532
- /**
533
- * Bind event listener.
534
- */
535
- on(event, handler) {
444
+ function createEmitter() {
445
+ const listeners = /* @__PURE__ */ new Map();
446
+ let firingDepth = 0;
447
+ let pendingCompaction;
448
+ function on(event, handler) {
536
449
  const key = SPLITTER + event;
537
- let list = this.listeners.get(key);
450
+ let list = listeners.get(key);
538
451
  if (!list) {
539
452
  list = [];
540
- this.listeners.set(key, list);
453
+ listeners.set(key, list);
541
454
  }
542
455
  list.push({ handler, executing: 0 });
543
- return this;
456
+ return api;
544
457
  }
545
- /**
546
- * Unbind event listener.
547
- * If handler is provided, removes only that handler.
548
- * If no handler, removes all handlers for the event.
549
- */
550
- off(event, handler) {
458
+ function off(event, handler) {
551
459
  const key = SPLITTER + event;
552
460
  if (handler) {
553
- const list = this.listeners.get(key);
554
- if (!list) return this;
555
- if (this.firingDepth > 0) {
461
+ const list = listeners.get(key);
462
+ if (!list) return api;
463
+ if (firingDepth > 0) {
556
464
  for (const listener of list) {
557
465
  if (listener.handler === handler) {
558
466
  listener.handler = noop;
559
- (this.pendingCompaction ??= /* @__PURE__ */ new Set()).add(key);
467
+ (pendingCompaction ??= /* @__PURE__ */ new Set()).add(key);
560
468
  break;
561
469
  }
562
470
  }
@@ -567,35 +475,20 @@ var EventEmitter = class {
567
475
  break;
568
476
  }
569
477
  }
570
- if (list.length === 0) this.listeners.delete(key);
478
+ if (list.length === 0) listeners.delete(key);
571
479
  }
572
480
  } else {
573
- this.listeners.delete(key);
574
- Reflect.deleteProperty(
575
- this,
576
- `on${event[0].toUpperCase() + event.slice(1)}`
577
- );
481
+ listeners.delete(key);
482
+ Reflect.deleteProperty(internal, onMethodName(event));
578
483
  }
579
- return this;
484
+ return api;
580
485
  }
581
- /**
582
- * Fire event, execute all bound handlers. Safe for re-entrant `off()` calls
583
- * during dispatch: removed handlers are replaced with noop and compacted
584
- * after the outermost fire returns.
585
- *
586
- * @param event - Event name
587
- * @param data - Event data (type property added automatically)
588
- * @param remove - Whether to remove all handlers after firing
589
- * @param lastToFirst - Whether to execute handlers in reverse order
590
- */
591
- fire(event, data, remove, lastToFirst) {
486
+ function fire(event, data, remove, lastToFirst) {
592
487
  const key = SPLITTER + event;
593
- const list = this.listeners.get(key);
594
- if (!data) {
595
- data = {};
596
- }
597
- data["type"] = event;
598
- this.firingDepth++;
488
+ const list = listeners.get(key);
489
+ const eventData = data ?? {};
490
+ eventData["type"] = event;
491
+ firingDepth++;
599
492
  try {
600
493
  if (list) {
601
494
  const len = list.length;
@@ -605,35 +498,45 @@ var EventEmitter = class {
605
498
  if (!listener) continue;
606
499
  if (listener.handler === noop) continue;
607
500
  listener.executing = 1;
608
- funcWithTry([listener.handler], [data], this, noop);
501
+ funcWithTry([listener.handler], [eventData], null, noop);
609
502
  listener.executing = "";
610
503
  }
611
504
  }
612
- const onMethodName = `on${event[0].toUpperCase() + event.slice(1)}`;
613
- const onMethod = this[onMethodName];
505
+ const onMethod = internal[onMethodName(event)];
614
506
  if (typeof onMethod === "function") {
615
- funcWithTry([onMethod], [data], this, noop);
507
+ funcWithTry([onMethod], [eventData], null, noop);
616
508
  }
617
509
  if (remove) {
618
- this.off(event);
510
+ off(event);
619
511
  }
620
512
  } finally {
621
- this.firingDepth--;
622
- if (this.firingDepth === 0 && this.pendingCompaction) {
623
- for (const k of this.pendingCompaction) {
624
- const l = this.listeners.get(k);
513
+ firingDepth--;
514
+ if (firingDepth === 0 && pendingCompaction) {
515
+ for (const k of pendingCompaction) {
516
+ const l = listeners.get(k);
625
517
  if (!l) continue;
626
518
  for (let i = l.length - 1; i >= 0; i--) {
627
519
  if (l[i].handler === noop) l.splice(i, 1);
628
520
  }
629
- if (l.length === 0) this.listeners.delete(k);
521
+ if (l.length === 0) listeners.delete(k);
630
522
  }
631
- this.pendingCompaction = void 0;
523
+ pendingCompaction = void 0;
632
524
  }
633
525
  }
634
- return this;
526
+ return api;
635
527
  }
636
- };
528
+ function onMethodName(event) {
529
+ return "on" + event[0].toUpperCase() + event.slice(1);
530
+ }
531
+ const internal = {
532
+ on,
533
+ off,
534
+ fire,
535
+ listeners
536
+ };
537
+ const api = internal;
538
+ return api;
539
+ }
637
540
 
638
541
  // src/state.ts
639
542
  var appData = {};
@@ -641,10 +544,11 @@ var keyRefCounts = {};
641
544
  var changedKeys = /* @__PURE__ */ new Set();
642
545
  var stashedChangedKeys = EMPTY_STRING_SET;
643
546
  var dataIsChanged = false;
644
- var emitter = new EventEmitter();
547
+ var emitter = createEmitter();
645
548
  var booted = false;
646
549
  function markBooted() {
647
550
  booted = true;
551
+ void booted;
648
552
  }
649
553
  function setupKeysRef(keys2) {
650
554
  const keyList = keys2.split(",");
@@ -705,17 +609,15 @@ var State = {
705
609
  return stashedChangedKeys;
706
610
  },
707
611
  /**
708
- * Create mixin to clean up state keys on view destroy.
709
- * Must be used in view.mixins array.
612
+ * Create a cleanup function for state keys on view destroy.
613
+ * Call inside setup: `State.clean("keys")(ctx)` or `useEvent("destroy", State.clean("keys"))`
710
614
  */
711
615
  clean(keys2) {
712
- return {
713
- make: function() {
714
- const keyList = setupKeysRef(keys2);
715
- this.on("destroy", () => {
716
- teardownKeysRef(keyList);
717
- });
718
- }
616
+ return (ctx) => {
617
+ const keyList = setupKeysRef(keys2);
618
+ ctx.on("destroy", () => {
619
+ teardownKeysRef(keyList);
620
+ });
719
621
  };
720
622
  },
721
623
  /**
@@ -743,9 +645,9 @@ var State = {
743
645
  };
744
646
 
745
647
  // src/router.ts
746
- var emitter2 = new EventEmitter();
747
- var hrefCache = new Cache();
748
- var changedCache = new Cache();
648
+ var emitter2 = createEmitter();
649
+ var hrefCache = createCache();
650
+ var changedCache = createCache();
749
651
  var lastLocation = createEmptyLocation();
750
652
  var lastChanged;
751
653
  var silent = 0;
@@ -789,11 +691,7 @@ function attachViewAndPath(loc) {
789
691
  path = cachedDefaultPath;
790
692
  }
791
693
  if (cachedRewrite) {
792
- path = cachedRewrite(
793
- path,
794
- loc["params"],
795
- cachedRoutes
796
- );
694
+ path = cachedRewrite(path, loc["params"], cachedRoutes);
797
695
  }
798
696
  const viewEntry = cachedRoutes[path] || cachedUnmatchedView || cachedDefaultView;
799
697
  loc["path"] = path;
@@ -996,7 +894,7 @@ var Router = {
996
894
  updateUrl(tPath, tParams, lastLocation, replace, silentFlag, lQuery);
997
895
  },
998
896
  /**
999
- * Register an async-friendly navigation guard. See `RouterInterface.beforeEach`.
897
+ * Register an async-friendly navigation guard. See `RouterApi.beforeEach`.
1000
898
  */
1001
899
  beforeEach(guard) {
1002
900
  beforeEachGuards.push(guard);
@@ -1146,7 +1044,7 @@ var selectorEvents = {};
1146
1044
  var rangeEvents = {};
1147
1045
  var rangeFrames = {};
1148
1046
  var elementGuid = 0;
1149
- var eventInfoCache = new Cache({
1047
+ var eventInfoCache = createCache({
1150
1048
  maxSize: 30,
1151
1049
  bufferSize: 10
1152
1050
  });
@@ -1199,7 +1097,7 @@ function findFrameInfo(current, eventType) {
1199
1097
  if (frame) {
1200
1098
  const view = frame.view;
1201
1099
  if (view) {
1202
- const selectorEntry = view.eventSelectorMap[eventType];
1100
+ const selectorEntry = { selectors: [] };
1203
1101
  if (selectorEntry) {
1204
1102
  for (const selectorName of selectorEntry.selectors) {
1205
1103
  const entry = {
@@ -1217,7 +1115,7 @@ function findFrameInfo(current, eventType) {
1217
1115
  }
1218
1116
  }
1219
1117
  }
1220
- if (view.template && !backtrace) {
1118
+ if (view.getTemplate() && !backtrace) {
1221
1119
  if (match && !match.id) {
1222
1120
  match.id = frameId;
1223
1121
  }
@@ -1269,8 +1167,9 @@ function domEventProcessor(domEvent) {
1269
1167
  const frame = frameId ? frameGetter?.(frameId) : void 0;
1270
1168
  const view = frame?.view;
1271
1169
  if (view) {
1272
- const eventName = handlerName + SPLITTER + eventType;
1273
- const fn = Reflect.get(view, eventName);
1170
+ const eventKey = handlerName + "<" + eventType + ">";
1171
+ const events = typeof view.getEvents === "function" ? view.getEvents() : void 0;
1172
+ const fn = events?.[eventKey];
1274
1173
  if (fn) {
1275
1174
  const extendedEvent = domEvent;
1276
1175
  extendedEvent.eventTarget = target;
@@ -1349,80 +1248,18 @@ var EventDelegator = {
1349
1248
  }
1350
1249
  };
1351
1250
 
1352
- // src/module-loader.ts
1353
- var config = {
1354
- rootId: "root",
1355
- routeMode: "history",
1356
- hashbang: "#!",
1357
- error: (error) => {
1358
- throw error;
1359
- }
1360
- };
1361
- function use(names, callback) {
1362
- const nameList = typeof names === "string" ? [names] : names;
1363
- const loadPromise = (() => {
1364
- if (config.require) {
1365
- const result = config.require(nameList);
1366
- if (result && typeof result.then === "function") {
1367
- return result;
1368
- }
1369
- return Promise.resolve([]);
1370
- }
1371
- return Promise.all(
1372
- nameList.map((name) => {
1373
- const importPath = name.startsWith(".") || name.startsWith("/") ? name : `./${name}`;
1374
- return import(
1375
- /* @vite-ignore */
1376
- /* webpackIgnore: true */
1377
- importPath
1378
- ).then((mod) => {
1379
- return mod && (mod["__esModule"] || // For Webpack
1380
- typeof mod["default"] === "function") ? mod["default"] : mod;
1381
- }).catch((err) => {
1382
- const errorHandler = config.error;
1383
- if (errorHandler) {
1384
- errorHandler(err instanceof Error ? err : new Error(String(err)));
1385
- }
1386
- return void 0;
1387
- });
1388
- })
1389
- );
1390
- })();
1391
- if (callback) {
1392
- loadPromise.then((modules) => {
1393
- callback(...modules);
1394
- });
1395
- }
1396
- return loadPromise;
1397
- }
1398
-
1399
- // src/frame-registry.ts
1400
- var frameRegistry = /* @__PURE__ */ new Map();
1401
- function getFrame(id) {
1402
- return frameRegistry.get(id);
1403
- }
1404
- function getAllFrames() {
1405
- return frameRegistry;
1406
- }
1407
- function registerFrame(id, frame) {
1408
- frameRegistry.set(id, frame);
1409
- }
1410
- function removeFrame(id) {
1411
- frameRegistry.delete(id);
1412
- }
1413
-
1414
- // src/dom.ts
1415
- var wrapMeta = {
1416
- option: [1, "<select multiple>"],
1417
- thead: [1, "<table>"],
1418
- col: [2, "<table><colgroup>"],
1419
- tr: [2, "<table><tbody>"],
1420
- td: [3, "<table><tbody><tr>"],
1421
- area: [1, "<map>"],
1422
- param: [1, "<object>"],
1423
- svg: [1, '<svg xmlns="' + SVG_NS + '">'],
1424
- math: [1, '<math xmlns="' + MATH_NS + '">'],
1425
- _: [0, ""]
1251
+ // src/dom.ts
1252
+ var wrapMeta = {
1253
+ option: [1, "<select multiple>"],
1254
+ thead: [1, "<table>"],
1255
+ col: [2, "<table><colgroup>"],
1256
+ tr: [2, "<table><tbody>"],
1257
+ td: [3, "<table><tbody><tr>"],
1258
+ area: [1, "<map>"],
1259
+ param: [1, "<object>"],
1260
+ svg: [1, '<svg xmlns="' + SVG_NS + '">'],
1261
+ math: [1, '<math xmlns="' + MATH_NS + '">'],
1262
+ _: [0, ""]
1426
1263
  };
1427
1264
  wrapMeta["optgroup"] = wrapMeta["option"];
1428
1265
  wrapMeta["tbody"] = wrapMeta["tfoot"] = wrapMeta["colgroup"] = wrapMeta["caption"] = wrapMeta["thead"];
@@ -1685,7 +1522,7 @@ function vdomCreate(tag, props, children, specials) {
1685
1522
  html: String(props ?? "")
1686
1523
  };
1687
1524
  }
1688
- const propsObj = props || {};
1525
+ const propsObj = typeof props === "object" && props !== null ? props : {};
1689
1526
  const specialsObj = specials || {};
1690
1527
  const unary = children === 1;
1691
1528
  let compareKey;
@@ -1756,12 +1593,7 @@ function vdomCreate(tag, props, children, specials) {
1756
1593
  const parsed = parseUri(value);
1757
1594
  isLarkView2 = parsed.path;
1758
1595
  if (!viewList) viewList = [];
1759
- viewList.push([
1760
- isLarkView2,
1761
- propsObj["lark-owner"],
1762
- value,
1763
- parsed.params
1764
- ]);
1596
+ viewList.push([isLarkView2, propsObj["lark-owner"], value, parsed.params]);
1765
1597
  if (!compareKey) {
1766
1598
  compareKey = tag + SPLITTER + isLarkView2;
1767
1599
  }
@@ -1797,6 +1629,11 @@ function vdomCreateNode(vnode, owner, ref) {
1797
1629
  if (tag === V_TEXT_NODE) {
1798
1630
  return document.createTextNode(vnode.html);
1799
1631
  }
1632
+ if (tag === SPLITTER) {
1633
+ const template = document.createElement("template");
1634
+ template.innerHTML = vnode.html;
1635
+ return template.content.firstChild || document.createTextNode("");
1636
+ }
1800
1637
  const sTag = typeof tag === "string" ? tag : tag.toString();
1801
1638
  const ns = VDOM_NS_MAP[sTag] || owner.namespaceURI;
1802
1639
  const el = document.createElementNS(ns, sTag);
@@ -1880,6 +1717,14 @@ function vdomSetNode(realNode, oldParent, lastVDom, newVDom, ref, frame, keys2,
1880
1717
  return;
1881
1718
  }
1882
1719
  if (lastTag === newTag) {
1720
+ if (newTag === SPLITTER) {
1721
+ if (lastVDom.html !== newVDom.html) {
1722
+ ref.changed = 1;
1723
+ domUnmountFrames(frame, realNode);
1724
+ oldParent.replaceChild(vdomCreateNode(newVDom, oldParent, ref), realNode);
1725
+ }
1726
+ return;
1727
+ }
1883
1728
  if (lastVDom.attrs === newVDom.attrs && lastVDom.html === newVDom.html) {
1884
1729
  if (newVDom.hasSpecials) {
1885
1730
  vdomSyncFormState(realNode, newVDom);
@@ -1888,12 +1733,7 @@ function vdomSetNode(realNode, oldParent, lastVDom, newVDom, ref, frame, keys2,
1888
1733
  }
1889
1734
  let attrChanged = 0;
1890
1735
  if (lastVDom.attrs !== newVDom.attrs || newVDom.hasSpecials) {
1891
- attrChanged = vdomSetAttributes(
1892
- realNode,
1893
- newVDom,
1894
- ref,
1895
- lastVDom
1896
- );
1736
+ attrChanged = vdomSetAttributes(realNode, newVDom, ref, lastVDom);
1897
1737
  if (attrChanged) ref.changed = 1;
1898
1738
  }
1899
1739
  let updateChildren = true;
@@ -1907,16 +1747,7 @@ function vdomSetNode(realNode, oldParent, lastVDom, newVDom, ref, frame, keys2,
1907
1747
  }
1908
1748
  vdomSyncFormState(realNode, newVDom);
1909
1749
  if (updateChildren && !newVDom.selfClose) {
1910
- vdomSetChildNodes(
1911
- realNode,
1912
- lastVDom,
1913
- newVDom,
1914
- ref,
1915
- frame,
1916
- keys2,
1917
- rootView,
1918
- ready
1919
- );
1750
+ vdomSetChildNodes(realNode, lastVDom, newVDom, ref, frame, keys2, rootView, ready);
1920
1751
  }
1921
1752
  } else {
1922
1753
  ref.changed = 1;
@@ -1986,17 +1817,7 @@ function vdomSetChildNodes(realNode, lastVDom, newVDom, ref, frame, keys2, view,
1986
1817
  const nc = newChildren[newHead];
1987
1818
  if (!isSameVDomNode(nc, oc)) break;
1988
1819
  if (nc.tag === SPLITTER || oc.tag === SPLITTER) break;
1989
- vdomSetNode(
1990
- oldDomNodes[headIdx],
1991
- realNode,
1992
- oc,
1993
- nc,
1994
- ref,
1995
- frame,
1996
- keys2,
1997
- view,
1998
- ready
1999
- );
1820
+ vdomSetNode(oldDomNodes[headIdx], realNode, oc, nc, ref, frame, keys2, view, ready);
2000
1821
  usedOldDomNodes.add(oldDomNodes[headIdx]);
2001
1822
  headIdx++;
2002
1823
  newHead++;
@@ -2006,17 +1827,7 @@ function vdomSetChildNodes(realNode, lastVDom, newVDom, ref, frame, keys2, view,
2006
1827
  const nc = newChildren[newTail];
2007
1828
  if (!isSameVDomNode(nc, oc)) break;
2008
1829
  if (nc.tag === SPLITTER || oc.tag === SPLITTER) break;
2009
- vdomSetNode(
2010
- oldDomNodes[tailIdx],
2011
- realNode,
2012
- oc,
2013
- nc,
2014
- ref,
2015
- frame,
2016
- keys2,
2017
- view,
2018
- ready
2019
- );
1830
+ vdomSetNode(oldDomNodes[tailIdx], realNode, oc, nc, ref, frame, keys2, view, ready);
2020
1831
  usedOldDomNodes.add(oldDomNodes[tailIdx]);
2021
1832
  tailIdx--;
2022
1833
  newTail--;
@@ -2139,113 +1950,88 @@ function createVDomRef(viewId) {
2139
1950
  }
2140
1951
 
2141
1952
  // src/updater.ts
2142
- var Updater = class {
2143
- /** View ID (same as owner frame ID) */
2144
- viewId;
2145
- /** Current data object */
2146
- data;
2147
- /** Ref data for template rendering */
2148
- refData;
2149
- /** Changed keys in current digest cycle */
2150
- changedKeys = /* @__PURE__ */ new Set();
2151
- /** Whether data has changed since last digest */
2152
- hasChangedFlag = 0;
2153
- /**
2154
- * Digesting queue: supports re-digest during digest.
2155
- * Holds pending callbacks; `null` is used as a sentinel marking the start
2156
- * of an active digest cycle, so `runDigest` can detect re-entrant calls.
2157
- */
2158
- digestingQueue = [];
2159
- /** Monotonically increasing version, bumped each time data actually changes. */
2160
- version = 0;
2161
- /** Snapshot of `version` taken by `snapshot()`, used by `altered()`. */
2162
- snapshotVersion;
2163
- /** Last rendered VDOM tree (only used when virtualDom is enabled) */
2164
- vdom;
2165
- constructor(viewId) {
2166
- this.viewId = viewId;
2167
- this.data = { vId: viewId };
2168
- const refCounter = {};
2169
- refCounter[SPLITTER] = 1;
2170
- this.refData = refCounter;
2171
- this.hasChangedFlag = 1;
2172
- }
2173
- /**
2174
- * Get data by key.
2175
- * Returns entire data object if key is omitted.
2176
- */
2177
- get(key) {
2178
- let result = this.data;
1953
+ function createUpdater(viewId) {
1954
+ let data = { vId: viewId };
1955
+ const refData = {};
1956
+ refData[SPLITTER] = 1;
1957
+ let changedKeys2 = /* @__PURE__ */ new Set();
1958
+ let hasChangedFlag = 0;
1959
+ const digestingQueue = [];
1960
+ let version = 0;
1961
+ let snapshotVersion;
1962
+ let vdom;
1963
+ hasChangedFlag = 1;
1964
+ function get(key) {
1965
+ let result = data;
2179
1966
  if (key) {
2180
- result = this.data[key];
1967
+ result = data[key];
2181
1968
  }
2182
1969
  return result;
2183
1970
  }
2184
- /**
2185
- * Set data, tracking changed keys.
2186
- * Returns this for chaining.
2187
- */
2188
- set(data, excludes) {
2189
- const changed = setData(
2190
- data,
2191
- this.data,
2192
- this.changedKeys,
2193
- excludes || EMPTY_STRING_SET
2194
- );
1971
+ function set(newData, excludes) {
1972
+ const changed = setData(newData, data, changedKeys2, excludes || EMPTY_STRING_SET);
2195
1973
  if (changed) {
2196
- this.version++;
2197
- this.hasChangedFlag = 1;
1974
+ version++;
1975
+ hasChangedFlag = 1;
2198
1976
  }
2199
- return this;
1977
+ return api;
2200
1978
  }
2201
- /**
2202
- * Detect changes and trigger DOM re-render.
2203
- *
2204
- * The core rendering pipeline:
2205
- * 1. Set data if provided
2206
- * 2. If changed, run DOM diff (template → new DOM → diff against old DOM)
2207
- * 3. Apply DOM operations
2208
- * 4. Apply ID updates
2209
- * 5. Call endUpdate on views that need re-rendering
2210
- * 6. Support re-digest during digest via queue
2211
- */
2212
- digest(data, excludes, callback) {
2213
- if (data) {
2214
- this.set(data, excludes);
1979
+ function digest(newData, excludes, callback) {
1980
+ if (newData) {
1981
+ set(newData, excludes);
2215
1982
  }
2216
- const digesting = this.digestingQueue;
2217
1983
  if (callback) {
2218
- digesting.push(callback);
1984
+ digestingQueue.push(callback);
2219
1985
  }
2220
- if (digesting.length > 0 && digesting[0] === null) {
1986
+ if (digestingQueue.length > 0 && digestingQueue[0] === null) {
2221
1987
  return;
2222
1988
  }
2223
- this.runDigest(digesting);
1989
+ runDigest(digestingQueue);
2224
1990
  }
2225
- /**
2226
- * Core digest execution.
2227
- */
2228
- runDigest(digesting) {
1991
+ function runDigest(digesting) {
2229
1992
  const startIndex = digesting.length;
2230
1993
  digesting.push(null);
2231
- const keys2 = this.changedKeys;
2232
- const changed = this.hasChangedFlag;
2233
- this.hasChangedFlag = 0;
2234
- this.changedKeys = /* @__PURE__ */ new Set();
2235
- const frame = getFrame(this.viewId);
1994
+ const keys2 = changedKeys2;
1995
+ const changed = hasChangedFlag;
1996
+ const frame = Frame.get(viewId);
2236
1997
  const view = frame?.view;
2237
- const node = getById(this.viewId);
2238
- if (changed && view && node && view.signature > 0 && frame) {
2239
- const template = view.template;
1998
+ const node = getById(viewId);
1999
+ if (changed && view && node && view.signature.value > 0 && frame) {
2000
+ hasChangedFlag = 0;
2001
+ changedKeys2 = /* @__PURE__ */ new Set();
2002
+ const template = view.getTemplate();
2240
2003
  if (typeof template === "function") {
2241
- if (config.virtualDom) {
2242
- const vdomTemplate = template;
2243
- const newVDom = vdomTemplate(this.data, this.viewId, this.refData);
2244
- const ref = createVDomRef(this.viewId);
2004
+ const result = template(
2005
+ data,
2006
+ viewId,
2007
+ refData,
2008
+ encodeHTML,
2009
+ strSafe,
2010
+ encodeURIExtra,
2011
+ refFn,
2012
+ encodeQuote
2013
+ );
2014
+ if (typeof result === "string") {
2015
+ const newDom = domGetNode(result, node);
2016
+ const ref = createDomRef();
2017
+ domSetChildNodes(node, newDom, ref, frame, keys2);
2018
+ applyIdUpdates(ref.idUpdates);
2019
+ applyDomOps(ref.domOps);
2020
+ for (const v of ref.views) {
2021
+ if (v.render) {
2022
+ funcWithTry(v.render, [], v, noop);
2023
+ }
2024
+ }
2025
+ if (ref.hasChanged || !view.rendered.value) {
2026
+ view.endUpdate(viewId);
2027
+ }
2028
+ } else {
2029
+ const newVDom = result;
2030
+ const ref = createVDomRef(viewId);
2245
2031
  const ready = () => {
2246
- this.vdom = newVDom;
2247
- if (ref.changed || !view.rendered) {
2248
- view.endUpdate(this.viewId);
2032
+ vdom = newVDom;
2033
+ if (ref.changed || !view.rendered.value) {
2034
+ view.endUpdate(viewId);
2249
2035
  }
2250
2036
  for (const [el, prop, val] of ref.nodeProps) {
2251
2037
  Reflect.set(el, prop, val);
@@ -2256,45 +2042,14 @@ var Updater = class {
2256
2042
  }
2257
2043
  }
2258
2044
  };
2259
- vdomSetChildNodes(
2260
- node,
2261
- this.vdom,
2262
- newVDom,
2263
- ref,
2264
- frame,
2265
- keys2,
2266
- view,
2267
- ready
2268
- );
2269
- } else {
2270
- const html = template(
2271
- this.data,
2272
- this.viewId,
2273
- this.refData,
2274
- encodeHTML,
2275
- strSafe,
2276
- encodeURIExtra,
2277
- refFn,
2278
- encodeQuote
2279
- );
2280
- const newDom = domGetNode(html, node);
2281
- const ref = createDomRef();
2282
- domSetChildNodes(node, newDom, ref, frame, keys2);
2283
- applyIdUpdates(ref.idUpdates);
2284
- applyDomOps(ref.domOps);
2285
- for (const v of ref.views) {
2286
- if (v.render) {
2287
- funcWithTry(v.render, [], v, noop);
2288
- }
2289
- }
2290
- if (ref.hasChanged || !view.rendered) {
2291
- view.endUpdate(this.viewId);
2292
- }
2045
+ vdomSetChildNodes(node, vdom, newVDom, ref, frame, keys2, view, ready);
2293
2046
  }
2294
2047
  }
2048
+ } else {
2049
+ changedKeys2 = /* @__PURE__ */ new Set();
2295
2050
  }
2296
2051
  if (digesting.length > startIndex + 1) {
2297
- this.runDigest(digesting);
2052
+ runDigest(digesting);
2298
2053
  } else {
2299
2054
  const callbacks = digesting.slice();
2300
2055
  digesting.length = 0;
@@ -2303,44 +2058,19 @@ var Updater = class {
2303
2058
  }
2304
2059
  }
2305
2060
  }
2306
- /**
2307
- * Save a snapshot of the current data version for `altered()` detection.
2308
- * Cheap O(1) — records the current monotonic version, no serialization.
2309
- */
2310
- snapshot() {
2311
- this.snapshotVersion = this.version;
2312
- return this;
2061
+ function snapshot() {
2062
+ snapshotVersion = version;
2063
+ return api;
2313
2064
  }
2314
- /**
2315
- * Check whether data has changed since the last snapshot.
2316
- * Returns undefined when no snapshot has been taken yet.
2317
- */
2318
- altered() {
2319
- if (this.snapshotVersion === void 0) return void 0;
2320
- return this.version !== this.snapshotVersion;
2065
+ function altered() {
2066
+ if (snapshotVersion === void 0) return void 0;
2067
+ return version !== snapshotVersion;
2321
2068
  }
2322
- /**
2323
- * Translate a refData reference back to its original value.
2324
- *
2325
- * The ref protocol is `SPLITTER` + ascii decimal digits — the exact format
2326
- * emitted by `refFn`. We require that exact shape so a user-supplied
2327
- * string that merely begins with SPLITTER is never accidentally resolved
2328
- * (or mishandled as a "missing ref").
2329
- */
2330
- translate(data) {
2331
- if (typeof data !== "string" || !isRefToken(data)) return data;
2332
- return hasOwnProperty(this.refData, data) ? this.refData[data] : data;
2069
+ function translate(dataVal) {
2070
+ if (typeof dataVal !== "string" || !isRefToken(dataVal)) return dataVal;
2071
+ return hasOwnProperty(refData, dataVal) ? refData[dataVal] : dataVal;
2333
2072
  }
2334
- /**
2335
- * Resolve a dotted property path against refData.
2336
- *
2337
- * Only safe property-path syntax is supported: `a`, `a.b`, `a.b.c`.
2338
- * Numeric literals (e.g. `1`, `1.5`) are returned as numbers. Anything else
2339
- * returns `undefined` — we no longer evaluate arbitrary JavaScript via
2340
- * `new Function`, so the method is CSP-safe and cannot be used as an
2341
- * injection vector.
2342
- */
2343
- parse(expr) {
2073
+ function parse(expr) {
2344
2074
  const trimmed = expr.trim();
2345
2075
  if (!trimmed) return void 0;
2346
2076
  if (/^-?\d+(?:\.\d+)?$/.test(trimmed)) {
@@ -2349,245 +2079,329 @@ var Updater = class {
2349
2079
  if (!/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*$/.test(trimmed)) {
2350
2080
  return void 0;
2351
2081
  }
2352
- let cur = this.refData;
2082
+ let cur = refData;
2353
2083
  for (const segment of trimmed.split(".")) {
2354
2084
  if (cur == null || typeof cur !== "object") return void 0;
2355
- cur = cur[segment];
2085
+ cur = Reflect.get(cur, segment);
2356
2086
  }
2357
2087
  return cur;
2358
2088
  }
2359
- /**
2360
- * Get the set of keys changed since the last digest (for external inspection).
2361
- */
2362
- getChangedKeys() {
2363
- return this.changedKeys;
2364
- }
2365
- };
2366
-
2367
- // src/view-registry.ts
2368
- var viewClassRegistry = {};
2369
- function getViewClass(path) {
2370
- return viewClassRegistry[path];
2371
- }
2372
- function registerViewClass(viewPath, ViewClass) {
2373
- const parsed = parseUri(viewPath);
2374
- const path = parsed.path;
2375
- if (path) {
2376
- viewClassRegistry[path] = ViewClass;
2377
- }
2089
+ function getChangedKeys() {
2090
+ return changedKeys2;
2091
+ }
2092
+ function forceDigest() {
2093
+ hasChangedFlag = 1;
2094
+ changedKeys2 = new Set(Object.keys(data));
2095
+ digest();
2096
+ }
2097
+ const api = {
2098
+ get,
2099
+ set,
2100
+ digest,
2101
+ forceDigest,
2102
+ snapshot,
2103
+ altered,
2104
+ refData,
2105
+ translate,
2106
+ parse,
2107
+ getChangedKeys
2108
+ };
2109
+ return api;
2378
2110
  }
2379
- function invalidateViewClass(viewPath) {
2380
- const parsed = parseUri(viewPath);
2381
- const path = parsed.path;
2382
- if (path) {
2383
- Reflect.deleteProperty(viewClassRegistry, path);
2384
- }
2111
+
2112
+ // src/store.ts
2113
+ var COMPUTED_BRAND = /* @__PURE__ */ Symbol("lark-store-computed");
2114
+ function isComputedMarker(val) {
2115
+ return val !== null && typeof val === "object" && Reflect.get(val, COMPUTED_BRAND) === true;
2385
2116
  }
2386
- function getViewClassRegistry() {
2387
- return viewClassRegistry;
2117
+ function computed(deps, fn) {
2118
+ return { [COMPUTED_BRAND]: true, deps, fn };
2388
2119
  }
2389
-
2390
- // src/hmr.ts
2391
- function reloadViews(viewPath) {
2392
- const allFrames = getAllFrames();
2393
- const toReload = [];
2394
- for (const [, frame] of allFrames) {
2395
- if (frame.viewPath) {
2396
- const parsed = parseUri(frame.viewPath);
2397
- if (parsed.path === viewPath) {
2398
- toReload.push({ frame, fullPath: frame.viewPath });
2120
+ var storeRegistry = /* @__PURE__ */ new Map();
2121
+ function createStore(name, creator) {
2122
+ const listeners = /* @__PURE__ */ new Set();
2123
+ const computedDefs = /* @__PURE__ */ new Map();
2124
+ const computedKeys = /* @__PURE__ */ new Set();
2125
+ const actionKeys = /* @__PURE__ */ new Set();
2126
+ let state;
2127
+ let destroyed = false;
2128
+ const getState = () => state;
2129
+ const setState = (partial) => {
2130
+ if (destroyed) return;
2131
+ const prevState = state;
2132
+ const resolved = typeof partial === "function" ? partial(prevState) : partial;
2133
+ const nextState = { ...prevState };
2134
+ let changed = false;
2135
+ for (const key in resolved) {
2136
+ if (Object.prototype.hasOwnProperty.call(resolved, key) && !computedKeys.has(key) && !actionKeys.has(key)) {
2137
+ const newVal = Reflect.get(resolved, key);
2138
+ if (!Object.is(Reflect.get(prevState, key), newVal)) {
2139
+ Reflect.set(nextState, key, newVal);
2140
+ changed = true;
2141
+ }
2142
+ }
2143
+ }
2144
+ if (!changed) return;
2145
+ state = nextState;
2146
+ recomputeIfNeeded(prevState);
2147
+ for (const listener of listeners) {
2148
+ listener(state, prevState);
2149
+ }
2150
+ };
2151
+ const recomputeIfNeeded = (prevState) => {
2152
+ if (computedDefs.size === 0) return;
2153
+ const changedKeys2 = /* @__PURE__ */ new Set();
2154
+ for (const key of Object.keys(state)) {
2155
+ if (!Object.is(Reflect.get(state, key), Reflect.get(prevState, key))) {
2156
+ changedKeys2.add(key);
2157
+ }
2158
+ }
2159
+ for (const [key, def] of computedDefs) {
2160
+ if (def.deps.some((dep) => changedKeys2.has(dep))) {
2161
+ const newVal = def.fn();
2162
+ if (!Object.is(Reflect.get(state, key), newVal)) {
2163
+ Reflect.set(state, key, newVal);
2164
+ }
2399
2165
  }
2400
2166
  }
2167
+ };
2168
+ const subscribe = (listener) => {
2169
+ listeners.add(listener);
2170
+ return () => {
2171
+ listeners.delete(listener);
2172
+ };
2173
+ };
2174
+ const destroy = () => {
2175
+ destroyed = true;
2176
+ listeners.clear();
2177
+ storeRegistry.delete(name);
2178
+ };
2179
+ const api = { getState, setState, subscribe, destroy };
2180
+ const body = creator(setState, getState);
2181
+ const initialState = {};
2182
+ const actions = {};
2183
+ for (const key of Object.keys(body)) {
2184
+ const val = Reflect.get(body, key);
2185
+ if (isComputedMarker(val)) {
2186
+ computedDefs.set(key, val);
2187
+ computedKeys.add(key);
2188
+ initialState[key] = void 0;
2189
+ } else if (typeof val === "function") {
2190
+ Reflect.set(actions, key, val);
2191
+ actionKeys.add(key);
2192
+ } else {
2193
+ initialState[key] = val;
2194
+ }
2401
2195
  }
2402
- for (const { frame, fullPath } of toReload) {
2403
- frame.mountView(fullPath);
2196
+ state = { ...initialState, ...actions };
2197
+ for (const [key, def] of computedDefs) {
2198
+ Reflect.set(state, key, def.fn());
2404
2199
  }
2200
+ storeRegistry.set(name, api);
2201
+ return api;
2405
2202
  }
2406
- function acceptView(hot, viewPath) {
2407
- hot.accept((newModule) => {
2408
- const candidate = newModule?.default ?? newModule;
2409
- if (typeof candidate === "function") {
2410
- const NewViewClass = candidate;
2411
- registerViewClass(viewPath, NewViewClass);
2412
- reloadViews(viewPath);
2413
- return;
2414
- }
2415
- if (getViewClass(viewPath)) {
2416
- reloadViews(viewPath);
2417
- return;
2418
- }
2419
- hot.invalidate();
2420
- });
2203
+ function isLarkView(instance) {
2204
+ if (!instance || typeof instance !== "object") return false;
2205
+ const updater = Reflect.get(instance, "updater");
2206
+ return updater !== null && typeof updater === "object" && typeof Reflect.get(updater, "set") === "function" && typeof Reflect.get(updater, "digest") === "function";
2421
2207
  }
2422
- function disposeView(hot, viewPath) {
2423
- hot.dispose(() => {
2424
- invalidateViewClass(viewPath);
2208
+ function bindStore(view, store, selector) {
2209
+ if (!isLarkView(view)) return () => {
2210
+ };
2211
+ const extract = (s) => {
2212
+ if (selector) return selector(s);
2213
+ const result = {};
2214
+ for (const key in s) {
2215
+ if (Object.prototype.hasOwnProperty.call(s, key) && typeof s[key] !== "function") {
2216
+ result[key] = s[key];
2217
+ }
2218
+ }
2219
+ return result;
2220
+ };
2221
+ view.updater.set(extract(store.getState()));
2222
+ view.updater.digest();
2223
+ const off = store.subscribe((state) => {
2224
+ view.updater.set(extract(state));
2225
+ view.updater.digest();
2425
2226
  });
2227
+ view.on("destroy", off);
2228
+ return off;
2426
2229
  }
2427
2230
 
2428
- // src/view.ts
2429
- var VIEW_GLOBALS = {};
2430
- if (typeof window !== "undefined") {
2431
- VIEW_GLOBALS["window"] = window;
2231
+ // src/hooks.ts
2232
+ var currentCtx = null;
2233
+ function setCurrentCtx(ctx) {
2234
+ currentCtx = ctx;
2432
2235
  }
2433
- if (typeof document !== "undefined") {
2434
- VIEW_GLOBALS["document"] = document;
2236
+ function getCtx() {
2237
+ if (!currentCtx) {
2238
+ throw new Error("Hooks can only be called inside a view setup function");
2239
+ }
2240
+ return currentCtx;
2435
2241
  }
2436
- var View = class _View {
2437
- /** View ID (same as owner frame ID) */
2438
- id = "";
2439
- /** Owner frame */
2440
- owner = 0;
2441
- /** Updater instance */
2442
- updater;
2443
- /** Signature: > 0 means active, incremented on render, 0 = destroyed */
2444
- signature = 0;
2445
- /** Whether rendered at least once */
2446
- rendered;
2447
- /** Whether view has template */
2448
- template;
2449
- /** Location observation config */
2450
- locationObserved = {
2451
- flag: 0,
2452
- keys: [],
2453
- observePath: false
2242
+ function useState(key, initial) {
2243
+ const ctx = getCtx();
2244
+ const existing = ctx.updater.get(key);
2245
+ if (existing === void 0) {
2246
+ ctx.updater.set({ [key]: initial });
2247
+ }
2248
+ const getter = () => ctx.updater.get(key);
2249
+ const setter = (v) => {
2250
+ ctx.updater.set({ [key]: v }).digest();
2454
2251
  };
2455
- /** Observed state keys */
2456
- observedStateKeys;
2457
- /** Resource map */
2458
- resources = {};
2459
- /** Whether endUpdate pending */
2460
- endUpdatePending;
2461
- /** Internal event storage */
2462
- _events = new EventEmitter();
2463
- // ============================================================
2464
- // Getters for prototype-stored event maps
2465
- // ============================================================
2466
- /** Prototype-stored event maps shape (set by View.prepare). */
2467
- get protoEventState() {
2468
- return Object.getPrototypeOf(this);
2469
- }
2470
- /**
2471
- * Event bitmask map: eventType -> bitmask (1=root, 2=selector).
2472
- * Read from prototype ($evtObjMap) set by View.prepare.
2473
- * Using a getter avoids ES6 class field shadowing the prototype value.
2474
- */
2475
- get eventObjectMap() {
2476
- return this.protoEventState.$evtObjMap ?? {};
2477
- }
2478
- /**
2479
- * Selector event map: eventType -> selector list.
2480
- * Read from prototype ($selMap) set by View.prepare.
2481
- */
2482
- get eventSelectorMap() {
2483
- return this.protoEventState.$selMap ?? {};
2252
+ return [getter, setter];
2253
+ }
2254
+ function useEffect(fn, _deps) {
2255
+ const ctx = getCtx();
2256
+ const cleanup = fn();
2257
+ if (typeof cleanup === "function") {
2258
+ ctx.cleanups.push(cleanup);
2484
2259
  }
2485
- /**
2486
- * Global event list: [{handler, element, eventName, modifiers}].
2487
- * Read from prototype ($globalEvtList) set by View.prepare.
2488
- */
2489
- get globalEventList() {
2490
- return this.protoEventState.$globalEvtList ?? [];
2260
+ }
2261
+ function useStore(store, selector) {
2262
+ const ctx = getCtx();
2263
+ bindStore(ctx, store, selector);
2264
+ if (selector) {
2265
+ return () => {
2266
+ const state = store.getState();
2267
+ return selector(state);
2268
+ };
2491
2269
  }
2492
- // ============================================================
2493
- // Instance lifecycle methods
2494
- // ============================================================
2495
- /**
2496
- * Initialize view (called by Frame when mounting).
2497
- */
2498
- init() {
2270
+ return () => {
2271
+ const data = ctx.updater.get();
2272
+ const result = {};
2273
+ for (const key of Object.keys(data)) {
2274
+ if (key !== "vId" && typeof data[key] !== "function") {
2275
+ result[key] = data[key];
2276
+ }
2277
+ }
2278
+ return result;
2279
+ };
2280
+ }
2281
+ function useInterval(fn, delay) {
2282
+ const ctx = getCtx();
2283
+ const timer = setInterval(fn, delay);
2284
+ ctx.cleanups.push(() => clearInterval(timer));
2285
+ }
2286
+ function useTimeout(fn, delay) {
2287
+ const ctx = getCtx();
2288
+ const timer = setTimeout(fn, delay);
2289
+ ctx.cleanups.push(() => clearTimeout(timer));
2290
+ }
2291
+ function useResource(key, resource, destroyOnRender = false) {
2292
+ const ctx = getCtx();
2293
+ ctx.capture(key, resource, destroyOnRender);
2294
+ }
2295
+ function useEvent(event, handler) {
2296
+ const ctx = getCtx();
2297
+ const off = ctx.on(event, handler);
2298
+ ctx.cleanups.push(off);
2299
+ }
2300
+
2301
+ // src/view.ts
2302
+ var VIEW_GLOBALS = {};
2303
+ if (typeof window !== "undefined") {
2304
+ VIEW_GLOBALS["window"] = window;
2305
+ }
2306
+ if (typeof document !== "undefined") {
2307
+ VIEW_GLOBALS["document"] = document;
2308
+ }
2309
+ function defineView(setup) {
2310
+ return setup;
2311
+ }
2312
+ function createCtx(frame) {
2313
+ const id = frame.id;
2314
+ const updater = createUpdater(id);
2315
+ const emitter3 = createEmitter();
2316
+ const signature = { value: 0 };
2317
+ const rendered = { value: false };
2318
+ const resources = {};
2319
+ const locationObserved = {
2320
+ flag: 0,
2321
+ keys: [],
2322
+ observePath: false
2323
+ };
2324
+ const mutable = {
2325
+ observedStateKeys: void 0,
2326
+ endUpdatePending: void 0,
2327
+ template: void 0,
2328
+ events: void 0,
2329
+ assignFn: void 0
2330
+ };
2331
+ const cleanups = [];
2332
+ function on(event, handler) {
2333
+ emitter3.on(event, handler);
2334
+ return () => emitter3.off(event, handler);
2499
2335
  }
2500
- /**
2501
- * Render view template (called by Frame after init).
2502
- * Wrapped by View.wrapMethod to manage signature + resources.
2503
- */
2504
- render() {
2505
- this.updater.digest();
2336
+ function off(event, handler) {
2337
+ emitter3.off(event, handler);
2506
2338
  }
2507
- // ============================================================
2508
- // Event methods (delegate to internal EventEmitter)
2509
- // ============================================================
2510
- on(event, handler) {
2511
- this._events.on(event, handler);
2512
- return this;
2339
+ function fire(event, data, remove, lastToFirst) {
2340
+ emitter3.fire(event, data, remove, lastToFirst);
2513
2341
  }
2514
- off(event, handler) {
2515
- this._events.off(event, handler);
2516
- return this;
2342
+ function capture(key, resource, destroyOnRender = false) {
2343
+ if (resource !== void 0) {
2344
+ destroyResource(resources, key, true, resource);
2345
+ resources[key] = { entity: resource, destroyOnRender };
2346
+ } else {
2347
+ const entry = resources[key];
2348
+ return entry ? entry.entity : void 0;
2349
+ }
2350
+ return resource;
2517
2351
  }
2518
- fire(event, data, remove, lastToFirst) {
2519
- this._events.fire(event, data, remove, lastToFirst);
2520
- return this;
2352
+ function release(key, destroy = true) {
2353
+ return destroyResource(resources, key, destroy);
2521
2354
  }
2522
- // ============================================================
2523
- // Update methods
2524
- // ============================================================
2525
- /** Get the owning frame, asserting it has been bound. */
2526
- get ownerFrame() {
2527
- return this.owner;
2355
+ function render() {
2356
+ if (signature.value > 0) {
2357
+ signature.value++;
2358
+ fire("render");
2359
+ destroyAllResources(ctx, false);
2360
+ if (typeof ctx.renderMethod === "function") {
2361
+ funcWithTry(ctx.renderMethod, [], ctx, noop);
2362
+ } else {
2363
+ updater.digest();
2364
+ }
2365
+ }
2528
2366
  }
2529
- /**
2530
- * Notify view that HTML update is about to begin.
2531
- * Unmounts child frames in the update zone.
2532
- */
2533
- beginUpdate(id) {
2534
- if (this.signature > 0 && this.endUpdatePending !== void 0) {
2535
- this.ownerFrame.unmountZone(id);
2367
+ function beginUpdate(zoneId) {
2368
+ if (signature.value > 0 && mutable.endUpdatePending !== void 0) {
2369
+ frame.unmountZone(zoneId);
2536
2370
  }
2537
2371
  }
2538
- /**
2539
- * Notify view that HTML update has ended.
2540
- * Mounts child frames in the update zone and runs deferred invokes.
2541
- */
2542
- endUpdate(id, inner) {
2543
- if (this.signature > 0) {
2544
- const updateId = id || this.id;
2372
+ function endUpdate(zoneId, inner) {
2373
+ if (signature.value > 0) {
2374
+ const updateId = zoneId ?? id;
2545
2375
  let flag;
2546
2376
  if (inner) {
2547
2377
  flag = inner;
2548
2378
  } else {
2549
- flag = this.endUpdatePending;
2550
- this.endUpdatePending = 1;
2551
- this.rendered = true;
2379
+ flag = mutable.endUpdatePending;
2380
+ mutable.endUpdatePending = 1;
2381
+ rendered.value = true;
2552
2382
  }
2553
- const ownerFrame = this.ownerFrame;
2554
- ownerFrame.mountZone(updateId);
2383
+ frame.mountZone(updateId);
2555
2384
  if (!flag) {
2556
2385
  setTimeout(
2557
- this.wrapAsync(() => {
2558
- _View.runInvokes(ownerFrame);
2386
+ wrapAsync(() => {
2387
+ runInvokes(frame);
2559
2388
  }),
2560
2389
  0
2561
2390
  );
2562
2391
  }
2563
2392
  }
2564
2393
  }
2565
- // ============================================================
2566
- // Async wrapper
2567
- // ============================================================
2568
- /**
2569
- * Wrap an async callback to check view signature before executing.
2570
- * If the view has been re-rendered or destroyed, the callback is skipped.
2571
- */
2572
- wrapAsync(fn, context) {
2573
- const currentSignature = this.signature;
2394
+ function wrapAsync(fn, context) {
2395
+ const currentSignature = signature.value;
2574
2396
  return (...args) => {
2575
- if (currentSignature > 0 && currentSignature === this.signature) {
2576
- return fn.apply(context || this, args);
2397
+ if (currentSignature > 0 && currentSignature === signature.value) {
2398
+ return fn.apply(context ?? ctx, args);
2577
2399
  }
2578
2400
  return void 0;
2579
2401
  };
2580
2402
  }
2581
- // ============================================================
2582
- // Location observation
2583
- // ============================================================
2584
- /**
2585
- * Observe location parameters or path changes.
2586
- * When observed keys change, render() is called automatically.
2587
- */
2588
- observeLocation(params, observePath = false) {
2589
- const loc = this.locationObserved;
2590
- loc.flag = 1;
2403
+ function observeLocation(params, observePath = false) {
2404
+ locationObserved.flag = 1;
2591
2405
  if (typeof params === "object" && !Array.isArray(params)) {
2592
2406
  const opts = params;
2593
2407
  if (opts["path"]) {
@@ -2598,883 +2412,644 @@ var View = class _View {
2598
2412
  params = paramKeys;
2599
2413
  }
2600
2414
  }
2601
- loc.observePath = observePath;
2415
+ locationObserved.observePath = observePath;
2602
2416
  if (params) {
2603
2417
  if (typeof params === "string") {
2604
- loc.keys = params.split(",");
2418
+ locationObserved.keys = params.split(",");
2605
2419
  } else if (Array.isArray(params)) {
2606
- loc.keys = params;
2420
+ locationObserved.keys = params;
2607
2421
  }
2608
2422
  }
2609
2423
  }
2610
- // ============================================================
2611
- // State observation
2612
- // ============================================================
2613
- /**
2614
- * Observe State data keys for changes.
2615
- * When observed keys change via State.digest(), render() is called.
2616
- */
2617
- observeState(observedKeys) {
2618
- if (typeof observedKeys === "string") {
2619
- this.observedStateKeys = observedKeys.split(",");
2424
+ function observeState(keys2) {
2425
+ if (typeof keys2 === "string") {
2426
+ mutable.observedStateKeys = keys2.split(",");
2620
2427
  } else {
2621
- this.observedStateKeys = observedKeys;
2428
+ mutable.observedStateKeys = keys2;
2622
2429
  }
2623
2430
  }
2624
- // ============================================================
2625
- // Resource management
2626
- // ============================================================
2627
- /**
2628
- * Capture (register) a resource under a key.
2629
- * If a resource already exists at that key, it's destroyed first.
2630
- * When destroyOnRender=true, the resource is destroyed on next render call.
2631
- */
2632
- capture(key, resource, destroyOnRender = false) {
2633
- const cache = this.resources;
2634
- if (resource) {
2635
- _View.destroyResource(cache, key, true, resource);
2636
- cache[key] = {
2637
- entity: resource,
2638
- destroyOnRender
2639
- };
2640
- } else {
2641
- const entry = cache[key];
2642
- return entry ? entry.entity : void 0;
2431
+ function leaveTip(message, condition) {
2432
+ const changeState = { a: 0, b: 0 };
2433
+ function isRouteChange(e) {
2434
+ return "prevent" in e && "reject" in e && "resolve" in e;
2643
2435
  }
2644
- return resource;
2645
- }
2646
- /**
2647
- * Release a captured resource.
2648
- * If destroy=true, calls the resource's destroy() method.
2649
- */
2650
- release(key, destroy = true) {
2651
- return _View.destroyResource(this.resources, key, destroy);
2652
- }
2653
- // ============================================================
2654
- // Leave tip
2655
- // ============================================================
2656
- /**
2657
- * Set up a leave confirmation for route changes and page unload.
2658
- */
2659
- leaveTip(message, condition) {
2660
- const changeListener = function(e) {
2436
+ const changeListener = (e) => {
2437
+ if (!e) return;
2661
2438
  const isRouterChange = e.type === RouterEvents.CHANGE;
2662
2439
  const aKey = isRouterChange ? "a" : "b";
2663
2440
  const bKey = isRouterChange ? "b" : "a";
2664
- if (changeListener[aKey]) {
2665
- e.prevent?.();
2666
- e.reject?.();
2441
+ if (changeState[aKey]) {
2442
+ if (isRouteChange(e)) {
2443
+ e.prevent();
2444
+ e.reject();
2445
+ }
2667
2446
  } else if (condition()) {
2668
- e.prevent?.();
2669
- changeListener[bKey] = 1;
2670
- e.resolve?.();
2447
+ if (isRouteChange(e)) {
2448
+ e.prevent();
2449
+ changeState[bKey] = 1;
2450
+ e.resolve();
2451
+ }
2671
2452
  }
2672
2453
  };
2673
2454
  const unloadListener = (e) => {
2674
- if (condition()) {
2675
- e["msg"] = message;
2455
+ if (e && condition()) {
2456
+ Reflect.set(e, "msg", message);
2676
2457
  }
2677
2458
  };
2678
2459
  Router.on(RouterEvents.CHANGE, changeListener);
2679
2460
  Router.on(RouterEvents.PAGE_UNLOAD, unloadListener);
2680
- this.on("unload", changeListener);
2681
- this.on("destroy", () => {
2461
+ on("unload", changeListener);
2462
+ on("destroy", () => {
2682
2463
  Router.off(RouterEvents.CHANGE, changeListener);
2683
2464
  Router.off(RouterEvents.PAGE_UNLOAD, unloadListener);
2684
2465
  });
2685
2466
  }
2686
- // ============================================================
2687
- // Static public methods
2688
- // ============================================================
2689
- /** Collected makes from mixins */
2690
- static makes;
2691
- /**
2692
- * Prepare a View subclass by scanning its prototype for event method patterns.
2693
- * Pattern: `$?name<eventType1,eventType2>(&modifiers)`
2694
- *
2695
- * Only runs once per View subclass (guarded by makes marker).
2696
- * Called from Frame.mountView before creating the view instance.
2697
- */
2698
- static prepare(oView) {
2699
- if (oView.makes) {
2700
- return oView.makes;
2701
- }
2702
- const makes = [];
2703
- oView.makes = makes;
2704
- const eventsObject = {};
2705
- const eventsList = [];
2706
- const selectorObject = {};
2707
- const mixins = Reflect.get(oView.prototype, "mixins");
2708
- if (mixins && Array.isArray(mixins)) {
2709
- _View.mergeMixins(mixins, oView, makes);
2710
- }
2711
- for (const p in oView.prototype) {
2712
- if (!hasOwnProperty(oView.prototype, p)) continue;
2713
- const currentFn = Reflect.get(oView.prototype, p);
2714
- if (typeof currentFn !== "function") continue;
2715
- const matches = p.match(VIEW_EVENT_METHOD_REGEXP);
2716
- if (!matches) continue;
2717
- const isSelector = matches[1];
2718
- const selectorOrCallback = matches[2];
2719
- const events = matches[3];
2720
- const modifiers = matches[4];
2721
- const mod = {};
2722
- if (modifiers) {
2723
- for (const item of modifiers.split(",")) {
2724
- mod[item] = true;
2725
- }
2726
- }
2727
- const eventTypes = events.split(",");
2728
- for (const item of eventTypes) {
2729
- const globalNode = VIEW_GLOBALS[selectorOrCallback];
2730
- let mask = 1;
2731
- if (isSelector) {
2732
- if (globalNode) {
2733
- eventsList.push({
2734
- handler: currentFn,
2735
- element: globalNode,
2736
- eventName: item,
2737
- modifiers: mod
2738
- });
2739
- continue;
2740
- }
2741
- mask = 2;
2742
- let selectorEntry = selectorObject[item];
2743
- if (!selectorEntry) {
2744
- selectorEntry = selectorObject[item] = {
2745
- selectors: []
2746
- };
2747
- }
2748
- if (!selectorEntry[selectorOrCallback]) {
2749
- selectorEntry[selectorOrCallback] = 1;
2750
- selectorEntry.selectors.push(selectorOrCallback);
2751
- }
2752
- }
2753
- eventsObject[item] = (eventsObject[item] || 0) | mask;
2754
- const combinedKey = selectorOrCallback + SPLITTER + item;
2755
- const existingFn = Reflect.get(oView.prototype, combinedKey);
2756
- if (!existingFn) {
2757
- Reflect.set(oView.prototype, combinedKey, currentFn);
2758
- } else if (typeof existingFn === "function") {
2759
- const mixinFn = currentFn;
2760
- const existingMixin = existingFn;
2761
- if (existingMixin.marker) {
2762
- if (mixinFn.marker) {
2763
- Reflect.set(
2764
- oView.prototype,
2765
- combinedKey,
2766
- _View.processMixinsSameEvent(mixinFn, existingMixin)
2767
- );
2768
- } else if (hasOwnProperty(oView.prototype, p)) {
2769
- Reflect.set(oView.prototype, combinedKey, currentFn);
2770
- }
2771
- }
2772
- }
2467
+ function init(_params) {
2468
+ }
2469
+ function getTemplate() {
2470
+ return mutable.template;
2471
+ }
2472
+ function setTemplate(v) {
2473
+ mutable.template = v;
2474
+ }
2475
+ function getObservedStateKeys() {
2476
+ return mutable.observedStateKeys;
2477
+ }
2478
+ function setObservedStateKeys(v) {
2479
+ mutable.observedStateKeys = v;
2480
+ }
2481
+ function getEndUpdatePending() {
2482
+ return mutable.endUpdatePending;
2483
+ }
2484
+ function setEndUpdatePending(v) {
2485
+ mutable.endUpdatePending = v;
2486
+ }
2487
+ function getEvents() {
2488
+ return mutable.events;
2489
+ }
2490
+ function setEvents(v) {
2491
+ mutable.events = v;
2492
+ }
2493
+ function getAssign() {
2494
+ return mutable.assignFn;
2495
+ }
2496
+ function setAssign(v) {
2497
+ mutable.assignFn = v;
2498
+ }
2499
+ const ctx = {
2500
+ id,
2501
+ owner: frame,
2502
+ updater,
2503
+ signature,
2504
+ rendered,
2505
+ getTemplate,
2506
+ setTemplate,
2507
+ locationObserved,
2508
+ getObservedStateKeys,
2509
+ setObservedStateKeys,
2510
+ resources,
2511
+ emitter: emitter3,
2512
+ getEndUpdatePending,
2513
+ setEndUpdatePending,
2514
+ getEvents,
2515
+ setEvents,
2516
+ cleanups,
2517
+ getAssign,
2518
+ setAssign,
2519
+ render,
2520
+ init,
2521
+ beginUpdate,
2522
+ endUpdate,
2523
+ wrapAsync,
2524
+ observeLocation,
2525
+ observeState,
2526
+ capture,
2527
+ release,
2528
+ leaveTip,
2529
+ fire,
2530
+ on,
2531
+ off
2532
+ };
2533
+ return ctx;
2534
+ }
2535
+ function registerEvents(ctx) {
2536
+ const events = ctx.getEvents();
2537
+ if (!events) return;
2538
+ for (const key of Object.keys(events)) {
2539
+ if (!hasOwnProperty(events, key)) continue;
2540
+ const handler = events[key];
2541
+ if (typeof handler !== "function") continue;
2542
+ const matches = key.match(VIEW_EVENT_METHOD_REGEXP);
2543
+ if (!matches) continue;
2544
+ const isSelector = matches[1];
2545
+ const selectorOrCallback = matches[2];
2546
+ const eventTypes = matches[3];
2547
+ const modifiers = matches[4];
2548
+ const mod = {};
2549
+ if (modifiers) {
2550
+ for (const item of modifiers.split(",")) {
2551
+ mod[item] = true;
2773
2552
  }
2774
2553
  }
2775
- _View.wrapMethod(asRecord(oView.prototype), "render", "$renderWrap");
2776
- Reflect.set(oView.prototype, "$evtObjMap", eventsObject);
2777
- Reflect.set(oView.prototype, "$globalEvtList", eventsList);
2778
- Reflect.set(oView.prototype, "$selMap", selectorObject);
2779
- return makes;
2780
- }
2781
- /**
2782
- * Bind or unbind event delegation for a view instance.
2783
- * Called from Frame during mount/unmount.
2784
- */
2785
- static delegateEvents(view, destroy = false) {
2786
- const eventsObject = view.eventObjectMap;
2787
- const selectorObject = view.eventSelectorMap;
2788
- const eventsList = view.globalEventList;
2789
- for (const e in eventsObject) {
2790
- if (hasOwnProperty(eventsObject, e)) {
2791
- if (destroy) {
2792
- EventDelegator.unbind(e, !!selectorObject[e]);
2793
- } else {
2794
- EventDelegator.bind(e, !!selectorObject[e]);
2795
- }
2554
+ for (const eventType of eventTypes.split(",")) {
2555
+ const globalNode = VIEW_GLOBALS[selectorOrCallback];
2556
+ if (isSelector && globalNode) {
2557
+ registerGlobalEvent(ctx, globalNode, eventType, handler, mod, key);
2558
+ } else if (isSelector) {
2559
+ EventDelegator.bind(eventType, true);
2560
+ } else {
2561
+ EventDelegator.bind(eventType, false);
2796
2562
  }
2797
2563
  }
2798
- for (const entry of eventsList) {
2799
- if (destroy) {
2800
- entry.element.removeEventListener(
2801
- entry.eventName,
2802
- entry.boundHandler
2803
- );
2564
+ }
2565
+ }
2566
+ function unregisterEvents(ctx) {
2567
+ const events = ctx.getEvents();
2568
+ if (!events) return;
2569
+ for (const key of Object.keys(events)) {
2570
+ if (!hasOwnProperty(events, key)) continue;
2571
+ const matches = key.match(VIEW_EVENT_METHOD_REGEXP);
2572
+ if (!matches) continue;
2573
+ const isSelector = matches[1];
2574
+ const selectorOrCallback = matches[2];
2575
+ const eventTypes = matches[3];
2576
+ for (const eventType of eventTypes.split(",")) {
2577
+ const globalNode = VIEW_GLOBALS[selectorOrCallback];
2578
+ if (isSelector && globalNode) {
2579
+ } else if (isSelector) {
2580
+ EventDelegator.unbind(eventType, true);
2804
2581
  } else {
2805
- const handler = entry.handler;
2806
- const element = entry.element;
2807
- const modifiers = entry.modifiers;
2808
- entry.boundHandler = function(domEvent) {
2809
- const extendedEvent = domEvent;
2810
- extendedEvent.eventTarget = element;
2811
- if (modifiers) {
2812
- const kbEvent = domEvent;
2813
- if (modifiers["ctrl"] && !kbEvent.ctrlKey || modifiers["shift"] && !kbEvent.shiftKey || modifiers["alt"] && !kbEvent.altKey || modifiers["meta"] && !kbEvent.metaKey) {
2814
- return;
2815
- }
2816
- }
2817
- funcWithTry(handler, [domEvent], view, noop);
2818
- };
2819
- entry.element.addEventListener(
2820
- entry.eventName,
2821
- entry.boundHandler
2822
- );
2582
+ EventDelegator.unbind(eventType, false);
2823
2583
  }
2824
2584
  }
2825
2585
  }
2826
- /**
2827
- * Destroy all resources managed by a view.
2828
- * If lastly=true, destroy ALL resources; otherwise only destroyOnRender ones.
2829
- */
2830
- static destroyAllResources(view, lastly) {
2831
- const cache = view.resources;
2832
- for (const p in cache) {
2833
- if (hasOwnProperty(cache, p)) {
2834
- const entry = cache[p];
2835
- if (lastly || entry.destroyOnRender) {
2836
- _View.destroyResource(cache, p, true);
2586
+ EventDelegator.clearRangeEvents(ctx.id);
2587
+ }
2588
+ function registerGlobalEvent(ctx, element, eventName, handler, modifiers, _key) {
2589
+ const listener = {
2590
+ handleEvent(domEvent) {
2591
+ Reflect.set(domEvent, "eventTarget", element);
2592
+ if (modifiers) {
2593
+ const ctrlKey = Reflect.get(domEvent, "ctrlKey");
2594
+ const shiftKey = Reflect.get(domEvent, "shiftKey");
2595
+ const altKey = Reflect.get(domEvent, "altKey");
2596
+ const metaKey = Reflect.get(domEvent, "metaKey");
2597
+ if (modifiers["ctrl"] && !ctrlKey || modifiers["shift"] && !shiftKey || modifiers["alt"] && !altKey || modifiers["meta"] && !metaKey) {
2598
+ return;
2837
2599
  }
2838
2600
  }
2601
+ funcWithTry(handler, [domEvent], ctx, noop);
2839
2602
  }
2840
- }
2841
- /**
2842
- * Process deferred invoke calls on a frame.
2843
- */
2844
- static runInvokes(frame) {
2845
- const list = frame.invokeList;
2846
- if (!list) return;
2847
- while (list.length) {
2848
- const entry = list.shift();
2849
- if (entry && !entry.removed) {
2850
- frame.invoke(entry.name, entry.args);
2603
+ };
2604
+ element.addEventListener(eventName, listener);
2605
+ ctx.on("destroy", () => {
2606
+ element.removeEventListener(eventName, listener);
2607
+ });
2608
+ }
2609
+ function destroyAllResources(ctx, lastly) {
2610
+ const cache = ctx.resources;
2611
+ for (const p in cache) {
2612
+ if (hasOwnProperty(cache, p)) {
2613
+ const entry = cache[p];
2614
+ if (lastly || entry.destroyOnRender) {
2615
+ destroyResource(cache, p, true);
2851
2616
  }
2852
2617
  }
2853
2618
  }
2854
- // ============================================================
2855
- // Static private methods
2856
- // ============================================================
2857
- /**
2858
- * Wrap a method on the prototype to add signature checking and resource cleanup.
2859
- */
2860
- static wrapMethod(proto, fnName, shortKey) {
2861
- const originalFn = proto[fnName];
2862
- if (typeof originalFn !== "function") return;
2863
- const originalAsFn = originalFn;
2864
- const wrapped = function(...args) {
2865
- if (this.signature > 0) {
2866
- this.signature++;
2867
- this.fire("render");
2868
- _View.destroyAllResources(this, false);
2869
- const lookup = asRecord(this);
2870
- const candidate = lookup[fnName];
2871
- const instanceFn = typeof candidate === "function" ? candidate : originalAsFn;
2872
- const fnToCall = instanceFn === wrapped ? originalAsFn : instanceFn;
2873
- return funcWithTry(fnToCall, args, this, noop);
2874
- }
2875
- return void 0;
2876
- };
2877
- proto[fnName] = wrapped;
2878
- proto[shortKey] = wrapped;
2619
+ }
2620
+ function destroyResource(cache, key, callDestroy, oldEntity) {
2621
+ const entry = cache[key];
2622
+ if (!entry || entry.entity === oldEntity) return void 0;
2623
+ const entity = entry.entity;
2624
+ if (entity && typeof entity === "object") {
2625
+ const destroyFn = Reflect.get(entity, "destroy");
2626
+ if (typeof destroyFn === "function" && callDestroy) {
2627
+ funcWithTry(destroyFn, [], entity, noop);
2628
+ }
2629
+ }
2630
+ Reflect.deleteProperty(cache, key);
2631
+ return entity;
2632
+ }
2633
+ function runInvokes(frame) {
2634
+ const list = frame.invokeList;
2635
+ if (!list) return;
2636
+ while (list.length) {
2637
+ const entry = list.shift();
2638
+ if (entry && !entry.removed) {
2639
+ frame.invoke(entry.name, entry.args);
2640
+ }
2879
2641
  }
2880
- /**
2881
- * When two mixins define the same event method, merge them into
2882
- * a single function that calls both in sequence.
2883
- */
2884
- static processMixinsSameEvent(additional, exist) {
2885
- let temp;
2886
- if (exist.handlerList) {
2887
- temp = exist;
2888
- } else {
2889
- const merged = function(...e) {
2890
- funcWithTry(merged.handlerList ?? [], e, this, noop);
2891
- };
2892
- merged.handlerList = [exist];
2893
- merged.marker = 1;
2894
- temp = merged;
2895
- }
2896
- temp.handlerList = (temp.handlerList ?? []).concat(
2897
- additional.handlerList ?? [additional]
2898
- );
2899
- return temp;
2642
+ }
2643
+ function mountCtx(frame, setup, params) {
2644
+ const ctx = createCtx(frame);
2645
+ setCurrentCtx(ctx);
2646
+ let descriptor;
2647
+ try {
2648
+ descriptor = setup(ctx, params);
2649
+ } finally {
2650
+ setCurrentCtx(null);
2651
+ }
2652
+ ctx.setTemplate(descriptor.template);
2653
+ ctx.setEvents(descriptor.events);
2654
+ if (descriptor.assign) {
2655
+ ctx.setAssign(descriptor.assign);
2656
+ }
2657
+ ctx.signature.value = 1;
2658
+ frame.view = ctx;
2659
+ registerEvents(ctx);
2660
+ if (ctx.getTemplate()) {
2661
+ ctx.render();
2662
+ } else {
2663
+ ctx.endUpdate();
2900
2664
  }
2901
- /**
2902
- * Merge an array of mixin objects into the view prototype.
2903
- */
2904
- static mergeMixins(mixins, viewClass, makes) {
2905
- const proto = asRecord(viewClass.prototype);
2906
- const temp = {};
2907
- for (const node of mixins) {
2908
- for (const p in node) {
2909
- if (!hasOwnProperty(node, p)) continue;
2910
- const fn = node[p];
2911
- if (typeof fn !== "function") continue;
2912
- const mixinFn = fn;
2913
- const exist = temp[p];
2914
- if (p === "make") {
2915
- makes.push(mixinFn);
2916
- continue;
2917
- }
2918
- if (VIEW_EVENT_METHOD_REGEXP.test(p)) {
2919
- if (exist) {
2920
- temp[p] = _View.processMixinsSameEvent(mixinFn, exist);
2921
- } else {
2922
- mixinFn.marker = 1;
2923
- temp[p] = mixinFn;
2924
- }
2925
- } else if (!exist) {
2926
- temp[p] = mixinFn;
2927
- }
2928
- }
2929
- }
2930
- for (const p in temp) {
2931
- if (!hasOwnProperty(proto, p)) {
2932
- proto[p] = temp[p];
2933
- }
2934
- }
2665
+ return ctx;
2666
+ }
2667
+ function unmountCtx(ctx) {
2668
+ for (let i = ctx.cleanups.length - 1; i >= 0; i--) {
2669
+ const cleanup = ctx.cleanups[i];
2670
+ funcWithTry(cleanup, [], null, noop);
2671
+ }
2672
+ ctx.cleanups.length = 0;
2673
+ unregisterEvents(ctx);
2674
+ destroyAllResources(ctx, true);
2675
+ if (ctx.signature.value > 0) {
2676
+ ctx.fire("destroy", void 0, true, true);
2677
+ }
2678
+ EventDelegator.clearRangeEvents(ctx.id);
2679
+ ctx.signature.value = 0;
2680
+ }
2681
+
2682
+ // src/module-loader.ts
2683
+ var config = {
2684
+ rootId: "root",
2685
+ routeMode: "history",
2686
+ hashbang: "#!",
2687
+ error: (error) => {
2688
+ throw error;
2935
2689
  }
2936
- /**
2937
- * Destroy a single resource entry.
2938
- */
2939
- static destroyResource(cache, key, callDestroy, oldEntity) {
2940
- const entry = cache[key];
2941
- if (!entry || entry.entity === oldEntity) return void 0;
2942
- const entity = entry.entity;
2943
- if (entity && typeof entity === "object") {
2944
- const destroyFn = entity["destroy"];
2945
- if (typeof destroyFn === "function" && callDestroy) {
2946
- funcWithTry(destroyFn, [], entity, noop);
2690
+ };
2691
+ function use(names, callback) {
2692
+ const nameList = typeof names === "string" ? [names] : names;
2693
+ const loadPromise = (() => {
2694
+ if (config.require) {
2695
+ const result = config.require(nameList);
2696
+ if (result && typeof result.then === "function") {
2697
+ return result;
2947
2698
  }
2699
+ return Promise.resolve([]);
2948
2700
  }
2949
- Reflect.deleteProperty(cache, key);
2950
- return entity;
2951
- }
2952
- // ============================================================
2953
- // Static: extend and merge
2954
- // ============================================================
2955
- /**
2956
- * Extend View to create a new View subclass.
2957
- *
2958
- * Supports:
2959
- * - props.make: constructor-like init (called with initParams + {node, deep})
2960
- * - props.mixins: array of mixin objects
2961
- * - Event method patterns: `'name<click>'` etc.
2962
- */
2963
- static extend(props, statics) {
2964
- const definedProps = props ?? {};
2965
- const make = definedProps["make"];
2966
- const makes = [];
2967
- if (typeof make === "function") {
2968
- makes.push(make);
2969
- }
2970
- const ParentView = this;
2971
- const ChildView = class extends ParentView {
2972
- constructor(nodeId, ownerFrame, initParams, node, mixinCtors) {
2973
- super(nodeId, ownerFrame, initParams, node, []);
2974
- const instanceProps = this;
2975
- for (const key in definedProps) {
2976
- if (hasOwnProperty(definedProps, key) && key !== "make" && key !== "render") {
2977
- instanceProps[key] = definedProps[key];
2978
- }
2979
- }
2980
- this.id = nodeId;
2981
- this.owner = ownerFrame;
2982
- this.updater = new Updater(nodeId);
2983
- const params = [
2984
- initParams,
2985
- {
2986
- node,
2987
- deep: !this.template
2701
+ return Promise.all(
2702
+ nameList.map((name) => {
2703
+ const importPath = name.startsWith(".") || name.startsWith("/") ? name : `./${name}`;
2704
+ return import(
2705
+ /* @vite-ignore */
2706
+ /* webpackIgnore: true */
2707
+ importPath
2708
+ ).then((mod) => {
2709
+ return mod && (mod["__esModule"] || // For Webpack
2710
+ typeof mod["default"] === "function") ? mod["default"] : mod;
2711
+ }).catch((err) => {
2712
+ const errorHandler = config.error;
2713
+ if (errorHandler) {
2714
+ errorHandler(err instanceof Error ? err : new Error(String(err)));
2988
2715
  }
2989
- ];
2990
- const concatCtors = makes.concat(mixinCtors || []);
2991
- if (concatCtors.length) {
2992
- funcWithTry(concatCtors, params, this, noop);
2993
- }
2994
- }
2995
- };
2996
- for (const key in definedProps) {
2997
- if (hasOwnProperty(definedProps, key) && key !== "make") {
2998
- Reflect.set(ChildView.prototype, key, definedProps[key]);
2999
- }
3000
- }
3001
- if (statics) {
3002
- for (const key in statics) {
3003
- if (hasOwnProperty(statics, key)) {
3004
- Reflect.set(ChildView, key, statics[key]);
3005
- }
3006
- }
3007
- }
3008
- return ChildView;
3009
- }
3010
- /**
3011
- * Merge mixins into View prototype.
3012
- */
3013
- static merge(...mixins) {
3014
- const existingCtors = this.makes || [];
3015
- _View.mergeMixins(mixins, this, existingCtors);
3016
- return this;
2716
+ return void 0;
2717
+ });
2718
+ })
2719
+ );
2720
+ })();
2721
+ if (callback) {
2722
+ loadPromise.then((modules) => {
2723
+ callback(...modules);
2724
+ });
3017
2725
  }
3018
- // ============================================================
3019
- // HMR support (static accept / dispose)
3020
- // ============================================================
3021
- /**
3022
- * Set up HMR accept handler for this view module.
3023
- *
3024
- * When the module is hot-replaced, the new View class is extracted from
3025
- * the new module, registered in the view registry, and all currently
3026
- * mounted frames using this viewPath are re-mounted.
3027
- *
3028
- * No-op when `hot` is undefined (production / non-HMR environment).
3029
- *
3030
- * ```ts
3031
- * if (import.meta.hot) {
3032
- * HomeView.accept(import.meta.hot, 'home');
3033
- * }
3034
- * ```
3035
- */
3036
- static accept(hot, viewPath) {
3037
- if (!hot) return;
3038
- acceptView(hot, viewPath);
2726
+ return loadPromise;
2727
+ }
2728
+
2729
+ // src/view-registry.ts
2730
+ var viewSetupRegistry = {};
2731
+ function getViewClass(path) {
2732
+ return viewSetupRegistry[path];
2733
+ }
2734
+ function registerViewClass(viewPath, setup) {
2735
+ const parsed = parseUri(viewPath);
2736
+ const path = parsed.path;
2737
+ if (path) {
2738
+ viewSetupRegistry[path] = setup;
3039
2739
  }
3040
- /**
3041
- * Set up HMR dispose handler for this view module.
3042
- *
3043
- * When the module is about to be replaced, the old View class is removed
3044
- * from the registry so subsequent lookups don't return the stale class.
3045
- *
3046
- * No-op when `hot` is undefined (production / non-HMR environment).
3047
- *
3048
- * ```ts
3049
- * if (import.meta.hot) {
3050
- * HomeView.dispose(import.meta.hot, 'home');
3051
- * }
3052
- * ```
3053
- */
3054
- static dispose(hot, viewPath) {
3055
- if (!hot) return;
3056
- disposeView(hot, viewPath);
2740
+ }
2741
+ function invalidateViewClass(viewPath) {
2742
+ const parsed = parseUri(viewPath);
2743
+ const path = parsed.path;
2744
+ if (path) {
2745
+ Reflect.deleteProperty(viewSetupRegistry, path);
3057
2746
  }
3058
- };
3059
- function defineView(props, statics) {
3060
- return View.extend(props, statics);
2747
+ }
2748
+ function getViewClassRegistry() {
2749
+ return viewSetupRegistry;
3061
2750
  }
3062
2751
 
3063
2752
  // src/frame.ts
2753
+ var frameRegistry = /* @__PURE__ */ new Map();
3064
2754
  var rootFrame;
3065
2755
  var globalAlter;
3066
- var MAX_FRAME_POOL = 64;
3067
- var frameCache = [];
3068
- var staticEmitter = new EventEmitter();
3069
- var Frame = class _Frame extends EventEmitter {
3070
- /** Frame ID (same as owner DOM element ID) */
3071
- id;
3072
- /** Parent Frame ID */
3073
- _parentId = void 0;
3074
- get parentId() {
3075
- return this._parentId;
3076
- }
3077
- /** Children map: id -> id */
3078
- childrenMap = {};
3079
- /** Children count */
3080
- childrenCount = 0;
3081
- /** Ready count (children that have fired 'created') */
3082
- readyCount = 0;
3083
- /** Set of child frame IDs that have fired 'created' */
3084
- readyMap = /* @__PURE__ */ new Set();
3085
- /** View instance */
3086
- viewInstance;
3087
- /** Get view instance (read-only) */
3088
- get view() {
3089
- return this.viewInstance;
3090
- }
3091
- /** Invoke list for deferred method calls */
3092
- invokeList = [];
3093
- /** Signature for async operation tracking */
3094
- signature = 1;
3095
- /** Whether view has altered */
3096
- hasAltered = 0;
3097
- /** Whether view is destroyed */
3098
- destroyed = 0;
3099
- /** View path (v-lark attribute value) */
3100
- viewPath;
3101
- /** Original template before mount */
3102
- originalTemplate;
3103
- /** Hold fire created flag */
3104
- holdFireCreated = 0;
3105
- /** Children created flag */
3106
- childrenCreated = 0;
3107
- /** Children alter flag */
3108
- childrenAlter = 0;
3109
- constructor(id, parentId) {
3110
- super();
3111
- this.id = id;
3112
- if (parentId) {
3113
- this._parentId = parentId;
3114
- }
3115
- registerFrame(id, this);
3116
- const element = document.getElementById(id);
3117
- if (element) {
3118
- element.frame = this;
3119
- element.frameBound = 1;
3120
- }
3121
- _Frame.fire("add", { frame: this });
3122
- }
3123
- // ============================================================
3124
- // Instance methods
3125
- // ============================================================
3126
- /**
3127
- * Mount a view to this frame.
3128
- *
3129
- * Complete flow:
3130
- * 1. Parse viewPath, translate query params from parent
3131
- * 2. Unmount current view
3132
- * 3. Load View class (via require or provided ViewClass)
3133
- * 4. View_Prepare (scan event methods)
3134
- * 5. Create View instance
3135
- * 6. View_DelegateEvents (bind DOM events)
3136
- * 7. Call view.init()
3137
- * 8. If view has template, call render via Updater
3138
- * 9. If no template, call endUpdate directly
3139
- */
3140
- mountView(viewPath, viewInitParams) {
3141
- const node = document.getElementById(this.id);
3142
- const pId = this.parentId;
3143
- if (!this.hasAltered && node) {
3144
- this.hasAltered = 1;
3145
- this.originalTemplate = node.innerHTML;
3146
- }
3147
- this.unmountView();
3148
- this.destroyed = 0;
3149
- const parsed = parseUri(viewPath || "");
3150
- const viewClassName = parsed.path;
3151
- if (!node || !viewClassName) return;
3152
- this.viewPath = viewPath;
3153
- const params = parsed["params"];
3154
- translateQuery(pId || this.id, viewPath, params);
3155
- const initParams = { ...params };
3156
- if (viewInitParams) {
3157
- assign(initParams, viewInitParams);
3158
- }
3159
- const sign = this.signature;
3160
- const registered = getViewClass(viewClassName);
3161
- if (registered) {
3162
- this.doMountView(registered, initParams, node, sign);
3163
- return;
3164
- }
3165
- use(viewClassName, (ViewClass) => {
3166
- if (sign !== this.signature) return;
3167
- if (typeof ViewClass === "function") {
3168
- const ViewClassTyped = ViewClass;
3169
- registerViewClass(viewClassName, ViewClassTyped);
3170
- this.doMountView(ViewClassTyped, initParams, node, sign);
3171
- } else {
3172
- const error = new Error(`Cannot load view: ${viewClassName}`);
3173
- const errorHandler = config.error;
3174
- if (errorHandler) {
3175
- errorHandler(error);
3176
- }
2756
+ var staticEmitter = createEmitter();
2757
+ function isViewSetup(fn) {
2758
+ return typeof fn === "function";
2759
+ }
2760
+ function createFrame(id, parentId) {
2761
+ const emitter3 = createEmitter();
2762
+ const invokeList = [];
2763
+ const childrenMap = {};
2764
+ const readyMap = /* @__PURE__ */ new Set();
2765
+ let viewPath;
2766
+ function getViewPath() {
2767
+ return viewPath;
2768
+ }
2769
+ const frame = {
2770
+ id,
2771
+ getViewPath,
2772
+ parentId,
2773
+ view: void 0,
2774
+ invokeList,
2775
+ signature: 1,
2776
+ destroyed: 0,
2777
+ hasAltered: 0,
2778
+ originalTemplate: void 0,
2779
+ holdFireCreated: 0,
2780
+ childrenCreated: 0,
2781
+ childrenAlter: 0,
2782
+ childrenMap,
2783
+ childrenCount: 0,
2784
+ readyCount: 0,
2785
+ readyMap,
2786
+ emitter: emitter3,
2787
+ mountView(viewPathArg, viewInitParams) {
2788
+ const node = document.getElementById(frame.id);
2789
+ const pId = frame.parentId;
2790
+ if (!frame.hasAltered && node) {
2791
+ frame.hasAltered = 1;
2792
+ frame.originalTemplate = node.innerHTML;
3177
2793
  }
3178
- });
3179
- }
3180
- /**
3181
- * Internal: actually mount the view after class is loaded.
3182
- */
3183
- doMountView(ViewClass, params, node, sign) {
3184
- if (sign !== this.signature) return;
3185
- const mixinConstructors = View.prepare(ViewClass);
3186
- const Constructor = ViewClass;
3187
- const view = new Constructor(
3188
- this.id,
3189
- this,
3190
- params,
3191
- node,
3192
- mixinConstructors
3193
- );
3194
- this.viewInstance = view;
3195
- view.signature = 1;
3196
- View.delegateEvents(view);
3197
- const initResult = funcWithTry(
3198
- view.init,
3199
- [params, { node, deep: !view.template }],
3200
- view,
3201
- noop
3202
- );
3203
- const nextSign = ++this.signature;
3204
- Promise.resolve(initResult).then(() => {
3205
- if (nextSign !== this.signature) return;
3206
- if (view.template) {
3207
- view.render();
3208
- } else {
3209
- this.hasAltered = 0;
3210
- if (!view.endUpdatePendingFlag) {
3211
- view.endUpdate();
2794
+ frame.unmountView();
2795
+ frame.destroyed = 0;
2796
+ const parsed = parseUri(viewPathArg || "");
2797
+ const viewClassName = parsed.path;
2798
+ if (!node || !viewClassName) return;
2799
+ viewPath = viewPathArg;
2800
+ const params = parsed.params;
2801
+ translateQuery(pId ?? frame.id, viewPathArg, params);
2802
+ const initParams = { ...params };
2803
+ if (viewInitParams) {
2804
+ assign(initParams, viewInitParams);
2805
+ }
2806
+ const sign = frame.signature;
2807
+ const registered = getViewClass(viewClassName);
2808
+ if (registered) {
2809
+ doMountView(registered, initParams, node, sign);
2810
+ return;
2811
+ }
2812
+ use(viewClassName, (loadedModule) => {
2813
+ if (sign !== frame.signature) return;
2814
+ if (isViewSetup(loadedModule)) {
2815
+ registerViewClass(viewClassName, loadedModule);
2816
+ doMountView(loadedModule, initParams, node, sign);
2817
+ } else {
2818
+ const error = new Error(`Cannot load view: ${viewClassName}`);
2819
+ const errorHandler = config.error;
2820
+ if (errorHandler) {
2821
+ errorHandler(error);
2822
+ }
3212
2823
  }
2824
+ });
2825
+ },
2826
+ unmountView() {
2827
+ const currentView = frame.view;
2828
+ frame.invokeList.length = 0;
2829
+ if (!currentView) return;
2830
+ if (!globalAlter) {
2831
+ globalAlter = { id: frame.id };
3213
2832
  }
3214
- });
3215
- }
3216
- /**
3217
- * Unmount current view.
3218
- */
3219
- unmountView() {
3220
- const view = this.view;
3221
- this.invokeList = [];
3222
- if (!view) return;
3223
- if (!globalAlter) {
3224
- globalAlter = { id: this.id };
3225
- }
3226
- this.destroyed = 1;
3227
- this.unmountZone();
3228
- notifyAlter(this, globalAlter);
3229
- if (view.signature > 0) {
3230
- view.fire("destroy", void 0, true, true);
3231
- }
3232
- EventDelegator.clearRangeEvents(this.id);
3233
- delete this["viewInstance"];
3234
- const node = document.getElementById(this.id);
3235
- if (node && this.originalTemplate) {
3236
- node.innerHTML = this.originalTemplate;
3237
- }
3238
- globalAlter = void 0;
3239
- unmark(view);
3240
- }
3241
- /**
3242
- * Mount a child frame.
3243
- */
3244
- mountFrame(frameId, viewPath, viewInitParams) {
3245
- notifyAlter(this, { id: frameId });
3246
- let childFrame = getFrame(frameId);
3247
- if (!childFrame) {
3248
- if (!this.childrenMap[frameId]) {
3249
- this.childrenCount++;
2833
+ frame.destroyed = 1;
2834
+ frame.unmountZone();
2835
+ notifyAlter(frame, globalAlter);
2836
+ unmountCtx(currentView);
2837
+ frame.view = void 0;
2838
+ const node = document.getElementById(frame.id);
2839
+ if (node && frame.originalTemplate) {
2840
+ node.innerHTML = frame.originalTemplate;
3250
2841
  }
3251
- this.childrenMap[frameId] = frameId;
3252
- childFrame = frameCache.pop();
3253
- if (childFrame) {
3254
- reInitFrame(childFrame, frameId, this.id);
3255
- } else {
3256
- childFrame = new _Frame(frameId, this.id);
2842
+ globalAlter = void 0;
2843
+ unmark(currentView);
2844
+ },
2845
+ mountFrame(frameId, viewPathArg, viewInitParams) {
2846
+ notifyAlter(frame, { id: frameId });
2847
+ let childFrame = frameRegistry.get(frameId);
2848
+ if (!childFrame) {
2849
+ if (!frame.childrenMap[frameId]) {
2850
+ frame.childrenCount++;
2851
+ }
2852
+ frame.childrenMap[frameId] = frameId;
2853
+ childFrame = createFrame(frameId, frame.id);
3257
2854
  }
3258
- }
3259
- childFrame.mountView(viewPath, viewInitParams);
3260
- return childFrame;
3261
- }
3262
- /**
3263
- * Unmount a child frame.
3264
- */
3265
- unmountFrame(id) {
3266
- const targetId = id ? this.childrenMap[id] : this.id;
3267
- const frame = getFrame(targetId);
3268
- if (!frame) return;
3269
- const wasCreated = frame.readyCount > 0;
3270
- const pId = frame.parentId;
3271
- frame.unmountView();
3272
- removeFrame2(targetId, wasCreated);
3273
- reInitFrameForCache(frame);
3274
- if (frameCache.length < MAX_FRAME_POOL) {
3275
- frameCache.push(frame);
3276
- }
3277
- const parent = getFrame(pId || "");
3278
- if (parent && parent.childrenMap[targetId]) {
3279
- Reflect.deleteProperty(parent.childrenMap, targetId);
3280
- parent.childrenCount--;
3281
- notifyCreated(parent);
3282
- }
3283
- }
3284
- /**
3285
- * Mount all views in a zone.
3286
- */
3287
- mountZone(zoneId) {
3288
- const targetZone = zoneId || this.id;
3289
- this.holdFireCreated = 1;
3290
- const rootEl = document.getElementById(targetZone);
3291
- if (!rootEl) return;
3292
- const viewElements = rootEl.querySelectorAll(`[${LARK_VIEW}]`);
3293
- const frames = [];
3294
- viewElements.forEach((el) => {
3295
- if (!(el instanceof HTMLElement)) return;
3296
- if (htmlElIsBound(el)) return;
3297
- const elId = ensureElementId(el, "frame_");
3298
- el.frameBound = 1;
3299
- const viewPath = getAttribute(el, LARK_VIEW);
3300
- frames.push([elId, viewPath]);
3301
- });
3302
- for (const [frameId, viewPath] of frames) {
3303
- this.mountFrame(frameId, viewPath);
3304
- }
3305
- this.holdFireCreated = 0;
3306
- notifyCreated(this);
3307
- }
3308
- /**
3309
- * Unmount all views in a zone.
3310
- */
3311
- unmountZone(zoneId) {
3312
- for (const childId in this.childrenMap) {
3313
- if (hasOwnProperty(this.childrenMap, childId)) {
3314
- if (!zoneId || childId !== zoneId) {
3315
- this.unmountFrame(childId);
2855
+ childFrame.mountView(viewPathArg, viewInitParams);
2856
+ return childFrame;
2857
+ },
2858
+ unmountFrame(id2) {
2859
+ const targetId = id2 ? frame.childrenMap[id2] : frame.id;
2860
+ const targetFrame = frameRegistry.get(targetId);
2861
+ if (!targetFrame) return;
2862
+ const wasCreated = targetFrame.readyCount > 0;
2863
+ const pId = targetFrame.parentId;
2864
+ targetFrame.unmountView();
2865
+ removeFrame(targetId, wasCreated);
2866
+ const parent = frameRegistry.get(pId ?? "");
2867
+ if (parent && parent.childrenMap[targetId]) {
2868
+ Reflect.deleteProperty(parent.childrenMap, targetId);
2869
+ parent.childrenCount--;
2870
+ notifyCreated(parent);
2871
+ }
2872
+ },
2873
+ mountZone(zoneId) {
2874
+ const targetZone = zoneId ?? frame.id;
2875
+ frame.holdFireCreated = 1;
2876
+ const rootEl = document.getElementById(targetZone);
2877
+ if (!rootEl) return;
2878
+ const viewElements = rootEl.querySelectorAll(`[${LARK_VIEW}]`);
2879
+ const frames = [];
2880
+ viewElements.forEach((el) => {
2881
+ if (!(el instanceof HTMLElement)) return;
2882
+ if (htmlElIsBound(el)) return;
2883
+ const elId = ensureElementId(el, "frame_");
2884
+ Reflect.set(el, "frameBound", 1);
2885
+ const viewPathArg = getAttribute(el, LARK_VIEW);
2886
+ if (viewPathArg) {
2887
+ frames.push([elId, viewPathArg]);
3316
2888
  }
2889
+ });
2890
+ for (const [frameId, viewPathArg] of frames) {
2891
+ frame.mountFrame(frameId, viewPathArg);
3317
2892
  }
3318
- }
3319
- notifyCreated(this);
3320
- }
3321
- /**
3322
- * Get all child frame IDs.
3323
- */
3324
- children() {
3325
- const result = [];
3326
- for (const id in this.childrenMap) {
3327
- if (hasOwnProperty(this.childrenMap, id)) {
3328
- result.push(id);
2893
+ frame.holdFireCreated = 0;
2894
+ notifyCreated(frame);
2895
+ },
2896
+ unmountZone(zoneId) {
2897
+ for (const childId in frame.childrenMap) {
2898
+ if (hasOwnProperty(frame.childrenMap, childId)) {
2899
+ if (!zoneId || childId !== zoneId) {
2900
+ frame.unmountFrame(childId);
2901
+ }
2902
+ }
3329
2903
  }
3330
- }
3331
- return result;
3332
- }
3333
- /**
3334
- * Get parent frame at given level.
3335
- * @param level - How many levels up (default 1)
3336
- */
3337
- parent(level = 1) {
3338
- let frame = void 0;
3339
- let currentPid = this.parentId;
3340
- let n = level >>> 0 || 1;
3341
- while (currentPid && n--) {
3342
- frame = getFrame(currentPid);
3343
- currentPid = frame?.parentId;
3344
- }
3345
- return frame;
3346
- }
3347
- /**
3348
- * Invoke a method on the view.
3349
- */
3350
- invoke(name, args) {
3351
- let result;
3352
- const view = this.view;
3353
- if (view && view.rendered) {
3354
- const fn = Reflect.get(view, name);
3355
- if (typeof fn === "function") {
3356
- result = funcWithTry(fn, args || [], view, noop);
2904
+ notifyCreated(frame);
2905
+ },
2906
+ parent(level = 1) {
2907
+ let result = void 0;
2908
+ let currentPid = frame.parentId;
2909
+ let n = level >>> 0 || 1;
2910
+ while (currentPid && n--) {
2911
+ result = frameRegistry.get(currentPid);
2912
+ currentPid = result?.parentId;
3357
2913
  }
3358
- } else {
3359
- const key = SPLITTER + name;
3360
- let existingEntry;
3361
- for (const entry of this.invokeList) {
3362
- if (entry.key === key) {
3363
- existingEntry = entry;
3364
- break;
2914
+ return result;
2915
+ },
2916
+ invoke(name, args) {
2917
+ let result;
2918
+ const currentView = frame.view;
2919
+ if (currentView && currentView.rendered.value) {
2920
+ const fn = Reflect.get(currentView, name);
2921
+ if (typeof fn === "function") {
2922
+ result = funcWithTry(fn, args ?? [], currentView, noop);
3365
2923
  }
2924
+ } else {
2925
+ const key = SPLITTER + name;
2926
+ let existingEntry;
2927
+ for (const entry of frame.invokeList) {
2928
+ if (entry.key === key) {
2929
+ existingEntry = entry;
2930
+ break;
2931
+ }
2932
+ }
2933
+ if (existingEntry) {
2934
+ existingEntry.removed = args === existingEntry.args;
2935
+ }
2936
+ const newEntry = {
2937
+ name,
2938
+ args: args ?? [],
2939
+ key
2940
+ };
2941
+ frame.invokeList.push(newEntry);
3366
2942
  }
3367
- if (existingEntry) {
3368
- existingEntry.removed = args === existingEntry.args;
2943
+ return result;
2944
+ },
2945
+ children() {
2946
+ const result = [];
2947
+ for (const id2 in frame.childrenMap) {
2948
+ if (hasOwnProperty(frame.childrenMap, id2)) {
2949
+ result.push(id2);
2950
+ }
3369
2951
  }
3370
- const newEntry = {
3371
- name,
3372
- args: args || [],
3373
- key
3374
- };
3375
- this.invokeList.push(newEntry);
2952
+ return result;
2953
+ },
2954
+ on(event, handler) {
2955
+ emitter3.on(event, handler);
2956
+ return frame;
2957
+ },
2958
+ off(event, handler) {
2959
+ emitter3.off(event, handler);
2960
+ return frame;
2961
+ },
2962
+ fire(event, data) {
2963
+ emitter3.fire(event, data);
2964
+ return frame;
3376
2965
  }
3377
- return result;
3378
- }
3379
- /**
3380
- * Type-safe variant of `invoke`.
3381
- *
3382
- * `invoke()` accepts any string and any args, which silently hides
3383
- * mismatched call sites when a method gets renamed. `invokeTyped` carries
3384
- * the view's method signature through TypeScript so the compiler catches
3385
- * those mistakes:
3386
- *
3387
- * ```ts
3388
- * type Home = View & { loadData(id: string): Promise<void> };
3389
- * frame.invokeTyped<Home, "loadData">("loadData", ["user-1"]);
3390
- * ```
3391
- *
3392
- * Behavior is identical to `invoke` at runtime — same defer / direct-call
3393
- * paths — so it's a drop-in safer overload.
3394
- */
3395
- invokeTyped(name, args) {
3396
- return this.invoke(name, args);
2966
+ };
2967
+ frameRegistry.set(id, frame);
2968
+ const element = document.getElementById(id);
2969
+ if (element) {
2970
+ Reflect.set(element, "frame", frame);
2971
+ Reflect.set(element, "frameBound", 1);
3397
2972
  }
3398
- // ============================================================
3399
- // Static methods
3400
- // ============================================================
2973
+ staticEmitter.fire("add", { frame });
2974
+ return frame;
2975
+ }
2976
+ function doMountView(setup, params, node, sign) {
2977
+ const frameId = node.id;
2978
+ const frame = frameRegistry.get(frameId);
2979
+ if (!frame) return;
2980
+ if (sign !== frame.signature) return;
2981
+ const ctx = mountCtx(frame, setup, params);
2982
+ frame.view = ctx;
2983
+ runInvokes(frame);
2984
+ }
2985
+ var Frame = {
3401
2986
  /** Get frame by ID */
3402
- static get(id) {
3403
- return getFrame(id);
3404
- }
2987
+ get(id) {
2988
+ return frameRegistry.get(id);
2989
+ },
3405
2990
  /** Get all frames */
3406
- static getAll() {
3407
- return getAllFrames();
3408
- }
2991
+ getAll() {
2992
+ return frameRegistry;
2993
+ },
3409
2994
  /**
3410
- * Returns the existing root frame, or `undefined` if none has been created.
3411
- * Pure getter — never creates a Frame, never touches the DOM.
3412
- *
3413
- * Use `Frame.createRoot(id)` to create the root explicitly during framework
3414
- * boot. For Micro-Frontend hosts that own multiple independent containers,
3415
- * use `new Frame(containerId)` directly so each MF mount has its own root.
2995
+ * Returns the existing root frame, or undefined if none has been created.
3416
2996
  */
3417
- static getRoot() {
2997
+ getRoot() {
3418
2998
  return rootFrame;
3419
- }
2999
+ },
3420
3000
  /**
3421
- * Create (or return) the singleton root frame for this app.
3422
- *
3423
- * Idempotent: subsequent calls always return the original root regardless
3424
- * of `rootId` — so passing a different id later is silently ignored.
3425
- * `Framework.boot()` is the canonical caller; user code rarely needs this.
3001
+ * Create (or return) the singleton root frame.
3002
+ * Idempotent: subsequent calls always return the original root.
3426
3003
  */
3427
- static createRoot(rootId) {
3004
+ createRoot(rootId) {
3428
3005
  if (!rootFrame) {
3429
- rootId = rootId || "root";
3430
- let rootElement = document.getElementById(rootId);
3006
+ const id = rootId ?? "root";
3007
+ let rootElement = document.getElementById(id);
3431
3008
  if (!rootElement) {
3432
3009
  rootElement = document.body;
3433
- rootElement.id = rootId;
3010
+ rootElement.id = id;
3434
3011
  }
3435
- rootFrame = new _Frame(rootId);
3012
+ rootFrame = createFrame(id);
3436
3013
  }
3437
3014
  return rootFrame;
3438
- }
3015
+ },
3439
3016
  /** Bind event listener (static) */
3440
- static on(event, handler) {
3017
+ on(event, handler) {
3441
3018
  staticEmitter.on(event, handler);
3442
- return _Frame;
3443
- }
3019
+ return Frame;
3020
+ },
3444
3021
  /** Unbind event listener (static) */
3445
- static off(event, handler) {
3022
+ off(event, handler) {
3446
3023
  staticEmitter.off(event, handler);
3447
- return _Frame;
3448
- }
3024
+ return Frame;
3025
+ },
3449
3026
  /** Fire event (static) */
3450
- static fire(event, data) {
3027
+ fire(event, data) {
3451
3028
  staticEmitter.fire(event, data);
3452
3029
  }
3453
3030
  };
3454
3031
  function htmlElIsBound(element) {
3455
- return !!element.frameBound;
3032
+ return !!Reflect.get(element, "frameBound");
3456
3033
  }
3457
- function removeFrame2(id, wasCreated) {
3458
- const frameInstance = getFrame(id);
3034
+ function removeFrame(id, wasCreated) {
3035
+ const frameInstance = frameRegistry.get(id);
3459
3036
  if (!frameInstance) return;
3460
- removeFrame(id);
3461
- Frame.fire("remove", { frame: frameInstance, fcc: wasCreated });
3037
+ frameRegistry.delete(id);
3038
+ staticEmitter.fire("remove", { frame: frameInstance, fcc: wasCreated });
3462
3039
  const element = document.getElementById(id);
3463
3040
  if (element) {
3464
- element.frameBound = 0;
3041
+ Reflect.set(element, "frameBound", 0);
3465
3042
  Reflect.deleteProperty(element, "frame");
3466
3043
  }
3467
3044
  }
3468
3045
  function notifyCreated(frameInstance) {
3469
- if (!frameInstance["childrenCreated"] && !frameInstance["holdFireCreated"] && frameInstance["childrenCount"] === frameInstance["readyCount"]) {
3470
- if (!frameInstance["childrenCreated"]) {
3471
- frameInstance["childrenCreated"] = 1;
3472
- frameInstance["childrenAlter"] = 0;
3473
- frameInstance.fire("created");
3474
- }
3046
+ if (!frameInstance.childrenCreated && !frameInstance.holdFireCreated && frameInstance.childrenCount === frameInstance.readyCount) {
3047
+ frameInstance.childrenCreated = 1;
3048
+ frameInstance.childrenAlter = 0;
3049
+ frameInstance.emitter.fire("created");
3475
3050
  const pId = frameInstance.parentId;
3476
3051
  if (pId) {
3477
- const parent = getFrame(pId);
3052
+ const parent = frameRegistry.get(pId);
3478
3053
  if (parent && !parent.readyMap.has(frameInstance.id)) {
3479
3054
  parent.readyMap.add(frameInstance.id);
3480
3055
  parent.readyCount++;
@@ -3484,13 +3059,13 @@ function notifyCreated(frameInstance) {
3484
3059
  }
3485
3060
  }
3486
3061
  function notifyAlter(frameInstance, data) {
3487
- if (!frameInstance["childrenAlter"] && frameInstance["childrenCreated"]) {
3488
- frameInstance["childrenCreated"] = 0;
3489
- frameInstance["childrenAlter"] = 1;
3490
- frameInstance.fire("alter", data);
3062
+ if (!frameInstance.childrenAlter && frameInstance.childrenCreated) {
3063
+ frameInstance.childrenCreated = 0;
3064
+ frameInstance.childrenAlter = 1;
3065
+ frameInstance.emitter.fire("alter", data);
3491
3066
  const pId = frameInstance.parentId;
3492
3067
  if (pId) {
3493
- const parent = getFrame(pId);
3068
+ const parent = frameRegistry.get(pId);
3494
3069
  if (parent && parent.readyMap.has(frameInstance.id)) {
3495
3070
  parent.readyCount--;
3496
3071
  parent.readyMap.delete(frameInstance.id);
@@ -3499,483 +3074,310 @@ function notifyAlter(frameInstance, data) {
3499
3074
  }
3500
3075
  }
3501
3076
  }
3502
- function reInitFrame(frame, id, parentId) {
3503
- Reflect.set(frame, "id", id);
3504
- frame["_parentId"] = parentId;
3505
- frame["childrenMap"] = {};
3506
- frame["childrenCount"] = 0;
3507
- frame["readyCount"] = 0;
3508
- frame["signature"] = 1;
3509
- frame["readyMap"] = /* @__PURE__ */ new Set();
3510
- frame["invokeList"] = [];
3511
- registerFrame(id, frame);
3512
- }
3513
- function reInitFrameForCache(frame) {
3514
- Reflect.set(frame, "id", "");
3515
- frame["_parentId"] = void 0;
3516
- frame["childrenMap"] = {};
3517
- frame["readyMap"] = /* @__PURE__ */ new Set();
3518
- }
3519
3077
  function translateQuery(pId, src, params) {
3520
- const parentFrame = getFrame(pId);
3078
+ const parentFrame = frameRegistry.get(pId);
3521
3079
  const parentView = parentFrame?.view;
3522
3080
  if (!parentView) return;
3523
3081
  const parentRefData = parentView.updater.refData;
3524
3082
  if (!parentRefData) return;
3525
3083
  if (src.indexOf(SPLITTER) > 0) {
3526
3084
  translateData(parentRefData, params);
3527
- const paramsRec = params;
3528
- const splitterValue = paramsRec[SPLITTER];
3529
- if (splitterValue && typeof splitterValue === "object") {
3530
- assign(params, splitterValue);
3085
+ const splitterValue = Reflect.get(params, SPLITTER);
3086
+ if (isRecord(splitterValue)) {
3087
+ for (const k in splitterValue) {
3088
+ if (hasOwnProperty(splitterValue, k)) {
3089
+ const v = splitterValue[k];
3090
+ if (typeof v === "string") {
3091
+ params[k] = v;
3092
+ }
3093
+ }
3094
+ }
3531
3095
  Reflect.deleteProperty(params, SPLITTER);
3532
3096
  }
3533
3097
  }
3534
3098
  }
3535
3099
 
3536
3100
  // src/cross-site.ts
3537
- var preparePromises = {};
3538
- var projectsMap = null;
3539
- function loadRemoteView(viewPath, bizCode) {
3540
- const crossConfigs = config.crossConfigs || window.crossConfigs;
3541
- const currentName = config.projectName || "";
3542
- const slashIndex = viewPath.indexOf("/");
3543
- const projectName = slashIndex > -1 ? viewPath.substring(0, slashIndex) : viewPath;
3544
- if (projectName === currentName) return Promise.resolve();
3545
- if (!preparePromises[projectName]) {
3546
- if (!projectsMap) {
3547
- const map = toMap(crossConfigs || [], "projectName");
3548
- projectsMap = map;
3549
- }
3550
- const info = projectsMap[projectName];
3551
- if (!info) {
3552
- return Promise.reject(
3553
- new Error(`Cannot find ${projectName} from crossConfigs`)
3554
- );
3555
- }
3556
- preparePromises[projectName] = use(`${projectName}/prepare`).then((modules) => {
3557
- let mod = modules[0];
3558
- if (mod && typeof mod === "object" && mod !== null) {
3559
- const rec = mod;
3560
- if (rec["__esModule"]) {
3561
- mod = rec["default"];
3562
- }
3563
- }
3564
- if (typeof mod === "function") {
3565
- return mod({ bizCode });
3566
- }
3567
- return void 0;
3568
- }).catch((err) => {
3569
- Reflect.deleteProperty(preparePromises, projectName);
3570
- throw err;
3571
- });
3572
- }
3573
- return preparePromises[projectName];
3574
- }
3101
+ var projectsMap = /* @__PURE__ */ new Map();
3575
3102
  function resetProjectsMap() {
3576
- projectsMap = null;
3103
+ projectsMap.clear();
3577
3104
  }
3578
- var skeletonTemplate = (data, viewId) => {
3579
- let skeletonHtml = "<div>Loading...</div>";
3580
- if (data && typeof data === "object") {
3581
- const candidate = data.skeleton;
3582
- if (typeof candidate === "string") skeletonHtml = candidate;
3105
+ function getProject(projectName) {
3106
+ return projectsMap.get(projectName);
3107
+ }
3108
+ if (typeof config !== "undefined" && config.crossSites) {
3109
+ for (const cs of config.crossSites) {
3110
+ projectsMap.set(cs.projectName, cs);
3583
3111
  }
3584
- return `<div id="mf_${viewId}">${skeletonHtml}</div>`;
3585
- };
3586
- var CrossSite = View.extend({
3587
- /** Skeleton template renders loading state + child container */
3588
- template: skeletonTemplate,
3589
- init(params) {
3590
- this.$sign = 0;
3591
- this.on("destroy", () => {
3592
- this.$sign = -1;
3593
- });
3594
- this.assign?.(params);
3595
- },
3596
- assign(data) {
3597
- this.$view = typeof data["view"] === "string" ? data["view"] : "";
3598
- const nested = data["params"];
3599
- const nestedParams = nested && typeof nested === "object" ? nested : {};
3600
- this.$params = { ...data, ...nestedParams };
3601
- this.updater.set({
3602
- skeleton: data["skeleton"],
3603
- skeletonParams: data["skeletonParams"] || {},
3604
- bizCode: data["bizCode"]
3605
- });
3606
- if (this.$sign > 0) {
3607
- this.updateView();
3608
- }
3609
- return false;
3610
- },
3611
- async updateView() {
3612
- const sign = ++this.$sign;
3613
- const stored = this.updater.get();
3614
- const bizCode = stored.bizCode;
3615
- try {
3616
- await loadRemoteView(this.$view, bizCode);
3617
- } catch (ex) {
3618
- const node = document.getElementById("mf_" + this.id);
3619
- if (node) {
3620
- const err = ex instanceof Error ? ex : new Error(String(ex));
3621
- node.innerHTML = err.message || String(err);
3622
- }
3623
- }
3624
- if (this.$sign !== sign) return;
3625
- const mf = Frame.get("mf_" + this.id);
3626
- const parsedNew = parseUri(this.$view);
3627
- const newPath = parsedNew.path;
3628
- const oldPath = mf?.viewPath ? parseUri(mf.viewPath).path : "";
3629
- const view = mf?.view;
3630
- if (newPath === oldPath && view && typeof view.assign === "function") {
3631
- const result = funcWithTry(view.assign, [this.$params], view, noop);
3632
- if (result) {
3633
- view.render();
3634
- }
3635
- return;
3636
- }
3637
- const owner = this.owner;
3638
- if (owner && typeof owner !== "number") {
3639
- owner.mountFrame("mf_" + this.id, this.$view, this.$params);
3640
- }
3641
- },
3642
- render() {
3643
- const params = this.$params;
3644
- this.updater.digest({
3645
- skeleton: params?.["skeleton"]
3112
+ }
3113
+ var CrossSite = (ctx, params) => {
3114
+ const p = params ?? {};
3115
+ const viewPath = p["view"] ?? "";
3116
+ const bizCode = p["bizCode"] ?? "";
3117
+ const skeleton = p["skeleton"] ?? "Loading\u2026";
3118
+ const state = {
3119
+ sign: 0,
3120
+ mounted: false,
3121
+ lastViewPath: void 0
3122
+ };
3123
+ const parsed = parseUri(viewPath);
3124
+ const parts = parsed.path.split("/");
3125
+ const projectName = parts[0] ?? "";
3126
+ const remotePath = parts.slice(1).join("/");
3127
+ const projectConfig = getProject(projectName);
3128
+ const currentSign = ++state.sign;
3129
+ if (projectConfig && remotePath) {
3130
+ use(`${projectName}/prepare`, () => {
3131
+ if (currentSign !== state.sign) return;
3132
+ use(viewPath, () => {
3133
+ if (currentSign !== state.sign) return;
3134
+ const frameId = `mf_${ctx.id}`;
3135
+ ctx.owner.mountFrame(frameId, viewPath, {
3136
+ bizCode,
3137
+ project: projectName
3138
+ });
3139
+ state.mounted = true;
3140
+ state.lastViewPath = viewPath;
3141
+ ctx.updater.set({ _csLoaded: true }).digest();
3142
+ });
3646
3143
  });
3647
- this.updateView();
3648
- },
3649
- /**
3650
- * Invoke a method on the remote view.
3651
- * Usage: mf.invoke('callView', methodName, ...args)
3652
- */
3653
- callView(name, ...args) {
3654
- const mf = Frame.get("mf_" + this.id);
3655
- return mf?.invoke(name, args);
3656
- }
3657
- });
3658
- var cross_site_default = CrossSite;
3659
-
3660
- // src/service.ts
3661
- var Payload = class {
3662
- /** Payload data */
3663
- data;
3664
- /** Internal cache info */
3665
- cacheInfo;
3666
- constructor(data = {}) {
3667
- this.data = data;
3668
- }
3669
- /** Get a value from payload data */
3670
- get(key) {
3671
- return this.data[key];
3672
- }
3673
- /** Set a value in payload data */
3674
- set(keyOrData, value) {
3675
- if (typeof keyOrData === "string") {
3676
- this.data[keyOrData] = value;
3677
- } else {
3678
- assign(this.data, keyOrData);
3679
- }
3680
- return this;
3681
3144
  }
3682
- };
3683
- var FETCH_FLAGS_ALL = 1;
3684
- var FETCH_FLAGS_ONE = 2;
3685
- var Service = class {
3686
- /** Service instance ID */
3687
- id = "";
3688
- /** Whether service is busy (1 = busy) */
3689
- busy = 0;
3690
- /** Whether service is destroyed (1 = destroyed) */
3691
- destroyed = 0;
3692
- /** Task queue for sequential operations */
3693
- taskQueue = [];
3694
- /** Previous dequeue arguments */
3695
- prevArgs = [];
3696
- /** Instance event emitter */
3697
- _emitter = new EventEmitter();
3698
- constructor() {
3699
- this.id = generateId("service");
3700
- }
3701
- // ============================================================
3702
- // Instance accessors for type-level data
3703
- // ============================================================
3704
- /** Instance event emitter (public accessor) */
3705
- get emitter() {
3706
- return this._emitter;
3707
- }
3708
- /**
3709
- * Get internals object for serviceSend compatibility.
3710
- * References per-type static state from the current class.
3711
- */
3712
- get internals() {
3713
- const constructor = this.constructor;
3714
- return {
3715
- metaList: constructor._metaList,
3716
- payloadCache: constructor._payloadCache,
3717
- pendingCacheKeys: constructor._pendingCacheKeys,
3718
- syncFn: constructor._syncFn,
3719
- staticEmitter: constructor._staticEmitter
3720
- };
3721
- }
3722
- /**
3723
- * Get type reference (the constructor) for serviceSend compatibility.
3724
- * Static methods like get/create are accessible via the constructor.
3725
- */
3726
- get type() {
3727
- return this.constructor;
3728
- }
3729
- // ============================================================
3730
- // Instance methods
3731
- // ============================================================
3732
- /**
3733
- * Fetch all endpoints, callback when all complete.
3734
- * Uses cache when available.
3735
- */
3736
- all(attrs, done) {
3737
- serviceSend(this, attrs, done, FETCH_FLAGS_ALL, false);
3738
- return this;
3739
- }
3740
- /**
3741
- * Fetch all endpoints, callback on each completion.
3742
- */
3743
- one(attrs, done) {
3744
- serviceSend(this, attrs, done, FETCH_FLAGS_ONE, false);
3745
- return this;
3746
- }
3747
- /**
3748
- * Fetch all endpoints, skip cache (always request).
3749
- */
3750
- save(attrs, done) {
3751
- serviceSend(this, attrs, done, FETCH_FLAGS_ALL, true);
3752
- return this;
3753
- }
3754
- /**
3755
- * Enqueue a task for sequential execution.
3756
- */
3757
- enqueue(callback) {
3758
- if (!this.destroyed) {
3759
- this.taskQueue.push(callback);
3760
- this.dequeue(...this.prevArgs);
3761
- }
3762
- return this;
3763
- }
3764
- /**
3765
- * Dequeue and execute the next task in queue.
3766
- */
3767
- dequeue(...args) {
3768
- if (!this.busy && !this.destroyed) {
3769
- this.busy = 1;
3770
- setTimeout(() => {
3771
- this.busy = 0;
3772
- if (!this.destroyed) {
3773
- const task2 = this.taskQueue.shift();
3774
- if (task2) {
3775
- this.prevArgs = args;
3776
- funcWithTry(task2, args, this, noop);
3777
- }
3778
- }
3779
- }, 0);
3145
+ ctx.on("destroy", () => {
3146
+ state.sign++;
3147
+ state.mounted = false;
3148
+ state.lastViewPath = void 0;
3149
+ });
3150
+ const renderSkeleton = () => {
3151
+ if (state.mounted) {
3152
+ return '<div class="lark-cs-container" style="display:none"></div>';
3780
3153
  }
3154
+ return `<div class="lark-cs-skeleton" data-lark-cs-skeleton="1">${skeleton}</div>`;
3155
+ };
3156
+ return {
3157
+ template: renderSkeleton,
3158
+ events: {}
3159
+ };
3160
+ };
3161
+ var cross_site_default = CrossSite;
3162
+
3163
+ // src/service.ts
3164
+ function isPayload(v) {
3165
+ if (v == null || typeof v !== "object") return false;
3166
+ return "data" in v && "get" in v;
3167
+ }
3168
+ function isServiceMetaEntry(v) {
3169
+ if (v == null || typeof v !== "object") return false;
3170
+ return "name" in v && "url" in v;
3171
+ }
3172
+ function createPayload(data = {}) {
3173
+ const payloadData = data;
3174
+ function get(key) {
3175
+ return payloadData[key];
3781
3176
  }
3782
- /**
3783
- * Destroy the service instance.
3784
- * After destruction, no new requests can be sent.
3785
- */
3786
- destroy() {
3787
- this.destroyed = 1;
3788
- this.taskQueue = [];
3789
- }
3790
- // Instance event methods (delegate to instance emitter)
3791
- on(event, handler) {
3792
- this._emitter.on(event, handler);
3793
- return this;
3794
- }
3795
- off(event, handler) {
3796
- this._emitter.off(event, handler);
3797
- return this;
3798
- }
3799
- fire(event, data) {
3800
- this._emitter.fire(event, data);
3801
- return this;
3177
+ function set(keyOrData, value) {
3178
+ if (typeof keyOrData === "string") {
3179
+ payloadData[keyOrData] = value;
3180
+ } else {
3181
+ assign(payloadData, keyOrData);
3182
+ }
3183
+ return api;
3802
3184
  }
3803
- // ============================================================
3804
- // Per-type static state
3805
- // ============================================================
3806
- /** Per-type metadata registry */
3807
- static _metaList = {};
3808
- /** Per-type payload cache (LFU with frequency eviction) */
3809
- static _payloadCache = new Cache({
3810
- maxSize: 20,
3811
- bufferSize: 5
3185
+ const api = { data: payloadData, get, set };
3186
+ return api;
3187
+ }
3188
+ var FETCH_FLAGS_ALL = 1;
3189
+ var FETCH_FLAGS_ONE = 2;
3190
+ function createService(syncFn, cacheMax = 20, cacheBuffer = 5) {
3191
+ const metaList = {};
3192
+ const payloadCache = createCache({
3193
+ maxSize: cacheMax,
3194
+ bufferSize: cacheBuffer
3812
3195
  });
3813
- /** Per-type pending cache keys for deduplication */
3814
- static _pendingCacheKeys = {};
3815
- /** Per-type sync function */
3816
- static _syncFn = noop;
3817
- /** Per-type static event emitter */
3818
- static _staticEmitter = new EventEmitter();
3819
- /** Per-type cache max size */
3820
- static _cacheMax = 20;
3821
- /** Per-type cache buffer size */
3822
- static _cacheBuffer = 5;
3823
- // ============================================================
3824
- // Static methods (operate on per-type state via `this`)
3825
- // ============================================================
3826
- /**
3827
- * Register API endpoint metadata.
3828
- */
3829
- static add(attrs) {
3196
+ const pendingCacheKeys = {};
3197
+ const staticEmitter2 = createEmitter();
3198
+ const internals = {
3199
+ metaList,
3200
+ payloadCache,
3201
+ pendingCacheKeys,
3202
+ syncFn,
3203
+ staticEmitter: staticEmitter2
3204
+ };
3205
+ function add(attrs) {
3830
3206
  if (!Array.isArray(attrs)) {
3831
3207
  attrs = [attrs];
3832
3208
  }
3833
- for (const payload of attrs) {
3834
- if (payload) {
3835
- const name = payload.name;
3836
- const cache = payload.cache;
3837
- payload.cache = cache ? cache | 0 : 0;
3838
- this._metaList[name] = payload;
3209
+ for (const entry of attrs) {
3210
+ if (entry) {
3211
+ const name = entry.name;
3212
+ const cache = entry.cache;
3213
+ entry.cache = cache ? cache | 0 : 0;
3214
+ metaList[name] = entry;
3839
3215
  }
3840
3216
  }
3841
3217
  }
3842
- /**
3843
- * Get metadata for an API endpoint.
3844
- */
3845
- static meta(attrs) {
3218
+ function meta(attrs) {
3846
3219
  const name = typeof attrs === "string" ? attrs : String(attrs["name"] ?? "");
3847
- const known = this._metaList[name];
3220
+ const known = metaList[name];
3848
3221
  if (known) return known;
3849
- return attrs;
3222
+ if (isServiceMetaEntry(attrs)) {
3223
+ return attrs;
3224
+ }
3225
+ return { name, url: "" };
3850
3226
  }
3851
- /**
3852
- * Create a Payload for an API request.
3853
- */
3854
- static create(attrs) {
3855
- const meta = this.meta(attrs);
3856
- const cache = toCacheValue(attrs["cache"]) || meta.cache || 0;
3857
- const entity = new Payload();
3858
- entity.set(meta);
3227
+ function create(attrs) {
3228
+ const m = meta(attrs);
3229
+ const cache = toCacheValue(attrs["cache"]) || m.cache || 0;
3230
+ const entity = createPayload();
3231
+ entity.set(m);
3859
3232
  entity.cacheInfo = {
3860
- name: meta.name,
3861
- after: typeof meta.after === "function" ? meta.after : void 0,
3862
- cleans: typeof meta.cleanKeys === "string" ? meta.cleanKeys : void 0,
3863
- key: cache ? defaultCacheKey(meta, attrs) : "",
3233
+ name: m.name,
3234
+ after: typeof m.after === "function" ? m.after : void 0,
3235
+ cleans: typeof m.cleanKeys === "string" ? m.cleanKeys : void 0,
3236
+ key: cache ? defaultCacheKey(m, attrs) : "",
3864
3237
  time: 0
3865
3238
  };
3866
3239
  if (attrs !== null) {
3867
3240
  entity.set(attrs);
3868
3241
  }
3869
- const before = meta.before;
3242
+ const before = m.before;
3870
3243
  if (typeof before === "function") {
3871
3244
  funcWithTry(before, [entity], entity, noop);
3872
3245
  }
3873
- this._staticEmitter.fire("begin", { payload: entity });
3246
+ staticEmitter2.fire("begin", { payload: entity });
3874
3247
  return entity;
3875
3248
  }
3876
- /**
3877
- * Get or create a Payload for an API request.
3878
- */
3879
- static get(attrs, createNew) {
3249
+ function get(attrs, createNew) {
3880
3250
  let entity;
3881
3251
  let needsUpdate = false;
3882
3252
  if (!createNew) {
3883
- entity = this.cached(attrs);
3253
+ entity = cached(attrs);
3884
3254
  }
3885
3255
  if (!entity) {
3886
- entity = this.create(attrs);
3256
+ entity = create(attrs);
3887
3257
  needsUpdate = true;
3888
3258
  }
3889
3259
  return { entity, needsUpdate };
3890
3260
  }
3891
- /**
3892
- * Get cached Payload if available and not expired.
3893
- */
3894
- static cached(attrs) {
3895
- const meta = this.meta(attrs);
3896
- const cache = toCacheValue(attrs["cache"]) || meta.cache || 0;
3261
+ function cached(attrs) {
3262
+ const m = meta(attrs);
3263
+ const cache = toCacheValue(attrs["cache"]) || m.cache || 0;
3897
3264
  let cacheKey = "";
3898
3265
  if (cache) {
3899
- cacheKey = defaultCacheKey(meta, attrs);
3266
+ cacheKey = defaultCacheKey(m, attrs);
3900
3267
  }
3901
3268
  if (cacheKey) {
3902
- const info = this._pendingCacheKeys[cacheKey];
3269
+ const info = pendingCacheKeys[cacheKey];
3903
3270
  if (info) {
3904
3271
  const entity = info.entity;
3905
- return entity instanceof Payload ? entity : void 0;
3272
+ return isPayload(entity) ? entity : void 0;
3906
3273
  }
3907
- const cached = this._payloadCache.get(cacheKey);
3908
- if (cached && cached.cacheInfo) {
3909
- if (Date.now() - cached.cacheInfo.time > cache) {
3910
- this._payloadCache.del(cacheKey);
3274
+ const cachedPayload = payloadCache.get(cacheKey);
3275
+ if (cachedPayload && cachedPayload.cacheInfo) {
3276
+ if (Date.now() - cachedPayload.cacheInfo.time > cache) {
3277
+ payloadCache.del(cacheKey);
3911
3278
  return void 0;
3912
3279
  }
3913
- return cached;
3280
+ return cachedPayload;
3914
3281
  }
3915
3282
  }
3916
3283
  return void 0;
3917
3284
  }
3918
- /**
3919
- * Clear cached payloads by endpoint name.
3920
- */
3921
- static clear(names) {
3922
- const nameSet = new Set(
3923
- (typeof names === "string" ? names : names.join(",")).split(",")
3924
- );
3285
+ function clear(names) {
3286
+ const nameSet = new Set((typeof names === "string" ? names : names.join(",")).split(","));
3925
3287
  const keysToDelete = [];
3926
- this._payloadCache.forEach((payload) => {
3288
+ payloadCache.forEach((payload) => {
3927
3289
  const info = payload?.cacheInfo;
3928
3290
  if (info && info.key && nameSet.has(info.name)) {
3929
3291
  keysToDelete.push(info.key);
3930
3292
  }
3931
3293
  });
3932
3294
  for (const key of keysToDelete) {
3933
- this._payloadCache.del(key);
3295
+ payloadCache.del(key);
3934
3296
  }
3935
3297
  }
3936
- // Static event methods (operate on per-type emitter)
3937
- static on(event, handler) {
3938
- this._staticEmitter.on(event, handler);
3298
+ function on(event, handler) {
3299
+ staticEmitter2.on(event, handler);
3939
3300
  }
3940
- static off(event, handler) {
3941
- this._staticEmitter.off(event, handler);
3301
+ function off(event, handler) {
3302
+ staticEmitter2.off(event, handler);
3942
3303
  }
3943
- static fire(event, data) {
3944
- this._staticEmitter.fire(event, data);
3304
+ function fire(event, data) {
3305
+ staticEmitter2.fire(event, data);
3945
3306
  }
3946
- /**
3947
- * Create a new Service subclass with a custom sync function.
3948
- *
3949
- * Each subclass gets its OWN copies of every per-type static field
3950
- * (`_metaList`, `_payloadCache`, `_pendingCacheKeys`, `_syncFn`,
3951
- * `_staticEmitter`, `_cacheMax`, `_cacheBuffer`) via `static override`.
3952
- * This is intentional: it ensures that endpoint metadata, cache state,
3953
- * in-flight dedup keys, and event subscribers are fully isolated between
3954
- * different Service types, even when one extends another.
3955
- *
3956
- * **Do not refactor these `static override` declarations away** — sharing
3957
- * them through prototype inheritance would let endpoints registered on one
3958
- * subclass leak into another, and the LFU cache evictions of one type
3959
- * would race with those of another.
3960
- */
3961
- static extend(newSyncFn, newCacheMax, newCacheBuffer) {
3962
- const ParentService = this;
3963
- class ChildService extends ParentService {
3964
- // Intentionally per-subclass — see Service.extend doc.
3965
- static _metaList = {};
3966
- static _payloadCache = new Cache({
3967
- maxSize: newCacheMax || ParentService._cacheMax,
3968
- bufferSize: newCacheBuffer || ParentService._cacheBuffer
3969
- });
3970
- static _pendingCacheKeys = {};
3971
- static _syncFn = newSyncFn;
3972
- static _staticEmitter = new EventEmitter();
3973
- static _cacheMax = newCacheMax || ParentService._cacheMax;
3974
- static _cacheBuffer = newCacheBuffer || ParentService._cacheBuffer;
3307
+ function instance() {
3308
+ const id = generateId("service");
3309
+ const instEmitter = createEmitter();
3310
+ const taskQueue = [];
3311
+ let prevArgs = [];
3312
+ function all(attrs, done) {
3313
+ serviceSend(inst, attrs, done, FETCH_FLAGS_ALL, false, internals);
3314
+ return inst;
3315
+ }
3316
+ function one(attrs, done) {
3317
+ serviceSend(inst, attrs, done, FETCH_FLAGS_ONE, false, internals);
3318
+ return inst;
3319
+ }
3320
+ function save(attrs, done) {
3321
+ serviceSend(inst, attrs, done, FETCH_FLAGS_ALL, true, internals);
3322
+ return inst;
3323
+ }
3324
+ function enqueue(callback) {
3325
+ if (!inst.destroyed) {
3326
+ taskQueue.push(callback);
3327
+ dequeue(...prevArgs);
3328
+ }
3329
+ return inst;
3330
+ }
3331
+ function dequeue(...args) {
3332
+ if (!inst.busy && !inst.destroyed) {
3333
+ inst.busy = 1;
3334
+ setTimeout(() => {
3335
+ inst.busy = 0;
3336
+ if (!inst.destroyed) {
3337
+ const task2 = taskQueue.shift();
3338
+ if (task2) {
3339
+ prevArgs = args;
3340
+ funcWithTry(task2, args, inst, noop);
3341
+ }
3342
+ }
3343
+ }, 0);
3344
+ }
3975
3345
  }
3976
- return ChildService;
3346
+ function destroy() {
3347
+ inst.destroyed = 1;
3348
+ taskQueue.length = 0;
3349
+ }
3350
+ function onInst(event, handler) {
3351
+ instEmitter.on(event, handler);
3352
+ return inst;
3353
+ }
3354
+ function offInst(event, handler) {
3355
+ instEmitter.off(event, handler);
3356
+ return inst;
3357
+ }
3358
+ function fireInst(event, data) {
3359
+ instEmitter.fire(event, data);
3360
+ return inst;
3361
+ }
3362
+ const inst = {
3363
+ id,
3364
+ busy: 0,
3365
+ destroyed: 0,
3366
+ emitter: instEmitter,
3367
+ all,
3368
+ one,
3369
+ save,
3370
+ enqueue,
3371
+ dequeue,
3372
+ destroy,
3373
+ on: onInst,
3374
+ off: offInst,
3375
+ fire: fireInst
3376
+ };
3377
+ return inst;
3977
3378
  }
3978
- };
3379
+ return { add, meta, create, get, cached, clear, on, off, fire, instance };
3380
+ }
3979
3381
  var metaJsonCache = /* @__PURE__ */ new WeakMap();
3980
3382
  function getMetaJson(meta) {
3981
3383
  let cached = metaJsonCache.get(meta);
@@ -3996,10 +3398,10 @@ function toCacheValue(v) {
3996
3398
  }
3997
3399
  return 0;
3998
3400
  }
3999
- function serviceSend(service, attrs, done, flag, save) {
3401
+ function serviceSend(service, attrs, done, flag, save, internals) {
4000
3402
  if (service.destroyed) return;
4001
3403
  if (service.busy) {
4002
- const queued = () => serviceSend(service, attrs, done, flag, save);
3404
+ const queued = () => serviceSend(service, attrs, done, flag, save, internals);
4003
3405
  service.enqueue(queued);
4004
3406
  return;
4005
3407
  }
@@ -4012,8 +3414,7 @@ function serviceSend(service, attrs, done, flag, save) {
4012
3414
  } else {
4013
3415
  attrList = [attrs];
4014
3416
  }
4015
- const internals = service.internals;
4016
- const { syncFn, pendingCacheKeys, staticEmitter: staticEmitter2 } = internals;
3417
+ const { syncFn, pendingCacheKeys, staticEmitter: staticEmitter2, payloadCache } = internals;
4017
3418
  let requestCount = 0;
4018
3419
  const total = attrList.length;
4019
3420
  const doneArr = new Array(total + 1);
@@ -4038,7 +3439,7 @@ function serviceSend(service, attrs, done, flag, save) {
4038
3439
  }
4039
3440
  }
4040
3441
  if (flag === FETCH_FLAGS_ONE) {
4041
- funcWithTry(done, [error || null, payload, finish, idx], service, noop);
3442
+ funcWithTry(done, [error ?? null, payload, finish, idx], service, noop);
4042
3443
  }
4043
3444
  }
4044
3445
  if (newPayload) {
@@ -4048,9 +3449,9 @@ function serviceSend(service, attrs, done, flag, save) {
4048
3449
  for (const attr of attrList) {
4049
3450
  if (!attr) continue;
4050
3451
  const attrObj = typeof attr === "string" ? { name: attr } : attr;
4051
- const payloadInfo = service.type.get(attrObj, save);
3452
+ const payloadInfo = internals ? getPayload(internals, attrObj, save) : { entity: createPayload(), needsUpdate: true };
4052
3453
  const payloadEntity = payloadInfo.entity;
4053
- const cacheKey = payloadEntity.cacheInfo?.key || "";
3454
+ const cacheKey = payloadEntity.cacheInfo?.key ?? "";
4054
3455
  doneArr[requestCount + 1] = payloadEntity;
4055
3456
  const complete = remoteComplete.bind(null, requestCount++);
4056
3457
  if (cacheKey && pendingCacheKeys[cacheKey]) {
@@ -4063,9 +3464,9 @@ function serviceSend(service, attrs, done, flag, save) {
4063
3464
  const cacheComplete = () => {
4064
3465
  const list = pendingCacheKeys[cacheKey];
4065
3466
  const entity = list.entity;
4066
- if (entity instanceof Payload && entity.cacheInfo) {
3467
+ if (isPayload(entity) && entity.cacheInfo) {
4067
3468
  entity.cacheInfo.time = Date.now();
4068
- internals.payloadCache.set(cacheKey, entity);
3469
+ payloadCache.set(cacheKey, entity);
4069
3470
  }
4070
3471
  Reflect.deleteProperty(pendingCacheKeys, cacheKey);
4071
3472
  for (const cb of list) {
@@ -4081,6 +3482,55 @@ function serviceSend(service, attrs, done, flag, save) {
4081
3482
  }
4082
3483
  }
4083
3484
  }
3485
+ function getPayload(internals, attrs, createNew) {
3486
+ const metaList = internals.metaList;
3487
+ const name = String(attrs["name"] ?? "");
3488
+ const known = metaList[name];
3489
+ const m = known ?? (isServiceMetaEntry(attrs) ? attrs : { name, url: "" });
3490
+ const cache = toCacheValue(attrs["cache"]) || m.cache || 0;
3491
+ let cacheKey = "";
3492
+ if (cache) {
3493
+ cacheKey = defaultCacheKey(m, attrs);
3494
+ }
3495
+ let entity;
3496
+ let needsUpdate = false;
3497
+ if (!createNew && cacheKey) {
3498
+ const info = internals.pendingCacheKeys[cacheKey];
3499
+ if (info) {
3500
+ const e = info.entity;
3501
+ if (isPayload(e)) entity = e;
3502
+ }
3503
+ if (!entity) {
3504
+ const cachedPayload = internals.payloadCache.get(cacheKey);
3505
+ if (cachedPayload && cachedPayload.cacheInfo) {
3506
+ if (Date.now() - cachedPayload.cacheInfo.time > cache) {
3507
+ internals.payloadCache.del(cacheKey);
3508
+ } else {
3509
+ entity = cachedPayload;
3510
+ }
3511
+ }
3512
+ }
3513
+ }
3514
+ if (!entity) {
3515
+ entity = createPayload();
3516
+ entity.set(m);
3517
+ entity.cacheInfo = {
3518
+ name: m.name,
3519
+ after: typeof m.after === "function" ? m.after : void 0,
3520
+ cleans: typeof m.cleanKeys === "string" ? m.cleanKeys : void 0,
3521
+ key: cacheKey,
3522
+ time: 0
3523
+ };
3524
+ if (attrs) entity.set(attrs);
3525
+ const before = m.before;
3526
+ if (typeof before === "function") {
3527
+ funcWithTry(before, [entity], entity, noop);
3528
+ }
3529
+ internals.staticEmitter.fire("begin", { payload: entity });
3530
+ needsUpdate = true;
3531
+ }
3532
+ return { entity, needsUpdate };
3533
+ }
4084
3534
 
4085
3535
  // src/devtool.ts
4086
3536
  var FrameDevtoolBridge = {
@@ -4091,10 +3541,10 @@ var FrameDevtoolBridge = {
4091
3541
  MSG_TREE_DELTA: "LARK_DEVTOOL_TREE_DELTA"
4092
3542
  };
4093
3543
  function serializeView(view) {
4094
- const evtMap = view.eventObjectMap;
3544
+ const evtMap = {};
4095
3545
  const eventMethodKeys = evtMap ? Object.keys(evtMap) : [];
4096
3546
  const resourceKeys = view.resources ? Object.keys(view.resources) : [];
4097
- const hasAssign = typeof view["assign"] === "function";
3547
+ const hasAssign = typeof view.getAssign() === "function";
4098
3548
  let updaterData = null;
4099
3549
  try {
4100
3550
  const ref = view.updater?.refData;
@@ -4110,14 +3560,14 @@ function serializeView(view) {
4110
3560
  return {
4111
3561
  id: view.id,
4112
3562
  rendered: !!view.rendered,
4113
- signature: view.signature,
4114
- observedStateKeys: view.observedStateKeys ?? null,
3563
+ signature: view.signature.value,
3564
+ observedStateKeys: view.getObservedStateKeys() ?? null,
4115
3565
  locationObserved: {
4116
3566
  flag: view.locationObserved.flag,
4117
3567
  keys: view.locationObserved.keys,
4118
3568
  observePath: view.locationObserved.observePath
4119
3569
  },
4120
- hasTemplate: !!view.template,
3570
+ hasTemplate: !!view.getTemplate(),
4121
3571
  eventMethodKeys,
4122
3572
  resourceKeys,
4123
3573
  hasAssign,
@@ -4138,7 +3588,7 @@ function serializeFrame(frameId) {
4138
3588
  return {
4139
3589
  id: frame.id,
4140
3590
  parentId: frame.parentId ?? null,
4141
- viewPath: frame.viewPath ?? null,
3591
+ viewPath: frame.getViewPath() ?? null,
4142
3592
  childrenCount: frame.childrenCount,
4143
3593
  readyCount: frame.readyCount,
4144
3594
  childrenCreated: frame.childrenCreated,
@@ -4183,10 +3633,7 @@ function installFrameDevtoolBridge() {
4183
3633
  if (type === FrameDevtoolBridge.MSG_PING) {
4184
3634
  const source = event.source;
4185
3635
  if (source) {
4186
- source.postMessage(
4187
- { type: FrameDevtoolBridge.MSG_PONG },
4188
- { targetOrigin: "*" }
4189
- );
3636
+ source.postMessage({ type: FrameDevtoolBridge.MSG_PONG }, { targetOrigin: "*" });
4190
3637
  }
4191
3638
  return;
4192
3639
  }
@@ -4214,15 +3661,15 @@ function pushTreeUpdate() {
4214
3661
  const treeJson = JSON.stringify(tree);
4215
3662
  if (treeJson !== lastTreeJson) {
4216
3663
  lastTreeJson = treeJson;
4217
- window.parent.postMessage(
4218
- { type: FrameDevtoolBridge.MSG_TREE_DELTA, data: tree },
4219
- "*"
4220
- );
3664
+ window.parent.postMessage({ type: FrameDevtoolBridge.MSG_TREE_DELTA, data: tree }, "*");
4221
3665
  }
4222
3666
  }
4223
3667
 
4224
3668
  // src/framework.ts
4225
3669
  var booted3 = false;
3670
+ function isAnyFunc(v) {
3671
+ return typeof v === "function";
3672
+ }
4226
3673
  var taskList = [];
4227
3674
  var taskIndex = 0;
4228
3675
  var taskScheduled = false;
@@ -4231,7 +3678,7 @@ function executeTaskChunk(deadline) {
4231
3678
  const startTime = Date.now();
4232
3679
  while (true) {
4233
3680
  const fn = taskList[taskIndex];
4234
- if (!fn) {
3681
+ if (!isAnyFunc(fn)) {
4235
3682
  taskList.length = 0;
4236
3683
  taskIndex = 0;
4237
3684
  taskScheduled = false;
@@ -4247,7 +3694,8 @@ function executeTaskChunk(deadline) {
4247
3694
  return;
4248
3695
  }
4249
3696
  const context = taskList[taskIndex + 1];
4250
- const args = taskList[taskIndex + 2];
3697
+ const rawArgs = taskList[taskIndex + 2];
3698
+ const args = Array.isArray(rawArgs) ? rawArgs : [];
4251
3699
  funcWithTry(fn, args, context, noop);
4252
3700
  taskIndex += 3;
4253
3701
  }
@@ -4271,7 +3719,7 @@ function task(fn, args, context) {
4271
3719
  }
4272
3720
  var dispatcherUpdateTag = 0;
4273
3721
  function isThenable(value) {
4274
- return !!value && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
3722
+ return !!value && (typeof value === "object" || typeof value === "function") && "then" in value && typeof Reflect.get(value, "then") === "function";
4275
3723
  }
4276
3724
  function viewIsObserveChanged(view) {
4277
3725
  const loc = view.locationObserved;
@@ -4295,7 +3743,7 @@ function viewIsObserveChanged(view) {
4295
3743
  return result;
4296
3744
  }
4297
3745
  function stateIsObserveChanged(view, stateKeys) {
4298
- const observedKeys = view.observedStateKeys;
3746
+ const observedKeys = view.getObservedStateKeys();
4299
3747
  if (!observedKeys) return false;
4300
3748
  for (const key of observedKeys) {
4301
3749
  if (stateKeys.has(key)) return true;
@@ -4307,21 +3755,16 @@ function dispatcherUpdate(frame, stateKeys) {
4307
3755
  const drain = (s) => {
4308
3756
  while (s.length > 0) {
4309
3757
  const current = s.pop();
4310
- const tagged = current;
3758
+ if (!current) continue;
4311
3759
  const view = current.view;
4312
- if (!view || tagged.dispatcherUpdateTag === dispatcherUpdateTag || view.signature <= 1) {
3760
+ if (!view || current.dispatcherUpdateTag === dispatcherUpdateTag || view.signature.value <= 1) {
4313
3761
  continue;
4314
3762
  }
4315
- tagged.dispatcherUpdateTag = dispatcherUpdateTag;
3763
+ current.dispatcherUpdateTag = dispatcherUpdateTag;
4316
3764
  const isChanged = stateKeys ? stateIsObserveChanged(view, stateKeys) : viewIsObserveChanged(view);
4317
3765
  let renderPromise;
4318
3766
  if (isChanged) {
4319
- const renderResult = funcWithTry(
4320
- view.renderMethod ?? view.render,
4321
- [],
4322
- view,
4323
- noop
4324
- );
3767
+ const renderResult = funcWithTry(view.renderMethod ?? view.render, [], view, noop);
4325
3768
  if (isThenable(renderResult)) {
4326
3769
  renderPromise = renderResult;
4327
3770
  }
@@ -4349,10 +3792,9 @@ function dispatcherUpdate(frame, stateKeys) {
4349
3792
  function dispatcherNotifyChange(e) {
4350
3793
  const rootFrame2 = Frame.getRoot();
4351
3794
  if (!rootFrame2) return;
4352
- const routeEvent = e;
4353
- const view = routeEvent.view;
4354
- if (view) {
4355
- const viewPath = typeof view === "object" && view !== null ? String(view.to || "") : String(view);
3795
+ if ("view" in e && e.view !== void 0) {
3796
+ const view = e.view;
3797
+ const viewPath = typeof view === "object" && view !== null ? String(Reflect.get(view, "to") || "") : String(view);
4356
3798
  rootFrame2.mountView(viewPath);
4357
3799
  } else {
4358
3800
  dispatcherUpdateTag++;
@@ -4391,16 +3833,16 @@ function waitZoneViewsRendered(viewId, timeout) {
4391
3833
  }
4392
3834
  function getConfigImpl(key) {
4393
3835
  if (key === void 0) return config;
4394
- return config[key];
3836
+ return Reflect.get(config, key);
4395
3837
  }
4396
3838
  var Framework = {
4397
3839
  // ============================================================
4398
3840
  // Lifecycle
4399
3841
  // ============================================================
4400
- /** Read framework configuration. See `FrameworkInterface.getConfig`. */
3842
+ /** Read framework configuration. See `FrameworkApi.getConfig`. */
4401
3843
  getConfig: getConfigImpl,
4402
3844
  /**
4403
- * Merge a patch into framework configuration. See `FrameworkInterface.setConfig`.
3845
+ * Merge a patch into framework configuration. See `FrameworkApi.setConfig`.
4404
3846
  */
4405
3847
  setConfig(patch) {
4406
3848
  if (patch && typeof patch === "object") {
@@ -4426,11 +3868,13 @@ var Framework = {
4426
3868
  booted3 = true;
4427
3869
  markBooted();
4428
3870
  markRouterBooted();
4429
- installFrameDevtoolBridge();
3871
+ if (config.devtool) {
3872
+ installFrameDevtoolBridge();
3873
+ }
4430
3874
  const rootFrame2 = Frame.createRoot(config.rootId);
4431
3875
  Router._bind();
4432
3876
  const defaultView = config.defaultView || "";
4433
- if (defaultView && !rootFrame2.view) {
3877
+ if (defaultView && !rootFrame2.getViewPath()) {
4434
3878
  rootFrame2.mountView(defaultView);
4435
3879
  }
4436
3880
  },
@@ -4446,7 +3890,7 @@ var Framework = {
4446
3890
  /** Unmark (invalidate) async callbacks */
4447
3891
  unmark,
4448
3892
  /** Fire a custom DOM event on a target */
4449
- dispatch: dispatchEvent,
3893
+ dispatchEvent,
4450
3894
  /** Execute function in try-catch, ignoring errors */
4451
3895
  task,
4452
3896
  /** Promise-based setTimeout */
@@ -4457,32 +3901,20 @@ var Framework = {
4457
3901
  use,
4458
3902
  /** Wait for zone views to be rendered */
4459
3903
  waitZoneViewsRendered,
4460
- WAIT_OK,
4461
- WAIT_TIMEOUT_OR_NOT_FOUND,
4462
- /**
4463
- * Convert array to hash map.
4464
- */
4465
- toMap,
4466
- /**
4467
- * Execute function in try-catch.
4468
- */
4469
- toTry: funcWithTry,
4470
3904
  /**
4471
3905
  * Convert path + params to URL string.
4472
3906
  */
4473
- toUrl: toUri,
3907
+ toUri,
4474
3908
  /**
4475
3909
  * Parse URI string into path and params.
4476
3910
  */
4477
- parseUrl: parseUri,
3911
+ parseUri,
3912
+ WAIT_OK,
3913
+ WAIT_TIMEOUT_OR_NOT_FOUND,
4478
3914
  /**
4479
3915
  * Mix properties from source to target.
4480
3916
  */
4481
- mix: assign,
4482
- /**
4483
- * Check if object has own property.
4484
- */
4485
- has: hasOwnProperty,
3917
+ assign,
4486
3918
  /**
4487
3919
  * Get object keys.
4488
3920
  */
@@ -4490,27 +3922,19 @@ var Framework = {
4490
3922
  /**
4491
3923
  * Check if node A is inside node B.
4492
3924
  */
4493
- inside: nodeInside,
4494
- /**
4495
- * Get element by ID (shorthand for document.getElementById).
4496
- */
4497
- node: getById,
4498
- /**
4499
- * Apply CSS style.
4500
- */
4501
- applyStyle,
3925
+ nodeInside,
4502
3926
  /**
4503
3927
  * Generate globally unique ID.
4504
3928
  */
4505
- guid: generateId,
3929
+ generateId,
4506
3930
  /**
4507
- * Cache class.
3931
+ * Cache factory (functional).
4508
3932
  */
4509
- Cache,
3933
+ createCache,
4510
3934
  /**
4511
3935
  * Ensure element has an ID.
4512
3936
  */
4513
- nodeId(element) {
3937
+ ensureNodeId(element) {
4514
3938
  if (!element.id) {
4515
3939
  element.id = generateId("l_");
4516
3940
  }
@@ -4519,7 +3943,7 @@ var Framework = {
4519
3943
  /**
4520
3944
  * Base class with EventEmitter.
4521
3945
  */
4522
- Base: EventEmitter,
3946
+ createEmitter,
4523
3947
  // ============================================================
4524
3948
  // Module access
4525
3949
  // ============================================================
@@ -4527,21 +3951,11 @@ var Framework = {
4527
3951
  Router,
4528
3952
  /** State module */
4529
3953
  State,
4530
- /** View class */
4531
- View,
3954
+ /** View factory (functional) */
3955
+ defineView,
4532
3956
  /** Frame class */
4533
3957
  Frame
4534
3958
  };
4535
- if (typeof window !== "undefined") {
4536
- window.__lark_Framework = Framework;
4537
- window.__lark_State = State;
4538
- window.__lark_Router = Router;
4539
- window.__lark_Frame = Frame;
4540
- window.__lark_View = View;
4541
- window.__lark_invalidateViewClass = invalidateViewClass;
4542
- window.__lark_getViewClassRegistry = getViewClassRegistry;
4543
- window.__lark_registerViewClass = registerViewClass;
4544
- }
4545
3959
 
4546
3960
  // src/url-state.ts
4547
3961
  function useUrlState(view, initialState) {
@@ -4566,156 +3980,312 @@ function useUrlState(view, initialState) {
4566
3980
  return [getState(), setState];
4567
3981
  }
4568
3982
 
4569
- // src/store.ts
4570
- var COMPUTED_BRAND = /* @__PURE__ */ Symbol("lark-store-computed");
4571
- function isComputedMarker(val) {
4572
- return val !== null && typeof val === "object" && val[COMPUTED_BRAND] === true;
3983
+ // src/hmr.ts
3984
+ function reloadViews(viewPath) {
3985
+ const allFrames = Frame.getAll();
3986
+ const toReload = [];
3987
+ for (const [, frame] of allFrames) {
3988
+ const vp = frame.getViewPath();
3989
+ if (vp) {
3990
+ const parsed = parseUri(vp);
3991
+ if (parsed.path === viewPath) {
3992
+ toReload.push({ frame, fullPath: vp });
3993
+ }
3994
+ }
3995
+ }
3996
+ for (const { frame, fullPath } of toReload) {
3997
+ frame.mountView(fullPath);
3998
+ }
4573
3999
  }
4574
- function computed(deps, fn) {
4575
- return { [COMPUTED_BRAND]: true, deps, fn };
4000
+ function hotSwapView(frame, newSetup) {
4001
+ const oldView = frame.view;
4002
+ if (!oldView) {
4003
+ const vp = frame.getViewPath();
4004
+ if (vp) frame.mountView(vp);
4005
+ return;
4006
+ }
4007
+ for (let i = oldView.cleanups.length - 1; i >= 0; i--) {
4008
+ oldView.cleanups[i]();
4009
+ }
4010
+ oldView.cleanups.length = 0;
4011
+ unregisterEvents(oldView);
4012
+ destroyAllResources(oldView, false);
4013
+ setCurrentCtx(oldView);
4014
+ let descriptor;
4015
+ try {
4016
+ descriptor = newSetup(oldView, void 0);
4017
+ } finally {
4018
+ setCurrentCtx(null);
4019
+ }
4020
+ oldView.setTemplate(descriptor.template);
4021
+ oldView.setEvents(descriptor.events);
4022
+ if (descriptor.assign) oldView.setAssign(descriptor.assign);
4023
+ registerEvents(oldView);
4024
+ if (oldView.signature.value > 0) {
4025
+ oldView.signature.value++;
4026
+ oldView.fire("render");
4027
+ destroyAllResources(oldView, false);
4028
+ oldView.updater.forceDigest();
4029
+ }
4576
4030
  }
4577
- var storeRegistry = /* @__PURE__ */ new Map();
4578
- function create(name, creator) {
4579
- const listeners = /* @__PURE__ */ new Set();
4580
- const computedDefs = /* @__PURE__ */ new Map();
4581
- const computedKeys = /* @__PURE__ */ new Set();
4582
- const actionKeys = /* @__PURE__ */ new Set();
4583
- let state;
4584
- let destroyed = false;
4585
- const getState = () => state;
4586
- const setState = (partial) => {
4587
- if (destroyed) return;
4588
- const prevState = state;
4589
- const resolved = typeof partial === "function" ? partial(prevState) : partial;
4590
- const nextState = { ...prevState };
4591
- let changed = false;
4592
- for (const key in resolved) {
4593
- if (Object.prototype.hasOwnProperty.call(resolved, key) && !computedKeys.has(key) && !actionKeys.has(key)) {
4594
- const newVal = resolved[key];
4595
- if (!Object.is(prevState[key], newVal)) {
4596
- nextState[key] = newVal;
4597
- changed = true;
4598
- }
4031
+ function findFramesByViewPath(viewPath) {
4032
+ const result = [];
4033
+ for (const [, frame] of Frame.getAll()) {
4034
+ const vp = frame.getViewPath();
4035
+ if (vp) {
4036
+ const parsed = parseUri(vp);
4037
+ if (parsed.path === viewPath) {
4038
+ result.push({ frame, fullPath: vp });
4599
4039
  }
4600
4040
  }
4601
- if (!changed) return;
4602
- state = nextState;
4603
- recomputeIfNeeded(prevState);
4604
- for (const listener of listeners) {
4605
- listener(state, prevState);
4606
- }
4607
- };
4608
- const recomputeIfNeeded = (prevState) => {
4609
- if (computedDefs.size === 0) return;
4610
- const changedKeys2 = /* @__PURE__ */ new Set();
4611
- for (const key of Object.keys(state)) {
4612
- if (!Object.is(
4613
- state[key],
4614
- prevState[key]
4615
- )) {
4616
- changedKeys2.add(key);
4617
- }
4041
+ }
4042
+ return result;
4043
+ }
4044
+ function hotSwapFrames(viewPath, newSetup) {
4045
+ const targets = findFramesByViewPath(viewPath);
4046
+ for (const { frame } of targets) {
4047
+ hotSwapView(frame, newSetup);
4048
+ }
4049
+ }
4050
+ function hotSwapByTemplate(oldTemplate, newTemplate) {
4051
+ if (!oldTemplate || !newTemplate || oldTemplate === newTemplate) return;
4052
+ for (const [, frame] of Frame.getAll()) {
4053
+ const view = frame.view;
4054
+ if (!view || view.getTemplate() !== oldTemplate) continue;
4055
+ view.setTemplate(newTemplate);
4056
+ if (view.signature.value > 0) {
4057
+ view.signature.value++;
4058
+ view.fire("render");
4059
+ destroyAllResources(view, false);
4060
+ view.updater.forceDigest();
4618
4061
  }
4619
- for (const [key, def] of computedDefs) {
4620
- if (def.deps.some((dep) => changedKeys2.has(dep))) {
4621
- const newVal = def.fn();
4622
- if (!Object.is(state[key], newVal)) {
4623
- state[key] = newVal;
4624
- }
4062
+ }
4063
+ }
4064
+ function hotSwapByView(oldSetup, newSetup) {
4065
+ if (!oldSetup || !newSetup || oldSetup === newSetup) return;
4066
+ const reg = getViewClassRegistry();
4067
+ for (const path in reg) {
4068
+ if (reg[path] === oldSetup) reg[path] = newSetup;
4069
+ }
4070
+ for (const [, frame] of Frame.getAll()) {
4071
+ const view = frame.view;
4072
+ const vp = frame.getViewPath();
4073
+ if (view && vp) {
4074
+ const parsed = parseUri(vp);
4075
+ if (reg[parsed.path] === newSetup) {
4076
+ hotSwapView(frame, newSetup);
4625
4077
  }
4626
4078
  }
4627
- };
4628
- const subscribe = (listener) => {
4629
- listeners.add(listener);
4630
- return () => {
4631
- listeners.delete(listener);
4632
- };
4633
- };
4634
- const destroy = () => {
4635
- destroyed = true;
4636
- listeners.clear();
4637
- storeRegistry.delete(name);
4638
- };
4639
- const api = { getState, setState, subscribe, destroy };
4640
- const body = creator(setState, getState);
4641
- const initialState = {};
4642
- const actions = {};
4643
- for (const key of Object.keys(body)) {
4644
- const val = body[key];
4645
- if (isComputedMarker(val)) {
4646
- computedDefs.set(key, val);
4647
- computedKeys.add(key);
4648
- initialState[key] = void 0;
4649
- } else if (typeof val === "function") {
4650
- actions[key] = val;
4651
- actionKeys.add(key);
4652
- } else {
4653
- initialState[key] = val;
4079
+ }
4080
+ }
4081
+
4082
+ // src/hmr-inject.ts
4083
+ var TEMPLATE_VAR = "__larkTemplate";
4084
+ function getTemplateHmrSnippet(bundler) {
4085
+ if (bundler === "vite") {
4086
+ return `
4087
+ // \u2500\u2500 Lark template HMR (auto-injected by larkMvcPlugin \u2014 Vite) \u2500\u2500
4088
+ if (import.meta.hot) {
4089
+ import.meta.hot.dispose(function(__larkData) {
4090
+ __larkData.oldTemplate = ${TEMPLATE_VAR};
4091
+ });
4092
+ import.meta.hot.accept(function(__larkNewModule) {
4093
+ var __larkNew = __larkNewModule && __larkNewModule.default;
4094
+ var __larkOld = import.meta.hot.data && import.meta.hot.data.oldTemplate;
4095
+ if (__larkOld && __larkNew && __larkOld !== __larkNew) {
4096
+ import("@lark.js/mvc").then(function(m) {
4097
+ if (m && m.hotSwapByTemplate) m.hotSwapByTemplate(__larkOld, __larkNew);
4098
+ });
4654
4099
  }
4100
+ });
4101
+ }
4102
+ `;
4655
4103
  }
4656
- state = { ...initialState, ...actions };
4657
- for (const [key, def] of computedDefs) {
4658
- state[key] = def.fn();
4104
+ return `
4105
+ // \u2500\u2500 Lark template HMR (auto-injected by larkMvcPlugin \u2014 ${bundler}) \u2500\u2500
4106
+ if (typeof module !== "undefined" && module.hot) {
4107
+ module.hot.dispose(function(__larkData) {
4108
+ __larkData.oldTemplate = ${TEMPLATE_VAR};
4109
+ });
4110
+ module.hot.accept(function() {
4111
+ var __larkNew = ${TEMPLATE_VAR};
4112
+ var __larkOld = module.hot && module.hot.data && module.hot.data.oldTemplate;
4113
+ if (__larkOld && __larkNew && __larkOld !== __larkNew) {
4114
+ import("@lark.js/mvc").then(function(m) {
4115
+ if (m && m.hotSwapByTemplate) m.hotSwapByTemplate(__larkOld, __larkNew);
4116
+ });
4117
+ }
4118
+ });
4119
+ }
4120
+ `;
4121
+ }
4122
+ function injectTemplateHmrSnippet(source, bundler) {
4123
+ return source + "\n" + getTemplateHmrSnippet(bundler);
4124
+ }
4125
+ function getViewHmrSnippet(bundler) {
4126
+ const VIEW_VAR = "__larkViewDefault";
4127
+ if (bundler === "vite") {
4128
+ return `
4129
+ // \u2500\u2500 Lark view class HMR (auto-injected by larkMvcPlugin \u2014 Vite) \u2500\u2500
4130
+ if (import.meta.hot) {
4131
+ import.meta.hot.dispose(function(__larkData) {
4132
+ __larkData.oldClass = ${VIEW_VAR};
4133
+ });
4134
+ import.meta.hot.accept(function(__larkNewModule) {
4135
+ var __larkNew = __larkNewModule && __larkNewModule.default;
4136
+ var __larkOld = import.meta.hot.data && import.meta.hot.data.oldClass;
4137
+ if (__larkOld && __larkNew && __larkOld !== __larkNew) {
4138
+ import("@lark.js/mvc").then(function(m) {
4139
+ if (m && m.hotSwapByView) m.hotSwapByView(__larkOld, __larkNew);
4140
+ });
4141
+ }
4142
+ });
4143
+ }
4144
+ `;
4659
4145
  }
4660
- storeRegistry.set(name, api);
4661
- return api;
4146
+ return `
4147
+ // \u2500\u2500 Lark view class HMR (auto-injected by larkMvcPlugin \u2014 ${bundler}) \u2500\u2500
4148
+ if (typeof module !== "undefined" && module.hot) {
4149
+ module.hot.dispose(function(__larkData) {
4150
+ __larkData.oldClass = ${VIEW_VAR};
4151
+ });
4152
+ module.hot.accept(function() {
4153
+ var __larkNew = ${VIEW_VAR};
4154
+ var __larkOld = module.hot && module.hot.data && module.hot.data.oldClass;
4155
+ if (__larkOld && __larkNew && __larkOld !== __larkNew) {
4156
+ import("@lark.js/mvc").then(function(m) {
4157
+ if (m && m.hotSwapByView) m.hotSwapByView(__larkOld, __larkNew);
4158
+ });
4159
+ }
4160
+ });
4662
4161
  }
4663
- function isLarkView(instance) {
4664
- if (!instance || typeof instance !== "object") return false;
4665
- const obj = instance;
4666
- const updater = obj["updater"];
4667
- return updater !== null && typeof updater === "object" && typeof updater["set"] === "function" && typeof updater["digest"] === "function";
4162
+ `;
4668
4163
  }
4669
- function bindStore(view, store, selector) {
4670
- if (!isLarkView(view)) return () => {
4671
- };
4672
- const extract = (s) => {
4673
- if (selector) return selector(s);
4674
- const result = {};
4675
- for (const key in s) {
4676
- if (Object.prototype.hasOwnProperty.call(s, key) && typeof s[key] !== "function") {
4677
- result[key] = s[key];
4164
+ var HTML_IMPORT_RE = /import\s+(?:template\s+from\s+|.*from\s+)?["'][^"']+\.html["']/;
4165
+ function importsHtmlTemplate(source) {
4166
+ return HTML_IMPORT_RE.test(source);
4167
+ }
4168
+ function injectViewHmr(source, bundler) {
4169
+ if (!importsHtmlTemplate(source)) return source;
4170
+ const exportDefaultRe = /export\s+default\s+/;
4171
+ const match = exportDefaultRe.exec(source);
4172
+ if (!match) return source;
4173
+ const startIdx = match.index + match[0].length;
4174
+ const endIdx = findExpressionEnd(source, startIdx);
4175
+ if (endIdx === -1) return source;
4176
+ const expression = source.substring(startIdx, endIdx);
4177
+ const before = source.substring(0, match.index);
4178
+ const after = source.substring(endIdx);
4179
+ const VIEW_VAR = "__larkViewDefault";
4180
+ const transformed = before + `const ${VIEW_VAR} = ${expression};
4181
+ export default ${VIEW_VAR};` + after + "\n" + getViewHmrSnippet(bundler);
4182
+ return transformed;
4183
+ }
4184
+ function findExpressionEnd(source, startIdx) {
4185
+ let depth = 0;
4186
+ let inString = null;
4187
+ let inTemplate = false;
4188
+ let inLineComment = false;
4189
+ let inBlockComment = false;
4190
+ for (let i = startIdx; i < source.length; i++) {
4191
+ const ch = source[i];
4192
+ const next = source[i + 1];
4193
+ if (inLineComment) {
4194
+ if (ch === "\n") inLineComment = false;
4195
+ continue;
4196
+ }
4197
+ if (inBlockComment) {
4198
+ if (ch === "*" && next === "/") {
4199
+ inBlockComment = false;
4200
+ i++;
4678
4201
  }
4202
+ continue;
4679
4203
  }
4680
- return result;
4681
- };
4682
- view.updater.set(extract(store.getState()));
4683
- view.updater.digest();
4684
- const off = store.subscribe((state) => {
4685
- view.updater.set(extract(state));
4686
- view.updater.digest();
4687
- });
4688
- view.on("destroy", off);
4689
- return off;
4204
+ if (inString) {
4205
+ if (ch === "\\") {
4206
+ i++;
4207
+ continue;
4208
+ }
4209
+ if (ch === inString) inString = null;
4210
+ continue;
4211
+ }
4212
+ if (inTemplate) {
4213
+ if (ch === "\\") {
4214
+ i++;
4215
+ continue;
4216
+ }
4217
+ if (ch === "`") inTemplate = false;
4218
+ continue;
4219
+ }
4220
+ if (ch === "/" && next === "/") {
4221
+ inLineComment = true;
4222
+ i++;
4223
+ continue;
4224
+ }
4225
+ if (ch === "/" && next === "*") {
4226
+ inBlockComment = true;
4227
+ i++;
4228
+ continue;
4229
+ }
4230
+ if (ch === '"' || ch === "'") {
4231
+ inString = ch;
4232
+ continue;
4233
+ }
4234
+ if (ch === "`") {
4235
+ inTemplate = true;
4236
+ continue;
4237
+ }
4238
+ if (ch === "(" || ch === "{" || ch === "[") {
4239
+ depth++;
4240
+ continue;
4241
+ }
4242
+ if (ch === ")" || ch === "}" || ch === "]") {
4243
+ depth--;
4244
+ if (depth === 0) {
4245
+ return i + 1;
4246
+ }
4247
+ continue;
4248
+ }
4249
+ if (depth === 0 && ch === ";") {
4250
+ return i;
4251
+ }
4252
+ }
4253
+ return -1;
4690
4254
  }
4691
4255
  export {
4692
4256
  CALL_BREAK_TIME,
4693
- Cache,
4694
4257
  cross_site_default as CrossSite,
4695
4258
  EVENT_METHOD_REGEXP,
4696
4259
  EventDelegator,
4697
- EventEmitter,
4698
4260
  Frame,
4699
4261
  Framework,
4700
4262
  LARK_VIEW,
4701
- Payload,
4702
4263
  RouterEvents as ROUTER_EVENTS,
4703
4264
  Router,
4704
4265
  SPLITTER,
4705
- Service,
4706
4266
  State,
4707
4267
  TAG_NAME_REGEXP,
4708
- Updater,
4709
4268
  VIEW_EVENT_METHOD_REGEXP,
4710
- View,
4711
- applyStyle,
4712
4269
  bindStore,
4713
4270
  computed,
4714
- create,
4271
+ createCache,
4272
+ createEmitter,
4273
+ createFrame,
4274
+ createPayload,
4275
+ createService,
4276
+ createStore,
4277
+ createUpdater,
4715
4278
  createVDomRef,
4716
4279
  defineView,
4717
4280
  config as frameworkConfig,
4718
4281
  getRouteMode,
4282
+ hotSwapByTemplate,
4283
+ hotSwapByView,
4284
+ hotSwapFrames,
4285
+ hotSwapView,
4286
+ importsHtmlTemplate,
4287
+ injectTemplateHmrSnippet,
4288
+ injectViewHmr,
4719
4289
  invalidateViewClass,
4720
4290
  mark,
4721
4291
  markBooted,
@@ -4726,6 +4296,13 @@ export {
4726
4296
  resetProjectsMap,
4727
4297
  unmark,
4728
4298
  use,
4299
+ useEffect,
4300
+ useEvent,
4301
+ useInterval,
4302
+ useResource,
4303
+ useState,
4304
+ useStore,
4305
+ useTimeout,
4729
4306
  useUrlState,
4730
4307
  vdomCreate
4731
4308
  };