@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.cjs CHANGED
@@ -20,7 +20,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
- Bag: () => Bag,
24
23
  CALL_BREAK_TIME: () => CALL_BREAK_TIME,
25
24
  Cache: () => Cache,
26
25
  EVENT_METHOD_REGEXP: () => EVENT_METHOD_REGEXP,
@@ -29,6 +28,7 @@ __export(index_exports, {
29
28
  Frame: () => Frame,
30
29
  Framework: () => Framework,
31
30
  LARK_VIEW: () => LARK_VIEW,
31
+ Payload: () => Payload,
32
32
  Platform: () => Platform,
33
33
  ROUTER_EVENTS: () => ROUTER_EVENTS,
34
34
  Router: () => Router,
@@ -67,7 +67,6 @@ __export(index_exports, {
67
67
  getUseStore: () => getUseStore,
68
68
  has: () => has,
69
69
  installFrameVisualizerBridge: () => installFrameVisualizerBridge,
70
- isArray: () => isArray,
71
70
  isPlainObject: () => isPlainObject,
72
71
  isPrimitive: () => isPrimitive,
73
72
  isPrimitiveOrFunc: () => isPrimitiveOrFunc,
@@ -147,7 +146,6 @@ function isPlainObject(value) {
147
146
  if (proto === null) return true;
148
147
  return proto === Object.prototype || proto === null;
149
148
  }
150
- var isArray = Array.isArray;
151
149
  function isPrimitiveOrFunc(value) {
152
150
  return !value || typeof value !== "object" && typeof value !== "function";
153
151
  }
@@ -188,13 +186,13 @@ function assign(target, ...sources) {
188
186
  return target;
189
187
  }
190
188
  function funcWithTry(fns, args, context, configError) {
191
- const fnArray = isArray(fns) ? fns : [fns];
189
+ const fnArray = Array.isArray(fns) ? fns : [fns];
192
190
  let ret;
193
191
  for (const fn of fnArray) {
194
192
  try {
195
193
  ret = Function.prototype.apply.call(fn, context, args);
196
194
  } catch (e) {
197
- configError(e);
195
+ configError?.(e);
198
196
  }
199
197
  }
200
198
  return ret;
@@ -222,7 +220,7 @@ function translateData(data, value) {
222
220
  }
223
221
  return value;
224
222
  }
225
- if (isPlainObject(value) || isArray(value)) {
223
+ if (isPlainObject(value) || Array.isArray(value)) {
226
224
  for (const p in value) {
227
225
  if (has(value, p)) {
228
226
  const val = value[p];
@@ -317,11 +315,11 @@ function now() {
317
315
  }
318
316
  function classExtend(make, base, props, statics) {
319
317
  const baseProto = base["prototype"] ?? {};
320
- const cProto = Object.create(baseProto);
321
- assign(cProto, props);
318
+ const classProto = Object.create(baseProto);
319
+ assign(classProto, props);
322
320
  Object.assign(make, statics);
323
- cProto.constructor = make;
324
- make["prototype"] = cProto;
321
+ classProto.constructor = make;
322
+ make["prototype"] = classProto;
325
323
  }
326
324
 
327
325
  // src/apply-style.ts
@@ -362,8 +360,8 @@ function applyStyle(styleIdOrPairs, css) {
362
360
  }
363
361
 
364
362
  // src/mark.ts
365
- var DELETED_KEY = SPLITTER + "$a";
366
- var MARK_OBJECT_KEY = SPLITTER + "$b";
363
+ var DELETED_KEY = SPLITTER + "$delFlag";
364
+ var MARK_OBJECT_KEY = SPLITTER + "$markKey";
367
365
  function mark(host, key) {
368
366
  let sign = 0;
369
367
  const hostRecord = host;
@@ -387,7 +385,7 @@ function unmark(host) {
387
385
 
388
386
  // src/safeguard.ts
389
387
  var proxiesPool = /* @__PURE__ */ new Map();
390
- var SAFEGUARD_SENTINEL = "_sf_";
388
+ var SAFEGUARD_SENTINEL = "_safe_";
391
389
  function safeguard(data, getter, setter, isRoot) {
392
390
  if (typeof window.__lark_Debug === "undefined" || !window.__lark_Debug) {
393
391
  return data;
@@ -428,7 +426,7 @@ function safeguard(data, getter, setter, isRoot) {
428
426
  if (!prefix && getter) {
429
427
  getter(property);
430
428
  }
431
- if (!isRoot && has(target, property) && (isArray(out) || isPlainObject(out))) {
429
+ if (!isRoot && has(target, property) && (Array.isArray(out) || isPlainObject(out))) {
432
430
  return build(prefix + property + ".", out);
433
431
  }
434
432
  return out;
@@ -603,7 +601,10 @@ var EventEmitter = class {
603
601
  }
604
602
  } else {
605
603
  this.listeners.delete(key);
606
- Reflect.deleteProperty(this, `on${event}`);
604
+ Reflect.deleteProperty(
605
+ this,
606
+ `on${event[0].toUpperCase() + event.slice(1)}`
607
+ );
607
608
  }
608
609
  return this;
609
610
  }
@@ -644,7 +645,7 @@ var EventEmitter = class {
644
645
  }
645
646
  }
646
647
  }
647
- const onMethodName = `on${event}`;
648
+ const onMethodName = `on${event[0].toUpperCase() + event.slice(1)}`;
648
649
  const onMethod = this[onMethodName];
649
650
  if (typeof onMethod === "function") {
650
651
  funcWithTry(
@@ -835,6 +836,7 @@ var State = {
835
836
  emitter.fire(event, data, remove);
836
837
  return State;
837
838
  }
839
+ // onChanged: noop,
838
840
  };
839
841
 
840
842
  // src/router.ts
@@ -1140,9 +1142,9 @@ var Router = {
1140
1142
  window.addEventListener("hashchange", watchChange);
1141
1143
  window.addEventListener("popstate", watchChange);
1142
1144
  window.addEventListener("beforeunload", (domEvent) => {
1143
- const te = {};
1144
- Router.fire(ROUTER_EVENTS.PAGE_UNLOAD, te);
1145
- const msg = te["msg"];
1145
+ const data = {};
1146
+ Router.fire(ROUTER_EVENTS.PAGE_UNLOAD, data);
1147
+ const msg = data["msg"];
1146
1148
  if (msg) {
1147
1149
  domEvent.returnValue = msg;
1148
1150
  }
@@ -1174,16 +1176,16 @@ var frameGetter;
1174
1176
  function parseEventInfo(eventInfo) {
1175
1177
  const cached = eventInfoCache.get(eventInfo);
1176
1178
  if (cached) {
1177
- return assign({}, cached, { r: eventInfo });
1179
+ return assign({}, cached, { value: eventInfo });
1178
1180
  }
1179
1181
  const match = eventInfo.match(EVENT_METHOD_REGEXP) || [];
1180
1182
  const result = {
1181
- v: match[1] || "",
1182
- n: match[2] || "",
1183
- i: match[3] || ""
1183
+ id: match[1] || "",
1184
+ name: match[2] || "",
1185
+ params: match[3] || ""
1184
1186
  };
1185
1187
  eventInfoCache.set(eventInfo, result);
1186
- return assign({}, result, { r: eventInfo });
1188
+ return assign({}, result, { value: eventInfo });
1187
1189
  }
1188
1190
  function findFrameInfo(current, eventType) {
1189
1191
  const eventInfos = [];
@@ -1193,7 +1195,7 @@ function findFrameInfo(current, eventType) {
1193
1195
  if (info) {
1194
1196
  match = parseEventInfo(info);
1195
1197
  }
1196
- if (match && !match.v || selectorEvents[eventType]) {
1198
+ if (match && !match.id || selectorEvents[eventType]) {
1197
1199
  let selectorFrameId = "#";
1198
1200
  let backtrace = 0;
1199
1201
  while (begin && begin !== document.body) {
@@ -1219,10 +1221,10 @@ function findFrameInfo(current, eventType) {
1219
1221
  if (selectorEntry) {
1220
1222
  for (const selectorName of selectorEntry.selectors) {
1221
1223
  const entry = {
1222
- r: selectorName,
1223
- v: frameId,
1224
- n: selectorName,
1225
- i: ""
1224
+ value: selectorName,
1225
+ id: frameId,
1226
+ name: selectorName,
1227
+ params: ""
1226
1228
  };
1227
1229
  if (selectorName) {
1228
1230
  if (!backtrace && elementMatchesSelector(current, selectorName)) {
@@ -1234,8 +1236,8 @@ function findFrameInfo(current, eventType) {
1234
1236
  }
1235
1237
  }
1236
1238
  if (view.template && !backtrace) {
1237
- if (match && !match.v) {
1238
- match.v = frameId;
1239
+ if (match && !match.id) {
1240
+ match.id = frameId;
1239
1241
  }
1240
1242
  break;
1241
1243
  }
@@ -1251,10 +1253,10 @@ function findFrameInfo(current, eventType) {
1251
1253
  }
1252
1254
  if (match) {
1253
1255
  eventInfos.push({
1254
- v: match.v,
1255
- r: match.r,
1256
- n: match.n,
1257
- i: match.i
1256
+ id: match.id,
1257
+ value: match.value,
1258
+ name: match.name,
1259
+ params: match.params
1258
1260
  });
1259
1261
  }
1260
1262
  return eventInfos;
@@ -1275,7 +1277,7 @@ function domEventProcessor(domEvent) {
1275
1277
  const eventInfos = findFrameInfo(current, eventType);
1276
1278
  if (eventInfos.length) {
1277
1279
  for (const info of eventInfos) {
1278
- const { v: frameId, n: handlerName, i: params } = info;
1280
+ const { id: frameId, name: handlerName, params } = info;
1279
1281
  if (lastFrameId !== frameId) {
1280
1282
  if (lastFrameId && domEvent.isPropagationStopped?.()) {
1281
1283
  break;
@@ -1374,8 +1376,8 @@ var WrapMeta = {
1374
1376
  td: [3, "<table><tbody><tr>"],
1375
1377
  area: [1, "<map>"],
1376
1378
  param: [1, "<object>"],
1377
- g: [1, '<svg xmlns="' + SVG_NS + '">'],
1378
- m: [1, '<math xmlns="' + MATH_NS + '">'],
1379
+ svg: [1, '<svg xmlns="' + SVG_NS + '">'],
1380
+ math: [1, '<math xmlns="' + MATH_NS + '">'],
1379
1381
  _: [0, ""]
1380
1382
  };
1381
1383
  WrapMeta["optgroup"] = WrapMeta["option"];
@@ -1408,9 +1410,9 @@ function vdomGetNode(html, refNode) {
1408
1410
  const ns = refNode.namespaceURI;
1409
1411
  let tag;
1410
1412
  if (ns === SVG_NS) {
1411
- tag = "g";
1413
+ tag = "svg";
1412
1414
  } else if (ns === MATH_NS) {
1413
- tag = "m";
1415
+ tag = "math";
1414
1416
  } else {
1415
1417
  const match = TAG_NAME_REGEXP.exec(html);
1416
1418
  tag = match ? match[1] : "";
@@ -1855,221 +1857,6 @@ if (typeof window !== "undefined") {
1855
1857
  if (typeof document !== "undefined") {
1856
1858
  VIEW_GLOBALS["document"] = document;
1857
1859
  }
1858
- function viewPrepare(oView) {
1859
- if (oView.ctors) {
1860
- return oView.ctors;
1861
- }
1862
- const ctors = [];
1863
- oView.ctors = ctors;
1864
- const proto = oView.prototype;
1865
- const eventsObject = {};
1866
- const eventsList = [];
1867
- const selectorObject = {};
1868
- const mixins = proto["mixins"];
1869
- if (mixins && Array.isArray(mixins)) {
1870
- viewMergeMixins(mixins, oView, ctors);
1871
- }
1872
- for (const p in proto) {
1873
- if (!has(proto, p)) continue;
1874
- const currentFn = proto[p];
1875
- if (typeof currentFn !== "function") continue;
1876
- const matches = p.match(VIEW_EVENT_METHOD_REGEXP);
1877
- if (!matches) continue;
1878
- const isSelector = matches[1];
1879
- const selectorOrCallback = matches[2];
1880
- const events = matches[3];
1881
- const modifiers = matches[4];
1882
- const mod = {};
1883
- if (modifiers) {
1884
- for (const item of modifiers.split(",")) {
1885
- mod[item] = true;
1886
- }
1887
- }
1888
- const eventTypes = events.split(",");
1889
- for (const item of eventTypes) {
1890
- const globalNode = VIEW_GLOBALS[selectorOrCallback];
1891
- let mask = 1;
1892
- if (isSelector) {
1893
- if (globalNode) {
1894
- eventsList.push({
1895
- handler: currentFn,
1896
- element: globalNode,
1897
- eventName: item,
1898
- modifiers: mod
1899
- });
1900
- continue;
1901
- }
1902
- mask = 2;
1903
- let selectorEntry = selectorObject[item];
1904
- if (!selectorEntry) {
1905
- selectorEntry = selectorObject[item] = {
1906
- selectors: []
1907
- };
1908
- }
1909
- if (!selectorEntry[selectorOrCallback]) {
1910
- selectorEntry[selectorOrCallback] = 1;
1911
- selectorEntry.selectors.push(selectorOrCallback);
1912
- }
1913
- }
1914
- eventsObject[item] = (eventsObject[item] || 0) | mask;
1915
- const combinedKey = selectorOrCallback + SPLITTER + item;
1916
- const existingFn = proto[combinedKey];
1917
- if (!existingFn) {
1918
- proto[combinedKey] = currentFn;
1919
- } else {
1920
- const mixinFn = currentFn;
1921
- const existingMixin = existingFn;
1922
- if (existingMixin.b) {
1923
- if (mixinFn.b) {
1924
- proto[combinedKey] = processMixinsSameEvent(
1925
- currentFn,
1926
- existingFn
1927
- );
1928
- } else if (has(proto, p)) {
1929
- proto[combinedKey] = currentFn;
1930
- }
1931
- }
1932
- }
1933
- }
1934
- }
1935
- viewWrapMethod(proto, "render", "$b");
1936
- proto["$eo"] = eventsObject;
1937
- proto["$el"] = eventsList;
1938
- proto["$so"] = selectorObject;
1939
- proto["$f"] = proto["assign"];
1940
- return ctors;
1941
- }
1942
- function viewWrapMethod(proto, fnName, shortKey) {
1943
- const originalFn = proto[fnName];
1944
- if (typeof originalFn !== "function") return;
1945
- const wrapped = function(...args) {
1946
- if (this.signature > 0) {
1947
- this.signature++;
1948
- this.fire("rendercall");
1949
- destroyAllResources(this, false);
1950
- const instanceFn = typeof this[fnName] === "function" ? this[fnName] : originalFn;
1951
- const fnToCall = instanceFn === wrapped ? originalFn : instanceFn;
1952
- return funcWithTry(fnToCall, args, this, noop);
1953
- }
1954
- return void 0;
1955
- };
1956
- proto[fnName] = wrapped;
1957
- proto[shortKey] = wrapped;
1958
- }
1959
- function processMixinsSameEvent(additional, exist) {
1960
- let temp;
1961
- const existMixin = exist;
1962
- if (existMixin.a) {
1963
- temp = existMixin;
1964
- } else {
1965
- temp = function(...e) {
1966
- funcWithTry(temp.a ?? [], e, this, noop);
1967
- };
1968
- temp.a = [exist];
1969
- temp.b = 1;
1970
- }
1971
- const additionalMixin = additional;
1972
- temp.a = (temp.a ?? []).concat(additionalMixin.a ?? [additional]);
1973
- return temp;
1974
- }
1975
- function viewMergeMixins(mixins, viewClass, ctors) {
1976
- const proto = viewClass.prototype;
1977
- const temp = {};
1978
- for (const node of mixins) {
1979
- for (const p in node) {
1980
- if (!has(node, p)) continue;
1981
- const fn = node[p];
1982
- const exist = temp[p];
1983
- if (p === "make") {
1984
- ctors.push(fn);
1985
- continue;
1986
- }
1987
- if (VIEW_EVENT_METHOD_REGEXP.test(p)) {
1988
- if (exist) {
1989
- temp[p] = processMixinsSameEvent(fn, exist);
1990
- } else {
1991
- fn.b = 1;
1992
- temp[p] = fn;
1993
- }
1994
- } else {
1995
- if (!exist) {
1996
- temp[p] = fn;
1997
- }
1998
- }
1999
- }
2000
- }
2001
- for (const p in temp) {
2002
- if (!has(proto, p)) {
2003
- proto[p] = temp[p];
2004
- }
2005
- }
2006
- }
2007
- function viewDelegateEvents(view, destroy = false) {
2008
- const proto = Object.getPrototypeOf(view) ?? {};
2009
- const eventsObject = proto["$eo"] || view.eventObjectMap;
2010
- const selectorObject = proto["$so"] || view.eventSelectorMap;
2011
- const eventsList = proto["$el"] || view.globalEventList;
2012
- for (const e in eventsObject) {
2013
- if (has(eventsObject, e)) {
2014
- if (destroy) {
2015
- EventDelegator.unbind(e, !!selectorObject[e]);
2016
- } else {
2017
- EventDelegator.bind(e, !!selectorObject[e]);
2018
- }
2019
- }
2020
- }
2021
- for (const entry of eventsList) {
2022
- if (destroy) {
2023
- entry.element.removeEventListener(
2024
- entry.eventName,
2025
- entry.boundHandler
2026
- );
2027
- } else {
2028
- const handler = entry.handler;
2029
- const element = entry.element;
2030
- const modifiers = entry.modifiers;
2031
- entry.boundHandler = function(domEvent) {
2032
- const extendedEvent = domEvent;
2033
- extendedEvent.eventTarget = element;
2034
- if (modifiers) {
2035
- const kbEvent = domEvent;
2036
- if (modifiers["ctrl"] && !kbEvent.ctrlKey || modifiers["shift"] && !kbEvent.shiftKey || modifiers["alt"] && !kbEvent.altKey || modifiers["meta"] && !kbEvent.metaKey) {
2037
- return;
2038
- }
2039
- }
2040
- funcWithTry(handler, [domEvent], view, noop);
2041
- };
2042
- entry.element.addEventListener(
2043
- entry.eventName,
2044
- entry.boundHandler
2045
- );
2046
- }
2047
- }
2048
- }
2049
- function destroyAllResources(view, lastly) {
2050
- const cache = view.resources;
2051
- for (const p in cache) {
2052
- if (has(cache, p)) {
2053
- const entry = cache[p];
2054
- if (lastly || entry.destroyOnRender) {
2055
- destroyResource(cache, p, true);
2056
- }
2057
- }
2058
- }
2059
- }
2060
- function destroyResource(cache, key, callDestroy, oldEntity) {
2061
- const entry = cache[key];
2062
- if (!entry || entry.entity === oldEntity) return void 0;
2063
- const entity = entry.entity;
2064
- if (entity && typeof entity === "object") {
2065
- const destroyFn = entity["destroy"];
2066
- if (typeof destroyFn === "function" && callDestroy) {
2067
- funcWithTry(destroyFn, [], entity, noop);
2068
- }
2069
- }
2070
- Reflect.deleteProperty(cache, key);
2071
- return entity;
2072
- }
2073
1860
  var View = class _View {
2074
1861
  /** View ID (same as owner frame ID) */
2075
1862
  id = "";
@@ -2093,18 +1880,43 @@ var View = class _View {
2093
1880
  observedStateKeys;
2094
1881
  /** Resource map */
2095
1882
  resources = {};
2096
- /** Selector event map: eventType -> handler name list */
2097
- eventSelectorMap = {};
2098
- /** Event object map: eventType -> bitmask */
2099
- eventObjectMap = {};
2100
- /** Global event list */
2101
- globalEventList = [];
2102
1883
  /** Assign method reference */
2103
1884
  assignMethod;
2104
1885
  /** Whether endUpdate pending */
2105
1886
  endUpdatePending;
2106
1887
  /** Internal event storage */
2107
1888
  _events = new EventEmitter();
1889
+ // ============================================================
1890
+ // Getters for prototype-stored event maps
1891
+ // ============================================================
1892
+ /**
1893
+ * Event bitmask map: eventType -> bitmask (1=root, 2=selector).
1894
+ * Read from prototype ($evtObjMap) set by View.prepare.
1895
+ * Using a getter avoids ES6 class field shadowing the prototype value.
1896
+ */
1897
+ get eventObjectMap() {
1898
+ const proto = Object.getPrototypeOf(this);
1899
+ return proto["$evtObjMap"] || {};
1900
+ }
1901
+ /**
1902
+ * Selector event map: eventType -> selector list.
1903
+ * Read from prototype ($selMap) set by View.prepare.
1904
+ */
1905
+ get eventSelectorMap() {
1906
+ const proto = Object.getPrototypeOf(this);
1907
+ return proto["$selMap"] || {};
1908
+ }
1909
+ /**
1910
+ * Global event list: [{handler, element, eventName, modifiers}].
1911
+ * Read from prototype ($globalEvtList) set by View.prepare.
1912
+ */
1913
+ get globalEventList() {
1914
+ const proto = Object.getPrototypeOf(this);
1915
+ return proto["$globalEvtList"] || [];
1916
+ }
1917
+ // ============================================================
1918
+ // Instance lifecycle methods
1919
+ // ============================================================
2108
1920
  /**
2109
1921
  * Initialize view (called by Frame when mounting).
2110
1922
  */
@@ -2112,14 +1924,7 @@ var View = class _View {
2112
1924
  }
2113
1925
  /**
2114
1926
  * Render view template (called by Frame after init).
2115
- * Wrapped by View_WrapMethod to manage signature + resources.
2116
- *
2117
- * Default implementation calls updater.digest() which:
2118
- * 1. Executes the template function with current data
2119
- * 2. Runs VDOM diff against previous DOM
2120
- * 3. Applies DOM operations
2121
- * 4. Calls endUpdate to mount child frames
2122
- *
1927
+ * Wrapped by View.wrapMethod to manage signature + resources.
2123
1928
  */
2124
1929
  render() {
2125
1930
  this.updater.digest();
@@ -2171,7 +1976,7 @@ var View = class _View {
2171
1976
  if (!flag) {
2172
1977
  setTimeout(
2173
1978
  this.wrapAsync(() => {
2174
- runInvokes(ownerFrame);
1979
+ _View.runInvokes(ownerFrame);
2175
1980
  }),
2176
1981
  0
2177
1982
  );
@@ -2247,7 +2052,7 @@ var View = class _View {
2247
2052
  capture(key, resource, destroyOnRender = false) {
2248
2053
  const cache = this.resources;
2249
2054
  if (resource) {
2250
- destroyResource(cache, key, true, resource);
2055
+ _View.destroyResource(cache, key, true, resource);
2251
2056
  cache[key] = {
2252
2057
  entity: resource,
2253
2058
  destroyOnRender
@@ -2263,7 +2068,7 @@ var View = class _View {
2263
2068
  * If destroy=true, calls the resource's destroy() method.
2264
2069
  */
2265
2070
  release(key, destroy = true) {
2266
- return destroyResource(this.resources, key, destroy);
2071
+ return _View.destroyResource(this.resources, key, destroy);
2267
2072
  }
2268
2073
  // ============================================================
2269
2074
  // Leave tip
@@ -2309,10 +2114,273 @@ var View = class _View {
2309
2114
  });
2310
2115
  }
2311
2116
  // ============================================================
2312
- // Static: extend and merge
2117
+ // Static public methods
2313
2118
  // ============================================================
2314
2119
  /** Collected ctors from mixins */
2315
2120
  static ctors;
2121
+ /**
2122
+ * Prepare a View subclass by scanning its prototype for event method patterns.
2123
+ * Pattern: `$?name<eventType1,eventType2>(&modifiers)`
2124
+ *
2125
+ * Only runs once per View subclass (guarded by ctors marker).
2126
+ * Called from Frame.mountView before creating the view instance.
2127
+ */
2128
+ static prepare(oView) {
2129
+ if (oView.ctors) {
2130
+ return oView.ctors;
2131
+ }
2132
+ const ctors = [];
2133
+ oView.ctors = ctors;
2134
+ const proto = oView.prototype;
2135
+ const eventsObject = {};
2136
+ const eventsList = [];
2137
+ const selectorObject = {};
2138
+ const mixins = proto["mixins"];
2139
+ if (mixins && Array.isArray(mixins)) {
2140
+ _View.mergeMixins(mixins, oView, ctors);
2141
+ }
2142
+ for (const p in proto) {
2143
+ if (!has(proto, p)) continue;
2144
+ const currentFn = proto[p];
2145
+ if (typeof currentFn !== "function") continue;
2146
+ const matches = p.match(VIEW_EVENT_METHOD_REGEXP);
2147
+ if (!matches) continue;
2148
+ const isSelector = matches[1];
2149
+ const selectorOrCallback = matches[2];
2150
+ const events = matches[3];
2151
+ const modifiers = matches[4];
2152
+ const mod = {};
2153
+ if (modifiers) {
2154
+ for (const item of modifiers.split(",")) {
2155
+ mod[item] = true;
2156
+ }
2157
+ }
2158
+ const eventTypes = events.split(",");
2159
+ for (const item of eventTypes) {
2160
+ const globalNode = VIEW_GLOBALS[selectorOrCallback];
2161
+ let mask = 1;
2162
+ if (isSelector) {
2163
+ if (globalNode) {
2164
+ eventsList.push({
2165
+ handler: currentFn,
2166
+ element: globalNode,
2167
+ eventName: item,
2168
+ modifiers: mod
2169
+ });
2170
+ continue;
2171
+ }
2172
+ mask = 2;
2173
+ let selectorEntry = selectorObject[item];
2174
+ if (!selectorEntry) {
2175
+ selectorEntry = selectorObject[item] = {
2176
+ selectors: []
2177
+ };
2178
+ }
2179
+ if (!selectorEntry[selectorOrCallback]) {
2180
+ selectorEntry[selectorOrCallback] = 1;
2181
+ selectorEntry.selectors.push(selectorOrCallback);
2182
+ }
2183
+ }
2184
+ eventsObject[item] = (eventsObject[item] || 0) | mask;
2185
+ const combinedKey = selectorOrCallback + SPLITTER + item;
2186
+ const existingFn = proto[combinedKey];
2187
+ if (!existingFn) {
2188
+ proto[combinedKey] = currentFn;
2189
+ } else {
2190
+ const mixinFn = currentFn;
2191
+ const existingMixin = existingFn;
2192
+ if (existingMixin.marker) {
2193
+ if (mixinFn.marker) {
2194
+ proto[combinedKey] = _View.processMixinsSameEvent(
2195
+ currentFn,
2196
+ existingFn
2197
+ );
2198
+ } else if (has(proto, p)) {
2199
+ proto[combinedKey] = currentFn;
2200
+ }
2201
+ }
2202
+ }
2203
+ }
2204
+ }
2205
+ _View.wrapMethod(proto, "render", "$renderWrap");
2206
+ proto["$evtObjMap"] = eventsObject;
2207
+ proto["$globalEvtList"] = eventsList;
2208
+ proto["$selMap"] = selectorObject;
2209
+ proto["$assignFn"] = proto["assign"];
2210
+ return ctors;
2211
+ }
2212
+ /**
2213
+ * Bind or unbind event delegation for a view instance.
2214
+ * Called from Frame during mount/unmount.
2215
+ */
2216
+ static delegateEvents(view, destroy = false) {
2217
+ const eventsObject = view.eventObjectMap;
2218
+ const selectorObject = view.eventSelectorMap;
2219
+ const eventsList = view.globalEventList;
2220
+ for (const e in eventsObject) {
2221
+ if (has(eventsObject, e)) {
2222
+ if (destroy) {
2223
+ EventDelegator.unbind(e, !!selectorObject[e]);
2224
+ } else {
2225
+ EventDelegator.bind(e, !!selectorObject[e]);
2226
+ }
2227
+ }
2228
+ }
2229
+ for (const entry of eventsList) {
2230
+ if (destroy) {
2231
+ entry.element.removeEventListener(
2232
+ entry.eventName,
2233
+ entry.boundHandler
2234
+ );
2235
+ } else {
2236
+ const handler = entry.handler;
2237
+ const element = entry.element;
2238
+ const modifiers = entry.modifiers;
2239
+ entry.boundHandler = function(domEvent) {
2240
+ const extendedEvent = domEvent;
2241
+ extendedEvent.eventTarget = element;
2242
+ if (modifiers) {
2243
+ const kbEvent = domEvent;
2244
+ if (modifiers["ctrl"] && !kbEvent.ctrlKey || modifiers["shift"] && !kbEvent.shiftKey || modifiers["alt"] && !kbEvent.altKey || modifiers["meta"] && !kbEvent.metaKey) {
2245
+ return;
2246
+ }
2247
+ }
2248
+ funcWithTry(handler, [domEvent], view, noop);
2249
+ };
2250
+ entry.element.addEventListener(
2251
+ entry.eventName,
2252
+ entry.boundHandler
2253
+ );
2254
+ }
2255
+ }
2256
+ }
2257
+ /**
2258
+ * Destroy all resources managed by a view.
2259
+ * If lastly=true, destroy ALL resources; otherwise only destroyOnRender ones.
2260
+ */
2261
+ static destroyAllResources(view, lastly) {
2262
+ const cache = view.resources;
2263
+ for (const p in cache) {
2264
+ if (has(cache, p)) {
2265
+ const entry = cache[p];
2266
+ if (lastly || entry.destroyOnRender) {
2267
+ _View.destroyResource(cache, p, true);
2268
+ }
2269
+ }
2270
+ }
2271
+ }
2272
+ /**
2273
+ * Process deferred invoke calls on a frame.
2274
+ */
2275
+ static 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
+ // Static private methods
2287
+ // ============================================================
2288
+ /**
2289
+ * Wrap a method on the prototype to add signature checking and resource cleanup.
2290
+ */
2291
+ static wrapMethod(proto, fnName, shortKey) {
2292
+ const originalFn = proto[fnName];
2293
+ if (typeof originalFn !== "function") return;
2294
+ const wrapped = function(...args) {
2295
+ if (this.signature > 0) {
2296
+ this.signature++;
2297
+ this.fire("render");
2298
+ _View.destroyAllResources(this, false);
2299
+ const instanceFn = typeof this[fnName] === "function" ? this[fnName] : originalFn;
2300
+ const fnToCall = instanceFn === wrapped ? originalFn : instanceFn;
2301
+ return funcWithTry(fnToCall, args, this, noop);
2302
+ }
2303
+ return void 0;
2304
+ };
2305
+ proto[fnName] = wrapped;
2306
+ proto[shortKey] = wrapped;
2307
+ }
2308
+ /**
2309
+ * When two mixins define the same event method, merge them into
2310
+ * a single function that calls both in sequence.
2311
+ */
2312
+ static processMixinsSameEvent(additional, exist) {
2313
+ let temp;
2314
+ const existMixin = exist;
2315
+ if (existMixin.handlerList) {
2316
+ temp = existMixin;
2317
+ } else {
2318
+ temp = function(...e) {
2319
+ funcWithTry(temp.handlerList ?? [], e, this, noop);
2320
+ };
2321
+ temp.handlerList = [exist];
2322
+ temp.marker = 1;
2323
+ }
2324
+ const additionalMixin = additional;
2325
+ temp.handlerList = (temp.handlerList ?? []).concat(
2326
+ additionalMixin.handlerList ?? [additional]
2327
+ );
2328
+ return temp;
2329
+ }
2330
+ /**
2331
+ * Merge an array of mixin objects into the view prototype.
2332
+ */
2333
+ static mergeMixins(mixins, viewClass, ctors) {
2334
+ const proto = viewClass.prototype;
2335
+ const temp = {};
2336
+ for (const node of mixins) {
2337
+ for (const p in node) {
2338
+ if (!has(node, p)) continue;
2339
+ const fn = node[p];
2340
+ const exist = temp[p];
2341
+ if (p === "make") {
2342
+ ctors.push(fn);
2343
+ continue;
2344
+ }
2345
+ if (VIEW_EVENT_METHOD_REGEXP.test(p)) {
2346
+ if (exist) {
2347
+ temp[p] = _View.processMixinsSameEvent(fn, exist);
2348
+ } else {
2349
+ fn.marker = 1;
2350
+ temp[p] = fn;
2351
+ }
2352
+ } else {
2353
+ if (!exist) {
2354
+ temp[p] = fn;
2355
+ }
2356
+ }
2357
+ }
2358
+ }
2359
+ for (const p in temp) {
2360
+ if (!has(proto, p)) {
2361
+ proto[p] = temp[p];
2362
+ }
2363
+ }
2364
+ }
2365
+ /**
2366
+ * Destroy a single resource entry.
2367
+ */
2368
+ static destroyResource(cache, key, callDestroy, oldEntity) {
2369
+ const entry = cache[key];
2370
+ if (!entry || entry.entity === oldEntity) return void 0;
2371
+ const entity = entry.entity;
2372
+ if (entity && typeof entity === "object") {
2373
+ const destroyFn = entity["destroy"];
2374
+ if (typeof destroyFn === "function" && callDestroy) {
2375
+ funcWithTry(destroyFn, [], entity, noop);
2376
+ }
2377
+ }
2378
+ Reflect.deleteProperty(cache, key);
2379
+ return entity;
2380
+ }
2381
+ // ============================================================
2382
+ // Static: extend and merge
2383
+ // ============================================================
2316
2384
  /**
2317
2385
  * Extend View to create a new View subclass.
2318
2386
  *
@@ -2333,7 +2401,7 @@ var View = class _View {
2333
2401
  constructor(nodeId, ownerFrame, initParams, node, mixinCtors) {
2334
2402
  super(nodeId, ownerFrame, initParams, node, []);
2335
2403
  for (const key in props) {
2336
- if (has(props, key) && key !== "make") {
2404
+ if (has(props, key) && key !== "make" && key !== "render") {
2337
2405
  this[key] = props[key];
2338
2406
  }
2339
2407
  }
@@ -2366,36 +2434,17 @@ var View = class _View {
2366
2434
  }
2367
2435
  }
2368
2436
  }
2369
- ChildView.merge = viewMerge;
2370
- ChildView.extend = _View.extend;
2371
2437
  return ChildView;
2372
2438
  }
2373
2439
  /**
2374
2440
  * Merge mixins into View prototype.
2375
2441
  */
2376
2442
  static merge(...mixins) {
2377
- const self = this;
2378
- const existingCtors = self.ctors || [];
2379
- viewMergeMixins(mixins, self, existingCtors);
2380
- return self;
2443
+ const existingCtors = this.ctors || [];
2444
+ _View.mergeMixins(mixins, this, existingCtors);
2445
+ return this;
2381
2446
  }
2382
2447
  };
2383
- function viewMerge(...mixins) {
2384
- const self = this;
2385
- const existingCtors = self.ctors || [];
2386
- viewMergeMixins(mixins, self, existingCtors);
2387
- return self;
2388
- }
2389
- function runInvokes(frame) {
2390
- const list = frame.invokeList;
2391
- if (!list) return;
2392
- while (list.length) {
2393
- const entry = list.shift();
2394
- if (entry && !entry.removed) {
2395
- frame.invoke(entry.name, entry.args);
2396
- }
2397
- }
2398
- }
2399
2448
 
2400
2449
  // src/frame.ts
2401
2450
  var frameRegistry = /* @__PURE__ */ new Map();
@@ -2503,7 +2552,7 @@ var Frame = class _Frame extends EventEmitter {
2503
2552
  */
2504
2553
  doMountView(ViewClass, params, node, sign) {
2505
2554
  if (sign !== this.signature) return;
2506
- const mixinCtors = viewPrepare(ViewClass);
2555
+ const mixinCtors = View.prepare(ViewClass);
2507
2556
  const ViewConstructor = ViewClass;
2508
2557
  const view = new ViewConstructor(
2509
2558
  this.id,
@@ -2514,7 +2563,7 @@ var Frame = class _Frame extends EventEmitter {
2514
2563
  );
2515
2564
  this.viewInstance = view;
2516
2565
  view.signature = 1;
2517
- viewDelegateEvents(view);
2566
+ View.delegateEvents(view);
2518
2567
  const initResult = funcWithTry(
2519
2568
  view.init,
2520
2569
  [params, { node, deep: !view.template }],
@@ -2525,13 +2574,10 @@ var Frame = class _Frame extends EventEmitter {
2525
2574
  Promise.resolve(initResult).then(() => {
2526
2575
  if (nextSign !== this.signature) return;
2527
2576
  if (view.template) {
2528
- const renderFn = view.$b;
2529
- if (renderFn) {
2530
- renderFn.call(view);
2531
- }
2577
+ view.render();
2532
2578
  } else {
2533
2579
  this.hasAltered = 0;
2534
- if (!view.$e) {
2580
+ if (!view.endUpdatePendingFlag) {
2535
2581
  view.endUpdate();
2536
2582
  }
2537
2583
  }
@@ -2718,7 +2764,7 @@ var Frame = class _Frame extends EventEmitter {
2718
2764
  /** Get or create root frame */
2719
2765
  static root(rootId) {
2720
2766
  if (!rootFrame) {
2721
- rootId = rootId || "lark-root";
2767
+ rootId = rootId || "root";
2722
2768
  let rootElement = document.getElementById(rootId);
2723
2769
  if (!rootElement) {
2724
2770
  rootElement = document.body;
@@ -2839,19 +2885,19 @@ function registerViewClass(viewPath, ViewClass) {
2839
2885
  }
2840
2886
 
2841
2887
  // src/service.ts
2842
- var Bag = class {
2843
- /** Bag data */
2888
+ var Payload = class {
2889
+ /** Payload data */
2844
2890
  data;
2845
2891
  /** Internal cache info */
2846
2892
  cacheInfo;
2847
2893
  constructor(data = {}) {
2848
2894
  this.data = data;
2849
2895
  }
2850
- /** Get a value from bag data */
2896
+ /** Get a value from payload data */
2851
2897
  get(key) {
2852
2898
  return this.data[key];
2853
2899
  }
2854
- /** Set a value in bag data */
2900
+ /** Set a value in payload data */
2855
2901
  set(keyOrData, value) {
2856
2902
  if (typeof keyOrData === "string") {
2857
2903
  this.data[keyOrData] = value;
@@ -2863,287 +2909,366 @@ var Bag = class {
2863
2909
  };
2864
2910
  var FETCH_FLAGS_ALL = 1;
2865
2911
  var FETCH_FLAGS_ONE = 2;
2866
- function createServiceType(syncFn, cacheMax = 20, cacheBuffer = 5) {
2867
- const metas = {};
2868
- const bagCache = new Cache({
2869
- maxSize: cacheMax,
2870
- bufferSize: cacheBuffer
2871
- });
2872
- const pendingCacheKeys = {};
2873
- const staticEmitter2 = new EventEmitter();
2874
- const serviceType = {
2875
- add(attrs) {
2876
- if (!isArray(attrs)) {
2877
- attrs = [attrs];
2878
- }
2879
- for (const bag of attrs) {
2880
- if (bag) {
2881
- const name = bag.name;
2882
- const cache = bag.cache;
2883
- bag.cache = cache ? cache | 0 : 0;
2884
- metas[name] = bag;
2885
- }
2886
- }
2887
- },
2888
- meta(attrs) {
2889
- const name = typeof attrs === "string" ? attrs : attrs["name"];
2890
- return metas[name] || attrs;
2891
- },
2892
- create(attrs) {
2893
- const meta = serviceType.meta(attrs);
2894
- const cache = attrs["cache"] | 0 || meta.cache || 0;
2895
- const entity = new Bag();
2896
- entity.set(meta);
2897
- entity.cacheInfo = {
2898
- name: meta.name,
2899
- after: meta.after,
2900
- cleans: meta.cleans,
2901
- key: cache ? defaultCacheKey(meta, attrs) : "",
2902
- time: 0
2903
- };
2904
- if (typeof attrs === "object" && attrs !== null) {
2905
- entity.set(attrs);
2906
- }
2907
- const before = meta.before;
2908
- if (before) {
2909
- funcWithTry(before, [entity], entity, noop);
2910
- }
2911
- staticEmitter2.fire("begin", { bag: entity });
2912
- return entity;
2913
- },
2914
- get(attrs, createNew) {
2915
- let entity;
2916
- let needsUpdate = false;
2917
- if (!createNew) {
2918
- entity = serviceType.cached(attrs);
2919
- }
2920
- if (!entity) {
2921
- entity = serviceType.create(attrs);
2922
- needsUpdate = true;
2923
- }
2924
- return { entity, needsUpdate };
2925
- },
2926
- cached(attrs) {
2927
- const meta = serviceType.meta(attrs);
2928
- const cache = attrs["cache"] | 0 || meta.cache || 0;
2929
- let cacheKey = "";
2930
- if (cache) {
2931
- cacheKey = defaultCacheKey(meta, attrs);
2932
- }
2933
- if (cacheKey) {
2934
- const info = pendingCacheKeys[cacheKey];
2935
- if (info) {
2936
- return info.e;
2937
- }
2938
- const cached = bagCache.get(cacheKey);
2939
- if (cached && cached.cacheInfo) {
2940
- if (now() - cached.cacheInfo.time > cache) {
2941
- bagCache.del(cacheKey);
2942
- return void 0;
2912
+ var Service = class {
2913
+ /** Service instance ID */
2914
+ id = "";
2915
+ /** Whether service is busy (1 = busy) */
2916
+ busy = 0;
2917
+ /** Whether service is destroyed (1 = destroyed) */
2918
+ destroyed = 0;
2919
+ /** Task queue for sequential operations */
2920
+ taskQueue = [];
2921
+ /** Previous dequeue arguments */
2922
+ prevArgs = [];
2923
+ /** Instance event emitter */
2924
+ _emitter = new EventEmitter();
2925
+ constructor() {
2926
+ this.id = generateId("service");
2927
+ }
2928
+ // ============================================================
2929
+ // Instance accessors for type-level data
2930
+ // ============================================================
2931
+ /** Instance event emitter (public accessor) */
2932
+ get emitter() {
2933
+ return this._emitter;
2934
+ }
2935
+ /**
2936
+ * Get internals object for serviceSend compatibility.
2937
+ * References per-type static state from the current class.
2938
+ */
2939
+ get internals() {
2940
+ const ctor = this.constructor;
2941
+ return {
2942
+ metaList: ctor._metaList,
2943
+ payloadCache: ctor._payloadCache,
2944
+ pendingCacheKeys: ctor._pendingCacheKeys,
2945
+ syncFn: ctor._syncFn,
2946
+ staticEmitter: ctor._staticEmitter
2947
+ };
2948
+ }
2949
+ /**
2950
+ * Get type reference (the constructor) for serviceSend compatibility.
2951
+ * Static methods like get/create are accessible via the constructor.
2952
+ */
2953
+ get type() {
2954
+ return this.constructor;
2955
+ }
2956
+ // ============================================================
2957
+ // Instance methods
2958
+ // ============================================================
2959
+ /**
2960
+ * Fetch all endpoints, callback when all complete.
2961
+ * Uses cache when available.
2962
+ */
2963
+ all(attrs, done) {
2964
+ serviceSend(this, attrs, done, FETCH_FLAGS_ALL, false);
2965
+ return this;
2966
+ }
2967
+ /**
2968
+ * Fetch all endpoints, callback on each completion.
2969
+ */
2970
+ one(attrs, done) {
2971
+ serviceSend(this, attrs, done, FETCH_FLAGS_ONE, false);
2972
+ return this;
2973
+ }
2974
+ /**
2975
+ * Fetch all endpoints, skip cache (always request).
2976
+ */
2977
+ save(attrs, done) {
2978
+ serviceSend(this, attrs, done, FETCH_FLAGS_ALL, true);
2979
+ return this;
2980
+ }
2981
+ /**
2982
+ * Enqueue a task for sequential execution.
2983
+ */
2984
+ enqueue(callback) {
2985
+ if (!this.destroyed) {
2986
+ this.taskQueue.push(callback);
2987
+ this.dequeue(...this.prevArgs);
2988
+ }
2989
+ return this;
2990
+ }
2991
+ /**
2992
+ * Dequeue and execute the next task in queue.
2993
+ */
2994
+ dequeue(...args) {
2995
+ if (!this.busy && !this.destroyed) {
2996
+ this.busy = 1;
2997
+ setTimeout(() => {
2998
+ this.busy = 0;
2999
+ if (!this.destroyed) {
3000
+ const task2 = this.taskQueue.shift();
3001
+ if (task2) {
3002
+ this.prevArgs = args;
3003
+ funcWithTry(task2, args, this, noop);
2943
3004
  }
2944
- return cached;
2945
3005
  }
3006
+ }, 0);
3007
+ }
3008
+ }
3009
+ /**
3010
+ * Destroy the service instance.
3011
+ * After destruction, no new requests can be sent.
3012
+ */
3013
+ destroy() {
3014
+ this.destroyed = 1;
3015
+ this.taskQueue = [];
3016
+ }
3017
+ // Instance event methods (delegate to instance emitter)
3018
+ on(event, handler) {
3019
+ this._emitter.on(event, handler);
3020
+ return this;
3021
+ }
3022
+ off(event, handler) {
3023
+ this._emitter.off(event, handler);
3024
+ return this;
3025
+ }
3026
+ fire(event, data) {
3027
+ this._emitter.fire(event, data);
3028
+ return this;
3029
+ }
3030
+ // ============================================================
3031
+ // Per-type static state
3032
+ // ============================================================
3033
+ /** Per-type metadata registry */
3034
+ static _metaList = {};
3035
+ /** Per-type payload cache (LFU with frequency eviction) */
3036
+ static _payloadCache = new Cache({
3037
+ maxSize: 20,
3038
+ bufferSize: 5
3039
+ });
3040
+ /** Per-type pending cache keys for deduplication */
3041
+ static _pendingCacheKeys = {};
3042
+ /** Per-type sync function */
3043
+ static _syncFn = noop;
3044
+ /** Per-type static event emitter */
3045
+ static _staticEmitter = new EventEmitter();
3046
+ /** Per-type cache max size */
3047
+ static _cacheMax = 20;
3048
+ /** Per-type cache buffer size */
3049
+ static _cacheBuffer = 5;
3050
+ // ============================================================
3051
+ // Static methods (operate on per-type state via `this`)
3052
+ // ============================================================
3053
+ /**
3054
+ * Register API endpoint metadata.
3055
+ */
3056
+ static add(attrs) {
3057
+ if (!Array.isArray(attrs)) {
3058
+ attrs = [attrs];
3059
+ }
3060
+ for (const payload of attrs) {
3061
+ if (payload) {
3062
+ const name = payload.name;
3063
+ const cache = payload.cache;
3064
+ payload.cache = cache ? cache | 0 : 0;
3065
+ this._metaList[name] = payload;
2946
3066
  }
2947
- return void 0;
2948
- },
2949
- clear(names) {
2950
- const nameList = (typeof names === "string" ? names : names.join(",")).split(",");
2951
- const nameSet = {};
2952
- for (const n of nameList) {
2953
- nameSet[n] = 1;
3067
+ }
3068
+ }
3069
+ /**
3070
+ * Get metadata for an API endpoint.
3071
+ */
3072
+ static meta(attrs) {
3073
+ const name = typeof attrs === "string" ? attrs : attrs["name"];
3074
+ return this._metaList[name] || attrs;
3075
+ }
3076
+ /**
3077
+ * Create a Payload for an API request.
3078
+ */
3079
+ static create(attrs) {
3080
+ const meta = this.meta(attrs);
3081
+ const cache = attrs["cache"] | 0 || meta.cache || 0;
3082
+ const entity = new Payload();
3083
+ entity.set(meta);
3084
+ entity.cacheInfo = {
3085
+ name: meta.name,
3086
+ after: meta.after,
3087
+ cleans: meta.cleanKeys,
3088
+ key: cache ? defaultCacheKey(meta, attrs) : "",
3089
+ time: 0
3090
+ };
3091
+ if (typeof attrs === "object" && attrs !== null) {
3092
+ entity.set(attrs);
3093
+ }
3094
+ const before = meta.before;
3095
+ if (before) {
3096
+ funcWithTry(before, [entity], entity, noop);
3097
+ }
3098
+ this._staticEmitter.fire("begin", { payload: entity });
3099
+ return entity;
3100
+ }
3101
+ /**
3102
+ * Get or create a Payload for an API request.
3103
+ */
3104
+ static get(attrs, createNew) {
3105
+ let entity;
3106
+ let needsUpdate = false;
3107
+ if (!createNew) {
3108
+ entity = this.cached(attrs);
3109
+ }
3110
+ if (!entity) {
3111
+ entity = this.create(attrs);
3112
+ needsUpdate = true;
3113
+ }
3114
+ return { entity, needsUpdate };
3115
+ }
3116
+ /**
3117
+ * Get cached Payload if available and not expired.
3118
+ */
3119
+ static cached(attrs) {
3120
+ const meta = this.meta(attrs);
3121
+ const cache = attrs["cache"] | 0 || meta.cache || 0;
3122
+ let cacheKey = "";
3123
+ if (cache) {
3124
+ cacheKey = defaultCacheKey(meta, attrs);
3125
+ }
3126
+ if (cacheKey) {
3127
+ const info = this._pendingCacheKeys[cacheKey];
3128
+ if (info) {
3129
+ return info.entity;
2954
3130
  }
2955
- const keysToDelete = [];
2956
- bagCache.forEach((bag) => {
2957
- if (bag?.cacheInfo && nameSet[bag.cacheInfo.name]) {
2958
- if (bag.cacheInfo.key) {
2959
- keysToDelete.push(bag.cacheInfo.key);
2960
- }
3131
+ const cached = this._payloadCache.get(cacheKey);
3132
+ if (cached && cached.cacheInfo) {
3133
+ if (now() - cached.cacheInfo.time > cache) {
3134
+ this._payloadCache.del(cacheKey);
3135
+ return void 0;
2961
3136
  }
2962
- });
2963
- for (const key of keysToDelete) {
2964
- bagCache.del(key);
3137
+ return cached;
2965
3138
  }
2966
- },
2967
- on(event, handler) {
2968
- staticEmitter2.on(event, handler);
2969
- },
2970
- off(event, handler) {
2971
- staticEmitter2.off(event, handler);
2972
- },
2973
- fire(event, data) {
2974
- staticEmitter2.fire(event, data);
2975
- },
2976
- extend(newSyncFn, newCacheMax, newCacheBuffer) {
2977
- return createServiceType(
2978
- newSyncFn,
2979
- newCacheMax || cacheMax,
2980
- newCacheBuffer || cacheBuffer
2981
- );
2982
3139
  }
2983
- };
2984
- const internals = {
2985
- metas,
2986
- bagCache,
2987
- pendingCacheKeys,
2988
- syncFn,
2989
- staticEmitter: staticEmitter2
2990
- };
2991
- function ServiceInstance() {
2992
- this.id = generateId("s");
2993
- this["$e"] = 0;
2994
- this["$d"] = 0;
2995
- this["$g"] = [];
2996
- this["$h"] = [];
2997
- this._emitter = new EventEmitter();
2998
- this._internals = internals;
2999
- this._type = serviceType;
3000
- }
3001
- ServiceInstance.prototype = {
3002
- all(attrs, done) {
3003
- serviceSend(this, attrs, done, FETCH_FLAGS_ALL, false);
3004
- return this;
3005
- },
3006
- one(attrs, done) {
3007
- serviceSend(this, attrs, done, FETCH_FLAGS_ONE, false);
3008
- return this;
3009
- },
3010
- save(attrs, done) {
3011
- serviceSend(this, attrs, done, FETCH_FLAGS_ALL, true);
3012
- return this;
3013
- },
3014
- enqueue(callback) {
3015
- if (!this["$d"]) {
3016
- this["$g"].push(callback);
3017
- this.dequeue(...this["$h"]);
3018
- }
3019
- return this;
3020
- },
3021
- dequeue(...args) {
3022
- if (!this["$e"] && !this["$d"]) {
3023
- this["$e"] = 1;
3024
- setTimeout(() => {
3025
- this["$e"] = 0;
3026
- if (!this["$d"]) {
3027
- const task2 = this["$g"].shift();
3028
- if (task2) {
3029
- this["$h"] = args;
3030
- funcWithTry(task2, args, this, noop);
3031
- }
3032
- }
3033
- }, 0);
3140
+ return void 0;
3141
+ }
3142
+ /**
3143
+ * Clear cached payloads by endpoint name.
3144
+ */
3145
+ static clear(names) {
3146
+ const nameList = (typeof names === "string" ? names : names.join(",")).split(",");
3147
+ const nameSet = {};
3148
+ for (const n of nameList) {
3149
+ nameSet[n] = 1;
3150
+ }
3151
+ const keysToDelete = [];
3152
+ this._payloadCache.forEach((payload) => {
3153
+ if (payload?.cacheInfo && nameSet[payload.cacheInfo.name]) {
3154
+ if (payload.cacheInfo.key) {
3155
+ keysToDelete.push(payload.cacheInfo.key);
3156
+ }
3034
3157
  }
3035
- },
3036
- destroy() {
3037
- this["$d"] = 1;
3038
- this["$g"] = [];
3039
- },
3040
- on(event, handler) {
3041
- this._emitter.on(event, handler);
3042
- return this;
3043
- },
3044
- off(event, handler) {
3045
- this._emitter.off(event, handler);
3046
- return this;
3047
- },
3048
- fire(event, data) {
3049
- this._emitter.fire(event, data);
3050
- return this;
3158
+ });
3159
+ for (const key of keysToDelete) {
3160
+ this._payloadCache.del(key);
3051
3161
  }
3052
- };
3053
- const staticsMap = {
3054
- add: serviceType.add,
3055
- meta: serviceType.meta,
3056
- create: serviceType.create,
3057
- get: serviceType.get,
3058
- cached: serviceType.cached,
3059
- clear: serviceType.clear,
3060
- on: serviceType.on,
3061
- off: serviceType.off,
3062
- fire: serviceType.fire,
3063
- extend: serviceType.extend,
3064
- _internals: internals
3065
- };
3066
- const constructor = Object.assign(
3067
- ServiceInstance,
3068
- staticsMap
3069
- );
3070
- return constructor;
3071
- }
3072
- var Service = createServiceType(noop);
3162
+ }
3163
+ // Static event methods (operate on per-type emitter)
3164
+ static on(event, handler) {
3165
+ this._staticEmitter.on(event, handler);
3166
+ }
3167
+ static off(event, handler) {
3168
+ this._staticEmitter.off(event, handler);
3169
+ }
3170
+ static fire(event, data) {
3171
+ this._staticEmitter.fire(event, data);
3172
+ }
3173
+ /**
3174
+ * Create a new Service subclass with a custom sync function.
3175
+ * Each subclass gets its own per-type state (metaList, cache, etc.)
3176
+ * to ensure isolation between different Service types.
3177
+ */
3178
+ static extend(newSyncFn, newCacheMax, newCacheBuffer) {
3179
+ const ParentService = this;
3180
+ class ChildService extends ParentService {
3181
+ static _metaList = {};
3182
+ static _payloadCache = new Cache({
3183
+ maxSize: newCacheMax || ParentService._cacheMax,
3184
+ bufferSize: newCacheBuffer || ParentService._cacheBuffer
3185
+ });
3186
+ static _pendingCacheKeys = {};
3187
+ static _syncFn = newSyncFn;
3188
+ static _staticEmitter = new EventEmitter();
3189
+ static _cacheMax = newCacheMax || ParentService._cacheMax;
3190
+ static _cacheBuffer = newCacheBuffer || ParentService._cacheBuffer;
3191
+ }
3192
+ return ChildService;
3193
+ }
3194
+ };
3073
3195
  function defaultCacheKey(meta, attrs) {
3074
3196
  return JSON.stringify(attrs) + SPLITTER + JSON.stringify(meta);
3075
3197
  }
3076
3198
  function serviceSend(service, attrs, done, flag, save) {
3077
- if (service["$d"]) return;
3078
- if (service["$e"]) {
3199
+ if (service["destroyed"]) return;
3200
+ if (service["busy"]) {
3079
3201
  service.enqueue(
3080
3202
  serviceSend.bind(null, service, attrs, done, flag, save)
3081
3203
  );
3082
3204
  return;
3083
3205
  }
3084
- service["$e"] = 1;
3206
+ service["busy"] = 1;
3085
3207
  let attrList;
3086
3208
  if (typeof attrs === "string") {
3087
3209
  attrList = [{ name: attrs }];
3088
- } else if (isArray(attrs)) {
3210
+ } else if (Array.isArray(attrs)) {
3089
3211
  attrList = attrs;
3090
3212
  } else {
3091
3213
  attrList = [attrs];
3092
3214
  }
3093
- const internals = service._internals;
3215
+ const internals = service.internals;
3094
3216
  const { syncFn, pendingCacheKeys, staticEmitter: staticEmitter2 } = internals;
3095
3217
  let requestCount = 0;
3096
3218
  const total = attrList.length;
3097
3219
  const doneArr = new Array(total + 1);
3098
3220
  const errorArgs = [];
3099
3221
  const remoteComplete = (idx, error) => {
3100
- const bag = doneArr[idx + 1];
3101
- let newBag = false;
3222
+ const payload = doneArr[idx + 1];
3223
+ let newPayload = false;
3102
3224
  if (error) {
3103
3225
  errorArgs[idx] = error;
3104
- staticEmitter2.fire("fail", { bag, error });
3226
+ staticEmitter2.fire("fail", { payload, error });
3105
3227
  } else {
3106
- newBag = true;
3107
- staticEmitter2.fire("done", { bag });
3228
+ newPayload = true;
3229
+ staticEmitter2.fire("done", { payload });
3108
3230
  }
3109
- if (!service["$d"]) {
3231
+ if (!service["destroyed"]) {
3110
3232
  const finish = requestCount === total;
3111
3233
  if (finish) {
3112
- service["$e"] = 0;
3234
+ service["busy"] = 0;
3113
3235
  if (flag === FETCH_FLAGS_ALL) {
3114
3236
  doneArr[0] = errorArgs;
3115
3237
  funcWithTry(done, doneArr, service, noop);
3116
3238
  }
3117
3239
  }
3118
3240
  if (flag === FETCH_FLAGS_ONE) {
3119
- funcWithTry(done, [error || null, bag, finish, idx], service, noop);
3241
+ funcWithTry(done, [error || null, payload, finish, idx], service, noop);
3120
3242
  }
3121
3243
  }
3122
- if (newBag) {
3123
- staticEmitter2.fire("end", { bag, error });
3244
+ if (newPayload) {
3245
+ staticEmitter2.fire("end", { payload, error });
3124
3246
  }
3125
3247
  };
3126
3248
  for (const attr of attrList) {
3127
3249
  if (!attr) continue;
3128
3250
  const attrObj = typeof attr === "string" ? { name: attr } : attr;
3129
- const bagInfo = service._type.get(attrObj, save);
3130
- const bagEntity = bagInfo.entity;
3131
- const cacheKey = bagEntity.cacheInfo?.key || "";
3251
+ const payloadInfo = service.type.get(
3252
+ attrObj,
3253
+ save
3254
+ );
3255
+ const payloadEntity = payloadInfo.entity;
3256
+ const cacheKey = payloadEntity.cacheInfo?.key || "";
3132
3257
  const complete = remoteComplete.bind(null, requestCount++);
3133
3258
  if (cacheKey && pendingCacheKeys[cacheKey]) {
3134
3259
  pendingCacheKeys[cacheKey].push(complete);
3135
- } else if (bagInfo.needsUpdate) {
3260
+ } else if (payloadInfo.needsUpdate) {
3136
3261
  if (cacheKey) {
3137
3262
  const cacheList = [complete];
3138
- cacheList.e = bagEntity;
3263
+ cacheList.entity = payloadEntity;
3139
3264
  pendingCacheKeys[cacheKey] = cacheList;
3140
3265
  const cacheComplete = () => {
3141
3266
  const list = pendingCacheKeys[cacheKey];
3142
- const entity = list.e;
3267
+ const entity = list.entity;
3143
3268
  if (entity.cacheInfo) {
3144
3269
  entity.cacheInfo.time = now();
3145
3270
  }
3146
- internals.bagCache.set(cacheKey, entity);
3271
+ internals.payloadCache.set(cacheKey, entity);
3147
3272
  Reflect.deleteProperty(pendingCacheKeys, cacheKey);
3148
3273
  for (const cb of list) {
3149
3274
  if (typeof cb === "function") {
@@ -3151,9 +3276,9 @@ function serviceSend(service, attrs, done, flag, save) {
3151
3276
  }
3152
3277
  }
3153
3278
  };
3154
- syncFn(bagEntity, cacheComplete);
3279
+ syncFn(payloadEntity, cacheComplete);
3155
3280
  } else {
3156
- syncFn(bagEntity, complete);
3281
+ syncFn(payloadEntity, complete);
3157
3282
  }
3158
3283
  } else {
3159
3284
  complete();
@@ -3161,12 +3286,12 @@ function serviceSend(service, attrs, done, flag, save) {
3161
3286
  }
3162
3287
  }
3163
3288
 
3164
- // src/frame-visualizer/index.ts
3165
- var MSG_PING = "LARK_VIS_PING";
3166
- var MSG_PONG = "LARK_VIS_PONG";
3167
- var MSG_REQUEST_TREE = "LARK_VIS_REQUEST_TREE";
3168
- var MSG_TREE = "LARK_VIS_TREE";
3169
- var MSG_TREE_DELTA = "LARK_VIS_TREE_DELTA";
3289
+ // src/frame-visualizer.ts
3290
+ var MSG_PING = "LARK_VISUALIZER_PING";
3291
+ var MSG_PONG = "LARK_VISUALIZER_PONG";
3292
+ var MSG_REQUEST_TREE = "LARK_VISUALIZER_REQUEST_TREE";
3293
+ var MSG_TREE = "LARK_VISUALIZER_TREE";
3294
+ var MSG_TREE_DELTA = "LARK_VISUALIZER_TREE_DELTA";
3170
3295
  function serializeView(view) {
3171
3296
  return {
3172
3297
  id: view.id,
@@ -3271,7 +3396,7 @@ function pushTreeUpdate() {
3271
3396
 
3272
3397
  // src/framework.ts
3273
3398
  var config = {
3274
- rootId: "lark-root",
3399
+ rootId: "root",
3275
3400
  hashbang: "#!",
3276
3401
  error: (error) => {
3277
3402
  throw error;
@@ -3364,7 +3489,12 @@ function dispatcherUpdate(frame, stateKeys) {
3364
3489
  const isChanged = stateKeys ? stateIsObserveChanged(view, stateKeys) : viewIsObserveChanged(view);
3365
3490
  let renderPromise;
3366
3491
  if (isChanged) {
3367
- const renderResult = funcWithTry(view.$b ?? view.render, [], view, noop);
3492
+ const renderResult = funcWithTry(
3493
+ view.renderMethod ?? view.render,
3494
+ [],
3495
+ view,
3496
+ noop
3497
+ );
3368
3498
  if (renderResult && typeof renderResult.then === "function") {
3369
3499
  renderPromise = renderResult;
3370
3500
  }
@@ -3386,13 +3516,13 @@ function dispatcherUpdate(frame, stateKeys) {
3386
3516
  }
3387
3517
  function dispatcherNotifyChange(e) {
3388
3518
  const rootFrame2 = Frame.root();
3389
- const view = e["view"];
3519
+ const view = e.view;
3390
3520
  if (view) {
3391
3521
  const viewPath = typeof view === "object" && view !== null ? String(view.to || "") : String(view);
3392
3522
  rootFrame2.mountView(viewPath);
3393
3523
  } else {
3394
3524
  dispatcherUpdateTag++;
3395
- dispatcherUpdate(rootFrame2, e["keys"]);
3525
+ dispatcherUpdate(rootFrame2, e.keys);
3396
3526
  }
3397
3527
  }
3398
3528
  function dispatchEvent(target, eventType, eventInit) {
@@ -3403,8 +3533,6 @@ function dispatchEvent(target, eventType, eventInit) {
3403
3533
  });
3404
3534
  target.dispatchEvent(event);
3405
3535
  }
3406
- var Base = class extends EventEmitter {
3407
- };
3408
3536
  function use(names, callback) {
3409
3537
  if (!config.require) {
3410
3538
  if (callback) callback();
@@ -3419,7 +3547,7 @@ function use(names, callback) {
3419
3547
  }
3420
3548
  }
3421
3549
  var WAIT_OK = 1;
3422
- var WAIT_TIMEOUT_OR_UNFOUND = 0;
3550
+ var WAIT_TIMEOUT_OR_NOT_FOUND = 0;
3423
3551
  function waitZoneViewsRendered(viewId, timeout) {
3424
3552
  if (timeout == null) {
3425
3553
  timeout = 30 * 1e3;
@@ -3430,7 +3558,7 @@ function waitZoneViewsRendered(viewId, timeout) {
3430
3558
  const check = () => {
3431
3559
  const currentTime = now();
3432
3560
  if (currentTime > endTime || !checkFrame) {
3433
- resolve(WAIT_TIMEOUT_OR_UNFOUND);
3561
+ resolve(WAIT_TIMEOUT_OR_NOT_FOUND);
3434
3562
  } else if (checkFrame.childrenCount === checkFrame.readyCount) {
3435
3563
  resolve(WAIT_OK);
3436
3564
  } else {
@@ -3467,10 +3595,10 @@ var Framework = {
3467
3595
  Router._setConfig(config);
3468
3596
  EventDelegator.setFrameGetter((id) => Frame.get(id));
3469
3597
  Router.on(ROUTER_EVENTS.CHANGED, (data) => {
3470
- dispatcherNotifyChange(data);
3598
+ if (data) dispatcherNotifyChange(data);
3471
3599
  });
3472
3600
  State.on(ROUTER_EVENTS.CHANGED, (data) => {
3473
- dispatcherNotifyChange(data);
3601
+ if (data) dispatcherNotifyChange(data);
3474
3602
  });
3475
3603
  booted3 = true;
3476
3604
  markBooted();
@@ -3507,7 +3635,7 @@ var Framework = {
3507
3635
  /** Wait for zone views to be rendered */
3508
3636
  waitZoneViewsRendered,
3509
3637
  WAIT_OK,
3510
- WAIT_TIMEOUT_OR_UNFOUND,
3638
+ WAIT_TIMEOUT_OR_NOT_FOUND,
3511
3639
  /**
3512
3640
  * Convert array to hash map.
3513
3641
  */
@@ -3572,7 +3700,7 @@ var Framework = {
3572
3700
  /**
3573
3701
  * Base class with EventEmitter.
3574
3702
  */
3575
- Base,
3703
+ Base: EventEmitter,
3576
3704
  // ============================================================
3577
3705
  // Module access
3578
3706
  // ============================================================
@@ -3590,10 +3718,11 @@ if (typeof window !== "undefined") {
3590
3718
  window.__lark_State = State;
3591
3719
  window.__lark_Router = Router;
3592
3720
  window.__lark_Frame = Frame;
3721
+ window.__lark_View = View;
3593
3722
  }
3594
3723
 
3595
3724
  // src/store.ts
3596
- var LARK_GLOBAL = "lark_global";
3725
+ var LARK_GLOBAL = "lark-global";
3597
3726
  var Platform = /* @__PURE__ */ ((Platform2) => {
3598
3727
  Platform2["Lark"] = "lark";
3599
3728
  Platform2["React"] = "react";
@@ -3636,8 +3765,10 @@ var Queue = class {
3636
3765
  const flushTickTask = () => {
3637
3766
  while (queue.length > 0) {
3638
3767
  const task2 = queue.shift();
3639
- pendingTasks.delete(task2);
3640
- runTask(task2);
3768
+ if (task2) {
3769
+ pendingTasks.delete(task2);
3770
+ runTask(task2);
3771
+ }
3641
3772
  }
3642
3773
  };
3643
3774
  Promise.resolve().then(flushTickTask);
@@ -3736,9 +3867,11 @@ var setStateConfig = (target, config2) => {
3736
3867
  if (target && isObject(config2)) StateConfigMap.set(target, config2);
3737
3868
  };
3738
3869
  var getStateConfig = (target, key) => {
3739
- if (!StateConfigMap.has(target)) return void 0;
3870
+ if (!StateConfigMap.has(target)) {
3871
+ return void 0;
3872
+ }
3740
3873
  const config2 = StateConfigMap.get(target);
3741
- return key ? config2[key] : config2;
3874
+ return key ? config2?.[key] : config2;
3742
3875
  };
3743
3876
  var isState = (target) => isObject(target) && StateConfigMap.has(target);
3744
3877
  var createLinkKeys = (target, property) => {
@@ -4305,14 +4438,11 @@ function defineStore(name, creator, config2) {
4305
4438
  const useStore = adapter.useStore;
4306
4439
  const store = new StoreClass(name, config2);
4307
4440
  store[_storeCreate](
4308
- creator(
4309
- store[_innerStore](),
4310
- extendApis
4311
- )
4441
+ creator(store[_innerStore](), extendApis)
4312
4442
  );
4313
4443
  Object.defineProperties(useStore, {
4314
4444
  $storeName: { value: name, configurable: true },
4315
- $del: { value: () => store[_storeDestroy](), configurable: true }
4445
+ $destroyFn: { value: () => store[_storeDestroy](), configurable: true }
4316
4446
  });
4317
4447
  if (!StoreCache.has(name)) {
4318
4448
  StoreCache.set(name, { store, creator, config: config2, useStore });
@@ -4377,7 +4507,7 @@ function observeCell(state, cb, immediate = true) {
4377
4507
  }
4378
4508
  function multi(useStore) {
4379
4509
  const storeName = useStore["$storeName"];
4380
- const flagSym = `lark_comp_${storeName}`;
4510
+ const flagSym = `lark-comp-${storeName}`;
4381
4511
  const map = /* @__PURE__ */ new Map();
4382
4512
  let rootViewPath;
4383
4513
  const getFlag = (viewContext) => {
@@ -4386,7 +4516,7 @@ function multi(useStore) {
4386
4516
  const viewId = owner?.["id"] || "";
4387
4517
  let flag;
4388
4518
  if (viewPath === rootViewPath) {
4389
- flag = `${flagSym}_${viewId}`;
4519
+ flag = `${flagSym}-${viewId}`;
4390
4520
  } else {
4391
4521
  flag = owner?.["viewInitParams"]?.[flagSym];
4392
4522
  }
@@ -4403,14 +4533,13 @@ function multi(useStore) {
4403
4533
  };
4404
4534
  const useFn = ((view) => {
4405
4535
  if (!view)
4406
- throw new Error("[lark-store] multi: cannot find the view instance");
4536
+ throw new Error(
4537
+ "[@lark.js/mvc error] multi: cannot find the view instance"
4538
+ );
4407
4539
  const viewCtx = view;
4408
4540
  const flag = viewCtx[flagSym];
4409
4541
  if (map.has(flag)) return map.get(flag);
4410
- const newFn = cloneStore(
4411
- flag,
4412
- useStore
4413
- );
4542
+ const newFn = cloneStore(flag, useStore);
4414
4543
  map.set(flag, newFn);
4415
4544
  return useFn(view);
4416
4545
  });
@@ -4548,7 +4677,7 @@ function convertArtSyntax(source, debug) {
4548
4677
  }
4549
4678
  if (blockStack.length > 0) {
4550
4679
  const unclosed = blockStack.map((b) => `"${b.ctrl}" at line ${b.line}`).join(", ");
4551
- throw new Error(`[@lark/mvc error] unclosed block(s): ${unclosed}`);
4680
+ throw new Error(`[@lark.js/mvc error] unclosed block(s): ${unclosed}`);
4552
4681
  }
4553
4682
  return result.join("");
4554
4683
  }
@@ -4642,12 +4771,12 @@ function convertArtExpression(code, debug, lineNo, blockStack = []) {
4642
4771
  }
4643
4772
  return `${debugPrefix}<%}else{%>`;
4644
4773
  }
4645
- case "each": {
4646
- blockStack.push({ ctrl: "each", line: lineNo });
4774
+ case "forOf": {
4775
+ blockStack.push({ ctrl: "forOf", line: lineNo });
4647
4776
  const object = tokens[0];
4648
4777
  if (tokens.length > 1 && tokens[1] !== "as") {
4649
4778
  throw new Error(
4650
- `[@lark/mvc error] bad each syntax: {{${code}}}. Expected "as" keyword, got "${tokens[1]}". Usage: {{each list as item [index]}}`
4779
+ `[@lark.js/mvc error] bad forOf syntax: {{${code}}}. Expected "as" keyword, got "${tokens[1]}". Usage: {{forOf list as item [index]}}`
4651
4780
  );
4652
4781
  }
4653
4782
  const restTokens = tokens.slice(2);
@@ -4669,12 +4798,12 @@ function convertArtExpression(code, debug, lineNo, blockStack = []) {
4669
4798
  }
4670
4799
  return `${debugPrefix}<%for(let ${index}=0${refExpr},${refObjCount}=${refObj}.length${lastCount};${index}<${refObjCount};${index}++){${firstAndLast}${valueDecl}%>`;
4671
4800
  }
4672
- case "parse": {
4673
- blockStack.push({ ctrl: "parse", line: lineNo });
4801
+ case "forIn": {
4802
+ blockStack.push({ ctrl: "forIn", line: lineNo });
4674
4803
  const object = tokens[0];
4675
4804
  if (tokens.length > 1 && tokens[1] !== "as") {
4676
4805
  throw new Error(
4677
- `[@lark/mvc error] bad parse syntax: {{${code}}}. Expected "as" keyword, got "${tokens[1]}". Usage: {{for-in obj as val [key]}}`
4806
+ `[@lark.js/mvc error] bad forIn syntax: {{${code}}}. Expected "as" keyword, got "${tokens[1]}". Usage: {{for-in obj as val [key]}}`
4678
4807
  );
4679
4808
  }
4680
4809
  const restTokens2 = tokens.slice(2);
@@ -4694,19 +4823,19 @@ function convertArtExpression(code, debug, lineNo, blockStack = []) {
4694
4823
  case "set":
4695
4824
  return `${debugPrefix}<%let ${tokens.join(" ")};%>`;
4696
4825
  case "/if":
4697
- case "/each":
4698
- case "/parse":
4826
+ case "/forOf":
4827
+ case "/forIn":
4699
4828
  case "/for": {
4700
4829
  const expectedCtrl = keyword.substring(1);
4701
4830
  const last = blockStack.pop();
4702
4831
  if (!last) {
4703
4832
  throw new Error(
4704
- `[@lark/mvc error] unexpected {{${code}}}: no matching open block`
4833
+ `[@lark.js/mvc error] unexpected {{${code}}}: no matching open block`
4705
4834
  );
4706
4835
  }
4707
4836
  if (last.ctrl !== expectedCtrl) {
4708
4837
  throw new Error(
4709
- `[@lark/mvc error] unexpected {{${code}}}: expected {{/${last.ctrl}}} to close block opened at line ${last.line}`
4838
+ `[@lark.js/mvc error] unexpected {{${code}}}: expected {{/${last.ctrl}}} to close block opened at line ${last.line}`
4710
4839
  );
4711
4840
  }
4712
4841
  return `${debugPrefix}<%}%>`;
@@ -4770,7 +4899,7 @@ function parseAsExpr(expr) {
4770
4899
  function compileToFunction(source, debug, file) {
4771
4900
  const matcher = /<%([@=!:])?([\s\S]*?)%>|$/g;
4772
4901
  let index = 0;
4773
- let funcSource = `$p+='`;
4902
+ let funcSource = `$out+='`;
4774
4903
  let hasAtRule = false;
4775
4904
  const escapeSlashRegExp = /\\|'/g;
4776
4905
  const escapeBreakReturnRegExp = /\r|\n/g;
@@ -4796,17 +4925,17 @@ function compileToFunction(source, debug, file) {
4796
4925
  }
4797
4926
  if (operate === "@") {
4798
4927
  hasAtRule = true;
4799
- funcSource += `'+($expr='<%${operate + expr}%>',$i($$ref,${content}))+'`;
4928
+ funcSource += `'+($dbgExpr='<%${operate + expr}%>',$refFn($refAlt,${content}))+'`;
4800
4929
  } else if (operate === "=" || operate === ":") {
4801
- funcSource += `'+($expr='<%${operate + expr}%>',$e(${content}))+'`;
4930
+ funcSource += `'+($dbgExpr='<%${operate + expr}%>',$encHtml(${content}))+'`;
4802
4931
  } else if (operate === "!") {
4803
- if (!content.startsWith("$eu(") || !content.endsWith(")")) {
4804
- content = `$n(${content})`;
4932
+ if (!content.startsWith("$encUri(") || !content.endsWith(")")) {
4933
+ content = `$strSafe(${content})`;
4805
4934
  }
4806
- funcSource += `'+($expr='<%${operate + expr}%>',${content})+'`;
4935
+ funcSource += `'+($dbgExpr='<%${operate + expr}%>',${content})+'`;
4807
4936
  } else if (content) {
4808
4937
  if (line > -1) {
4809
- funcSource += `';$line=${line};$art='${art}';`;
4938
+ funcSource += `';$dbgLine=${line};$dbgArt='${art}';`;
4810
4939
  content = "";
4811
4940
  } else {
4812
4941
  funcSource += `';`;
@@ -4815,19 +4944,19 @@ function compileToFunction(source, debug, file) {
4815
4944
  funcSource = funcSource.substring(0, funcSource.length - 4) + ";";
4816
4945
  }
4817
4946
  if (expr) {
4818
- funcSource += `$expr='<%${expr}%>';`;
4947
+ funcSource += `$dbgExpr='<%${expr}%>';`;
4819
4948
  }
4820
- funcSource += content + `;$p+='`;
4949
+ funcSource += content + `;$out+='`;
4821
4950
  }
4822
4951
  } else {
4823
4952
  if (operate === "@") {
4824
4953
  hasAtRule = true;
4825
- funcSource += `'+$i($$ref,${content})+'`;
4954
+ funcSource += `'+$refFn($refAlt,${content})+'`;
4826
4955
  } else if (operate === "=" || operate === ":") {
4827
- funcSource += `'+$e(${content})+'`;
4956
+ funcSource += `'+$encHtml(${content})+'`;
4828
4957
  } else if (operate === "!") {
4829
- if (!content.startsWith("$eu(") || !content.endsWith(")")) {
4830
- content = `$n(${content})`;
4958
+ if (!content.startsWith("$encUri(") || !content.endsWith(")")) {
4959
+ content = `$strSafe(${content})`;
4831
4960
  }
4832
4961
  funcSource += `'+${content}+'`;
4833
4962
  } else if (content) {
@@ -4835,28 +4964,28 @@ function compileToFunction(source, debug, file) {
4835
4964
  if (funcSource.endsWith(`+'';`)) {
4836
4965
  funcSource = funcSource.substring(0, funcSource.length - 4) + ";";
4837
4966
  }
4838
- funcSource += `${content};$p+='`;
4967
+ funcSource += `${content};$out+='`;
4839
4968
  }
4840
4969
  }
4841
4970
  return match;
4842
4971
  });
4843
4972
  funcSource += `';`;
4844
- funcSource = funcSource.replace(/\$p\+='';/g, "");
4845
- funcSource = funcSource.replace(/\$p\+=''\+/g, "$p+=");
4973
+ funcSource = funcSource.replace(/\$out\+='';/g, "");
4974
+ funcSource = funcSource.replace(/\$out\+=''\+/g, "$out+=");
4846
4975
  if (debug) {
4847
4976
  const filePart = file ? `\\r\\n\\tat file:${file}` : "";
4848
- 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;}`;
4977
+ 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;}`;
4849
4978
  }
4850
4979
  const viewIdRegExp = new RegExp(String.fromCharCode(31), "g");
4851
4980
  funcSource = funcSource.replace(viewIdRegExp, `'+$viewId+'`);
4852
- 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;}}` : "";
4853
- 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)}`;
4854
- 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)}`;
4855
- const encodeQuote = `if(!$eq){let $qr=/['"\\\\]/g;$eq=v=>$n(v).replace($qr,'\\\\$&')}`;
4856
- const refFallback = "if(!$$ref)$$ref=$$;";
4981
+ 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;}}` : "";
4982
+ 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)}`;
4983
+ 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)}`;
4984
+ const encodeQuote = `if(!$encQuote){let $qReg=/['"\\\\]/g;$encQuote=v=>$strSafe(v).replace($qReg,'\\\\$&')}`;
4985
+ const refFallback = "if(!$refAlt)$refAlt=$data;";
4857
4986
  const fns = `${refFallback}${encode}${encodeURIMore}${encodeQuote}${atRule};`;
4858
- const fullSource = `${fns}let $g='\\x1e',$_temp,$p=''{{VARS}};${funcSource}return $p`;
4859
- return `($$,$viewId,$$ref,$e,$n,$eu,$i,$eq)=>{${fullSource}}`;
4987
+ const fullSource = `${fns}let $splitter='\\x1e',$tmp,$out=''{{VARS}};${funcSource}return $out`;
4988
+ return `($data,$viewId,$refAlt,$encHtml,$strSafe,$encUri,$refFn,$encQuote)=>{${fullSource}}`;
4860
4989
  }
4861
4990
  function compileTemplate(source, options = {}) {
4862
4991
  const { debug = false, globalVars = [], file } = options;
@@ -4865,17 +4994,17 @@ function compileTemplate(source, options = {}) {
4865
4994
  const viewEventProcessed = processViewEvents(converted);
4866
4995
  const finalSource = restoreComments(viewEventProcessed, comments);
4867
4996
  const funcBody = compileToFunction(finalSource, debug, file);
4868
- const varDeclarations = globalVars.map((key) => `,${key}=$$.${key}`).join("");
4997
+ const varDeclarations = globalVars.map((key) => `,${key}=$data.${key}`).join("");
4869
4998
  const funcWithVars = funcBody.replace("{{VARS}}", () => varDeclarations);
4870
4999
  return `export default function(data, selfId, refData) {
4871
- let $$ = data || {},
5000
+ let $data = data || {},
4872
5001
  $viewId = selfId || '';
4873
- return (${funcWithVars})($$, $viewId, refData,
4874
- /* $e */ v => String(v == null ? '' : v).replace(/[&<>"'\`]/g, m => '&' + ({'&':'amp','<':'lt','>':'gt','"':'#34',"'":'#39','\`':'#96'})[m] + ';'),
4875
- /* $n */ v => String(v == null ? '' : v),
4876
- /* $eu */ null,
4877
- /* $i */ null,
4878
- /* $eq */ null
5002
+ return (${funcWithVars})($data, $viewId, refData,
5003
+ /* $encHtml */ v => String(v == null ? '' : v).replace(/[&<>"'\`]/g, m => '&' + ({'&':'amp','<':'lt','>':'gt','"':'#34',"'":'#39','\`':'#96'})[m] + ';'),
5004
+ /* $strSafe */ v => String(v == null ? '' : v),
5005
+ /* $encUri */ null,
5006
+ /* $refFn */ null,
5007
+ /* $encQuote */ null
4879
5008
  );
4880
5009
  }`;
4881
5010
  }
@@ -4984,7 +5113,7 @@ function fallbackExtractVariables(source) {
4984
5113
  while ((m = outputRegExp.exec(source)) !== null) {
4985
5114
  vars.add(m[1]);
4986
5115
  }
4987
- const eachRegExp = /\{\{each\s+([a-zA-Z_$][\w$]*)\s+as/g;
5116
+ const eachRegExp = /\{\{forOf\s+([a-zA-Z_$][\w$]*)\s+as/g;
4988
5117
  while ((m = eachRegExp.exec(source)) !== null) {
4989
5118
  vars.add(m[1]);
4990
5119
  }
@@ -5030,52 +5159,64 @@ var BUILTIN_GLOBALS = {
5030
5159
  //
5031
5160
  // These variables appear in the generated template function signature
5032
5161
  // or body. They must be excluded from extractGlobalVars() so that
5033
- // they are not mistaken for user data variables and destructured from $$.
5162
+ // they are not mistaken for user data variables and destructured from $data.
5034
5163
  // SPLITTER character constant (same as \x1e), used as namespace separator
5035
5164
  // for refData keys, event attribute encoding, and internal data structures.
5036
- // Declared as: let $g='\x1e'
5037
- $g: 1,
5038
- // refData — the data object passed from Updater to the template function.
5039
- // User variables are destructured from $$ at the top of the function:
5040
- // let {name, age} = $$;
5165
+ // Declared as: let $splitter='\x1e'
5166
+ $splitter: 1,
5167
+ // Data — the data object passed from Updater to the template function.
5168
+ // User variables are destructured from $data at the top of the function:
5169
+ // let {name, age} = $data;
5041
5170
  // This is the first parameter of the generated arrow function.
5042
- $$: 1,
5171
+ $data: 1,
5043
5172
  // Null-safe toString: v => '' + (v == null ? '' : v)
5044
5173
  // Converts null/undefined to empty string, otherwise calls toString().
5045
5174
  // Wraps every {{!raw}} output to prevent "null" / "undefined" rendering.
5046
- $n: 1,
5047
- // HTML entity encoder: v => $n(v).replace(/[&<>"'`]/g, entityMap)
5175
+ $strSafe: 1,
5176
+ // HTML entity encoder: v => $strSafe(v).replace(/[&<>"'`]/g, entityMap)
5048
5177
  // Encodes &, <, >, ", ', ` to HTML entities (&amp; &lt; etc.)
5049
5178
  // Applied to all {{=escaped}} and {{:binding}} outputs.
5050
- $e: 1,
5051
- // HTML entity map — internal object used by $e:
5179
+ $encHtml: 1,
5180
+ // HTML entity map — internal object used by $encHtml:
5052
5181
  // {'&':'amp','<':'gt','>':'gt','"':'#34','\'':'#39','`':'#96'}
5053
- // Not a standalone function; referenced inside $e's closure.
5054
- $em: 1,
5055
- // HTML entity RegExp — internal regexp used by $e:
5182
+ // Not a standalone function; referenced inside $encHtml's closure.
5183
+ $entMap: 1,
5184
+ // HTML entity RegExp — internal regexp used by $encHtml:
5056
5185
  // /[&<>"'`]/g
5057
- $er: 1,
5058
- // HTML entity replacer function — internal helper used by $e:
5059
- // m => '&' + $em[m] + ';'
5060
- // Maps each matched character to its entity string.
5061
- $ef: 1,
5186
+ $entReg: 1,
5187
+ // HTML entity replacer function — internal helper used by $encHtml:
5188
+ // m => '&' + $entMap[m] + ';'
5189
+ // Maps matched character to its entity string.
5190
+ $entFn: 1,
5062
5191
  // Output buffer — the string accumulator for rendered HTML.
5063
- // All template output is appended via $p += '...'.
5064
- // Declared as: let $p = ''
5065
- $p: 1,
5192
+ // All template output is appended via $out += '...'.
5193
+ // Declared as: let $out = ''
5194
+ $out: 1,
5066
5195
  // Reference lookup: (refData, value) => key
5067
5196
  // Finds or allocates a SPLITTER-prefixed key in refData for a given
5068
5197
  // object reference. Used by {{@ref}} operator for passing object
5069
5198
  // references to child views via v-lark attributes.
5070
- $i: 1,
5071
- // URI encoder: v => encodeURIComponent($n(v)).replace(/[!')(*]/g, extraMap)
5199
+ $refFn: 1,
5200
+ // URI encoder: v => encodeURIComponent($strSafe(v)).replace(/[!')(*]/g, extraMap)
5072
5201
  // Extends encodeURIComponent with encoding of ! ' ( ) *.
5073
5202
  // Applied to values in @event URL parameters and {{!uri}} contexts.
5074
- $eu: 1,
5075
- // Quote encoder: v => $n(v).replace(/['"\\]/g, '\\$&')
5203
+ $encUri: 1,
5204
+ // URI encode map internal object used by $encUri:
5205
+ // {'!':'%21','\'':'%27','(':'%28',')':'%29','*':'%2A'}
5206
+ $uriMap: 1,
5207
+ // URI encode replacer — internal helper used by $encUri:
5208
+ // m => $uriMap[m]
5209
+ $uriFn: 1,
5210
+ // URI encode regexp — internal regexp used by $encUri:
5211
+ // /[!')(*]/g
5212
+ $uriReg: 1,
5213
+ // Quote encoder: v => $strSafe(v).replace(/['"\\]/g, '\\$&')
5076
5214
  // Escapes quotes and backslashes for safe embedding in HTML attribute
5077
5215
  // values (e.g. data-json='...').
5078
- $eq: 1,
5216
+ $encQuote: 1,
5217
+ // Quote encode regexp — internal regexp used by $encQuote:
5218
+ // /['"\\]/g
5219
+ $qReg: 1,
5079
5220
  // View ID — the unique identifier of the owning View instance.
5080
5221
  // Injected into @event attribute values at render time so that
5081
5222
  // EventDelegator can dispatch events to the correct View handler.
@@ -5083,23 +5224,23 @@ var BUILTIN_GLOBALS = {
5083
5224
  $viewId: 1,
5084
5225
  // Debug: current expression text — stores the template expression being
5085
5226
  // evaluated, for error reporting. Only present in debug mode.
5086
- // e.g. $expr='<%=user.name%>'
5087
- $expr: 1,
5227
+ // e.g. $dbgExpr='<%=user.name%>'
5228
+ $dbgExpr: 1,
5088
5229
  // Debug: original art syntax — stores the {{}} template syntax before
5089
5230
  // conversion, for error reporting. Only present in debug mode.
5090
- // e.g. $art='{{=user.name}}'
5091
- $art: 1,
5231
+ // e.g. $dbgArt='{{=user.name}}'
5232
+ $dbgArt: 1,
5092
5233
  // Debug: source line number — tracks the current line in the template
5093
5234
  // source, for error reporting. Only present in debug mode.
5094
- $line: 1,
5095
- // refData alias — fallback reference lookup table.
5096
- // Defaults to $$ when no explicit $$ref is provided.
5097
- // Ensures $i() does not crash when @ operator is used without refData.
5098
- $$ref: 1,
5235
+ $dbgLine: 1,
5236
+ // RefData alias — fallback reference lookup table.
5237
+ // Defaults to $data when no explicit $refAlt is provided.
5238
+ // Ensures $refFn() does not crash when @ operator is used without refData.
5239
+ $refAlt: 1,
5099
5240
  // Temporary variable — used by the compiler for intermediate
5100
5241
  // expression results in generated code (e.g. loop variables,
5101
- // conditional branches). Declared as: let $_temp
5102
- $_temp: 1,
5242
+ // conditional branches). Declared as: let $tmp
5243
+ $tmp: 1,
5103
5244
  // JS literals
5104
5245
  undefined: 1,
5105
5246
  null: 1,
@@ -5165,7 +5306,6 @@ var BUILTIN_GLOBALS = {
5165
5306
  var BUILTIN_GLOBAL_SET = new Set(Object.keys(BUILTIN_GLOBALS));
5166
5307
  // Annotate the CommonJS export names for ESM import in node:
5167
5308
  0 && (module.exports = {
5168
- Bag,
5169
5309
  CALL_BREAK_TIME,
5170
5310
  Cache,
5171
5311
  EVENT_METHOD_REGEXP,
@@ -5174,6 +5314,7 @@ var BUILTIN_GLOBAL_SET = new Set(Object.keys(BUILTIN_GLOBALS));
5174
5314
  Frame,
5175
5315
  Framework,
5176
5316
  LARK_VIEW,
5317
+ Payload,
5177
5318
  Platform,
5178
5319
  ROUTER_EVENTS,
5179
5320
  Router,
@@ -5212,7 +5353,6 @@ var BUILTIN_GLOBAL_SET = new Set(Object.keys(BUILTIN_GLOBALS));
5212
5353
  getUseStore,
5213
5354
  has,
5214
5355
  installFrameVisualizerBridge,
5215
- isArray,
5216
5356
  isPlainObject,
5217
5357
  isPrimitive,
5218
5358
  isPrimitiveOrFunc,