@lark.js/mvc 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,5030 @@
1
+ // src/constants.ts
2
+ var globalCounter = 0;
3
+ var SPLITTER = "";
4
+ var ROUTER_EVENTS = {
5
+ CHANGE: "change",
6
+ CHANGED: "changed",
7
+ PAGE_UNLOAD: "page_unload"
8
+ };
9
+ var TAG_KEY = "lark-key";
10
+ var TAG_ATTR_KEY = "lark-attr-key";
11
+ var TAG_VIEW_KEY = "lark-view-key";
12
+ var LARK_VIEW = "lark-view";
13
+ var EVT_METHOD_REG = new RegExp(
14
+ `(?:([\\w-]+)${SPLITTER})?([^(]+)\\(([\\s\\S]*?)?\\)`
15
+ );
16
+ var VIEW_EVT_METHOD_REG = /^(\$?)([\w]*)<(.*?)>(?:<([\w ,]*)>)?$/;
17
+ var URL_TRIM_HASH_REGEX = /(?:^.*\/\/[^/]+|#.*$)/gi;
18
+ var URL_TRIM_QUERY_REGEX = /^[^#]*#?!?/;
19
+ var URL_PARAM_REGEX = /([^=&?/#]+)=?([^&#?]*)/g;
20
+ var URL_QUERY_HASH_REGEX = /[#?].*$/;
21
+ var SVG_NS = "http://www.w3.org/2000/svg";
22
+ var MATH_NS = "http://www.w3.org/1998/Math/MathML";
23
+ var TAG_NAME_REGEX = /<([a-z][^/\0>\x20\t\r\n\f]+)/i;
24
+ var CALL_BREAK_TIME = 48;
25
+ function nextCounter() {
26
+ return ++globalCounter;
27
+ }
28
+
29
+ // src/utils.ts
30
+ function isPlainObject(value) {
31
+ if (typeof value !== "object" || value === null) return false;
32
+ const proto = Object.getPrototypeOf(value);
33
+ if (proto === null) return true;
34
+ return proto === Object.prototype || proto === null;
35
+ }
36
+ var isArray = Array.isArray;
37
+ function isPrimitiveOrFunc(value) {
38
+ return !value || typeof value !== "object" && typeof value !== "function";
39
+ }
40
+ function isPrimitive(value) {
41
+ return !value || typeof value !== "object";
42
+ }
43
+ var _localCounter = 0;
44
+ function generateId(prefix) {
45
+ return (prefix || "lark_") + _localCounter++;
46
+ }
47
+ function syncCounter(val) {
48
+ _localCounter = val;
49
+ }
50
+ function noop() {
51
+ }
52
+ function has(owner, prop) {
53
+ return owner != null && Object.prototype.hasOwnProperty.call(owner, prop);
54
+ }
55
+ function keys(obj) {
56
+ const result = [];
57
+ for (const p in obj) {
58
+ if (has(obj, p)) {
59
+ result.push(p);
60
+ }
61
+ }
62
+ return result;
63
+ }
64
+ function assign(target, ...sources) {
65
+ for (const source of sources) {
66
+ if (source) {
67
+ for (const p in source) {
68
+ if (has(source, p)) {
69
+ target[p] = source[p];
70
+ }
71
+ }
72
+ }
73
+ }
74
+ return target;
75
+ }
76
+ function funcWithTry(fns, args, context, configError) {
77
+ const fnArray = isArray(fns) ? fns : [fns];
78
+ let ret;
79
+ for (const fn of fnArray) {
80
+ try {
81
+ ret = Function.prototype.apply.call(fn, context, args);
82
+ } catch (e) {
83
+ configError(e);
84
+ }
85
+ }
86
+ return ret;
87
+ }
88
+ function setData(newData, oldData, changedKeys2, excludes) {
89
+ let changed = false;
90
+ for (const p in newData) {
91
+ if (has(newData, p)) {
92
+ const now2 = newData[p];
93
+ const old = oldData[p];
94
+ if ((!isPrimitiveOrFunc(now2) || old !== now2) && !excludes.has(p)) {
95
+ changedKeys2[p] = 1;
96
+ changed = true;
97
+ }
98
+ oldData[p] = now2;
99
+ }
100
+ }
101
+ return changed;
102
+ }
103
+ function translateData(data, value) {
104
+ if (isPrimitive(value)) {
105
+ const prop = String(value);
106
+ if (prop[0] === SPLITTER && has(data, prop)) {
107
+ return data[prop];
108
+ }
109
+ return value;
110
+ }
111
+ if (isPlainObject(value) || isArray(value)) {
112
+ for (const p in value) {
113
+ if (has(value, p)) {
114
+ const val = value[p];
115
+ const newVal = translateData(data, val);
116
+ value[p] = newVal;
117
+ }
118
+ }
119
+ return value;
120
+ }
121
+ return value;
122
+ }
123
+ function getById(id) {
124
+ if (!id) return null;
125
+ if (typeof id === "object") return id;
126
+ return document.getElementById(id);
127
+ }
128
+ function getAttribute(element, attr) {
129
+ return Element.prototype.getAttribute.call(element, attr) ?? "";
130
+ }
131
+ function ensureElementId(element) {
132
+ const id = element.getAttribute("id");
133
+ if (id) return id;
134
+ element.autoId = 1;
135
+ const newId = generateId();
136
+ element.id = newId;
137
+ return newId;
138
+ }
139
+ function nodeInside(a, b) {
140
+ const aNode = typeof a === "string" ? document.getElementById(a) : a;
141
+ const bNode = typeof b === "string" ? document.getElementById(b) : b;
142
+ if (!aNode || !bNode) return false;
143
+ if (aNode === bNode) return true;
144
+ try {
145
+ return (bNode.compareDocumentPosition(aNode) & 16) === 16;
146
+ } catch {
147
+ return false;
148
+ }
149
+ }
150
+ var paramsTemp = {};
151
+ function paramsReplacer(_match, name, value) {
152
+ try {
153
+ paramsTemp[name] = decodeURIComponent(value || "");
154
+ } catch {
155
+ paramsTemp[name] = value || "";
156
+ }
157
+ return "";
158
+ }
159
+ function parseUri(uri) {
160
+ paramsTemp = {};
161
+ const path = uri.replace(URL_QUERY_HASH_REGEX, "");
162
+ const pathname = path;
163
+ const actualPath = uri === pathname && IS_URL_PARAMS.test(pathname) ? "" : pathname;
164
+ uri.replace(actualPath, "").replace(URL_PARAM_REGEX, paramsReplacer);
165
+ return {
166
+ path: actualPath,
167
+ params: { ...paramsTemp }
168
+ };
169
+ }
170
+ var IS_URL_PARAMS = {
171
+ test(s) {
172
+ return /(?!^)=|&/.test(s);
173
+ }
174
+ };
175
+ function toUri(path, params, keepEmptyObject) {
176
+ const pairs = [];
177
+ let hasParams = false;
178
+ for (const p in params) {
179
+ if (has(params, p)) {
180
+ const v = String(params[p] ?? "");
181
+ if (!keepEmptyObject || v || has(keepEmptyObject, p)) {
182
+ pairs.push(`${p}=${encodeURIComponent(v)}`);
183
+ hasParams = true;
184
+ }
185
+ }
186
+ }
187
+ if (hasParams) {
188
+ path += (path && (~path.indexOf("?") ? "&" : "?")) + pairs.join("&");
189
+ }
190
+ return path;
191
+ }
192
+ function toMap(list, key) {
193
+ const map = {};
194
+ if (!list) return map;
195
+ for (const item of list) {
196
+ const mapKey = key ? String(item[key]) : String(item);
197
+ map[mapKey] = key ? item : (map[mapKey] || 0) + 1;
198
+ }
199
+ return map;
200
+ }
201
+ function now() {
202
+ return Date.now ? Date.now() : (/* @__PURE__ */ new Date()).getTime();
203
+ }
204
+ function classExtend(ctor, base, props, statics) {
205
+ const baseProto = base["prototype"] ?? {};
206
+ const cProto = Object.create(baseProto);
207
+ assign(cProto, props);
208
+ Object.assign(ctor, statics);
209
+ cProto.constructor = ctor;
210
+ ctor["prototype"] = cProto;
211
+ }
212
+
213
+ // src/apply-style.ts
214
+ var injectedStyleIds = /* @__PURE__ */ new Set();
215
+ function applyStyle(styleIdOrPairs, css) {
216
+ if (Array.isArray(styleIdOrPairs)) {
217
+ if (styleIdOrPairs.length % 2 !== 0) {
218
+ throw new Error("Invalid array of [id, content] pairs");
219
+ }
220
+ const cleanupCallbacks = [];
221
+ for (let i = 0; i < styleIdOrPairs.length; i += 2) {
222
+ const styleId = styleIdOrPairs[i];
223
+ const styleContent = styleIdOrPairs[i + 1];
224
+ const cleanup = applyStyle(styleId, styleContent);
225
+ if (cleanup !== noop) {
226
+ cleanupCallbacks.push(cleanup);
227
+ }
228
+ }
229
+ return () => {
230
+ for (const cleanup of cleanupCallbacks) {
231
+ cleanup();
232
+ }
233
+ };
234
+ }
235
+ if (css && !injectedStyleIds.has(styleIdOrPairs)) {
236
+ injectedStyleIds.add(styleIdOrPairs);
237
+ const styleElement = document.createElement("style");
238
+ styleElement.setAttribute("from", "lark");
239
+ styleElement.id = styleIdOrPairs;
240
+ styleElement.textContent = css;
241
+ document.head.append(styleElement);
242
+ return () => {
243
+ injectedStyleIds.delete(styleIdOrPairs);
244
+ styleElement.remove();
245
+ };
246
+ }
247
+ return noop;
248
+ }
249
+
250
+ // src/mark.ts
251
+ var DELETED_KEY = SPLITTER + "$a";
252
+ var MARK_OBJECT_KEY = SPLITTER + "$b";
253
+ function mark(host, key) {
254
+ let sign = 0;
255
+ const hostRecord = host;
256
+ if (!hostRecord[DELETED_KEY]) {
257
+ const markHost = hostRecord[MARK_OBJECT_KEY] || (hostRecord[MARK_OBJECT_KEY] = {});
258
+ if (!Object.prototype.hasOwnProperty.call(markHost, key)) {
259
+ markHost[key] = 0;
260
+ }
261
+ sign = ++markHost[key];
262
+ }
263
+ return () => {
264
+ const temp = hostRecord[MARK_OBJECT_KEY];
265
+ return !!(temp && sign === temp[key]);
266
+ };
267
+ }
268
+ function unmark(host) {
269
+ const hostRecord = host;
270
+ hostRecord[MARK_OBJECT_KEY] = 0;
271
+ hostRecord[DELETED_KEY] = 1;
272
+ }
273
+
274
+ // src/safeguard.ts
275
+ var proxiesPool = /* @__PURE__ */ new Map();
276
+ var SAFEGUARD_SENTINEL = "_sf_";
277
+ function safeguard(data, getter, setter, isRoot) {
278
+ if (typeof window.__lark_debug === "undefined" || !window.__lark_debug) {
279
+ return data;
280
+ }
281
+ if (typeof Proxy === "undefined") {
282
+ return data;
283
+ }
284
+ if (isPrimitive(data)) {
285
+ return data;
286
+ }
287
+ const build = (prefix, obj) => {
288
+ const cacheKey = (getter || "") + "" + (setter || "");
289
+ const cached = proxiesPool.get(obj);
290
+ if (cached && cached.cacheKey === cacheKey) {
291
+ return cached.entity;
292
+ }
293
+ if (Reflect.get(obj, SAFEGUARD_SENTINEL)) {
294
+ return obj;
295
+ }
296
+ const entity = new Proxy(obj, {
297
+ set(target, property, value) {
298
+ if (!setter && !prefix) {
299
+ throw new Error(
300
+ "Avoid write back, key: " + prefix + property + " value:" + value + " more: https://github.com/hangtiancheng/h"
301
+ );
302
+ }
303
+ Reflect.set(target, property, value);
304
+ if (setter) {
305
+ setter(prefix + property, value);
306
+ }
307
+ return true;
308
+ },
309
+ get(target, property) {
310
+ if (property === SAFEGUARD_SENTINEL) {
311
+ return true;
312
+ }
313
+ const out = Reflect.get(target, property);
314
+ if (!prefix && getter) {
315
+ getter(property);
316
+ }
317
+ if (!isRoot && has(target, property) && (isArray(out) || isPlainObject(out))) {
318
+ return build(prefix + property + ".", out);
319
+ }
320
+ return out;
321
+ }
322
+ });
323
+ proxiesPool.set(obj, { cacheKey, entity });
324
+ return entity;
325
+ };
326
+ return build("", data);
327
+ }
328
+
329
+ // src/cache.ts
330
+ function sortCacheEntries(a, b) {
331
+ return b.frequency - a.frequency || b.lastTimestamp - a.lastTimestamp;
332
+ }
333
+ var Cache = class {
334
+ /** Cache entries array */
335
+ entries = [];
336
+ /** Fast lookup: prefixed key -> entry */
337
+ lookup = /* @__PURE__ */ new Map();
338
+ /** Buffer size for eviction */
339
+ bufferSize;
340
+ /** Maximum cache size */
341
+ maxSize;
342
+ /** Total capacity (maxSize + bufferSize) */
343
+ capacity;
344
+ /** Callback when entry is removed */
345
+ onRemove;
346
+ /** Sort comparator */
347
+ comparator;
348
+ constructor(options = {}) {
349
+ this.maxSize = options.maxSize ?? 20;
350
+ this.bufferSize = options.bufferSize ?? 5;
351
+ this.capacity = this.maxSize + this.bufferSize;
352
+ this.onRemove = options.onRemove;
353
+ this.comparator = options.sortComparator ?? sortCacheEntries;
354
+ }
355
+ /** Prefix a key with SPLITTER for namespace isolation */
356
+ prefixKey(key) {
357
+ return SPLITTER + key;
358
+ }
359
+ /**
360
+ * Get a cached value by key.
361
+ * Updates frequency and timestamp for cache sorting.
362
+ */
363
+ get(key) {
364
+ const prefixedKey = this.prefixKey(key);
365
+ const entry = this.lookup.get(prefixedKey);
366
+ if (!entry) return void 0;
367
+ entry.frequency++;
368
+ entry.lastTimestamp = nextCounter();
369
+ return entry.value;
370
+ }
371
+ /**
372
+ * Iterate all cached values.
373
+ */
374
+ forEach(callback) {
375
+ for (const entry of this.entries) {
376
+ callback(entry.value);
377
+ }
378
+ }
379
+ /**
380
+ * Set or update a cached value.
381
+ * If key already exists, updates value and increments frequency.
382
+ * If cache exceeds capacity, triggers eviction.
383
+ */
384
+ set(key, value) {
385
+ const prefixedKey = this.prefixKey(key);
386
+ const existing = this.lookup.get(prefixedKey);
387
+ if (existing) {
388
+ existing.value = value;
389
+ existing.frequency++;
390
+ existing.lastTimestamp = nextCounter();
391
+ return;
392
+ }
393
+ if (this.entries.length >= this.capacity) {
394
+ this.evictEntries();
395
+ }
396
+ const entry = {
397
+ originalKey: key,
398
+ value,
399
+ frequency: 1,
400
+ lastTimestamp: nextCounter()
401
+ };
402
+ this.entries.push(entry);
403
+ this.lookup.set(prefixedKey, entry);
404
+ }
405
+ /**
406
+ * Delete a cached entry.
407
+ */
408
+ del(key) {
409
+ const prefixedKey = this.prefixKey(key);
410
+ const entry = this.lookup.get(prefixedKey);
411
+ if (!entry) return;
412
+ entry.frequency = -1;
413
+ entry.value = void 0;
414
+ this.lookup.delete(prefixedKey);
415
+ if (this.onRemove) {
416
+ this.onRemove(key);
417
+ }
418
+ }
419
+ /**
420
+ * Check if a key exists in cache.
421
+ */
422
+ has(key) {
423
+ return this.lookup.has(this.prefixKey(key));
424
+ }
425
+ /** Get current cache size */
426
+ get size() {
427
+ return this.entries.length;
428
+ }
429
+ /** Clear all entries */
430
+ clear() {
431
+ if (this.onRemove) {
432
+ for (const entry of this.entries) {
433
+ this.onRemove(entry.originalKey);
434
+ }
435
+ }
436
+ this.entries = [];
437
+ this.lookup.clear();
438
+ }
439
+ /** Evict least-used entries from cache */
440
+ evictEntries() {
441
+ this.entries.sort(this.comparator);
442
+ let count = this.bufferSize;
443
+ while (count-- > 0 && this.entries.length > 0) {
444
+ const entry = this.entries.pop();
445
+ if (entry && entry.frequency > 0) {
446
+ this.lookup.delete(this.prefixKey(entry.originalKey));
447
+ if (this.onRemove) {
448
+ this.onRemove(entry.originalKey);
449
+ }
450
+ }
451
+ }
452
+ this.entries = this.entries.filter((e) => e.frequency !== -1);
453
+ }
454
+ };
455
+
456
+ // src/event-emitter.ts
457
+ var EventEmitter = class {
458
+ /** Event listeners: prefixed key -> listener array */
459
+ listeners = /* @__PURE__ */ new Map();
460
+ /**
461
+ * Bind event listener.
462
+ */
463
+ on(event, handler) {
464
+ const key = SPLITTER + event;
465
+ let list = this.listeners.get(key);
466
+ if (!list) {
467
+ list = [];
468
+ this.listeners.set(key, list);
469
+ }
470
+ list.push({ handler, executing: 0 });
471
+ return this;
472
+ }
473
+ /**
474
+ * Unbind event listener.
475
+ * If handler is provided, removes only that handler.
476
+ * If no handler, removes all handlers for the event.
477
+ */
478
+ off(event, handler) {
479
+ const key = SPLITTER + event;
480
+ if (handler) {
481
+ const list = this.listeners.get(key);
482
+ if (list) {
483
+ for (const listener of list) {
484
+ if (listener.handler === handler) {
485
+ listener.handler = noop;
486
+ break;
487
+ }
488
+ }
489
+ }
490
+ } else {
491
+ this.listeners.delete(key);
492
+ Reflect.deleteProperty(this, `on${event}`);
493
+ }
494
+ return this;
495
+ }
496
+ /**
497
+ * Fire event, execute all bound handlers.
498
+ * Supports executing state management: handlers removed during
499
+ * execution are safely handled.
500
+ *
501
+ * @param event - Event name
502
+ * @param data - Event data (type property added automatically)
503
+ * @param remove - Whether to remove all handlers after firing
504
+ * @param lastToFirst - Whether to execute handlers in reverse order
505
+ */
506
+ fire(event, data, remove, lastToFirst) {
507
+ const key = SPLITTER + event;
508
+ const list = this.listeners.get(key);
509
+ if (!data) {
510
+ data = {};
511
+ }
512
+ data["type"] = event;
513
+ if (list) {
514
+ let end = list.length;
515
+ const len = end - 1;
516
+ while (end--) {
517
+ const idx = lastToFirst ? end : len - end;
518
+ const listener = list[idx];
519
+ if (listener.handler !== noop) {
520
+ listener.executing = 1;
521
+ funcWithTry(
522
+ [listener.handler],
523
+ [data],
524
+ this,
525
+ noop
526
+ );
527
+ listener.executing = "";
528
+ } else if (!listener.executing) {
529
+ list.splice(idx, 1);
530
+ }
531
+ }
532
+ }
533
+ const onMethodName = `on${event}`;
534
+ const onMethod = this[onMethodName];
535
+ if (typeof onMethod === "function") {
536
+ funcWithTry(
537
+ [onMethod],
538
+ [data],
539
+ this,
540
+ noop
541
+ );
542
+ }
543
+ if (remove) {
544
+ this.off(event);
545
+ }
546
+ return this;
547
+ }
548
+ };
549
+
550
+ // src/state.ts
551
+ var appData = {};
552
+ var keyRefCounts = {};
553
+ var changedKeys = {};
554
+ var stashedChangedKeys = {};
555
+ var dataIsChanged = false;
556
+ var dataWhereSet = {};
557
+ var emitter = new EventEmitter();
558
+ var booted = false;
559
+ function markBooted() {
560
+ booted = true;
561
+ }
562
+ function setupKeysRef(keys2) {
563
+ const keyList = keys2.split(",");
564
+ for (const key of keyList) {
565
+ if (has(keyRefCounts, key)) {
566
+ keyRefCounts[key]++;
567
+ } else {
568
+ keyRefCounts[key] = 1;
569
+ }
570
+ }
571
+ return keyList;
572
+ }
573
+ function teardownKeysRef(keyList) {
574
+ for (const key of keyList) {
575
+ if (has(keyRefCounts, key)) {
576
+ const count = --keyRefCounts[key];
577
+ if (count <= 0) {
578
+ Reflect.deleteProperty(keyRefCounts, key);
579
+ Reflect.deleteProperty(appData, key);
580
+ if (typeof window.__lark_debug !== "undefined" && window.__lark_debug) {
581
+ Reflect.deleteProperty(dataWhereSet, key);
582
+ }
583
+ }
584
+ }
585
+ }
586
+ }
587
+ var notifyStarted = 0;
588
+ var notifyList = [];
589
+ var notifyTimer;
590
+ function clearNotify(key) {
591
+ for (let i = notifyList.length; i--; ) {
592
+ if (notifyList[i]?.key === key) {
593
+ notifyList.splice(i, 1);
594
+ }
595
+ }
596
+ }
597
+ function doNotify() {
598
+ const locker = {};
599
+ for (const n of notifyList) {
600
+ if (!locker[n.key]) {
601
+ console.warn(n.message);
602
+ locker[n.key] = 1;
603
+ }
604
+ }
605
+ notifyList.length = 0;
606
+ notifyStarted = 0;
607
+ }
608
+ function delayNotify(key, message) {
609
+ clearTimeout(notifyTimer);
610
+ notifyStarted = 0;
611
+ notifyList.push({ key, message });
612
+ if (!notifyStarted) {
613
+ notifyStarted = 1;
614
+ notifyTimer = setTimeout(doNotify, 500);
615
+ }
616
+ }
617
+ var State = {
618
+ /**
619
+ * Get data from state.
620
+ */
621
+ get(key) {
622
+ const result = key ? appData[key] : appData;
623
+ if (typeof window.__lark_debug !== "undefined" && window.__lark_debug) {
624
+ return safeguard(
625
+ result,
626
+ (dataKey) => {
627
+ if (booted && has(dataWhereSet, dataKey) && dataWhereSet[dataKey] !== window.location.pathname) {
628
+ console.warn(
629
+ `beware! You get state:"{State}.${dataKey}" where it set by page:${dataWhereSet[dataKey]}`
630
+ );
631
+ }
632
+ },
633
+ (path, _value) => {
634
+ const sub = key || path;
635
+ delayNotify(
636
+ sub,
637
+ `beware! You direct modify "{State}.${sub}" You should call State.set() and State.digest() to notify other views`
638
+ );
639
+ }
640
+ );
641
+ }
642
+ return result;
643
+ },
644
+ /**
645
+ * Set data to state.
646
+ */
647
+ set(data, excludes) {
648
+ dataIsChanged = setData(data, appData, changedKeys, excludes || /* @__PURE__ */ new Set()) || dataIsChanged;
649
+ if (typeof window.__lark_debug !== "undefined" && window.__lark_debug && booted) {
650
+ for (const p in data) {
651
+ dataWhereSet[p] = window.location.pathname;
652
+ }
653
+ }
654
+ return State;
655
+ },
656
+ /**
657
+ * Detect data changes and fire changed event if any.
658
+ */
659
+ digest(data, excludes) {
660
+ if (data) {
661
+ State.set(data, excludes);
662
+ }
663
+ if (dataIsChanged) {
664
+ if (typeof window.__lark_debug !== "undefined" && window.__lark_debug) {
665
+ for (const p in changedKeys) {
666
+ if (has(changedKeys, p)) {
667
+ clearNotify(p);
668
+ }
669
+ }
670
+ }
671
+ dataIsChanged = false;
672
+ const keys2 = {};
673
+ for (const k in changedKeys) {
674
+ if (has(changedKeys, k)) {
675
+ keys2[k] = 1;
676
+ }
677
+ }
678
+ stashedChangedKeys = keys2;
679
+ changedKeys = {};
680
+ emitter.fire(ROUTER_EVENTS.CHANGED, { keys: keys2 });
681
+ }
682
+ },
683
+ /**
684
+ * Get diff of what changed in last digest.
685
+ */
686
+ diff() {
687
+ return stashedChangedKeys;
688
+ },
689
+ /**
690
+ * Create mixin to clean up state keys on view destroy.
691
+ * Must be used in view.mixins array.
692
+ */
693
+ clean(keys2) {
694
+ return {
695
+ ctor: function() {
696
+ const keyList = setupKeysRef(keys2);
697
+ this.on("destroy", () => {
698
+ teardownKeysRef(keyList);
699
+ });
700
+ }
701
+ };
702
+ },
703
+ /**
704
+ * Bind event listener.
705
+ */
706
+ on(event, handler) {
707
+ emitter.on(event, handler);
708
+ return State;
709
+ },
710
+ /**
711
+ * Unbind event listener.
712
+ */
713
+ off(event, handler) {
714
+ emitter.off(event, handler);
715
+ return State;
716
+ },
717
+ /**
718
+ * Fire event.
719
+ */
720
+ fire(event, data, remove) {
721
+ emitter.fire(event, data, remove);
722
+ return State;
723
+ }
724
+ };
725
+
726
+ // src/router.ts
727
+ var emitter2 = new EventEmitter();
728
+ var hrefCache = new Cache();
729
+ var changedCache = new Cache();
730
+ var lastLocation = createEmptyLocation();
731
+ var lastChanged;
732
+ var silent = 0;
733
+ var booted2 = false;
734
+ var cachedRoutes;
735
+ var cachedUnmatchedView;
736
+ var cachedDefaultView;
737
+ var cachedDefaultPath;
738
+ var cachedRewrite;
739
+ var defaultTitle;
740
+ var frameworkConfig;
741
+ function createEmptyLocation() {
742
+ return {
743
+ href: "",
744
+ srcQuery: "",
745
+ srcHash: "",
746
+ query: { path: "", params: {} },
747
+ hash: { path: "", params: {} },
748
+ params: {},
749
+ get: (_key, defaultValue) => defaultValue ?? ""
750
+ };
751
+ }
752
+ function getParam(key, defaultValue) {
753
+ return this["params"][key] || (defaultValue !== void 0 ? defaultValue : "");
754
+ }
755
+ function attachViewAndPath(loc) {
756
+ if (!frameworkConfig) return;
757
+ if (!cachedRoutes) {
758
+ cachedRoutes = frameworkConfig.routes || {};
759
+ cachedUnmatchedView = frameworkConfig.unmatchedView;
760
+ cachedDefaultView = frameworkConfig.defaultView;
761
+ cachedDefaultPath = frameworkConfig.defaultPath || "/";
762
+ cachedRewrite = frameworkConfig.rewrite;
763
+ }
764
+ if (!loc.view) {
765
+ let path = loc.hash["path"] || cachedDefaultPath || "/";
766
+ if (cachedRewrite) {
767
+ path = cachedRewrite(
768
+ path,
769
+ loc["params"],
770
+ cachedRoutes
771
+ );
772
+ }
773
+ const viewEntry = cachedRoutes[path] || cachedUnmatchedView || cachedDefaultView;
774
+ loc["path"] = path;
775
+ loc.view = typeof viewEntry === "string" ? viewEntry : viewEntry?.view || "";
776
+ if (typeof viewEntry === "object" && viewEntry) {
777
+ assign(loc, viewEntry);
778
+ }
779
+ }
780
+ }
781
+ function getChanged(oldLoc, newLoc) {
782
+ const oKey = oldLoc.href;
783
+ const nKey = newLoc.href;
784
+ const tKey = oKey + SPLITTER + nKey;
785
+ const cached = changedCache.get(tKey);
786
+ if (cached) {
787
+ return cached;
788
+ }
789
+ let hasChanged = false;
790
+ const changedParams = {};
791
+ const setDiff = (key, oldVal, newVal) => {
792
+ const from = oldVal || "";
793
+ const to = newVal || "";
794
+ if (from !== to) {
795
+ changedParams[key] = { from, to };
796
+ hasChanged = true;
797
+ }
798
+ };
799
+ const allParamKeys = /* @__PURE__ */ new Set([
800
+ ...Object.keys(oldLoc["params"]),
801
+ ...Object.keys(newLoc["params"])
802
+ ]);
803
+ for (const key of allParamKeys) {
804
+ setDiff(key, oldLoc["params"][key], newLoc["params"][key]);
805
+ }
806
+ const result = {
807
+ ["params"]: changedParams,
808
+ force: !oKey,
809
+ changed: hasChanged
810
+ };
811
+ setDiff("path", oldLoc["path"], newLoc["path"]);
812
+ if (changedParams["path"]) {
813
+ result["path"] = changedParams["path"];
814
+ hasChanged = true;
815
+ result.changed = true;
816
+ }
817
+ const viewKey = "view";
818
+ setDiff(viewKey, oldLoc.view, newLoc.view);
819
+ if (changedParams[viewKey]) {
820
+ result.view = changedParams[viewKey];
821
+ hasChanged = true;
822
+ result.changed = true;
823
+ }
824
+ const finalResult = {
825
+ changed: hasChanged,
826
+ diff: result
827
+ };
828
+ changedCache.set(tKey, finalResult);
829
+ return finalResult;
830
+ }
831
+ function updateHash(path, replace) {
832
+ const hashbang = frameworkConfig?.hashbang || "#!";
833
+ const fullPath = path === "" ? "" : hashbang + path;
834
+ if (replace) {
835
+ window.location.replace(fullPath);
836
+ } else {
837
+ window.location.hash = fullPath;
838
+ }
839
+ }
840
+ function updateUrl(path, params, loc, replace, silentFlag, lQuery) {
841
+ path = toUri(path, params, lQuery);
842
+ if (path !== loc.srcHash) {
843
+ silent = silentFlag ? 1 : 0;
844
+ updateHash(path, replace);
845
+ }
846
+ }
847
+ var Router = {
848
+ /**
849
+ * Parse href into Location object.
850
+ * Defaults to window.location.href.
851
+ */
852
+ parse(href) {
853
+ href = href || window.location.href;
854
+ const cached = hrefCache.get(href);
855
+ if (cached) {
856
+ return cached;
857
+ }
858
+ const srcQuery = href.replace(URL_TRIM_HASH_REGEX, "");
859
+ const srcHash = href.replace(URL_TRIM_QUERY_REGEX, "");
860
+ const query = parseUri(srcQuery);
861
+ const hash = parseUri(srcHash);
862
+ const params = assign({}, query["params"], hash["params"]);
863
+ const location = {
864
+ href,
865
+ srcQuery,
866
+ srcHash,
867
+ query: { path: query.path, params: query["params"] },
868
+ hash: { path: hash.path, params: hash["params"] },
869
+ ["params"]: params,
870
+ get: getParam
871
+ };
872
+ if (booted2) {
873
+ attachViewAndPath(location);
874
+ hrefCache.set(href, location);
875
+ }
876
+ if (typeof window.__lark_debug !== "undefined" && window.__lark_debug) {
877
+ location["params"] = safeguard(location["params"]);
878
+ }
879
+ return location;
880
+ },
881
+ /**
882
+ * Compute diff between current and previous location.
883
+ * Fires 'changed' event if location changed and not silent.
884
+ */
885
+ diff() {
886
+ const location = Router.parse();
887
+ const changed = getChanged(lastLocation, location);
888
+ lastLocation = location;
889
+ if (!silent && changed.changed) {
890
+ lastChanged = changed.diff;
891
+ if (lastChanged["path"]) {
892
+ document.title = defaultTitle || document.title;
893
+ }
894
+ emitter2.fire(
895
+ ROUTER_EVENTS.CHANGED,
896
+ lastChanged
897
+ );
898
+ }
899
+ silent = 0;
900
+ if (typeof window.__lark_debug !== "undefined" && window.__lark_debug && lastChanged) {
901
+ lastChanged = safeguard(lastChanged);
902
+ }
903
+ return lastChanged;
904
+ },
905
+ /**
906
+ * Navigate to new URL.
907
+ *
908
+ * @param pathOrParams - Path string or params object
909
+ * @param params - Query parameters (if pathOrParams is string)
910
+ * @param replace - Whether to replace current history entry
911
+ * @param silentFlag - Whether to silently update without firing events
912
+ */
913
+ to(pathOrParams, params, replace, silentFlag) {
914
+ let tPath = "";
915
+ let tParams;
916
+ if (!params && typeof pathOrParams === "object") {
917
+ tParams = pathOrParams;
918
+ } else {
919
+ const parsed = parseUri(pathOrParams);
920
+ tPath = parsed.path;
921
+ tParams = { ...parsed["params"] };
922
+ if (params) {
923
+ assign(tParams, params);
924
+ }
925
+ }
926
+ const lPath = lastLocation["path"] || "";
927
+ const lParams = lastLocation["params"];
928
+ const lQuery = {};
929
+ for (const k in lastLocation.query["params"]) {
930
+ if (has(lastLocation.query["params"], k)) {
931
+ lQuery[k] = 1;
932
+ }
933
+ }
934
+ if (tPath) {
935
+ if (!has(window, "history")) {
936
+ for (const qKey in lQuery) {
937
+ if (has(lQuery, qKey) && !has(tParams, qKey)) {
938
+ tParams[qKey] = "";
939
+ }
940
+ }
941
+ }
942
+ } else if (lParams) {
943
+ tPath = lPath;
944
+ tParams = assign({}, lParams, tParams);
945
+ }
946
+ updateUrl(
947
+ tPath,
948
+ tParams,
949
+ lastLocation,
950
+ replace,
951
+ silentFlag,
952
+ lQuery
953
+ );
954
+ },
955
+ /**
956
+ * Join multiple path segments into a single path.
957
+ */
958
+ join(...paths) {
959
+ let result = paths.join("/");
960
+ result = result.replace(/\/\.\//g, "/");
961
+ const doubleDotRegex = /\/[^/]+\/\.\.\//;
962
+ while (doubleDotRegex.test(result)) {
963
+ result = result.replace(doubleDotRegex, "/");
964
+ }
965
+ result = result.replace(/\/{2,}/g, "/");
966
+ return result;
967
+ },
968
+ /** Bind event listener */
969
+ on(event, handler) {
970
+ emitter2.on(event, handler);
971
+ return Router;
972
+ },
973
+ /** Unbind event listener */
974
+ off(event, handler) {
975
+ emitter2.off(event, handler);
976
+ return Router;
977
+ },
978
+ /** Fire event */
979
+ fire(event, data, remove) {
980
+ emitter2.fire(event, data, remove);
981
+ return Router;
982
+ },
983
+ /**
984
+ * Internal: bind hashchange and beforeunload events.
985
+ * Called by Framework.boot().
986
+ */
987
+ _bind() {
988
+ defaultTitle = document.title;
989
+ let lastHash = Router.parse().srcHash;
990
+ let suspend;
991
+ const watchChange = () => {
992
+ if (suspend) {
993
+ return;
994
+ }
995
+ const loc = Router.parse();
996
+ const newHash = loc.srcHash;
997
+ if (newHash !== lastHash) {
998
+ const changeEvent = {
999
+ p: 0,
1000
+ reject: () => {
1001
+ changeEvent.p = 1;
1002
+ suspend = "";
1003
+ updateHash(lastHash);
1004
+ },
1005
+ resolve: () => {
1006
+ changeEvent.p = 1;
1007
+ lastHash = newHash;
1008
+ suspend = "";
1009
+ updateHash(newHash);
1010
+ Router.diff();
1011
+ },
1012
+ prevent: () => {
1013
+ suspend = 1;
1014
+ }
1015
+ };
1016
+ Router.fire(
1017
+ ROUTER_EVENTS.CHANGE,
1018
+ changeEvent
1019
+ );
1020
+ if (!suspend && !changeEvent.p) {
1021
+ changeEvent.resolve();
1022
+ }
1023
+ }
1024
+ };
1025
+ Router.notify = watchChange;
1026
+ window.addEventListener("hashchange", watchChange);
1027
+ window.addEventListener("popstate", watchChange);
1028
+ window.addEventListener("beforeunload", (domEvent) => {
1029
+ const te = {};
1030
+ Router.fire(ROUTER_EVENTS.PAGE_UNLOAD, te);
1031
+ const msg = te["msg"];
1032
+ if (msg) {
1033
+ domEvent.returnValue = msg;
1034
+ }
1035
+ });
1036
+ Router.diff();
1037
+ },
1038
+ /**
1039
+ * Internal: set framework config for route resolution.
1040
+ */
1041
+ _setConfig(cfg) {
1042
+ frameworkConfig = cfg;
1043
+ }
1044
+ };
1045
+ function markRouterBooted() {
1046
+ booted2 = true;
1047
+ }
1048
+
1049
+ // src/event-delegator.ts
1050
+ var rootEvents = {};
1051
+ var selectorEvents = {};
1052
+ var rangeEvents = {};
1053
+ var rangeFrames = {};
1054
+ var elementGuid = 0;
1055
+ var eventInfoCache = new Cache({
1056
+ maxSize: 30,
1057
+ bufferSize: 10
1058
+ });
1059
+ var frameGetter;
1060
+ function parseEventInfo(eventInfo) {
1061
+ const cached = eventInfoCache.get(eventInfo);
1062
+ if (cached) {
1063
+ return assign({}, cached, { r: eventInfo });
1064
+ }
1065
+ const match = eventInfo.match(EVT_METHOD_REG) || [];
1066
+ const result = {
1067
+ v: match[1] || "",
1068
+ n: match[2] || "",
1069
+ i: match[3] || ""
1070
+ };
1071
+ eventInfoCache.set(eventInfo, result);
1072
+ return assign({}, result, { r: eventInfo });
1073
+ }
1074
+ function findFrameInfo(current, eventType) {
1075
+ const eventInfos = [];
1076
+ let begin = current;
1077
+ const info = current.getAttribute(`lark-${eventType}`);
1078
+ let match;
1079
+ if (info) {
1080
+ match = parseEventInfo(info);
1081
+ }
1082
+ if (match && !match.v || selectorEvents[eventType]) {
1083
+ let selectorFrameId = "#";
1084
+ let backtrace = 0;
1085
+ while (begin && begin !== document.body) {
1086
+ const beginId = begin.id;
1087
+ if (beginId && frameGetter?.(beginId)) {
1088
+ selectorFrameId = beginId;
1089
+ break;
1090
+ }
1091
+ begin = begin.parentElement;
1092
+ }
1093
+ const currentId = current.id;
1094
+ if (currentId && frameGetter?.(currentId)) {
1095
+ backtrace = 1;
1096
+ selectorFrameId = currentId;
1097
+ }
1098
+ let frameId = selectorFrameId;
1099
+ do {
1100
+ const frame = frameId ? frameGetter?.(frameId) : void 0;
1101
+ if (frame) {
1102
+ const view = frame.view;
1103
+ if (view) {
1104
+ const selectorEntry = view.eventSelectorMap[eventType];
1105
+ if (selectorEntry) {
1106
+ for (const selectorName of selectorEntry.selectors) {
1107
+ const entry = {
1108
+ r: selectorName,
1109
+ v: frameId,
1110
+ n: selectorName,
1111
+ i: ""
1112
+ };
1113
+ if (selectorName) {
1114
+ if (!backtrace && elementMatchesSelector(current, selectorName)) {
1115
+ eventInfos.push(entry);
1116
+ }
1117
+ } else if (backtrace) {
1118
+ eventInfos.unshift(entry);
1119
+ }
1120
+ }
1121
+ }
1122
+ if (view.tmpl && !backtrace) {
1123
+ if (match && !match.v) {
1124
+ match.v = frameId;
1125
+ }
1126
+ break;
1127
+ }
1128
+ backtrace = 0;
1129
+ }
1130
+ }
1131
+ if (frame) {
1132
+ frameId = frame.parentId || "";
1133
+ } else {
1134
+ break;
1135
+ }
1136
+ } while (frameId);
1137
+ }
1138
+ if (match) {
1139
+ eventInfos.push({
1140
+ v: match.v,
1141
+ r: match.r,
1142
+ n: match.n,
1143
+ i: match.i
1144
+ });
1145
+ }
1146
+ return eventInfos;
1147
+ }
1148
+ function elementMatchesSelector(element, selector) {
1149
+ try {
1150
+ return element.matches?.(selector) ?? false;
1151
+ } catch {
1152
+ return false;
1153
+ }
1154
+ }
1155
+ function domEventProcessor(domEvent) {
1156
+ const target = domEvent.target;
1157
+ const eventType = domEvent.type;
1158
+ let lastFrameId = "";
1159
+ let current = target;
1160
+ while (current && current !== document.body) {
1161
+ const eventInfos = findFrameInfo(current, eventType);
1162
+ if (eventInfos.length) {
1163
+ for (const info of eventInfos) {
1164
+ const { v: frameId, n: handlerName, i: params } = info;
1165
+ if (lastFrameId !== frameId) {
1166
+ if (lastFrameId && domEvent.isPropagationStopped?.()) {
1167
+ break;
1168
+ }
1169
+ lastFrameId = frameId;
1170
+ }
1171
+ const frame = frameId ? frameGetter?.(frameId) : void 0;
1172
+ const view = frame?.view;
1173
+ if (view) {
1174
+ const eventName = handlerName + SPLITTER + eventType;
1175
+ const fn = Reflect.get(view, eventName);
1176
+ if (fn) {
1177
+ const extendedEvent = domEvent;
1178
+ extendedEvent.eventTarget = target;
1179
+ extendedEvent.params = params ? parseUri(params).params : {};
1180
+ funcWithTry(fn, [extendedEvent], view, noop);
1181
+ }
1182
+ }
1183
+ }
1184
+ }
1185
+ const rangeFrameId = current.getAttribute("data-range-fid");
1186
+ const rangeGuid = current.getAttribute("data-range-guid");
1187
+ if (rangeFrameId && rangeGuid) {
1188
+ const rangeMap = rangeEvents[rangeFrameId];
1189
+ if (rangeMap?.[rangeGuid]?.[eventType]) {
1190
+ break;
1191
+ }
1192
+ }
1193
+ if (domEvent.isPropagationStopped?.()) {
1194
+ break;
1195
+ }
1196
+ current = current.parentElement;
1197
+ }
1198
+ }
1199
+ var EventDelegator = {
1200
+ /**
1201
+ * Bind a DOM event type to document body.
1202
+ */
1203
+ bind(eventType, hasSelector = false) {
1204
+ const counter = rootEvents[eventType] || 0;
1205
+ if (counter === 0) {
1206
+ document.body.addEventListener(eventType, domEventProcessor, true);
1207
+ }
1208
+ rootEvents[eventType] = counter + 1;
1209
+ if (hasSelector) {
1210
+ selectorEvents[eventType] = (selectorEvents[eventType] || 0) + 1;
1211
+ }
1212
+ },
1213
+ /**
1214
+ * Unbind a DOM event type from document body.
1215
+ */
1216
+ unbind(eventType, hasSelector = false) {
1217
+ const counter = rootEvents[eventType] || 0;
1218
+ if (counter <= 1) {
1219
+ document.body.removeEventListener(eventType, domEventProcessor, true);
1220
+ Reflect.deleteProperty(rootEvents, eventType);
1221
+ } else {
1222
+ rootEvents[eventType] = counter - 1;
1223
+ }
1224
+ if (hasSelector) {
1225
+ const selectorCounter = selectorEvents[eventType] || 0;
1226
+ if (selectorCounter <= 1) {
1227
+ Reflect.deleteProperty(selectorEvents, eventType);
1228
+ } else {
1229
+ selectorEvents[eventType] = selectorCounter - 1;
1230
+ }
1231
+ }
1232
+ },
1233
+ /**
1234
+ * Clean up range events for a destroyed view.
1235
+ */
1236
+ clearRangeEvents(viewId) {
1237
+ Reflect.deleteProperty(rangeEvents, viewId);
1238
+ Reflect.deleteProperty(rangeFrames, viewId);
1239
+ },
1240
+ /**
1241
+ * Set the frame getter function (called by Framework.boot).
1242
+ */
1243
+ setFrameGetter(getter) {
1244
+ frameGetter = getter;
1245
+ },
1246
+ /**
1247
+ * Get next element GUID.
1248
+ */
1249
+ nextElementGuid() {
1250
+ return ++elementGuid;
1251
+ }
1252
+ };
1253
+
1254
+ // src/vdom.ts
1255
+ var WrapMeta = {
1256
+ option: [1, "<select multiple>"],
1257
+ thead: [1, "<table>"],
1258
+ col: [2, "<table><colgroup>"],
1259
+ tr: [2, "<table><tbody>"],
1260
+ td: [3, "<table><tbody><tr>"],
1261
+ area: [1, "<map>"],
1262
+ param: [1, "<object>"],
1263
+ g: [1, '<svg xmlns="' + SVG_NS + '">'],
1264
+ m: [1, '<math xmlns="' + MATH_NS + '">'],
1265
+ _: [0, ""]
1266
+ };
1267
+ WrapMeta["optgroup"] = WrapMeta["option"];
1268
+ WrapMeta["tbody"] = WrapMeta["tfoot"] = WrapMeta["colgroup"] = WrapMeta["caption"] = WrapMeta["thead"];
1269
+ WrapMeta["th"] = WrapMeta["td"];
1270
+ var VDoc = document.implementation.createHTMLDocument("");
1271
+ var VBase = VDoc.createElement("base");
1272
+ VBase.href = document.location.href;
1273
+ VDoc.head.appendChild(VBase);
1274
+ var VDomSpecials = {
1275
+ INPUT: ["value", "checked"],
1276
+ TEXTAREA: ["value"],
1277
+ OPTION: ["selected"]
1278
+ };
1279
+ function vdomUnmountFrames(frame, node) {
1280
+ if (node.nodeType === 1) {
1281
+ const el = node;
1282
+ const id = el.getAttribute("id");
1283
+ if (id) {
1284
+ frame.unmountZone(id);
1285
+ const children = frame.children();
1286
+ if (children.includes(id)) {
1287
+ frame.unmountFrame(id);
1288
+ }
1289
+ }
1290
+ }
1291
+ }
1292
+ function vdomGetNode(html, refNode) {
1293
+ const tmp = VDoc.createElement("div");
1294
+ const ns = refNode.namespaceURI;
1295
+ let tag;
1296
+ if (ns === SVG_NS) {
1297
+ tag = "g";
1298
+ } else if (ns === MATH_NS) {
1299
+ tag = "m";
1300
+ } else {
1301
+ const match = TAG_NAME_REGEX.exec(html);
1302
+ tag = match ? match[1] : "";
1303
+ }
1304
+ const wrap = WrapMeta[tag] || WrapMeta["_"];
1305
+ tmp.innerHTML = wrap[1] + html;
1306
+ let j = wrap[0];
1307
+ while (j--) {
1308
+ const last = tmp.lastChild;
1309
+ if (last) tmp.replaceChildren(last);
1310
+ }
1311
+ return tmp;
1312
+ }
1313
+ function vdomGetCompareKey(node) {
1314
+ if (node.nodeType !== 1) return void 0;
1315
+ const el = node;
1316
+ if (el.compareKeyCached) {
1317
+ return el.cachedCompareKey;
1318
+ }
1319
+ let key = el.autoId ? "" : el.getAttribute("id") || void 0;
1320
+ if (!key) {
1321
+ key = el.getAttribute(TAG_KEY) || void 0;
1322
+ }
1323
+ if (!key) {
1324
+ const larkView = el.getAttribute(LARK_VIEW);
1325
+ if (larkView) {
1326
+ key = parseUri(larkView).path || void 0;
1327
+ }
1328
+ }
1329
+ el.compareKeyCached = 1;
1330
+ el.cachedCompareKey = key || "";
1331
+ return key;
1332
+ }
1333
+ function vdomSpecialDiff(oldNode, newNode) {
1334
+ const nodeName = oldNode.nodeName;
1335
+ const specials = VDomSpecials[nodeName];
1336
+ if (!specials) return 0;
1337
+ const oldEl = oldNode;
1338
+ const newEl = newNode;
1339
+ let result = 0;
1340
+ for (const prop of specials) {
1341
+ if (oldEl[prop] !== newEl[prop]) {
1342
+ result = 1;
1343
+ oldEl[prop] = newEl[prop];
1344
+ }
1345
+ }
1346
+ return result;
1347
+ }
1348
+ function vdomSetAttributes(oldNode, newNode, ref, keepId) {
1349
+ const oldEl = oldNode;
1350
+ Reflect.deleteProperty(oldEl, "compareKeyCached");
1351
+ const oldAttrs = oldNode.attributes;
1352
+ const newAttrs = newNode.attributes;
1353
+ for (let i = oldAttrs.length; i--; ) {
1354
+ const name = oldAttrs[i].name;
1355
+ if (!newNode.hasAttribute(name)) {
1356
+ if (name === "id") {
1357
+ if (!keepId) {
1358
+ ref.idUpdates.push([oldNode, ""]);
1359
+ }
1360
+ } else {
1361
+ ref.hasChanged = 1;
1362
+ oldNode.removeAttribute(name);
1363
+ }
1364
+ }
1365
+ }
1366
+ for (let i = newAttrs.length; i--; ) {
1367
+ const attr = newAttrs[i];
1368
+ const key = attr.name;
1369
+ const value = attr.value;
1370
+ if (oldNode.getAttribute(key) !== value) {
1371
+ if (key === "id") {
1372
+ ref.idUpdates.push([oldNode, value]);
1373
+ } else {
1374
+ ref.hasChanged = 1;
1375
+ oldNode.setAttribute(key, value);
1376
+ }
1377
+ }
1378
+ }
1379
+ }
1380
+ function vdomSetChildNodes(oldParent, newParent, ref, frame, keys_) {
1381
+ let oldNode = oldParent.lastChild;
1382
+ let newNode = newParent.firstChild;
1383
+ let extra = 0;
1384
+ const keyedNodes = {};
1385
+ const newKeyedNodes = {};
1386
+ while (oldNode) {
1387
+ extra++;
1388
+ const nodeKey = vdomGetCompareKey(oldNode);
1389
+ if (nodeKey) {
1390
+ const bucket = keyedNodes[nodeKey] || (keyedNodes[nodeKey] = []);
1391
+ bucket.push(oldNode);
1392
+ }
1393
+ oldNode = oldNode.previousSibling;
1394
+ }
1395
+ while (newNode) {
1396
+ const nodeKey = vdomGetCompareKey(newNode);
1397
+ if (nodeKey) {
1398
+ newKeyedNodes[nodeKey] = (newKeyedNodes[nodeKey] || 0) + 1;
1399
+ }
1400
+ newNode = newNode.nextSibling;
1401
+ }
1402
+ newNode = newParent.firstChild;
1403
+ oldNode = oldParent.firstChild;
1404
+ while (newNode) {
1405
+ extra--;
1406
+ const tempNew = newNode;
1407
+ newNode = newNode.nextSibling;
1408
+ const nodeKey = vdomGetCompareKey(tempNew);
1409
+ let foundNode = nodeKey ? keyedNodes[nodeKey] : void 0;
1410
+ if (foundNode && (foundNode = foundNode.slice()) && foundNode.length) {
1411
+ const matched = foundNode.pop();
1412
+ while (matched !== oldNode) {
1413
+ const next = oldNode?.nextSibling ?? null;
1414
+ oldParent.appendChild(oldNode);
1415
+ oldNode = next;
1416
+ }
1417
+ oldNode = matched.nextSibling;
1418
+ if (nodeKey && newKeyedNodes[nodeKey]) {
1419
+ newKeyedNodes[nodeKey]--;
1420
+ }
1421
+ vdomSetNode(matched, tempNew, oldParent, ref, frame, keys_);
1422
+ } else if (oldNode) {
1423
+ const tempOld2 = oldNode;
1424
+ const oldKey = vdomGetCompareKey(tempOld2);
1425
+ if (oldKey && keyedNodes[oldKey] && newKeyedNodes[oldKey]) {
1426
+ extra++;
1427
+ ref.hasChanged = 1;
1428
+ ref.domOps.push([8, oldParent, tempNew, tempOld2]);
1429
+ } else {
1430
+ oldNode = oldNode.nextSibling;
1431
+ vdomSetNode(tempOld2, tempNew, oldParent, ref, frame, keys_);
1432
+ }
1433
+ } else {
1434
+ ref.hasChanged = 1;
1435
+ ref.domOps.push([1, oldParent, tempNew]);
1436
+ }
1437
+ }
1438
+ let tempOld = oldParent.lastChild;
1439
+ while (extra-- > 0) {
1440
+ if (tempOld) {
1441
+ vdomUnmountFrames(frame, tempOld);
1442
+ ref.domOps.push([2, oldParent, tempOld]);
1443
+ tempOld = tempOld.previousSibling;
1444
+ ref.hasChanged = 1;
1445
+ }
1446
+ }
1447
+ }
1448
+ function vdomSetNode(oldNode, newNode, oldParent, ref, frame, keys_) {
1449
+ if (vdomSpecialDiff(oldNode, newNode) || oldNode.nodeType === 1 && oldNode.hasAttribute(TAG_VIEW_KEY) || !(oldNode.isEqualNode && oldNode.isEqualNode(newNode))) {
1450
+ if (oldNode.nodeType === newNode.nodeType && oldNode.nodeName === newNode.nodeName) {
1451
+ if (oldNode.nodeType === 1) {
1452
+ const oldEl = oldNode;
1453
+ const newEl = newNode;
1454
+ const staticKey = newEl.getAttribute(TAG_KEY);
1455
+ if (staticKey && staticKey === oldEl.getAttribute(TAG_KEY)) {
1456
+ return;
1457
+ }
1458
+ const newLarkView = newEl.getAttribute(LARK_VIEW);
1459
+ const updateAttribute = !newEl.getAttribute(TAG_ATTR_KEY) || newEl.getAttribute(TAG_ATTR_KEY) !== oldEl.getAttribute(TAG_ATTR_KEY);
1460
+ let updateChildren = true;
1461
+ if (newLarkView) {
1462
+ const oldFrameId = oldEl.getAttribute("id") || "";
1463
+ const newViewPath = parseUri(newLarkView).path;
1464
+ const oldLarkView = oldEl.getAttribute(LARK_VIEW);
1465
+ const oldViewPath = oldLarkView ? parseUri(oldLarkView).path : "";
1466
+ if (oldFrameId && newViewPath === oldViewPath) {
1467
+ updateChildren = false;
1468
+ }
1469
+ }
1470
+ if (updateAttribute) {
1471
+ vdomSetAttributes(oldEl, newEl, ref, !!newLarkView);
1472
+ }
1473
+ if (updateChildren) {
1474
+ vdomSetChildNodes(oldEl, newEl, ref, frame, keys_);
1475
+ }
1476
+ } else if (oldNode.nodeValue !== newNode.nodeValue) {
1477
+ ref.hasChanged = 1;
1478
+ oldNode.nodeValue = newNode.nodeValue;
1479
+ }
1480
+ } else {
1481
+ ref.hasChanged = 1;
1482
+ vdomUnmountFrames(frame, oldNode);
1483
+ ref.domOps.push([4, oldParent, newNode, oldNode]);
1484
+ }
1485
+ }
1486
+ }
1487
+ function createVdomRef() {
1488
+ return {
1489
+ idUpdates: [],
1490
+ views: [],
1491
+ domOps: [],
1492
+ hasChanged: 0
1493
+ };
1494
+ }
1495
+ function applyVdomOps(ops) {
1496
+ for (const op of ops) {
1497
+ switch (op[0]) {
1498
+ case 1:
1499
+ op[1].appendChild(op[2]);
1500
+ break;
1501
+ case 2:
1502
+ op[1].removeChild(op[2]);
1503
+ break;
1504
+ case 4:
1505
+ op[1].replaceChild(op[2], op[3]);
1506
+ break;
1507
+ case 8:
1508
+ op[1].insertBefore(op[2], op[3]);
1509
+ break;
1510
+ }
1511
+ }
1512
+ }
1513
+ function applyIdUpdates(updates) {
1514
+ for (const [element, newId] of updates) {
1515
+ if (newId) {
1516
+ element.setAttribute("id", newId);
1517
+ } else {
1518
+ element.removeAttribute("id");
1519
+ }
1520
+ }
1521
+ }
1522
+ var EncoderMap = {
1523
+ "&": "amp",
1524
+ "<": "lt",
1525
+ ">": "gt",
1526
+ '"': "#34",
1527
+ "'": "#39",
1528
+ "`": "#96"
1529
+ };
1530
+ var ENCODE_REGEX = /[&<>"'`]/g;
1531
+ function encodeHTML(v) {
1532
+ return String(v == null ? "" : v).replace(
1533
+ ENCODE_REGEX,
1534
+ (m) => "&" + EncoderMap[m] + ";"
1535
+ );
1536
+ }
1537
+ function encodeSafe(v) {
1538
+ return String(v == null ? "" : v);
1539
+ }
1540
+ var URIMap = {
1541
+ "!": "%21",
1542
+ "'": "%27",
1543
+ "(": "%28",
1544
+ ")": "%29",
1545
+ "*": "%2A"
1546
+ };
1547
+ var URI_ENCODE_REGEX = /[!')(*]/g;
1548
+ function encodeURIExtra(v) {
1549
+ return encodeURIComponent(encodeSafe(v)).replace(
1550
+ URI_ENCODE_REGEX,
1551
+ (m) => URIMap[m]
1552
+ );
1553
+ }
1554
+ var QUOTE_REGEX = /['"\\]/g;
1555
+ function encodeQ(v) {
1556
+ return encodeSafe(v).replace(QUOTE_REGEX, "\\$&");
1557
+ }
1558
+
1559
+ // src/updater.ts
1560
+ function updaterRef(refData, value, key) {
1561
+ const counter = refData[SPLITTER];
1562
+ for (let i = counter; --i; ) {
1563
+ key = SPLITTER + i;
1564
+ if (refData[key] === value) {
1565
+ return key;
1566
+ }
1567
+ }
1568
+ key = SPLITTER + refData[SPLITTER]++;
1569
+ refData[key] = value;
1570
+ return key;
1571
+ }
1572
+ var Updater = class {
1573
+ /** View ID (same as owner frame ID) */
1574
+ viewId;
1575
+ /** Current data object */
1576
+ data;
1577
+ /** Ref data for template rendering */
1578
+ refData;
1579
+ /** Changed keys in current digest cycle */
1580
+ changedKeys = {};
1581
+ /** Whether data has changed since last digest */
1582
+ hasChangedFlag = 0;
1583
+ /** Digesting queue: supports re-digest during digest */
1584
+ digestingQueue = [];
1585
+ /** Snapshot JSON string for altered() detection */
1586
+ snapshotJson;
1587
+ constructor(viewId) {
1588
+ this.viewId = viewId;
1589
+ this.data = { vId: viewId };
1590
+ const refCounter = {};
1591
+ refCounter[SPLITTER] = 1;
1592
+ this.refData = refCounter;
1593
+ this.hasChangedFlag = 1;
1594
+ }
1595
+ /**
1596
+ * Get data by key.
1597
+ * Returns entire data object if key is omitted.
1598
+ */
1599
+ get(key) {
1600
+ let result = this.data;
1601
+ if (key) {
1602
+ result = this.data[key];
1603
+ }
1604
+ if (typeof window !== "undefined" && window.__lark_debug) {
1605
+ return safeguard(result);
1606
+ }
1607
+ return result;
1608
+ }
1609
+ /**
1610
+ * Set data, tracking changed keys.
1611
+ * Returns this for chaining.
1612
+ */
1613
+ set(data, excludes) {
1614
+ this.hasChangedFlag = setData(data, this.data, this.changedKeys, excludes || /* @__PURE__ */ new Set()) || this.hasChangedFlag ? 1 : 0;
1615
+ return this;
1616
+ }
1617
+ /**
1618
+ * Detect changes and trigger VDOM re-render.
1619
+ *
1620
+ * The core rendering pipeline:
1621
+ * 1. Set data if provided
1622
+ * 2. If changed, run VDOM diff (template → new DOM → diff against old DOM)
1623
+ * 3. Apply DOM operations
1624
+ * 4. Apply ID updates
1625
+ * 5. Call endUpdate on views that need re-rendering
1626
+ * 6. Support re-digest during digest via queue
1627
+ */
1628
+ digest(data, excludes, callback) {
1629
+ if (data) {
1630
+ this.set(data, excludes);
1631
+ }
1632
+ const digesting = this.digestingQueue;
1633
+ if (callback) {
1634
+ digesting.push(callback);
1635
+ }
1636
+ if (digesting.length > 0 && digesting[0] === null) {
1637
+ return;
1638
+ }
1639
+ this.runDigest(digesting);
1640
+ }
1641
+ /**
1642
+ * Core digest execution.
1643
+ */
1644
+ runDigest(digesting) {
1645
+ const startIndex = digesting.length;
1646
+ digesting.push(null);
1647
+ const keys2 = this.changedKeys;
1648
+ const changed = this.hasChangedFlag;
1649
+ this.hasChangedFlag = 0;
1650
+ this.changedKeys = {};
1651
+ const frame = Frame.get(this.viewId);
1652
+ const view = frame?.view;
1653
+ const node = getById(this.viewId);
1654
+ if (changed && view && node && view.signature > 0) {
1655
+ const tmpl = view.tmpl;
1656
+ if (tmpl && typeof tmpl === "function") {
1657
+ const html = tmpl(
1658
+ this.data,
1659
+ this.viewId,
1660
+ this.refData,
1661
+ encodeHTML,
1662
+ encodeSafe,
1663
+ encodeURIExtra,
1664
+ updaterRef,
1665
+ encodeQ
1666
+ );
1667
+ const newDom = vdomGetNode(html, node);
1668
+ const ref = createVdomRef();
1669
+ vdomSetChildNodes(node, newDom, ref, frame, keys2);
1670
+ applyIdUpdates(ref.idUpdates);
1671
+ applyVdomOps(ref.domOps);
1672
+ for (const v of ref.views) {
1673
+ if (v.render) {
1674
+ funcWithTry(v.render, [], v, noop);
1675
+ }
1676
+ }
1677
+ if (ref.hasChanged || !view.rendered) {
1678
+ view.endUpdate(this.viewId);
1679
+ }
1680
+ }
1681
+ }
1682
+ if (digesting.length > startIndex + 1) {
1683
+ this.runDigest(digesting);
1684
+ } else {
1685
+ const callbacks = digesting.slice();
1686
+ digesting.length = 0;
1687
+ for (const cb of callbacks) {
1688
+ if (cb) cb();
1689
+ }
1690
+ }
1691
+ }
1692
+ /**
1693
+ * Save a snapshot of current data for altered() detection.
1694
+ */
1695
+ snapshot() {
1696
+ this.snapshotJson = JSON.stringify(this.data);
1697
+ return this;
1698
+ }
1699
+ /**
1700
+ * Check if data has changed since last snapshot.
1701
+ */
1702
+ altered() {
1703
+ if (this.snapshotJson) {
1704
+ return this.snapshotJson !== JSON.stringify(this.data);
1705
+ }
1706
+ return void 0;
1707
+ }
1708
+ /**
1709
+ * Translate data references (SPLITTER-prefixed values).
1710
+ */
1711
+ translate(data) {
1712
+ if (typeof data === "string" && data[0] === SPLITTER) {
1713
+ return has(this.refData, data) ? this.refData[data] : data;
1714
+ }
1715
+ return data;
1716
+ }
1717
+ /**
1718
+ * Parse expression with data context.
1719
+ */
1720
+ parse(expr) {
1721
+ try {
1722
+ const fn = new Function("data", `with(data) { return ${expr}; }`);
1723
+ return fn(this.refData);
1724
+ } catch {
1725
+ return void 0;
1726
+ }
1727
+ }
1728
+ /**
1729
+ * Get changed keys (for external inspection).
1730
+ */
1731
+ getChangedKeys() {
1732
+ return this.changedKeys;
1733
+ }
1734
+ };
1735
+
1736
+ // src/view.ts
1737
+ var VIEW_GLOBALS = {};
1738
+ if (typeof window !== "undefined") {
1739
+ VIEW_GLOBALS["win"] = window;
1740
+ }
1741
+ if (typeof document !== "undefined") {
1742
+ VIEW_GLOBALS["doc"] = document;
1743
+ }
1744
+ function viewPrepare(oView) {
1745
+ if (oView.ctors) {
1746
+ return oView.ctors;
1747
+ }
1748
+ const ctors = [];
1749
+ oView.ctors = ctors;
1750
+ const proto = oView.prototype;
1751
+ const eventsObject = {};
1752
+ const eventsList = [];
1753
+ const selectorObject = {};
1754
+ const mixins = proto["mixins"];
1755
+ if (mixins && Array.isArray(mixins)) {
1756
+ viewMergeMixins(mixins, oView, ctors);
1757
+ }
1758
+ for (const p in proto) {
1759
+ if (!has(proto, p)) continue;
1760
+ const currentFn = proto[p];
1761
+ if (typeof currentFn !== "function") continue;
1762
+ const matches = p.match(VIEW_EVT_METHOD_REG);
1763
+ if (!matches) continue;
1764
+ const isSelector = matches[1];
1765
+ const selectorOrCallback = matches[2];
1766
+ const events = matches[3];
1767
+ const modifiers = matches[4];
1768
+ const mod = {};
1769
+ if (modifiers) {
1770
+ for (const item of modifiers.split(",")) {
1771
+ mod[item] = true;
1772
+ }
1773
+ }
1774
+ const eventTypes = events.split(",");
1775
+ for (const item of eventTypes) {
1776
+ const globalNode = VIEW_GLOBALS[selectorOrCallback];
1777
+ let mask = 1;
1778
+ if (isSelector) {
1779
+ if (globalNode) {
1780
+ eventsList.push({
1781
+ handler: currentFn,
1782
+ element: globalNode,
1783
+ eventName: item,
1784
+ modifiers: mod
1785
+ });
1786
+ continue;
1787
+ }
1788
+ mask = 2;
1789
+ let selectorEntry = selectorObject[item];
1790
+ if (!selectorEntry) {
1791
+ selectorEntry = selectorObject[item] = {
1792
+ selectors: []
1793
+ };
1794
+ }
1795
+ if (!selectorEntry[selectorOrCallback]) {
1796
+ selectorEntry[selectorOrCallback] = 1;
1797
+ selectorEntry.selectors.push(selectorOrCallback);
1798
+ }
1799
+ }
1800
+ eventsObject[item] = (eventsObject[item] || 0) | mask;
1801
+ const combinedKey = selectorOrCallback + SPLITTER + item;
1802
+ const existingFn = proto[combinedKey];
1803
+ if (!existingFn) {
1804
+ proto[combinedKey] = currentFn;
1805
+ } else {
1806
+ const mixinFn = currentFn;
1807
+ const existingMixin = existingFn;
1808
+ if (existingMixin.b) {
1809
+ if (mixinFn.b) {
1810
+ proto[combinedKey] = processMixinsSameEvent(
1811
+ currentFn,
1812
+ existingFn
1813
+ );
1814
+ } else if (has(proto, p)) {
1815
+ proto[combinedKey] = currentFn;
1816
+ }
1817
+ }
1818
+ }
1819
+ }
1820
+ }
1821
+ viewWrapMethod(proto, "render", "$b");
1822
+ proto["$eo"] = eventsObject;
1823
+ proto["$el"] = eventsList;
1824
+ proto["$so"] = selectorObject;
1825
+ proto["$f"] = proto["assign"];
1826
+ return ctors;
1827
+ }
1828
+ function viewWrapMethod(proto, fnName, shortKey) {
1829
+ const originalFn = proto[fnName];
1830
+ if (typeof originalFn !== "function") return;
1831
+ const wrapped = function(...args) {
1832
+ if (this.signature > 0) {
1833
+ this.signature++;
1834
+ this.fire("rendercall");
1835
+ destroyAllResources(this, false);
1836
+ const instanceFn = typeof this[fnName] === "function" ? this[fnName] : originalFn;
1837
+ const fnToCall = instanceFn === wrapped ? originalFn : instanceFn;
1838
+ return funcWithTry(fnToCall, args, this, noop);
1839
+ }
1840
+ return void 0;
1841
+ };
1842
+ proto[fnName] = wrapped;
1843
+ proto[shortKey] = wrapped;
1844
+ }
1845
+ function processMixinsSameEvent(additional, exist) {
1846
+ let temp;
1847
+ const existMixin = exist;
1848
+ if (existMixin.a) {
1849
+ temp = existMixin;
1850
+ } else {
1851
+ temp = function(...e) {
1852
+ funcWithTry(temp.a ?? [], e, this, noop);
1853
+ };
1854
+ temp.a = [exist];
1855
+ temp.b = 1;
1856
+ }
1857
+ const additionalMixin = additional;
1858
+ temp.a = (temp.a ?? []).concat(additionalMixin.a ?? [additional]);
1859
+ return temp;
1860
+ }
1861
+ function viewMergeMixins(mixins, viewClass, ctors) {
1862
+ const proto = viewClass.prototype;
1863
+ const temp = {};
1864
+ for (const node of mixins) {
1865
+ for (const p in node) {
1866
+ if (!has(node, p)) continue;
1867
+ const fn = node[p];
1868
+ const exist = temp[p];
1869
+ if (p === "ctor") {
1870
+ ctors.push(fn);
1871
+ continue;
1872
+ }
1873
+ if (VIEW_EVT_METHOD_REG.test(p)) {
1874
+ if (exist) {
1875
+ temp[p] = processMixinsSameEvent(fn, exist);
1876
+ } else {
1877
+ fn.b = 1;
1878
+ temp[p] = fn;
1879
+ }
1880
+ } else {
1881
+ if (!exist) {
1882
+ temp[p] = fn;
1883
+ }
1884
+ }
1885
+ }
1886
+ }
1887
+ for (const p in temp) {
1888
+ if (!has(proto, p)) {
1889
+ proto[p] = temp[p];
1890
+ }
1891
+ }
1892
+ }
1893
+ function viewDelegateEvents(view, destroy = false) {
1894
+ const proto = Object.getPrototypeOf(view) ?? {};
1895
+ const eventsObject = proto["$eo"] || view.eventObjectMap;
1896
+ const selectorObject = proto["$so"] || view.eventSelectorMap;
1897
+ const eventsList = proto["$el"] || view.globalEventList;
1898
+ for (const e in eventsObject) {
1899
+ if (has(eventsObject, e)) {
1900
+ if (destroy) {
1901
+ EventDelegator.unbind(e, !!selectorObject[e]);
1902
+ } else {
1903
+ EventDelegator.bind(e, !!selectorObject[e]);
1904
+ }
1905
+ }
1906
+ }
1907
+ for (const entry of eventsList) {
1908
+ if (destroy) {
1909
+ entry.element.removeEventListener(
1910
+ entry.eventName,
1911
+ entry.boundHandler
1912
+ );
1913
+ } else {
1914
+ const handler = entry.handler;
1915
+ const element = entry.element;
1916
+ const modifiers = entry.modifiers;
1917
+ entry.boundHandler = function(domEvent) {
1918
+ const extendedEvent = domEvent;
1919
+ extendedEvent.eventTarget = element;
1920
+ if (modifiers) {
1921
+ const kbEvent = domEvent;
1922
+ if (modifiers["ctrl"] && !kbEvent.ctrlKey || modifiers["shift"] && !kbEvent.shiftKey || modifiers["alt"] && !kbEvent.altKey || modifiers["meta"] && !kbEvent.metaKey) {
1923
+ return;
1924
+ }
1925
+ }
1926
+ funcWithTry(handler, [domEvent], view, noop);
1927
+ };
1928
+ entry.element.addEventListener(
1929
+ entry.eventName,
1930
+ entry.boundHandler
1931
+ );
1932
+ }
1933
+ }
1934
+ }
1935
+ function destroyAllResources(view, lastly) {
1936
+ const cache = view.resources;
1937
+ for (const p in cache) {
1938
+ if (has(cache, p)) {
1939
+ const entry = cache[p];
1940
+ if (lastly || entry.destroyOnRender) {
1941
+ destroyResource(cache, p, true);
1942
+ }
1943
+ }
1944
+ }
1945
+ }
1946
+ function destroyResource(cache, key, callDestroy, oldEntity) {
1947
+ const entry = cache[key];
1948
+ if (!entry || entry.entity === oldEntity) return void 0;
1949
+ const entity = entry.entity;
1950
+ if (entity && typeof entity === "object") {
1951
+ const destroyFn = entity["destroy"];
1952
+ if (typeof destroyFn === "function" && callDestroy) {
1953
+ funcWithTry(destroyFn, [], entity, noop);
1954
+ }
1955
+ }
1956
+ Reflect.deleteProperty(cache, key);
1957
+ return entity;
1958
+ }
1959
+ var View = class _View {
1960
+ /** View ID (same as owner frame ID) */
1961
+ id = "";
1962
+ /** Owner frame */
1963
+ owner = 0;
1964
+ /** Updater instance */
1965
+ updater;
1966
+ /** Signature: > 0 means active, incremented on render, 0 = destroyed */
1967
+ signature = 0;
1968
+ /** Whether rendered at least once */
1969
+ rendered;
1970
+ /** Whether view has template */
1971
+ tmpl;
1972
+ /** Location observation config */
1973
+ locationObserved = {
1974
+ flag: 0,
1975
+ keys: [],
1976
+ observePath: false
1977
+ };
1978
+ /** Observed state keys */
1979
+ observedStateKeys;
1980
+ /** Resource map */
1981
+ resources = {};
1982
+ /** Selector event map: eventType -> handler name list */
1983
+ eventSelectorMap = {};
1984
+ /** Event object map: eventType -> bitmask */
1985
+ eventObjectMap = {};
1986
+ /** Global event list */
1987
+ globalEventList = [];
1988
+ /** Assign method reference */
1989
+ assignMethod;
1990
+ /** Whether endUpdate pending */
1991
+ endUpdatePending;
1992
+ /** Internal event storage */
1993
+ _events = new EventEmitter();
1994
+ /**
1995
+ * Initialize view (called by Frame when mounting).
1996
+ */
1997
+ init() {
1998
+ }
1999
+ /**
2000
+ * Render view template (called by Frame after init).
2001
+ * Wrapped by View_WrapMethod to manage signature + resources.
2002
+ *
2003
+ * Default implementation calls updater.digest() which:
2004
+ * 1. Executes the template function with current data
2005
+ * 2. Runs VDOM diff against previous DOM
2006
+ * 3. Applies DOM operations
2007
+ * 4. Calls endUpdate to mount child frames
2008
+ *
2009
+ */
2010
+ render() {
2011
+ this.updater.digest();
2012
+ }
2013
+ // ============================================================
2014
+ // Event methods (delegate to internal EventEmitter)
2015
+ // ============================================================
2016
+ on(event, handler) {
2017
+ this._events.on(event, handler);
2018
+ return this;
2019
+ }
2020
+ off(event, handler) {
2021
+ this._events.off(event, handler);
2022
+ return this;
2023
+ }
2024
+ fire(event, data, remove, lastToFirst) {
2025
+ this._events.fire(event, data, remove, lastToFirst);
2026
+ return this;
2027
+ }
2028
+ // ============================================================
2029
+ // Update methods
2030
+ // ============================================================
2031
+ /**
2032
+ * Notify view that HTML update is about to begin.
2033
+ * Unmounts child frames in the update zone.
2034
+ */
2035
+ beginUpdate(id) {
2036
+ if (this.signature > 0 && this.endUpdatePending !== void 0) {
2037
+ this.owner.unmountZone(id, true);
2038
+ }
2039
+ }
2040
+ /**
2041
+ * Notify view that HTML update has ended.
2042
+ * Mounts child frames in the update zone and runs deferred invokes.
2043
+ */
2044
+ endUpdate(id, inner) {
2045
+ if (this.signature > 0) {
2046
+ const updateId = id || this.id;
2047
+ let flag;
2048
+ if (inner) {
2049
+ flag = inner;
2050
+ } else {
2051
+ flag = this.endUpdatePending;
2052
+ this.endUpdatePending = 1;
2053
+ this.rendered = true;
2054
+ }
2055
+ const ownerFrame = this.owner;
2056
+ ownerFrame.mountZone(updateId, inner);
2057
+ if (!flag) {
2058
+ setTimeout(
2059
+ this.wrapAsync(() => {
2060
+ runInvokes(ownerFrame);
2061
+ }),
2062
+ 0
2063
+ );
2064
+ }
2065
+ }
2066
+ }
2067
+ // ============================================================
2068
+ // Async wrapper
2069
+ // ============================================================
2070
+ /**
2071
+ * Wrap an async callback to check view signature before executing.
2072
+ * If the view has been re-rendered or destroyed, the callback is skipped.
2073
+ */
2074
+ wrapAsync(fn, context) {
2075
+ const currentSignature = this.signature;
2076
+ return (...args) => {
2077
+ if (currentSignature > 0 && currentSignature === this.signature) {
2078
+ return fn.apply(context || this, args);
2079
+ }
2080
+ return void 0;
2081
+ };
2082
+ }
2083
+ // ============================================================
2084
+ // Location observation
2085
+ // ============================================================
2086
+ /**
2087
+ * Observe location parameters or path changes.
2088
+ * When observed keys change, render() is called automatically.
2089
+ */
2090
+ observeLocation(params, observePath = false) {
2091
+ const loc = this.locationObserved;
2092
+ loc.flag = 1;
2093
+ if (typeof params === "object" && !Array.isArray(params)) {
2094
+ if (params["path"]) {
2095
+ observePath = true;
2096
+ }
2097
+ const paramKeys = params["params"];
2098
+ if (paramKeys) {
2099
+ params = paramKeys;
2100
+ }
2101
+ }
2102
+ loc.observePath = observePath;
2103
+ if (params) {
2104
+ if (typeof params === "string") {
2105
+ loc.keys = params.split(",");
2106
+ } else if (Array.isArray(params)) {
2107
+ loc.keys = params;
2108
+ }
2109
+ }
2110
+ }
2111
+ // ============================================================
2112
+ // State observation
2113
+ // ============================================================
2114
+ /**
2115
+ * Observe State data keys for changes.
2116
+ * When observed keys change via State.digest(), render() is called.
2117
+ */
2118
+ observeState(observedKeys) {
2119
+ if (typeof observedKeys === "string") {
2120
+ this.observedStateKeys = observedKeys.split(",");
2121
+ } else {
2122
+ this.observedStateKeys = observedKeys;
2123
+ }
2124
+ }
2125
+ // ============================================================
2126
+ // Resource management
2127
+ // ============================================================
2128
+ /**
2129
+ * Capture (register) a resource under a key.
2130
+ * If a resource already exists at that key, it's destroyed first.
2131
+ * When destroyOnRender=true, the resource is destroyed on next render call.
2132
+ */
2133
+ capture(key, resource, destroyOnRender = false) {
2134
+ const cache = this.resources;
2135
+ if (resource) {
2136
+ destroyResource(cache, key, true, resource);
2137
+ cache[key] = {
2138
+ entity: resource,
2139
+ destroyOnRender
2140
+ };
2141
+ } else {
2142
+ const entry = cache[key];
2143
+ return entry ? entry.entity : void 0;
2144
+ }
2145
+ return resource;
2146
+ }
2147
+ /**
2148
+ * Release a captured resource.
2149
+ * If destroy=true, calls the resource's destroy() method.
2150
+ */
2151
+ release(key, destroy = true) {
2152
+ return destroyResource(this.resources, key, destroy);
2153
+ }
2154
+ // ============================================================
2155
+ // Leave tip
2156
+ // ============================================================
2157
+ /**
2158
+ * Set up a leave confirmation for route changes and page unload.
2159
+ */
2160
+ leaveTip(message, condition) {
2161
+ const changeListener = function(e) {
2162
+ const isRouterChange = e.type === ROUTER_EVENTS.CHANGE;
2163
+ const aKey = isRouterChange ? "a" : "b";
2164
+ const bKey = isRouterChange ? "b" : "a";
2165
+ if (changeListener[aKey]) {
2166
+ if (typeof e.prevent === "function") {
2167
+ e.prevent();
2168
+ }
2169
+ if (typeof e.reject === "function") {
2170
+ e.reject();
2171
+ }
2172
+ } else if (condition()) {
2173
+ if (typeof e.prevent === "function") {
2174
+ e.prevent();
2175
+ }
2176
+ changeListener[bKey] = 1;
2177
+ if (typeof e.resolve === "function") {
2178
+ e.resolve();
2179
+ }
2180
+ }
2181
+ };
2182
+ const unloadListener = (e) => {
2183
+ if (condition()) {
2184
+ e["msg"] = message;
2185
+ }
2186
+ };
2187
+ const changeFn = changeListener;
2188
+ const unloadFn = unloadListener;
2189
+ Router.on(ROUTER_EVENTS.CHANGE, changeFn);
2190
+ Router.on(ROUTER_EVENTS.PAGE_UNLOAD, unloadFn);
2191
+ this.on("unload", changeFn);
2192
+ this.on("destroy", () => {
2193
+ Router.off(ROUTER_EVENTS.CHANGE, changeFn);
2194
+ Router.off(ROUTER_EVENTS.PAGE_UNLOAD, unloadFn);
2195
+ });
2196
+ }
2197
+ // ============================================================
2198
+ // Static: extend and merge
2199
+ // ============================================================
2200
+ /** Collected ctors from mixins */
2201
+ static ctors;
2202
+ /**
2203
+ * Extend View to create a new View subclass.
2204
+ *
2205
+ * Supports:
2206
+ * - props.ctor: constructor-like init (called with initParams + {node, deep})
2207
+ * - props.mixins: array of mixin objects
2208
+ * - Event method patterns: `'name<click>'` etc.
2209
+ */
2210
+ static extend(props, statics) {
2211
+ props = props || {};
2212
+ const ctor = props["ctor"];
2213
+ const ctors = [];
2214
+ if (ctor) {
2215
+ ctors.push(ctor);
2216
+ }
2217
+ const ParentView = this;
2218
+ const ChildView = class extends ParentView {
2219
+ constructor(nodeId, ownerFrame, initParams, node, mixinCtors) {
2220
+ super(nodeId, ownerFrame, initParams, node, []);
2221
+ for (const key in props) {
2222
+ if (has(props, key) && key !== "ctor") {
2223
+ this[key] = props[key];
2224
+ }
2225
+ }
2226
+ this.id = nodeId;
2227
+ this.owner = ownerFrame;
2228
+ this.updater = new Updater(nodeId);
2229
+ const params = [
2230
+ initParams,
2231
+ {
2232
+ node,
2233
+ deep: !this.tmpl
2234
+ }
2235
+ ];
2236
+ const concatCtors = ctors.concat(mixinCtors || []);
2237
+ if (concatCtors.length) {
2238
+ funcWithTry(concatCtors, params, this, noop);
2239
+ }
2240
+ }
2241
+ };
2242
+ const proto = ChildView.prototype;
2243
+ for (const key in props) {
2244
+ if (has(props, key) && key !== "ctor") {
2245
+ proto[key] = props[key];
2246
+ }
2247
+ }
2248
+ if (statics) {
2249
+ for (const key in statics) {
2250
+ if (has(statics, key)) {
2251
+ ChildView[key] = statics[key];
2252
+ }
2253
+ }
2254
+ }
2255
+ ChildView.merge = viewMerge;
2256
+ ChildView.extend = _View.extend;
2257
+ return ChildView;
2258
+ }
2259
+ /**
2260
+ * Merge mixins into View prototype.
2261
+ */
2262
+ static merge(...mixins) {
2263
+ const self = this;
2264
+ const existingCtors = self.ctors || [];
2265
+ viewMergeMixins(mixins, self, existingCtors);
2266
+ return self;
2267
+ }
2268
+ };
2269
+ function viewMerge(...mixins) {
2270
+ const self = this;
2271
+ const existingCtors = self.ctors || [];
2272
+ viewMergeMixins(mixins, self, existingCtors);
2273
+ return self;
2274
+ }
2275
+ function runInvokes(frame) {
2276
+ const list = frame.invokeList;
2277
+ if (!list) return;
2278
+ while (list.length) {
2279
+ const entry = list.shift();
2280
+ if (entry && !entry.removed) {
2281
+ frame.invoke(entry.name, entry.args);
2282
+ }
2283
+ }
2284
+ }
2285
+
2286
+ // src/frame.ts
2287
+ var frameRegistry = /* @__PURE__ */ new Map();
2288
+ var rootFrame;
2289
+ var globalAlter;
2290
+ var frameCache = [];
2291
+ var staticEmitter = new EventEmitter();
2292
+ var viewClassRegistry = {};
2293
+ var Frame = class _Frame extends EventEmitter {
2294
+ /** Frame ID (same as owner DOM element ID) */
2295
+ id;
2296
+ /** Parent Frame ID */
2297
+ _parentId = void 0;
2298
+ get parentId() {
2299
+ return this._parentId;
2300
+ }
2301
+ /** Children map: id -> id */
2302
+ childrenMap = {};
2303
+ /** Children count */
2304
+ childrenCount = 0;
2305
+ /** Ready count (children that have fired 'created') */
2306
+ readyCount = 0;
2307
+ /** Ready map: id -> 1 */
2308
+ readyMap = {};
2309
+ /** View instance */
2310
+ viewInstance;
2311
+ /** Get view instance (read-only) */
2312
+ get view() {
2313
+ return this.viewInstance;
2314
+ }
2315
+ /** Invoke list for deferred method calls */
2316
+ invokeList = [];
2317
+ /** Signature for async operation tracking */
2318
+ signature = 1;
2319
+ /** Whether view has altered */
2320
+ hasAltered = 0;
2321
+ /** Whether view is destroyed */
2322
+ destroyed = 0;
2323
+ /** View path (lark-view attribute value) */
2324
+ viewPath;
2325
+ /** Original template before mount */
2326
+ originalTemplate;
2327
+ /** Hold fire created flag */
2328
+ holdFireCreated = 0;
2329
+ /** Children created flag */
2330
+ childrenCreated = 0;
2331
+ /** Children alter flag */
2332
+ childrenAlter = 0;
2333
+ constructor(id, parentId) {
2334
+ super();
2335
+ this.id = id;
2336
+ if (parentId) {
2337
+ this._parentId = parentId;
2338
+ }
2339
+ frameRegistry.set(id, this);
2340
+ const element = document.getElementById(id);
2341
+ if (element) {
2342
+ element.frame = this;
2343
+ element.frameBound = 1;
2344
+ }
2345
+ _Frame.fire("add", { frame: this });
2346
+ }
2347
+ // ============================================================
2348
+ // Instance methods
2349
+ // ============================================================
2350
+ /**
2351
+ * Mount a view to this frame.
2352
+ *
2353
+ * Complete flow:
2354
+ * 1. Parse viewPath, translate query params from parent
2355
+ * 2. Unmount current view
2356
+ * 3. Load View class (via require or provided ViewClass)
2357
+ * 4. View_Prepare (scan event methods)
2358
+ * 5. Create View instance
2359
+ * 6. View_DelegateEvents (bind DOM events)
2360
+ * 7. Call view.init()
2361
+ * 8. If view has tmpl, call render via Updater
2362
+ * 9. If no tmpl, call endUpdate directly
2363
+ */
2364
+ mountView(viewPath, viewInitParams) {
2365
+ const node = document.getElementById(this.id);
2366
+ const pId = this.parentId;
2367
+ if (!this.hasAltered && node) {
2368
+ this.hasAltered = 1;
2369
+ this.originalTemplate = node.innerHTML;
2370
+ }
2371
+ this.unmountView();
2372
+ this.destroyed = 0;
2373
+ const parsed = parseUri(viewPath || "");
2374
+ const viewClassName = parsed.path;
2375
+ if (!node || !viewClassName) return;
2376
+ this.viewPath = viewPath;
2377
+ const params = parsed["params"];
2378
+ translateQuery(pId || this.id, viewPath, params);
2379
+ if (viewInitParams) {
2380
+ assign(params, viewInitParams);
2381
+ }
2382
+ const sign = this.signature;
2383
+ if (viewClassRegistry[viewClassName]) {
2384
+ this.doMountView(viewClassRegistry[viewClassName], params, node, sign);
2385
+ }
2386
+ }
2387
+ /**
2388
+ * Internal: actually mount the view after class is loaded.
2389
+ */
2390
+ doMountView(ViewClass, params, node, sign) {
2391
+ if (sign !== this.signature) return;
2392
+ const mixinCtors = viewPrepare(ViewClass);
2393
+ const ViewConstructor = ViewClass;
2394
+ const view = new ViewConstructor(
2395
+ this.id,
2396
+ this,
2397
+ params,
2398
+ node,
2399
+ mixinCtors
2400
+ );
2401
+ this.viewInstance = view;
2402
+ view.signature = 1;
2403
+ viewDelegateEvents(view);
2404
+ const initResult = funcWithTry(
2405
+ view.init,
2406
+ [params, { node, deep: !view.tmpl }],
2407
+ view,
2408
+ noop
2409
+ );
2410
+ const nextSign = ++this.signature;
2411
+ Promise.resolve(initResult).then(() => {
2412
+ if (nextSign !== this.signature) return;
2413
+ if (view.tmpl) {
2414
+ const renderFn = view.$b;
2415
+ if (renderFn) {
2416
+ renderFn.call(view);
2417
+ }
2418
+ } else {
2419
+ this.hasAltered = 0;
2420
+ if (!view.$e) {
2421
+ view.endUpdate();
2422
+ }
2423
+ }
2424
+ });
2425
+ }
2426
+ /**
2427
+ * Unmount current view.
2428
+ */
2429
+ unmountView() {
2430
+ const view = this.view;
2431
+ this.invokeList = [];
2432
+ if (!view) return;
2433
+ if (!globalAlter) {
2434
+ globalAlter = { id: this.id };
2435
+ }
2436
+ this.destroyed = 1;
2437
+ this.unmountZone();
2438
+ notifyAlter(this, globalAlter);
2439
+ if (view.signature > 0) {
2440
+ view.fire("destroy", void 0, true, true);
2441
+ }
2442
+ EventDelegator.clearRangeEvents(this.id);
2443
+ delete this["viewInstance"];
2444
+ const node = document.getElementById(this.id);
2445
+ if (node && this.originalTemplate) {
2446
+ node.innerHTML = this.originalTemplate;
2447
+ }
2448
+ globalAlter = void 0;
2449
+ unmark(view);
2450
+ }
2451
+ /**
2452
+ * Mount a child frame.
2453
+ */
2454
+ mountFrame(frameId, viewPath, viewInitParams) {
2455
+ notifyAlter(this, { id: frameId });
2456
+ let childFrame = frameRegistry.get(frameId);
2457
+ if (!childFrame) {
2458
+ if (!this.childrenMap[frameId]) {
2459
+ this.childrenCount++;
2460
+ }
2461
+ this.childrenMap[frameId] = frameId;
2462
+ childFrame = frameCache.pop();
2463
+ if (childFrame) {
2464
+ reInitFrame(childFrame, frameId, this.id);
2465
+ } else {
2466
+ childFrame = new _Frame(frameId, this.id);
2467
+ }
2468
+ }
2469
+ childFrame.mountView(viewPath, viewInitParams);
2470
+ return childFrame;
2471
+ }
2472
+ /**
2473
+ * Unmount a child frame.
2474
+ */
2475
+ unmountFrame(id, _inner) {
2476
+ const targetId = id ? this.childrenMap[id] : this.id;
2477
+ const frame = frameRegistry.get(targetId);
2478
+ if (!frame) return;
2479
+ const wasCreated = frame.readyCount > 0;
2480
+ const pId = frame.parentId;
2481
+ frame.unmountView();
2482
+ removeFrame(targetId, wasCreated);
2483
+ reInitFrameForCache(frame);
2484
+ frameCache.push(frame);
2485
+ const parent = frameRegistry.get(pId || "");
2486
+ if (parent && parent.childrenMap[targetId]) {
2487
+ Reflect.deleteProperty(parent.childrenMap, targetId);
2488
+ parent.childrenCount--;
2489
+ notifyCreated(parent);
2490
+ }
2491
+ }
2492
+ /**
2493
+ * Mount all views in a zone.
2494
+ */
2495
+ mountZone(zoneId, _inner) {
2496
+ const targetZone = zoneId || this.id;
2497
+ this.holdFireCreated = 1;
2498
+ const rootEl = document.getElementById(targetZone);
2499
+ if (!rootEl) return;
2500
+ const viewElements = rootEl.querySelectorAll(`[${LARK_VIEW}]`);
2501
+ const frames = [];
2502
+ viewElements.forEach((el) => {
2503
+ const htmlEl = el;
2504
+ if (!htmlEl.frameBound) {
2505
+ const elId = ensureElementId2(htmlEl);
2506
+ htmlEl.frameBound = 1;
2507
+ const viewPath = getAttribute(htmlEl, LARK_VIEW);
2508
+ frames.push([elId, viewPath]);
2509
+ }
2510
+ });
2511
+ for (const [frameId, viewPath] of frames) {
2512
+ this.mountFrame(frameId, viewPath);
2513
+ }
2514
+ this.holdFireCreated = 0;
2515
+ notifyCreated(this);
2516
+ }
2517
+ /**
2518
+ * Unmount all views in a zone.
2519
+ */
2520
+ unmountZone(zoneId, _inner) {
2521
+ for (const childId in this.childrenMap) {
2522
+ if (has(this.childrenMap, childId)) {
2523
+ if (!zoneId || childId !== zoneId) {
2524
+ this.unmountFrame(childId);
2525
+ }
2526
+ }
2527
+ }
2528
+ notifyCreated(this);
2529
+ }
2530
+ /**
2531
+ * Get all child frame IDs.
2532
+ */
2533
+ children() {
2534
+ const result = [];
2535
+ for (const id in this.childrenMap) {
2536
+ if (has(this.childrenMap, id)) {
2537
+ result.push(id);
2538
+ }
2539
+ }
2540
+ return result;
2541
+ }
2542
+ /**
2543
+ * Get parent frame at given level.
2544
+ * @param level - How many levels up (default 1)
2545
+ */
2546
+ parent(level = 1) {
2547
+ let frame = void 0;
2548
+ let currentPid = this.parentId;
2549
+ let n = level >>> 0 || 1;
2550
+ while (currentPid && n--) {
2551
+ frame = frameRegistry.get(currentPid);
2552
+ currentPid = frame?.parentId;
2553
+ }
2554
+ return frame;
2555
+ }
2556
+ /**
2557
+ * Invoke a method on the view.
2558
+ */
2559
+ invoke(name, args) {
2560
+ let result;
2561
+ const view = this.view;
2562
+ if (view && view.rendered) {
2563
+ const fn = view[name];
2564
+ if (typeof fn === "function") {
2565
+ result = funcWithTry(
2566
+ fn,
2567
+ args || [],
2568
+ view,
2569
+ noop
2570
+ );
2571
+ }
2572
+ } else {
2573
+ const key = SPLITTER + name;
2574
+ let existingEntry;
2575
+ for (const entry of this.invokeList) {
2576
+ if (entry.key === key) {
2577
+ existingEntry = entry;
2578
+ break;
2579
+ }
2580
+ }
2581
+ if (existingEntry) {
2582
+ existingEntry.removed = args === existingEntry.args;
2583
+ }
2584
+ const newEntry = {
2585
+ name,
2586
+ args: args || [],
2587
+ key
2588
+ };
2589
+ this.invokeList.push(newEntry);
2590
+ }
2591
+ return result;
2592
+ }
2593
+ // ============================================================
2594
+ // Static methods
2595
+ // ============================================================
2596
+ /** Get frame by ID */
2597
+ static get(id) {
2598
+ return frameRegistry.get(id);
2599
+ }
2600
+ /** Get all frames */
2601
+ static getAll() {
2602
+ return frameRegistry;
2603
+ }
2604
+ /** Get or create root frame */
2605
+ static root(rootId) {
2606
+ if (!rootFrame) {
2607
+ rootId = rootId || "lark-root";
2608
+ let rootElement = document.getElementById(rootId);
2609
+ if (!rootElement) {
2610
+ rootElement = document.body;
2611
+ rootElement.id = rootId;
2612
+ }
2613
+ rootFrame = new _Frame(rootId);
2614
+ }
2615
+ return rootFrame;
2616
+ }
2617
+ /** Bind event listener (static) */
2618
+ static on(event, handler) {
2619
+ staticEmitter.on(event, handler);
2620
+ return _Frame;
2621
+ }
2622
+ /** Unbind event listener (static) */
2623
+ static off(event, handler) {
2624
+ staticEmitter.off(event, handler);
2625
+ return _Frame;
2626
+ }
2627
+ /** Fire event (static) */
2628
+ static fire(event, data) {
2629
+ staticEmitter.fire(event, data);
2630
+ }
2631
+ };
2632
+ function ensureElementId2(element) {
2633
+ const id = element.getAttribute("id");
2634
+ if (id) return id;
2635
+ element.autoId = 1;
2636
+ const newId = generateId("frame_");
2637
+ element.id = newId;
2638
+ return newId;
2639
+ }
2640
+ function removeFrame(id, wasCreated) {
2641
+ const frameInstance = frameRegistry.get(id);
2642
+ if (!frameInstance) return;
2643
+ frameRegistry.delete(id);
2644
+ Frame.fire("remove", { frame: frameInstance, fcc: wasCreated });
2645
+ const element = document.getElementById(id);
2646
+ if (element) {
2647
+ element.frameBound = 0;
2648
+ Reflect.deleteProperty(element, "frame");
2649
+ }
2650
+ }
2651
+ function notifyCreated(frameInstance) {
2652
+ if (!frameInstance["childrenCreated"] && !frameInstance["holdFireCreated"] && frameInstance["childrenCount"] === frameInstance["readyCount"]) {
2653
+ if (!frameInstance["childrenCreated"]) {
2654
+ frameInstance["childrenCreated"] = 1;
2655
+ frameInstance["childrenAlter"] = 0;
2656
+ frameInstance.fire("created");
2657
+ }
2658
+ const pId = frameInstance.parentId;
2659
+ if (pId) {
2660
+ const parent = frameRegistry.get(pId);
2661
+ if (parent && !parent.readyMap[frameInstance.id]) {
2662
+ parent.readyMap[frameInstance.id] = 1;
2663
+ parent.readyCount++;
2664
+ notifyCreated(parent);
2665
+ }
2666
+ }
2667
+ }
2668
+ }
2669
+ function notifyAlter(frameInstance, data) {
2670
+ if (!frameInstance["childrenAlter"] && frameInstance["childrenCreated"]) {
2671
+ frameInstance["childrenCreated"] = 0;
2672
+ frameInstance["childrenAlter"] = 1;
2673
+ frameInstance.fire("alter", data);
2674
+ const pId = frameInstance.parentId;
2675
+ if (pId) {
2676
+ const parent = frameRegistry.get(pId);
2677
+ if (parent && parent.readyMap[frameInstance.id]) {
2678
+ parent.readyCount--;
2679
+ Reflect.deleteProperty(parent.readyMap, frameInstance.id);
2680
+ notifyAlter(parent, data);
2681
+ }
2682
+ }
2683
+ }
2684
+ }
2685
+ function reInitFrame(frame, id, parentId) {
2686
+ Reflect.set(frame, "id", id);
2687
+ frame["_parentId"] = parentId;
2688
+ frame["childrenMap"] = {};
2689
+ frame["childrenCount"] = 0;
2690
+ frame["readyCount"] = 0;
2691
+ frame["signature"] = 1;
2692
+ frame["readyMap"] = {};
2693
+ frame["invokeList"] = [];
2694
+ frameRegistry.set(id, frame);
2695
+ }
2696
+ function reInitFrameForCache(frame) {
2697
+ Reflect.set(frame, "id", "");
2698
+ frame["_parentId"] = void 0;
2699
+ frame["childrenMap"] = {};
2700
+ frame["readyMap"] = {};
2701
+ }
2702
+ function translateQuery(pId, src, params) {
2703
+ const parentFrame = frameRegistry.get(pId);
2704
+ const parentView = parentFrame?.view;
2705
+ if (!parentView) return;
2706
+ const parentRefData = parentView.updater.refData;
2707
+ if (!parentRefData) return;
2708
+ if (src.indexOf(SPLITTER) > 0) {
2709
+ translateData(parentRefData, params);
2710
+ if (params[SPLITTER]) {
2711
+ assign(
2712
+ params,
2713
+ params[SPLITTER]
2714
+ );
2715
+ Reflect.deleteProperty(params, SPLITTER);
2716
+ }
2717
+ }
2718
+ }
2719
+ function registerViewClass(viewPath, ViewClass) {
2720
+ const parsed = parseUri(viewPath);
2721
+ const path = parsed.path;
2722
+ if (path) {
2723
+ viewClassRegistry[path] = ViewClass;
2724
+ }
2725
+ }
2726
+
2727
+ // src/service.ts
2728
+ var Bag = class {
2729
+ /** Bag data */
2730
+ data;
2731
+ /** Internal cache info */
2732
+ cacheInfo;
2733
+ constructor(data = {}) {
2734
+ this.data = data;
2735
+ }
2736
+ /** Get a value from bag data */
2737
+ get(key) {
2738
+ return this.data[key];
2739
+ }
2740
+ /** Set a value in bag data */
2741
+ set(keyOrData, value) {
2742
+ if (typeof keyOrData === "string") {
2743
+ this.data[keyOrData] = value;
2744
+ } else {
2745
+ assign(this.data, keyOrData);
2746
+ }
2747
+ return this;
2748
+ }
2749
+ };
2750
+ var FETCH_FLAGS_ALL = 1;
2751
+ var FETCH_FLAGS_ONE = 2;
2752
+ function createServiceType(syncFn, cacheMax = 20, cacheBuffer = 5) {
2753
+ const metas = {};
2754
+ const bagCache = new Cache({
2755
+ maxSize: cacheMax,
2756
+ bufferSize: cacheBuffer
2757
+ });
2758
+ const pendingCacheKeys = {};
2759
+ const staticEmitter2 = new EventEmitter();
2760
+ const serviceType = {
2761
+ add(attrs) {
2762
+ if (!isArray(attrs)) {
2763
+ attrs = [attrs];
2764
+ }
2765
+ for (const bag of attrs) {
2766
+ if (bag) {
2767
+ const name = bag.name;
2768
+ const cache = bag.cache;
2769
+ bag.cache = cache ? cache | 0 : 0;
2770
+ metas[name] = bag;
2771
+ }
2772
+ }
2773
+ },
2774
+ meta(attrs) {
2775
+ const name = typeof attrs === "string" ? attrs : attrs["name"];
2776
+ return metas[name] || attrs;
2777
+ },
2778
+ create(attrs) {
2779
+ const meta = serviceType.meta(attrs);
2780
+ const cache = attrs["cache"] | 0 || meta.cache || 0;
2781
+ const entity = new Bag();
2782
+ entity.set(meta);
2783
+ entity.cacheInfo = {
2784
+ name: meta.name,
2785
+ after: meta.after,
2786
+ cleans: meta.cleans,
2787
+ key: cache ? defaultCacheKey(meta, attrs) : "",
2788
+ time: 0
2789
+ };
2790
+ if (typeof attrs === "object" && attrs !== null) {
2791
+ entity.set(attrs);
2792
+ }
2793
+ const before = meta.before;
2794
+ if (before) {
2795
+ funcWithTry(before, [entity], entity, noop);
2796
+ }
2797
+ staticEmitter2.fire("begin", { bag: entity });
2798
+ return entity;
2799
+ },
2800
+ get(attrs, createNew) {
2801
+ let entity;
2802
+ let needsUpdate = false;
2803
+ if (!createNew) {
2804
+ entity = serviceType.cached(attrs);
2805
+ }
2806
+ if (!entity) {
2807
+ entity = serviceType.create(attrs);
2808
+ needsUpdate = true;
2809
+ }
2810
+ return { entity, needsUpdate };
2811
+ },
2812
+ cached(attrs) {
2813
+ const meta = serviceType.meta(attrs);
2814
+ const cache = attrs["cache"] | 0 || meta.cache || 0;
2815
+ let cacheKey = "";
2816
+ if (cache) {
2817
+ cacheKey = defaultCacheKey(meta, attrs);
2818
+ }
2819
+ if (cacheKey) {
2820
+ const info = pendingCacheKeys[cacheKey];
2821
+ if (info) {
2822
+ return info.e;
2823
+ }
2824
+ const cached = bagCache.get(cacheKey);
2825
+ if (cached && cached.cacheInfo) {
2826
+ if (now() - cached.cacheInfo.time > cache) {
2827
+ bagCache.del(cacheKey);
2828
+ return void 0;
2829
+ }
2830
+ return cached;
2831
+ }
2832
+ }
2833
+ return void 0;
2834
+ },
2835
+ clear(names) {
2836
+ const nameList = (typeof names === "string" ? names : names.join(",")).split(",");
2837
+ const nameSet = {};
2838
+ for (const n of nameList) {
2839
+ nameSet[n] = 1;
2840
+ }
2841
+ const keysToDelete = [];
2842
+ bagCache.forEach((bag) => {
2843
+ if (bag?.cacheInfo && nameSet[bag.cacheInfo.name]) {
2844
+ if (bag.cacheInfo.key) {
2845
+ keysToDelete.push(bag.cacheInfo.key);
2846
+ }
2847
+ }
2848
+ });
2849
+ for (const key of keysToDelete) {
2850
+ bagCache.del(key);
2851
+ }
2852
+ },
2853
+ on(event, handler) {
2854
+ staticEmitter2.on(event, handler);
2855
+ },
2856
+ off(event, handler) {
2857
+ staticEmitter2.off(event, handler);
2858
+ },
2859
+ fire(event, data) {
2860
+ staticEmitter2.fire(event, data);
2861
+ },
2862
+ extend(newSyncFn, newCacheMax, newCacheBuffer) {
2863
+ return createServiceType(
2864
+ newSyncFn,
2865
+ newCacheMax || cacheMax,
2866
+ newCacheBuffer || cacheBuffer
2867
+ );
2868
+ }
2869
+ };
2870
+ const internals = {
2871
+ metas,
2872
+ bagCache,
2873
+ pendingCacheKeys,
2874
+ syncFn,
2875
+ staticEmitter: staticEmitter2
2876
+ };
2877
+ function ServiceInstance() {
2878
+ this.id = generateId("s");
2879
+ this["$e"] = 0;
2880
+ this["$d"] = 0;
2881
+ this["$g"] = [];
2882
+ this["$h"] = [];
2883
+ this._emitter = new EventEmitter();
2884
+ this._internals = internals;
2885
+ this._type = serviceType;
2886
+ }
2887
+ ServiceInstance.prototype = {
2888
+ all(attrs, done) {
2889
+ serviceSend(this, attrs, done, FETCH_FLAGS_ALL, false);
2890
+ return this;
2891
+ },
2892
+ one(attrs, done) {
2893
+ serviceSend(this, attrs, done, FETCH_FLAGS_ONE, false);
2894
+ return this;
2895
+ },
2896
+ save(attrs, done) {
2897
+ serviceSend(this, attrs, done, FETCH_FLAGS_ALL, true);
2898
+ return this;
2899
+ },
2900
+ enqueue(callback) {
2901
+ if (!this["$d"]) {
2902
+ this["$g"].push(callback);
2903
+ this.dequeue(...this["$h"]);
2904
+ }
2905
+ return this;
2906
+ },
2907
+ dequeue(...args) {
2908
+ if (!this["$e"] && !this["$d"]) {
2909
+ this["$e"] = 1;
2910
+ setTimeout(() => {
2911
+ this["$e"] = 0;
2912
+ if (!this["$d"]) {
2913
+ const task2 = this["$g"].shift();
2914
+ if (task2) {
2915
+ this["$h"] = args;
2916
+ funcWithTry(task2, args, this, noop);
2917
+ }
2918
+ }
2919
+ }, 0);
2920
+ }
2921
+ },
2922
+ destroy() {
2923
+ this["$d"] = 1;
2924
+ this["$g"] = [];
2925
+ },
2926
+ on(event, handler) {
2927
+ this._emitter.on(event, handler);
2928
+ return this;
2929
+ },
2930
+ off(event, handler) {
2931
+ this._emitter.off(event, handler);
2932
+ return this;
2933
+ },
2934
+ fire(event, data) {
2935
+ this._emitter.fire(event, data);
2936
+ return this;
2937
+ }
2938
+ };
2939
+ const staticsMap = {
2940
+ add: serviceType.add,
2941
+ meta: serviceType.meta,
2942
+ create: serviceType.create,
2943
+ get: serviceType.get,
2944
+ cached: serviceType.cached,
2945
+ clear: serviceType.clear,
2946
+ on: serviceType.on,
2947
+ off: serviceType.off,
2948
+ fire: serviceType.fire,
2949
+ extend: serviceType.extend,
2950
+ _internals: internals
2951
+ };
2952
+ const constructor = Object.assign(
2953
+ ServiceInstance,
2954
+ staticsMap
2955
+ );
2956
+ return constructor;
2957
+ }
2958
+ var Service = createServiceType(noop);
2959
+ function defaultCacheKey(meta, attrs) {
2960
+ return JSON.stringify(attrs) + SPLITTER + JSON.stringify(meta);
2961
+ }
2962
+ function serviceSend(service, attrs, done, flag, save) {
2963
+ if (service["$d"]) return;
2964
+ if (service["$e"]) {
2965
+ service.enqueue(
2966
+ serviceSend.bind(null, service, attrs, done, flag, save)
2967
+ );
2968
+ return;
2969
+ }
2970
+ service["$e"] = 1;
2971
+ let attrList;
2972
+ if (typeof attrs === "string") {
2973
+ attrList = [{ name: attrs }];
2974
+ } else if (isArray(attrs)) {
2975
+ attrList = attrs;
2976
+ } else {
2977
+ attrList = [attrs];
2978
+ }
2979
+ const internals = service._internals;
2980
+ const { syncFn, pendingCacheKeys, staticEmitter: staticEmitter2 } = internals;
2981
+ let requestCount = 0;
2982
+ const total = attrList.length;
2983
+ const doneArr = new Array(total + 1);
2984
+ const errorArgs = [];
2985
+ const remoteComplete = (idx, error) => {
2986
+ const bag = doneArr[idx + 1];
2987
+ let newBag = false;
2988
+ if (error) {
2989
+ errorArgs[idx] = error;
2990
+ staticEmitter2.fire("fail", { bag, error });
2991
+ } else {
2992
+ newBag = true;
2993
+ staticEmitter2.fire("done", { bag });
2994
+ }
2995
+ if (!service["$d"]) {
2996
+ const finish = requestCount === total;
2997
+ if (finish) {
2998
+ service["$e"] = 0;
2999
+ if (flag === FETCH_FLAGS_ALL) {
3000
+ doneArr[0] = errorArgs;
3001
+ funcWithTry(done, doneArr, service, noop);
3002
+ }
3003
+ }
3004
+ if (flag === FETCH_FLAGS_ONE) {
3005
+ funcWithTry(done, [error || null, bag, finish, idx], service, noop);
3006
+ }
3007
+ }
3008
+ if (newBag) {
3009
+ staticEmitter2.fire("end", { bag, error });
3010
+ }
3011
+ };
3012
+ for (const attr of attrList) {
3013
+ if (!attr) continue;
3014
+ const attrObj = typeof attr === "string" ? { name: attr } : attr;
3015
+ const bagInfo = service._type.get(attrObj, save);
3016
+ const bagEntity = bagInfo.entity;
3017
+ const cacheKey = bagEntity.cacheInfo?.key || "";
3018
+ const complete = remoteComplete.bind(null, requestCount++);
3019
+ if (cacheKey && pendingCacheKeys[cacheKey]) {
3020
+ pendingCacheKeys[cacheKey].push(complete);
3021
+ } else if (bagInfo.needsUpdate) {
3022
+ if (cacheKey) {
3023
+ const cacheList = [complete];
3024
+ cacheList.e = bagEntity;
3025
+ pendingCacheKeys[cacheKey] = cacheList;
3026
+ const cacheComplete = () => {
3027
+ const list = pendingCacheKeys[cacheKey];
3028
+ const entity = list.e;
3029
+ if (entity.cacheInfo) {
3030
+ entity.cacheInfo.time = now();
3031
+ }
3032
+ internals.bagCache.set(cacheKey, entity);
3033
+ Reflect.deleteProperty(pendingCacheKeys, cacheKey);
3034
+ for (const cb of list) {
3035
+ if (typeof cb === "function") {
3036
+ cb();
3037
+ }
3038
+ }
3039
+ };
3040
+ syncFn(bagEntity, cacheComplete);
3041
+ } else {
3042
+ syncFn(bagEntity, complete);
3043
+ }
3044
+ } else {
3045
+ complete();
3046
+ }
3047
+ }
3048
+ }
3049
+
3050
+ // src/framework.ts
3051
+ var config = {
3052
+ rootId: "lark-root",
3053
+ hashbang: "#!",
3054
+ error: (error) => {
3055
+ throw error;
3056
+ }
3057
+ };
3058
+ var booted3 = false;
3059
+ var taskList = [];
3060
+ var taskIndex = 0;
3061
+ var taskScheduled = false;
3062
+ function executeTaskChunk(deadline) {
3063
+ const hasDeadline = !!deadline;
3064
+ const startTime = now();
3065
+ while (true) {
3066
+ const fn = taskList[taskIndex];
3067
+ if (!fn) {
3068
+ taskList.length = 0;
3069
+ taskIndex = 0;
3070
+ taskScheduled = false;
3071
+ return;
3072
+ }
3073
+ if (hasDeadline && deadline) {
3074
+ if (deadline.timeRemaining() <= 0) {
3075
+ scheduleTaskChunk();
3076
+ return;
3077
+ }
3078
+ } else if (now() - startTime > CALL_BREAK_TIME && taskList.length > taskIndex + 3) {
3079
+ scheduleTaskChunk();
3080
+ return;
3081
+ }
3082
+ const context = taskList[taskIndex + 1];
3083
+ const args = taskList[taskIndex + 2];
3084
+ funcWithTry(fn, args, context, noop);
3085
+ taskIndex += 3;
3086
+ }
3087
+ }
3088
+ function scheduleTaskChunk() {
3089
+ const scheduler = window.scheduler;
3090
+ if (scheduler && typeof scheduler.postTask === "function") {
3091
+ scheduler.postTask(() => executeTaskChunk(), { priority: "background" });
3092
+ } else if (typeof window.requestIdleCallback === "function") {
3093
+ window.requestIdleCallback(executeTaskChunk);
3094
+ } else {
3095
+ setTimeout(executeTaskChunk, 0);
3096
+ }
3097
+ }
3098
+ function task(fn, args, context) {
3099
+ taskList.push(fn, context, args || []);
3100
+ if (!taskScheduled) {
3101
+ taskScheduled = true;
3102
+ scheduleTaskChunk();
3103
+ }
3104
+ }
3105
+ var dispatcherUpdateTag = 0;
3106
+ function viewIsObserveChanged(view) {
3107
+ const loc = view.locationObserved;
3108
+ let result = false;
3109
+ if (loc.flag) {
3110
+ if (loc.observePath) {
3111
+ const lastChanged2 = Router.diff();
3112
+ result = !!lastChanged2?.path;
3113
+ }
3114
+ if (!result && loc.keys.length) {
3115
+ const lastChanged2 = Router.diff();
3116
+ const changedParams = lastChanged2?.params;
3117
+ if (changedParams) {
3118
+ for (const key of loc.keys) {
3119
+ result = has(changedParams, key);
3120
+ if (result) break;
3121
+ }
3122
+ }
3123
+ }
3124
+ }
3125
+ return result;
3126
+ }
3127
+ function stateIsObserveChanged(view, stateKeys) {
3128
+ const observedKeys = view.observedStateKeys;
3129
+ if (!observedKeys) return false;
3130
+ for (const key of observedKeys) {
3131
+ if (has(stateKeys, key)) return true;
3132
+ }
3133
+ return false;
3134
+ }
3135
+ function dispatcherUpdate(frame, stateKeys) {
3136
+ const frameInternal = frame;
3137
+ const view = frame.view;
3138
+ if (!view || frameInternal.dispatcherUpdateTag === dispatcherUpdateTag || view.signature <= 1) {
3139
+ return;
3140
+ }
3141
+ frameInternal.dispatcherUpdateTag = dispatcherUpdateTag;
3142
+ const isChanged = stateKeys ? stateIsObserveChanged(view, stateKeys) : viewIsObserveChanged(view);
3143
+ let renderPromise;
3144
+ if (isChanged) {
3145
+ const renderResult = funcWithTry(view.$b ?? view.render, [], view, noop);
3146
+ if (renderResult && typeof renderResult.then === "function") {
3147
+ renderPromise = renderResult;
3148
+ }
3149
+ }
3150
+ const children = frame.children();
3151
+ const recurse = () => {
3152
+ for (const childId of children) {
3153
+ const childFrame = Frame.get(childId);
3154
+ if (childFrame) {
3155
+ dispatcherUpdate(childFrame, stateKeys);
3156
+ }
3157
+ }
3158
+ };
3159
+ if (renderPromise) {
3160
+ renderPromise.then(recurse);
3161
+ } else {
3162
+ recurse();
3163
+ }
3164
+ }
3165
+ function dispatcherNotifyChange(e) {
3166
+ const rootFrame2 = Frame.root();
3167
+ const view = e["view"];
3168
+ if (view) {
3169
+ const viewPath = typeof view === "object" && view !== null ? String(view.to || "") : String(view);
3170
+ rootFrame2.mountView(viewPath);
3171
+ } else {
3172
+ dispatcherUpdateTag++;
3173
+ dispatcherUpdate(rootFrame2, e["keys"]);
3174
+ }
3175
+ }
3176
+ function dispatchEvent(target, eventType, eventInit) {
3177
+ const event = new CustomEvent(eventType, {
3178
+ bubbles: true,
3179
+ cancelable: true,
3180
+ ...eventInit
3181
+ });
3182
+ target.dispatchEvent(event);
3183
+ }
3184
+ var Base = class extends EventEmitter {
3185
+ };
3186
+ function use(names, callback) {
3187
+ if (!config.require) {
3188
+ if (callback) callback();
3189
+ return;
3190
+ }
3191
+ const nameList = typeof names === "string" ? [names] : names;
3192
+ const result = config.require(nameList);
3193
+ if (result && typeof result.then === "function") {
3194
+ result.then((modules) => {
3195
+ if (callback) callback(modules);
3196
+ });
3197
+ }
3198
+ }
3199
+ var WAIT_OK = 1;
3200
+ var WAIT_TIMEOUT_OR_UNFOUND = 0;
3201
+ function waitZoneViewsRendered(viewId, timeout) {
3202
+ if (timeout == null) {
3203
+ timeout = 30 * 1e3;
3204
+ }
3205
+ const checkFrame = Frame.get(viewId);
3206
+ const endTime = now() + timeout;
3207
+ return new Promise((resolve) => {
3208
+ const check = () => {
3209
+ const currentTime = now();
3210
+ if (currentTime > endTime || !checkFrame) {
3211
+ resolve(WAIT_TIMEOUT_OR_UNFOUND);
3212
+ } else if (checkFrame.childrenCount === checkFrame.readyCount) {
3213
+ resolve(WAIT_OK);
3214
+ } else {
3215
+ setTimeout(check, 9);
3216
+ }
3217
+ };
3218
+ setTimeout(check, 9);
3219
+ });
3220
+ }
3221
+ var Framework = {
3222
+ // ============================================================
3223
+ // Lifecycle
3224
+ // ============================================================
3225
+ /**
3226
+ * Get or set framework configuration.
3227
+ */
3228
+ config(cfg) {
3229
+ if (!cfg) {
3230
+ return config;
3231
+ }
3232
+ if (typeof cfg === "string") {
3233
+ return config[cfg];
3234
+ }
3235
+ assign(config, cfg);
3236
+ return config;
3237
+ },
3238
+ /**
3239
+ * Boot the framework.
3240
+ */
3241
+ boot(cfg) {
3242
+ if (cfg && typeof cfg === "object") {
3243
+ assign(config, cfg);
3244
+ }
3245
+ Router._setConfig(config);
3246
+ EventDelegator.setFrameGetter((id) => Frame.get(id));
3247
+ Router.on(ROUTER_EVENTS.CHANGED, (data) => {
3248
+ dispatcherNotifyChange(data);
3249
+ });
3250
+ State.on(ROUTER_EVENTS.CHANGED, (data) => {
3251
+ dispatcherNotifyChange(data);
3252
+ });
3253
+ booted3 = true;
3254
+ markBooted();
3255
+ markRouterBooted();
3256
+ const rootFrame2 = Frame.root(config.rootId);
3257
+ Router._bind();
3258
+ const defaultView = config.defaultView || "";
3259
+ if (defaultView) {
3260
+ rootFrame2.mountView(defaultView);
3261
+ }
3262
+ },
3263
+ /** Whether framework has booted */
3264
+ isBooted() {
3265
+ return booted3;
3266
+ },
3267
+ // ============================================================
3268
+ // Utility proxies
3269
+ // ============================================================
3270
+ /** Mark async callback validity tracker */
3271
+ mark,
3272
+ /** Unmark (invalidate) async callbacks */
3273
+ unmark,
3274
+ /** Fire a custom DOM event on a target */
3275
+ dispatch: dispatchEvent,
3276
+ /** Execute function in try-catch, ignoring errors */
3277
+ task,
3278
+ /** Promise-based setTimeout */
3279
+ delay(time) {
3280
+ return new Promise((resolve) => setTimeout(resolve, time));
3281
+ },
3282
+ /** Load modules via configured require */
3283
+ use,
3284
+ /** Wait for zone views to be rendered */
3285
+ waitZoneViewsRendered,
3286
+ WAIT_OK,
3287
+ WAIT_TIMEOUT_OR_UNFOUND,
3288
+ /**
3289
+ * Convert array to hash map.
3290
+ */
3291
+ toMap,
3292
+ /**
3293
+ * Execute function in try-catch.
3294
+ */
3295
+ toTry: funcWithTry,
3296
+ /**
3297
+ * Convert path + params to URL string.
3298
+ */
3299
+ toUrl: toUri,
3300
+ /**
3301
+ * Parse URI string into path and params.
3302
+ */
3303
+ parseUrl: parseUri,
3304
+ /**
3305
+ * Mix properties from source to target.
3306
+ */
3307
+ mix: assign,
3308
+ /**
3309
+ * Check if object has own property.
3310
+ */
3311
+ has,
3312
+ /**
3313
+ * Get object keys.
3314
+ */
3315
+ keys,
3316
+ /**
3317
+ * Check if node A is inside node B.
3318
+ */
3319
+ inside: nodeInside,
3320
+ /**
3321
+ * Get element by ID (shorthand for document.getElementById).
3322
+ */
3323
+ node: getById,
3324
+ /**
3325
+ * Apply CSS style.
3326
+ */
3327
+ applyStyle,
3328
+ /**
3329
+ * Generate globally unique ID.
3330
+ */
3331
+ guid: generateId,
3332
+ /**
3333
+ * Proxy-based debug guard.
3334
+ */
3335
+ guard: safeguard,
3336
+ /**
3337
+ * Cache class.
3338
+ */
3339
+ Cache,
3340
+ /**
3341
+ * Ensure element has an ID.
3342
+ */
3343
+ nodeId(element) {
3344
+ if (!element.id) {
3345
+ element.id = generateId("l_");
3346
+ }
3347
+ return element.id;
3348
+ },
3349
+ /**
3350
+ * Base class with EventEmitter.
3351
+ */
3352
+ Base,
3353
+ // ============================================================
3354
+ // Module access
3355
+ // ============================================================
3356
+ /** Router module */
3357
+ Router,
3358
+ /** State module */
3359
+ State,
3360
+ /** View class */
3361
+ View,
3362
+ /** Frame class */
3363
+ Frame
3364
+ };
3365
+ if (typeof window !== "undefined") {
3366
+ window.__lark_Framework = Framework;
3367
+ window.__lark_State = State;
3368
+ window.__lark_Router = Router;
3369
+ window.__lark_Frame = Frame;
3370
+ }
3371
+
3372
+ // src/store.ts
3373
+ var LARK_GLOBAL = "lark_global";
3374
+ var Platform = /* @__PURE__ */ ((Platform2) => {
3375
+ Platform2["Lark"] = "lark";
3376
+ Platform2["React"] = "react";
3377
+ Platform2["Node"] = "node";
3378
+ return Platform2;
3379
+ })(Platform || {});
3380
+ var isFunction = (val) => typeof val === "function";
3381
+ var isObject = (val) => val !== null && typeof val === "object";
3382
+ var isPromise = (val) => isObject(val) && isFunction(val["then"]);
3383
+ var hasOwnProperty = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
3384
+ var deepClone = (obj) => {
3385
+ if (!obj || !isObject(obj)) return {};
3386
+ const newData = Array.isArray(obj) ? [] : Object.create(Object.getPrototypeOf(obj));
3387
+ for (const key in obj) {
3388
+ if (hasOwnProperty(obj, key)) {
3389
+ const value = obj[key];
3390
+ newData[key] = isObject(value) ? deepClone(value) : value;
3391
+ }
3392
+ }
3393
+ return newData;
3394
+ };
3395
+ var cloneData = (data) => isObject(data) ? deepClone(data) : data;
3396
+ var getDataByKey = (target, key) => {
3397
+ let data;
3398
+ const rec = target;
3399
+ if (key.includes(".")) {
3400
+ key.split(".").forEach((k, index) => {
3401
+ data = index === 0 ? rec?.[k] : data?.[k];
3402
+ });
3403
+ } else {
3404
+ data = rec?.[key];
3405
+ }
3406
+ return data;
3407
+ };
3408
+ var Queue = class {
3409
+ pendingTasks = /* @__PURE__ */ new Set();
3410
+ queue = [];
3411
+ flushTasks() {
3412
+ const { pendingTasks, queue } = this;
3413
+ const flushTickTask = () => {
3414
+ while (queue.length > 0) {
3415
+ const task2 = queue.shift();
3416
+ pendingTasks.delete(task2);
3417
+ runTask(task2);
3418
+ }
3419
+ };
3420
+ Promise.resolve().then(flushTickTask);
3421
+ }
3422
+ add(tasks) {
3423
+ const isQueueEmpty = this.queue.length === 0;
3424
+ for (const { cb, params } of tasks) {
3425
+ addParams2Callback(cb, params);
3426
+ if (!this.pendingTasks.has(cb)) {
3427
+ this.queue.push(cb);
3428
+ this.pendingTasks.add(cb);
3429
+ }
3430
+ }
3431
+ if (isQueueEmpty) this.flushTasks();
3432
+ }
3433
+ delete(tasks) {
3434
+ if (this.pendingTasks.size === 0) return;
3435
+ for (const { cb } of tasks) {
3436
+ if (this.pendingTasks.has(cb)) {
3437
+ this.pendingTasks.delete(cb);
3438
+ const index = this.queue.findIndex((item) => item === cb);
3439
+ if (index !== -1) this.queue.splice(index, 1);
3440
+ }
3441
+ }
3442
+ }
3443
+ clear() {
3444
+ this.queue = [];
3445
+ this.pendingTasks.clear();
3446
+ }
3447
+ };
3448
+ var addParams2Callback = (cb, params) => {
3449
+ if (!cb || !params) return;
3450
+ const cbObj = cb;
3451
+ if (isObject(cb) && isObject(cbObj["params"])) {
3452
+ Object.assign(cbObj["params"], params);
3453
+ } else {
3454
+ cbObj["params"] = params;
3455
+ }
3456
+ };
3457
+ var runTask = (cb) => {
3458
+ const cbObj = cb;
3459
+ const params = cbObj["params"];
3460
+ delete cbObj["params"];
3461
+ try {
3462
+ cb(params);
3463
+ } catch {
3464
+ }
3465
+ };
3466
+ var getDefScheduler = () => new Queue();
3467
+ var run = (tasks, scheduler) => {
3468
+ if (scheduler) {
3469
+ if (isFunction(scheduler)) {
3470
+ for (const { cb, params } of tasks) scheduler(cb, params);
3471
+ } else {
3472
+ scheduler.add(tasks);
3473
+ }
3474
+ } else {
3475
+ for (const { cb, params } of tasks) cb(params);
3476
+ }
3477
+ };
3478
+ var ArrMethods = {};
3479
+ ["indexOf", "lastIndexOf", "includes"].forEach((key) => {
3480
+ const rawMethod = Array.prototype[key];
3481
+ ArrMethods[key] = function(...args) {
3482
+ let res = rawMethod.apply(this, args);
3483
+ if (res === -1 || res === false) {
3484
+ res = rawMethod.apply(
3485
+ this,
3486
+ args.map((item) => ProxyCache.get(item) ?? item)
3487
+ );
3488
+ }
3489
+ return res;
3490
+ };
3491
+ });
3492
+ var inArrUpdate = false;
3493
+ ["unshift", "shift", "push", "pop", "splice"].forEach((key) => {
3494
+ const rawMethod = Array.prototype[key];
3495
+ ArrMethods[key] = function(...args) {
3496
+ inArrUpdate = true;
3497
+ const result = rawMethod.apply(this, args);
3498
+ inArrUpdate = false;
3499
+ return result;
3500
+ };
3501
+ });
3502
+ function needKeepArrItem(target, newVal, key) {
3503
+ if (!Array.isArray(target) || !inArrUpdate) return false;
3504
+ return getLinkKeys(newVal) === createLinkKeys(target, key);
3505
+ }
3506
+ var defStateConfig = {
3507
+ belong: LARK_GLOBAL,
3508
+ linkKeys: "",
3509
+ shallow: false
3510
+ };
3511
+ var StateConfigMap = /* @__PURE__ */ new WeakMap();
3512
+ var setStateConfig = (target, config2) => {
3513
+ if (target && isObject(config2)) StateConfigMap.set(target, config2);
3514
+ };
3515
+ var getStateConfig = (target, key) => {
3516
+ if (!StateConfigMap.has(target)) return void 0;
3517
+ const config2 = StateConfigMap.get(target);
3518
+ return key ? config2[key] : config2;
3519
+ };
3520
+ var isState = (target) => isObject(target) && StateConfigMap.has(target);
3521
+ var createLinkKeys = (target, property) => {
3522
+ if (!hasLinkKeys(target)) return property;
3523
+ if (Array.isArray(target)) return getLinkKeys(target) ?? property;
3524
+ return `${getLinkKeys(target)}.${property}`;
3525
+ };
3526
+ var formatLinkKeys = (target, key) => {
3527
+ const originKeysStr = getLinkKeys(target);
3528
+ if (!originKeysStr) return null;
3529
+ const linkKeys = [];
3530
+ const originKeys = originKeysStr.split(".");
3531
+ let pre = "";
3532
+ for (const k of originKeys) {
3533
+ const curr = pre ? `${pre}.${k}` : k;
3534
+ linkKeys.push(curr);
3535
+ pre = curr;
3536
+ }
3537
+ linkKeys.push(`${originKeysStr}.${key}`);
3538
+ return linkKeys;
3539
+ };
3540
+ var getLinkKeys = (target) => getStateConfig(target, "linkKeys");
3541
+ var hasLinkKeys = (target) => !!getLinkKeys(target);
3542
+ var ProxyCache = /* @__PURE__ */ new WeakMap();
3543
+ var keepKey = null;
3544
+ var keep = (key) => {
3545
+ if (keepKey) throw new Error("[lark-store] keepKey is not null");
3546
+ keepKey = key;
3547
+ };
3548
+ var needKeep = (key) => {
3549
+ if (keepKey === key) {
3550
+ keepKey = null;
3551
+ return true;
3552
+ }
3553
+ return false;
3554
+ };
3555
+ var canSetNewVal = (params) => {
3556
+ const { property, newVal, oldVal } = params;
3557
+ if (hasOwnProperty(params.target, property) && newVal === oldVal)
3558
+ return false;
3559
+ return true;
3560
+ };
3561
+ var getNewVal = (params) => {
3562
+ const { target, newVal, property, needKeepVal = false } = params;
3563
+ const config2 = getStateConfig(target);
3564
+ if (!isObject(newVal)) return newVal;
3565
+ if (needKeepArrItem(target, newVal, property)) return newVal;
3566
+ if (config2?.shallow) return newVal;
3567
+ if (needKeepVal) return newVal;
3568
+ const linkKeys = createLinkKeys(target, property);
3569
+ const newState = createState(newVal, {
3570
+ ...config2,
3571
+ linkKeys
3572
+ });
3573
+ if (isPromise(newVal)) handlePromise(newVal, target, property);
3574
+ return newState;
3575
+ };
3576
+ var genPayload = (params) => {
3577
+ const { target, property, newVal, oldVal } = params;
3578
+ const config2 = getStateConfig(target) || defStateConfig;
3579
+ return {
3580
+ belong: config2.belong || LARK_GLOBAL,
3581
+ target,
3582
+ keys: formatLinkKeys(target, property) || [property],
3583
+ newVal,
3584
+ oldVal
3585
+ };
3586
+ };
3587
+ var handlePromise = (child, parent, key) => {
3588
+ child.then((res) => {
3589
+ const parentProxy = ProxyCache.get(parent);
3590
+ if (parentProxy) {
3591
+ const oldVal = parentProxy[key];
3592
+ if (ProxyCache.get(child) === oldVal) {
3593
+ parentProxy[key] = res;
3594
+ }
3595
+ }
3596
+ }).catch((err) => {
3597
+ const parentProxy = ProxyCache.get(parent);
3598
+ if (parentProxy) {
3599
+ const oldVal = parentProxy[key];
3600
+ if (ProxyCache.get(child) === oldVal) {
3601
+ parentProxy[key] = err;
3602
+ }
3603
+ }
3604
+ });
3605
+ };
3606
+ var _deleteKey = "_delete";
3607
+ var _markObjKey = "_markObj";
3608
+ var mark2 = (host, key) => {
3609
+ let sign;
3610
+ if (!host[_deleteKey]) {
3611
+ const markHost = host[_markObjKey] || (host[_markObjKey] = {});
3612
+ if (!hasOwnProperty(markHost, key)) {
3613
+ markHost[key] = 0;
3614
+ }
3615
+ sign = ++markHost[key];
3616
+ }
3617
+ return () => {
3618
+ const temp = host[_markObjKey];
3619
+ return !!temp && sign === temp[key];
3620
+ };
3621
+ };
3622
+ var unmark2 = (host) => {
3623
+ host[_deleteKey] = 1;
3624
+ host[_markObjKey] = 0;
3625
+ };
3626
+ function createState(initialData, config2) {
3627
+ if (!isObject(initialData)) return initialData;
3628
+ const mergedConfig = Object.assign({ ...defStateConfig }, config2);
3629
+ const data = Array.isArray(initialData) ? [] : Object.create(Object.getPrototypeOf(initialData));
3630
+ const handler = {
3631
+ get(target, property, _receiver) {
3632
+ if (Array.isArray(target) && property in ArrMethods) {
3633
+ return ArrMethods[property];
3634
+ }
3635
+ return Reflect.get(target, property);
3636
+ },
3637
+ set(target, property, newVal, receiver) {
3638
+ const strProp = property;
3639
+ const oldVal = Reflect.get(target, property);
3640
+ const preventTrigger = isLazySet(strProp);
3641
+ const needKeepVal = needKeep(strProp);
3642
+ const canSet = canSetNewVal({
3643
+ target,
3644
+ property: strProp,
3645
+ newVal,
3646
+ oldVal
3647
+ });
3648
+ if (!canSet) return true;
3649
+ const proxyTarget = receiver || state;
3650
+ newVal = getNewVal({
3651
+ target: proxyTarget,
3652
+ property: strProp,
3653
+ newVal,
3654
+ needKeepVal
3655
+ });
3656
+ Reflect.set(target, property, newVal, receiver);
3657
+ if (preventTrigger) return true;
3658
+ const payload = genPayload({
3659
+ target: proxyTarget,
3660
+ property: strProp,
3661
+ newVal,
3662
+ oldVal
3663
+ });
3664
+ trigger(payload);
3665
+ return true;
3666
+ },
3667
+ deleteProperty(target, property) {
3668
+ const strProp = property;
3669
+ if (!hasOwnProperty(target, strProp)) return true;
3670
+ const oldVal = Reflect.get(target, property);
3671
+ Reflect.deleteProperty(target, property);
3672
+ const payload = genPayload({
3673
+ target: state,
3674
+ property: strProp,
3675
+ newVal: null,
3676
+ oldVal
3677
+ });
3678
+ trigger(payload);
3679
+ return true;
3680
+ }
3681
+ };
3682
+ const state = new Proxy(data, handler);
3683
+ ProxyCache.set(initialData, state);
3684
+ setStateConfig(state, mergedConfig);
3685
+ lazySet(state, initialData);
3686
+ return state;
3687
+ }
3688
+ var currLazySetKey = null;
3689
+ function lazySet(target, data) {
3690
+ if (isObject(data)) {
3691
+ Reflect.ownKeys(data).forEach((key) => {
3692
+ const strKey = key;
3693
+ if (currLazySetKey) throw new Error("[lark-store] lazy set key conflict");
3694
+ currLazySetKey = strKey;
3695
+ target[strKey] = data[strKey];
3696
+ });
3697
+ }
3698
+ }
3699
+ function isLazySet(property) {
3700
+ if (currLazySetKey === property) {
3701
+ currLazySetKey = "";
3702
+ return true;
3703
+ }
3704
+ return false;
3705
+ }
3706
+ function shallowSet(target, key, data) {
3707
+ if (!isState(target))
3708
+ throw new Error("[lark-store] shallowSet only supports state objects");
3709
+ if (!isObject(data)) return target[key] = data;
3710
+ keep(key);
3711
+ const config2 = getStateConfig(target);
3712
+ const linkKeys = createLinkKeys(target, key);
3713
+ target[key] = createState(data, {
3714
+ ...config2,
3715
+ linkKeys,
3716
+ shallow: true
3717
+ });
3718
+ }
3719
+ var GlobalDeps = /* @__PURE__ */ new Map();
3720
+ function track(payload) {
3721
+ const { belong, trackList = [] } = payload;
3722
+ let deps = GlobalDeps.get(belong);
3723
+ if (!deps) GlobalDeps.set(belong, deps = /* @__PURE__ */ new Map());
3724
+ for (const { key, cb } of trackList) {
3725
+ if (isFunction(cb)) {
3726
+ let callbacks = deps.get(key);
3727
+ if (!callbacks) deps.set(key, callbacks = /* @__PURE__ */ new Set());
3728
+ callbacks.add(cb);
3729
+ }
3730
+ }
3731
+ }
3732
+ function trigger(payload) {
3733
+ const { belong, keys: keys2 } = payload;
3734
+ const store = getStore(belong);
3735
+ if (store && store.status !== 2 /* ACTIVE */ && belong !== LARK_GLOBAL)
3736
+ return;
3737
+ const deps = GlobalDeps.get(belong);
3738
+ if (!deps) return;
3739
+ const tasks = /* @__PURE__ */ new Set();
3740
+ for (const key of keys2) {
3741
+ const callbacks = deps.get(key);
3742
+ if (callbacks) {
3743
+ for (const cb of callbacks) {
3744
+ tasks.add({ cb, params: { [key]: true } });
3745
+ }
3746
+ }
3747
+ }
3748
+ if (tasks.size === 0) return;
3749
+ const scheduler = store?.scheduler;
3750
+ run(Array.from(tasks), scheduler);
3751
+ }
3752
+ function clear(payload) {
3753
+ if (!payload) {
3754
+ GlobalDeps.clear();
3755
+ return;
3756
+ }
3757
+ const { belong, clearList } = payload;
3758
+ const deps = GlobalDeps.get(belong);
3759
+ if (!deps) return;
3760
+ const store = getStore(belong);
3761
+ const scheduler = store?.scheduler;
3762
+ if (clearList) {
3763
+ for (const { key, cb } of clearList) {
3764
+ const callbacks = deps.get(key);
3765
+ if (callbacks) {
3766
+ if (!cb) {
3767
+ deps.delete(key);
3768
+ } else if (callbacks.has(cb)) {
3769
+ callbacks.delete(cb);
3770
+ if (scheduler && !isFunction(scheduler)) {
3771
+ scheduler.delete([{ cb }]);
3772
+ }
3773
+ if (callbacks.size === 0) deps.delete(key);
3774
+ }
3775
+ }
3776
+ }
3777
+ } else {
3778
+ deps.clear();
3779
+ GlobalDeps.delete(belong);
3780
+ if (scheduler && !isFunction(scheduler)) {
3781
+ scheduler.clear();
3782
+ }
3783
+ }
3784
+ }
3785
+ var _storeName = /* @__PURE__ */ Symbol("store-name");
3786
+ var _storeStatus = /* @__PURE__ */ Symbol("store-status");
3787
+ var _storeScheduler = /* @__PURE__ */ Symbol("store-scheduler");
3788
+ var _storeCreate = /* @__PURE__ */ Symbol("fn:store-create");
3789
+ var _storeBoot = /* @__PURE__ */ Symbol("fn:store-boot");
3790
+ var _storeDestroy = /* @__PURE__ */ Symbol("fn:store-destroy");
3791
+ var _innerStore = /* @__PURE__ */ Symbol("inner-store");
3792
+ var _outerStore = /* @__PURE__ */ Symbol("outer-store");
3793
+ var _originState = /* @__PURE__ */ Symbol("origin-state");
3794
+ var _stateKeys = /* @__PURE__ */ Symbol("state-keys");
3795
+ var _storeState = /* @__PURE__ */ Symbol("store-state");
3796
+ var _storeDefScheduler = /* @__PURE__ */ Symbol("store-def-scheduler");
3797
+ var _storeProxy = /* @__PURE__ */ Symbol("fn:store-proxy");
3798
+ var BaseStore = class {
3799
+ [_storeStatus] = 0 /* BEFORE_CREATE */;
3800
+ [_storeDefScheduler] = getDefScheduler;
3801
+ [_storeBoot]() {
3802
+ this[_storeStatus] = 2 /* ACTIVE */;
3803
+ return this[_storeProxy](true);
3804
+ }
3805
+ [_storeDestroy]() {
3806
+ clear({ belong: this[_storeName] });
3807
+ this[_storeState] = createState(this[_originState], {
3808
+ belong: this[_storeName]
3809
+ });
3810
+ this[_storeStatus] = 3 /* DESTROYED */;
3811
+ }
3812
+ /**
3813
+ *
3814
+ * @param body - The object returned by the creator function
3815
+ * @param excludeFns - Function keys to exclude from handlers (e.g. ['observe'])
3816
+ */
3817
+ [_storeCreate](body, excludeFns = ["observe"]) {
3818
+ this[_storeStatus] = 1 /* CREATED */;
3819
+ if (isObject(body)) {
3820
+ const state = {};
3821
+ const handlers = {};
3822
+ Reflect.ownKeys(body).forEach((key) => {
3823
+ const strKey = key;
3824
+ const val = body[strKey];
3825
+ if (isFunction(val)) {
3826
+ if (!excludeFns.includes(strKey)) handlers[strKey] = val;
3827
+ } else {
3828
+ state[strKey] = val;
3829
+ }
3830
+ });
3831
+ Object.assign(this, handlers);
3832
+ this[_originState] = cloneData(state);
3833
+ this[_stateKeys] = Object.keys(state);
3834
+ this[_storeState] = createState(state, { belong: this[_storeName] });
3835
+ }
3836
+ }
3837
+ constructor(name, config2) {
3838
+ this[_storeName] = name;
3839
+ this[_storeScheduler] = config2?.scheduler || this[_storeDefScheduler]();
3840
+ this[_outerStore] = this[_storeProxy](true);
3841
+ }
3842
+ [_innerStore]() {
3843
+ return this[_storeProxy]();
3844
+ }
3845
+ get status() {
3846
+ return this[_storeStatus];
3847
+ }
3848
+ get storeName() {
3849
+ return this[_storeName];
3850
+ }
3851
+ get scheduler() {
3852
+ return this[_storeScheduler];
3853
+ }
3854
+ [_storeProxy](toOut = false) {
3855
+ const self = this;
3856
+ return new Proxy(self, {
3857
+ get(target, property) {
3858
+ if (self[_stateKeys].includes(property)) {
3859
+ const val = self[_storeState][property];
3860
+ return toOut ? cloneData(val) : val;
3861
+ }
3862
+ return Reflect.get(target, property);
3863
+ },
3864
+ set(_target, property, val) {
3865
+ if (toOut) return true;
3866
+ if (self[_stateKeys].includes(property)) {
3867
+ self[_storeState][property] = val;
3868
+ }
3869
+ return true;
3870
+ },
3871
+ has(target, property) {
3872
+ return Reflect.has(target, property) || self[_stateKeys].includes(property);
3873
+ }
3874
+ });
3875
+ }
3876
+ };
3877
+ var LarkUtils = {
3878
+ isLarkView(instance) {
3879
+ return isObject(instance) && !!instance.updater;
3880
+ },
3881
+ getRender(view) {
3882
+ return view.updater.digest.bind(view.updater);
3883
+ },
3884
+ getDataSetter(view) {
3885
+ return view.updater.set.bind(view.updater);
3886
+ },
3887
+ onDestroy(view, cb) {
3888
+ view.on("destroy", cb);
3889
+ }
3890
+ };
3891
+ var getLarkAdapter = (storeName) => ({
3892
+ Store: LarkStore,
3893
+ useStore: ((view) => {
3894
+ const store = getStore(storeName);
3895
+ return store[_storeBoot](view);
3896
+ })
3897
+ });
3898
+ var innerObserveFlags = /* @__PURE__ */ Symbol("store-inner-observe-flags");
3899
+ var boundViews = /* @__PURE__ */ Symbol("bound-views");
3900
+ var LarkStore = class extends BaseStore {
3901
+ [boundViews] = /* @__PURE__ */ new Set();
3902
+ [innerObserveFlags] = /* @__PURE__ */ new Set();
3903
+ [_storeBoot](view) {
3904
+ if (view && LarkUtils.isLarkView(view) && !this[boundViews].has(view)) {
3905
+ this[boundViews].add(view);
3906
+ LarkUtils.onDestroy(view, () => {
3907
+ this[boundViews].delete(view);
3908
+ });
3909
+ }
3910
+ return super[_storeBoot]();
3911
+ }
3912
+ [_storeDestroy]() {
3913
+ this[boundViews].clear();
3914
+ this[innerObserveFlags].clear();
3915
+ super[_storeDestroy]();
3916
+ }
3917
+ observe(view, keys2, defCallback) {
3918
+ if (this[_storeStatus] !== 2 /* ACTIVE */) return noop;
3919
+ let observeKeys = keys2;
3920
+ const _view = view;
3921
+ const renderFn = _view ? LarkUtils.getRender(_view) : noop;
3922
+ const dateSetterFn = _view ? LarkUtils.getDataSetter(_view) : noop;
3923
+ const isInnerObserve = !view;
3924
+ const innerFlags = /* @__PURE__ */ new Set();
3925
+ const storeInnerObserveFlags = this[innerObserveFlags];
3926
+ if (isFunction(keys2)) {
3927
+ const res = keys2();
3928
+ if (Array.isArray(res)) observeKeys = res;
3929
+ }
3930
+ const defSetter = (immediate, key, alias, transform) => () => {
3931
+ const stateVal = getDataByKey(this, key);
3932
+ let data = { [alias || key]: stateVal };
3933
+ if (transform && isFunction(transform)) {
3934
+ const newData = transform(stateVal);
3935
+ if (isObject(newData)) data = newData;
3936
+ }
3937
+ if (immediate) dateSetterFn(data);
3938
+ else renderFn(data);
3939
+ };
3940
+ const getList = (immediate = false) => {
3941
+ const list = [];
3942
+ for (const item of observeKeys) {
3943
+ if (!item) continue;
3944
+ const payload = typeof item === "string" ? { key: item } : item;
3945
+ const { cb: cbDefault = defCallback, key } = payload;
3946
+ let cb = cbDefault;
3947
+ const { alias, lazy = true, transform } = payload;
3948
+ if (!key) continue;
3949
+ const c1 = !immediate;
3950
+ const c2 = !cb && !!_view;
3951
+ const c3 = !!cb && String(lazy) === "false";
3952
+ if (!(c1 || c2 || c3)) continue;
3953
+ if (isInnerObserve && cb) {
3954
+ const flag = `storeInner_${key}_${observeKeys.join("-")}_${cb.toString()}`;
3955
+ if (!storeInnerObserveFlags.has(flag)) {
3956
+ storeInnerObserveFlags.add(flag);
3957
+ innerFlags.add(flag);
3958
+ }
3959
+ }
3960
+ if (c2) cb = defSetter(immediate, key, alias, transform);
3961
+ if (cb) list.push({ key, cb });
3962
+ }
3963
+ return list;
3964
+ };
3965
+ const trackList = getList();
3966
+ track({ belong: this[_storeName], trackList });
3967
+ if (_view) {
3968
+ LarkUtils.onDestroy(
3969
+ _view,
3970
+ () => clear({ belong: this[_storeName], clearList: trackList })
3971
+ );
3972
+ }
3973
+ if (!isInnerObserve) run(getList(true));
3974
+ return () => {
3975
+ clear({ belong: this[_storeName], clearList: trackList });
3976
+ innerFlags.forEach((flag) => storeInnerObserveFlags.delete(flag));
3977
+ };
3978
+ }
3979
+ };
3980
+ var getReactAdapter = (storeName) => ({
3981
+ Store: ReactStore,
3982
+ useStore: (() => {
3983
+ const store = getStore(storeName);
3984
+ return store[_storeBoot]();
3985
+ })
3986
+ });
3987
+ var observeSym = /* @__PURE__ */ Symbol("observe");
3988
+ var getLastState = /* @__PURE__ */ Symbol("get-last-state");
3989
+ var ReactStore = class extends BaseStore {
3990
+ stateChangeCount = 0;
3991
+ lastCount = 0;
3992
+ lastState = null;
3993
+ constructor(name, config2) {
3994
+ const effectiveConfig = config2 && !config2.scheduler ? {
3995
+ ...config2,
3996
+ scheduler: (cb, params) => cb(params)
3997
+ } : config2;
3998
+ super(name, effectiveConfig);
3999
+ }
4000
+ [_storeCreate](body) {
4001
+ super[_storeCreate](body);
4002
+ this[observeSym](() => {
4003
+ this.stateChangeCount += 1;
4004
+ });
4005
+ }
4006
+ isStateChanged() {
4007
+ const currCount = this.stateChangeCount;
4008
+ const changed = this.lastCount !== currCount;
4009
+ this.lastCount = currCount;
4010
+ return changed;
4011
+ }
4012
+ [getLastState](handlers) {
4013
+ if (this.isStateChanged() || !this.lastState) {
4014
+ const state = this[_storeState];
4015
+ const immutableState = freezeData(state);
4016
+ this.lastState = Object.assign({}, handlers, immutableState);
4017
+ }
4018
+ return this.lastState;
4019
+ }
4020
+ [observeSym](cb) {
4021
+ const tasks = this[_stateKeys].map((key) => ({ key, cb }));
4022
+ track({ belong: this[_storeName], trackList: tasks });
4023
+ return () => clear({ belong: this[_storeName], clearList: tasks });
4024
+ }
4025
+ };
4026
+ var freezeData = (target) => {
4027
+ const data = {};
4028
+ const keys2 = Object.keys(target);
4029
+ for (const key of keys2) {
4030
+ const value = target[key];
4031
+ if (Array.isArray(value)) {
4032
+ data[key] = value.map(
4033
+ (item) => isObject(item) ? freezeData(item) : item
4034
+ );
4035
+ } else if (isObject(value)) {
4036
+ data[key] = freezeData(value);
4037
+ } else {
4038
+ data[key] = value;
4039
+ }
4040
+ }
4041
+ return data;
4042
+ };
4043
+ var getNodeAdapter = (storeName) => ({
4044
+ Store: NodeStore,
4045
+ useStore: (() => {
4046
+ const store = getStore(storeName);
4047
+ return store[_storeBoot]();
4048
+ })
4049
+ });
4050
+ var NodeStore = class extends BaseStore {
4051
+ observe(key, callback, immediate = true) {
4052
+ const tasks = [{ key, cb: callback }];
4053
+ track({ belong: this[_storeName], trackList: tasks });
4054
+ if (immediate) run(tasks);
4055
+ return () => clear({ belong: this[_storeName], clearList: tasks });
4056
+ }
4057
+ };
4058
+ var getAdapter = (platform, storeName) => {
4059
+ switch (platform) {
4060
+ case "react" /* React */:
4061
+ return getReactAdapter(storeName);
4062
+ case "node" /* Node */:
4063
+ return getNodeAdapter(storeName);
4064
+ case "lark" /* Lark */:
4065
+ default:
4066
+ return getLarkAdapter(storeName);
4067
+ }
4068
+ };
4069
+ var getPlatform = (comp) => {
4070
+ if (LarkUtils.isLarkView(comp)) return "lark" /* Lark */;
4071
+ return void 0;
4072
+ };
4073
+ var extendApis = { lazySet, shallowSet };
4074
+ var StoreCache = /* @__PURE__ */ new Map();
4075
+ function defineStore(name, creator, config2) {
4076
+ if (StoreCache.has(name)) {
4077
+ name = name + "_copy";
4078
+ }
4079
+ const { platform = "lark" /* Lark */ } = config2 || {};
4080
+ const adapter = getAdapter(platform, name);
4081
+ const StoreClass = adapter.Store;
4082
+ const useStore = adapter.useStore;
4083
+ const store = new StoreClass(name, config2);
4084
+ store[_storeCreate](
4085
+ creator(
4086
+ store[_innerStore](),
4087
+ extendApis
4088
+ )
4089
+ );
4090
+ Object.defineProperties(useStore, {
4091
+ $storeName: { value: name, configurable: true },
4092
+ $del: { value: () => store[_storeDestroy](), configurable: true }
4093
+ });
4094
+ if (!StoreCache.has(name)) {
4095
+ StoreCache.set(name, { store, creator, config: config2, useStore });
4096
+ }
4097
+ return useStore;
4098
+ }
4099
+ function getStore(name) {
4100
+ if (name && StoreCache.has(name)) {
4101
+ const entry = StoreCache.get(name);
4102
+ return entry ? entry.store : void 0;
4103
+ }
4104
+ return void 0;
4105
+ }
4106
+ function delStore(name) {
4107
+ if (name && StoreCache.has(name)) StoreCache.delete(name);
4108
+ }
4109
+ function getUseStore(name) {
4110
+ if (name && StoreCache.has(name)) {
4111
+ const entry = StoreCache.get(name);
4112
+ return entry ? entry.useStore : void 0;
4113
+ }
4114
+ return void 0;
4115
+ }
4116
+ function cloneStore(name, useStore, config2) {
4117
+ const oldStoreName = useStore["$storeName"];
4118
+ const cached = StoreCache.get(oldStoreName);
4119
+ const oldStoreCreator = cached?.creator;
4120
+ const oldConfig = cached?.config || {};
4121
+ if (!name || !oldStoreCreator) return;
4122
+ const mergedConfig = { ...oldConfig, ...config2 || {} };
4123
+ return defineStore(
4124
+ name,
4125
+ oldStoreCreator,
4126
+ mergedConfig
4127
+ );
4128
+ }
4129
+ function isStoreActive(name) {
4130
+ if (name && StoreCache.has(name)) {
4131
+ const entry = StoreCache.get(name);
4132
+ return entry ? entry.store.status === 2 /* ACTIVE */ : false;
4133
+ }
4134
+ return false;
4135
+ }
4136
+ var cellCount = 0;
4137
+ function cell(data) {
4138
+ return createState(data, {
4139
+ belong: LARK_GLOBAL,
4140
+ linkKeys: `${LARK_GLOBAL}_${cellCount++}`
4141
+ });
4142
+ }
4143
+ function observeCell(state, cb, immediate = true) {
4144
+ const linkKeys = getLinkKeys(state);
4145
+ if (!linkKeys)
4146
+ return () => {
4147
+ };
4148
+ const keys2 = linkKeys.split(".");
4149
+ const key = keys2[keys2.length - 1];
4150
+ const list = [{ key, cb }];
4151
+ track({ belong: LARK_GLOBAL, trackList: list });
4152
+ if (immediate) cb();
4153
+ return () => clear({ belong: LARK_GLOBAL, clearList: list });
4154
+ }
4155
+ function multi(useStore) {
4156
+ const storeName = useStore["$storeName"];
4157
+ const flagSym = `lark_comp_${storeName}`;
4158
+ const map = /* @__PURE__ */ new Map();
4159
+ let rootViewPath;
4160
+ const getFlag = (viewContext) => {
4161
+ const owner = viewContext["owner"];
4162
+ const viewPath = owner?.["path"] || "";
4163
+ const viewId = owner?.["id"] || "";
4164
+ let flag;
4165
+ if (viewPath === rootViewPath) {
4166
+ flag = `${flagSym}_${viewId}`;
4167
+ } else {
4168
+ flag = owner?.["viewInitParams"]?.[flagSym];
4169
+ }
4170
+ if (owner && isFunction(owner["mountFrame"])) {
4171
+ const rawMountFrame = owner["mountFrame"];
4172
+ owner["mountFrame"] = (vfId, viewPath2, viewInitParams = {}) => rawMountFrame.call(
4173
+ owner,
4174
+ vfId,
4175
+ viewPath2,
4176
+ Object.assign(viewInitParams, { [flagSym]: flag })
4177
+ );
4178
+ }
4179
+ return flag;
4180
+ };
4181
+ const useFn = ((view) => {
4182
+ if (!view)
4183
+ throw new Error("[lark-store] multi: cannot find the view instance");
4184
+ const viewCtx = view;
4185
+ const flag = viewCtx[flagSym];
4186
+ if (map.has(flag)) return map.get(flag);
4187
+ const newFn = cloneStore(
4188
+ flag,
4189
+ useStore
4190
+ );
4191
+ map.set(flag, newFn);
4192
+ return useFn(view);
4193
+ });
4194
+ const mixinObj = {
4195
+ ctor() {
4196
+ if (!rootViewPath) {
4197
+ const owner = this["owner"];
4198
+ rootViewPath = owner?.["path"] || "";
4199
+ }
4200
+ this[flagSym] = getFlag(this);
4201
+ }
4202
+ };
4203
+ return [useFn, mixinObj];
4204
+ }
4205
+
4206
+ // src/compiler.ts
4207
+ import { parse as babelParse } from "@babel/parser";
4208
+ var SPLITTER2 = "";
4209
+ var VIEW_ID_PLACEHOLDER = "";
4210
+ function jsObjectToUrlParams(paramsStr) {
4211
+ const trimmed = paramsStr.trim();
4212
+ if (!/^[{[]/.test(trimmed) && /=/.test(trimmed)) {
4213
+ return trimmed;
4214
+ }
4215
+ const objMatch = trimmed.match(/^\{(.*)\}$/s);
4216
+ if (objMatch) {
4217
+ const inner = objMatch[1];
4218
+ const pairs = [];
4219
+ const pairRegex = /(\w+)\s*:\s*(?:'([^']*)'|"([^"]*)"|([^,}]+))/g;
4220
+ let m;
4221
+ while ((m = pairRegex.exec(inner)) !== null) {
4222
+ const key = m[1];
4223
+ const value = m[2] ?? m[3] ?? m[4]?.trim() ?? "";
4224
+ pairs.push(`${key}=${value}`);
4225
+ }
4226
+ return pairs.join("&");
4227
+ }
4228
+ return trimmed;
4229
+ }
4230
+ function protectComments(source) {
4231
+ const comments = [];
4232
+ const protectedSource = source.replace(/<!--[\s\S]*?-->/g, (match) => {
4233
+ comments.push(match);
4234
+ return `__lark_comment_${comments.length - 1}__`;
4235
+ });
4236
+ return { protectedSource, comments };
4237
+ }
4238
+ function restoreComments(source, comments) {
4239
+ return source.replace(/__lark_comment_(\d+)__/g, (_, index) => {
4240
+ return comments[parseInt(index, 10)];
4241
+ });
4242
+ }
4243
+ function processLarkEvents(source) {
4244
+ return source.replace(
4245
+ /lark-(\w+)="([^"]+)"/g,
4246
+ (fullAttr, eventName, attrValue) => {
4247
+ const eventMatch = attrValue.match(/^(\w+)\((.*)\)$/s);
4248
+ if (!eventMatch) return fullAttr;
4249
+ const handlerName = eventMatch[1];
4250
+ const paramsStr = eventMatch[2].trim();
4251
+ if (!paramsStr) {
4252
+ return `lark-${eventName}="${VIEW_ID_PLACEHOLDER}${SPLITTER2}${handlerName}()"`;
4253
+ }
4254
+ const urlParams = jsObjectToUrlParams(paramsStr);
4255
+ return `lark-${eventName}="${VIEW_ID_PLACEHOLDER}${SPLITTER2}${handlerName}(${urlParams})"`;
4256
+ }
4257
+ );
4258
+ }
4259
+ function addLineMarkers(source) {
4260
+ const lines = source.split(/\r\n?|\n/);
4261
+ const result = [];
4262
+ let lineNo = 0;
4263
+ const openTag = "{{";
4264
+ for (const line of lines) {
4265
+ const parts = line.split(openTag);
4266
+ if (parts.length > 1) {
4267
+ const reconstructed = parts.map((part, i) => {
4268
+ if (i === 0) return part;
4269
+ return openTag + SPLITTER2 + ++lineNo;
4270
+ }).join("");
4271
+ result.push(reconstructed);
4272
+ } else {
4273
+ result.push(line);
4274
+ }
4275
+ }
4276
+ return result.join("\n");
4277
+ }
4278
+ function extractArtInfo(art) {
4279
+ const m = art.match(new RegExp(`^${SPLITTER2}(\\d+)([\\s\\S]+)`));
4280
+ if (m) {
4281
+ let code = m[2].trimStart();
4282
+ if (code.startsWith("if(")) {
4283
+ code = code.substring(0, 2) + " " + code.substring(2);
4284
+ } else if (code.startsWith("for(")) {
4285
+ code = code.substring(0, 3) + " " + code.substring(3);
4286
+ }
4287
+ return { line: parseInt(m[1], 10), art: code };
4288
+ }
4289
+ return null;
4290
+ }
4291
+ function convertArtSyntax(source, debug) {
4292
+ const markedSource = debug ? addLineMarkers(source) : source;
4293
+ const openTag = "{{";
4294
+ const parts = markedSource.split(openTag);
4295
+ const result = [parts[0]];
4296
+ const blockStack = [];
4297
+ for (let i = 1; i < parts.length; i++) {
4298
+ const part = parts[i];
4299
+ const closeIdx = findCloseBrace(part);
4300
+ if (closeIdx === -1) {
4301
+ result.push(openTag + part);
4302
+ continue;
4303
+ }
4304
+ const code = part.substring(0, closeIdx);
4305
+ const rest = part.substring(closeIdx + 2);
4306
+ let lineNo = -1;
4307
+ let cleanCode = code;
4308
+ if (debug) {
4309
+ const info = extractArtInfo(code);
4310
+ if (info) {
4311
+ lineNo = info.line;
4312
+ cleanCode = info.art;
4313
+ }
4314
+ } else {
4315
+ cleanCode = code.trim();
4316
+ }
4317
+ const converted = convertArtExpression(
4318
+ cleanCode,
4319
+ debug,
4320
+ lineNo,
4321
+ blockStack
4322
+ );
4323
+ result.push(converted);
4324
+ result.push(rest);
4325
+ }
4326
+ if (blockStack.length > 0) {
4327
+ const unclosed = blockStack.map((b) => `"${b.ctrl}" at line ${b.line}`).join(", ");
4328
+ throw new Error(`[Lark Error(tmpl-art)] unclosed block(s): ${unclosed}`);
4329
+ }
4330
+ return result.join("");
4331
+ }
4332
+ function findCloseBrace(str) {
4333
+ let leftCount = 0;
4334
+ let rightCount = 0;
4335
+ let maybeCount = 0;
4336
+ let maybeAt = -1;
4337
+ for (let i = 0; i < str.length; i++) {
4338
+ const c = str.charAt(i);
4339
+ if (c !== "}") {
4340
+ if (maybeCount >= 2 && maybeAt === -1) {
4341
+ maybeAt = i;
4342
+ }
4343
+ maybeCount = 0;
4344
+ rightCount = 0;
4345
+ }
4346
+ if (c === "{") {
4347
+ leftCount++;
4348
+ } else if (c === "}") {
4349
+ maybeCount++;
4350
+ if (!leftCount) {
4351
+ rightCount++;
4352
+ if (rightCount === 2) {
4353
+ return i - 1;
4354
+ }
4355
+ } else {
4356
+ leftCount--;
4357
+ }
4358
+ }
4359
+ }
4360
+ if (!leftCount && maybeCount >= 2 && maybeAt === -1) {
4361
+ maybeAt = str.length - 2;
4362
+ }
4363
+ if (maybeAt > -1) {
4364
+ return maybeAt - 2;
4365
+ }
4366
+ return -1;
4367
+ }
4368
+ function trimOuterParens(expr) {
4369
+ expr = expr.trim();
4370
+ while (expr.startsWith("(") && expr.endsWith(")")) {
4371
+ let depth = 0;
4372
+ let matched = true;
4373
+ for (let i = 0; i < expr.length - 1; i++) {
4374
+ const c = expr.charAt(i);
4375
+ if (c === "(") depth++;
4376
+ else if (c === ")") depth--;
4377
+ if (depth === 0 && i < expr.length - 1) {
4378
+ matched = false;
4379
+ break;
4380
+ }
4381
+ }
4382
+ if (!matched) break;
4383
+ expr = expr.substring(1, expr.length - 1).trim();
4384
+ }
4385
+ return expr;
4386
+ }
4387
+ function convertArtExpression(code, debug, lineNo, blockStack = []) {
4388
+ code = code.trim();
4389
+ const debugPrefix = debug && lineNo > -1 ? `<%'${lineNo}${code.replace(/\\|'/g, "\\$&").replace(/\r\n?|\n/g, "\\n")}'%>` : "";
4390
+ const ifForMatch = code.match(/^\s*(if|for)\s*\(/);
4391
+ if (ifForMatch) {
4392
+ const keyword2 = ifForMatch[1];
4393
+ const expr = code.substring(ifForMatch[0].length);
4394
+ if (keyword2 === "if") {
4395
+ blockStack.push({ ctrl: "if", line: lineNo });
4396
+ const rawExpr = expr.replace(/\)\s*$/, "");
4397
+ const cleanExpr = trimOuterParens(rawExpr);
4398
+ return `${debugPrefix}<%if(${cleanExpr}){%>`;
4399
+ }
4400
+ blockStack.push({ ctrl: "for", line: lineNo });
4401
+ const forExpr = expr.replace(/\)\s*$/, "");
4402
+ return `${debugPrefix}<%for(${forExpr}){%>`;
4403
+ }
4404
+ const tokens = code.split(/\s+/);
4405
+ const keyword = tokens.shift();
4406
+ switch (keyword) {
4407
+ case "if": {
4408
+ blockStack.push({ ctrl: "if", line: lineNo });
4409
+ const rawExpr = tokens.join(" ").trim();
4410
+ const expr = trimOuterParens(rawExpr);
4411
+ return `${debugPrefix}<%if(${expr}){%>`;
4412
+ }
4413
+ case "else": {
4414
+ if (tokens[0] === "if") {
4415
+ tokens.shift();
4416
+ const rawExpr = tokens.join(" ").trim();
4417
+ const expr = trimOuterParens(rawExpr);
4418
+ return `${debugPrefix}<%}else if(${expr}){%>`;
4419
+ }
4420
+ return `${debugPrefix}<%}else{%>`;
4421
+ }
4422
+ case "each": {
4423
+ blockStack.push({ ctrl: "each", line: lineNo });
4424
+ const object = tokens[0];
4425
+ if (tokens.length > 1 && tokens[1] !== "as") {
4426
+ throw new Error(
4427
+ `[Lark Error(tmpl-art)] bad each syntax: {{${code}}}. Expected "as" keyword, got "${tokens[1]}". Usage: {{each list as item [index]}}`
4428
+ );
4429
+ }
4430
+ const restTokens = tokens.slice(2);
4431
+ const asValue = restTokens.join(" ");
4432
+ const asExpr = parseAsExpr(asValue);
4433
+ const index = asExpr.key || "_i";
4434
+ const refObj = /[.[\]]/.test(object) ? `_art_obj_${object.replace(/[^\w]/g, "_")}` : object;
4435
+ const refExpr = /[.[\]]/.test(object) ? `,${refObj}=${object}` : "";
4436
+ const refObjCount = "_l";
4437
+ const valueDecl = asExpr.vars ? `let ${asExpr.vars}=${refObj}[${index}]` : "";
4438
+ let firstAndLast = "";
4439
+ let lastCount = "";
4440
+ if (asExpr.first) {
4441
+ firstAndLast += `let ${asExpr.first}=${index}===0;`;
4442
+ }
4443
+ if (asExpr.last) {
4444
+ lastCount = `,_lc=${refObjCount}-1`;
4445
+ firstAndLast += `let ${asExpr.last}=${index}===_lc;`;
4446
+ }
4447
+ return `${debugPrefix}<%for(let ${index}=0${refExpr},${refObjCount}=${refObj}.length${lastCount};${index}<${refObjCount};${index}++){${firstAndLast}${valueDecl}%>`;
4448
+ }
4449
+ case "forin": {
4450
+ blockStack.push({ ctrl: "forin", line: lineNo });
4451
+ const object = tokens[0];
4452
+ if (tokens.length > 1 && tokens[1] !== "as") {
4453
+ throw new Error(
4454
+ `[Lark Error(tmpl-art)] bad forin syntax: {{${code}}}. Expected "as" keyword, got "${tokens[1]}". Usage: {{forin obj as val [key]}}`
4455
+ );
4456
+ }
4457
+ const restTokens2 = tokens.slice(2);
4458
+ const asValue2 = restTokens2.join(" ");
4459
+ const asExpr2 = parseAsExpr(asValue2);
4460
+ const key1 = asExpr2.key || "_k";
4461
+ const refObj2 = /[.[\]]/.test(object) ? `_art_obj_${object.replace(/[^\w]/g, "_")}` : object;
4462
+ const refExpr2 = /[.[\]]/.test(object) ? `let ${refObj2}=${object};` : "";
4463
+ const valueDecl2 = asExpr2.vars ? `let ${asExpr2.vars}=${refObj2}[${key1}]` : "";
4464
+ return `${debugPrefix}<%${refExpr2}for(let ${key1} in ${refObj2}){${valueDecl2}%>`;
4465
+ }
4466
+ case "for": {
4467
+ blockStack.push({ ctrl: "for", line: lineNo });
4468
+ const expr = tokens.join(" ").trim();
4469
+ return `${debugPrefix}<%for(${expr}){%>`;
4470
+ }
4471
+ case "set":
4472
+ return `${debugPrefix}<%let ${tokens.join(" ")};%>`;
4473
+ case "/if":
4474
+ case "/each":
4475
+ case "/forin":
4476
+ case "/for": {
4477
+ const expectedCtrl = keyword.substring(1);
4478
+ const last = blockStack.pop();
4479
+ if (!last) {
4480
+ throw new Error(
4481
+ `[Lark Error(tmpl-art)] unexpected {{${code}}}: no matching open block`
4482
+ );
4483
+ }
4484
+ if (last.ctrl !== expectedCtrl) {
4485
+ throw new Error(
4486
+ `[Lark Error(tmpl-art)] unexpected {{${code}}}: expected {{/${last.ctrl}}} to close block opened at line ${last.line}`
4487
+ );
4488
+ }
4489
+ return `${debugPrefix}<%}%>`;
4490
+ }
4491
+ default:
4492
+ return `${debugPrefix}<%${code}%>`;
4493
+ }
4494
+ }
4495
+ function parseAsExpr(expr) {
4496
+ expr = expr.trim();
4497
+ if (!expr) {
4498
+ return { vars: "", key: "", last: "", first: "", bad: false };
4499
+ }
4500
+ if (expr.startsWith("{") || expr.startsWith("[")) {
4501
+ const stack = [];
4502
+ let vars = "";
4503
+ let key = "";
4504
+ let last = "";
4505
+ let first = "";
4506
+ let pos = 0;
4507
+ let bad = false;
4508
+ for (const c of expr) {
4509
+ if (pos === 0) vars += c;
4510
+ else if (pos === 1) key += c;
4511
+ else if (pos === 2) last += c;
4512
+ else if (pos === 3) first += c;
4513
+ if (c === "{" || c === "[") stack.push(c);
4514
+ else if (c === "}") {
4515
+ if (stack[stack.length - 1] === "{") stack.pop();
4516
+ else {
4517
+ bad = true;
4518
+ break;
4519
+ }
4520
+ } else if (c === "]") {
4521
+ if (stack[stack.length - 1] === "[") stack.pop();
4522
+ else {
4523
+ bad = true;
4524
+ break;
4525
+ }
4526
+ } else if (c === " " && !stack.length) {
4527
+ pos++;
4528
+ }
4529
+ }
4530
+ return {
4531
+ vars: vars.trim(),
4532
+ key: key.trim(),
4533
+ last: last.trim(),
4534
+ first: first.trim(),
4535
+ bad: bad || stack.length > 0
4536
+ };
4537
+ }
4538
+ const parts = expr.split(/\s+/);
4539
+ return {
4540
+ vars: parts[0] || "",
4541
+ key: parts[1] || "",
4542
+ last: parts[2] || "",
4543
+ first: parts[3] || "",
4544
+ bad: false
4545
+ };
4546
+ }
4547
+ function compileToFunction(source, debug, file) {
4548
+ const matcher = /<%([@=!:])?([\s\S]*?)%>|$/g;
4549
+ let index = 0;
4550
+ let funcSource = `$p+='`;
4551
+ let hasAtRule = false;
4552
+ const escapeSlashRegExp = /\\|'/g;
4553
+ const escapeBreakReturnRegExp = /\r|\n/g;
4554
+ source.replace(matcher, (match, operate, content, offset) => {
4555
+ funcSource += source.substring(index, offset).replace(escapeSlashRegExp, "\\$&").replace(escapeBreakReturnRegExp, "\\n");
4556
+ index = offset + match.length;
4557
+ if (debug) {
4558
+ let expr = source.substring(
4559
+ index - match.length + 2 + (operate ? 1 : 0),
4560
+ index - 2
4561
+ );
4562
+ const x11 = String.fromCharCode(17);
4563
+ const artReg = new RegExp(`^'(\\d+)${x11}([^${x11}]+)${x11}'$`);
4564
+ const artM = expr.match(artReg);
4565
+ let art = "";
4566
+ let line = -1;
4567
+ if (artM) {
4568
+ expr = expr.replace(artReg, "");
4569
+ art = artM[2];
4570
+ line = parseInt(artM[1], 10);
4571
+ } else {
4572
+ expr = expr.replace(escapeSlashRegExp, "\\$&").replace(escapeBreakReturnRegExp, "\\n");
4573
+ }
4574
+ if (operate === "@") {
4575
+ hasAtRule = true;
4576
+ funcSource += `'+($expr='<%${operate + expr}%>',$i($$ref,${content}))+'`;
4577
+ } else if (operate === "=" || operate === ":") {
4578
+ funcSource += `'+($expr='<%${operate + expr}%>',$e(${content}))+'`;
4579
+ } else if (operate === "!") {
4580
+ if (!content.startsWith("$eu(") || !content.endsWith(")")) {
4581
+ content = `$n(${content})`;
4582
+ }
4583
+ funcSource += `'+($expr='<%${operate + expr}%>',${content})+'`;
4584
+ } else if (content) {
4585
+ if (line > -1) {
4586
+ funcSource += `';$line=${line};$art='${art}';`;
4587
+ content = "";
4588
+ } else {
4589
+ funcSource += `';`;
4590
+ }
4591
+ if (funcSource.endsWith(`+'';`)) {
4592
+ funcSource = funcSource.substring(0, funcSource.length - 4) + ";";
4593
+ }
4594
+ if (expr) {
4595
+ funcSource += `$expr='<%${expr}%>';`;
4596
+ }
4597
+ funcSource += content + `;$p+='`;
4598
+ }
4599
+ } else {
4600
+ if (operate === "@") {
4601
+ hasAtRule = true;
4602
+ funcSource += `'+$i($$ref,${content})+'`;
4603
+ } else if (operate === "=" || operate === ":") {
4604
+ funcSource += `'+$e(${content})+'`;
4605
+ } else if (operate === "!") {
4606
+ if (!content.startsWith("$eu(") || !content.endsWith(")")) {
4607
+ content = `$n(${content})`;
4608
+ }
4609
+ funcSource += `'+${content}+'`;
4610
+ } else if (content) {
4611
+ funcSource += `';`;
4612
+ if (funcSource.endsWith(`+'';`)) {
4613
+ funcSource = funcSource.substring(0, funcSource.length - 4) + ";";
4614
+ }
4615
+ funcSource += `${content};$p+='`;
4616
+ }
4617
+ }
4618
+ return match;
4619
+ });
4620
+ funcSource += `';`;
4621
+ funcSource = funcSource.replace(/\$p\+='';/g, "");
4622
+ funcSource = funcSource.replace(/\$p\+=''\+/g, "$p+=");
4623
+ if (debug) {
4624
+ const filePart = file ? `\\r\\n\\tat file:${file}` : "";
4625
+ funcSource = `let $expr,$art,$line;try{${funcSource}}catch(ex){let msg='render view error:'+(ex.message||ex);if($art)msg+='\\r\\n\\tsrc art:{{'+$art+'}}\\r\\n\\tat line:'+$line;msg+='\\r\\n\\t'+($art?'translate to:':'expr:');msg+=$expr+'${filePart}';throw msg;}`;
4626
+ }
4627
+ const viewIdReg = new RegExp(String.fromCharCode(31), "g");
4628
+ funcSource = funcSource.replace(viewIdReg, `'+$viewId+'`);
4629
+ const atRule = hasAtRule ? `if(!$i){$i=(ref,v,k,f)=>{for(f=ref[$g];--f;)if(ref[k=$g+f]===v)return k;ref[k=$g+ref[$g]++]=v;return k;}}` : "";
4630
+ const encode = `if(!$n){let $em={'&':'amp','<':'lt','>':'gt','"':'#34','\\'':'#39','\`':'#96'},$er=/[&<>"'\`]/g,$ef=m=>'&'+$em[m]+';';$n=v=>''+(v==null?'':v);$e=v=>$n(v).replace($er,$ef)}`;
4631
+ const encodeURIMore = `if(!$eu){let $um={'!':'%21','\\'':'%27','(':'%28',')':'%29','*':'%2A'},$uf=m=>$um[m],$uq=/[!')(*]/g;$eu=v=>encodeURIComponent($n(v)).replace($uq,$uf)}`;
4632
+ const encodeQuote = `if(!$eq){let $qr=/['"\\\\]/g;$eq=v=>$n(v).replace($qr,'\\\\$&')}`;
4633
+ const refFallback = "if(!$$ref)$$ref=$$;";
4634
+ const fns = `${refFallback}${encode}${encodeURIMore}${encodeQuote}${atRule};`;
4635
+ const fullSource = `${fns}let $g='\\x1e',$_temp,$p=''{{VARS}};${funcSource}return $p`;
4636
+ return `($$,$viewId,$$ref,$e,$n,$eu,$i,$eq)=>{${fullSource}}`;
4637
+ }
4638
+ function compileTemplate(source, options = {}) {
4639
+ const { debug = false, globalVars = [], file } = options;
4640
+ const { protectedSource, comments } = protectComments(source);
4641
+ const converted = convertArtSyntax(protectedSource, debug);
4642
+ const larkEventProcessed = processLarkEvents(converted);
4643
+ const finalSource = restoreComments(larkEventProcessed, comments);
4644
+ const funcBody = compileToFunction(finalSource, debug, file);
4645
+ const varDeclarations = globalVars.map((key) => `,${key}=$$.${key}`).join("");
4646
+ const funcWithVars = funcBody.replace("{{VARS}}", () => varDeclarations);
4647
+ return `export default function(data, selfId, refData) {
4648
+ let $$ = data || {},
4649
+ $viewId = selfId || '';
4650
+ return (${funcWithVars})($$, $viewId, refData,
4651
+ /* $e */ v => String(v == null ? '' : v).replace(/[&<>"'\`]/g, m => '&' + ({'&':'amp','<':'lt','>':'gt','"':'#34',"'":'#39','\`':'#96'})[m] + ';'),
4652
+ /* $n */ v => String(v == null ? '' : v),
4653
+ /* $eu */ null,
4654
+ /* $i */ null,
4655
+ /* $eq */ null
4656
+ );
4657
+ }`;
4658
+ }
4659
+ function extractGlobalVars(source) {
4660
+ const { protectedSource, comments: _comments } = protectComments(source);
4661
+ const larkEventProcessed = processLarkEvents(protectedSource);
4662
+ const converted = convertArtSyntax(larkEventProcessed, false);
4663
+ const tmpl = restoreComments(converted, _comments);
4664
+ const tmplCmdReg = /<%([@=!:])?([\s\S]*?)%>|$/g;
4665
+ const fnParts = [];
4666
+ const htmlStore = {};
4667
+ let htmlIndex = 0;
4668
+ let lastIndex = 0;
4669
+ const htmlKey = "";
4670
+ tmpl.replace(
4671
+ tmplCmdReg,
4672
+ (match, operate, content, offset) => {
4673
+ const start = operate ? 3 : 2;
4674
+ const htmlText = tmpl.substring(lastIndex, offset + start);
4675
+ const key = htmlKey + htmlIndex++ + htmlKey;
4676
+ htmlStore[key] = htmlText;
4677
+ lastIndex = offset + match.length - 2;
4678
+ if (operate && content.trim()) {
4679
+ fnParts.push(';"' + key + '";', "[" + content + "]");
4680
+ } else {
4681
+ fnParts.push(';"' + key + '";', content || "");
4682
+ }
4683
+ return match;
4684
+ }
4685
+ );
4686
+ let fn = fnParts.join("");
4687
+ fn = `(function(){${fn}})`;
4688
+ let ast;
4689
+ try {
4690
+ ast = babelParse(fn, {
4691
+ sourceType: "script",
4692
+ allowReturnOutsideFunction: true,
4693
+ allowAwaitOutsideFunction: true
4694
+ });
4695
+ } catch {
4696
+ return fallbackExtractVariables(source);
4697
+ }
4698
+ const globalExists = { ...BUILTIN_GLOBALS };
4699
+ const globalVars = /* @__PURE__ */ Object.create(null);
4700
+ const fnRange = [];
4701
+ walkAst(ast, {
4702
+ VariableDeclarator(node) {
4703
+ if (node.id.type === "Identifier") {
4704
+ const name = node.id.name;
4705
+ globalExists[name] = node.init ? 3 : 2;
4706
+ }
4707
+ },
4708
+ FunctionDeclaration(node) {
4709
+ if (node.id) {
4710
+ globalExists[node.id.name] = 3;
4711
+ }
4712
+ fnRange.push(node);
4713
+ },
4714
+ FunctionExpression(node) {
4715
+ fnRange.push(node);
4716
+ },
4717
+ ArrowFunctionExpression(node) {
4718
+ fnRange.push(node);
4719
+ },
4720
+ CallExpression(node) {
4721
+ if (node.callee.type === "Identifier") {
4722
+ globalExists[node.callee.name] = 1;
4723
+ }
4724
+ }
4725
+ });
4726
+ const functionParams = /* @__PURE__ */ Object.create(null);
4727
+ for (const fnNode of fnRange) {
4728
+ const params = "params" in fnNode ? fnNode.params : [];
4729
+ for (const p of params) {
4730
+ if (p.type === "Identifier") {
4731
+ functionParams[p.name] = 1;
4732
+ } else if (p.type === "AssignmentPattern" && p.left.type === "Identifier") {
4733
+ functionParams[p.left.name] = 1;
4734
+ } else if (p.type === "RestElement" && p.argument.type === "Identifier") {
4735
+ functionParams[p.argument.name] = 1;
4736
+ }
4737
+ }
4738
+ }
4739
+ walkAst(ast, {
4740
+ Identifier(node) {
4741
+ const name = node.name;
4742
+ if (globalExists[name]) return;
4743
+ if (functionParams[name]) return;
4744
+ globalVars[name] = 1;
4745
+ },
4746
+ AssignmentExpression(node) {
4747
+ if (node.left.type === "Identifier") {
4748
+ const name = node.left.name;
4749
+ if (!globalExists[name] || globalExists[name] === 1) {
4750
+ globalExists[name] = (globalExists[name] || 0) + 1;
4751
+ }
4752
+ }
4753
+ }
4754
+ });
4755
+ return Object.keys(globalVars);
4756
+ }
4757
+ function fallbackExtractVariables(source) {
4758
+ const vars = /* @__PURE__ */ new Set();
4759
+ const outputReg = /\{\{[:=!@]\s*([a-zA-Z_$][\w$]*)[^}]*\}\}/g;
4760
+ let m;
4761
+ while ((m = outputReg.exec(source)) !== null) {
4762
+ vars.add(m[1]);
4763
+ }
4764
+ const eachReg = /\{\{each\s+([a-zA-Z_$][\w$]*)\s+as/g;
4765
+ while ((m = eachReg.exec(source)) !== null) {
4766
+ vars.add(m[1]);
4767
+ }
4768
+ const ifReg = /\{\{(?:else\s+)?if\s+([a-zA-Z_$][\w$]*)[^}]*\}\}/g;
4769
+ while ((m = ifReg.exec(source)) !== null) {
4770
+ vars.add(m[1]);
4771
+ }
4772
+ return Array.from(vars).filter((v) => !BUILTIN_GLOBAL_SET.has(v));
4773
+ }
4774
+ function walkAst(ast, visitors) {
4775
+ function visit(node) {
4776
+ const type = node.type;
4777
+ if (visitors[type]) {
4778
+ visitors[type](node);
4779
+ }
4780
+ for (const key of Object.keys(node)) {
4781
+ if (key === "type" || key === "start" || key === "end" || key === "loc" || key === "range")
4782
+ continue;
4783
+ if (type === "MemberExpression" && key === "property" && !node.computed)
4784
+ continue;
4785
+ if (type === "ObjectProperty" && key === "key" && !node.computed) {
4786
+ continue;
4787
+ }
4788
+ if (type === "ObjectMethod" && key === "key" && !node.computed) {
4789
+ continue;
4790
+ }
4791
+ const child = node[key];
4792
+ if (Array.isArray(child)) {
4793
+ for (const item of child) {
4794
+ if (item && typeof item === "object" && typeof item.type === "string") {
4795
+ visit(item);
4796
+ }
4797
+ }
4798
+ } else if (child && typeof child === "object" && typeof child.type === "string") {
4799
+ visit(child);
4800
+ }
4801
+ }
4802
+ }
4803
+ visit(ast);
4804
+ }
4805
+ var BUILTIN_GLOBALS = {
4806
+ // ─── Template runtime helpers (injected by compileToFunction) ───────
4807
+ //
4808
+ // These variables appear in the generated template function signature
4809
+ // or body. They must be excluded from extractGlobalVars() so that
4810
+ // they are not mistaken for user data variables and destructured from $$.
4811
+ // SPLITTER character constant (same as \x1e), used as namespace separator
4812
+ // for refData keys, event attribute encoding, and internal data structures.
4813
+ // Declared as: let $g='\x1e'
4814
+ $g: 1,
4815
+ // refData — the data object passed from Updater to the template function.
4816
+ // User variables are destructured from $$ at the top of the function:
4817
+ // let {name, age} = $$;
4818
+ // This is the first parameter of the generated arrow function.
4819
+ $$: 1,
4820
+ // Null-safe toString: v => '' + (v == null ? '' : v)
4821
+ // Converts null/undefined to empty string, otherwise calls toString().
4822
+ // Wraps every {{!raw}} output to prevent "null" / "undefined" rendering.
4823
+ $n: 1,
4824
+ // HTML entity encoder: v => $n(v).replace(/[&<>"'`]/g, entityMap)
4825
+ // Encodes &, <, >, ", ', ` to HTML entities (&amp; &lt; etc.)
4826
+ // Applied to all {{=escaped}} and {{:binding}} outputs.
4827
+ $e: 1,
4828
+ // HTML entity map — internal object used by $e:
4829
+ // {'&':'amp','<':'gt','>':'gt','"':'#34','\'':'#39','`':'#96'}
4830
+ // Not a standalone function; referenced inside $e's closure.
4831
+ $em: 1,
4832
+ // HTML entity RegExp — internal regex used by $e:
4833
+ // /[&<>"'`]/g
4834
+ $er: 1,
4835
+ // HTML entity replacer function — internal helper used by $e:
4836
+ // m => '&' + $em[m] + ';'
4837
+ // Maps each matched character to its entity string.
4838
+ $ef: 1,
4839
+ // Output buffer — the string accumulator for rendered HTML.
4840
+ // All template output is appended via $p += '...'.
4841
+ // Declared as: let $p = ''
4842
+ $p: 1,
4843
+ // Reference lookup: (refData, value) => key
4844
+ // Finds or allocates a SPLITTER-prefixed key in refData for a given
4845
+ // object reference. Used by {{@ref}} operator for passing object
4846
+ // references to child views via lark-view attributes.
4847
+ // Aligned with mx.js Updater_Ref.
4848
+ $i: 1,
4849
+ // URI encoder: v => encodeURIComponent($n(v)).replace(/[!')(*]/g, extraMap)
4850
+ // Extends encodeURIComponent with encoding of ! ' ( ) *.
4851
+ // Applied to values in lark-event URL parameters and {{!uri}} contexts.
4852
+ $eu: 1,
4853
+ // Quote encoder: v => $n(v).replace(/['"\\]/g, '\\$&')
4854
+ // Escapes quotes and backslashes for safe embedding in HTML attribute
4855
+ // values (e.g. data-json='...').
4856
+ $eq: 1,
4857
+ // View ID — the unique identifier of the owning View instance.
4858
+ // Injected into lark-event attribute values at render time so that
4859
+ // EventDelegator can dispatch events to the correct View handler.
4860
+ // The \x1f placeholder in compiled output is replaced with '+$viewId+'.
4861
+ $viewId: 1,
4862
+ // Debug: current expression text — stores the template expression being
4863
+ // evaluated, for error reporting. Only present in debug mode.
4864
+ // e.g. $expr='<%=user.name%>'
4865
+ $expr: 1,
4866
+ // Debug: original art syntax — stores the {{}} template syntax before
4867
+ // conversion, for error reporting. Only present in debug mode.
4868
+ // e.g. $art='{{=user.name}}'
4869
+ $art: 1,
4870
+ // Debug: source line number — tracks the current line in the template
4871
+ // source, for error reporting. Only present in debug mode.
4872
+ $line: 1,
4873
+ // refData alias — fallback reference lookup table.
4874
+ // Defaults to $$ when no explicit $$ref is provided.
4875
+ // Ensures $i() does not crash when @ operator is used without refData.
4876
+ $$ref: 1,
4877
+ // Temporary variable — used by the compiler for intermediate
4878
+ // expression results in generated code (e.g. loop variables,
4879
+ // conditional branches). Declared as: let $_temp
4880
+ $_temp: 1,
4881
+ // JS literals
4882
+ undefined: 1,
4883
+ null: 1,
4884
+ true: 1,
4885
+ false: 1,
4886
+ NaN: 1,
4887
+ Infinity: 1,
4888
+ // JS built-in globals
4889
+ window: 1,
4890
+ self: 1,
4891
+ globalThis: 1,
4892
+ document: 1,
4893
+ console: 1,
4894
+ JSON: 1,
4895
+ Math: 1,
4896
+ Intl: 1,
4897
+ Promise: 1,
4898
+ Symbol: 1,
4899
+ Number: 1,
4900
+ String: 1,
4901
+ Boolean: 1,
4902
+ Array: 1,
4903
+ Object: 1,
4904
+ Date: 1,
4905
+ RegExp: 1,
4906
+ Error: 1,
4907
+ TypeError: 1,
4908
+ RangeError: 1,
4909
+ SyntaxError: 1,
4910
+ Map: 1,
4911
+ Set: 1,
4912
+ WeakMap: 1,
4913
+ WeakSet: 1,
4914
+ Proxy: 1,
4915
+ Reflect: 1,
4916
+ ArrayBuffer: 1,
4917
+ DataView: 1,
4918
+ Float32Array: 1,
4919
+ Float64Array: 1,
4920
+ Int8Array: 1,
4921
+ Int16Array: 1,
4922
+ Int32Array: 1,
4923
+ Uint8Array: 1,
4924
+ Uint16Array: 1,
4925
+ Uint32Array: 1,
4926
+ Uint8ClampedArray: 1,
4927
+ // Functions
4928
+ parseInt: 1,
4929
+ parseFloat: 1,
4930
+ isNaN: 1,
4931
+ isFinite: 1,
4932
+ encodeURIComponent: 1,
4933
+ decodeURIComponent: 1,
4934
+ encodeURI: 1,
4935
+ decodeURI: 1,
4936
+ // Babel helpers
4937
+ arguments: 1,
4938
+ this: 1,
4939
+ require: 1,
4940
+ // Lark framework
4941
+ Lark: 1
4942
+ };
4943
+ var BUILTIN_GLOBAL_SET = new Set(Object.keys(BUILTIN_GLOBALS));
4944
+ export {
4945
+ Bag,
4946
+ CALL_BREAK_TIME,
4947
+ Cache,
4948
+ EVT_METHOD_REG,
4949
+ EventDelegator,
4950
+ EventEmitter,
4951
+ Frame,
4952
+ Framework,
4953
+ LARK_VIEW,
4954
+ Platform,
4955
+ ROUTER_EVENTS,
4956
+ Router,
4957
+ SPLITTER,
4958
+ Service,
4959
+ State,
4960
+ TAG_ATTR_KEY,
4961
+ TAG_KEY,
4962
+ TAG_NAME_REGEX,
4963
+ TAG_VIEW_KEY,
4964
+ Updater,
4965
+ VIEW_EVT_METHOD_REG,
4966
+ View,
4967
+ applyIdUpdates,
4968
+ applyStyle,
4969
+ applyVdomOps,
4970
+ assign,
4971
+ cell,
4972
+ classExtend,
4973
+ cloneData,
4974
+ cloneStore,
4975
+ compileTemplate,
4976
+ createState,
4977
+ createVdomRef,
4978
+ defineStore,
4979
+ delStore,
4980
+ encodeHTML,
4981
+ encodeQ,
4982
+ encodeSafe,
4983
+ encodeURIExtra,
4984
+ ensureElementId,
4985
+ extractGlobalVars,
4986
+ funcWithTry,
4987
+ generateId,
4988
+ getAttribute,
4989
+ getById,
4990
+ getPlatform,
4991
+ getStore,
4992
+ getUseStore,
4993
+ has,
4994
+ isArray,
4995
+ isPlainObject,
4996
+ isPrimitive,
4997
+ isPrimitiveOrFunc,
4998
+ isState,
4999
+ isStoreActive,
5000
+ keys,
5001
+ lazySet,
5002
+ mark,
5003
+ markBooted,
5004
+ markRouterBooted,
5005
+ multi,
5006
+ nextCounter,
5007
+ nodeInside,
5008
+ noop,
5009
+ now,
5010
+ observeCell,
5011
+ parseUri,
5012
+ registerViewClass,
5013
+ safeguard,
5014
+ setData,
5015
+ shallowSet,
5016
+ mark2 as storeMark,
5017
+ unmark2 as storeUnmark,
5018
+ syncCounter,
5019
+ toMap,
5020
+ toUri,
5021
+ translateData,
5022
+ unmark,
5023
+ vdomGetCompareKey,
5024
+ vdomGetNode,
5025
+ vdomSetAttributes,
5026
+ vdomSetChildNodes,
5027
+ vdomSetNode,
5028
+ vdomSpecialDiff,
5029
+ vdomUnmountFrames
5030
+ };