@lark.js/mvc 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -38,7 +38,6 @@ function isPlainObject(value) {
38
38
  if (proto === null) return true;
39
39
  return proto === Object.prototype || proto === null;
40
40
  }
41
- var isArray = Array.isArray;
42
41
  function isPrimitiveOrFunc(value) {
43
42
  return !value || typeof value !== "object" && typeof value !== "function";
44
43
  }
@@ -79,13 +78,13 @@ function assign(target, ...sources) {
79
78
  return target;
80
79
  }
81
80
  function funcWithTry(fns, args, context, configError) {
82
- const fnArray = isArray(fns) ? fns : [fns];
81
+ const fnArray = Array.isArray(fns) ? fns : [fns];
83
82
  let ret;
84
83
  for (const fn of fnArray) {
85
84
  try {
86
85
  ret = Function.prototype.apply.call(fn, context, args);
87
86
  } catch (e) {
88
- configError(e);
87
+ configError?.(e);
89
88
  }
90
89
  }
91
90
  return ret;
@@ -113,7 +112,7 @@ function translateData(data, value) {
113
112
  }
114
113
  return value;
115
114
  }
116
- if (isPlainObject(value) || isArray(value)) {
115
+ if (isPlainObject(value) || Array.isArray(value)) {
117
116
  for (const p in value) {
118
117
  if (has(value, p)) {
119
118
  const val = value[p];
@@ -208,11 +207,11 @@ function now() {
208
207
  }
209
208
  function classExtend(make, base, props, statics) {
210
209
  const baseProto = base["prototype"] ?? {};
211
- const cProto = Object.create(baseProto);
212
- assign(cProto, props);
210
+ const classProto = Object.create(baseProto);
211
+ assign(classProto, props);
213
212
  Object.assign(make, statics);
214
- cProto.constructor = make;
215
- make["prototype"] = cProto;
213
+ classProto.constructor = make;
214
+ make["prototype"] = classProto;
216
215
  }
217
216
 
218
217
  // src/apply-style.ts
@@ -253,8 +252,8 @@ function applyStyle(styleIdOrPairs, css) {
253
252
  }
254
253
 
255
254
  // src/mark.ts
256
- var DELETED_KEY = SPLITTER + "$a";
257
- var MARK_OBJECT_KEY = SPLITTER + "$b";
255
+ var DELETED_KEY = SPLITTER + "$delFlag";
256
+ var MARK_OBJECT_KEY = SPLITTER + "$markKey";
258
257
  function mark(host, key) {
259
258
  let sign = 0;
260
259
  const hostRecord = host;
@@ -278,7 +277,7 @@ function unmark(host) {
278
277
 
279
278
  // src/safeguard.ts
280
279
  var proxiesPool = /* @__PURE__ */ new Map();
281
- var SAFEGUARD_SENTINEL = "_sf_";
280
+ var SAFEGUARD_SENTINEL = "_safe_";
282
281
  function safeguard(data, getter, setter, isRoot) {
283
282
  if (typeof window.__lark_Debug === "undefined" || !window.__lark_Debug) {
284
283
  return data;
@@ -319,7 +318,7 @@ function safeguard(data, getter, setter, isRoot) {
319
318
  if (!prefix && getter) {
320
319
  getter(property);
321
320
  }
322
- if (!isRoot && has(target, property) && (isArray(out) || isPlainObject(out))) {
321
+ if (!isRoot && has(target, property) && (Array.isArray(out) || isPlainObject(out))) {
323
322
  return build(prefix + property + ".", out);
324
323
  }
325
324
  return out;
@@ -494,7 +493,10 @@ var EventEmitter = class {
494
493
  }
495
494
  } else {
496
495
  this.listeners.delete(key);
497
- Reflect.deleteProperty(this, `on${event}`);
496
+ Reflect.deleteProperty(
497
+ this,
498
+ `on${event[0].toUpperCase() + event.slice(1)}`
499
+ );
498
500
  }
499
501
  return this;
500
502
  }
@@ -535,7 +537,7 @@ var EventEmitter = class {
535
537
  }
536
538
  }
537
539
  }
538
- const onMethodName = `on${event}`;
540
+ const onMethodName = `on${event[0].toUpperCase() + event.slice(1)}`;
539
541
  const onMethod = this[onMethodName];
540
542
  if (typeof onMethod === "function") {
541
543
  funcWithTry(
@@ -726,6 +728,7 @@ var State = {
726
728
  emitter.fire(event, data, remove);
727
729
  return State;
728
730
  }
731
+ // onChanged: noop,
729
732
  };
730
733
 
731
734
  // src/router.ts
@@ -1031,9 +1034,9 @@ var Router = {
1031
1034
  window.addEventListener("hashchange", watchChange);
1032
1035
  window.addEventListener("popstate", watchChange);
1033
1036
  window.addEventListener("beforeunload", (domEvent) => {
1034
- const te = {};
1035
- Router.fire(ROUTER_EVENTS.PAGE_UNLOAD, te);
1036
- const msg = te["msg"];
1037
+ const data = {};
1038
+ Router.fire(ROUTER_EVENTS.PAGE_UNLOAD, data);
1039
+ const msg = data["msg"];
1037
1040
  if (msg) {
1038
1041
  domEvent.returnValue = msg;
1039
1042
  }
@@ -1065,16 +1068,16 @@ var frameGetter;
1065
1068
  function parseEventInfo(eventInfo) {
1066
1069
  const cached = eventInfoCache.get(eventInfo);
1067
1070
  if (cached) {
1068
- return assign({}, cached, { r: eventInfo });
1071
+ return assign({}, cached, { value: eventInfo });
1069
1072
  }
1070
1073
  const match = eventInfo.match(EVENT_METHOD_REGEXP) || [];
1071
1074
  const result = {
1072
- v: match[1] || "",
1073
- n: match[2] || "",
1074
- i: match[3] || ""
1075
+ id: match[1] || "",
1076
+ name: match[2] || "",
1077
+ params: match[3] || ""
1075
1078
  };
1076
1079
  eventInfoCache.set(eventInfo, result);
1077
- return assign({}, result, { r: eventInfo });
1080
+ return assign({}, result, { value: eventInfo });
1078
1081
  }
1079
1082
  function findFrameInfo(current, eventType) {
1080
1083
  const eventInfos = [];
@@ -1084,7 +1087,7 @@ function findFrameInfo(current, eventType) {
1084
1087
  if (info) {
1085
1088
  match = parseEventInfo(info);
1086
1089
  }
1087
- if (match && !match.v || selectorEvents[eventType]) {
1090
+ if (match && !match.id || selectorEvents[eventType]) {
1088
1091
  let selectorFrameId = "#";
1089
1092
  let backtrace = 0;
1090
1093
  while (begin && begin !== document.body) {
@@ -1110,10 +1113,10 @@ function findFrameInfo(current, eventType) {
1110
1113
  if (selectorEntry) {
1111
1114
  for (const selectorName of selectorEntry.selectors) {
1112
1115
  const entry = {
1113
- r: selectorName,
1114
- v: frameId,
1115
- n: selectorName,
1116
- i: ""
1116
+ value: selectorName,
1117
+ id: frameId,
1118
+ name: selectorName,
1119
+ params: ""
1117
1120
  };
1118
1121
  if (selectorName) {
1119
1122
  if (!backtrace && elementMatchesSelector(current, selectorName)) {
@@ -1125,8 +1128,8 @@ function findFrameInfo(current, eventType) {
1125
1128
  }
1126
1129
  }
1127
1130
  if (view.template && !backtrace) {
1128
- if (match && !match.v) {
1129
- match.v = frameId;
1131
+ if (match && !match.id) {
1132
+ match.id = frameId;
1130
1133
  }
1131
1134
  break;
1132
1135
  }
@@ -1142,10 +1145,10 @@ function findFrameInfo(current, eventType) {
1142
1145
  }
1143
1146
  if (match) {
1144
1147
  eventInfos.push({
1145
- v: match.v,
1146
- r: match.r,
1147
- n: match.n,
1148
- i: match.i
1148
+ id: match.id,
1149
+ value: match.value,
1150
+ name: match.name,
1151
+ params: match.params
1149
1152
  });
1150
1153
  }
1151
1154
  return eventInfos;
@@ -1166,7 +1169,7 @@ function domEventProcessor(domEvent) {
1166
1169
  const eventInfos = findFrameInfo(current, eventType);
1167
1170
  if (eventInfos.length) {
1168
1171
  for (const info of eventInfos) {
1169
- const { v: frameId, n: handlerName, i: params } = info;
1172
+ const { id: frameId, name: handlerName, params } = info;
1170
1173
  if (lastFrameId !== frameId) {
1171
1174
  if (lastFrameId && domEvent.isPropagationStopped?.()) {
1172
1175
  break;
@@ -1265,8 +1268,8 @@ var WrapMeta = {
1265
1268
  td: [3, "<table><tbody><tr>"],
1266
1269
  area: [1, "<map>"],
1267
1270
  param: [1, "<object>"],
1268
- g: [1, '<svg xmlns="' + SVG_NS + '">'],
1269
- m: [1, '<math xmlns="' + MATH_NS + '">'],
1271
+ svg: [1, '<svg xmlns="' + SVG_NS + '">'],
1272
+ math: [1, '<math xmlns="' + MATH_NS + '">'],
1270
1273
  _: [0, ""]
1271
1274
  };
1272
1275
  WrapMeta["optgroup"] = WrapMeta["option"];
@@ -1299,9 +1302,9 @@ function vdomGetNode(html, refNode) {
1299
1302
  const ns = refNode.namespaceURI;
1300
1303
  let tag;
1301
1304
  if (ns === SVG_NS) {
1302
- tag = "g";
1305
+ tag = "svg";
1303
1306
  } else if (ns === MATH_NS) {
1304
- tag = "m";
1307
+ tag = "math";
1305
1308
  } else {
1306
1309
  const match = TAG_NAME_REGEXP.exec(html);
1307
1310
  tag = match ? match[1] : "";
@@ -1746,221 +1749,6 @@ if (typeof window !== "undefined") {
1746
1749
  if (typeof document !== "undefined") {
1747
1750
  VIEW_GLOBALS["document"] = document;
1748
1751
  }
1749
- function viewPrepare(oView) {
1750
- if (oView.ctors) {
1751
- return oView.ctors;
1752
- }
1753
- const ctors = [];
1754
- oView.ctors = ctors;
1755
- const proto = oView.prototype;
1756
- const eventsObject = {};
1757
- const eventsList = [];
1758
- const selectorObject = {};
1759
- const mixins = proto["mixins"];
1760
- if (mixins && Array.isArray(mixins)) {
1761
- viewMergeMixins(mixins, oView, ctors);
1762
- }
1763
- for (const p in proto) {
1764
- if (!has(proto, p)) continue;
1765
- const currentFn = proto[p];
1766
- if (typeof currentFn !== "function") continue;
1767
- const matches = p.match(VIEW_EVENT_METHOD_REGEXP);
1768
- if (!matches) continue;
1769
- const isSelector = matches[1];
1770
- const selectorOrCallback = matches[2];
1771
- const events = matches[3];
1772
- const modifiers = matches[4];
1773
- const mod = {};
1774
- if (modifiers) {
1775
- for (const item of modifiers.split(",")) {
1776
- mod[item] = true;
1777
- }
1778
- }
1779
- const eventTypes = events.split(",");
1780
- for (const item of eventTypes) {
1781
- const globalNode = VIEW_GLOBALS[selectorOrCallback];
1782
- let mask = 1;
1783
- if (isSelector) {
1784
- if (globalNode) {
1785
- eventsList.push({
1786
- handler: currentFn,
1787
- element: globalNode,
1788
- eventName: item,
1789
- modifiers: mod
1790
- });
1791
- continue;
1792
- }
1793
- mask = 2;
1794
- let selectorEntry = selectorObject[item];
1795
- if (!selectorEntry) {
1796
- selectorEntry = selectorObject[item] = {
1797
- selectors: []
1798
- };
1799
- }
1800
- if (!selectorEntry[selectorOrCallback]) {
1801
- selectorEntry[selectorOrCallback] = 1;
1802
- selectorEntry.selectors.push(selectorOrCallback);
1803
- }
1804
- }
1805
- eventsObject[item] = (eventsObject[item] || 0) | mask;
1806
- const combinedKey = selectorOrCallback + SPLITTER + item;
1807
- const existingFn = proto[combinedKey];
1808
- if (!existingFn) {
1809
- proto[combinedKey] = currentFn;
1810
- } else {
1811
- const mixinFn = currentFn;
1812
- const existingMixin = existingFn;
1813
- if (existingMixin.b) {
1814
- if (mixinFn.b) {
1815
- proto[combinedKey] = processMixinsSameEvent(
1816
- currentFn,
1817
- existingFn
1818
- );
1819
- } else if (has(proto, p)) {
1820
- proto[combinedKey] = currentFn;
1821
- }
1822
- }
1823
- }
1824
- }
1825
- }
1826
- viewWrapMethod(proto, "render", "$b");
1827
- proto["$eo"] = eventsObject;
1828
- proto["$el"] = eventsList;
1829
- proto["$so"] = selectorObject;
1830
- proto["$f"] = proto["assign"];
1831
- return ctors;
1832
- }
1833
- function viewWrapMethod(proto, fnName, shortKey) {
1834
- const originalFn = proto[fnName];
1835
- if (typeof originalFn !== "function") return;
1836
- const wrapped = function(...args) {
1837
- if (this.signature > 0) {
1838
- this.signature++;
1839
- this.fire("rendercall");
1840
- destroyAllResources(this, false);
1841
- const instanceFn = typeof this[fnName] === "function" ? this[fnName] : originalFn;
1842
- const fnToCall = instanceFn === wrapped ? originalFn : instanceFn;
1843
- return funcWithTry(fnToCall, args, this, noop);
1844
- }
1845
- return void 0;
1846
- };
1847
- proto[fnName] = wrapped;
1848
- proto[shortKey] = wrapped;
1849
- }
1850
- function processMixinsSameEvent(additional, exist) {
1851
- let temp;
1852
- const existMixin = exist;
1853
- if (existMixin.a) {
1854
- temp = existMixin;
1855
- } else {
1856
- temp = function(...e) {
1857
- funcWithTry(temp.a ?? [], e, this, noop);
1858
- };
1859
- temp.a = [exist];
1860
- temp.b = 1;
1861
- }
1862
- const additionalMixin = additional;
1863
- temp.a = (temp.a ?? []).concat(additionalMixin.a ?? [additional]);
1864
- return temp;
1865
- }
1866
- function viewMergeMixins(mixins, viewClass, ctors) {
1867
- const proto = viewClass.prototype;
1868
- const temp = {};
1869
- for (const node of mixins) {
1870
- for (const p in node) {
1871
- if (!has(node, p)) continue;
1872
- const fn = node[p];
1873
- const exist = temp[p];
1874
- if (p === "make") {
1875
- ctors.push(fn);
1876
- continue;
1877
- }
1878
- if (VIEW_EVENT_METHOD_REGEXP.test(p)) {
1879
- if (exist) {
1880
- temp[p] = processMixinsSameEvent(fn, exist);
1881
- } else {
1882
- fn.b = 1;
1883
- temp[p] = fn;
1884
- }
1885
- } else {
1886
- if (!exist) {
1887
- temp[p] = fn;
1888
- }
1889
- }
1890
- }
1891
- }
1892
- for (const p in temp) {
1893
- if (!has(proto, p)) {
1894
- proto[p] = temp[p];
1895
- }
1896
- }
1897
- }
1898
- function viewDelegateEvents(view, destroy = false) {
1899
- const proto = Object.getPrototypeOf(view) ?? {};
1900
- const eventsObject = proto["$eo"] || view.eventObjectMap;
1901
- const selectorObject = proto["$so"] || view.eventSelectorMap;
1902
- const eventsList = proto["$el"] || view.globalEventList;
1903
- for (const e in eventsObject) {
1904
- if (has(eventsObject, e)) {
1905
- if (destroy) {
1906
- EventDelegator.unbind(e, !!selectorObject[e]);
1907
- } else {
1908
- EventDelegator.bind(e, !!selectorObject[e]);
1909
- }
1910
- }
1911
- }
1912
- for (const entry of eventsList) {
1913
- if (destroy) {
1914
- entry.element.removeEventListener(
1915
- entry.eventName,
1916
- entry.boundHandler
1917
- );
1918
- } else {
1919
- const handler = entry.handler;
1920
- const element = entry.element;
1921
- const modifiers = entry.modifiers;
1922
- entry.boundHandler = function(domEvent) {
1923
- const extendedEvent = domEvent;
1924
- extendedEvent.eventTarget = element;
1925
- if (modifiers) {
1926
- const kbEvent = domEvent;
1927
- if (modifiers["ctrl"] && !kbEvent.ctrlKey || modifiers["shift"] && !kbEvent.shiftKey || modifiers["alt"] && !kbEvent.altKey || modifiers["meta"] && !kbEvent.metaKey) {
1928
- return;
1929
- }
1930
- }
1931
- funcWithTry(handler, [domEvent], view, noop);
1932
- };
1933
- entry.element.addEventListener(
1934
- entry.eventName,
1935
- entry.boundHandler
1936
- );
1937
- }
1938
- }
1939
- }
1940
- function destroyAllResources(view, lastly) {
1941
- const cache = view.resources;
1942
- for (const p in cache) {
1943
- if (has(cache, p)) {
1944
- const entry = cache[p];
1945
- if (lastly || entry.destroyOnRender) {
1946
- destroyResource(cache, p, true);
1947
- }
1948
- }
1949
- }
1950
- }
1951
- function destroyResource(cache, key, callDestroy, oldEntity) {
1952
- const entry = cache[key];
1953
- if (!entry || entry.entity === oldEntity) return void 0;
1954
- const entity = entry.entity;
1955
- if (entity && typeof entity === "object") {
1956
- const destroyFn = entity["destroy"];
1957
- if (typeof destroyFn === "function" && callDestroy) {
1958
- funcWithTry(destroyFn, [], entity, noop);
1959
- }
1960
- }
1961
- Reflect.deleteProperty(cache, key);
1962
- return entity;
1963
- }
1964
1752
  var View = class _View {
1965
1753
  /** View ID (same as owner frame ID) */
1966
1754
  id = "";
@@ -1984,18 +1772,43 @@ var View = class _View {
1984
1772
  observedStateKeys;
1985
1773
  /** Resource map */
1986
1774
  resources = {};
1987
- /** Selector event map: eventType -> handler name list */
1988
- eventSelectorMap = {};
1989
- /** Event object map: eventType -> bitmask */
1990
- eventObjectMap = {};
1991
- /** Global event list */
1992
- globalEventList = [];
1993
1775
  /** Assign method reference */
1994
1776
  assignMethod;
1995
1777
  /** Whether endUpdate pending */
1996
1778
  endUpdatePending;
1997
1779
  /** Internal event storage */
1998
1780
  _events = new EventEmitter();
1781
+ // ============================================================
1782
+ // Getters for prototype-stored event maps
1783
+ // ============================================================
1784
+ /**
1785
+ * Event bitmask map: eventType -> bitmask (1=root, 2=selector).
1786
+ * Read from prototype ($evtObjMap) set by View.prepare.
1787
+ * Using a getter avoids ES6 class field shadowing the prototype value.
1788
+ */
1789
+ get eventObjectMap() {
1790
+ const proto = Object.getPrototypeOf(this);
1791
+ return proto["$evtObjMap"] || {};
1792
+ }
1793
+ /**
1794
+ * Selector event map: eventType -> selector list.
1795
+ * Read from prototype ($selMap) set by View.prepare.
1796
+ */
1797
+ get eventSelectorMap() {
1798
+ const proto = Object.getPrototypeOf(this);
1799
+ return proto["$selMap"] || {};
1800
+ }
1801
+ /**
1802
+ * Global event list: [{handler, element, eventName, modifiers}].
1803
+ * Read from prototype ($globalEvtList) set by View.prepare.
1804
+ */
1805
+ get globalEventList() {
1806
+ const proto = Object.getPrototypeOf(this);
1807
+ return proto["$globalEvtList"] || [];
1808
+ }
1809
+ // ============================================================
1810
+ // Instance lifecycle methods
1811
+ // ============================================================
1999
1812
  /**
2000
1813
  * Initialize view (called by Frame when mounting).
2001
1814
  */
@@ -2003,14 +1816,7 @@ var View = class _View {
2003
1816
  }
2004
1817
  /**
2005
1818
  * Render view template (called by Frame after init).
2006
- * Wrapped by View_WrapMethod to manage signature + resources.
2007
- *
2008
- * Default implementation calls updater.digest() which:
2009
- * 1. Executes the template function with current data
2010
- * 2. Runs VDOM diff against previous DOM
2011
- * 3. Applies DOM operations
2012
- * 4. Calls endUpdate to mount child frames
2013
- *
1819
+ * Wrapped by View.wrapMethod to manage signature + resources.
2014
1820
  */
2015
1821
  render() {
2016
1822
  this.updater.digest();
@@ -2062,7 +1868,7 @@ var View = class _View {
2062
1868
  if (!flag) {
2063
1869
  setTimeout(
2064
1870
  this.wrapAsync(() => {
2065
- runInvokes(ownerFrame);
1871
+ _View.runInvokes(ownerFrame);
2066
1872
  }),
2067
1873
  0
2068
1874
  );
@@ -2138,7 +1944,7 @@ var View = class _View {
2138
1944
  capture(key, resource, destroyOnRender = false) {
2139
1945
  const cache = this.resources;
2140
1946
  if (resource) {
2141
- destroyResource(cache, key, true, resource);
1947
+ _View.destroyResource(cache, key, true, resource);
2142
1948
  cache[key] = {
2143
1949
  entity: resource,
2144
1950
  destroyOnRender
@@ -2154,7 +1960,7 @@ var View = class _View {
2154
1960
  * If destroy=true, calls the resource's destroy() method.
2155
1961
  */
2156
1962
  release(key, destroy = true) {
2157
- return destroyResource(this.resources, key, destroy);
1963
+ return _View.destroyResource(this.resources, key, destroy);
2158
1964
  }
2159
1965
  // ============================================================
2160
1966
  // Leave tip
@@ -2200,10 +2006,273 @@ var View = class _View {
2200
2006
  });
2201
2007
  }
2202
2008
  // ============================================================
2203
- // Static: extend and merge
2009
+ // Static public methods
2204
2010
  // ============================================================
2205
2011
  /** Collected ctors from mixins */
2206
2012
  static ctors;
2013
+ /**
2014
+ * Prepare a View subclass by scanning its prototype for event method patterns.
2015
+ * Pattern: `$?name<eventType1,eventType2>(&modifiers)`
2016
+ *
2017
+ * Only runs once per View subclass (guarded by ctors marker).
2018
+ * Called from Frame.mountView before creating the view instance.
2019
+ */
2020
+ static prepare(oView) {
2021
+ if (oView.ctors) {
2022
+ return oView.ctors;
2023
+ }
2024
+ const ctors = [];
2025
+ oView.ctors = ctors;
2026
+ const proto = oView.prototype;
2027
+ const eventsObject = {};
2028
+ const eventsList = [];
2029
+ const selectorObject = {};
2030
+ const mixins = proto["mixins"];
2031
+ if (mixins && Array.isArray(mixins)) {
2032
+ _View.mergeMixins(mixins, oView, ctors);
2033
+ }
2034
+ for (const p in proto) {
2035
+ if (!has(proto, p)) continue;
2036
+ const currentFn = proto[p];
2037
+ if (typeof currentFn !== "function") continue;
2038
+ const matches = p.match(VIEW_EVENT_METHOD_REGEXP);
2039
+ if (!matches) continue;
2040
+ const isSelector = matches[1];
2041
+ const selectorOrCallback = matches[2];
2042
+ const events = matches[3];
2043
+ const modifiers = matches[4];
2044
+ const mod = {};
2045
+ if (modifiers) {
2046
+ for (const item of modifiers.split(",")) {
2047
+ mod[item] = true;
2048
+ }
2049
+ }
2050
+ const eventTypes = events.split(",");
2051
+ for (const item of eventTypes) {
2052
+ const globalNode = VIEW_GLOBALS[selectorOrCallback];
2053
+ let mask = 1;
2054
+ if (isSelector) {
2055
+ if (globalNode) {
2056
+ eventsList.push({
2057
+ handler: currentFn,
2058
+ element: globalNode,
2059
+ eventName: item,
2060
+ modifiers: mod
2061
+ });
2062
+ continue;
2063
+ }
2064
+ mask = 2;
2065
+ let selectorEntry = selectorObject[item];
2066
+ if (!selectorEntry) {
2067
+ selectorEntry = selectorObject[item] = {
2068
+ selectors: []
2069
+ };
2070
+ }
2071
+ if (!selectorEntry[selectorOrCallback]) {
2072
+ selectorEntry[selectorOrCallback] = 1;
2073
+ selectorEntry.selectors.push(selectorOrCallback);
2074
+ }
2075
+ }
2076
+ eventsObject[item] = (eventsObject[item] || 0) | mask;
2077
+ const combinedKey = selectorOrCallback + SPLITTER + item;
2078
+ const existingFn = proto[combinedKey];
2079
+ if (!existingFn) {
2080
+ proto[combinedKey] = currentFn;
2081
+ } else {
2082
+ const mixinFn = currentFn;
2083
+ const existingMixin = existingFn;
2084
+ if (existingMixin.marker) {
2085
+ if (mixinFn.marker) {
2086
+ proto[combinedKey] = _View.processMixinsSameEvent(
2087
+ currentFn,
2088
+ existingFn
2089
+ );
2090
+ } else if (has(proto, p)) {
2091
+ proto[combinedKey] = currentFn;
2092
+ }
2093
+ }
2094
+ }
2095
+ }
2096
+ }
2097
+ _View.wrapMethod(proto, "render", "$renderWrap");
2098
+ proto["$evtObjMap"] = eventsObject;
2099
+ proto["$globalEvtList"] = eventsList;
2100
+ proto["$selMap"] = selectorObject;
2101
+ proto["$assignFn"] = proto["assign"];
2102
+ return ctors;
2103
+ }
2104
+ /**
2105
+ * Bind or unbind event delegation for a view instance.
2106
+ * Called from Frame during mount/unmount.
2107
+ */
2108
+ static delegateEvents(view, destroy = false) {
2109
+ const eventsObject = view.eventObjectMap;
2110
+ const selectorObject = view.eventSelectorMap;
2111
+ const eventsList = view.globalEventList;
2112
+ for (const e in eventsObject) {
2113
+ if (has(eventsObject, e)) {
2114
+ if (destroy) {
2115
+ EventDelegator.unbind(e, !!selectorObject[e]);
2116
+ } else {
2117
+ EventDelegator.bind(e, !!selectorObject[e]);
2118
+ }
2119
+ }
2120
+ }
2121
+ for (const entry of eventsList) {
2122
+ if (destroy) {
2123
+ entry.element.removeEventListener(
2124
+ entry.eventName,
2125
+ entry.boundHandler
2126
+ );
2127
+ } else {
2128
+ const handler = entry.handler;
2129
+ const element = entry.element;
2130
+ const modifiers = entry.modifiers;
2131
+ entry.boundHandler = function(domEvent) {
2132
+ const extendedEvent = domEvent;
2133
+ extendedEvent.eventTarget = element;
2134
+ if (modifiers) {
2135
+ const kbEvent = domEvent;
2136
+ if (modifiers["ctrl"] && !kbEvent.ctrlKey || modifiers["shift"] && !kbEvent.shiftKey || modifiers["alt"] && !kbEvent.altKey || modifiers["meta"] && !kbEvent.metaKey) {
2137
+ return;
2138
+ }
2139
+ }
2140
+ funcWithTry(handler, [domEvent], view, noop);
2141
+ };
2142
+ entry.element.addEventListener(
2143
+ entry.eventName,
2144
+ entry.boundHandler
2145
+ );
2146
+ }
2147
+ }
2148
+ }
2149
+ /**
2150
+ * Destroy all resources managed by a view.
2151
+ * If lastly=true, destroy ALL resources; otherwise only destroyOnRender ones.
2152
+ */
2153
+ static destroyAllResources(view, lastly) {
2154
+ const cache = view.resources;
2155
+ for (const p in cache) {
2156
+ if (has(cache, p)) {
2157
+ const entry = cache[p];
2158
+ if (lastly || entry.destroyOnRender) {
2159
+ _View.destroyResource(cache, p, true);
2160
+ }
2161
+ }
2162
+ }
2163
+ }
2164
+ /**
2165
+ * Process deferred invoke calls on a frame.
2166
+ */
2167
+ static runInvokes(frame) {
2168
+ const list = frame.invokeList;
2169
+ if (!list) return;
2170
+ while (list.length) {
2171
+ const entry = list.shift();
2172
+ if (entry && !entry.removed) {
2173
+ frame.invoke(entry.name, entry.args);
2174
+ }
2175
+ }
2176
+ }
2177
+ // ============================================================
2178
+ // Static private methods
2179
+ // ============================================================
2180
+ /**
2181
+ * Wrap a method on the prototype to add signature checking and resource cleanup.
2182
+ */
2183
+ static wrapMethod(proto, fnName, shortKey) {
2184
+ const originalFn = proto[fnName];
2185
+ if (typeof originalFn !== "function") return;
2186
+ const wrapped = function(...args) {
2187
+ if (this.signature > 0) {
2188
+ this.signature++;
2189
+ this.fire("render");
2190
+ _View.destroyAllResources(this, false);
2191
+ const instanceFn = typeof this[fnName] === "function" ? this[fnName] : originalFn;
2192
+ const fnToCall = instanceFn === wrapped ? originalFn : instanceFn;
2193
+ return funcWithTry(fnToCall, args, this, noop);
2194
+ }
2195
+ return void 0;
2196
+ };
2197
+ proto[fnName] = wrapped;
2198
+ proto[shortKey] = wrapped;
2199
+ }
2200
+ /**
2201
+ * When two mixins define the same event method, merge them into
2202
+ * a single function that calls both in sequence.
2203
+ */
2204
+ static processMixinsSameEvent(additional, exist) {
2205
+ let temp;
2206
+ const existMixin = exist;
2207
+ if (existMixin.handlerList) {
2208
+ temp = existMixin;
2209
+ } else {
2210
+ temp = function(...e) {
2211
+ funcWithTry(temp.handlerList ?? [], e, this, noop);
2212
+ };
2213
+ temp.handlerList = [exist];
2214
+ temp.marker = 1;
2215
+ }
2216
+ const additionalMixin = additional;
2217
+ temp.handlerList = (temp.handlerList ?? []).concat(
2218
+ additionalMixin.handlerList ?? [additional]
2219
+ );
2220
+ return temp;
2221
+ }
2222
+ /**
2223
+ * Merge an array of mixin objects into the view prototype.
2224
+ */
2225
+ static mergeMixins(mixins, viewClass, ctors) {
2226
+ const proto = viewClass.prototype;
2227
+ const temp = {};
2228
+ for (const node of mixins) {
2229
+ for (const p in node) {
2230
+ if (!has(node, p)) continue;
2231
+ const fn = node[p];
2232
+ const exist = temp[p];
2233
+ if (p === "make") {
2234
+ ctors.push(fn);
2235
+ continue;
2236
+ }
2237
+ if (VIEW_EVENT_METHOD_REGEXP.test(p)) {
2238
+ if (exist) {
2239
+ temp[p] = _View.processMixinsSameEvent(fn, exist);
2240
+ } else {
2241
+ fn.marker = 1;
2242
+ temp[p] = fn;
2243
+ }
2244
+ } else {
2245
+ if (!exist) {
2246
+ temp[p] = fn;
2247
+ }
2248
+ }
2249
+ }
2250
+ }
2251
+ for (const p in temp) {
2252
+ if (!has(proto, p)) {
2253
+ proto[p] = temp[p];
2254
+ }
2255
+ }
2256
+ }
2257
+ /**
2258
+ * Destroy a single resource entry.
2259
+ */
2260
+ static destroyResource(cache, key, callDestroy, oldEntity) {
2261
+ const entry = cache[key];
2262
+ if (!entry || entry.entity === oldEntity) return void 0;
2263
+ const entity = entry.entity;
2264
+ if (entity && typeof entity === "object") {
2265
+ const destroyFn = entity["destroy"];
2266
+ if (typeof destroyFn === "function" && callDestroy) {
2267
+ funcWithTry(destroyFn, [], entity, noop);
2268
+ }
2269
+ }
2270
+ Reflect.deleteProperty(cache, key);
2271
+ return entity;
2272
+ }
2273
+ // ============================================================
2274
+ // Static: extend and merge
2275
+ // ============================================================
2207
2276
  /**
2208
2277
  * Extend View to create a new View subclass.
2209
2278
  *
@@ -2224,7 +2293,7 @@ var View = class _View {
2224
2293
  constructor(nodeId, ownerFrame, initParams, node, mixinCtors) {
2225
2294
  super(nodeId, ownerFrame, initParams, node, []);
2226
2295
  for (const key in props) {
2227
- if (has(props, key) && key !== "make") {
2296
+ if (has(props, key) && key !== "make" && key !== "render") {
2228
2297
  this[key] = props[key];
2229
2298
  }
2230
2299
  }
@@ -2257,36 +2326,17 @@ var View = class _View {
2257
2326
  }
2258
2327
  }
2259
2328
  }
2260
- ChildView.merge = viewMerge;
2261
- ChildView.extend = _View.extend;
2262
2329
  return ChildView;
2263
2330
  }
2264
2331
  /**
2265
2332
  * Merge mixins into View prototype.
2266
2333
  */
2267
2334
  static merge(...mixins) {
2268
- const self = this;
2269
- const existingCtors = self.ctors || [];
2270
- viewMergeMixins(mixins, self, existingCtors);
2271
- return self;
2335
+ const existingCtors = this.ctors || [];
2336
+ _View.mergeMixins(mixins, this, existingCtors);
2337
+ return this;
2272
2338
  }
2273
2339
  };
2274
- function viewMerge(...mixins) {
2275
- const self = this;
2276
- const existingCtors = self.ctors || [];
2277
- viewMergeMixins(mixins, self, existingCtors);
2278
- return self;
2279
- }
2280
- function runInvokes(frame) {
2281
- const list = frame.invokeList;
2282
- if (!list) return;
2283
- while (list.length) {
2284
- const entry = list.shift();
2285
- if (entry && !entry.removed) {
2286
- frame.invoke(entry.name, entry.args);
2287
- }
2288
- }
2289
- }
2290
2340
 
2291
2341
  // src/frame.ts
2292
2342
  var frameRegistry = /* @__PURE__ */ new Map();
@@ -2394,7 +2444,7 @@ var Frame = class _Frame extends EventEmitter {
2394
2444
  */
2395
2445
  doMountView(ViewClass, params, node, sign) {
2396
2446
  if (sign !== this.signature) return;
2397
- const mixinCtors = viewPrepare(ViewClass);
2447
+ const mixinCtors = View.prepare(ViewClass);
2398
2448
  const ViewConstructor = ViewClass;
2399
2449
  const view = new ViewConstructor(
2400
2450
  this.id,
@@ -2405,7 +2455,7 @@ var Frame = class _Frame extends EventEmitter {
2405
2455
  );
2406
2456
  this.viewInstance = view;
2407
2457
  view.signature = 1;
2408
- viewDelegateEvents(view);
2458
+ View.delegateEvents(view);
2409
2459
  const initResult = funcWithTry(
2410
2460
  view.init,
2411
2461
  [params, { node, deep: !view.template }],
@@ -2416,13 +2466,10 @@ var Frame = class _Frame extends EventEmitter {
2416
2466
  Promise.resolve(initResult).then(() => {
2417
2467
  if (nextSign !== this.signature) return;
2418
2468
  if (view.template) {
2419
- const renderFn = view.$b;
2420
- if (renderFn) {
2421
- renderFn.call(view);
2422
- }
2469
+ view.render();
2423
2470
  } else {
2424
2471
  this.hasAltered = 0;
2425
- if (!view.$e) {
2472
+ if (!view.endUpdatePendingFlag) {
2426
2473
  view.endUpdate();
2427
2474
  }
2428
2475
  }
@@ -2609,7 +2656,7 @@ var Frame = class _Frame extends EventEmitter {
2609
2656
  /** Get or create root frame */
2610
2657
  static root(rootId) {
2611
2658
  if (!rootFrame) {
2612
- rootId = rootId || "lark-root";
2659
+ rootId = rootId || "root";
2613
2660
  let rootElement = document.getElementById(rootId);
2614
2661
  if (!rootElement) {
2615
2662
  rootElement = document.body;
@@ -2730,19 +2777,19 @@ function registerViewClass(viewPath, ViewClass) {
2730
2777
  }
2731
2778
 
2732
2779
  // src/service.ts
2733
- var Bag = class {
2734
- /** Bag data */
2780
+ var Payload = class {
2781
+ /** Payload data */
2735
2782
  data;
2736
2783
  /** Internal cache info */
2737
2784
  cacheInfo;
2738
2785
  constructor(data = {}) {
2739
2786
  this.data = data;
2740
2787
  }
2741
- /** Get a value from bag data */
2788
+ /** Get a value from payload data */
2742
2789
  get(key) {
2743
2790
  return this.data[key];
2744
2791
  }
2745
- /** Set a value in bag data */
2792
+ /** Set a value in payload data */
2746
2793
  set(keyOrData, value) {
2747
2794
  if (typeof keyOrData === "string") {
2748
2795
  this.data[keyOrData] = value;
@@ -2754,287 +2801,366 @@ var Bag = class {
2754
2801
  };
2755
2802
  var FETCH_FLAGS_ALL = 1;
2756
2803
  var FETCH_FLAGS_ONE = 2;
2757
- function createServiceType(syncFn, cacheMax = 20, cacheBuffer = 5) {
2758
- const metas = {};
2759
- const bagCache = new Cache({
2760
- maxSize: cacheMax,
2761
- bufferSize: cacheBuffer
2762
- });
2763
- const pendingCacheKeys = {};
2764
- const staticEmitter2 = new EventEmitter();
2765
- const serviceType = {
2766
- add(attrs) {
2767
- if (!isArray(attrs)) {
2768
- attrs = [attrs];
2769
- }
2770
- for (const bag of attrs) {
2771
- if (bag) {
2772
- const name = bag.name;
2773
- const cache = bag.cache;
2774
- bag.cache = cache ? cache | 0 : 0;
2775
- metas[name] = bag;
2776
- }
2777
- }
2778
- },
2779
- meta(attrs) {
2780
- const name = typeof attrs === "string" ? attrs : attrs["name"];
2781
- return metas[name] || attrs;
2782
- },
2783
- create(attrs) {
2784
- const meta = serviceType.meta(attrs);
2785
- const cache = attrs["cache"] | 0 || meta.cache || 0;
2786
- const entity = new Bag();
2787
- entity.set(meta);
2788
- entity.cacheInfo = {
2789
- name: meta.name,
2790
- after: meta.after,
2791
- cleans: meta.cleans,
2792
- key: cache ? defaultCacheKey(meta, attrs) : "",
2793
- time: 0
2794
- };
2795
- if (typeof attrs === "object" && attrs !== null) {
2796
- entity.set(attrs);
2797
- }
2798
- const before = meta.before;
2799
- if (before) {
2800
- funcWithTry(before, [entity], entity, noop);
2801
- }
2802
- staticEmitter2.fire("begin", { bag: entity });
2803
- return entity;
2804
- },
2805
- get(attrs, createNew) {
2806
- let entity;
2807
- let needsUpdate = false;
2808
- if (!createNew) {
2809
- entity = serviceType.cached(attrs);
2810
- }
2811
- if (!entity) {
2812
- entity = serviceType.create(attrs);
2813
- needsUpdate = true;
2814
- }
2815
- return { entity, needsUpdate };
2816
- },
2817
- cached(attrs) {
2818
- const meta = serviceType.meta(attrs);
2819
- const cache = attrs["cache"] | 0 || meta.cache || 0;
2820
- let cacheKey = "";
2821
- if (cache) {
2822
- cacheKey = defaultCacheKey(meta, attrs);
2823
- }
2824
- if (cacheKey) {
2825
- const info = pendingCacheKeys[cacheKey];
2826
- if (info) {
2827
- return info.e;
2828
- }
2829
- const cached = bagCache.get(cacheKey);
2830
- if (cached && cached.cacheInfo) {
2831
- if (now() - cached.cacheInfo.time > cache) {
2832
- bagCache.del(cacheKey);
2833
- return void 0;
2804
+ var Service = class {
2805
+ /** Service instance ID */
2806
+ id = "";
2807
+ /** Whether service is busy (1 = busy) */
2808
+ busy = 0;
2809
+ /** Whether service is destroyed (1 = destroyed) */
2810
+ destroyed = 0;
2811
+ /** Task queue for sequential operations */
2812
+ taskQueue = [];
2813
+ /** Previous dequeue arguments */
2814
+ prevArgs = [];
2815
+ /** Instance event emitter */
2816
+ _emitter = new EventEmitter();
2817
+ constructor() {
2818
+ this.id = generateId("service");
2819
+ }
2820
+ // ============================================================
2821
+ // Instance accessors for type-level data
2822
+ // ============================================================
2823
+ /** Instance event emitter (public accessor) */
2824
+ get emitter() {
2825
+ return this._emitter;
2826
+ }
2827
+ /**
2828
+ * Get internals object for serviceSend compatibility.
2829
+ * References per-type static state from the current class.
2830
+ */
2831
+ get internals() {
2832
+ const ctor = this.constructor;
2833
+ return {
2834
+ metaList: ctor._metaList,
2835
+ payloadCache: ctor._payloadCache,
2836
+ pendingCacheKeys: ctor._pendingCacheKeys,
2837
+ syncFn: ctor._syncFn,
2838
+ staticEmitter: ctor._staticEmitter
2839
+ };
2840
+ }
2841
+ /**
2842
+ * Get type reference (the constructor) for serviceSend compatibility.
2843
+ * Static methods like get/create are accessible via the constructor.
2844
+ */
2845
+ get type() {
2846
+ return this.constructor;
2847
+ }
2848
+ // ============================================================
2849
+ // Instance methods
2850
+ // ============================================================
2851
+ /**
2852
+ * Fetch all endpoints, callback when all complete.
2853
+ * Uses cache when available.
2854
+ */
2855
+ all(attrs, done) {
2856
+ serviceSend(this, attrs, done, FETCH_FLAGS_ALL, false);
2857
+ return this;
2858
+ }
2859
+ /**
2860
+ * Fetch all endpoints, callback on each completion.
2861
+ */
2862
+ one(attrs, done) {
2863
+ serviceSend(this, attrs, done, FETCH_FLAGS_ONE, false);
2864
+ return this;
2865
+ }
2866
+ /**
2867
+ * Fetch all endpoints, skip cache (always request).
2868
+ */
2869
+ save(attrs, done) {
2870
+ serviceSend(this, attrs, done, FETCH_FLAGS_ALL, true);
2871
+ return this;
2872
+ }
2873
+ /**
2874
+ * Enqueue a task for sequential execution.
2875
+ */
2876
+ enqueue(callback) {
2877
+ if (!this.destroyed) {
2878
+ this.taskQueue.push(callback);
2879
+ this.dequeue(...this.prevArgs);
2880
+ }
2881
+ return this;
2882
+ }
2883
+ /**
2884
+ * Dequeue and execute the next task in queue.
2885
+ */
2886
+ dequeue(...args) {
2887
+ if (!this.busy && !this.destroyed) {
2888
+ this.busy = 1;
2889
+ setTimeout(() => {
2890
+ this.busy = 0;
2891
+ if (!this.destroyed) {
2892
+ const task2 = this.taskQueue.shift();
2893
+ if (task2) {
2894
+ this.prevArgs = args;
2895
+ funcWithTry(task2, args, this, noop);
2834
2896
  }
2835
- return cached;
2836
2897
  }
2898
+ }, 0);
2899
+ }
2900
+ }
2901
+ /**
2902
+ * Destroy the service instance.
2903
+ * After destruction, no new requests can be sent.
2904
+ */
2905
+ destroy() {
2906
+ this.destroyed = 1;
2907
+ this.taskQueue = [];
2908
+ }
2909
+ // Instance event methods (delegate to instance emitter)
2910
+ on(event, handler) {
2911
+ this._emitter.on(event, handler);
2912
+ return this;
2913
+ }
2914
+ off(event, handler) {
2915
+ this._emitter.off(event, handler);
2916
+ return this;
2917
+ }
2918
+ fire(event, data) {
2919
+ this._emitter.fire(event, data);
2920
+ return this;
2921
+ }
2922
+ // ============================================================
2923
+ // Per-type static state
2924
+ // ============================================================
2925
+ /** Per-type metadata registry */
2926
+ static _metaList = {};
2927
+ /** Per-type payload cache (LFU with frequency eviction) */
2928
+ static _payloadCache = new Cache({
2929
+ maxSize: 20,
2930
+ bufferSize: 5
2931
+ });
2932
+ /** Per-type pending cache keys for deduplication */
2933
+ static _pendingCacheKeys = {};
2934
+ /** Per-type sync function */
2935
+ static _syncFn = noop;
2936
+ /** Per-type static event emitter */
2937
+ static _staticEmitter = new EventEmitter();
2938
+ /** Per-type cache max size */
2939
+ static _cacheMax = 20;
2940
+ /** Per-type cache buffer size */
2941
+ static _cacheBuffer = 5;
2942
+ // ============================================================
2943
+ // Static methods (operate on per-type state via `this`)
2944
+ // ============================================================
2945
+ /**
2946
+ * Register API endpoint metadata.
2947
+ */
2948
+ static add(attrs) {
2949
+ if (!Array.isArray(attrs)) {
2950
+ attrs = [attrs];
2951
+ }
2952
+ for (const payload of attrs) {
2953
+ if (payload) {
2954
+ const name = payload.name;
2955
+ const cache = payload.cache;
2956
+ payload.cache = cache ? cache | 0 : 0;
2957
+ this._metaList[name] = payload;
2837
2958
  }
2838
- return void 0;
2839
- },
2840
- clear(names) {
2841
- const nameList = (typeof names === "string" ? names : names.join(",")).split(",");
2842
- const nameSet = {};
2843
- for (const n of nameList) {
2844
- nameSet[n] = 1;
2959
+ }
2960
+ }
2961
+ /**
2962
+ * Get metadata for an API endpoint.
2963
+ */
2964
+ static meta(attrs) {
2965
+ const name = typeof attrs === "string" ? attrs : attrs["name"];
2966
+ return this._metaList[name] || attrs;
2967
+ }
2968
+ /**
2969
+ * Create a Payload for an API request.
2970
+ */
2971
+ static create(attrs) {
2972
+ const meta = this.meta(attrs);
2973
+ const cache = attrs["cache"] | 0 || meta.cache || 0;
2974
+ const entity = new Payload();
2975
+ entity.set(meta);
2976
+ entity.cacheInfo = {
2977
+ name: meta.name,
2978
+ after: meta.after,
2979
+ cleans: meta.cleanKeys,
2980
+ key: cache ? defaultCacheKey(meta, attrs) : "",
2981
+ time: 0
2982
+ };
2983
+ if (typeof attrs === "object" && attrs !== null) {
2984
+ entity.set(attrs);
2985
+ }
2986
+ const before = meta.before;
2987
+ if (before) {
2988
+ funcWithTry(before, [entity], entity, noop);
2989
+ }
2990
+ this._staticEmitter.fire("begin", { payload: entity });
2991
+ return entity;
2992
+ }
2993
+ /**
2994
+ * Get or create a Payload for an API request.
2995
+ */
2996
+ static get(attrs, createNew) {
2997
+ let entity;
2998
+ let needsUpdate = false;
2999
+ if (!createNew) {
3000
+ entity = this.cached(attrs);
3001
+ }
3002
+ if (!entity) {
3003
+ entity = this.create(attrs);
3004
+ needsUpdate = true;
3005
+ }
3006
+ return { entity, needsUpdate };
3007
+ }
3008
+ /**
3009
+ * Get cached Payload if available and not expired.
3010
+ */
3011
+ static cached(attrs) {
3012
+ const meta = this.meta(attrs);
3013
+ const cache = attrs["cache"] | 0 || meta.cache || 0;
3014
+ let cacheKey = "";
3015
+ if (cache) {
3016
+ cacheKey = defaultCacheKey(meta, attrs);
3017
+ }
3018
+ if (cacheKey) {
3019
+ const info = this._pendingCacheKeys[cacheKey];
3020
+ if (info) {
3021
+ return info.entity;
2845
3022
  }
2846
- const keysToDelete = [];
2847
- bagCache.forEach((bag) => {
2848
- if (bag?.cacheInfo && nameSet[bag.cacheInfo.name]) {
2849
- if (bag.cacheInfo.key) {
2850
- keysToDelete.push(bag.cacheInfo.key);
2851
- }
3023
+ const cached = this._payloadCache.get(cacheKey);
3024
+ if (cached && cached.cacheInfo) {
3025
+ if (now() - cached.cacheInfo.time > cache) {
3026
+ this._payloadCache.del(cacheKey);
3027
+ return void 0;
2852
3028
  }
2853
- });
2854
- for (const key of keysToDelete) {
2855
- bagCache.del(key);
3029
+ return cached;
2856
3030
  }
2857
- },
2858
- on(event, handler) {
2859
- staticEmitter2.on(event, handler);
2860
- },
2861
- off(event, handler) {
2862
- staticEmitter2.off(event, handler);
2863
- },
2864
- fire(event, data) {
2865
- staticEmitter2.fire(event, data);
2866
- },
2867
- extend(newSyncFn, newCacheMax, newCacheBuffer) {
2868
- return createServiceType(
2869
- newSyncFn,
2870
- newCacheMax || cacheMax,
2871
- newCacheBuffer || cacheBuffer
2872
- );
2873
3031
  }
2874
- };
2875
- const internals = {
2876
- metas,
2877
- bagCache,
2878
- pendingCacheKeys,
2879
- syncFn,
2880
- staticEmitter: staticEmitter2
2881
- };
2882
- function ServiceInstance() {
2883
- this.id = generateId("s");
2884
- this["$e"] = 0;
2885
- this["$d"] = 0;
2886
- this["$g"] = [];
2887
- this["$h"] = [];
2888
- this._emitter = new EventEmitter();
2889
- this._internals = internals;
2890
- this._type = serviceType;
2891
- }
2892
- ServiceInstance.prototype = {
2893
- all(attrs, done) {
2894
- serviceSend(this, attrs, done, FETCH_FLAGS_ALL, false);
2895
- return this;
2896
- },
2897
- one(attrs, done) {
2898
- serviceSend(this, attrs, done, FETCH_FLAGS_ONE, false);
2899
- return this;
2900
- },
2901
- save(attrs, done) {
2902
- serviceSend(this, attrs, done, FETCH_FLAGS_ALL, true);
2903
- return this;
2904
- },
2905
- enqueue(callback) {
2906
- if (!this["$d"]) {
2907
- this["$g"].push(callback);
2908
- this.dequeue(...this["$h"]);
2909
- }
2910
- return this;
2911
- },
2912
- dequeue(...args) {
2913
- if (!this["$e"] && !this["$d"]) {
2914
- this["$e"] = 1;
2915
- setTimeout(() => {
2916
- this["$e"] = 0;
2917
- if (!this["$d"]) {
2918
- const task2 = this["$g"].shift();
2919
- if (task2) {
2920
- this["$h"] = args;
2921
- funcWithTry(task2, args, this, noop);
2922
- }
2923
- }
2924
- }, 0);
3032
+ return void 0;
3033
+ }
3034
+ /**
3035
+ * Clear cached payloads by endpoint name.
3036
+ */
3037
+ static clear(names) {
3038
+ const nameList = (typeof names === "string" ? names : names.join(",")).split(",");
3039
+ const nameSet = {};
3040
+ for (const n of nameList) {
3041
+ nameSet[n] = 1;
3042
+ }
3043
+ const keysToDelete = [];
3044
+ this._payloadCache.forEach((payload) => {
3045
+ if (payload?.cacheInfo && nameSet[payload.cacheInfo.name]) {
3046
+ if (payload.cacheInfo.key) {
3047
+ keysToDelete.push(payload.cacheInfo.key);
3048
+ }
2925
3049
  }
2926
- },
2927
- destroy() {
2928
- this["$d"] = 1;
2929
- this["$g"] = [];
2930
- },
2931
- on(event, handler) {
2932
- this._emitter.on(event, handler);
2933
- return this;
2934
- },
2935
- off(event, handler) {
2936
- this._emitter.off(event, handler);
2937
- return this;
2938
- },
2939
- fire(event, data) {
2940
- this._emitter.fire(event, data);
2941
- return this;
3050
+ });
3051
+ for (const key of keysToDelete) {
3052
+ this._payloadCache.del(key);
2942
3053
  }
2943
- };
2944
- const staticsMap = {
2945
- add: serviceType.add,
2946
- meta: serviceType.meta,
2947
- create: serviceType.create,
2948
- get: serviceType.get,
2949
- cached: serviceType.cached,
2950
- clear: serviceType.clear,
2951
- on: serviceType.on,
2952
- off: serviceType.off,
2953
- fire: serviceType.fire,
2954
- extend: serviceType.extend,
2955
- _internals: internals
2956
- };
2957
- const constructor = Object.assign(
2958
- ServiceInstance,
2959
- staticsMap
2960
- );
2961
- return constructor;
2962
- }
2963
- var Service = createServiceType(noop);
3054
+ }
3055
+ // Static event methods (operate on per-type emitter)
3056
+ static on(event, handler) {
3057
+ this._staticEmitter.on(event, handler);
3058
+ }
3059
+ static off(event, handler) {
3060
+ this._staticEmitter.off(event, handler);
3061
+ }
3062
+ static fire(event, data) {
3063
+ this._staticEmitter.fire(event, data);
3064
+ }
3065
+ /**
3066
+ * Create a new Service subclass with a custom sync function.
3067
+ * Each subclass gets its own per-type state (metaList, cache, etc.)
3068
+ * to ensure isolation between different Service types.
3069
+ */
3070
+ static extend(newSyncFn, newCacheMax, newCacheBuffer) {
3071
+ const ParentService = this;
3072
+ class ChildService extends ParentService {
3073
+ static _metaList = {};
3074
+ static _payloadCache = new Cache({
3075
+ maxSize: newCacheMax || ParentService._cacheMax,
3076
+ bufferSize: newCacheBuffer || ParentService._cacheBuffer
3077
+ });
3078
+ static _pendingCacheKeys = {};
3079
+ static _syncFn = newSyncFn;
3080
+ static _staticEmitter = new EventEmitter();
3081
+ static _cacheMax = newCacheMax || ParentService._cacheMax;
3082
+ static _cacheBuffer = newCacheBuffer || ParentService._cacheBuffer;
3083
+ }
3084
+ return ChildService;
3085
+ }
3086
+ };
2964
3087
  function defaultCacheKey(meta, attrs) {
2965
3088
  return JSON.stringify(attrs) + SPLITTER + JSON.stringify(meta);
2966
3089
  }
2967
3090
  function serviceSend(service, attrs, done, flag, save) {
2968
- if (service["$d"]) return;
2969
- if (service["$e"]) {
3091
+ if (service["destroyed"]) return;
3092
+ if (service["busy"]) {
2970
3093
  service.enqueue(
2971
3094
  serviceSend.bind(null, service, attrs, done, flag, save)
2972
3095
  );
2973
3096
  return;
2974
3097
  }
2975
- service["$e"] = 1;
3098
+ service["busy"] = 1;
2976
3099
  let attrList;
2977
3100
  if (typeof attrs === "string") {
2978
3101
  attrList = [{ name: attrs }];
2979
- } else if (isArray(attrs)) {
3102
+ } else if (Array.isArray(attrs)) {
2980
3103
  attrList = attrs;
2981
3104
  } else {
2982
3105
  attrList = [attrs];
2983
3106
  }
2984
- const internals = service._internals;
3107
+ const internals = service.internals;
2985
3108
  const { syncFn, pendingCacheKeys, staticEmitter: staticEmitter2 } = internals;
2986
3109
  let requestCount = 0;
2987
3110
  const total = attrList.length;
2988
3111
  const doneArr = new Array(total + 1);
2989
3112
  const errorArgs = [];
2990
3113
  const remoteComplete = (idx, error) => {
2991
- const bag = doneArr[idx + 1];
2992
- let newBag = false;
3114
+ const payload = doneArr[idx + 1];
3115
+ let newPayload = false;
2993
3116
  if (error) {
2994
3117
  errorArgs[idx] = error;
2995
- staticEmitter2.fire("fail", { bag, error });
3118
+ staticEmitter2.fire("fail", { payload, error });
2996
3119
  } else {
2997
- newBag = true;
2998
- staticEmitter2.fire("done", { bag });
3120
+ newPayload = true;
3121
+ staticEmitter2.fire("done", { payload });
2999
3122
  }
3000
- if (!service["$d"]) {
3123
+ if (!service["destroyed"]) {
3001
3124
  const finish = requestCount === total;
3002
3125
  if (finish) {
3003
- service["$e"] = 0;
3126
+ service["busy"] = 0;
3004
3127
  if (flag === FETCH_FLAGS_ALL) {
3005
3128
  doneArr[0] = errorArgs;
3006
3129
  funcWithTry(done, doneArr, service, noop);
3007
3130
  }
3008
3131
  }
3009
3132
  if (flag === FETCH_FLAGS_ONE) {
3010
- funcWithTry(done, [error || null, bag, finish, idx], service, noop);
3133
+ funcWithTry(done, [error || null, payload, finish, idx], service, noop);
3011
3134
  }
3012
3135
  }
3013
- if (newBag) {
3014
- staticEmitter2.fire("end", { bag, error });
3136
+ if (newPayload) {
3137
+ staticEmitter2.fire("end", { payload, error });
3015
3138
  }
3016
3139
  };
3017
3140
  for (const attr of attrList) {
3018
3141
  if (!attr) continue;
3019
3142
  const attrObj = typeof attr === "string" ? { name: attr } : attr;
3020
- const bagInfo = service._type.get(attrObj, save);
3021
- const bagEntity = bagInfo.entity;
3022
- const cacheKey = bagEntity.cacheInfo?.key || "";
3143
+ const payloadInfo = service.type.get(
3144
+ attrObj,
3145
+ save
3146
+ );
3147
+ const payloadEntity = payloadInfo.entity;
3148
+ const cacheKey = payloadEntity.cacheInfo?.key || "";
3023
3149
  const complete = remoteComplete.bind(null, requestCount++);
3024
3150
  if (cacheKey && pendingCacheKeys[cacheKey]) {
3025
3151
  pendingCacheKeys[cacheKey].push(complete);
3026
- } else if (bagInfo.needsUpdate) {
3152
+ } else if (payloadInfo.needsUpdate) {
3027
3153
  if (cacheKey) {
3028
3154
  const cacheList = [complete];
3029
- cacheList.e = bagEntity;
3155
+ cacheList.entity = payloadEntity;
3030
3156
  pendingCacheKeys[cacheKey] = cacheList;
3031
3157
  const cacheComplete = () => {
3032
3158
  const list = pendingCacheKeys[cacheKey];
3033
- const entity = list.e;
3159
+ const entity = list.entity;
3034
3160
  if (entity.cacheInfo) {
3035
3161
  entity.cacheInfo.time = now();
3036
3162
  }
3037
- internals.bagCache.set(cacheKey, entity);
3163
+ internals.payloadCache.set(cacheKey, entity);
3038
3164
  Reflect.deleteProperty(pendingCacheKeys, cacheKey);
3039
3165
  for (const cb of list) {
3040
3166
  if (typeof cb === "function") {
@@ -3042,9 +3168,9 @@ function serviceSend(service, attrs, done, flag, save) {
3042
3168
  }
3043
3169
  }
3044
3170
  };
3045
- syncFn(bagEntity, cacheComplete);
3171
+ syncFn(payloadEntity, cacheComplete);
3046
3172
  } else {
3047
- syncFn(bagEntity, complete);
3173
+ syncFn(payloadEntity, complete);
3048
3174
  }
3049
3175
  } else {
3050
3176
  complete();
@@ -3052,12 +3178,12 @@ function serviceSend(service, attrs, done, flag, save) {
3052
3178
  }
3053
3179
  }
3054
3180
 
3055
- // src/frame-visualizer/index.ts
3056
- var MSG_PING = "LARK_VIS_PING";
3057
- var MSG_PONG = "LARK_VIS_PONG";
3058
- var MSG_REQUEST_TREE = "LARK_VIS_REQUEST_TREE";
3059
- var MSG_TREE = "LARK_VIS_TREE";
3060
- var MSG_TREE_DELTA = "LARK_VIS_TREE_DELTA";
3181
+ // src/frame-visualizer.ts
3182
+ var MSG_PING = "LARK_VISUALIZER_PING";
3183
+ var MSG_PONG = "LARK_VISUALIZER_PONG";
3184
+ var MSG_REQUEST_TREE = "LARK_VISUALIZER_REQUEST_TREE";
3185
+ var MSG_TREE = "LARK_VISUALIZER_TREE";
3186
+ var MSG_TREE_DELTA = "LARK_VISUALIZER_TREE_DELTA";
3061
3187
  function serializeView(view) {
3062
3188
  return {
3063
3189
  id: view.id,
@@ -3162,7 +3288,7 @@ function pushTreeUpdate() {
3162
3288
 
3163
3289
  // src/framework.ts
3164
3290
  var config = {
3165
- rootId: "lark-root",
3291
+ rootId: "root",
3166
3292
  hashbang: "#!",
3167
3293
  error: (error) => {
3168
3294
  throw error;
@@ -3255,7 +3381,12 @@ function dispatcherUpdate(frame, stateKeys) {
3255
3381
  const isChanged = stateKeys ? stateIsObserveChanged(view, stateKeys) : viewIsObserveChanged(view);
3256
3382
  let renderPromise;
3257
3383
  if (isChanged) {
3258
- const renderResult = funcWithTry(view.$b ?? view.render, [], view, noop);
3384
+ const renderResult = funcWithTry(
3385
+ view.renderMethod ?? view.render,
3386
+ [],
3387
+ view,
3388
+ noop
3389
+ );
3259
3390
  if (renderResult && typeof renderResult.then === "function") {
3260
3391
  renderPromise = renderResult;
3261
3392
  }
@@ -3277,13 +3408,13 @@ function dispatcherUpdate(frame, stateKeys) {
3277
3408
  }
3278
3409
  function dispatcherNotifyChange(e) {
3279
3410
  const rootFrame2 = Frame.root();
3280
- const view = e["view"];
3411
+ const view = e.view;
3281
3412
  if (view) {
3282
3413
  const viewPath = typeof view === "object" && view !== null ? String(view.to || "") : String(view);
3283
3414
  rootFrame2.mountView(viewPath);
3284
3415
  } else {
3285
3416
  dispatcherUpdateTag++;
3286
- dispatcherUpdate(rootFrame2, e["keys"]);
3417
+ dispatcherUpdate(rootFrame2, e.keys);
3287
3418
  }
3288
3419
  }
3289
3420
  function dispatchEvent(target, eventType, eventInit) {
@@ -3294,8 +3425,6 @@ function dispatchEvent(target, eventType, eventInit) {
3294
3425
  });
3295
3426
  target.dispatchEvent(event);
3296
3427
  }
3297
- var Base = class extends EventEmitter {
3298
- };
3299
3428
  function use(names, callback) {
3300
3429
  if (!config.require) {
3301
3430
  if (callback) callback();
@@ -3310,7 +3439,7 @@ function use(names, callback) {
3310
3439
  }
3311
3440
  }
3312
3441
  var WAIT_OK = 1;
3313
- var WAIT_TIMEOUT_OR_UNFOUND = 0;
3442
+ var WAIT_TIMEOUT_OR_NOT_FOUND = 0;
3314
3443
  function waitZoneViewsRendered(viewId, timeout) {
3315
3444
  if (timeout == null) {
3316
3445
  timeout = 30 * 1e3;
@@ -3321,7 +3450,7 @@ function waitZoneViewsRendered(viewId, timeout) {
3321
3450
  const check = () => {
3322
3451
  const currentTime = now();
3323
3452
  if (currentTime > endTime || !checkFrame) {
3324
- resolve(WAIT_TIMEOUT_OR_UNFOUND);
3453
+ resolve(WAIT_TIMEOUT_OR_NOT_FOUND);
3325
3454
  } else if (checkFrame.childrenCount === checkFrame.readyCount) {
3326
3455
  resolve(WAIT_OK);
3327
3456
  } else {
@@ -3358,10 +3487,10 @@ var Framework = {
3358
3487
  Router._setConfig(config);
3359
3488
  EventDelegator.setFrameGetter((id) => Frame.get(id));
3360
3489
  Router.on(ROUTER_EVENTS.CHANGED, (data) => {
3361
- dispatcherNotifyChange(data);
3490
+ if (data) dispatcherNotifyChange(data);
3362
3491
  });
3363
3492
  State.on(ROUTER_EVENTS.CHANGED, (data) => {
3364
- dispatcherNotifyChange(data);
3493
+ if (data) dispatcherNotifyChange(data);
3365
3494
  });
3366
3495
  booted3 = true;
3367
3496
  markBooted();
@@ -3398,7 +3527,7 @@ var Framework = {
3398
3527
  /** Wait for zone views to be rendered */
3399
3528
  waitZoneViewsRendered,
3400
3529
  WAIT_OK,
3401
- WAIT_TIMEOUT_OR_UNFOUND,
3530
+ WAIT_TIMEOUT_OR_NOT_FOUND,
3402
3531
  /**
3403
3532
  * Convert array to hash map.
3404
3533
  */
@@ -3463,7 +3592,7 @@ var Framework = {
3463
3592
  /**
3464
3593
  * Base class with EventEmitter.
3465
3594
  */
3466
- Base,
3595
+ Base: EventEmitter,
3467
3596
  // ============================================================
3468
3597
  // Module access
3469
3598
  // ============================================================
@@ -3481,10 +3610,11 @@ if (typeof window !== "undefined") {
3481
3610
  window.__lark_State = State;
3482
3611
  window.__lark_Router = Router;
3483
3612
  window.__lark_Frame = Frame;
3613
+ window.__lark_View = View;
3484
3614
  }
3485
3615
 
3486
3616
  // src/store.ts
3487
- var LARK_GLOBAL = "lark_global";
3617
+ var LARK_GLOBAL = "lark-global";
3488
3618
  var Platform = /* @__PURE__ */ ((Platform2) => {
3489
3619
  Platform2["Lark"] = "lark";
3490
3620
  Platform2["React"] = "react";
@@ -3527,8 +3657,10 @@ var Queue = class {
3527
3657
  const flushTickTask = () => {
3528
3658
  while (queue.length > 0) {
3529
3659
  const task2 = queue.shift();
3530
- pendingTasks.delete(task2);
3531
- runTask(task2);
3660
+ if (task2) {
3661
+ pendingTasks.delete(task2);
3662
+ runTask(task2);
3663
+ }
3532
3664
  }
3533
3665
  };
3534
3666
  Promise.resolve().then(flushTickTask);
@@ -3627,9 +3759,11 @@ var setStateConfig = (target, config2) => {
3627
3759
  if (target && isObject(config2)) StateConfigMap.set(target, config2);
3628
3760
  };
3629
3761
  var getStateConfig = (target, key) => {
3630
- if (!StateConfigMap.has(target)) return void 0;
3762
+ if (!StateConfigMap.has(target)) {
3763
+ return void 0;
3764
+ }
3631
3765
  const config2 = StateConfigMap.get(target);
3632
- return key ? config2[key] : config2;
3766
+ return key ? config2?.[key] : config2;
3633
3767
  };
3634
3768
  var isState = (target) => isObject(target) && StateConfigMap.has(target);
3635
3769
  var createLinkKeys = (target, property) => {
@@ -4196,14 +4330,11 @@ function defineStore(name, creator, config2) {
4196
4330
  const useStore = adapter.useStore;
4197
4331
  const store = new StoreClass(name, config2);
4198
4332
  store[_storeCreate](
4199
- creator(
4200
- store[_innerStore](),
4201
- extendApis
4202
- )
4333
+ creator(store[_innerStore](), extendApis)
4203
4334
  );
4204
4335
  Object.defineProperties(useStore, {
4205
4336
  $storeName: { value: name, configurable: true },
4206
- $del: { value: () => store[_storeDestroy](), configurable: true }
4337
+ $destroyFn: { value: () => store[_storeDestroy](), configurable: true }
4207
4338
  });
4208
4339
  if (!StoreCache.has(name)) {
4209
4340
  StoreCache.set(name, { store, creator, config: config2, useStore });
@@ -4268,7 +4399,7 @@ function observeCell(state, cb, immediate = true) {
4268
4399
  }
4269
4400
  function multi(useStore) {
4270
4401
  const storeName = useStore["$storeName"];
4271
- const flagSym = `lark_comp_${storeName}`;
4402
+ const flagSym = `lark-comp-${storeName}`;
4272
4403
  const map = /* @__PURE__ */ new Map();
4273
4404
  let rootViewPath;
4274
4405
  const getFlag = (viewContext) => {
@@ -4277,7 +4408,7 @@ function multi(useStore) {
4277
4408
  const viewId = owner?.["id"] || "";
4278
4409
  let flag;
4279
4410
  if (viewPath === rootViewPath) {
4280
- flag = `${flagSym}_${viewId}`;
4411
+ flag = `${flagSym}-${viewId}`;
4281
4412
  } else {
4282
4413
  flag = owner?.["viewInitParams"]?.[flagSym];
4283
4414
  }
@@ -4294,14 +4425,13 @@ function multi(useStore) {
4294
4425
  };
4295
4426
  const useFn = ((view) => {
4296
4427
  if (!view)
4297
- throw new Error("[lark-store] multi: cannot find the view instance");
4428
+ throw new Error(
4429
+ "[@lark.js/mvc error] multi: cannot find the view instance"
4430
+ );
4298
4431
  const viewCtx = view;
4299
4432
  const flag = viewCtx[flagSym];
4300
4433
  if (map.has(flag)) return map.get(flag);
4301
- const newFn = cloneStore(
4302
- flag,
4303
- useStore
4304
- );
4434
+ const newFn = cloneStore(flag, useStore);
4305
4435
  map.set(flag, newFn);
4306
4436
  return useFn(view);
4307
4437
  });
@@ -4439,7 +4569,7 @@ function convertArtSyntax(source, debug) {
4439
4569
  }
4440
4570
  if (blockStack.length > 0) {
4441
4571
  const unclosed = blockStack.map((b) => `"${b.ctrl}" at line ${b.line}`).join(", ");
4442
- throw new Error(`[@lark/mvc error] unclosed block(s): ${unclosed}`);
4572
+ throw new Error(`[@lark.js/mvc error] unclosed block(s): ${unclosed}`);
4443
4573
  }
4444
4574
  return result.join("");
4445
4575
  }
@@ -4533,12 +4663,12 @@ function convertArtExpression(code, debug, lineNo, blockStack = []) {
4533
4663
  }
4534
4664
  return `${debugPrefix}<%}else{%>`;
4535
4665
  }
4536
- case "each": {
4537
- blockStack.push({ ctrl: "each", line: lineNo });
4666
+ case "forOf": {
4667
+ blockStack.push({ ctrl: "forOf", line: lineNo });
4538
4668
  const object = tokens[0];
4539
4669
  if (tokens.length > 1 && tokens[1] !== "as") {
4540
4670
  throw new Error(
4541
- `[@lark/mvc error] bad each syntax: {{${code}}}. Expected "as" keyword, got "${tokens[1]}". Usage: {{each list as item [index]}}`
4671
+ `[@lark.js/mvc error] bad forOf syntax: {{${code}}}. Expected "as" keyword, got "${tokens[1]}". Usage: {{forOf list as item [index]}}`
4542
4672
  );
4543
4673
  }
4544
4674
  const restTokens = tokens.slice(2);
@@ -4560,12 +4690,12 @@ function convertArtExpression(code, debug, lineNo, blockStack = []) {
4560
4690
  }
4561
4691
  return `${debugPrefix}<%for(let ${index}=0${refExpr},${refObjCount}=${refObj}.length${lastCount};${index}<${refObjCount};${index}++){${firstAndLast}${valueDecl}%>`;
4562
4692
  }
4563
- case "parse": {
4564
- blockStack.push({ ctrl: "parse", line: lineNo });
4693
+ case "forIn": {
4694
+ blockStack.push({ ctrl: "forIn", line: lineNo });
4565
4695
  const object = tokens[0];
4566
4696
  if (tokens.length > 1 && tokens[1] !== "as") {
4567
4697
  throw new Error(
4568
- `[@lark/mvc error] bad parse syntax: {{${code}}}. Expected "as" keyword, got "${tokens[1]}". Usage: {{for-in obj as val [key]}}`
4698
+ `[@lark.js/mvc error] bad forIn syntax: {{${code}}}. Expected "as" keyword, got "${tokens[1]}". Usage: {{for-in obj as val [key]}}`
4569
4699
  );
4570
4700
  }
4571
4701
  const restTokens2 = tokens.slice(2);
@@ -4585,19 +4715,19 @@ function convertArtExpression(code, debug, lineNo, blockStack = []) {
4585
4715
  case "set":
4586
4716
  return `${debugPrefix}<%let ${tokens.join(" ")};%>`;
4587
4717
  case "/if":
4588
- case "/each":
4589
- case "/parse":
4718
+ case "/forOf":
4719
+ case "/forIn":
4590
4720
  case "/for": {
4591
4721
  const expectedCtrl = keyword.substring(1);
4592
4722
  const last = blockStack.pop();
4593
4723
  if (!last) {
4594
4724
  throw new Error(
4595
- `[@lark/mvc error] unexpected {{${code}}}: no matching open block`
4725
+ `[@lark.js/mvc error] unexpected {{${code}}}: no matching open block`
4596
4726
  );
4597
4727
  }
4598
4728
  if (last.ctrl !== expectedCtrl) {
4599
4729
  throw new Error(
4600
- `[@lark/mvc error] unexpected {{${code}}}: expected {{/${last.ctrl}}} to close block opened at line ${last.line}`
4730
+ `[@lark.js/mvc error] unexpected {{${code}}}: expected {{/${last.ctrl}}} to close block opened at line ${last.line}`
4601
4731
  );
4602
4732
  }
4603
4733
  return `${debugPrefix}<%}%>`;
@@ -4661,7 +4791,7 @@ function parseAsExpr(expr) {
4661
4791
  function compileToFunction(source, debug, file) {
4662
4792
  const matcher = /<%([@=!:])?([\s\S]*?)%>|$/g;
4663
4793
  let index = 0;
4664
- let funcSource = `$p+='`;
4794
+ let funcSource = `$out+='`;
4665
4795
  let hasAtRule = false;
4666
4796
  const escapeSlashRegExp = /\\|'/g;
4667
4797
  const escapeBreakReturnRegExp = /\r|\n/g;
@@ -4687,17 +4817,17 @@ function compileToFunction(source, debug, file) {
4687
4817
  }
4688
4818
  if (operate === "@") {
4689
4819
  hasAtRule = true;
4690
- funcSource += `'+($expr='<%${operate + expr}%>',$i($$ref,${content}))+'`;
4820
+ funcSource += `'+($dbgExpr='<%${operate + expr}%>',$refFn($refAlt,${content}))+'`;
4691
4821
  } else if (operate === "=" || operate === ":") {
4692
- funcSource += `'+($expr='<%${operate + expr}%>',$e(${content}))+'`;
4822
+ funcSource += `'+($dbgExpr='<%${operate + expr}%>',$encHtml(${content}))+'`;
4693
4823
  } else if (operate === "!") {
4694
- if (!content.startsWith("$eu(") || !content.endsWith(")")) {
4695
- content = `$n(${content})`;
4824
+ if (!content.startsWith("$encUri(") || !content.endsWith(")")) {
4825
+ content = `$strSafe(${content})`;
4696
4826
  }
4697
- funcSource += `'+($expr='<%${operate + expr}%>',${content})+'`;
4827
+ funcSource += `'+($dbgExpr='<%${operate + expr}%>',${content})+'`;
4698
4828
  } else if (content) {
4699
4829
  if (line > -1) {
4700
- funcSource += `';$line=${line};$art='${art}';`;
4830
+ funcSource += `';$dbgLine=${line};$dbgArt='${art}';`;
4701
4831
  content = "";
4702
4832
  } else {
4703
4833
  funcSource += `';`;
@@ -4706,19 +4836,19 @@ function compileToFunction(source, debug, file) {
4706
4836
  funcSource = funcSource.substring(0, funcSource.length - 4) + ";";
4707
4837
  }
4708
4838
  if (expr) {
4709
- funcSource += `$expr='<%${expr}%>';`;
4839
+ funcSource += `$dbgExpr='<%${expr}%>';`;
4710
4840
  }
4711
- funcSource += content + `;$p+='`;
4841
+ funcSource += content + `;$out+='`;
4712
4842
  }
4713
4843
  } else {
4714
4844
  if (operate === "@") {
4715
4845
  hasAtRule = true;
4716
- funcSource += `'+$i($$ref,${content})+'`;
4846
+ funcSource += `'+$refFn($refAlt,${content})+'`;
4717
4847
  } else if (operate === "=" || operate === ":") {
4718
- funcSource += `'+$e(${content})+'`;
4848
+ funcSource += `'+$encHtml(${content})+'`;
4719
4849
  } else if (operate === "!") {
4720
- if (!content.startsWith("$eu(") || !content.endsWith(")")) {
4721
- content = `$n(${content})`;
4850
+ if (!content.startsWith("$encUri(") || !content.endsWith(")")) {
4851
+ content = `$strSafe(${content})`;
4722
4852
  }
4723
4853
  funcSource += `'+${content}+'`;
4724
4854
  } else if (content) {
@@ -4726,28 +4856,28 @@ function compileToFunction(source, debug, file) {
4726
4856
  if (funcSource.endsWith(`+'';`)) {
4727
4857
  funcSource = funcSource.substring(0, funcSource.length - 4) + ";";
4728
4858
  }
4729
- funcSource += `${content};$p+='`;
4859
+ funcSource += `${content};$out+='`;
4730
4860
  }
4731
4861
  }
4732
4862
  return match;
4733
4863
  });
4734
4864
  funcSource += `';`;
4735
- funcSource = funcSource.replace(/\$p\+='';/g, "");
4736
- funcSource = funcSource.replace(/\$p\+=''\+/g, "$p+=");
4865
+ funcSource = funcSource.replace(/\$out\+='';/g, "");
4866
+ funcSource = funcSource.replace(/\$out\+=''\+/g, "$out+=");
4737
4867
  if (debug) {
4738
4868
  const filePart = file ? `\\r\\n\\tat file:${file}` : "";
4739
- 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;}`;
4869
+ funcSource = `let $dbgExpr,$dbgArt,$dbgLine;try{${funcSource}}catch(ex){let msg='render view error:'+(ex.message||ex);if($dbgArt)msg+='\\r\\n\\tsrc art:{{'+$dbgArt+'}}\\r\\n\\tat line:'+$dbgLine;msg+='\\r\\n\\t'+($dbgArt?'translate to:':'expr:');msg+=$dbgExpr+'${filePart}';throw msg;}`;
4740
4870
  }
4741
4871
  const viewIdRegExp = new RegExp(String.fromCharCode(31), "g");
4742
4872
  funcSource = funcSource.replace(viewIdRegExp, `'+$viewId+'`);
4743
- 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;}}` : "";
4744
- 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)}`;
4745
- 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)}`;
4746
- const encodeQuote = `if(!$eq){let $qr=/['"\\\\]/g;$eq=v=>$n(v).replace($qr,'\\\\$&')}`;
4747
- const refFallback = "if(!$$ref)$$ref=$$;";
4873
+ const atRule = hasAtRule ? `if(!$refFn){$refFn=(ref,v,k,f)=>{for(f=ref[$splitter];--f;)if(ref[k=$splitter+f]===v)return k;ref[k=$splitter+ref[$splitter]++]=v;return k;}}` : "";
4874
+ const encode = `if(!$strSafe){let $entMap={'&':'amp','<':'lt','>':'gt','"':'#34','\\'':'#39','\`':'#96'},$entReg=/[&<>"'\`]/g,$entFn=m=>'&'+$entMap[m]+';';$strSafe=v=>''+(v==null?'':v);$encHtml=v=>$strSafe(v).replace($entReg,$entFn)}`;
4875
+ const encodeURIMore = `if(!$encUri){let $uriMap={'!':'%21','\\'':'%27','(':'%28',')':'%29','*':'%2A'},$uriFn=m=>$uriMap[m],$uriReg=/[!')(*]/g;$encUri=v=>encodeURIComponent($strSafe(v)).replace($uriReg,$uriFn)}`;
4876
+ const encodeQuote = `if(!$encQuote){let $qReg=/['"\\\\]/g;$encQuote=v=>$strSafe(v).replace($qReg,'\\\\$&')}`;
4877
+ const refFallback = "if(!$refAlt)$refAlt=$data;";
4748
4878
  const fns = `${refFallback}${encode}${encodeURIMore}${encodeQuote}${atRule};`;
4749
- const fullSource = `${fns}let $g='\\x1e',$_temp,$p=''{{VARS}};${funcSource}return $p`;
4750
- return `($$,$viewId,$$ref,$e,$n,$eu,$i,$eq)=>{${fullSource}}`;
4879
+ const fullSource = `${fns}let $splitter='\\x1e',$tmp,$out=''{{VARS}};${funcSource}return $out`;
4880
+ return `($data,$viewId,$refAlt,$encHtml,$strSafe,$encUri,$refFn,$encQuote)=>{${fullSource}}`;
4751
4881
  }
4752
4882
  function compileTemplate(source, options = {}) {
4753
4883
  const { debug = false, globalVars = [], file } = options;
@@ -4756,17 +4886,17 @@ function compileTemplate(source, options = {}) {
4756
4886
  const viewEventProcessed = processViewEvents(converted);
4757
4887
  const finalSource = restoreComments(viewEventProcessed, comments);
4758
4888
  const funcBody = compileToFunction(finalSource, debug, file);
4759
- const varDeclarations = globalVars.map((key) => `,${key}=$$.${key}`).join("");
4889
+ const varDeclarations = globalVars.map((key) => `,${key}=$data.${key}`).join("");
4760
4890
  const funcWithVars = funcBody.replace("{{VARS}}", () => varDeclarations);
4761
4891
  return `export default function(data, selfId, refData) {
4762
- let $$ = data || {},
4892
+ let $data = data || {},
4763
4893
  $viewId = selfId || '';
4764
- return (${funcWithVars})($$, $viewId, refData,
4765
- /* $e */ v => String(v == null ? '' : v).replace(/[&<>"'\`]/g, m => '&' + ({'&':'amp','<':'lt','>':'gt','"':'#34',"'":'#39','\`':'#96'})[m] + ';'),
4766
- /* $n */ v => String(v == null ? '' : v),
4767
- /* $eu */ null,
4768
- /* $i */ null,
4769
- /* $eq */ null
4894
+ return (${funcWithVars})($data, $viewId, refData,
4895
+ /* $encHtml */ v => String(v == null ? '' : v).replace(/[&<>"'\`]/g, m => '&' + ({'&':'amp','<':'lt','>':'gt','"':'#34',"'":'#39','\`':'#96'})[m] + ';'),
4896
+ /* $strSafe */ v => String(v == null ? '' : v),
4897
+ /* $encUri */ null,
4898
+ /* $refFn */ null,
4899
+ /* $encQuote */ null
4770
4900
  );
4771
4901
  }`;
4772
4902
  }
@@ -4875,7 +5005,7 @@ function fallbackExtractVariables(source) {
4875
5005
  while ((m = outputRegExp.exec(source)) !== null) {
4876
5006
  vars.add(m[1]);
4877
5007
  }
4878
- const eachRegExp = /\{\{each\s+([a-zA-Z_$][\w$]*)\s+as/g;
5008
+ const eachRegExp = /\{\{forOf\s+([a-zA-Z_$][\w$]*)\s+as/g;
4879
5009
  while ((m = eachRegExp.exec(source)) !== null) {
4880
5010
  vars.add(m[1]);
4881
5011
  }
@@ -4921,52 +5051,64 @@ var BUILTIN_GLOBALS = {
4921
5051
  //
4922
5052
  // These variables appear in the generated template function signature
4923
5053
  // or body. They must be excluded from extractGlobalVars() so that
4924
- // they are not mistaken for user data variables and destructured from $$.
5054
+ // they are not mistaken for user data variables and destructured from $data.
4925
5055
  // SPLITTER character constant (same as \x1e), used as namespace separator
4926
5056
  // for refData keys, event attribute encoding, and internal data structures.
4927
- // Declared as: let $g='\x1e'
4928
- $g: 1,
4929
- // refData — the data object passed from Updater to the template function.
4930
- // User variables are destructured from $$ at the top of the function:
4931
- // let {name, age} = $$;
5057
+ // Declared as: let $splitter='\x1e'
5058
+ $splitter: 1,
5059
+ // Data — the data object passed from Updater to the template function.
5060
+ // User variables are destructured from $data at the top of the function:
5061
+ // let {name, age} = $data;
4932
5062
  // This is the first parameter of the generated arrow function.
4933
- $$: 1,
5063
+ $data: 1,
4934
5064
  // Null-safe toString: v => '' + (v == null ? '' : v)
4935
5065
  // Converts null/undefined to empty string, otherwise calls toString().
4936
5066
  // Wraps every {{!raw}} output to prevent "null" / "undefined" rendering.
4937
- $n: 1,
4938
- // HTML entity encoder: v => $n(v).replace(/[&<>"'`]/g, entityMap)
5067
+ $strSafe: 1,
5068
+ // HTML entity encoder: v => $strSafe(v).replace(/[&<>"'`]/g, entityMap)
4939
5069
  // Encodes &, <, >, ", ', ` to HTML entities (&amp; &lt; etc.)
4940
5070
  // Applied to all {{=escaped}} and {{:binding}} outputs.
4941
- $e: 1,
4942
- // HTML entity map — internal object used by $e:
5071
+ $encHtml: 1,
5072
+ // HTML entity map — internal object used by $encHtml:
4943
5073
  // {'&':'amp','<':'gt','>':'gt','"':'#34','\'':'#39','`':'#96'}
4944
- // Not a standalone function; referenced inside $e's closure.
4945
- $em: 1,
4946
- // HTML entity RegExp — internal regexp used by $e:
5074
+ // Not a standalone function; referenced inside $encHtml's closure.
5075
+ $entMap: 1,
5076
+ // HTML entity RegExp — internal regexp used by $encHtml:
4947
5077
  // /[&<>"'`]/g
4948
- $er: 1,
4949
- // HTML entity replacer function — internal helper used by $e:
4950
- // m => '&' + $em[m] + ';'
4951
- // Maps each matched character to its entity string.
4952
- $ef: 1,
5078
+ $entReg: 1,
5079
+ // HTML entity replacer function — internal helper used by $encHtml:
5080
+ // m => '&' + $entMap[m] + ';'
5081
+ // Maps matched character to its entity string.
5082
+ $entFn: 1,
4953
5083
  // Output buffer — the string accumulator for rendered HTML.
4954
- // All template output is appended via $p += '...'.
4955
- // Declared as: let $p = ''
4956
- $p: 1,
5084
+ // All template output is appended via $out += '...'.
5085
+ // Declared as: let $out = ''
5086
+ $out: 1,
4957
5087
  // Reference lookup: (refData, value) => key
4958
5088
  // Finds or allocates a SPLITTER-prefixed key in refData for a given
4959
5089
  // object reference. Used by {{@ref}} operator for passing object
4960
5090
  // references to child views via v-lark attributes.
4961
- $i: 1,
4962
- // URI encoder: v => encodeURIComponent($n(v)).replace(/[!')(*]/g, extraMap)
5091
+ $refFn: 1,
5092
+ // URI encoder: v => encodeURIComponent($strSafe(v)).replace(/[!')(*]/g, extraMap)
4963
5093
  // Extends encodeURIComponent with encoding of ! ' ( ) *.
4964
5094
  // Applied to values in @event URL parameters and {{!uri}} contexts.
4965
- $eu: 1,
4966
- // Quote encoder: v => $n(v).replace(/['"\\]/g, '\\$&')
5095
+ $encUri: 1,
5096
+ // URI encode map internal object used by $encUri:
5097
+ // {'!':'%21','\'':'%27','(':'%28',')':'%29','*':'%2A'}
5098
+ $uriMap: 1,
5099
+ // URI encode replacer — internal helper used by $encUri:
5100
+ // m => $uriMap[m]
5101
+ $uriFn: 1,
5102
+ // URI encode regexp — internal regexp used by $encUri:
5103
+ // /[!')(*]/g
5104
+ $uriReg: 1,
5105
+ // Quote encoder: v => $strSafe(v).replace(/['"\\]/g, '\\$&')
4967
5106
  // Escapes quotes and backslashes for safe embedding in HTML attribute
4968
5107
  // values (e.g. data-json='...').
4969
- $eq: 1,
5108
+ $encQuote: 1,
5109
+ // Quote encode regexp — internal regexp used by $encQuote:
5110
+ // /['"\\]/g
5111
+ $qReg: 1,
4970
5112
  // View ID — the unique identifier of the owning View instance.
4971
5113
  // Injected into @event attribute values at render time so that
4972
5114
  // EventDelegator can dispatch events to the correct View handler.
@@ -4974,23 +5116,23 @@ var BUILTIN_GLOBALS = {
4974
5116
  $viewId: 1,
4975
5117
  // Debug: current expression text — stores the template expression being
4976
5118
  // evaluated, for error reporting. Only present in debug mode.
4977
- // e.g. $expr='<%=user.name%>'
4978
- $expr: 1,
5119
+ // e.g. $dbgExpr='<%=user.name%>'
5120
+ $dbgExpr: 1,
4979
5121
  // Debug: original art syntax — stores the {{}} template syntax before
4980
5122
  // conversion, for error reporting. Only present in debug mode.
4981
- // e.g. $art='{{=user.name}}'
4982
- $art: 1,
5123
+ // e.g. $dbgArt='{{=user.name}}'
5124
+ $dbgArt: 1,
4983
5125
  // Debug: source line number — tracks the current line in the template
4984
5126
  // source, for error reporting. Only present in debug mode.
4985
- $line: 1,
4986
- // refData alias — fallback reference lookup table.
4987
- // Defaults to $$ when no explicit $$ref is provided.
4988
- // Ensures $i() does not crash when @ operator is used without refData.
4989
- $$ref: 1,
5127
+ $dbgLine: 1,
5128
+ // RefData alias — fallback reference lookup table.
5129
+ // Defaults to $data when no explicit $refAlt is provided.
5130
+ // Ensures $refFn() does not crash when @ operator is used without refData.
5131
+ $refAlt: 1,
4990
5132
  // Temporary variable — used by the compiler for intermediate
4991
5133
  // expression results in generated code (e.g. loop variables,
4992
- // conditional branches). Declared as: let $_temp
4993
- $_temp: 1,
5134
+ // conditional branches). Declared as: let $tmp
5135
+ $tmp: 1,
4994
5136
  // JS literals
4995
5137
  undefined: 1,
4996
5138
  null: 1,
@@ -5055,7 +5197,6 @@ var BUILTIN_GLOBALS = {
5055
5197
  };
5056
5198
  var BUILTIN_GLOBAL_SET = new Set(Object.keys(BUILTIN_GLOBALS));
5057
5199
  export {
5058
- Bag,
5059
5200
  CALL_BREAK_TIME,
5060
5201
  Cache,
5061
5202
  EVENT_METHOD_REGEXP,
@@ -5064,6 +5205,7 @@ export {
5064
5205
  Frame,
5065
5206
  Framework,
5066
5207
  LARK_VIEW,
5208
+ Payload,
5067
5209
  Platform,
5068
5210
  ROUTER_EVENTS,
5069
5211
  Router,
@@ -5102,7 +5244,6 @@ export {
5102
5244
  getUseStore,
5103
5245
  has,
5104
5246
  installFrameVisualizerBridge,
5105
- isArray,
5106
5247
  isPlainObject,
5107
5248
  isPrimitive,
5108
5249
  isPrimitiveOrFunc,