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