lanka 1.1.1 → 1.3.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 (63) 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/{ILankaScenarioMetadata-GoWWNEQL.d.ts → ILankaScenarioMetadata-Dj4GCqmX.d.ts} +1 -1
  5. package/dist/{ILankaScenarioVM-DUsI-fSc.d.ts → ILankaScenarioVM-DpKFL3iE.d.ts} +5 -4
  6. package/dist/{LankaError-xpI-qj35.d.ts → LankaError-D6RWNyLM.d.ts} +18 -0
  7. package/dist/{LankaScenarioLocator-D86TIwiu.d.ts → LankaScenarioLocator-CAENLhDO.d.ts} +3 -3
  8. package/dist/_extend/index.d.ts +6 -5
  9. package/dist/_extend/index.js +3 -3
  10. package/dist/_internal/index.d.ts +61 -8
  11. package/dist/_internal/index.js +25 -0
  12. package/dist/_internal/index.js.map +1 -1
  13. package/dist/{activeRuntime-B336NU5I.d.ts → activeRuntime-BYd2D6Lx.d.ts} +2 -2
  14. package/dist/bootstrap/index.d.ts +9 -8
  15. package/dist/bootstrap/index.js +4 -4
  16. package/dist/cache/index.d.ts +99 -0
  17. package/dist/cache/index.js +1 -0
  18. package/dist/cache/index.js.map +1 -0
  19. package/dist/{chunk-GK3E4CVW.js → chunk-5MAQVBI2.js} +4 -4
  20. package/dist/{chunk-UGXSGQPW.js → chunk-B7EYIAW7.js} +39 -3
  21. package/dist/chunk-B7EYIAW7.js.map +1 -0
  22. package/dist/chunk-CRIRTOLB.js +27 -0
  23. package/dist/chunk-CRIRTOLB.js.map +1 -0
  24. package/dist/{chunk-Q7QESSYF.js → chunk-G32H73QY.js} +10 -1
  25. package/dist/chunk-G32H73QY.js.map +1 -0
  26. package/dist/chunk-HVSQOMRE.js +69 -0
  27. package/dist/chunk-HVSQOMRE.js.map +1 -0
  28. package/dist/{chunk-73IVH3C6.js → chunk-NDCJBO47.js} +2 -2
  29. package/dist/{chunk-ECM2HZHS.js → chunk-UBGXDTXC.js} +16 -7
  30. package/dist/chunk-UBGXDTXC.js.map +1 -0
  31. package/dist/{createLanka-DI1CSy2Q.d.ts → createLanka-NrlvN-WQ.d.ts} +1 -1
  32. package/dist/errors/index.d.ts +22 -3
  33. package/dist/errors/index.js +8 -3
  34. package/dist/errors/index.js.map +1 -1
  35. package/dist/gateway/index.d.ts +4 -3
  36. package/dist/gateway/index.js +4 -3
  37. package/dist/gateway/index.js.map +1 -1
  38. package/dist/index.d.ts +9 -8
  39. package/dist/index.js +5 -5
  40. package/dist/{lankaStandardValidator-CL-r-zEV.d.ts → lankaStandardValidator-BUFnysK0.d.ts} +10 -0
  41. package/dist/locator/index.d.ts +3 -2
  42. package/dist/scenario/index.d.ts +56 -6
  43. package/dist/scenario/index.js +1 -1
  44. package/dist/storage/index.d.ts +120 -0
  45. package/dist/storage/index.js +1 -0
  46. package/dist/storage/index.js.map +1 -0
  47. package/dist/stream/index.d.ts +8 -7
  48. package/dist/validation/index.d.ts +7 -4
  49. package/dist/validation/index.js +5 -3
  50. package/dist/viewmodel/index.d.ts +101 -13
  51. package/dist/viewmodel/index.js +90 -48
  52. package/dist/viewmodel/index.js.map +1 -1
  53. package/package.json +11 -3
  54. package/skills/lanka-core/SKILL.md +68 -1
  55. package/skills/lanka-core/reference.md +312 -6
  56. package/skills/lanka-packages/SKILL.md +1 -1
  57. package/dist/chunk-ECM2HZHS.js.map +0 -1
  58. package/dist/chunk-GV5DUYST.js +0 -64
  59. package/dist/chunk-GV5DUYST.js.map +0 -1
  60. package/dist/chunk-Q7QESSYF.js.map +0 -1
  61. package/dist/chunk-UGXSGQPW.js.map +0 -1
  62. /package/dist/{chunk-GK3E4CVW.js.map → chunk-5MAQVBI2.js.map} +0 -0
  63. /package/dist/{chunk-73IVH3C6.js.map → chunk-NDCJBO47.js.map} +0 -0
@@ -1,8 +1,10 @@
1
1
  import {
2
- LankaValidationError,
3
2
  lankaStandardValidator
4
- } from "../chunk-GV5DUYST.js";
5
- import "../chunk-Q7QESSYF.js";
3
+ } from "../chunk-HVSQOMRE.js";
4
+ import {
5
+ LankaValidationError
6
+ } from "../chunk-CRIRTOLB.js";
7
+ import "../chunk-G32H73QY.js";
6
8
  export {
7
9
  LankaValidationError,
8
10
  lankaStandardValidator
@@ -1,5 +1,5 @@
1
1
  import { StoreApi, StateCreator, UseBoundStore } from 'zustand';
2
- import { a as ILankaScenario, T as TLankaReplayRequest, I as ILankaScenarioVM } from '../ILankaScenarioVM-DUsI-fSc.js';
2
+ import { a as ILankaScenario, T as TLankaReplayRequest, I as ILankaScenarioVM } from '../ILankaScenarioVM-DpKFL3iE.js';
3
3
  import { A as ALankaSharedStore } from '../ALankaSharedStore-B7uepuuk.js';
4
4
  import { StoreApi as StoreApi$1 } from 'zustand/vanilla';
5
5
 
@@ -45,6 +45,30 @@ interface ILankaScenarioBinding<TData, TState extends object, TGateways extends
45
45
 
46
46
  type TUnknownLankaScenarioBinding<TState extends object, TGateways extends object, TServices extends object> = ILankaScenarioBinding<unknown, TState & ILankaScenarioVM, TGateways, TServices>;
47
47
 
48
+ /**
49
+ * Scenario bindings as a ViewModel declares them: the list, or a factory for it.
50
+ *
51
+ * The factory form exists for the same reason `TLankaDependencyBag` has one, and
52
+ * for a sharper case. A ViewModel declared at module level writes its bindings as
53
+ * an array literal, and each entry names its scenario — `lankaScenarios.<name>`,
54
+ * which is a LOCATOR read. Written directly, that read happens while the module
55
+ * is being evaluated, and a module body can run before `createLanka` has: then
56
+ * the locator refuses with "lanka used before an instance existed" and nothing
57
+ * renders.
58
+ *
59
+ * Import order is not a defence. It holds inside one chunk, and a bundler decides
60
+ * chunks — in ES modules the body of an imported chunk runs before the body of
61
+ * the chunk importing it, so an application that put its ViewModels in their own
62
+ * chunks evaluated them ahead of its own `createLanka` call. Measured in a real
63
+ * app: 44 application chunks were statically imported by the entry, and its whole
64
+ * browser-level suite died on the first of them.
65
+ *
66
+ * `gateways` and `services` already accept a factory for the identical reason.
67
+ * Bindings were the one field left out, and they are the field that reads the
68
+ * locator most.
69
+ */
70
+ type TLankaScenarioBindingsDeclaration<TBinding> = readonly TBinding[] | (() => readonly TBinding[]);
71
+
48
72
  type TLankaAnyMutators = any;
49
73
 
50
74
  type TLankaVMEnhancer<TState> = (creator: StateCreator<TState, TLankaAnyMutators, TLankaAnyMutators>) => StateCreator<TState, TLankaAnyMutators, TLankaAnyMutators>;
@@ -59,7 +83,12 @@ interface ILankaVMConfig<State extends object, Actions extends object, TGateways
59
83
  enableAccessTrackingOptimization?: boolean;
60
84
  states?: State;
61
85
  createActions: (ctx: ILankaVMContext<State & Actions & ILankaScenarioVM, TGateways, Services>) => Actions;
62
- scenarioHandlers?: TUnknownLankaScenarioBinding<State & Actions, TGateways, Services>[];
86
+ /**
87
+ * A FACTORY postpones building the list until bind time — see
88
+ * `TLankaScenarioBindingsDeclaration` for the module-scope locator read it
89
+ * exists to avoid.
90
+ */
91
+ scenarioHandlers?: TLankaScenarioBindingsDeclaration<TUnknownLankaScenarioBinding<State & Actions, TGateways, Services>>;
63
92
  /** Non-gateway services. */
64
93
  services?: Services | (() => Services);
65
94
  /** TGateways (data layer) separated from services. */
@@ -130,7 +159,8 @@ interface ILankaStatelessScenarioBinding<TData, TState extends object, TGateways
130
159
  type TLankaStatelessVMConfig$1<Actions extends object, TGateways extends object = Record<string, never>, Services extends object = Record<string, never>> = {
131
160
  name: string;
132
161
  createActions: (ctx: ILankaStatelessVMContext<Actions & ILankaScenarioVM, TGateways, Services>) => Actions;
133
- scenarioHandlers?: ILankaStatelessScenarioBinding<unknown, Actions & ILankaScenarioVM, TGateways, Services>[];
162
+ /** A FACTORY is read at bind time — see `TLankaScenarioBindingsDeclaration`. */
163
+ scenarioHandlers?: TLankaScenarioBindingsDeclaration<ILankaStatelessScenarioBinding<unknown, Actions & ILankaScenarioVM, TGateways, Services>>;
134
164
  services?: Services | (() => Services);
135
165
  gateways?: TGateways | (() => TGateways);
136
166
  onInit?: (ctx: ILankaStatelessVMContext<Actions & ILankaScenarioVM, TGateways, Services>) => void;
@@ -193,7 +223,8 @@ interface ILankaSharedStoreVMConfig<TStoreState extends object, TActions extends
193
223
  enableAccessTrackingOptimization?: boolean;
194
224
  store: TStore;
195
225
  createActions: (ctx: ILankaSharedStoreVMContext<TStoreState, TStoreState & TActions & ILankaScenarioVM, TStore, TGateways, TServices>) => TActions;
196
- scenarioHandlers?: ILankaSharedStoreScenarioBinding<unknown, TStoreState, TActions, TStore, TGateways, TServices>[];
226
+ /** A FACTORY is read at bind time — see `TLankaScenarioBindingsDeclaration`. */
227
+ scenarioHandlers?: TLankaScenarioBindingsDeclaration<ILankaSharedStoreScenarioBinding<unknown, TStoreState, TActions, TStore, TGateways, TServices>>;
197
228
  services?: TServices | (() => TServices);
198
229
  gateways?: TGateways | (() => TGateways);
199
230
  onInit?: (ctx: ILankaSharedStoreVMContext<TStoreState, TStoreState & TActions & ILankaScenarioVM, TStore, TGateways, TServices>) => void;
@@ -226,6 +257,19 @@ declare function createLazySharedStoreLankaVM<StoreState extends object, Actions
226
257
  declare function createLazySharedStoreLankaVM<StoreState extends object, Actions extends object, Store extends ALankaSharedStore<StoreState>, TGateways extends object>(config: ILankaSharedStoreVMConfig<StoreState, Actions, Store, TGateways, Record<string, never>>): TLazySharedStoreReturn<StoreState, Actions, Store, TGateways, Record<string, never>>;
227
258
  declare function createLazySharedStoreLankaVM<StoreState extends object, Actions extends object, Store extends ALankaSharedStore<StoreState>, TGateways extends object, Services extends object>(config: ILankaSharedStoreVMConfig<StoreState, Actions, Store, TGateways, Services>): TLazySharedStoreReturn<StoreState, Actions, Store, TGateways, Services>;
228
259
 
260
+ /**
261
+ * The lifecycle moments a ViewModel took, as the scenario binder receives them.
262
+ *
263
+ * A hook is present only when the ViewModel DECLARED it — overrode the method,
264
+ * or passed it in config. Bootstrap registers a ViewModel for the sake of these
265
+ * calls, so "declared" is what decides registration; a default no-op must not
266
+ * count, or every ViewModel would sit in the scenario registry for nothing.
267
+ */
268
+ interface ILankaVMLifecycleHooks {
269
+ onInit?: () => void;
270
+ onReset?: () => void;
271
+ }
272
+
229
273
  /**
230
274
  * What every ViewModel is given, and the two moments it is told about.
231
275
  *
@@ -250,10 +294,36 @@ declare abstract class ALankaVMEnvironment<TGateways extends object = Record<str
250
294
  protected createGateways(): TGateways;
251
295
  /** Non-gateway collaborators, built once per ViewModel. */
252
296
  protected createServices(): Services;
253
- /** Runs after the scenarios are bound. */
297
+ /**
298
+ * Runs once the scenarios are bound: inside `startLanka()` for a ViewModel built
299
+ * at module level, inside `build()` for one built after bootstrap — in both
300
+ * cases before any screen has read the hook the build returns.
301
+ */
254
302
  protected onInit(): void;
255
- /** Runs when the screen goes away, before the scenarios are unbound. */
303
+ /**
304
+ * Runs when the ViewModel is released — the framework instance disposed, or a
305
+ * lazy ViewModel's `dispose()` — after its scenario subscriptions are gone.
306
+ */
256
307
  protected onReset(): void;
308
+ /**
309
+ * The two moments above as the scenario binder receives them: present only
310
+ * where this ViewModel took them.
311
+ *
312
+ * Framework plumbing, not an extension point — a ViewModel overrides `onInit`
313
+ * and `onReset`, never this. Protected because the three bases call it, and
314
+ * named after `toStyleContext` for the same reason: a derived view of the
315
+ * protected surface, assembled by the framework.
316
+ *
317
+ * Why "took them" decides anything: `onInit` runs inside `initializeScenario`,
318
+ * which bootstrap alone calls, on the ViewModels registered with it.
319
+ * Registration used to follow scenario bindings only, so a ViewModel that
320
+ * overrode `onInit` and bound nothing was never initialised — silently. Now a
321
+ * declared hook registers the ViewModel too, and a default no-op must not
322
+ * count, or every ViewModel would sit in the scenario registry for nothing.
323
+ * The functional bridges declare theirs as own properties over these methods,
324
+ * which is what this comparison sees.
325
+ */
326
+ protected toLifecycleHooks(): ILankaVMLifecycleHooks;
257
327
  }
258
328
 
259
329
  /**
@@ -269,7 +339,9 @@ declare abstract class ALankaVMEnvironment<TGateways extends object = Record<str
269
339
  * value or a thunk, the class supplies by overriding a method of the same name —
270
340
  * `states`, `scenarioHandlers`, `enhancers`, `onInit`, `onReset` — with the two
271
341
  * dependency suppliers named `createGateways` and `createServices`, because
272
- * `gateways` and `services` already name what they answer.
342
+ * `gateways` and `services` already name what they answer. `toLifecycleHooks`
343
+ * is protected too and is not one of these: it is how the framework reads the
344
+ * two hooks, and a ViewModel overrides the hooks, never it.
273
345
  *
274
346
  * ```ts
275
347
  * class TodoVM extends ALankaVM<ITodoState, ITodoActions, ITodoGateways> {
@@ -321,8 +393,14 @@ declare abstract class ALankaVM<State extends object, Actions extends object, TG
321
393
  protected trigger: <TData>(scenario: ILankaScenario<TData>, data?: TData) => void;
322
394
  /** The reactive fields the screen reads. */
323
395
  protected states(): State;
324
- /** The scenarios this ViewModel listens to, unsubscribed for it on reset. */
325
- protected scenarioHandlers(): TUnknownLankaScenarioBinding<State & Actions, TGateways, Services>[];
396
+ /**
397
+ * The scenarios this ViewModel listens to, unsubscribed for it on reset.
398
+ *
399
+ * Returning a FACTORY postpones building the list until bind time, which is
400
+ * what a ViewModel declared at module level needs when its entries name their
401
+ * scenarios through the locator. See `TLankaScenarioBindingsDeclaration`.
402
+ */
403
+ protected scenarioHandlers(): TLankaScenarioBindingsDeclaration<TUnknownLankaScenarioBinding<State & Actions, TGateways, Services>>;
326
404
  /** Store middleware — `persist`, `lankaDevtools` — the last one applied outermost. */
327
405
  protected enhancers(): TLankaVMEnhancer<State & Actions & ILankaScenarioVM>[];
328
406
  /** The actions the screen calls. Written against `this.set` and `this.get`. */
@@ -352,6 +430,9 @@ declare abstract class ALankaVM<State extends object, Actions extends object, TG
352
430
  * or a thunk, the class supplies by overriding a method of the same name, with
353
431
  * the two dependency suppliers named `createGateways` and `createServices`
354
432
  * because `gateways` and `services` already name what they answer.
433
+ * `toLifecycleHooks` is protected too and is not one of these: it is how the
434
+ * framework reads `onInit` and `onReset`, and a ViewModel overrides those,
435
+ * never it.
355
436
  *
356
437
  * ```ts
357
438
  * class SessionVM extends ALankaStatelessVM<ISessionActions, ISessionGateways> {
@@ -385,8 +466,13 @@ declare abstract class ALankaStatelessVM<Actions extends object, TGateways exten
385
466
  protected get: () => Actions & ILankaScenarioVM;
386
467
  /** Fires a scenario, which every ViewModel bound to it then hears. */
387
468
  protected trigger: <TData>(scenario: ILankaScenario<TData>, data?: TData) => void;
388
- /** The scenarios this ViewModel listens to, unsubscribed for it on reset. */
389
- protected scenarioHandlers(): ILankaStatelessScenarioBinding<unknown, Actions & ILankaScenarioVM, TGateways, Services>[];
469
+ /**
470
+ * The scenarios this ViewModel listens to, unsubscribed for it on reset.
471
+ *
472
+ * Returning a FACTORY postpones building the list until bind time. See
473
+ * `TLankaScenarioBindingsDeclaration`.
474
+ */
475
+ protected scenarioHandlers(): TLankaScenarioBindingsDeclaration<ILankaStatelessScenarioBinding<unknown, Actions & ILankaScenarioVM, TGateways, Services>>;
390
476
  /** The actions the screen calls. Written against `this.set` and `this.get`. */
391
477
  protected abstract createActions(): Actions;
392
478
  /**
@@ -427,7 +513,9 @@ declare abstract class ALankaStatelessVM<Actions extends object, TGateways exten
427
513
  * implementation: the store, the tracked hook and the two memoised state
428
514
  * references are the factory's, and duplicating them here would be the second
429
515
  * implementation the parity canon exists to prevent. What it gives a class-style
430
- * consumer is the same protected surface under the same names.
516
+ * consumer is the same protected surface under the same names — plus
517
+ * `toLifecycleHooks`, the framework's reading of `onInit`/`onReset`, which is
518
+ * not an extension point.
431
519
  *
432
520
  * Canon: `skills/parity/SKILL.md`.
433
521
  */
@@ -451,7 +539,7 @@ declare abstract class ALankaSharedStoreVM<StoreState extends object, Actions ex
451
539
  */
452
540
  protected readonly enableAccessTrackingOptimization: boolean;
453
541
  /** The scenarios this ViewModel listens to, unsubscribed for it on reset. */
454
- protected scenarioHandlers(): ILankaSharedStoreScenarioBinding<unknown, StoreState, Actions, Store, TGateways, Services>[];
542
+ protected scenarioHandlers(): TLankaScenarioBindingsDeclaration<ILankaSharedStoreScenarioBinding<unknown, StoreState, Actions, Store, TGateways, Services>>;
455
543
  /** The actions the screen calls. Written against `this.set` and `this.get`. */
456
544
  protected abstract createActions(): Actions;
457
545
  /** Builds the hook a screen calls. One ViewModel per call. */
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import {
3
3
  lankaScenarioBootstrap
4
- } from "../chunk-UGXSGQPW.js";
4
+ } from "../chunk-B7EYIAW7.js";
5
5
  import "../chunk-O5ROO7QF.js";
6
6
  import {
7
7
  lankaLogger
@@ -12,7 +12,7 @@ import {
12
12
  import "../chunk-BGVDPDX4.js";
13
13
 
14
14
  // src/viewmodel/_abstractions/lanka-vm-environment/ALankaVMEnvironment.ts
15
- var ALankaVMEnvironment = class {
15
+ var ALankaVMEnvironment = class _ALankaVMEnvironment {
16
16
  /** What `createGateways` answered: the data layer, kept apart from services. */
17
17
  gateways;
18
18
  /** What `createServices` answered: everything that is not a gateway. */
@@ -25,12 +25,44 @@ var ALankaVMEnvironment = class {
25
25
  createServices() {
26
26
  return {};
27
27
  }
28
- /** Runs after the scenarios are bound. */
28
+ /**
29
+ * Runs once the scenarios are bound: inside `startLanka()` for a ViewModel built
30
+ * at module level, inside `build()` for one built after bootstrap — in both
31
+ * cases before any screen has read the hook the build returns.
32
+ */
29
33
  onInit() {
30
34
  }
31
- /** Runs when the screen goes away, before the scenarios are unbound. */
35
+ /**
36
+ * Runs when the ViewModel is released — the framework instance disposed, or a
37
+ * lazy ViewModel's `dispose()` — after its scenario subscriptions are gone.
38
+ */
32
39
  onReset() {
33
40
  }
41
+ /**
42
+ * The two moments above as the scenario binder receives them: present only
43
+ * where this ViewModel took them.
44
+ *
45
+ * Framework plumbing, not an extension point — a ViewModel overrides `onInit`
46
+ * and `onReset`, never this. Protected because the three bases call it, and
47
+ * named after `toStyleContext` for the same reason: a derived view of the
48
+ * protected surface, assembled by the framework.
49
+ *
50
+ * Why "took them" decides anything: `onInit` runs inside `initializeScenario`,
51
+ * which bootstrap alone calls, on the ViewModels registered with it.
52
+ * Registration used to follow scenario bindings only, so a ViewModel that
53
+ * overrode `onInit` and bound nothing was never initialised — silently. Now a
54
+ * declared hook registers the ViewModel too, and a default no-op must not
55
+ * count, or every ViewModel would sit in the scenario registry for nothing.
56
+ * The functional bridges declare theirs as own properties over these methods,
57
+ * which is what this comparison sees.
58
+ */
59
+ toLifecycleHooks() {
60
+ const defaults = _ALankaVMEnvironment.prototype;
61
+ const hooks = {};
62
+ if (this.onInit !== defaults.onInit) hooks.onInit = () => this.onInit();
63
+ if (this.onReset !== defaults.onReset) hooks.onReset = () => this.onReset();
64
+ return hooks;
65
+ }
34
66
  };
35
67
 
36
68
  // src/viewmodel/_abstractions/lanka-vm/ALankaVM.ts
@@ -105,17 +137,20 @@ var createLankaBlindSpotTrap = (viewModelName, isArmed) => {
105
137
  };
106
138
 
107
139
  // src/viewmodel/_internal/create-lanka-scenario-binder/createLankaScenarioBinder.ts
140
+ var readBindings = (declared) => typeof declared === "function" ? declared() : declared ?? [];
141
+ var needsBootstrap = (config) => typeof config.bindings === "function" || config.bindings !== void 0 && config.bindings.length > 0 || config.onInit !== void 0 || config.onReset !== void 0;
108
142
  var createLankaScenarioBinder = (config) => {
109
143
  const subscriptions = /* @__PURE__ */ new Map();
110
144
  let isInitialized = false;
111
145
  return {
146
+ needsBootstrap: needsBootstrap(config),
112
147
  get isInitialized() {
113
148
  return isInitialized;
114
149
  },
115
150
  initializeScenario() {
116
151
  if (isInitialized) return;
117
152
  const context = config.context();
118
- for (const binding of config.bindings ?? []) {
153
+ for (const binding of readBindings(config.bindings)) {
119
154
  const key = binding.scenario.eventType;
120
155
  if (subscriptions.has(key)) continue;
121
156
  subscriptions.set(
@@ -221,7 +256,13 @@ var ALankaVM = class extends ALankaVMEnvironment {
221
256
  states() {
222
257
  return {};
223
258
  }
224
- /** The scenarios this ViewModel listens to, unsubscribed for it on reset. */
259
+ /**
260
+ * The scenarios this ViewModel listens to, unsubscribed for it on reset.
261
+ *
262
+ * Returning a FACTORY postpones building the list until bind time, which is
263
+ * what a ViewModel declared at module level needs when its entries name their
264
+ * scenarios through the locator. See `TLankaScenarioBindingsDeclaration`.
265
+ */
225
266
  scenarioHandlers() {
226
267
  return [];
227
268
  }
@@ -252,24 +293,20 @@ var ALankaVM = class extends ALankaVMEnvironment {
252
293
  this.name,
253
294
  getLankaFlags().isDevelopment === true
254
295
  );
255
- const bindings = this.scenarioHandlers();
296
+ const hooks = this.toLifecycleHooks();
297
+ const { initializeScenario, resetScenario, needsBootstrap: needsBootstrap2 } = createLankaScenarioBinder({
298
+ name: this.name,
299
+ bindings: this.scenarioHandlers(),
300
+ context: () => this.toStyleContext(),
301
+ onInit: hooks.onInit,
302
+ onReset: hooks.onReset
303
+ });
256
304
  const stateCreator = (set, get) => {
257
305
  this.set = set;
258
306
  this.get = blindSpot.observeGet(get);
259
307
  this.gateways = this.createGateways();
260
308
  this.services = this.createServices();
261
309
  const actions = blindSpot.observeActions(this.createActions());
262
- const { initializeScenario, resetScenario } = createLankaScenarioBinder({
263
- name: this.name,
264
- bindings,
265
- context: () => this.toStyleContext(),
266
- onInit: () => {
267
- this.onInit();
268
- },
269
- onReset: () => {
270
- this.onReset();
271
- }
272
- });
273
310
  return {
274
311
  ...this.states(),
275
312
  ...actions,
@@ -283,7 +320,7 @@ var ALankaVM = class extends ALankaVMEnvironment {
283
320
  );
284
321
  const store = create()(enhancedCreator);
285
322
  const registerScenarioViewModel = (viewModel) => {
286
- if (bindings.length > 0) {
323
+ if (needsBootstrap2) {
287
324
  lankaScenarioBootstrap.registerViewModel(viewModel, this.name);
288
325
  }
289
326
  };
@@ -336,11 +373,16 @@ function createLankaVM(config) {
336
373
  createActions() {
337
374
  return config.createActions(this.toStyleContext());
338
375
  }
339
- onInit() {
340
- config.onInit?.(this.toStyleContext());
341
- }
342
- onReset() {
343
- config.onReset?.(this.toStyleContext());
376
+ // The config's hooks become this instance's OWN `onInit`/`onReset`, assigned
377
+ // only when declared. An unconditional method override would make the base
378
+ // read every functional ViewModel as taking both hooks and register it with
379
+ // bootstrap for nothing; an own property is seen by the same test a class
380
+ // override is, and `this.onInit()` means the same thing in both styles.
381
+ constructor() {
382
+ super();
383
+ const { onInit, onReset } = config;
384
+ if (onInit) this.onInit = () => onInit(this.toStyleContext());
385
+ if (onReset) this.onReset = () => onReset(this.toStyleContext());
344
386
  }
345
387
  }
346
388
  return new FunctionalVM().build();
@@ -417,7 +459,12 @@ var ALankaStatelessVM = class extends ALankaVMEnvironment {
417
459
  trigger = (scenario, data) => {
418
460
  scenario.trigger(data);
419
461
  };
420
- /** The scenarios this ViewModel listens to, unsubscribed for it on reset. */
462
+ /**
463
+ * The scenarios this ViewModel listens to, unsubscribed for it on reset.
464
+ *
465
+ * Returning a FACTORY postpones building the list until bind time. See
466
+ * `TLankaScenarioBindingsDeclaration`.
467
+ */
421
468
  scenarioHandlers() {
422
469
  return [];
423
470
  }
@@ -448,20 +495,16 @@ var ALankaStatelessVM = class extends ALankaVMEnvironment {
448
495
  this.gateways = this.createGateways();
449
496
  this.services = this.createServices();
450
497
  const actions = this.createActions();
451
- const bindings = this.scenarioHandlers();
452
- const { initializeScenario, resetScenario } = createLankaScenarioBinder({
498
+ const hooks = this.toLifecycleHooks();
499
+ const { initializeScenario, resetScenario, needsBootstrap: needsBootstrap2 } = createLankaScenarioBinder({
453
500
  name: this.name,
454
- bindings,
501
+ bindings: this.scenarioHandlers(),
455
502
  context: () => this.toStyleContext(),
456
- onInit: () => {
457
- this.onInit();
458
- },
459
- onReset: () => {
460
- this.onReset();
461
- }
503
+ onInit: hooks.onInit,
504
+ onReset: hooks.onReset
462
505
  });
463
506
  state = { ...state, ...actions, initializeScenario, resetScenario };
464
- if (bindings.length > 0) {
507
+ if (needsBootstrap2) {
465
508
  lankaScenarioBootstrap.registerViewModel(state, this.name);
466
509
  }
467
510
  const useStatelessViewModel = ((selector) => selector ? selector(state) : state);
@@ -487,11 +530,13 @@ function createStatelessLankaVM(config) {
487
530
  createActions() {
488
531
  return config.createActions(this.toStyleContext());
489
532
  }
490
- onInit() {
491
- config.onInit?.(this.toStyleContext());
492
- }
493
- onReset() {
494
- config.onReset?.(this.toStyleContext());
533
+ // Own properties, assigned only when declared — see the same constructor in
534
+ // `createLankaVM` for why not a method override.
535
+ constructor() {
536
+ super();
537
+ const { onInit, onReset } = config;
538
+ if (onInit) this.onInit = () => onInit(this.toStyleContext());
539
+ if (onReset) this.onReset = () => onReset(this.toStyleContext());
495
540
  }
496
541
  }
497
542
  return new FunctionalStatelessVM().build();
@@ -538,7 +583,7 @@ function createSharedStoreLankaVM(config) {
538
583
  services,
539
584
  trigger: (scenario, data) => scenario.trigger(data)
540
585
  };
541
- const { initializeScenario, resetScenario } = createLankaScenarioBinder({
586
+ const { initializeScenario, resetScenario, needsBootstrap: needsBootstrap2 } = createLankaScenarioBinder({
542
587
  name: config.name,
543
588
  bindings: config.scenarioHandlers,
544
589
  context: () => ctx,
@@ -565,7 +610,7 @@ function createSharedStoreLankaVM(config) {
565
610
  const useSharedStoreViewModel = isAccessTrackingEnabled ? useTrackedSharedStoreViewModel : useUntrackedSharedStoreViewModel;
566
611
  useSharedStoreViewModel.getState = () => getFullState();
567
612
  useSharedStoreViewModel.getStoreState = () => config.store.getState();
568
- if (config.scenarioHandlers && config.scenarioHandlers.length > 0) {
613
+ if (needsBootstrap2) {
569
614
  const scenarioViewModel = {
570
615
  initializeScenario,
571
616
  resetScenario
@@ -612,6 +657,7 @@ var ALankaSharedStoreVM = class extends ALankaVMEnvironment {
612
657
  }
613
658
  /** Builds the hook a screen calls. One ViewModel per call. */
614
659
  build() {
660
+ const hooks = this.toLifecycleHooks();
615
661
  return createSharedStoreLankaVM({
616
662
  name: this.name,
617
663
  store: this.store,
@@ -628,12 +674,8 @@ var ALankaSharedStoreVM = class extends ALankaVMEnvironment {
628
674
  this.trigger = context.trigger;
629
675
  return this.createActions();
630
676
  },
631
- onInit: () => {
632
- this.onInit();
633
- },
634
- onReset: () => {
635
- this.onReset();
636
- }
677
+ onInit: hooks.onInit,
678
+ onReset: hooks.onReset
637
679
  });
638
680
  }
639
681
  };