@lark.js/mvc 0.0.10 → 0.0.12

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.
@@ -0,0 +1,3436 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/devtool.ts
21
+ var devtool_exports = {};
22
+ __export(devtool_exports, {
23
+ FrameDevtoolBridge: () => FrameDevtoolBridge,
24
+ installFrameDevtoolBridge: () => installFrameDevtoolBridge,
25
+ serializeFrameTree: () => serializeFrameTree
26
+ });
27
+ module.exports = __toCommonJS(devtool_exports);
28
+
29
+ // src/common.ts
30
+ var globalCounter = 0;
31
+ var SPLITTER = String.fromCharCode(30);
32
+ var RouterEvents = {
33
+ CHANGE: "change",
34
+ CHANGED: "changed",
35
+ PAGE_UNLOAD: "page_unload"
36
+ };
37
+ var LARK_VIEW = "v-lark";
38
+ var EVENT_METHOD_REGEXP = new RegExp(
39
+ `(?:([\\w-]+)${SPLITTER})?([^(]+)\\(([\\s\\S]*?)?\\)`
40
+ );
41
+ var VIEW_EVENT_METHOD_REGEXP = /^(\$?)([\w]*)<(.*?)>(?:<([\w ,]*)>)?$/;
42
+ var URL_TRIM_HASH_REGEXP = /(?:^.*\/\/[^/]+|#.*$)/gi;
43
+ var URL_TRIM_QUERY_REGEXP = /^[^#]*#?!?/;
44
+ var URL_PARAM_REGEXP = /([^=&?/#]+)=?([^&#?]*)/g;
45
+ var IS_URL_PARAMS = /(?!^)=|&/;
46
+ var URL_QUERY_HASH_REGEXP = /[#?].*$/;
47
+ var SVG_NS = "http://www.w3.org/2000/svg";
48
+ var MATH_NS = "http://www.w3.org/1998/Math/MathML";
49
+ var TAG_NAME_REGEXP = /<([a-z][^/\0>\x20\t\r\n\f]+)/i;
50
+ var V_TEXT_NODE = 0;
51
+ var VDOM_NS_MAP = {
52
+ svg: SVG_NS,
53
+ math: MATH_NS
54
+ };
55
+ function nextCounter() {
56
+ return ++globalCounter;
57
+ }
58
+ var HTML_ENT_MAP = {
59
+ "&": "amp",
60
+ "<": "lt",
61
+ ">": "gt",
62
+ '"': "#34",
63
+ "'": "#39",
64
+ "`": "#96"
65
+ };
66
+ var HTML_ENT_REGEXP = /[&<>"'`]/g;
67
+ function strSafe(v) {
68
+ return String(v == null ? "" : v);
69
+ }
70
+ function encodeHTML(v) {
71
+ return String(v == null ? "" : v).replace(
72
+ HTML_ENT_REGEXP,
73
+ (m) => "&" + HTML_ENT_MAP[m] + ";"
74
+ );
75
+ }
76
+ var URI_ENT_MAP = {
77
+ "!": "%21",
78
+ "'": "%27",
79
+ "(": "%28",
80
+ ")": "%29",
81
+ "*": "%2A"
82
+ };
83
+ var URI_ENT_REGEXP = /[!')(*]/g;
84
+ function encodeURIExtra(v) {
85
+ return encodeURIComponent(strSafe(v)).replace(
86
+ URI_ENT_REGEXP,
87
+ (m) => URI_ENT_MAP[m]
88
+ );
89
+ }
90
+ var QUOTE_ENT_REGEXP = /['"\\]/g;
91
+ function encodeQuote(v) {
92
+ return strSafe(v).replace(QUOTE_ENT_REGEXP, "\\$&");
93
+ }
94
+ function refFn(ref, value, key) {
95
+ const counter = ref[SPLITTER];
96
+ for (let i = counter; --i; ) {
97
+ key = SPLITTER + i;
98
+ if (ref[key] === value) return key;
99
+ }
100
+ key = SPLITTER + ref[SPLITTER]++;
101
+ ref[key] = value;
102
+ return key;
103
+ }
104
+ function isRefToken(s) {
105
+ if (s.length < 2 || s[0] !== SPLITTER) return false;
106
+ for (let i = 1; i < s.length; i++) {
107
+ const c = s.charCodeAt(i);
108
+ if (c < "0".charCodeAt(0) || c > "9".charCodeAt(0)) return false;
109
+ }
110
+ return true;
111
+ }
112
+
113
+ // src/utils.ts
114
+ var CALL_BREAK_TIME = 9;
115
+ var callQueue = [];
116
+ var callScheduled = false;
117
+ var schedulerYield = (() => {
118
+ try {
119
+ if (typeof globalThis?.scheduler?.yield === "function") {
120
+ return globalThis.scheduler.yield.bind(globalThis.scheduler);
121
+ }
122
+ } catch {
123
+ }
124
+ return void 0;
125
+ })();
126
+ async function startCall() {
127
+ callScheduled = false;
128
+ const startTime = performance.now();
129
+ while (callQueue.length > 0) {
130
+ const task = callQueue.shift();
131
+ try {
132
+ task();
133
+ } catch (e) {
134
+ console.error("scheduler task error:", e);
135
+ }
136
+ if (callQueue.length > 0 && performance.now() - startTime > CALL_BREAK_TIME) {
137
+ if (schedulerYield) {
138
+ await schedulerYield();
139
+ } else {
140
+ scheduleNextChunk();
141
+ return;
142
+ }
143
+ }
144
+ }
145
+ }
146
+ function scheduleNextChunk() {
147
+ setTimeout(() => startCall(), 0);
148
+ callScheduled = true;
149
+ }
150
+ function callFunction(fn, args) {
151
+ callQueue.push(() => fn(...args));
152
+ if (!callScheduled) {
153
+ scheduleNextChunk();
154
+ }
155
+ }
156
+ function isPlainObject(value) {
157
+ if (typeof value !== "object" || value === null) return false;
158
+ const proto = Object.getPrototypeOf(value);
159
+ return proto === null || proto === Object.prototype;
160
+ }
161
+ function isRecord(value) {
162
+ return typeof value === "object" && value !== null;
163
+ }
164
+ function asRecord(value) {
165
+ if (isRecord(value)) {
166
+ return value;
167
+ }
168
+ console.error("fallback to Object.fromEntries, even an empty object {}.");
169
+ if (Array.isArray(value)) {
170
+ return Object.fromEntries(value.entries());
171
+ }
172
+ return {};
173
+ }
174
+ function isPrimitiveOrFunc(value) {
175
+ return !value || typeof value !== "object" && typeof value !== "function";
176
+ }
177
+ function isPrimitive(value) {
178
+ return !value || typeof value !== "object";
179
+ }
180
+ var _localCounter = 0;
181
+ function generateId(prefix) {
182
+ return (prefix || "lark_") + _localCounter++;
183
+ }
184
+ function noop() {
185
+ }
186
+ function hasOwnProperty(owner, prop) {
187
+ return owner != null && Object.prototype.hasOwnProperty.call(owner, prop);
188
+ }
189
+ function assign(target, ...sources) {
190
+ for (const source of sources) {
191
+ if (source) {
192
+ for (const p in source) {
193
+ if (hasOwnProperty(source, p)) {
194
+ target[p] = source[p];
195
+ }
196
+ }
197
+ }
198
+ }
199
+ return target;
200
+ }
201
+ function funcWithTry(fns, args, context, configError) {
202
+ const fnArray = Array.isArray(fns) ? fns : [fns];
203
+ let ret;
204
+ for (const fn of fnArray) {
205
+ try {
206
+ ret = fn.apply(context, args);
207
+ } catch (e) {
208
+ configError?.(e);
209
+ }
210
+ }
211
+ return ret;
212
+ }
213
+ var EMPTY_STRING_SET = /* @__PURE__ */ new Set();
214
+ function setData(newData, oldData, changedKeys, excludes) {
215
+ let changed = false;
216
+ for (const p in newData) {
217
+ if (hasOwnProperty(newData, p)) {
218
+ const now = newData[p];
219
+ const old = oldData[p];
220
+ if ((!isPrimitiveOrFunc(now) || old !== now) && !excludes.has(p)) {
221
+ changedKeys.add(p);
222
+ changed = true;
223
+ }
224
+ oldData[p] = now;
225
+ }
226
+ }
227
+ return changed;
228
+ }
229
+ function translateData(data, value) {
230
+ if (isPrimitive(value)) {
231
+ const prop = String(value);
232
+ if (isRefToken(prop) && hasOwnProperty(data, prop)) {
233
+ return data[prop];
234
+ }
235
+ return value;
236
+ }
237
+ if (isPlainObject(value) || Array.isArray(value)) {
238
+ for (const p in value) {
239
+ if (hasOwnProperty(value, p)) {
240
+ const val = value[p];
241
+ const newVal = translateData(data, val);
242
+ value[p] = newVal;
243
+ }
244
+ }
245
+ return value;
246
+ }
247
+ return value;
248
+ }
249
+ function getById(id) {
250
+ if (!id) return null;
251
+ if (typeof id === "object") return id;
252
+ return document.getElementById(id);
253
+ }
254
+ function getAttribute(element, attr) {
255
+ return Element.prototype.getAttribute.call(element, attr) ?? "";
256
+ }
257
+ function ensureElementId(element, prefix) {
258
+ const id = element.getAttribute("id");
259
+ if (id) return id;
260
+ element.autoId = 1;
261
+ const newId = generateId(prefix);
262
+ element.id = newId;
263
+ return newId;
264
+ }
265
+ function parseUri(uri) {
266
+ const params = {};
267
+ const path = uri.replace(URL_QUERY_HASH_REGEXP, "");
268
+ const pathname = path;
269
+ const actualPath = uri === pathname && IS_URL_PARAMS.test(pathname) ? "" : pathname;
270
+ uri.replace(actualPath, "").replace(URL_PARAM_REGEXP, (_match, name, value) => {
271
+ try {
272
+ params[name] = decodeURIComponent(value || "");
273
+ } catch {
274
+ params[name] = value || "";
275
+ }
276
+ return "";
277
+ });
278
+ return { path: actualPath, params };
279
+ }
280
+ function toUri(path, params, keepEmpty) {
281
+ const pairs = [];
282
+ let hasParams = false;
283
+ for (const p in params) {
284
+ if (hasOwnProperty(params, p)) {
285
+ const v = String(params[p] ?? "");
286
+ if (!keepEmpty || v || keepEmpty.has(p)) {
287
+ pairs.push(`${p}=${encodeURIComponent(v)}`);
288
+ hasParams = true;
289
+ }
290
+ }
291
+ }
292
+ if (hasParams) {
293
+ path += (path && (~path.indexOf("?") ? "&" : "?")) + pairs.join("&");
294
+ }
295
+ return path;
296
+ }
297
+
298
+ // src/event-emitter.ts
299
+ var EventEmitter = class {
300
+ /** Event listeners: prefixed key -> listener array */
301
+ listeners = /* @__PURE__ */ new Map();
302
+ /** Number of `fire()` calls currently on the stack (re-entrancy depth). */
303
+ firingDepth = 0;
304
+ /** Keys whose listener list needs compaction after firing settles. */
305
+ pendingCompaction;
306
+ /**
307
+ * Bind event listener.
308
+ */
309
+ on(event, handler) {
310
+ const key = SPLITTER + event;
311
+ let list = this.listeners.get(key);
312
+ if (!list) {
313
+ list = [];
314
+ this.listeners.set(key, list);
315
+ }
316
+ list.push({ handler, executing: 0 });
317
+ return this;
318
+ }
319
+ /**
320
+ * Unbind event listener.
321
+ * If handler is provided, removes only that handler.
322
+ * If no handler, removes all handlers for the event.
323
+ */
324
+ off(event, handler) {
325
+ const key = SPLITTER + event;
326
+ if (handler) {
327
+ const list = this.listeners.get(key);
328
+ if (!list) return this;
329
+ if (this.firingDepth > 0) {
330
+ for (const listener of list) {
331
+ if (listener.handler === handler) {
332
+ listener.handler = noop;
333
+ (this.pendingCompaction ??= /* @__PURE__ */ new Set()).add(key);
334
+ break;
335
+ }
336
+ }
337
+ } else {
338
+ for (let i = 0; i < list.length; i++) {
339
+ if (list[i].handler === handler) {
340
+ list.splice(i, 1);
341
+ break;
342
+ }
343
+ }
344
+ if (list.length === 0) this.listeners.delete(key);
345
+ }
346
+ } else {
347
+ this.listeners.delete(key);
348
+ Reflect.deleteProperty(
349
+ this,
350
+ `on${event[0].toUpperCase() + event.slice(1)}`
351
+ );
352
+ }
353
+ return this;
354
+ }
355
+ /**
356
+ * Fire event, execute all bound handlers. Safe for re-entrant `off()` calls
357
+ * during dispatch: removed handlers are replaced with noop and compacted
358
+ * after the outermost fire returns.
359
+ *
360
+ * @param event - Event name
361
+ * @param data - Event data (type property added automatically)
362
+ * @param remove - Whether to remove all handlers after firing
363
+ * @param lastToFirst - Whether to execute handlers in reverse order
364
+ */
365
+ fire(event, data, remove, lastToFirst) {
366
+ const key = SPLITTER + event;
367
+ const list = this.listeners.get(key);
368
+ if (!data) {
369
+ data = {};
370
+ }
371
+ data["type"] = event;
372
+ this.firingDepth++;
373
+ try {
374
+ if (list) {
375
+ const len = list.length;
376
+ for (let i = 0; i < len; i++) {
377
+ const idx = lastToFirst ? len - 1 - i : i;
378
+ const listener = list[idx];
379
+ if (!listener) continue;
380
+ if (listener.handler === noop) continue;
381
+ listener.executing = 1;
382
+ funcWithTry([listener.handler], [data], this, noop);
383
+ listener.executing = "";
384
+ }
385
+ }
386
+ const onMethodName = `on${event[0].toUpperCase() + event.slice(1)}`;
387
+ const onMethod = this[onMethodName];
388
+ if (typeof onMethod === "function") {
389
+ funcWithTry([onMethod], [data], this, noop);
390
+ }
391
+ if (remove) {
392
+ this.off(event);
393
+ }
394
+ } finally {
395
+ this.firingDepth--;
396
+ if (this.firingDepth === 0 && this.pendingCompaction) {
397
+ for (const k of this.pendingCompaction) {
398
+ const l = this.listeners.get(k);
399
+ if (!l) continue;
400
+ for (let i = l.length - 1; i >= 0; i--) {
401
+ if (l[i].handler === noop) l.splice(i, 1);
402
+ }
403
+ if (l.length === 0) this.listeners.delete(k);
404
+ }
405
+ this.pendingCompaction = void 0;
406
+ }
407
+ }
408
+ return this;
409
+ }
410
+ };
411
+
412
+ // src/mark.ts
413
+ var hostStore = /* @__PURE__ */ new WeakMap();
414
+ function unmark(host) {
415
+ const record = hostStore.get(host);
416
+ if (record) {
417
+ record.deleted = true;
418
+ record.signs.clear();
419
+ } else {
420
+ hostStore.set(host, { signs: /* @__PURE__ */ new Map(), deleted: true });
421
+ }
422
+ }
423
+
424
+ // src/cache.ts
425
+ function sortCacheEntries(a, b) {
426
+ return b.frequency - a.frequency || b.lastTimestamp - a.lastTimestamp;
427
+ }
428
+ var Cache = class {
429
+ /** Cache entries array */
430
+ entries = [];
431
+ /** Fast lookup: prefixed key -> entry */
432
+ lookup = /* @__PURE__ */ new Map();
433
+ /** Buffer size for eviction */
434
+ bufferSize;
435
+ /** Maximum cache size */
436
+ maxSize;
437
+ /** Total capacity (maxSize + bufferSize) */
438
+ capacity;
439
+ /** Callback when entry is removed */
440
+ onRemove;
441
+ /** Sort comparator */
442
+ comparator;
443
+ constructor(options = {}) {
444
+ this.maxSize = options.maxSize ?? 20;
445
+ this.bufferSize = options.bufferSize ?? 5;
446
+ this.capacity = this.maxSize + this.bufferSize;
447
+ this.onRemove = options.onRemove;
448
+ this.comparator = options.sortComparator ?? sortCacheEntries;
449
+ }
450
+ /** Prefix a key with SPLITTER for namespace isolation */
451
+ prefixKey(key) {
452
+ return SPLITTER + key;
453
+ }
454
+ /**
455
+ * Get a cached value by key.
456
+ * Updates frequency and timestamp for cache sorting.
457
+ */
458
+ get(key) {
459
+ const prefixedKey = this.prefixKey(key);
460
+ const entry = this.lookup.get(prefixedKey);
461
+ if (!entry) return void 0;
462
+ entry.frequency++;
463
+ entry.lastTimestamp = nextCounter();
464
+ return entry.value;
465
+ }
466
+ /**
467
+ * Iterate all cached values.
468
+ */
469
+ forEach(callback) {
470
+ for (const entry of this.entries) {
471
+ callback(entry.value);
472
+ }
473
+ }
474
+ /**
475
+ * Set or update a cached value.
476
+ * If key already exists, updates value and increments frequency.
477
+ * If cache exceeds capacity, triggers eviction.
478
+ */
479
+ set(key, value) {
480
+ const prefixedKey = this.prefixKey(key);
481
+ const existing = this.lookup.get(prefixedKey);
482
+ if (existing) {
483
+ existing.value = value;
484
+ existing.frequency++;
485
+ existing.lastTimestamp = nextCounter();
486
+ return;
487
+ }
488
+ if (this.entries.length >= this.capacity) {
489
+ this.evictEntries();
490
+ }
491
+ const entry = {
492
+ originalKey: key,
493
+ value,
494
+ frequency: 1,
495
+ lastTimestamp: nextCounter()
496
+ };
497
+ this.entries.push(entry);
498
+ this.lookup.set(prefixedKey, entry);
499
+ }
500
+ /**
501
+ * Delete a cached entry. Removes it immediately from both the lookup map
502
+ * and the entries array so the GC can reclaim the value without waiting
503
+ * for the next eviction sweep.
504
+ */
505
+ del(key) {
506
+ const prefixedKey = this.prefixKey(key);
507
+ const entry = this.lookup.get(prefixedKey);
508
+ if (!entry) return;
509
+ this.lookup.delete(prefixedKey);
510
+ const idx = this.entries.indexOf(entry);
511
+ if (idx !== -1) this.entries.splice(idx, 1);
512
+ if (this.onRemove) {
513
+ this.onRemove(key);
514
+ }
515
+ }
516
+ /**
517
+ * Check if a key exists in cache.
518
+ */
519
+ has(key) {
520
+ return this.lookup.has(this.prefixKey(key));
521
+ }
522
+ /** Get current cache size */
523
+ get size() {
524
+ return this.entries.length;
525
+ }
526
+ /** Clear all entries */
527
+ clear() {
528
+ if (this.onRemove) {
529
+ for (const entry of this.entries) {
530
+ this.onRemove(entry.originalKey);
531
+ }
532
+ }
533
+ this.entries = [];
534
+ this.lookup.clear();
535
+ }
536
+ /**
537
+ * Evict the `bufferSize` worst entries from the cache.
538
+ *
539
+ * Uses single-pass partial selection (O(n·k)) instead of sorting the entire
540
+ * `entries` array (O(n log n)). For the typical `bufferSize = 5` this is
541
+ * effectively a linear scan with at most 5 in-bucket comparisons per
542
+ * iteration — and it avoids mutating the rest of `entries`.
543
+ */
544
+ evictEntries() {
545
+ const entries = this.entries;
546
+ const k = this.bufferSize;
547
+ if (k <= 0 || entries.length === 0) return;
548
+ if (entries.length <= k) {
549
+ for (const e of entries) {
550
+ this.lookup.delete(this.prefixKey(e.originalKey));
551
+ if (this.onRemove) this.onRemove(e.originalKey);
552
+ }
553
+ this.entries = [];
554
+ return;
555
+ }
556
+ const cmp = this.comparator;
557
+ const worst = [];
558
+ for (const entry of entries) {
559
+ if (worst.length < k) {
560
+ let i = worst.length;
561
+ while (i > 0 && cmp(entry, worst[i - 1]) > 0) i--;
562
+ worst.splice(i, 0, entry);
563
+ } else if (cmp(entry, worst[k - 1]) > 0) {
564
+ worst.pop();
565
+ let i = worst.length;
566
+ while (i > 0 && cmp(entry, worst[i - 1]) > 0) i--;
567
+ worst.splice(i, 0, entry);
568
+ }
569
+ }
570
+ const evictSet = new Set(worst);
571
+ for (const e of worst) {
572
+ this.lookup.delete(this.prefixKey(e.originalKey));
573
+ if (this.onRemove) this.onRemove(e.originalKey);
574
+ }
575
+ this.entries = entries.filter((e) => !evictSet.has(e));
576
+ }
577
+ };
578
+
579
+ // src/event-delegator.ts
580
+ var rootEvents = {};
581
+ var selectorEvents = {};
582
+ var rangeEvents = {};
583
+ var rangeFrames = {};
584
+ var elementGuid = 0;
585
+ var eventInfoCache = new Cache({
586
+ maxSize: 30,
587
+ bufferSize: 10
588
+ });
589
+ var frameGetter;
590
+ function parseEventInfo(eventInfo) {
591
+ const cached = eventInfoCache.get(eventInfo);
592
+ if (cached) {
593
+ return assign({}, cached, { value: eventInfo });
594
+ }
595
+ const match = eventInfo.match(EVENT_METHOD_REGEXP) || [];
596
+ const result = {
597
+ id: match[1] || "",
598
+ name: match[2] || "",
599
+ params: match[3] || ""
600
+ };
601
+ eventInfoCache.set(eventInfo, result);
602
+ return assign({}, result, { value: eventInfo });
603
+ }
604
+ function findFrameInfo(current, eventType) {
605
+ const eventInfos = [];
606
+ const info = current.getAttribute(`@${eventType}`);
607
+ const hasSelectorEvents = !!selectorEvents[eventType];
608
+ if (!info && !hasSelectorEvents) {
609
+ return eventInfos;
610
+ }
611
+ let begin = current;
612
+ let match;
613
+ if (info) {
614
+ match = parseEventInfo(info);
615
+ }
616
+ if (match && !match.id || hasSelectorEvents) {
617
+ let selectorFrameId = "#";
618
+ let backtrace = 0;
619
+ while (begin && begin !== document.body) {
620
+ const beginId = begin.id;
621
+ if (beginId && frameGetter?.(beginId)) {
622
+ selectorFrameId = beginId;
623
+ break;
624
+ }
625
+ begin = begin.parentElement;
626
+ }
627
+ const currentId = current.id;
628
+ if (currentId && frameGetter?.(currentId)) {
629
+ backtrace = 1;
630
+ selectorFrameId = currentId;
631
+ }
632
+ let frameId = selectorFrameId;
633
+ do {
634
+ const frame = frameId ? frameGetter?.(frameId) : void 0;
635
+ if (frame) {
636
+ const view = frame.view;
637
+ if (view) {
638
+ const selectorEntry = view.eventSelectorMap[eventType];
639
+ if (selectorEntry) {
640
+ for (const selectorName of selectorEntry.selectors) {
641
+ const entry = {
642
+ value: selectorName,
643
+ id: frameId,
644
+ name: selectorName,
645
+ params: ""
646
+ };
647
+ if (selectorName) {
648
+ if (!backtrace && elementMatchesSelector(current, selectorName)) {
649
+ eventInfos.push(entry);
650
+ }
651
+ } else if (backtrace) {
652
+ eventInfos.unshift(entry);
653
+ }
654
+ }
655
+ }
656
+ if (view.template && !backtrace) {
657
+ if (match && !match.id) {
658
+ match.id = frameId;
659
+ }
660
+ break;
661
+ }
662
+ backtrace = 0;
663
+ }
664
+ }
665
+ if (frame) {
666
+ frameId = frame.parentId || "";
667
+ } else {
668
+ break;
669
+ }
670
+ } while (frameId);
671
+ }
672
+ if (match) {
673
+ eventInfos.push({
674
+ id: match.id,
675
+ value: match.value,
676
+ name: match.name,
677
+ params: match.params
678
+ });
679
+ }
680
+ return eventInfos;
681
+ }
682
+ function elementMatchesSelector(element, selector) {
683
+ try {
684
+ return element.matches?.(selector) ?? false;
685
+ } catch {
686
+ return false;
687
+ }
688
+ }
689
+ function domEventProcessor(domEvent) {
690
+ const target = domEvent.target;
691
+ const eventType = domEvent.type;
692
+ let lastFrameId = "";
693
+ let current = target;
694
+ while (current && current !== document.body) {
695
+ const eventInfos = findFrameInfo(current, eventType);
696
+ if (eventInfos.length) {
697
+ for (const info of eventInfos) {
698
+ const { id: frameId, name: handlerName, params } = info;
699
+ if (lastFrameId !== frameId) {
700
+ if (lastFrameId && domEvent.isPropagationStopped?.()) {
701
+ break;
702
+ }
703
+ lastFrameId = frameId;
704
+ }
705
+ const frame = frameId ? frameGetter?.(frameId) : void 0;
706
+ const view = frame?.view;
707
+ if (view) {
708
+ const eventName = handlerName + SPLITTER + eventType;
709
+ const fn = Reflect.get(view, eventName);
710
+ if (fn) {
711
+ const extendedEvent = domEvent;
712
+ extendedEvent.eventTarget = target;
713
+ extendedEvent.params = params ? parseUri(params).params : {};
714
+ funcWithTry(fn, [extendedEvent], view, noop);
715
+ }
716
+ }
717
+ }
718
+ }
719
+ const rangeFrameId = current.getAttribute("data-range-fid");
720
+ const rangeGuid = current.getAttribute("data-range-guid");
721
+ if (rangeFrameId && rangeGuid) {
722
+ const rangeMap = rangeEvents[rangeFrameId];
723
+ if (rangeMap?.[rangeGuid]?.[eventType]) {
724
+ break;
725
+ }
726
+ }
727
+ if (domEvent.isPropagationStopped?.()) {
728
+ break;
729
+ }
730
+ current = current.parentElement;
731
+ }
732
+ }
733
+ var EventDelegator = {
734
+ /**
735
+ * Bind a DOM event type to document body.
736
+ */
737
+ bind(eventType, hasSelector = false) {
738
+ const counter = rootEvents[eventType] || 0;
739
+ if (counter === 0) {
740
+ document.body.addEventListener(eventType, domEventProcessor, true);
741
+ }
742
+ rootEvents[eventType] = counter + 1;
743
+ if (hasSelector) {
744
+ selectorEvents[eventType] = (selectorEvents[eventType] || 0) + 1;
745
+ }
746
+ },
747
+ /**
748
+ * Unbind a DOM event type from document body.
749
+ */
750
+ unbind(eventType, hasSelector = false) {
751
+ const counter = rootEvents[eventType] || 0;
752
+ if (counter <= 1) {
753
+ document.body.removeEventListener(eventType, domEventProcessor, true);
754
+ Reflect.deleteProperty(rootEvents, eventType);
755
+ } else {
756
+ rootEvents[eventType] = counter - 1;
757
+ }
758
+ if (hasSelector) {
759
+ const selectorCounter = selectorEvents[eventType] || 0;
760
+ if (selectorCounter <= 1) {
761
+ Reflect.deleteProperty(selectorEvents, eventType);
762
+ } else {
763
+ selectorEvents[eventType] = selectorCounter - 1;
764
+ }
765
+ }
766
+ },
767
+ /**
768
+ * Clean up range events for a destroyed view.
769
+ */
770
+ clearRangeEvents(viewId) {
771
+ Reflect.deleteProperty(rangeEvents, viewId);
772
+ Reflect.deleteProperty(rangeFrames, viewId);
773
+ },
774
+ /**
775
+ * Set the frame getter function (called by Framework.boot).
776
+ */
777
+ setFrameGetter(getter) {
778
+ frameGetter = getter;
779
+ },
780
+ /**
781
+ * Get next element GUID.
782
+ */
783
+ nextElementGuid() {
784
+ return ++elementGuid;
785
+ }
786
+ };
787
+
788
+ // src/safeguard.ts
789
+ var proxiesPool = /* @__PURE__ */ new Map();
790
+ var SAFEGUARD_SENTINEL = "_safe_";
791
+ function safeguard(data, getter, setter, isRoot) {
792
+ if (typeof window.__lark_Debug === "undefined" || !window.__lark_Debug) {
793
+ return data;
794
+ }
795
+ if (typeof Proxy === "undefined") {
796
+ return data;
797
+ }
798
+ if (isPrimitive(data)) {
799
+ return data;
800
+ }
801
+ const build = (prefix, obj) => {
802
+ const cacheKey = (getter || "") + "" + (setter || "");
803
+ const cached = proxiesPool.get(obj);
804
+ if (cached && cached.cacheKey === cacheKey) {
805
+ return cached.entity;
806
+ }
807
+ if (Reflect.get(obj, SAFEGUARD_SENTINEL)) {
808
+ return obj;
809
+ }
810
+ const entity = new Proxy(obj, {
811
+ set(target, property, value) {
812
+ if (!setter && !prefix) {
813
+ throw new Error(
814
+ "Avoid write back, key: " + prefix + property + " value:" + value + " more: https://github.com/hangtiancheng/lark"
815
+ );
816
+ }
817
+ Reflect.set(target, property, value);
818
+ if (setter) {
819
+ setter(prefix + property, value);
820
+ }
821
+ return true;
822
+ },
823
+ get(target, property) {
824
+ if (property === SAFEGUARD_SENTINEL) {
825
+ return true;
826
+ }
827
+ const out = Reflect.get(target, property);
828
+ if (!prefix && getter) {
829
+ getter(property);
830
+ }
831
+ if (!isRoot && hasOwnProperty(target, property) && (Array.isArray(out) || isPlainObject(out))) {
832
+ return build(prefix + property + ".", out);
833
+ }
834
+ return out;
835
+ }
836
+ });
837
+ proxiesPool.set(obj, { cacheKey, entity });
838
+ return entity;
839
+ };
840
+ return build("", data);
841
+ }
842
+
843
+ // src/module-loader.ts
844
+ var config = {
845
+ rootId: "root",
846
+ routeMode: "history",
847
+ hashbang: "#!",
848
+ error: (error) => {
849
+ throw error;
850
+ }
851
+ };
852
+ function use(names, callback) {
853
+ const nameList = typeof names === "string" ? [names] : names;
854
+ const loadPromise = (() => {
855
+ if (config.require) {
856
+ const result = config.require(nameList);
857
+ if (result && typeof result.then === "function") {
858
+ return result;
859
+ }
860
+ return Promise.resolve([]);
861
+ }
862
+ return Promise.all(
863
+ nameList.map((name) => {
864
+ const importPath = name.startsWith(".") || name.startsWith("/") ? name : `./${name}`;
865
+ return import(
866
+ /* @vite-ignore */
867
+ /* webpackIgnore: true */
868
+ importPath
869
+ ).then((mod) => {
870
+ return mod && (mod["__esModule"] || // For Webpack
871
+ typeof mod["default"] === "function") ? mod["default"] : mod;
872
+ }).catch((err) => {
873
+ const errorHandler = config.error;
874
+ if (errorHandler) {
875
+ errorHandler(err instanceof Error ? err : new Error(String(err)));
876
+ }
877
+ return void 0;
878
+ });
879
+ })
880
+ );
881
+ })();
882
+ if (callback) {
883
+ loadPromise.then((modules) => {
884
+ callback(...modules);
885
+ });
886
+ }
887
+ return loadPromise;
888
+ }
889
+
890
+ // src/dom.ts
891
+ var wrapMeta = {
892
+ option: [1, "<select multiple>"],
893
+ thead: [1, "<table>"],
894
+ col: [2, "<table><colgroup>"],
895
+ tr: [2, "<table><tbody>"],
896
+ td: [3, "<table><tbody><tr>"],
897
+ area: [1, "<map>"],
898
+ param: [1, "<object>"],
899
+ svg: [1, '<svg xmlns="' + SVG_NS + '">'],
900
+ math: [1, '<math xmlns="' + MATH_NS + '">'],
901
+ _: [0, ""]
902
+ };
903
+ wrapMeta["optgroup"] = wrapMeta["option"];
904
+ wrapMeta["tbody"] = wrapMeta["tfoot"] = wrapMeta["colgroup"] = wrapMeta["caption"] = wrapMeta["thead"];
905
+ wrapMeta["th"] = wrapMeta["td"];
906
+ var VDoc = document.implementation.createHTMLDocument("");
907
+ var VBase = VDoc.createElement("base");
908
+ VBase.href = document.location.href;
909
+ VDoc.head.appendChild(VBase);
910
+ var DomSpecials = {
911
+ INPUT: ["value", "checked"],
912
+ TEXTAREA: ["value"],
913
+ OPTION: ["selected"]
914
+ };
915
+ function domUnmountFrames(frame, node) {
916
+ if (!(node instanceof Element)) return;
917
+ const id = node.getAttribute("id");
918
+ if (!id) return;
919
+ frame.unmountZone(id);
920
+ if (frame.children().includes(id)) {
921
+ frame.unmountFrame(id);
922
+ }
923
+ }
924
+ function domGetNode(html, refNode) {
925
+ const tmp = VDoc.createElement("div");
926
+ const ns = refNode.namespaceURI;
927
+ let tag;
928
+ if (ns === SVG_NS) {
929
+ tag = "svg";
930
+ } else if (ns === MATH_NS) {
931
+ tag = "math";
932
+ } else {
933
+ const match = TAG_NAME_REGEXP.exec(html);
934
+ tag = match ? match[1] : "";
935
+ }
936
+ const wrap = wrapMeta[tag] || wrapMeta["_"];
937
+ tmp.innerHTML = wrap[1] + html;
938
+ let j = wrap[0];
939
+ while (j--) {
940
+ const last = tmp.lastChild;
941
+ if (last) tmp.replaceChildren(last);
942
+ }
943
+ return tmp;
944
+ }
945
+ function domGetCompareKey(node) {
946
+ if (node.nodeType !== 1) return void 0;
947
+ const el = node;
948
+ if (el.compareKeyCached) {
949
+ return el.cachedCompareKey;
950
+ }
951
+ let key = el.autoId ? "" : el.getAttribute("id") || void 0;
952
+ if (!key) {
953
+ const larkView = el.getAttribute(LARK_VIEW);
954
+ if (larkView) {
955
+ key = parseUri(larkView).path || void 0;
956
+ }
957
+ }
958
+ el.compareKeyCached = 1;
959
+ el.cachedCompareKey = key || "";
960
+ return key;
961
+ }
962
+ function domSpecialDiff(oldNode, newNode) {
963
+ const specials = DomSpecials[oldNode.nodeName];
964
+ if (!specials) return 0;
965
+ let result = 0;
966
+ for (const prop of specials) {
967
+ if (Reflect.get(oldNode, prop) !== Reflect.get(newNode, prop)) {
968
+ result = 1;
969
+ Reflect.set(oldNode, prop, Reflect.get(newNode, prop));
970
+ }
971
+ }
972
+ return result;
973
+ }
974
+ function domSetAttributes(oldNode, newNode, ref, keepId) {
975
+ const oldEl = oldNode;
976
+ Reflect.deleteProperty(oldEl, "compareKeyCached");
977
+ const oldAttrs = oldNode.attributes;
978
+ const newAttrs = newNode.attributes;
979
+ for (let i = oldAttrs.length; i--; ) {
980
+ const name = oldAttrs[i].name;
981
+ if (!newNode.hasAttribute(name)) {
982
+ if (name === "id") {
983
+ if (!keepId) {
984
+ ref.idUpdates.push([oldNode, ""]);
985
+ }
986
+ } else {
987
+ ref.hasChanged = 1;
988
+ oldNode.removeAttribute(name);
989
+ }
990
+ }
991
+ }
992
+ for (let i = newAttrs.length; i--; ) {
993
+ const attr = newAttrs[i];
994
+ const key = attr.name;
995
+ const value = attr.value;
996
+ if (oldNode.getAttribute(key) !== value) {
997
+ if (key === "id") {
998
+ ref.idUpdates.push([oldNode, value]);
999
+ } else {
1000
+ ref.hasChanged = 1;
1001
+ oldNode.setAttribute(key, value);
1002
+ }
1003
+ }
1004
+ }
1005
+ }
1006
+ function domSetChildNodes(oldParent, newParent, ref, frame, keys_) {
1007
+ let oldNode = oldParent.lastChild;
1008
+ let newNode = newParent.firstChild;
1009
+ let extra = 0;
1010
+ const keyedNodes = /* @__PURE__ */ new Map();
1011
+ const newKeyedNodes = /* @__PURE__ */ new Map();
1012
+ while (oldNode) {
1013
+ extra++;
1014
+ const nodeKey = domGetCompareKey(oldNode);
1015
+ if (nodeKey) {
1016
+ let bucket = keyedNodes.get(nodeKey);
1017
+ if (!bucket) {
1018
+ bucket = [];
1019
+ keyedNodes.set(nodeKey, bucket);
1020
+ }
1021
+ bucket.push(oldNode);
1022
+ }
1023
+ oldNode = oldNode.previousSibling;
1024
+ }
1025
+ while (newNode) {
1026
+ const nodeKey = domGetCompareKey(newNode);
1027
+ if (nodeKey) {
1028
+ newKeyedNodes.set(nodeKey, (newKeyedNodes.get(nodeKey) ?? 0) + 1);
1029
+ }
1030
+ newNode = newNode.nextSibling;
1031
+ }
1032
+ newNode = newParent.firstChild;
1033
+ oldNode = oldParent.firstChild;
1034
+ while (newNode) {
1035
+ extra--;
1036
+ const tempNew = newNode;
1037
+ newNode = newNode.nextSibling;
1038
+ const nodeKey = domGetCompareKey(tempNew);
1039
+ let foundNode = nodeKey ? keyedNodes.get(nodeKey) : void 0;
1040
+ if (foundNode && (foundNode = foundNode.slice()) && foundNode.length) {
1041
+ const matched = foundNode.pop();
1042
+ while (matched !== oldNode) {
1043
+ if (!oldNode) break;
1044
+ const next = oldNode.nextSibling;
1045
+ oldParent.appendChild(oldNode);
1046
+ oldNode = next;
1047
+ }
1048
+ oldNode = matched.nextSibling;
1049
+ if (nodeKey) {
1050
+ const c = newKeyedNodes.get(nodeKey);
1051
+ if (c) newKeyedNodes.set(nodeKey, c - 1);
1052
+ }
1053
+ domSetNode(matched, tempNew, oldParent, ref, frame, keys_);
1054
+ } else if (oldNode) {
1055
+ const tempOld2 = oldNode;
1056
+ const oldKey = domGetCompareKey(tempOld2);
1057
+ if (oldKey && keyedNodes.has(oldKey) && newKeyedNodes.get(oldKey)) {
1058
+ extra++;
1059
+ ref.hasChanged = 1;
1060
+ ref.domOps.push([8, oldParent, tempNew, tempOld2]);
1061
+ } else {
1062
+ oldNode = oldNode.nextSibling;
1063
+ domSetNode(tempOld2, tempNew, oldParent, ref, frame, keys_);
1064
+ }
1065
+ } else {
1066
+ ref.hasChanged = 1;
1067
+ ref.domOps.push([1, oldParent, tempNew]);
1068
+ }
1069
+ }
1070
+ let tempOld = oldParent.lastChild;
1071
+ while (extra-- > 0) {
1072
+ if (tempOld) {
1073
+ domUnmountFrames(frame, tempOld);
1074
+ ref.domOps.push([2, oldParent, tempOld]);
1075
+ tempOld = tempOld.previousSibling;
1076
+ ref.hasChanged = 1;
1077
+ }
1078
+ }
1079
+ }
1080
+ function domSetNode(oldNode, newNode, oldParent, ref, frame, keys_) {
1081
+ const oldAsEl = oldNode instanceof Element ? oldNode : null;
1082
+ const newAsEl = newNode instanceof Element ? newNode : null;
1083
+ const equalAsNodes = oldAsEl !== null && newAsEl !== null && oldAsEl.isEqualNode && oldAsEl.isEqualNode(newAsEl);
1084
+ if (domSpecialDiff(oldNode, newNode) || !equalAsNodes) {
1085
+ if (oldNode.nodeType === newNode.nodeType && oldNode.nodeName === newNode.nodeName) {
1086
+ if (oldAsEl !== null && newAsEl !== null) {
1087
+ const oldEl = oldAsEl;
1088
+ const newEl = newAsEl;
1089
+ const newLarkView = newEl.getAttribute(LARK_VIEW);
1090
+ let updateChildren = true;
1091
+ if (newLarkView) {
1092
+ const oldFrameId = oldEl.getAttribute("id") || "";
1093
+ const newViewPath = parseUri(newLarkView).path;
1094
+ const oldLarkView = oldEl.getAttribute(LARK_VIEW);
1095
+ const oldViewPath = oldLarkView ? parseUri(oldLarkView).path : "";
1096
+ if (oldFrameId && newViewPath === oldViewPath) {
1097
+ updateChildren = false;
1098
+ }
1099
+ }
1100
+ domSetAttributes(oldEl, newEl, ref, !!newLarkView);
1101
+ if (updateChildren) {
1102
+ domSetChildNodes(oldEl, newEl, ref, frame, keys_);
1103
+ }
1104
+ } else if (oldNode.nodeValue !== newNode.nodeValue) {
1105
+ ref.hasChanged = 1;
1106
+ oldNode.nodeValue = newNode.nodeValue;
1107
+ }
1108
+ } else {
1109
+ ref.hasChanged = 1;
1110
+ domUnmountFrames(frame, oldNode);
1111
+ ref.domOps.push([4, oldParent, newNode, oldNode]);
1112
+ }
1113
+ }
1114
+ }
1115
+ function createDomRef() {
1116
+ return {
1117
+ idUpdates: [],
1118
+ views: [],
1119
+ domOps: [],
1120
+ hasChanged: 0
1121
+ };
1122
+ }
1123
+ function applyDomOps(ops) {
1124
+ for (const op of ops) {
1125
+ switch (op[0]) {
1126
+ case 1:
1127
+ op[1].appendChild(op[2]);
1128
+ break;
1129
+ case 2:
1130
+ op[1].removeChild(op[2]);
1131
+ break;
1132
+ case 4:
1133
+ op[1].replaceChild(op[2], op[3]);
1134
+ break;
1135
+ case 8:
1136
+ op[1].insertBefore(op[2], op[3]);
1137
+ break;
1138
+ }
1139
+ }
1140
+ }
1141
+ function applyIdUpdates(updates) {
1142
+ for (const [element, newId] of updates) {
1143
+ if (newId) {
1144
+ element.setAttribute("id", newId);
1145
+ } else {
1146
+ element.removeAttribute("id");
1147
+ }
1148
+ }
1149
+ }
1150
+
1151
+ // src/vdom.ts
1152
+ var DOM_SPECIALS = {
1153
+ INPUT: ["value", "checked"],
1154
+ TEXTAREA: ["value"],
1155
+ OPTION: ["selected"]
1156
+ };
1157
+ function isSameVDomNode(a, b) {
1158
+ return a.compareKey && b.compareKey === a.compareKey || !a.compareKey && !b.compareKey && a.tag === b.tag || a.tag === SPLITTER || b.tag === SPLITTER;
1159
+ }
1160
+ function vdomCreateNode(vnode, owner, ref) {
1161
+ const tag = vnode.tag;
1162
+ if (tag === V_TEXT_NODE) {
1163
+ return document.createTextNode(vnode.html);
1164
+ }
1165
+ const sTag = typeof tag === "string" ? tag : tag.toString();
1166
+ const ns = VDOM_NS_MAP[sTag] || owner.namespaceURI;
1167
+ const el = document.createElementNS(ns, sTag);
1168
+ vdomSetAttributes(el, vnode, ref);
1169
+ el.innerHTML = vnode.html;
1170
+ return el;
1171
+ }
1172
+ function vdomSetAttributes(realNode, newVDom, ref, lastVDom) {
1173
+ let changed = 0;
1174
+ const nMap = newVDom.attrsMap || {};
1175
+ const nsMap = newVDom.attrsSpecials || {};
1176
+ if (lastVDom) {
1177
+ const oMap = lastVDom.attrsMap || {};
1178
+ const osMap = lastVDom.attrsSpecials || {};
1179
+ for (const key in oMap) {
1180
+ if (!hasOwnProperty(nMap, key)) {
1181
+ changed = 1;
1182
+ const sValue = osMap[key];
1183
+ if (sValue) {
1184
+ if (ref) {
1185
+ ref.nodeProps.push([realNode, sValue, ""]);
1186
+ } else {
1187
+ Reflect.set(realNode, sValue, "");
1188
+ }
1189
+ } else {
1190
+ realNode.removeAttribute(key);
1191
+ }
1192
+ }
1193
+ }
1194
+ }
1195
+ for (const key in nMap) {
1196
+ const value = nMap[key];
1197
+ const sKey = nsMap[key];
1198
+ if (sKey) {
1199
+ if (Reflect.get(realNode, sKey) !== value) {
1200
+ changed = 1;
1201
+ if (ref) {
1202
+ ref.nodeProps.push([realNode, sKey, value]);
1203
+ } else {
1204
+ Reflect.set(realNode, sKey, value);
1205
+ }
1206
+ }
1207
+ } else {
1208
+ const oldMap = lastVDom?.attrsMap;
1209
+ if (!oldMap || oldMap[key] !== value) {
1210
+ changed = 1;
1211
+ realNode.setAttribute(key, String(value ?? ""));
1212
+ }
1213
+ }
1214
+ }
1215
+ return changed;
1216
+ }
1217
+ function vdomSyncFormState(realNode, newVDom) {
1218
+ const specials = DOM_SPECIALS[realNode.nodeName];
1219
+ if (!specials) return 0;
1220
+ const nMap = newVDom.attrsMap || {};
1221
+ let result = 0;
1222
+ for (const prop of specials) {
1223
+ const newVal = nMap[prop];
1224
+ if (newVal !== void 0 && Reflect.get(realNode, prop) !== newVal) {
1225
+ result = 1;
1226
+ Reflect.set(realNode, prop, newVal);
1227
+ }
1228
+ }
1229
+ return result;
1230
+ }
1231
+ function vdomSetNode(realNode, oldParent, lastVDom, newVDom, ref, frame, keys, rootView, ready) {
1232
+ const lastTag = lastVDom.tag;
1233
+ const newTag = newVDom.tag;
1234
+ if (lastTag === V_TEXT_NODE || newTag === V_TEXT_NODE) {
1235
+ if (lastTag === newTag) {
1236
+ if (lastVDom.html !== newVDom.html) {
1237
+ ref.changed = 1;
1238
+ realNode.nodeValue = newVDom.html;
1239
+ }
1240
+ } else {
1241
+ ref.changed = 1;
1242
+ domUnmountFrames(frame, realNode);
1243
+ oldParent.replaceChild(vdomCreateNode(newVDom, oldParent, ref), realNode);
1244
+ }
1245
+ return;
1246
+ }
1247
+ if (lastTag === newTag) {
1248
+ if (lastVDom.attrs === newVDom.attrs && lastVDom.html === newVDom.html) {
1249
+ if (newVDom.hasSpecials) {
1250
+ vdomSyncFormState(realNode, newVDom);
1251
+ }
1252
+ return;
1253
+ }
1254
+ let attrChanged = 0;
1255
+ if (lastVDom.attrs !== newVDom.attrs || newVDom.hasSpecials) {
1256
+ attrChanged = vdomSetAttributes(
1257
+ realNode,
1258
+ newVDom,
1259
+ ref,
1260
+ lastVDom
1261
+ );
1262
+ if (attrChanged) ref.changed = 1;
1263
+ }
1264
+ let updateChildren = true;
1265
+ if (newVDom.isLarkView) {
1266
+ const oldFrameId = realNode.getAttribute("id") || "";
1267
+ const newViewPath = newVDom.isLarkView;
1268
+ const oldViewPath = lastVDom.isLarkView || "";
1269
+ if (oldFrameId && newViewPath === oldViewPath) {
1270
+ updateChildren = false;
1271
+ }
1272
+ }
1273
+ vdomSyncFormState(realNode, newVDom);
1274
+ if (updateChildren && !newVDom.selfClose) {
1275
+ vdomSetChildNodes(
1276
+ realNode,
1277
+ lastVDom,
1278
+ newVDom,
1279
+ ref,
1280
+ frame,
1281
+ keys,
1282
+ rootView,
1283
+ ready
1284
+ );
1285
+ }
1286
+ } else {
1287
+ ref.changed = 1;
1288
+ domUnmountFrames(frame, realNode);
1289
+ oldParent.replaceChild(vdomCreateNode(newVDom, oldParent, ref), realNode);
1290
+ }
1291
+ }
1292
+ function computeLIS(sequence) {
1293
+ const len = sequence.length;
1294
+ if (len === 0) return [];
1295
+ const result = [];
1296
+ const tails = [];
1297
+ const predecessors = new Array(len);
1298
+ let lisLength = 0;
1299
+ for (let i = 0; i < len; i++) {
1300
+ const value = sequence[i];
1301
+ if (value < 0) continue;
1302
+ let lo = 0;
1303
+ let hi = lisLength;
1304
+ while (lo < hi) {
1305
+ const mid = lo + hi >>> 1;
1306
+ if (sequence[tails[mid]] < value) lo = mid + 1;
1307
+ else hi = mid;
1308
+ }
1309
+ tails[lo] = i;
1310
+ predecessors[i] = lo > 0 ? tails[lo - 1] : -1;
1311
+ if (lo === lisLength) lisLength++;
1312
+ }
1313
+ let cursor = tails[lisLength - 1];
1314
+ for (let i = lisLength - 1; i >= 0; i--) {
1315
+ result[i] = cursor;
1316
+ cursor = predecessors[cursor];
1317
+ }
1318
+ return result;
1319
+ }
1320
+ function vdomSetChildNodes(realNode, lastVDom, newVDom, ref, frame, keys, view, ready) {
1321
+ if (!lastVDom) {
1322
+ ref.changed = 1;
1323
+ realNode.innerHTML = newVDom.html;
1324
+ callFunction(ready, []);
1325
+ return;
1326
+ }
1327
+ if (lastVDom.html === newVDom.html) {
1328
+ callFunction(ready, []);
1329
+ return;
1330
+ }
1331
+ const oldChildren = lastVDom.children;
1332
+ const newChildren = newVDom.children;
1333
+ const oldLen = oldChildren?.length || 0;
1334
+ const newLen = newChildren?.length || 0;
1335
+ if (oldLen === 0 && newLen === 0) {
1336
+ callFunction(ready, []);
1337
+ return;
1338
+ }
1339
+ const nodes = realNode.childNodes;
1340
+ const oldDomNodes = new Array(oldLen);
1341
+ for (let i = 0; i < oldLen; i++) {
1342
+ oldDomNodes[i] = nodes[i];
1343
+ }
1344
+ const usedOldDomNodes = /* @__PURE__ */ new Set();
1345
+ let headIdx = 0;
1346
+ let tailIdx = oldLen - 1;
1347
+ let newHead = 0;
1348
+ let newTail = newLen - 1;
1349
+ while (headIdx <= tailIdx && newHead <= newTail) {
1350
+ const oc = oldChildren[headIdx];
1351
+ const nc = newChildren[newHead];
1352
+ if (!isSameVDomNode(nc, oc)) break;
1353
+ if (nc.tag === SPLITTER || oc.tag === SPLITTER) break;
1354
+ vdomSetNode(
1355
+ oldDomNodes[headIdx],
1356
+ realNode,
1357
+ oc,
1358
+ nc,
1359
+ ref,
1360
+ frame,
1361
+ keys,
1362
+ view,
1363
+ ready
1364
+ );
1365
+ usedOldDomNodes.add(oldDomNodes[headIdx]);
1366
+ headIdx++;
1367
+ newHead++;
1368
+ }
1369
+ while (headIdx <= tailIdx && newHead <= newTail) {
1370
+ const oc = oldChildren[tailIdx];
1371
+ const nc = newChildren[newTail];
1372
+ if (!isSameVDomNode(nc, oc)) break;
1373
+ if (nc.tag === SPLITTER || oc.tag === SPLITTER) break;
1374
+ vdomSetNode(
1375
+ oldDomNodes[tailIdx],
1376
+ realNode,
1377
+ oc,
1378
+ nc,
1379
+ ref,
1380
+ frame,
1381
+ keys,
1382
+ view,
1383
+ ready
1384
+ );
1385
+ usedOldDomNodes.add(oldDomNodes[tailIdx]);
1386
+ tailIdx--;
1387
+ newTail--;
1388
+ }
1389
+ if (headIdx > tailIdx && newHead > newTail) {
1390
+ if (ref.asyncCount === 0) callFunction(ready, []);
1391
+ return;
1392
+ }
1393
+ const keyMap = {};
1394
+ for (let i = headIdx; i <= tailIdx; i++) {
1395
+ const c = oldChildren[i];
1396
+ if (c?.compareKey) {
1397
+ if (!keyMap[c.compareKey]) keyMap[c.compareKey] = [];
1398
+ keyMap[c.compareKey].push({ domNode: oldDomNodes[i], vdomNode: c });
1399
+ }
1400
+ }
1401
+ const newRemaining = newTail - newHead + 1;
1402
+ const sequence = new Array(newRemaining);
1403
+ for (let i = 0; i < newRemaining; i++) {
1404
+ const nc = newChildren[newHead + i];
1405
+ const cKey = nc.compareKey;
1406
+ const entries = cKey ? keyMap[cKey] : void 0;
1407
+ if (entries && entries.length > 0) {
1408
+ const entry = entries.shift();
1409
+ if (entries.length === 0) delete keyMap[cKey];
1410
+ const oldIdx = oldChildren.indexOf(entry.vdomNode, headIdx);
1411
+ sequence[i] = oldIdx >= 0 ? oldIdx : -1;
1412
+ usedOldDomNodes.add(entry.domNode);
1413
+ } else {
1414
+ sequence[i] = -1;
1415
+ }
1416
+ }
1417
+ if (newHead > newTail) {
1418
+ for (let i = 0; i < oldLen; i++) {
1419
+ const domNode = oldDomNodes[i];
1420
+ if (domNode && !usedOldDomNodes.has(domNode) && domNode.parentNode === realNode) {
1421
+ domUnmountFrames(frame, domNode);
1422
+ ref.changed = 1;
1423
+ realNode.removeChild(domNode);
1424
+ }
1425
+ }
1426
+ if (ref.asyncCount === 0) callFunction(ready, []);
1427
+ return;
1428
+ }
1429
+ if (headIdx > tailIdx) {
1430
+ const insertRef = tailIdx < oldLen ? oldDomNodes[tailIdx + 1] ?? null : null;
1431
+ for (let i = newHead; i <= newTail; i++) {
1432
+ ref.changed = 1;
1433
+ const newNode = vdomCreateNode(newChildren[i], realNode, ref);
1434
+ realNode.insertBefore(newNode, insertRef);
1435
+ }
1436
+ if (ref.asyncCount === 0) callFunction(ready, []);
1437
+ return;
1438
+ }
1439
+ const lis = computeLIS(sequence);
1440
+ let lisCursor = lis.length - 1;
1441
+ let nextNode = tailIdx + 1 < oldLen ? oldDomNodes[tailIdx + 1] : null;
1442
+ for (let j = newRemaining - 1; j >= 0; j--) {
1443
+ const newIdx = newHead + j;
1444
+ const nc = newChildren[newIdx];
1445
+ if (lisCursor >= 0 && lis[lisCursor] === j) {
1446
+ const oldIdx = sequence[j];
1447
+ vdomSetNode(
1448
+ oldDomNodes[oldIdx],
1449
+ realNode,
1450
+ oldChildren[oldIdx],
1451
+ nc,
1452
+ ref,
1453
+ frame,
1454
+ keys,
1455
+ view,
1456
+ ready
1457
+ );
1458
+ nextNode = oldDomNodes[oldIdx];
1459
+ lisCursor--;
1460
+ } else if (sequence[j] >= 0) {
1461
+ const oldIdx = sequence[j];
1462
+ ref.changed = 1;
1463
+ realNode.insertBefore(oldDomNodes[oldIdx], nextNode);
1464
+ vdomSetNode(
1465
+ oldDomNodes[oldIdx],
1466
+ realNode,
1467
+ oldChildren[oldIdx],
1468
+ nc,
1469
+ ref,
1470
+ frame,
1471
+ keys,
1472
+ view,
1473
+ ready
1474
+ );
1475
+ nextNode = oldDomNodes[oldIdx];
1476
+ } else {
1477
+ ref.changed = 1;
1478
+ const newNode = vdomCreateNode(nc, realNode, ref);
1479
+ realNode.insertBefore(newNode, nextNode);
1480
+ nextNode = newNode;
1481
+ }
1482
+ }
1483
+ for (let i = 0; i < oldLen; i++) {
1484
+ const domNode = oldDomNodes[i];
1485
+ if (domNode && !usedOldDomNodes.has(domNode) && domNode.parentNode === realNode) {
1486
+ domUnmountFrames(frame, domNode);
1487
+ ref.changed = 1;
1488
+ realNode.removeChild(domNode);
1489
+ }
1490
+ }
1491
+ if (ref.asyncCount === 0) {
1492
+ callFunction(ready, []);
1493
+ }
1494
+ }
1495
+ function createVDomRef(viewId) {
1496
+ return {
1497
+ viewId,
1498
+ viewRenders: [],
1499
+ nodeProps: [],
1500
+ asyncCount: 0,
1501
+ changed: 0,
1502
+ domOps: []
1503
+ };
1504
+ }
1505
+
1506
+ // src/updater.ts
1507
+ var Updater = class {
1508
+ /** View ID (same as owner frame ID) */
1509
+ viewId;
1510
+ /** Current data object */
1511
+ data;
1512
+ /** Ref data for template rendering */
1513
+ refData;
1514
+ /** Changed keys in current digest cycle */
1515
+ changedKeys = /* @__PURE__ */ new Set();
1516
+ /** Whether data has changed since last digest */
1517
+ hasChangedFlag = 0;
1518
+ /**
1519
+ * Digesting queue: supports re-digest during digest.
1520
+ * Holds pending callbacks; `null` is used as a sentinel marking the start
1521
+ * of an active digest cycle, so `runDigest` can detect re-entrant calls.
1522
+ */
1523
+ digestingQueue = [];
1524
+ /** Monotonically increasing version, bumped each time data actually changes. */
1525
+ version = 0;
1526
+ /** Snapshot of `version` taken by `snapshot()`, used by `altered()`. */
1527
+ snapshotVersion;
1528
+ /** Last rendered VDOM tree (only used when virtualDom is enabled) */
1529
+ vdom;
1530
+ constructor(viewId) {
1531
+ this.viewId = viewId;
1532
+ this.data = { vId: viewId };
1533
+ const refCounter = {};
1534
+ refCounter[SPLITTER] = 1;
1535
+ this.refData = refCounter;
1536
+ this.hasChangedFlag = 1;
1537
+ }
1538
+ /**
1539
+ * Get data by key.
1540
+ * Returns entire data object if key is omitted.
1541
+ */
1542
+ get(key) {
1543
+ let result = this.data;
1544
+ if (key) {
1545
+ result = this.data[key];
1546
+ }
1547
+ if (typeof window !== "undefined" && window.__lark_Debug) {
1548
+ return safeguard(result);
1549
+ }
1550
+ return result;
1551
+ }
1552
+ /**
1553
+ * Set data, tracking changed keys.
1554
+ * Returns this for chaining.
1555
+ */
1556
+ set(data, excludes) {
1557
+ const changed = setData(
1558
+ data,
1559
+ this.data,
1560
+ this.changedKeys,
1561
+ excludes || EMPTY_STRING_SET
1562
+ );
1563
+ if (changed) {
1564
+ this.version++;
1565
+ this.hasChangedFlag = 1;
1566
+ }
1567
+ return this;
1568
+ }
1569
+ /**
1570
+ * Detect changes and trigger DOM re-render.
1571
+ *
1572
+ * The core rendering pipeline:
1573
+ * 1. Set data if provided
1574
+ * 2. If changed, run DOM diff (template → new DOM → diff against old DOM)
1575
+ * 3. Apply DOM operations
1576
+ * 4. Apply ID updates
1577
+ * 5. Call endUpdate on views that need re-rendering
1578
+ * 6. Support re-digest during digest via queue
1579
+ */
1580
+ digest(data, excludes, callback) {
1581
+ if (data) {
1582
+ this.set(data, excludes);
1583
+ }
1584
+ const digesting = this.digestingQueue;
1585
+ if (callback) {
1586
+ digesting.push(callback);
1587
+ }
1588
+ if (digesting.length > 0 && digesting[0] === null) {
1589
+ return;
1590
+ }
1591
+ this.runDigest(digesting);
1592
+ }
1593
+ /**
1594
+ * Core digest execution.
1595
+ */
1596
+ runDigest(digesting) {
1597
+ const startIndex = digesting.length;
1598
+ digesting.push(null);
1599
+ const keys = this.changedKeys;
1600
+ const changed = this.hasChangedFlag;
1601
+ this.hasChangedFlag = 0;
1602
+ this.changedKeys = /* @__PURE__ */ new Set();
1603
+ const frame = Frame.get(this.viewId);
1604
+ const view = frame?.view;
1605
+ const node = getById(this.viewId);
1606
+ if (changed && view && node && view.signature > 0 && frame) {
1607
+ const template = view.template;
1608
+ if (typeof template === "function") {
1609
+ if (config.virtualDom) {
1610
+ const vdomTemplate = template;
1611
+ const newVDom = vdomTemplate(this.data, this.viewId, this.refData);
1612
+ const ref = createVDomRef(this.viewId);
1613
+ const ready = () => {
1614
+ this.vdom = newVDom;
1615
+ if (ref.changed || !view.rendered) {
1616
+ view.endUpdate(this.viewId);
1617
+ }
1618
+ for (const [el, prop, val] of ref.nodeProps) {
1619
+ Reflect.set(el, prop, val);
1620
+ }
1621
+ for (const v of ref.viewRenders) {
1622
+ if (v.render) {
1623
+ funcWithTry(v.render, [], v, noop);
1624
+ }
1625
+ }
1626
+ };
1627
+ vdomSetChildNodes(
1628
+ node,
1629
+ this.vdom,
1630
+ newVDom,
1631
+ ref,
1632
+ frame,
1633
+ keys,
1634
+ view,
1635
+ ready
1636
+ );
1637
+ } else {
1638
+ const html = template(
1639
+ this.data,
1640
+ this.viewId,
1641
+ this.refData,
1642
+ encodeHTML,
1643
+ strSafe,
1644
+ encodeURIExtra,
1645
+ refFn,
1646
+ encodeQuote
1647
+ );
1648
+ const newDom = domGetNode(html, node);
1649
+ const ref = createDomRef();
1650
+ domSetChildNodes(node, newDom, ref, frame, keys);
1651
+ applyIdUpdates(ref.idUpdates);
1652
+ applyDomOps(ref.domOps);
1653
+ for (const v of ref.views) {
1654
+ if (v.render) {
1655
+ funcWithTry(v.render, [], v, noop);
1656
+ }
1657
+ }
1658
+ if (ref.hasChanged || !view.rendered) {
1659
+ view.endUpdate(this.viewId);
1660
+ }
1661
+ }
1662
+ }
1663
+ }
1664
+ if (digesting.length > startIndex + 1) {
1665
+ this.runDigest(digesting);
1666
+ } else {
1667
+ const callbacks = digesting.slice();
1668
+ digesting.length = 0;
1669
+ for (const cb of callbacks) {
1670
+ if (cb) cb();
1671
+ }
1672
+ }
1673
+ }
1674
+ /**
1675
+ * Save a snapshot of the current data version for `altered()` detection.
1676
+ * Cheap O(1) — records the current monotonic version, no serialization.
1677
+ */
1678
+ snapshot() {
1679
+ this.snapshotVersion = this.version;
1680
+ return this;
1681
+ }
1682
+ /**
1683
+ * Check whether data has changed since the last snapshot.
1684
+ * Returns undefined when no snapshot has been taken yet.
1685
+ */
1686
+ altered() {
1687
+ if (this.snapshotVersion === void 0) return void 0;
1688
+ return this.version !== this.snapshotVersion;
1689
+ }
1690
+ /**
1691
+ * Translate a refData reference back to its original value.
1692
+ *
1693
+ * The ref protocol is `SPLITTER` + ascii decimal digits — the exact format
1694
+ * emitted by `refFn`. We require that exact shape so a user-supplied
1695
+ * string that merely begins with SPLITTER is never accidentally resolved
1696
+ * (or mishandled as a "missing ref").
1697
+ */
1698
+ translate(data) {
1699
+ if (typeof data !== "string" || !isRefToken(data)) return data;
1700
+ return hasOwnProperty(this.refData, data) ? this.refData[data] : data;
1701
+ }
1702
+ /**
1703
+ * Resolve a dotted property path against refData.
1704
+ *
1705
+ * Only safe property-path syntax is supported: `a`, `a.b`, `a.b.c`.
1706
+ * Numeric literals (e.g. `1`, `1.5`) are returned as numbers. Anything else
1707
+ * returns `undefined` — we no longer evaluate arbitrary JavaScript via
1708
+ * `new Function`, so the method is CSP-safe and cannot be used as an
1709
+ * injection vector.
1710
+ */
1711
+ parse(expr) {
1712
+ const trimmed = expr.trim();
1713
+ if (!trimmed) return void 0;
1714
+ if (/^-?\d+(?:\.\d+)?$/.test(trimmed)) {
1715
+ return Number(trimmed);
1716
+ }
1717
+ if (!/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*$/.test(trimmed)) {
1718
+ return void 0;
1719
+ }
1720
+ let cur = this.refData;
1721
+ for (const segment of trimmed.split(".")) {
1722
+ if (cur == null || typeof cur !== "object") return void 0;
1723
+ cur = cur[segment];
1724
+ }
1725
+ return cur;
1726
+ }
1727
+ /**
1728
+ * Get the set of keys changed since the last digest (for external inspection).
1729
+ */
1730
+ getChangedKeys() {
1731
+ return this.changedKeys;
1732
+ }
1733
+ };
1734
+
1735
+ // src/router.ts
1736
+ var emitter = new EventEmitter();
1737
+ var hrefCache = new Cache();
1738
+ var changedCache = new Cache();
1739
+ var lastLocation = createEmptyLocation();
1740
+ var lastChanged;
1741
+ var silent = 0;
1742
+ var booted = false;
1743
+ var cachedRoutes;
1744
+ var cachedUnmatchedView;
1745
+ var cachedDefaultView;
1746
+ var cachedDefaultPath;
1747
+ var cachedRewrite;
1748
+ var defaultTitle;
1749
+ var frameworkConfig;
1750
+ var routeMode = "history";
1751
+ var beforeEachGuards = [];
1752
+ function createEmptyLocation() {
1753
+ return {
1754
+ href: "",
1755
+ srcQuery: "",
1756
+ srcHash: "",
1757
+ query: { path: "", params: {} },
1758
+ hash: { path: "", params: {} },
1759
+ params: {},
1760
+ get: (_key, defaultValue) => defaultValue ?? ""
1761
+ };
1762
+ }
1763
+ function getParam(key, defaultValue) {
1764
+ return this["params"][key] || (defaultValue !== void 0 ? defaultValue : "");
1765
+ }
1766
+ function attachViewAndPath(loc) {
1767
+ if (!frameworkConfig) return;
1768
+ if (!cachedRoutes) {
1769
+ cachedRoutes = frameworkConfig.routes || {};
1770
+ cachedUnmatchedView = frameworkConfig.unmatchedView;
1771
+ cachedDefaultView = frameworkConfig.defaultView;
1772
+ cachedDefaultPath = frameworkConfig.defaultPath || "/";
1773
+ cachedRewrite = frameworkConfig.rewrite;
1774
+ }
1775
+ if (!loc.view) {
1776
+ const rawPath = routeMode === "history" ? loc.query["path"] || loc.hash["path"] : loc.hash["path"];
1777
+ let path = rawPath || cachedDefaultPath || "/";
1778
+ if (!cachedRoutes[path] && path === "/" && cachedDefaultPath && cachedDefaultPath !== "/") {
1779
+ path = cachedDefaultPath;
1780
+ }
1781
+ if (cachedRewrite) {
1782
+ path = cachedRewrite(
1783
+ path,
1784
+ loc["params"],
1785
+ cachedRoutes
1786
+ );
1787
+ }
1788
+ const viewEntry = cachedRoutes[path] || cachedUnmatchedView || cachedDefaultView;
1789
+ loc["path"] = path;
1790
+ loc.view = typeof viewEntry === "string" ? viewEntry : viewEntry?.view || "";
1791
+ if (typeof viewEntry === "object" && viewEntry) {
1792
+ assign(loc, viewEntry);
1793
+ }
1794
+ }
1795
+ }
1796
+ function getChanged(oldLoc, newLoc) {
1797
+ const oKey = oldLoc.href;
1798
+ const nKey = newLoc.href;
1799
+ const tKey = oKey + SPLITTER + nKey;
1800
+ const cached = changedCache.get(tKey);
1801
+ if (cached) {
1802
+ return cached;
1803
+ }
1804
+ let hasChanged = false;
1805
+ const changedParams = {};
1806
+ const setDiff = (key, oldVal, newVal) => {
1807
+ const from = oldVal || "";
1808
+ const to = newVal || "";
1809
+ if (from !== to) {
1810
+ changedParams[key] = { from, to };
1811
+ hasChanged = true;
1812
+ }
1813
+ };
1814
+ const allParamKeys = /* @__PURE__ */ new Set([
1815
+ ...Object.keys(oldLoc["params"]),
1816
+ ...Object.keys(newLoc["params"])
1817
+ ]);
1818
+ for (const key of allParamKeys) {
1819
+ setDiff(key, oldLoc["params"][key], newLoc["params"][key]);
1820
+ }
1821
+ const result = {
1822
+ ["params"]: changedParams,
1823
+ force: !oKey,
1824
+ changed: hasChanged
1825
+ };
1826
+ setDiff("path", oldLoc["path"], newLoc["path"]);
1827
+ if (changedParams["path"]) {
1828
+ result["path"] = changedParams["path"];
1829
+ result.changed = true;
1830
+ }
1831
+ const viewKey = "view";
1832
+ setDiff(viewKey, oldLoc.view, newLoc.view);
1833
+ if (changedParams[viewKey]) {
1834
+ result.view = changedParams[viewKey];
1835
+ result.changed = true;
1836
+ }
1837
+ const finalResult = {
1838
+ changed: hasChanged,
1839
+ diff: result
1840
+ };
1841
+ changedCache.set(tKey, finalResult);
1842
+ return finalResult;
1843
+ }
1844
+ function updateBrowserUrl(path, replace) {
1845
+ if (routeMode === "history") {
1846
+ const url = path || "/";
1847
+ const currentUrl = window.location.pathname + window.location.search;
1848
+ if (url === currentUrl) {
1849
+ return;
1850
+ }
1851
+ if (replace) {
1852
+ window.history.replaceState(null, "", url);
1853
+ } else {
1854
+ window.history.pushState(null, "", url);
1855
+ }
1856
+ return;
1857
+ }
1858
+ const hashbang = frameworkConfig?.hashbang || "#!";
1859
+ const fullPath = path === "" ? "" : hashbang + path;
1860
+ if (replace) {
1861
+ window.location.replace(fullPath);
1862
+ } else {
1863
+ window.location.hash = fullPath;
1864
+ }
1865
+ }
1866
+ function updateUrl(path, params, loc, replace, silentFlag, lQuery) {
1867
+ path = toUri(path, params, lQuery);
1868
+ const currentSrc = routeMode === "history" ? loc.srcQuery : loc.srcHash;
1869
+ if (path !== currentSrc) {
1870
+ silent = silentFlag ? 1 : 0;
1871
+ updateBrowserUrl(path, replace);
1872
+ if (routeMode === "history" && Router.notify) {
1873
+ Router.notify();
1874
+ }
1875
+ }
1876
+ }
1877
+ var Router = {
1878
+ /**
1879
+ * Parse href into Location object.
1880
+ * Defaults to window.location.href.
1881
+ */
1882
+ parse(href) {
1883
+ href = href || window.location.href;
1884
+ const cached = hrefCache.get(href);
1885
+ if (cached) {
1886
+ return cached;
1887
+ }
1888
+ let srcQuery;
1889
+ let srcHash;
1890
+ let query;
1891
+ let hash;
1892
+ if (routeMode === "history") {
1893
+ try {
1894
+ const urlObj = new URL(href, window.location.origin);
1895
+ srcQuery = urlObj.pathname + urlObj.search;
1896
+ srcHash = urlObj.hash ? urlObj.hash.replace(/^#!?/, "") : "";
1897
+ query = parseUri(srcQuery);
1898
+ hash = srcHash ? parseUri(srcHash) : { path: "", params: {} };
1899
+ } catch {
1900
+ srcQuery = href.replace(URL_TRIM_HASH_REGEXP, "");
1901
+ srcHash = href.replace(URL_TRIM_QUERY_REGEXP, "");
1902
+ query = parseUri(srcQuery);
1903
+ hash = parseUri(srcHash);
1904
+ }
1905
+ } else {
1906
+ srcQuery = href.replace(URL_TRIM_HASH_REGEXP, "");
1907
+ srcHash = href.replace(URL_TRIM_QUERY_REGEXP, "");
1908
+ query = parseUri(srcQuery);
1909
+ hash = parseUri(srcHash);
1910
+ }
1911
+ const params = assign({}, query["params"], hash["params"]);
1912
+ const location = {
1913
+ href,
1914
+ srcQuery,
1915
+ srcHash,
1916
+ query: { path: query.path, params: query["params"] },
1917
+ hash: { path: hash.path, params: hash["params"] },
1918
+ ["params"]: params,
1919
+ get: getParam
1920
+ };
1921
+ if (booted) {
1922
+ attachViewAndPath(location);
1923
+ hrefCache.set(href, location);
1924
+ }
1925
+ if (typeof window.__lark_Debug !== "undefined" && window.__lark_Debug) {
1926
+ location["params"] = safeguard(location["params"]);
1927
+ }
1928
+ return location;
1929
+ },
1930
+ /**
1931
+ * Compute diff between current and previous location.
1932
+ * Fires 'changed' event if location changed and not silent.
1933
+ */
1934
+ diff() {
1935
+ const location = Router.parse();
1936
+ const changed = getChanged(lastLocation, location);
1937
+ lastLocation = location;
1938
+ if (!silent && changed.changed) {
1939
+ lastChanged = changed.diff;
1940
+ if (lastChanged["path"]) {
1941
+ document.title = defaultTitle || document.title;
1942
+ }
1943
+ emitter.fire(RouterEvents.CHANGED, asRecord(lastChanged));
1944
+ }
1945
+ silent = 0;
1946
+ if (typeof window.__lark_Debug !== "undefined" && window.__lark_Debug && lastChanged) {
1947
+ lastChanged = safeguard(lastChanged);
1948
+ }
1949
+ return lastChanged;
1950
+ },
1951
+ /**
1952
+ * Navigate to new URL.
1953
+ *
1954
+ * @param pathOrParams - Path string or params object
1955
+ * @param params - Query parameters (if pathOrParams is string)
1956
+ * @param replace - Whether to replace current history entry
1957
+ * @param silentFlag - Whether to silently update without firing events
1958
+ */
1959
+ to(pathOrParams, params, replace, silentFlag) {
1960
+ let tPath = "";
1961
+ let tParams;
1962
+ if (!params && typeof pathOrParams === "object") {
1963
+ tParams = pathOrParams;
1964
+ } else {
1965
+ const parsed = parseUri(pathOrParams);
1966
+ tPath = parsed.path;
1967
+ tParams = { ...parsed["params"] };
1968
+ if (params) {
1969
+ assign(tParams, params);
1970
+ }
1971
+ }
1972
+ const lPath = lastLocation["path"] || "";
1973
+ const lParams = lastLocation["params"];
1974
+ const lQuery = /* @__PURE__ */ new Set();
1975
+ for (const k in lastLocation.query["params"]) {
1976
+ if (hasOwnProperty(lastLocation.query["params"], k)) {
1977
+ lQuery.add(k);
1978
+ }
1979
+ }
1980
+ if (tPath) {
1981
+ if (routeMode === "hash" && !hasOwnProperty(window, "history")) {
1982
+ for (const qKey of lQuery) {
1983
+ if (!hasOwnProperty(tParams, qKey)) {
1984
+ tParams[qKey] = "";
1985
+ }
1986
+ }
1987
+ }
1988
+ } else if (lParams) {
1989
+ tPath = lPath;
1990
+ tParams = assign({}, lParams, tParams);
1991
+ }
1992
+ updateUrl(tPath, tParams, lastLocation, replace, silentFlag, lQuery);
1993
+ },
1994
+ /**
1995
+ * Register an async-friendly navigation guard. See `RouterInterface.beforeEach`.
1996
+ */
1997
+ beforeEach(guard) {
1998
+ beforeEachGuards.push(guard);
1999
+ return () => {
2000
+ const idx = beforeEachGuards.indexOf(guard);
2001
+ if (idx !== -1) beforeEachGuards.splice(idx, 1);
2002
+ };
2003
+ },
2004
+ /**
2005
+ * Join multiple path segments into a single path.
2006
+ */
2007
+ join(...paths) {
2008
+ let result = paths.join("/");
2009
+ result = result.replace(/\/\.\//g, "/");
2010
+ const doubleDotRegExp = /\/[^/]+\/\.\.\//;
2011
+ while (doubleDotRegExp.test(result)) {
2012
+ result = result.replace(doubleDotRegExp, "/");
2013
+ }
2014
+ result = result.replace(/\/{2,}/g, "/");
2015
+ return result;
2016
+ },
2017
+ /** Bind event listener */
2018
+ on(event, handler) {
2019
+ emitter.on(event, handler);
2020
+ return Router;
2021
+ },
2022
+ /** Unbind event listener */
2023
+ off(event, handler) {
2024
+ emitter.off(event, handler);
2025
+ return Router;
2026
+ },
2027
+ /** Fire event */
2028
+ fire(event, data, remove) {
2029
+ emitter.fire(event, data, remove);
2030
+ return Router;
2031
+ },
2032
+ /**
2033
+ * Internal: bind routing events and beforeunload.
2034
+ * Called by Framework.boot().
2035
+ * In hash mode, listens to hashchange + popstate.
2036
+ * In history mode, listens to popstate only.
2037
+ */
2038
+ _bind() {
2039
+ defaultTitle = document.title;
2040
+ const getLocationKey = () => {
2041
+ if (routeMode === "history") {
2042
+ return window.location.pathname + window.location.search;
2043
+ }
2044
+ return Router.parse().srcHash;
2045
+ };
2046
+ let lastKey = getLocationKey();
2047
+ let suspend;
2048
+ const watchChange = () => {
2049
+ if (suspend) {
2050
+ return;
2051
+ }
2052
+ hrefCache.clear();
2053
+ const loc = Router.parse();
2054
+ const newKey = routeMode === "history" ? loc.srcQuery : loc.srcHash;
2055
+ if (newKey !== lastKey) {
2056
+ const changeEvent = {
2057
+ p: 0,
2058
+ reject: () => {
2059
+ changeEvent.p = 1;
2060
+ suspend = "";
2061
+ updateBrowserUrl(lastKey);
2062
+ },
2063
+ resolve: () => {
2064
+ changeEvent.p = 1;
2065
+ lastKey = newKey;
2066
+ suspend = "";
2067
+ updateBrowserUrl(newKey);
2068
+ Router.diff();
2069
+ },
2070
+ prevent: () => {
2071
+ suspend = 1;
2072
+ }
2073
+ };
2074
+ Router.fire(RouterEvents.CHANGE, changeEvent);
2075
+ if (suspend || changeEvent.p) {
2076
+ return;
2077
+ }
2078
+ if (beforeEachGuards.length === 0) {
2079
+ changeEvent.resolve();
2080
+ return;
2081
+ }
2082
+ const from = lastLocation;
2083
+ const to = loc;
2084
+ const guards = beforeEachGuards.slice();
2085
+ let chain = Promise.resolve(true);
2086
+ for (const guard of guards) {
2087
+ chain = chain.then((prev) => {
2088
+ if (prev === false) return false;
2089
+ return guard(to, from);
2090
+ });
2091
+ }
2092
+ chain.then(
2093
+ (result) => {
2094
+ if (changeEvent.p) return;
2095
+ if (result === false) {
2096
+ changeEvent.reject();
2097
+ } else {
2098
+ changeEvent.resolve();
2099
+ }
2100
+ },
2101
+ () => {
2102
+ if (!changeEvent.p) changeEvent.reject();
2103
+ }
2104
+ );
2105
+ }
2106
+ };
2107
+ Router.notify = watchChange;
2108
+ if (routeMode === "history") {
2109
+ window.addEventListener("popstate", watchChange);
2110
+ } else {
2111
+ window.addEventListener("hashchange", watchChange);
2112
+ window.addEventListener("popstate", watchChange);
2113
+ }
2114
+ window.addEventListener("beforeunload", (domEvent) => {
2115
+ const data = {};
2116
+ Router.fire(RouterEvents.PAGE_UNLOAD, data);
2117
+ const msg = data["msg"];
2118
+ if (msg) {
2119
+ domEvent.returnValue = msg;
2120
+ }
2121
+ });
2122
+ Router.diff();
2123
+ },
2124
+ /**
2125
+ * Internal: set framework config for route resolution.
2126
+ */
2127
+ _setConfig(cfg) {
2128
+ frameworkConfig = cfg;
2129
+ routeMode = cfg.routeMode || "history";
2130
+ }
2131
+ };
2132
+
2133
+ // src/view-registry.ts
2134
+ var viewClassRegistry = {};
2135
+ function getViewClass(path) {
2136
+ return viewClassRegistry[path];
2137
+ }
2138
+ function registerViewClass(viewPath, ViewClass) {
2139
+ const parsed = parseUri(viewPath);
2140
+ const path = parsed.path;
2141
+ if (path) {
2142
+ viewClassRegistry[path] = ViewClass;
2143
+ }
2144
+ }
2145
+ function invalidateViewClass(viewPath) {
2146
+ const parsed = parseUri(viewPath);
2147
+ const path = parsed.path;
2148
+ if (path) {
2149
+ Reflect.deleteProperty(viewClassRegistry, path);
2150
+ }
2151
+ }
2152
+
2153
+ // src/hmr.ts
2154
+ function reloadViews(viewPath) {
2155
+ const allFrames = Frame.getAll();
2156
+ const toReload = [];
2157
+ for (const [, frame] of allFrames) {
2158
+ if (frame.viewPath) {
2159
+ const parsed = parseUri(frame.viewPath);
2160
+ if (parsed.path === viewPath) {
2161
+ toReload.push({ frame, fullPath: frame.viewPath });
2162
+ }
2163
+ }
2164
+ }
2165
+ for (const { frame, fullPath } of toReload) {
2166
+ frame.mountView(fullPath);
2167
+ }
2168
+ }
2169
+ function acceptView(hot, viewPath) {
2170
+ hot.accept((newModule) => {
2171
+ const candidate = newModule?.default ?? newModule;
2172
+ if (typeof candidate === "function") {
2173
+ const NewViewClass = candidate;
2174
+ registerViewClass(viewPath, NewViewClass);
2175
+ reloadViews(viewPath);
2176
+ } else {
2177
+ hot.invalidate();
2178
+ }
2179
+ });
2180
+ }
2181
+ function disposeView(hot, viewPath) {
2182
+ hot.dispose(() => {
2183
+ invalidateViewClass(viewPath);
2184
+ });
2185
+ }
2186
+
2187
+ // src/view.ts
2188
+ var VIEW_GLOBALS = {};
2189
+ if (typeof window !== "undefined") {
2190
+ VIEW_GLOBALS["window"] = window;
2191
+ }
2192
+ if (typeof document !== "undefined") {
2193
+ VIEW_GLOBALS["document"] = document;
2194
+ }
2195
+ var View = class _View {
2196
+ /** View ID (same as owner frame ID) */
2197
+ id = "";
2198
+ /** Owner frame */
2199
+ owner = 0;
2200
+ /** Updater instance */
2201
+ updater;
2202
+ /** Signature: > 0 means active, incremented on render, 0 = destroyed */
2203
+ signature = 0;
2204
+ /** Whether rendered at least once */
2205
+ rendered;
2206
+ /** Whether view has template */
2207
+ template;
2208
+ /** Location observation config */
2209
+ locationObserved = {
2210
+ flag: 0,
2211
+ keys: [],
2212
+ observePath: false
2213
+ };
2214
+ /** Observed state keys */
2215
+ observedStateKeys;
2216
+ /** Resource map */
2217
+ resources = {};
2218
+ /** Whether endUpdate pending */
2219
+ endUpdatePending;
2220
+ /** Internal event storage */
2221
+ _events = new EventEmitter();
2222
+ // ============================================================
2223
+ // Getters for prototype-stored event maps
2224
+ // ============================================================
2225
+ /** Prototype-stored event maps shape (set by View.prepare). */
2226
+ get protoEventState() {
2227
+ return Object.getPrototypeOf(this);
2228
+ }
2229
+ /**
2230
+ * Event bitmask map: eventType -> bitmask (1=root, 2=selector).
2231
+ * Read from prototype ($evtObjMap) set by View.prepare.
2232
+ * Using a getter avoids ES6 class field shadowing the prototype value.
2233
+ */
2234
+ get eventObjectMap() {
2235
+ return this.protoEventState.$evtObjMap ?? {};
2236
+ }
2237
+ /**
2238
+ * Selector event map: eventType -> selector list.
2239
+ * Read from prototype ($selMap) set by View.prepare.
2240
+ */
2241
+ get eventSelectorMap() {
2242
+ return this.protoEventState.$selMap ?? {};
2243
+ }
2244
+ /**
2245
+ * Global event list: [{handler, element, eventName, modifiers}].
2246
+ * Read from prototype ($globalEvtList) set by View.prepare.
2247
+ */
2248
+ get globalEventList() {
2249
+ return this.protoEventState.$globalEvtList ?? [];
2250
+ }
2251
+ // ============================================================
2252
+ // Instance lifecycle methods
2253
+ // ============================================================
2254
+ /**
2255
+ * Initialize view (called by Frame when mounting).
2256
+ */
2257
+ init() {
2258
+ }
2259
+ /**
2260
+ * Render view template (called by Frame after init).
2261
+ * Wrapped by View.wrapMethod to manage signature + resources.
2262
+ */
2263
+ render() {
2264
+ this.updater.digest();
2265
+ }
2266
+ // ============================================================
2267
+ // Event methods (delegate to internal EventEmitter)
2268
+ // ============================================================
2269
+ on(event, handler) {
2270
+ this._events.on(event, handler);
2271
+ return this;
2272
+ }
2273
+ off(event, handler) {
2274
+ this._events.off(event, handler);
2275
+ return this;
2276
+ }
2277
+ fire(event, data, remove, lastToFirst) {
2278
+ this._events.fire(event, data, remove, lastToFirst);
2279
+ return this;
2280
+ }
2281
+ // ============================================================
2282
+ // Update methods
2283
+ // ============================================================
2284
+ /** Get the owning frame, asserting it has been bound. */
2285
+ get ownerFrame() {
2286
+ return this.owner;
2287
+ }
2288
+ /**
2289
+ * Notify view that HTML update is about to begin.
2290
+ * Unmounts child frames in the update zone.
2291
+ */
2292
+ beginUpdate(id) {
2293
+ if (this.signature > 0 && this.endUpdatePending !== void 0) {
2294
+ this.ownerFrame.unmountZone(id);
2295
+ }
2296
+ }
2297
+ /**
2298
+ * Notify view that HTML update has ended.
2299
+ * Mounts child frames in the update zone and runs deferred invokes.
2300
+ */
2301
+ endUpdate(id, inner) {
2302
+ if (this.signature > 0) {
2303
+ const updateId = id || this.id;
2304
+ let flag;
2305
+ if (inner) {
2306
+ flag = inner;
2307
+ } else {
2308
+ flag = this.endUpdatePending;
2309
+ this.endUpdatePending = 1;
2310
+ this.rendered = true;
2311
+ }
2312
+ const ownerFrame = this.ownerFrame;
2313
+ ownerFrame.mountZone(updateId);
2314
+ if (!flag) {
2315
+ setTimeout(
2316
+ this.wrapAsync(() => {
2317
+ _View.runInvokes(ownerFrame);
2318
+ }),
2319
+ 0
2320
+ );
2321
+ }
2322
+ }
2323
+ }
2324
+ // ============================================================
2325
+ // Async wrapper
2326
+ // ============================================================
2327
+ /**
2328
+ * Wrap an async callback to check view signature before executing.
2329
+ * If the view has been re-rendered or destroyed, the callback is skipped.
2330
+ */
2331
+ wrapAsync(fn, context) {
2332
+ const currentSignature = this.signature;
2333
+ return (...args) => {
2334
+ if (currentSignature > 0 && currentSignature === this.signature) {
2335
+ return fn.apply(context || this, args);
2336
+ }
2337
+ return void 0;
2338
+ };
2339
+ }
2340
+ // ============================================================
2341
+ // Location observation
2342
+ // ============================================================
2343
+ /**
2344
+ * Observe location parameters or path changes.
2345
+ * When observed keys change, render() is called automatically.
2346
+ */
2347
+ observeLocation(params, observePath = false) {
2348
+ const loc = this.locationObserved;
2349
+ loc.flag = 1;
2350
+ if (typeof params === "object" && !Array.isArray(params)) {
2351
+ const opts = params;
2352
+ if (opts["path"]) {
2353
+ observePath = true;
2354
+ }
2355
+ const paramKeys = opts["params"];
2356
+ if (typeof paramKeys === "string" || Array.isArray(paramKeys)) {
2357
+ params = paramKeys;
2358
+ }
2359
+ }
2360
+ loc.observePath = observePath;
2361
+ if (params) {
2362
+ if (typeof params === "string") {
2363
+ loc.keys = params.split(",");
2364
+ } else if (Array.isArray(params)) {
2365
+ loc.keys = params;
2366
+ }
2367
+ }
2368
+ }
2369
+ // ============================================================
2370
+ // State observation
2371
+ // ============================================================
2372
+ /**
2373
+ * Observe State data keys for changes.
2374
+ * When observed keys change via State.digest(), render() is called.
2375
+ */
2376
+ observeState(observedKeys) {
2377
+ if (typeof observedKeys === "string") {
2378
+ this.observedStateKeys = observedKeys.split(",");
2379
+ } else {
2380
+ this.observedStateKeys = observedKeys;
2381
+ }
2382
+ }
2383
+ // ============================================================
2384
+ // Resource management
2385
+ // ============================================================
2386
+ /**
2387
+ * Capture (register) a resource under a key.
2388
+ * If a resource already exists at that key, it's destroyed first.
2389
+ * When destroyOnRender=true, the resource is destroyed on next render call.
2390
+ */
2391
+ capture(key, resource, destroyOnRender = false) {
2392
+ const cache = this.resources;
2393
+ if (resource) {
2394
+ _View.destroyResource(cache, key, true, resource);
2395
+ cache[key] = {
2396
+ entity: resource,
2397
+ destroyOnRender
2398
+ };
2399
+ } else {
2400
+ const entry = cache[key];
2401
+ return entry ? entry.entity : void 0;
2402
+ }
2403
+ return resource;
2404
+ }
2405
+ /**
2406
+ * Release a captured resource.
2407
+ * If destroy=true, calls the resource's destroy() method.
2408
+ */
2409
+ release(key, destroy = true) {
2410
+ return _View.destroyResource(this.resources, key, destroy);
2411
+ }
2412
+ // ============================================================
2413
+ // Leave tip
2414
+ // ============================================================
2415
+ /**
2416
+ * Set up a leave confirmation for route changes and page unload.
2417
+ */
2418
+ leaveTip(message, condition) {
2419
+ const changeListener = function(e) {
2420
+ const isRouterChange = e.type === RouterEvents.CHANGE;
2421
+ const aKey = isRouterChange ? "a" : "b";
2422
+ const bKey = isRouterChange ? "b" : "a";
2423
+ if (changeListener[aKey]) {
2424
+ e.prevent?.();
2425
+ e.reject?.();
2426
+ } else if (condition()) {
2427
+ e.prevent?.();
2428
+ changeListener[bKey] = 1;
2429
+ e.resolve?.();
2430
+ }
2431
+ };
2432
+ const unloadListener = (e) => {
2433
+ if (condition()) {
2434
+ e["msg"] = message;
2435
+ }
2436
+ };
2437
+ Router.on(RouterEvents.CHANGE, changeListener);
2438
+ Router.on(RouterEvents.PAGE_UNLOAD, unloadListener);
2439
+ this.on("unload", changeListener);
2440
+ this.on("destroy", () => {
2441
+ Router.off(RouterEvents.CHANGE, changeListener);
2442
+ Router.off(RouterEvents.PAGE_UNLOAD, unloadListener);
2443
+ });
2444
+ }
2445
+ // ============================================================
2446
+ // Static public methods
2447
+ // ============================================================
2448
+ /** Collected makes from mixins */
2449
+ static makes;
2450
+ /**
2451
+ * Prepare a View subclass by scanning its prototype for event method patterns.
2452
+ * Pattern: `$?name<eventType1,eventType2>(&modifiers)`
2453
+ *
2454
+ * Only runs once per View subclass (guarded by makes marker).
2455
+ * Called from Frame.mountView before creating the view instance.
2456
+ */
2457
+ static prepare(oView) {
2458
+ if (oView.makes) {
2459
+ return oView.makes;
2460
+ }
2461
+ const makes = [];
2462
+ oView.makes = makes;
2463
+ const eventsObject = {};
2464
+ const eventsList = [];
2465
+ const selectorObject = {};
2466
+ const mixins = Reflect.get(oView.prototype, "mixins");
2467
+ if (mixins && Array.isArray(mixins)) {
2468
+ _View.mergeMixins(mixins, oView, makes);
2469
+ }
2470
+ for (const p in oView.prototype) {
2471
+ if (!hasOwnProperty(oView.prototype, p)) continue;
2472
+ const currentFn = Reflect.get(oView.prototype, p);
2473
+ if (typeof currentFn !== "function") continue;
2474
+ const matches = p.match(VIEW_EVENT_METHOD_REGEXP);
2475
+ if (!matches) continue;
2476
+ const isSelector = matches[1];
2477
+ const selectorOrCallback = matches[2];
2478
+ const events = matches[3];
2479
+ const modifiers = matches[4];
2480
+ const mod = {};
2481
+ if (modifiers) {
2482
+ for (const item of modifiers.split(",")) {
2483
+ mod[item] = true;
2484
+ }
2485
+ }
2486
+ const eventTypes = events.split(",");
2487
+ for (const item of eventTypes) {
2488
+ const globalNode = VIEW_GLOBALS[selectorOrCallback];
2489
+ let mask = 1;
2490
+ if (isSelector) {
2491
+ if (globalNode) {
2492
+ eventsList.push({
2493
+ handler: currentFn,
2494
+ element: globalNode,
2495
+ eventName: item,
2496
+ modifiers: mod
2497
+ });
2498
+ continue;
2499
+ }
2500
+ mask = 2;
2501
+ let selectorEntry = selectorObject[item];
2502
+ if (!selectorEntry) {
2503
+ selectorEntry = selectorObject[item] = {
2504
+ selectors: []
2505
+ };
2506
+ }
2507
+ if (!selectorEntry[selectorOrCallback]) {
2508
+ selectorEntry[selectorOrCallback] = 1;
2509
+ selectorEntry.selectors.push(selectorOrCallback);
2510
+ }
2511
+ }
2512
+ eventsObject[item] = (eventsObject[item] || 0) | mask;
2513
+ const combinedKey = selectorOrCallback + SPLITTER + item;
2514
+ const existingFn = Reflect.get(oView.prototype, combinedKey);
2515
+ if (!existingFn) {
2516
+ Reflect.set(oView.prototype, combinedKey, currentFn);
2517
+ } else if (typeof existingFn === "function") {
2518
+ const mixinFn = currentFn;
2519
+ const existingMixin = existingFn;
2520
+ if (existingMixin.marker) {
2521
+ if (mixinFn.marker) {
2522
+ Reflect.set(
2523
+ oView.prototype,
2524
+ combinedKey,
2525
+ _View.processMixinsSameEvent(mixinFn, existingMixin)
2526
+ );
2527
+ } else if (hasOwnProperty(oView.prototype, p)) {
2528
+ Reflect.set(oView.prototype, combinedKey, currentFn);
2529
+ }
2530
+ }
2531
+ }
2532
+ }
2533
+ }
2534
+ _View.wrapMethod(asRecord(oView.prototype), "render", "$renderWrap");
2535
+ Reflect.set(oView.prototype, "$evtObjMap", eventsObject);
2536
+ Reflect.set(oView.prototype, "$globalEvtList", eventsList);
2537
+ Reflect.set(oView.prototype, "$selMap", selectorObject);
2538
+ return makes;
2539
+ }
2540
+ /**
2541
+ * Bind or unbind event delegation for a view instance.
2542
+ * Called from Frame during mount/unmount.
2543
+ */
2544
+ static delegateEvents(view, destroy = false) {
2545
+ const eventsObject = view.eventObjectMap;
2546
+ const selectorObject = view.eventSelectorMap;
2547
+ const eventsList = view.globalEventList;
2548
+ for (const e in eventsObject) {
2549
+ if (hasOwnProperty(eventsObject, e)) {
2550
+ if (destroy) {
2551
+ EventDelegator.unbind(e, !!selectorObject[e]);
2552
+ } else {
2553
+ EventDelegator.bind(e, !!selectorObject[e]);
2554
+ }
2555
+ }
2556
+ }
2557
+ for (const entry of eventsList) {
2558
+ if (destroy) {
2559
+ entry.element.removeEventListener(
2560
+ entry.eventName,
2561
+ entry.boundHandler
2562
+ );
2563
+ } else {
2564
+ const handler = entry.handler;
2565
+ const element = entry.element;
2566
+ const modifiers = entry.modifiers;
2567
+ entry.boundHandler = function(domEvent) {
2568
+ const extendedEvent = domEvent;
2569
+ extendedEvent.eventTarget = element;
2570
+ if (modifiers) {
2571
+ const kbEvent = domEvent;
2572
+ if (modifiers["ctrl"] && !kbEvent.ctrlKey || modifiers["shift"] && !kbEvent.shiftKey || modifiers["alt"] && !kbEvent.altKey || modifiers["meta"] && !kbEvent.metaKey) {
2573
+ return;
2574
+ }
2575
+ }
2576
+ funcWithTry(handler, [domEvent], view, noop);
2577
+ };
2578
+ entry.element.addEventListener(
2579
+ entry.eventName,
2580
+ entry.boundHandler
2581
+ );
2582
+ }
2583
+ }
2584
+ }
2585
+ /**
2586
+ * Destroy all resources managed by a view.
2587
+ * If lastly=true, destroy ALL resources; otherwise only destroyOnRender ones.
2588
+ */
2589
+ static destroyAllResources(view, lastly) {
2590
+ const cache = view.resources;
2591
+ for (const p in cache) {
2592
+ if (hasOwnProperty(cache, p)) {
2593
+ const entry = cache[p];
2594
+ if (lastly || entry.destroyOnRender) {
2595
+ _View.destroyResource(cache, p, true);
2596
+ }
2597
+ }
2598
+ }
2599
+ }
2600
+ /**
2601
+ * Process deferred invoke calls on a frame.
2602
+ */
2603
+ static runInvokes(frame) {
2604
+ const list = frame.invokeList;
2605
+ if (!list) return;
2606
+ while (list.length) {
2607
+ const entry = list.shift();
2608
+ if (entry && !entry.removed) {
2609
+ frame.invoke(entry.name, entry.args);
2610
+ }
2611
+ }
2612
+ }
2613
+ // ============================================================
2614
+ // Static private methods
2615
+ // ============================================================
2616
+ /**
2617
+ * Wrap a method on the prototype to add signature checking and resource cleanup.
2618
+ */
2619
+ static wrapMethod(proto, fnName, shortKey) {
2620
+ const originalFn = proto[fnName];
2621
+ if (typeof originalFn !== "function") return;
2622
+ const originalAsFn = originalFn;
2623
+ const wrapped = function(...args) {
2624
+ if (this.signature > 0) {
2625
+ this.signature++;
2626
+ this.fire("render");
2627
+ _View.destroyAllResources(this, false);
2628
+ const lookup = asRecord(this);
2629
+ const candidate = lookup[fnName];
2630
+ const instanceFn = typeof candidate === "function" ? candidate : originalAsFn;
2631
+ const fnToCall = instanceFn === wrapped ? originalAsFn : instanceFn;
2632
+ return funcWithTry(fnToCall, args, this, noop);
2633
+ }
2634
+ return void 0;
2635
+ };
2636
+ proto[fnName] = wrapped;
2637
+ proto[shortKey] = wrapped;
2638
+ }
2639
+ /**
2640
+ * When two mixins define the same event method, merge them into
2641
+ * a single function that calls both in sequence.
2642
+ */
2643
+ static processMixinsSameEvent(additional, exist) {
2644
+ let temp;
2645
+ if (exist.handlerList) {
2646
+ temp = exist;
2647
+ } else {
2648
+ const merged = function(...e) {
2649
+ funcWithTry(merged.handlerList ?? [], e, this, noop);
2650
+ };
2651
+ merged.handlerList = [exist];
2652
+ merged.marker = 1;
2653
+ temp = merged;
2654
+ }
2655
+ temp.handlerList = (temp.handlerList ?? []).concat(
2656
+ additional.handlerList ?? [additional]
2657
+ );
2658
+ return temp;
2659
+ }
2660
+ /**
2661
+ * Merge an array of mixin objects into the view prototype.
2662
+ */
2663
+ static mergeMixins(mixins, viewClass, makes) {
2664
+ const proto = asRecord(viewClass.prototype);
2665
+ const temp = {};
2666
+ for (const node of mixins) {
2667
+ for (const p in node) {
2668
+ if (!hasOwnProperty(node, p)) continue;
2669
+ const fn = node[p];
2670
+ if (typeof fn !== "function") continue;
2671
+ const mixinFn = fn;
2672
+ const exist = temp[p];
2673
+ if (p === "make") {
2674
+ makes.push(mixinFn);
2675
+ continue;
2676
+ }
2677
+ if (VIEW_EVENT_METHOD_REGEXP.test(p)) {
2678
+ if (exist) {
2679
+ temp[p] = _View.processMixinsSameEvent(mixinFn, exist);
2680
+ } else {
2681
+ mixinFn.marker = 1;
2682
+ temp[p] = mixinFn;
2683
+ }
2684
+ } else if (!exist) {
2685
+ temp[p] = mixinFn;
2686
+ }
2687
+ }
2688
+ }
2689
+ for (const p in temp) {
2690
+ if (!hasOwnProperty(proto, p)) {
2691
+ proto[p] = temp[p];
2692
+ }
2693
+ }
2694
+ }
2695
+ /**
2696
+ * Destroy a single resource entry.
2697
+ */
2698
+ static destroyResource(cache, key, callDestroy, oldEntity) {
2699
+ const entry = cache[key];
2700
+ if (!entry || entry.entity === oldEntity) return void 0;
2701
+ const entity = entry.entity;
2702
+ if (entity && typeof entity === "object") {
2703
+ const destroyFn = entity["destroy"];
2704
+ if (typeof destroyFn === "function" && callDestroy) {
2705
+ funcWithTry(destroyFn, [], entity, noop);
2706
+ }
2707
+ }
2708
+ Reflect.deleteProperty(cache, key);
2709
+ return entity;
2710
+ }
2711
+ // ============================================================
2712
+ // Static: extend and merge
2713
+ // ============================================================
2714
+ /**
2715
+ * Extend View to create a new View subclass.
2716
+ *
2717
+ * Supports:
2718
+ * - props.make: constructor-like init (called with initParams + {node, deep})
2719
+ * - props.mixins: array of mixin objects
2720
+ * - Event method patterns: `'name<click>'` etc.
2721
+ */
2722
+ static extend(props, statics) {
2723
+ const definedProps = props ?? {};
2724
+ const make = definedProps["make"];
2725
+ const makes = [];
2726
+ if (typeof make === "function") {
2727
+ makes.push(make);
2728
+ }
2729
+ const ParentView = this;
2730
+ const ChildView = class extends ParentView {
2731
+ constructor(nodeId, ownerFrame, initParams, node, mixinCtors) {
2732
+ super(nodeId, ownerFrame, initParams, node, []);
2733
+ const instanceProps = this;
2734
+ for (const key in definedProps) {
2735
+ if (hasOwnProperty(definedProps, key) && key !== "make" && key !== "render") {
2736
+ instanceProps[key] = definedProps[key];
2737
+ }
2738
+ }
2739
+ this.id = nodeId;
2740
+ this.owner = ownerFrame;
2741
+ this.updater = new Updater(nodeId);
2742
+ const params = [
2743
+ initParams,
2744
+ {
2745
+ node,
2746
+ deep: !this.template
2747
+ }
2748
+ ];
2749
+ const concatCtors = makes.concat(mixinCtors || []);
2750
+ if (concatCtors.length) {
2751
+ funcWithTry(concatCtors, params, this, noop);
2752
+ }
2753
+ }
2754
+ };
2755
+ for (const key in definedProps) {
2756
+ if (hasOwnProperty(definedProps, key) && key !== "make") {
2757
+ Reflect.set(ChildView.prototype, key, definedProps[key]);
2758
+ }
2759
+ }
2760
+ if (statics) {
2761
+ for (const key in statics) {
2762
+ if (hasOwnProperty(statics, key)) {
2763
+ Reflect.set(ChildView, key, statics[key]);
2764
+ }
2765
+ }
2766
+ }
2767
+ return ChildView;
2768
+ }
2769
+ /**
2770
+ * Merge mixins into View prototype.
2771
+ */
2772
+ static merge(...mixins) {
2773
+ const existingCtors = this.makes || [];
2774
+ _View.mergeMixins(mixins, this, existingCtors);
2775
+ return this;
2776
+ }
2777
+ // ============================================================
2778
+ // HMR support (static accept / dispose)
2779
+ // ============================================================
2780
+ /**
2781
+ * Set up HMR accept handler for this view module.
2782
+ *
2783
+ * When the module is hot-replaced, the new View class is extracted from
2784
+ * the new module, registered in the view registry, and all currently
2785
+ * mounted frames using this viewPath are re-mounted.
2786
+ *
2787
+ * No-op when `hot` is undefined (production / non-HMR environment).
2788
+ *
2789
+ * ```ts
2790
+ * if (import.meta.hot) {
2791
+ * HomeView.accept(import.meta.hot, 'home');
2792
+ * }
2793
+ * ```
2794
+ */
2795
+ static accept(hot, viewPath) {
2796
+ if (!hot) return;
2797
+ acceptView(hot, viewPath);
2798
+ }
2799
+ /**
2800
+ * Set up HMR dispose handler for this view module.
2801
+ *
2802
+ * When the module is about to be replaced, the old View class is removed
2803
+ * from the registry so subsequent lookups don't return the stale class.
2804
+ *
2805
+ * No-op when `hot` is undefined (production / non-HMR environment).
2806
+ *
2807
+ * ```ts
2808
+ * if (import.meta.hot) {
2809
+ * HomeView.dispose(import.meta.hot, 'home');
2810
+ * }
2811
+ * ```
2812
+ */
2813
+ static dispose(hot, viewPath) {
2814
+ if (!hot) return;
2815
+ disposeView(hot, viewPath);
2816
+ }
2817
+ };
2818
+
2819
+ // src/frame.ts
2820
+ var frameRegistry = /* @__PURE__ */ new Map();
2821
+ var rootFrame;
2822
+ var globalAlter;
2823
+ var MAX_FRAME_POOL = 64;
2824
+ var frameCache = [];
2825
+ var staticEmitter = new EventEmitter();
2826
+ var Frame = class _Frame extends EventEmitter {
2827
+ /** Frame ID (same as owner DOM element ID) */
2828
+ id;
2829
+ /** Parent Frame ID */
2830
+ _parentId = void 0;
2831
+ get parentId() {
2832
+ return this._parentId;
2833
+ }
2834
+ /** Children map: id -> id */
2835
+ childrenMap = {};
2836
+ /** Children count */
2837
+ childrenCount = 0;
2838
+ /** Ready count (children that have fired 'created') */
2839
+ readyCount = 0;
2840
+ /** Set of child frame IDs that have fired 'created' */
2841
+ readyMap = /* @__PURE__ */ new Set();
2842
+ /** View instance */
2843
+ viewInstance;
2844
+ /** Get view instance (read-only) */
2845
+ get view() {
2846
+ return this.viewInstance;
2847
+ }
2848
+ /** Invoke list for deferred method calls */
2849
+ invokeList = [];
2850
+ /** Signature for async operation tracking */
2851
+ signature = 1;
2852
+ /** Whether view has altered */
2853
+ hasAltered = 0;
2854
+ /** Whether view is destroyed */
2855
+ destroyed = 0;
2856
+ /** View path (v-lark attribute value) */
2857
+ viewPath;
2858
+ /** Original template before mount */
2859
+ originalTemplate;
2860
+ /** Hold fire created flag */
2861
+ holdFireCreated = 0;
2862
+ /** Children created flag */
2863
+ childrenCreated = 0;
2864
+ /** Children alter flag */
2865
+ childrenAlter = 0;
2866
+ constructor(id, parentId) {
2867
+ super();
2868
+ this.id = id;
2869
+ if (parentId) {
2870
+ this._parentId = parentId;
2871
+ }
2872
+ frameRegistry.set(id, this);
2873
+ const element = document.getElementById(id);
2874
+ if (element) {
2875
+ element.frame = this;
2876
+ element.frameBound = 1;
2877
+ }
2878
+ _Frame.fire("add", { frame: this });
2879
+ }
2880
+ // ============================================================
2881
+ // Instance methods
2882
+ // ============================================================
2883
+ /**
2884
+ * Mount a view to this frame.
2885
+ *
2886
+ * Complete flow:
2887
+ * 1. Parse viewPath, translate query params from parent
2888
+ * 2. Unmount current view
2889
+ * 3. Load View class (via require or provided ViewClass)
2890
+ * 4. View_Prepare (scan event methods)
2891
+ * 5. Create View instance
2892
+ * 6. View_DelegateEvents (bind DOM events)
2893
+ * 7. Call view.init()
2894
+ * 8. If view has template, call render via Updater
2895
+ * 9. If no template, call endUpdate directly
2896
+ */
2897
+ mountView(viewPath, viewInitParams) {
2898
+ const node = document.getElementById(this.id);
2899
+ const pId = this.parentId;
2900
+ if (!this.hasAltered && node) {
2901
+ this.hasAltered = 1;
2902
+ this.originalTemplate = node.innerHTML;
2903
+ }
2904
+ this.unmountView();
2905
+ this.destroyed = 0;
2906
+ const parsed = parseUri(viewPath || "");
2907
+ const viewClassName = parsed.path;
2908
+ if (!node || !viewClassName) return;
2909
+ this.viewPath = viewPath;
2910
+ const params = parsed["params"];
2911
+ translateQuery(pId || this.id, viewPath, params);
2912
+ const initParams = { ...params };
2913
+ if (viewInitParams) {
2914
+ assign(initParams, viewInitParams);
2915
+ }
2916
+ const sign = this.signature;
2917
+ const registered = getViewClass(viewClassName);
2918
+ if (registered) {
2919
+ this.doMountView(registered, initParams, node, sign);
2920
+ return;
2921
+ }
2922
+ use(viewClassName, (ViewClass) => {
2923
+ if (sign !== this.signature) return;
2924
+ if (typeof ViewClass === "function") {
2925
+ const ViewClassTyped = ViewClass;
2926
+ registerViewClass(viewClassName, ViewClassTyped);
2927
+ this.doMountView(ViewClassTyped, initParams, node, sign);
2928
+ } else {
2929
+ const error = new Error(`Cannot load view: ${viewClassName}`);
2930
+ const errorHandler = config.error;
2931
+ if (errorHandler) {
2932
+ errorHandler(error);
2933
+ }
2934
+ }
2935
+ });
2936
+ }
2937
+ /**
2938
+ * Internal: actually mount the view after class is loaded.
2939
+ */
2940
+ doMountView(ViewClass, params, node, sign) {
2941
+ if (sign !== this.signature) return;
2942
+ const mixinConstructors = View.prepare(ViewClass);
2943
+ const Constructor = ViewClass;
2944
+ const view = new Constructor(
2945
+ this.id,
2946
+ this,
2947
+ params,
2948
+ node,
2949
+ mixinConstructors
2950
+ );
2951
+ this.viewInstance = view;
2952
+ view.signature = 1;
2953
+ View.delegateEvents(view);
2954
+ const initResult = funcWithTry(
2955
+ view.init,
2956
+ [params, { node, deep: !view.template }],
2957
+ view,
2958
+ noop
2959
+ );
2960
+ const nextSign = ++this.signature;
2961
+ Promise.resolve(initResult).then(() => {
2962
+ if (nextSign !== this.signature) return;
2963
+ if (view.template) {
2964
+ view.render();
2965
+ } else {
2966
+ this.hasAltered = 0;
2967
+ if (!view.endUpdatePendingFlag) {
2968
+ view.endUpdate();
2969
+ }
2970
+ }
2971
+ });
2972
+ }
2973
+ /**
2974
+ * Unmount current view.
2975
+ */
2976
+ unmountView() {
2977
+ const view = this.view;
2978
+ this.invokeList = [];
2979
+ if (!view) return;
2980
+ if (!globalAlter) {
2981
+ globalAlter = { id: this.id };
2982
+ }
2983
+ this.destroyed = 1;
2984
+ this.unmountZone();
2985
+ notifyAlter(this, globalAlter);
2986
+ if (view.signature > 0) {
2987
+ view.fire("destroy", void 0, true, true);
2988
+ }
2989
+ EventDelegator.clearRangeEvents(this.id);
2990
+ delete this["viewInstance"];
2991
+ const node = document.getElementById(this.id);
2992
+ if (node && this.originalTemplate) {
2993
+ node.innerHTML = this.originalTemplate;
2994
+ }
2995
+ globalAlter = void 0;
2996
+ unmark(view);
2997
+ }
2998
+ /**
2999
+ * Mount a child frame.
3000
+ */
3001
+ mountFrame(frameId, viewPath, viewInitParams) {
3002
+ notifyAlter(this, { id: frameId });
3003
+ let childFrame = frameRegistry.get(frameId);
3004
+ if (!childFrame) {
3005
+ if (!this.childrenMap[frameId]) {
3006
+ this.childrenCount++;
3007
+ }
3008
+ this.childrenMap[frameId] = frameId;
3009
+ childFrame = frameCache.pop();
3010
+ if (childFrame) {
3011
+ reInitFrame(childFrame, frameId, this.id);
3012
+ } else {
3013
+ childFrame = new _Frame(frameId, this.id);
3014
+ }
3015
+ }
3016
+ childFrame.mountView(viewPath, viewInitParams);
3017
+ return childFrame;
3018
+ }
3019
+ /**
3020
+ * Unmount a child frame.
3021
+ */
3022
+ unmountFrame(id) {
3023
+ const targetId = id ? this.childrenMap[id] : this.id;
3024
+ const frame = frameRegistry.get(targetId);
3025
+ if (!frame) return;
3026
+ const wasCreated = frame.readyCount > 0;
3027
+ const pId = frame.parentId;
3028
+ frame.unmountView();
3029
+ removeFrame(targetId, wasCreated);
3030
+ reInitFrameForCache(frame);
3031
+ if (frameCache.length < MAX_FRAME_POOL) {
3032
+ frameCache.push(frame);
3033
+ }
3034
+ const parent = frameRegistry.get(pId || "");
3035
+ if (parent && parent.childrenMap[targetId]) {
3036
+ Reflect.deleteProperty(parent.childrenMap, targetId);
3037
+ parent.childrenCount--;
3038
+ notifyCreated(parent);
3039
+ }
3040
+ }
3041
+ /**
3042
+ * Mount all views in a zone.
3043
+ */
3044
+ mountZone(zoneId) {
3045
+ const targetZone = zoneId || this.id;
3046
+ this.holdFireCreated = 1;
3047
+ const rootEl = document.getElementById(targetZone);
3048
+ if (!rootEl) return;
3049
+ const viewElements = rootEl.querySelectorAll(`[${LARK_VIEW}]`);
3050
+ const frames = [];
3051
+ viewElements.forEach((el) => {
3052
+ if (!(el instanceof HTMLElement)) return;
3053
+ if (htmlElIsBound(el)) return;
3054
+ const elId = ensureElementId(el, "frame_");
3055
+ el.frameBound = 1;
3056
+ const viewPath = getAttribute(el, LARK_VIEW);
3057
+ frames.push([elId, viewPath]);
3058
+ });
3059
+ for (const [frameId, viewPath] of frames) {
3060
+ this.mountFrame(frameId, viewPath);
3061
+ }
3062
+ this.holdFireCreated = 0;
3063
+ notifyCreated(this);
3064
+ }
3065
+ /**
3066
+ * Unmount all views in a zone.
3067
+ */
3068
+ unmountZone(zoneId) {
3069
+ for (const childId in this.childrenMap) {
3070
+ if (hasOwnProperty(this.childrenMap, childId)) {
3071
+ if (!zoneId || childId !== zoneId) {
3072
+ this.unmountFrame(childId);
3073
+ }
3074
+ }
3075
+ }
3076
+ notifyCreated(this);
3077
+ }
3078
+ /**
3079
+ * Get all child frame IDs.
3080
+ */
3081
+ children() {
3082
+ const result = [];
3083
+ for (const id in this.childrenMap) {
3084
+ if (hasOwnProperty(this.childrenMap, id)) {
3085
+ result.push(id);
3086
+ }
3087
+ }
3088
+ return result;
3089
+ }
3090
+ /**
3091
+ * Get parent frame at given level.
3092
+ * @param level - How many levels up (default 1)
3093
+ */
3094
+ parent(level = 1) {
3095
+ let frame = void 0;
3096
+ let currentPid = this.parentId;
3097
+ let n = level >>> 0 || 1;
3098
+ while (currentPid && n--) {
3099
+ frame = frameRegistry.get(currentPid);
3100
+ currentPid = frame?.parentId;
3101
+ }
3102
+ return frame;
3103
+ }
3104
+ /**
3105
+ * Invoke a method on the view.
3106
+ */
3107
+ invoke(name, args) {
3108
+ let result;
3109
+ const view = this.view;
3110
+ if (view && view.rendered) {
3111
+ const fn = Reflect.get(view, name);
3112
+ if (typeof fn === "function") {
3113
+ result = funcWithTry(fn, args || [], view, noop);
3114
+ }
3115
+ } else {
3116
+ const key = SPLITTER + name;
3117
+ let existingEntry;
3118
+ for (const entry of this.invokeList) {
3119
+ if (entry.key === key) {
3120
+ existingEntry = entry;
3121
+ break;
3122
+ }
3123
+ }
3124
+ if (existingEntry) {
3125
+ existingEntry.removed = args === existingEntry.args;
3126
+ }
3127
+ const newEntry = {
3128
+ name,
3129
+ args: args || [],
3130
+ key
3131
+ };
3132
+ this.invokeList.push(newEntry);
3133
+ }
3134
+ return result;
3135
+ }
3136
+ /**
3137
+ * Type-safe variant of `invoke`.
3138
+ *
3139
+ * `invoke()` accepts any string and any args, which silently hides
3140
+ * mismatched call sites when a method gets renamed. `invokeTyped` carries
3141
+ * the view's method signature through TypeScript so the compiler catches
3142
+ * those mistakes:
3143
+ *
3144
+ * ```ts
3145
+ * type Home = View & { loadData(id: string): Promise<void> };
3146
+ * frame.invokeTyped<Home, "loadData">("loadData", ["user-1"]);
3147
+ * ```
3148
+ *
3149
+ * Behavior is identical to `invoke` at runtime — same defer / direct-call
3150
+ * paths — so it's a drop-in safer overload.
3151
+ */
3152
+ invokeTyped(name, args) {
3153
+ return this.invoke(name, args);
3154
+ }
3155
+ // ============================================================
3156
+ // Static methods
3157
+ // ============================================================
3158
+ /** Get frame by ID */
3159
+ static get(id) {
3160
+ return frameRegistry.get(id);
3161
+ }
3162
+ /** Get all frames */
3163
+ static getAll() {
3164
+ return frameRegistry;
3165
+ }
3166
+ /**
3167
+ * Returns the existing root frame, or `undefined` if none has been created.
3168
+ * Pure getter — never creates a Frame, never touches the DOM.
3169
+ *
3170
+ * Use `Frame.createRoot(id)` to create the root explicitly during framework
3171
+ * boot. For Micro-Frontend hosts that own multiple independent containers,
3172
+ * use `new Frame(containerId)` directly so each MF mount has its own root.
3173
+ */
3174
+ static getRoot() {
3175
+ return rootFrame;
3176
+ }
3177
+ /**
3178
+ * Create (or return) the singleton root frame for this app.
3179
+ *
3180
+ * Idempotent: subsequent calls always return the original root regardless
3181
+ * of `rootId` — so passing a different id later is silently ignored.
3182
+ * `Framework.boot()` is the canonical caller; user code rarely needs this.
3183
+ */
3184
+ static createRoot(rootId) {
3185
+ if (!rootFrame) {
3186
+ rootId = rootId || "root";
3187
+ let rootElement = document.getElementById(rootId);
3188
+ if (!rootElement) {
3189
+ rootElement = document.body;
3190
+ rootElement.id = rootId;
3191
+ }
3192
+ rootFrame = new _Frame(rootId);
3193
+ }
3194
+ return rootFrame;
3195
+ }
3196
+ /** Bind event listener (static) */
3197
+ static on(event, handler) {
3198
+ staticEmitter.on(event, handler);
3199
+ return _Frame;
3200
+ }
3201
+ /** Unbind event listener (static) */
3202
+ static off(event, handler) {
3203
+ staticEmitter.off(event, handler);
3204
+ return _Frame;
3205
+ }
3206
+ /** Fire event (static) */
3207
+ static fire(event, data) {
3208
+ staticEmitter.fire(event, data);
3209
+ }
3210
+ };
3211
+ function htmlElIsBound(element) {
3212
+ return !!element.frameBound;
3213
+ }
3214
+ function removeFrame(id, wasCreated) {
3215
+ const frameInstance = frameRegistry.get(id);
3216
+ if (!frameInstance) return;
3217
+ frameRegistry.delete(id);
3218
+ Frame.fire("remove", { frame: frameInstance, fcc: wasCreated });
3219
+ const element = document.getElementById(id);
3220
+ if (element) {
3221
+ element.frameBound = 0;
3222
+ Reflect.deleteProperty(element, "frame");
3223
+ }
3224
+ }
3225
+ function notifyCreated(frameInstance) {
3226
+ if (!frameInstance["childrenCreated"] && !frameInstance["holdFireCreated"] && frameInstance["childrenCount"] === frameInstance["readyCount"]) {
3227
+ if (!frameInstance["childrenCreated"]) {
3228
+ frameInstance["childrenCreated"] = 1;
3229
+ frameInstance["childrenAlter"] = 0;
3230
+ frameInstance.fire("created");
3231
+ }
3232
+ const pId = frameInstance.parentId;
3233
+ if (pId) {
3234
+ const parent = frameRegistry.get(pId);
3235
+ if (parent && !parent.readyMap.has(frameInstance.id)) {
3236
+ parent.readyMap.add(frameInstance.id);
3237
+ parent.readyCount++;
3238
+ notifyCreated(parent);
3239
+ }
3240
+ }
3241
+ }
3242
+ }
3243
+ function notifyAlter(frameInstance, data) {
3244
+ if (!frameInstance["childrenAlter"] && frameInstance["childrenCreated"]) {
3245
+ frameInstance["childrenCreated"] = 0;
3246
+ frameInstance["childrenAlter"] = 1;
3247
+ frameInstance.fire("alter", data);
3248
+ const pId = frameInstance.parentId;
3249
+ if (pId) {
3250
+ const parent = frameRegistry.get(pId);
3251
+ if (parent && parent.readyMap.has(frameInstance.id)) {
3252
+ parent.readyCount--;
3253
+ parent.readyMap.delete(frameInstance.id);
3254
+ notifyAlter(parent, data);
3255
+ }
3256
+ }
3257
+ }
3258
+ }
3259
+ function reInitFrame(frame, id, parentId) {
3260
+ Reflect.set(frame, "id", id);
3261
+ frame["_parentId"] = parentId;
3262
+ frame["childrenMap"] = {};
3263
+ frame["childrenCount"] = 0;
3264
+ frame["readyCount"] = 0;
3265
+ frame["signature"] = 1;
3266
+ frame["readyMap"] = /* @__PURE__ */ new Set();
3267
+ frame["invokeList"] = [];
3268
+ frameRegistry.set(id, frame);
3269
+ }
3270
+ function reInitFrameForCache(frame) {
3271
+ Reflect.set(frame, "id", "");
3272
+ frame["_parentId"] = void 0;
3273
+ frame["childrenMap"] = {};
3274
+ frame["readyMap"] = /* @__PURE__ */ new Set();
3275
+ }
3276
+ function translateQuery(pId, src, params) {
3277
+ const parentFrame = frameRegistry.get(pId);
3278
+ const parentView = parentFrame?.view;
3279
+ if (!parentView) return;
3280
+ const parentRefData = parentView.updater.refData;
3281
+ if (!parentRefData) return;
3282
+ if (src.indexOf(SPLITTER) > 0) {
3283
+ translateData(parentRefData, params);
3284
+ const paramsRec = params;
3285
+ const splitterValue = paramsRec[SPLITTER];
3286
+ if (splitterValue && typeof splitterValue === "object") {
3287
+ assign(params, splitterValue);
3288
+ Reflect.deleteProperty(params, SPLITTER);
3289
+ }
3290
+ }
3291
+ }
3292
+
3293
+ // src/devtool.ts
3294
+ var FrameDevtoolBridge = {
3295
+ MSG_PING: "LARK_DEVTOOL_PING",
3296
+ MSG_PONG: "LARK_DEVTOOL_PONG",
3297
+ MSG_REQUEST_TREE: "LARK_DEVTOOL_REQUEST_TREE",
3298
+ MSG_TREE: "LARK_DEVTOOL_TREE",
3299
+ MSG_TREE_DELTA: "LARK_DEVTOOL_TREE_DELTA"
3300
+ };
3301
+ function serializeView(view) {
3302
+ const evtMap = view.eventObjectMap;
3303
+ const eventMethodKeys = evtMap ? Object.keys(evtMap) : [];
3304
+ const resourceKeys = view.resources ? Object.keys(view.resources) : [];
3305
+ const hasAssign = typeof view["assign"] === "function";
3306
+ let updaterData = null;
3307
+ try {
3308
+ const ref = view.updater?.refData;
3309
+ if (ref && typeof ref === "object") {
3310
+ updaterData = {};
3311
+ for (const k of Object.keys(ref)) {
3312
+ const v = ref[k];
3313
+ updaterData[k] = v === null || typeof v !== "object" ? v : `[${typeof v}]`;
3314
+ }
3315
+ }
3316
+ } catch {
3317
+ }
3318
+ return {
3319
+ id: view.id,
3320
+ rendered: !!view.rendered,
3321
+ signature: view.signature,
3322
+ observedStateKeys: view.observedStateKeys ?? null,
3323
+ locationObserved: {
3324
+ flag: view.locationObserved.flag,
3325
+ keys: view.locationObserved.keys,
3326
+ observePath: view.locationObserved.observePath
3327
+ },
3328
+ hasTemplate: !!view.template,
3329
+ eventMethodKeys,
3330
+ resourceKeys,
3331
+ hasAssign,
3332
+ updaterData
3333
+ };
3334
+ }
3335
+ function serializeFrame(frameId) {
3336
+ const frame = Frame.get(frameId);
3337
+ if (!frame) return null;
3338
+ const view = frame.view;
3339
+ const children = [];
3340
+ for (const childId of frame.children()) {
3341
+ const childNode = serializeFrame(childId);
3342
+ if (childNode) {
3343
+ children.push(childNode);
3344
+ }
3345
+ }
3346
+ return {
3347
+ id: frame.id,
3348
+ parentId: frame.parentId ?? null,
3349
+ viewPath: frame.viewPath ?? null,
3350
+ childrenCount: frame.childrenCount,
3351
+ readyCount: frame.readyCount,
3352
+ childrenCreated: frame.childrenCreated,
3353
+ childrenAlter: frame.childrenAlter,
3354
+ destroyed: frame.destroyed,
3355
+ view: view ? serializeView(view) : null,
3356
+ children
3357
+ };
3358
+ }
3359
+ function serializeFrameTree() {
3360
+ const root = Frame.getRoot();
3361
+ if (!root) {
3362
+ return { root: null, totalFrames: 0, timestamp: Date.now(), rootId: "" };
3363
+ }
3364
+ const rootNode = serializeFrame(root.id);
3365
+ let totalFrames = 0;
3366
+ const countFrames = (node) => {
3367
+ if (!node) return;
3368
+ totalFrames++;
3369
+ for (const child of node.children) {
3370
+ countFrames(child);
3371
+ }
3372
+ };
3373
+ countFrames(rootNode);
3374
+ return {
3375
+ root: rootNode,
3376
+ totalFrames,
3377
+ timestamp: Date.now(),
3378
+ rootId: root.id
3379
+ };
3380
+ }
3381
+ var bridgeInstalled = false;
3382
+ var lastTreeJson = "";
3383
+ function installFrameDevtoolBridge() {
3384
+ if (bridgeInstalled) return;
3385
+ if (typeof window === "undefined") return;
3386
+ bridgeInstalled = true;
3387
+ window.addEventListener("message", (event) => {
3388
+ const data = event.data;
3389
+ if (!data || typeof data !== "object") return;
3390
+ const type = data.type;
3391
+ if (type === FrameDevtoolBridge.MSG_PING) {
3392
+ const source = event.source;
3393
+ if (source) {
3394
+ source.postMessage(
3395
+ { type: FrameDevtoolBridge.MSG_PONG },
3396
+ { targetOrigin: "*" }
3397
+ );
3398
+ }
3399
+ return;
3400
+ }
3401
+ if (type === FrameDevtoolBridge.MSG_REQUEST_TREE) {
3402
+ const tree = serializeFrameTree();
3403
+ const source = event.source;
3404
+ if (source) {
3405
+ source.postMessage(
3406
+ { type: FrameDevtoolBridge.MSG_TREE, data: tree },
3407
+ { targetOrigin: "*" }
3408
+ );
3409
+ }
3410
+ }
3411
+ });
3412
+ Frame.on("add", () => {
3413
+ pushTreeUpdate();
3414
+ });
3415
+ Frame.on("remove", () => {
3416
+ pushTreeUpdate();
3417
+ });
3418
+ }
3419
+ function pushTreeUpdate() {
3420
+ if (window === window.parent) return;
3421
+ const tree = serializeFrameTree();
3422
+ const treeJson = JSON.stringify(tree);
3423
+ if (treeJson !== lastTreeJson) {
3424
+ lastTreeJson = treeJson;
3425
+ window.parent.postMessage(
3426
+ { type: FrameDevtoolBridge.MSG_TREE_DELTA, data: tree },
3427
+ "*"
3428
+ );
3429
+ }
3430
+ }
3431
+ // Annotate the CommonJS export names for ESM import in node:
3432
+ 0 && (module.exports = {
3433
+ FrameDevtoolBridge,
3434
+ installFrameDevtoolBridge,
3435
+ serializeFrameTree
3436
+ });