lanka 1.2.0 → 2.0.0

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.
Files changed (90) hide show
  1. package/README.md +3 -1
  2. package/dist/{ALankaGateway-CkW1LbKE.d.ts → ALankaGateway-BrVPaZN5.d.ts} +1 -1
  3. package/dist/ILankaFieldError-D5931-vT.d.ts +34 -0
  4. package/dist/ILankaReadableVM-BoPzHEPV.d.ts +72 -0
  5. package/dist/{ILankaScenarioMetadata-GoWWNEQL.d.ts → ILankaScenarioMetadata-Dj4GCqmX.d.ts} +1 -1
  6. package/dist/{ILankaScenarioVM-DUsI-fSc.d.ts → ILankaScenarioVM-DpKFL3iE.d.ts} +5 -4
  7. package/dist/{LankaError-xpI-qj35.d.ts → LankaError-D6RWNyLM.d.ts} +18 -0
  8. package/dist/{LankaScenarioLocator-D86TIwiu.d.ts → LankaScenarioLocator-Dki2Es3O.d.ts} +5 -5
  9. package/dist/{LankaSharedStoreLocator-zS2kLu-S.d.ts → LankaSharedStoreLocator-jsz_gnUm.d.ts} +1 -1
  10. package/dist/_extend/index.d.ts +233 -9
  11. package/dist/_extend/index.js +137 -8
  12. package/dist/_extend/index.js.map +1 -1
  13. package/dist/_internal/index.d.ts +62 -9
  14. package/dist/_internal/index.js +34 -3
  15. package/dist/_internal/index.js.map +1 -1
  16. package/dist/{activeRuntime-B336NU5I.d.ts → activeRuntime-BO-qY0yY.d.ts} +51 -4
  17. package/dist/bootstrap/index.d.ts +11 -10
  18. package/dist/bootstrap/index.js +7 -8
  19. package/dist/cache/index.d.ts +99 -0
  20. package/dist/cache/index.js +1 -0
  21. package/dist/cache/index.js.map +1 -0
  22. package/dist/{chunk-D27MREPB.js → chunk-7DQUF2QR.js} +2 -2
  23. package/dist/{chunk-UJEC7H6K.js → chunk-BHQ2SQ7P.js} +2 -2
  24. package/dist/chunk-CRIRTOLB.js +27 -0
  25. package/dist/chunk-CRIRTOLB.js.map +1 -0
  26. package/dist/chunk-EYDMBI5T.js +550 -0
  27. package/dist/chunk-EYDMBI5T.js.map +1 -0
  28. package/dist/{chunk-C2HP7CRD.js → chunk-FII3PW2G.js} +2 -2
  29. package/dist/{chunk-Q7QESSYF.js → chunk-G32H73QY.js} +10 -1
  30. package/dist/chunk-G32H73QY.js.map +1 -0
  31. package/dist/{chunk-DTO27QFR.js → chunk-H5TIUKRK.js} +2 -2
  32. package/dist/chunk-HVSQOMRE.js +69 -0
  33. package/dist/chunk-HVSQOMRE.js.map +1 -0
  34. package/dist/{chunk-73IVH3C6.js → chunk-O5EUTNR6.js} +3 -3
  35. package/dist/{chunk-RKYKK6MN.js → chunk-RUMUFHSL.js} +2 -2
  36. package/dist/{chunk-G3I7QIZR.js → chunk-SZVNPK24.js} +21 -14
  37. package/dist/chunk-SZVNPK24.js.map +1 -0
  38. package/dist/chunk-UDP6IXDS.js +17 -0
  39. package/dist/chunk-UDP6IXDS.js.map +1 -0
  40. package/dist/{chunk-5MAQVBI2.js → chunk-UMPSR4US.js} +3 -3
  41. package/dist/{chunk-BGVDPDX4.js → chunk-XGMXT4XZ.js} +20 -3
  42. package/dist/chunk-XGMXT4XZ.js.map +1 -0
  43. package/dist/config/index.js +3 -3
  44. package/dist/{createLanka-DI1CSy2Q.d.ts → createLanka-x4oSMs7P.d.ts} +2 -2
  45. package/dist/{createLankaScope-BiFxNQgl.d.ts → createLankaScope-DomNbEvb.d.ts} +1 -1
  46. package/dist/errors/index.d.ts +22 -3
  47. package/dist/errors/index.js +10 -5
  48. package/dist/errors/index.js.map +1 -1
  49. package/dist/gateway/index.d.ts +4 -3
  50. package/dist/gateway/index.js +9 -8
  51. package/dist/gateway/index.js.map +1 -1
  52. package/dist/index.d.ts +11 -10
  53. package/dist/index.js +9 -10
  54. package/dist/{lankaStandardValidator-CL-r-zEV.d.ts → lankaStandardValidator-BUFnysK0.d.ts} +10 -0
  55. package/dist/locator/index.d.ts +5 -4
  56. package/dist/locator/index.js +2 -2
  57. package/dist/logger/index.js +3 -3
  58. package/dist/mock/index.js +3 -3
  59. package/dist/scenario/index.d.ts +81 -6
  60. package/dist/scenario/index.js +6 -8
  61. package/dist/scenario/index.js.map +1 -1
  62. package/dist/storage/index.d.ts +120 -0
  63. package/dist/storage/index.js +1 -0
  64. package/dist/storage/index.js.map +1 -0
  65. package/dist/stream/index.d.ts +10 -9
  66. package/dist/validation/index.d.ts +7 -4
  67. package/dist/validation/index.js +5 -3
  68. package/dist/viewmodel/index.d.ts +188 -32
  69. package/dist/viewmodel/index.js +167 -171
  70. package/dist/viewmodel/index.js.map +1 -1
  71. package/package.json +11 -4
  72. package/skills/lanka-core/SKILL.md +70 -10
  73. package/skills/lanka-core/reference.md +417 -34
  74. package/skills/lanka-packages/SKILL.md +94 -22
  75. package/dist/chunk-BGVDPDX4.js.map +0 -1
  76. package/dist/chunk-G3I7QIZR.js.map +0 -1
  77. package/dist/chunk-GV5DUYST.js +0 -64
  78. package/dist/chunk-GV5DUYST.js.map +0 -1
  79. package/dist/chunk-O5ROO7QF.js +0 -315
  80. package/dist/chunk-O5ROO7QF.js.map +0 -1
  81. package/dist/chunk-Q7QESSYF.js.map +0 -1
  82. package/dist/chunk-UGXSGQPW.js +0 -180
  83. package/dist/chunk-UGXSGQPW.js.map +0 -1
  84. /package/dist/{chunk-D27MREPB.js.map → chunk-7DQUF2QR.js.map} +0 -0
  85. /package/dist/{chunk-UJEC7H6K.js.map → chunk-BHQ2SQ7P.js.map} +0 -0
  86. /package/dist/{chunk-C2HP7CRD.js.map → chunk-FII3PW2G.js.map} +0 -0
  87. /package/dist/{chunk-DTO27QFR.js.map → chunk-H5TIUKRK.js.map} +0 -0
  88. /package/dist/{chunk-73IVH3C6.js.map → chunk-O5EUTNR6.js.map} +0 -0
  89. /package/dist/{chunk-RKYKK6MN.js.map → chunk-RUMUFHSL.js.map} +0 -0
  90. /package/dist/{chunk-5MAQVBI2.js.map → chunk-UMPSR4US.js.map} +0 -0
@@ -1,18 +1,19 @@
1
- "use client";
1
+ import {
2
+ lankaBlindSpotRegistry
3
+ } from "../chunk-UDP6IXDS.js";
2
4
  import {
3
5
  lankaScenarioBootstrap
4
- } from "../chunk-UGXSGQPW.js";
5
- import "../chunk-O5ROO7QF.js";
6
+ } from "../chunk-EYDMBI5T.js";
6
7
  import {
7
8
  lankaLogger
8
- } from "../chunk-C2HP7CRD.js";
9
+ } from "../chunk-FII3PW2G.js";
9
10
  import {
10
11
  getLankaFlags
11
- } from "../chunk-D27MREPB.js";
12
- import "../chunk-BGVDPDX4.js";
12
+ } from "../chunk-7DQUF2QR.js";
13
+ import "../chunk-XGMXT4XZ.js";
13
14
 
14
15
  // src/viewmodel/_abstractions/lanka-vm-environment/ALankaVMEnvironment.ts
15
- var ALankaVMEnvironment = class {
16
+ var ALankaVMEnvironment = class _ALankaVMEnvironment {
16
17
  /** What `createGateways` answered: the data layer, kept apart from services. */
17
18
  gateways;
18
19
  /** What `createServices` answered: everything that is not a gateway. */
@@ -25,16 +26,48 @@ var ALankaVMEnvironment = class {
25
26
  createServices() {
26
27
  return {};
27
28
  }
28
- /** Runs after the scenarios are bound. */
29
+ /**
30
+ * Runs once the scenarios are bound: inside `startLanka()` for a ViewModel built
31
+ * at module level, inside `build()` for one built after bootstrap — in both
32
+ * cases before any screen has read the hook the build returns.
33
+ */
29
34
  onInit() {
30
35
  }
31
- /** Runs when the screen goes away, before the scenarios are unbound. */
36
+ /**
37
+ * Runs when the ViewModel is released — the framework instance disposed, or a
38
+ * lazy ViewModel's `dispose()` — after its scenario subscriptions are gone.
39
+ */
32
40
  onReset() {
33
41
  }
42
+ /**
43
+ * The two moments above as the scenario binder receives them: present only
44
+ * where this ViewModel took them.
45
+ *
46
+ * Framework plumbing, not an extension point — a ViewModel overrides `onInit`
47
+ * and `onReset`, never this. Protected because the three bases call it, and
48
+ * named after `toStyleContext` for the same reason: a derived view of the
49
+ * protected surface, assembled by the framework.
50
+ *
51
+ * Why "took them" decides anything: `onInit` runs inside `initializeScenario`,
52
+ * which bootstrap alone calls, on the ViewModels registered with it.
53
+ * Registration used to follow scenario bindings only, so a ViewModel that
54
+ * overrode `onInit` and bound nothing was never initialised — silently. Now a
55
+ * declared hook registers the ViewModel too, and a default no-op must not
56
+ * count, or every ViewModel would sit in the scenario registry for nothing.
57
+ * The functional bridges declare theirs as own properties over these methods,
58
+ * which is what this comparison sees.
59
+ */
60
+ toLifecycleHooks() {
61
+ const defaults = _ALankaVMEnvironment.prototype;
62
+ const hooks = {};
63
+ if (this.onInit !== defaults.onInit) hooks.onInit = () => this.onInit();
64
+ if (this.onReset !== defaults.onReset) hooks.onReset = () => this.onReset();
65
+ return hooks;
66
+ }
34
67
  };
35
68
 
36
69
  // src/viewmodel/_abstractions/lanka-vm/ALankaVM.ts
37
- import { create } from "zustand";
70
+ import { createStore } from "zustand/vanilla";
38
71
 
39
72
  // src/viewmodel/_internal/create-lanka-blind-spot-trap/createLankaBlindSpotTrap.ts
40
73
  var createLankaBlindSpotTrap = (viewModelName, isArmed) => {
@@ -106,10 +139,12 @@ var createLankaBlindSpotTrap = (viewModelName, isArmed) => {
106
139
 
107
140
  // src/viewmodel/_internal/create-lanka-scenario-binder/createLankaScenarioBinder.ts
108
141
  var readBindings = (declared) => typeof declared === "function" ? declared() : declared ?? [];
142
+ var needsBootstrap = (config) => typeof config.bindings === "function" || config.bindings !== void 0 && config.bindings.length > 0 || config.onInit !== void 0 || config.onReset !== void 0;
109
143
  var createLankaScenarioBinder = (config) => {
110
144
  const subscriptions = /* @__PURE__ */ new Map();
111
145
  let isInitialized = false;
112
146
  return {
147
+ needsBootstrap: needsBootstrap(config),
113
148
  get isInitialized() {
114
149
  return isInitialized;
115
150
  },
@@ -139,69 +174,6 @@ var createLankaScenarioBinder = (config) => {
139
174
  };
140
175
  };
141
176
 
142
- // src/viewmodel/_internal/create-lanka-tracked-hook/createLankaTrackedHook.ts
143
- import { useCallback, useRef, useSyncExternalStore } from "react";
144
- var asRecord = (state) => state;
145
- var createLankaTrackedHook = (config) => {
146
- return (selector) => {
147
- const selectorRef = useRef(selector);
148
- selectorRef.current = selector;
149
- const trackedKeysRef = useRef(/* @__PURE__ */ new Set());
150
- const trackedStateRef = useRef(null);
151
- const trackedProxyRef = useRef(null);
152
- const subscribe = useCallback(
153
- (onStoreChange) => config.subscribe((nextState, prevState) => {
154
- if (selectorRef.current) {
155
- onStoreChange();
156
- return;
157
- }
158
- const trackedKeys = trackedKeysRef.current;
159
- if (trackedKeys.size === 0) {
160
- onStoreChange();
161
- return;
162
- }
163
- const next = asRecord(nextState);
164
- const prev = asRecord(prevState);
165
- for (const key of trackedKeys) {
166
- if (!Object.is(next[key], prev[key])) {
167
- onStoreChange();
168
- return;
169
- }
170
- }
171
- config.onUntrackedChange?.(trackedKeys, next, prev);
172
- }),
173
- []
174
- );
175
- return useSyncExternalStore(subscribe, readTracked, readUntracked);
176
- function readTracked() {
177
- const state = config.readState();
178
- const activeSelector = selectorRef.current;
179
- if (activeSelector) return activeSelector(state);
180
- if (trackedStateRef.current === state && trackedProxyRef.current) {
181
- return trackedProxyRef.current;
182
- }
183
- const trackedKeys = /* @__PURE__ */ new Set();
184
- const proxyState = new Proxy(state, {
185
- get(target, prop, receiver) {
186
- if (typeof prop === "string") {
187
- trackedKeys.add(prop);
188
- }
189
- return Reflect.get(target, prop, receiver);
190
- }
191
- });
192
- trackedKeysRef.current = trackedKeys;
193
- trackedStateRef.current = state;
194
- trackedProxyRef.current = proxyState;
195
- return proxyState;
196
- }
197
- function readUntracked() {
198
- const state = config.readState();
199
- const activeSelector = selectorRef.current;
200
- return activeSelector ? activeSelector(state) : state;
201
- }
202
- };
203
- };
204
-
205
177
  // src/viewmodel/_abstractions/lanka-vm/ALankaVM.ts
206
178
  var ALankaVM = class extends ALankaVMEnvironment {
207
179
  /**
@@ -252,32 +224,35 @@ var ALankaVM = class extends ALankaVMEnvironment {
252
224
  trigger: this.trigger
253
225
  };
254
226
  }
255
- /** Builds the hook a screen calls. One store per call. */
227
+ /**
228
+ * Builds the ViewModel a screen reads. One store per call.
229
+ *
230
+ * What comes back is a STORE, not a hook: `getState`, `subscribe`, `setState`,
231
+ * plus the name and the tracking flag. A screen reaches it through its
232
+ * framework's binding — `useLankaVM(todoVM)` from `@lankajs/react`,
233
+ * `@lankajs/vue` and the rest of the shelf — and a program with no framework
234
+ * at all reads `getState()` and `subscribe()` directly.
235
+ */
256
236
  build() {
257
237
  lankaLogger.printViewModelLog("START Create VM", this.name);
258
238
  const blindSpot = createLankaBlindSpotTrap(
259
239
  this.name,
260
240
  getLankaFlags().isDevelopment === true
261
241
  );
262
- const bindings = this.scenarioHandlers();
263
- const hasBindings = typeof bindings === "function" || bindings.length > 0;
242
+ const hooks = this.toLifecycleHooks();
243
+ const { initializeScenario, resetScenario, needsBootstrap: needsBootstrap2 } = createLankaScenarioBinder({
244
+ name: this.name,
245
+ bindings: this.scenarioHandlers(),
246
+ context: () => this.toStyleContext(),
247
+ onInit: hooks.onInit,
248
+ onReset: hooks.onReset
249
+ });
264
250
  const stateCreator = (set, get) => {
265
251
  this.set = set;
266
252
  this.get = blindSpot.observeGet(get);
267
253
  this.gateways = this.createGateways();
268
254
  this.services = this.createServices();
269
255
  const actions = blindSpot.observeActions(this.createActions());
270
- const { initializeScenario, resetScenario } = createLankaScenarioBinder({
271
- name: this.name,
272
- bindings,
273
- context: () => this.toStyleContext(),
274
- onInit: () => {
275
- this.onInit();
276
- },
277
- onReset: () => {
278
- this.onReset();
279
- }
280
- });
281
256
  return {
282
257
  ...this.states(),
283
258
  ...actions,
@@ -289,29 +264,21 @@ var ALankaVM = class extends ALankaVMEnvironment {
289
264
  (creator, enhance) => enhance(creator),
290
265
  stateCreator
291
266
  );
292
- const store = create()(enhancedCreator);
293
- const registerScenarioViewModel = (viewModel) => {
294
- if (hasBindings) {
295
- lankaScenarioBootstrap.registerViewModel(viewModel, this.name);
267
+ const store = createStore()(enhancedCreator);
268
+ const viewModel = Object.defineProperties(store, {
269
+ name: { value: this.name, enumerable: false, configurable: true },
270
+ isAccessTracked: {
271
+ value: this.enableAccessTrackingOptimization,
272
+ enumerable: false,
273
+ configurable: true
296
274
  }
297
- };
298
- if (!this.enableAccessTrackingOptimization) {
299
- registerScenarioViewModel(store.getState());
300
- lankaLogger.printViewModelLog("FINISH Create VM", this.name);
301
- return store;
302
- }
303
- const useOptimizedViewModel = createLankaTrackedHook({
304
- subscribe: (onChange) => store.subscribe(onChange),
305
- readState: () => store.getState(),
306
- onUntrackedChange: blindSpot.isArmed ? blindSpot.report : void 0
307
275
  });
308
- useOptimizedViewModel.setState = store.setState;
309
- useOptimizedViewModel.getState = store.getState;
310
- useOptimizedViewModel.getInitialState = store.getInitialState;
311
- useOptimizedViewModel.subscribe = store.subscribe;
312
- registerScenarioViewModel(useOptimizedViewModel.getState());
276
+ lankaBlindSpotRegistry.remember(viewModel, blindSpot);
277
+ if (needsBootstrap2) {
278
+ lankaScenarioBootstrap.registerViewModel(store.getState(), this.name);
279
+ }
313
280
  lankaLogger.printViewModelLog("FINISH Create VM", this.name);
314
- return useOptimizedViewModel;
281
+ return viewModel;
315
282
  }
316
283
  };
317
284
 
@@ -344,17 +311,22 @@ function createLankaVM(config) {
344
311
  createActions() {
345
312
  return config.createActions(this.toStyleContext());
346
313
  }
347
- onInit() {
348
- config.onInit?.(this.toStyleContext());
349
- }
350
- onReset() {
351
- config.onReset?.(this.toStyleContext());
314
+ // The config's hooks become this instance's OWN `onInit`/`onReset`, assigned
315
+ // only when declared. An unconditional method override would make the base
316
+ // read every functional ViewModel as taking both hooks and register it with
317
+ // bootstrap for nothing; an own property is seen by the same test a class
318
+ // override is, and `this.onInit()` means the same thing in both styles.
319
+ constructor() {
320
+ super();
321
+ const { onInit, onReset } = config;
322
+ if (onInit) this.onInit = () => onInit(this.toStyleContext());
323
+ if (onReset) this.onReset = () => onReset(this.toStyleContext());
352
324
  }
353
325
  }
354
326
  return new FunctionalVM().build();
355
327
  }
356
328
 
357
- // src/viewmodel/_internal/create-lazy-lanka-hook/createLazyLankaHook.ts
329
+ // src/viewmodel/_internal/create-lazy-lanka-vm-proxy/createLazyLankaVMProxy.ts
358
330
  var lazySlot = (config) => {
359
331
  let store = null;
360
332
  return {
@@ -388,29 +360,31 @@ var lazySlot = (config) => {
388
360
  };
389
361
  };
390
362
  var PROMISE_MEMBERS = /* @__PURE__ */ new Set(["then", "catch", "finally"]);
391
- var forwardEveryMember = (slot) => (_target, property) => {
363
+ var forwardEveryMember = (slot, config) => (_target, property) => {
392
364
  if (property === "dispose") return slot.release;
393
365
  if (PROMISE_MEMBERS.has(property)) return void 0;
366
+ if (property === "name") return config.name;
367
+ if (property === "isAccessTracked") return config.isAccessTracked;
394
368
  return (...args) => {
395
369
  const built = slot.get();
396
370
  const member = built[property];
397
371
  return typeof member === "function" ? member.apply(built, args) : member;
398
372
  };
399
373
  };
400
- var createLazyLankaHook = (config) => {
374
+ var createLazyLankaVMProxy = (config) => {
401
375
  const slot = lazySlot(config);
402
- const hook = (selector) => {
403
- const built = slot.get();
404
- return selector ? built(selector) : built();
405
- };
406
- return new Proxy(hook, { get: forwardEveryMember(slot) });
376
+ return new Proxy(
377
+ {},
378
+ { get: forwardEveryMember(slot, config) }
379
+ );
407
380
  };
408
381
 
409
382
  // src/viewmodel/_factories/create-lazy-lanka-vm/createLazyLankaVM.ts
410
383
  function createLazyLankaVM(config) {
411
- return createLazyLankaHook({
384
+ return createLazyLankaVMProxy({
412
385
  name: config.name,
413
386
  kind: "VM",
387
+ isAccessTracked: config.enableAccessTrackingOptimization ?? true,
414
388
  create: () => createLankaVM(config)
415
389
  });
416
390
  }
@@ -449,7 +423,12 @@ var ALankaStatelessVM = class extends ALankaVMEnvironment {
449
423
  trigger: this.trigger
450
424
  };
451
425
  }
452
- /** Builds the hook a screen calls. One ViewModel per call. */
426
+ /**
427
+ * Builds the ViewModel a screen reads. One per call.
428
+ *
429
+ * What comes back answers the read half of the port and nothing more: there
430
+ * is no store to write to from outside, because there is no state to write.
431
+ */
453
432
  build() {
454
433
  lankaLogger.printViewModelLog("START Create slVM", this.name);
455
434
  let state = {};
@@ -461,26 +440,26 @@ var ALankaStatelessVM = class extends ALankaVMEnvironment {
461
440
  this.gateways = this.createGateways();
462
441
  this.services = this.createServices();
463
442
  const actions = this.createActions();
464
- const bindings = this.scenarioHandlers();
465
- const { initializeScenario, resetScenario } = createLankaScenarioBinder({
443
+ const hooks = this.toLifecycleHooks();
444
+ const { initializeScenario, resetScenario, needsBootstrap: needsBootstrap2 } = createLankaScenarioBinder({
466
445
  name: this.name,
467
- bindings,
446
+ bindings: this.scenarioHandlers(),
468
447
  context: () => this.toStyleContext(),
469
- onInit: () => {
470
- this.onInit();
471
- },
472
- onReset: () => {
473
- this.onReset();
474
- }
448
+ onInit: hooks.onInit,
449
+ onReset: hooks.onReset
475
450
  });
476
451
  state = { ...state, ...actions, initializeScenario, resetScenario };
477
- if (typeof bindings === "function" || bindings.length > 0) {
452
+ if (needsBootstrap2) {
478
453
  lankaScenarioBootstrap.registerViewModel(state, this.name);
479
454
  }
480
- const useStatelessViewModel = ((selector) => selector ? selector(state) : state);
481
- useStatelessViewModel.getState = () => state;
455
+ const statelessViewModel = {
456
+ name: this.name,
457
+ getState: () => state,
458
+ subscribe: () => () => void 0,
459
+ isAccessTracked: false
460
+ };
482
461
  lankaLogger.printViewModelLog("FINISH Create slVM", this.name);
483
- return useStatelessViewModel;
462
+ return statelessViewModel;
484
463
  }
485
464
  };
486
465
 
@@ -500,11 +479,13 @@ function createStatelessLankaVM(config) {
500
479
  createActions() {
501
480
  return config.createActions(this.toStyleContext());
502
481
  }
503
- onInit() {
504
- config.onInit?.(this.toStyleContext());
505
- }
506
- onReset() {
507
- config.onReset?.(this.toStyleContext());
482
+ // Own properties, assigned only when declared — see the same constructor in
483
+ // `createLankaVM` for why not a method override.
484
+ constructor() {
485
+ super();
486
+ const { onInit, onReset } = config;
487
+ if (onInit) this.onInit = () => onInit(this.toStyleContext());
488
+ if (onReset) this.onReset = () => onReset(this.toStyleContext());
508
489
  }
509
490
  }
510
491
  return new FunctionalStatelessVM().build();
@@ -512,15 +493,17 @@ function createStatelessLankaVM(config) {
512
493
 
513
494
  // src/viewmodel/_factories/create-lazy-stateless-lanka-vm/createLazyStatelessLankaVM.ts
514
495
  function createLazyStatelessLankaVM(config) {
515
- return createLazyLankaHook({
496
+ return createLazyLankaVMProxy({
516
497
  name: config.name,
517
498
  kind: "slVM",
499
+ // A stateless ViewModel has no reactive fields, so there is nothing whose
500
+ // reads could be worth recording — the eager one answers false too.
501
+ isAccessTracked: false,
518
502
  create: () => createStatelessLankaVM(config)
519
503
  });
520
504
  }
521
505
 
522
506
  // src/viewmodel/_factories/create-shared-store-lanka-vm/createSharedStoreLankaVM.ts
523
- import { useStore } from "zustand";
524
507
  function createSharedStoreLankaVM(config) {
525
508
  lankaLogger.printViewModelLog("START Create ssVM", config.name);
526
509
  let actions = {};
@@ -551,7 +534,7 @@ function createSharedStoreLankaVM(config) {
551
534
  services,
552
535
  trigger: (scenario, data) => scenario.trigger(data)
553
536
  };
554
- const { initializeScenario, resetScenario } = createLankaScenarioBinder({
537
+ const { initializeScenario, resetScenario, needsBootstrap: needsBootstrap2 } = createLankaScenarioBinder({
555
538
  name: config.name,
556
539
  bindings: config.scenarioHandlers,
557
540
  context: () => ctx,
@@ -562,24 +545,16 @@ function createSharedStoreLankaVM(config) {
562
545
  lastStoreStateRef = null;
563
546
  lastFullStateRef = null;
564
547
  const isAccessTrackingEnabled = config.enableAccessTrackingOptimization ?? true;
565
- const useTrackedSharedStoreViewModel = createLankaTrackedHook({
566
- subscribe: (onChange) => config.store.subscribe((storeState, prevStoreState) => {
567
- onChange(buildFullState(storeState), buildFullState(prevStoreState));
568
- }),
569
- readState: getFullState
570
- });
571
- const useUntrackedSharedStoreViewModel = ((selector) => useStore(config.store.getApi(), (storeState) => {
572
- const fullState = buildFullState(storeState);
573
- if (selector) {
574
- return selector(fullState);
575
- }
576
- return fullState;
577
- }));
578
- const useSharedStoreViewModel = isAccessTrackingEnabled ? useTrackedSharedStoreViewModel : useUntrackedSharedStoreViewModel;
579
- useSharedStoreViewModel.getState = () => getFullState();
580
- useSharedStoreViewModel.getStoreState = () => config.store.getState();
581
- const declaredBindings = config.scenarioHandlers;
582
- if (typeof declaredBindings === "function" || declaredBindings !== void 0 && declaredBindings.length > 0) {
548
+ const sharedStoreViewModel = {
549
+ name: config.name,
550
+ getState: getFullState,
551
+ getStoreState: () => config.store.getState(),
552
+ isAccessTracked: isAccessTrackingEnabled,
553
+ subscribe: (listener) => config.store.subscribe((storeState, prevStoreState) => {
554
+ listener(buildFullState(storeState), buildFullState(prevStoreState));
555
+ })
556
+ };
557
+ if (needsBootstrap2) {
583
558
  const scenarioViewModel = {
584
559
  initializeScenario,
585
560
  resetScenario
@@ -587,14 +562,15 @@ function createSharedStoreLankaVM(config) {
587
562
  lankaScenarioBootstrap.registerViewModel(scenarioViewModel, config.name);
588
563
  }
589
564
  lankaLogger.printViewModelLog("FINISH Create ssVM", config.name);
590
- return useSharedStoreViewModel;
565
+ return sharedStoreViewModel;
591
566
  }
592
567
 
593
568
  // src/viewmodel/_factories/create-lazy-shared-store-lanka-vm/createLazySharedStoreLankaVM.ts
594
569
  function createLazySharedStoreLankaVM(config) {
595
- return createLazyLankaHook({
570
+ return createLazyLankaVMProxy({
596
571
  name: config.name,
597
572
  kind: "ssVM",
573
+ isAccessTracked: config.enableAccessTrackingOptimization ?? true,
598
574
  create: () => createSharedStoreLankaVM(config)
599
575
  });
600
576
  }
@@ -626,6 +602,7 @@ var ALankaSharedStoreVM = class extends ALankaVMEnvironment {
626
602
  }
627
603
  /** Builds the hook a screen calls. One ViewModel per call. */
628
604
  build() {
605
+ const hooks = this.toLifecycleHooks();
629
606
  return createSharedStoreLankaVM({
630
607
  name: this.name,
631
608
  store: this.store,
@@ -642,24 +619,20 @@ var ALankaSharedStoreVM = class extends ALankaVMEnvironment {
642
619
  this.trigger = context.trigger;
643
620
  return this.createActions();
644
621
  },
645
- onInit: () => {
646
- this.onInit();
647
- },
648
- onReset: () => {
649
- this.onReset();
650
- }
622
+ onInit: hooks.onInit,
623
+ onReset: hooks.onReset
651
624
  });
652
625
  }
653
626
  };
654
627
 
655
628
  // src/viewmodel/_abstractions/lanka-shared-store/ALankaSharedStore.ts
656
- import { createStore } from "zustand/vanilla";
629
+ import { createStore as createStore2 } from "zustand/vanilla";
657
630
  var ALankaSharedStore = class {
658
631
  api;
659
632
  createInitialState;
660
633
  constructor(createInitialState) {
661
634
  this.createInitialState = createInitialState;
662
- this.api = createStore()(() => this.createInitialState());
635
+ this.api = createStore2()(() => this.createInitialState());
663
636
  }
664
637
  getApi() {
665
638
  return this.api;
@@ -701,11 +674,34 @@ var createLankaSharedStore = (createInitialState) => {
701
674
  }
702
675
  return new FunctionalSharedStore();
703
676
  };
677
+
678
+ // src/viewmodel/_factories/create-lanka-shallow-hold/createLankaShallowHold.ts
679
+ var isShallowEqual = (a, b) => {
680
+ if (Object.is(a, b)) return true;
681
+ if (typeof a !== "object" || a === null || typeof b !== "object" || b === null) return false;
682
+ const left = Object.keys(a);
683
+ const right = Object.keys(b);
684
+ if (left.length !== right.length) return false;
685
+ return left.every(
686
+ (key) => Object.hasOwn(b, key) && Object.is(a[key], b[key])
687
+ );
688
+ };
689
+ var createLankaShallowHold = () => {
690
+ let held;
691
+ let holding = false;
692
+ return (next) => {
693
+ if (holding && isShallowEqual(held, next)) return held;
694
+ holding = true;
695
+ held = next;
696
+ return next;
697
+ };
698
+ };
704
699
  export {
705
700
  ALankaSharedStore,
706
701
  ALankaSharedStoreVM,
707
702
  ALankaStatelessVM,
708
703
  ALankaVM,
704
+ createLankaShallowHold,
709
705
  createLankaSharedStore,
710
706
  createLankaVM,
711
707
  createLazyLankaVM,