lanka 1.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 (97) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +242 -0
  3. package/dist/ALankaGateway-ExlRGT3D.d.ts +122 -0
  4. package/dist/ALankaSharedStore-B7uepuuk.d.ts +29 -0
  5. package/dist/ILankaApiError-zI_2bNs0.d.ts +16 -0
  6. package/dist/ILankaRuntimeConfig-Vl436GWK.d.ts +62 -0
  7. package/dist/ILankaScenarioMetadata-Bu-yggTZ.d.ts +14 -0
  8. package/dist/ILankaScenarioVM-DuCyPoyT.d.ts +260 -0
  9. package/dist/LankaError-B1HtuIkw.d.ts +86 -0
  10. package/dist/LankaScenarioLocator-BGQHwf3n.d.ts +144 -0
  11. package/dist/LankaSharedStoreLocator-MvCpav5F.d.ts +126 -0
  12. package/dist/TLankaErrorHandler-Yfqtdh1M.d.ts +3 -0
  13. package/dist/_extend/index.d.ts +34 -0
  14. package/dist/_extend/index.js +36 -0
  15. package/dist/_extend/index.js.map +1 -0
  16. package/dist/_internal/index.d.ts +43 -0
  17. package/dist/_internal/index.js +34 -0
  18. package/dist/_internal/index.js.map +1 -0
  19. package/dist/activeRuntime-FcsSJvUg.d.ts +84 -0
  20. package/dist/bootstrap/index.d.ts +242 -0
  21. package/dist/bootstrap/index.js +22 -0
  22. package/dist/bootstrap/index.js.map +1 -0
  23. package/dist/chunk-73IVH3C6.js +52 -0
  24. package/dist/chunk-73IVH3C6.js.map +1 -0
  25. package/dist/chunk-BGVDPDX4.js +29 -0
  26. package/dist/chunk-BGVDPDX4.js.map +1 -0
  27. package/dist/chunk-C2HP7CRD.js +224 -0
  28. package/dist/chunk-C2HP7CRD.js.map +1 -0
  29. package/dist/chunk-D27MREPB.js +13 -0
  30. package/dist/chunk-D27MREPB.js.map +1 -0
  31. package/dist/chunk-DTO27QFR.js +59 -0
  32. package/dist/chunk-DTO27QFR.js.map +1 -0
  33. package/dist/chunk-EWVDJYCC.js +296 -0
  34. package/dist/chunk-EWVDJYCC.js.map +1 -0
  35. package/dist/chunk-FIR4XTBL.js +136 -0
  36. package/dist/chunk-FIR4XTBL.js.map +1 -0
  37. package/dist/chunk-ILQNYQY5.js +200 -0
  38. package/dist/chunk-ILQNYQY5.js.map +1 -0
  39. package/dist/chunk-J5LA3Q6D.js +14 -0
  40. package/dist/chunk-J5LA3Q6D.js.map +1 -0
  41. package/dist/chunk-MDZ42XRH.js +10 -0
  42. package/dist/chunk-MDZ42XRH.js.map +1 -0
  43. package/dist/chunk-Q3SOVBIJ.js +176 -0
  44. package/dist/chunk-Q3SOVBIJ.js.map +1 -0
  45. package/dist/chunk-Q7QESSYF.js +49 -0
  46. package/dist/chunk-Q7QESSYF.js.map +1 -0
  47. package/dist/chunk-RKYKK6MN.js +19 -0
  48. package/dist/chunk-RKYKK6MN.js.map +1 -0
  49. package/dist/chunk-RYFZCAQ3.js +637 -0
  50. package/dist/chunk-RYFZCAQ3.js.map +1 -0
  51. package/dist/chunk-UJEC7H6K.js +39 -0
  52. package/dist/chunk-UJEC7H6K.js.map +1 -0
  53. package/dist/chunk-XESL274R.js +23 -0
  54. package/dist/chunk-XESL274R.js.map +1 -0
  55. package/dist/chunk-YR4MZXMU.js +13 -0
  56. package/dist/chunk-YR4MZXMU.js.map +1 -0
  57. package/dist/config/index.d.ts +63 -0
  58. package/dist/config/index.js +16 -0
  59. package/dist/config/index.js.map +1 -0
  60. package/dist/createLankaScope-Bc_vChRs.d.ts +30 -0
  61. package/dist/errors/index.d.ts +37 -0
  62. package/dist/errors/index.js +16 -0
  63. package/dist/errors/index.js.map +1 -0
  64. package/dist/gateway/index.d.ts +258 -0
  65. package/dist/gateway/index.js +438 -0
  66. package/dist/gateway/index.js.map +1 -0
  67. package/dist/index.d.ts +20 -0
  68. package/dist/index.js +43 -0
  69. package/dist/index.js.map +1 -0
  70. package/dist/lankaHttpInFlight-Bk1eIuSx.d.ts +43 -0
  71. package/dist/lankaRequestMiddleware-DAC5kCb7.d.ts +62 -0
  72. package/dist/lankaStandardValidator-CL-r-zEV.d.ts +34 -0
  73. package/dist/locator/index.d.ts +282 -0
  74. package/dist/locator/index.js +67 -0
  75. package/dist/locator/index.js.map +1 -0
  76. package/dist/logger/index.d.ts +146 -0
  77. package/dist/logger/index.js +11 -0
  78. package/dist/logger/index.js.map +1 -0
  79. package/dist/mock/index.d.ts +16 -0
  80. package/dist/mock/index.js +58 -0
  81. package/dist/mock/index.js.map +1 -0
  82. package/dist/role/index.d.ts +48 -0
  83. package/dist/role/index.js +7 -0
  84. package/dist/role/index.js.map +1 -0
  85. package/dist/scenario/index.d.ts +204 -0
  86. package/dist/scenario/index.js +28 -0
  87. package/dist/scenario/index.js.map +1 -0
  88. package/dist/validation/index.d.ts +18 -0
  89. package/dist/validation/index.js +53 -0
  90. package/dist/validation/index.js.map +1 -0
  91. package/dist/viewmodel/index.d.ts +478 -0
  92. package/dist/viewmodel/index.js +701 -0
  93. package/dist/viewmodel/index.js.map +1 -0
  94. package/package.json +99 -0
  95. package/skills/lanka-core/SKILL.md +241 -0
  96. package/skills/lanka-core/reference.md +1090 -0
  97. package/skills/lanka-packages/SKILL.md +70 -0
@@ -0,0 +1,701 @@
1
+ "use client";
2
+ import {
3
+ lankaScenarioBootstrap
4
+ } from "../chunk-Q3SOVBIJ.js";
5
+ import "../chunk-EWVDJYCC.js";
6
+ import {
7
+ lankaLogger
8
+ } from "../chunk-C2HP7CRD.js";
9
+ import {
10
+ getLankaFlags
11
+ } from "../chunk-D27MREPB.js";
12
+ import "../chunk-BGVDPDX4.js";
13
+
14
+ // src/viewmodel/_abstractions/lanka-vm-environment/ALankaVMEnvironment.ts
15
+ var ALankaVMEnvironment = class {
16
+ /** What `createGateways` answered: the data layer, kept apart from services. */
17
+ gateways;
18
+ /** What `createServices` answered: everything that is not a gateway. */
19
+ services;
20
+ /** The data layer, built once per ViewModel. */
21
+ createGateways() {
22
+ return {};
23
+ }
24
+ /** Non-gateway collaborators, built once per ViewModel. */
25
+ createServices() {
26
+ return {};
27
+ }
28
+ /** Runs after the scenarios are bound. */
29
+ onInit() {
30
+ }
31
+ /** Runs when the screen goes away, before the scenarios are unbound. */
32
+ onReset() {
33
+ }
34
+ };
35
+
36
+ // src/viewmodel/_abstractions/lanka-vm/ALankaVM.ts
37
+ import { create } from "zustand";
38
+
39
+ // src/viewmodel/_internal/create-lanka-blind-spot-trap/createLankaBlindSpotTrap.ts
40
+ var createLankaBlindSpotTrap = (viewModelName, isArmed) => {
41
+ if (!isArmed) {
42
+ return {
43
+ isArmed: false,
44
+ observeGet: (get) => get,
45
+ observeActions: (declared) => declared,
46
+ report: () => void 0
47
+ };
48
+ }
49
+ const indirectReads = /* @__PURE__ */ new Map();
50
+ const warnedKeys = /* @__PURE__ */ new Set();
51
+ let activeActionName = null;
52
+ return {
53
+ isArmed: true,
54
+ observeGet(get) {
55
+ return () => {
56
+ const state = get();
57
+ const actionName = activeActionName;
58
+ if (actionName === null) return state;
59
+ return new Proxy(state, {
60
+ get(target, prop, receiver) {
61
+ if (typeof prop === "string") {
62
+ let keys = indirectReads.get(actionName);
63
+ if (!keys) {
64
+ keys = /* @__PURE__ */ new Set();
65
+ indirectReads.set(actionName, keys);
66
+ }
67
+ keys.add(prop);
68
+ }
69
+ return Reflect.get(target, prop, receiver);
70
+ }
71
+ });
72
+ };
73
+ },
74
+ observeActions(declared) {
75
+ const observed = { ...declared };
76
+ for (const [name, value] of Object.entries(declared)) {
77
+ if (typeof value !== "function") continue;
78
+ observed[name] = (...args) => {
79
+ const previous = activeActionName;
80
+ activeActionName = name;
81
+ try {
82
+ return value(...args);
83
+ } finally {
84
+ activeActionName = previous;
85
+ }
86
+ };
87
+ }
88
+ return observed;
89
+ },
90
+ report(trackedKeys, next, prev) {
91
+ for (const [changedKey, value] of Object.entries(next)) {
92
+ if (Object.is(value, prev[changedKey])) continue;
93
+ if (trackedKeys.has(changedKey) || warnedKeys.has(changedKey)) continue;
94
+ const viaGetter = [...trackedKeys].some(
95
+ (key) => indirectReads.get(key)?.has(changedKey)
96
+ );
97
+ if (!viaGetter) continue;
98
+ warnedKeys.add(changedKey);
99
+ console.warn(
100
+ `[lanka] ${viewModelName}: key "${changedKey}" changed but no re-render will follow. The component reads it only through a getter, and tracking sees direct proxy reads only. Set enableAccessTrackingOptimization: false on this ViewModel \u2014 destructuring the key in the view "for the side effect" reads as dead code and will not survive a refactor.`
101
+ );
102
+ }
103
+ }
104
+ };
105
+ };
106
+
107
+ // src/viewmodel/_internal/create-lanka-scenario-binder/createLankaScenarioBinder.ts
108
+ var createLankaScenarioBinder = (config) => {
109
+ const subscriptions = /* @__PURE__ */ new Map();
110
+ let isInitialized = false;
111
+ return {
112
+ get isInitialized() {
113
+ return isInitialized;
114
+ },
115
+ initializeScenario() {
116
+ if (isInitialized) return;
117
+ const context = config.context();
118
+ for (const binding of config.bindings ?? []) {
119
+ const key = binding.scenario.eventType;
120
+ if (subscriptions.has(key)) continue;
121
+ subscriptions.set(
122
+ key,
123
+ binding.scenario.subscribe(binding.handler(context), {
124
+ usedBy: config.name,
125
+ ...binding.options
126
+ })
127
+ );
128
+ }
129
+ config.onInit?.(context);
130
+ isInitialized = true;
131
+ },
132
+ resetScenario() {
133
+ for (const release of subscriptions.values()) release();
134
+ subscriptions.clear();
135
+ isInitialized = false;
136
+ config.onReset?.(config.context());
137
+ }
138
+ };
139
+ };
140
+
141
+ // src/viewmodel/_internal/create-lanka-tracked-hook/createLankaTrackedHook.ts
142
+ import { useCallback, useRef, useSyncExternalStore } from "react";
143
+ var asRecord = (state) => state;
144
+ var createLankaTrackedHook = (config) => {
145
+ return (selector) => {
146
+ const selectorRef = useRef(selector);
147
+ selectorRef.current = selector;
148
+ const trackedKeysRef = useRef(/* @__PURE__ */ new Set());
149
+ const trackedStateRef = useRef(null);
150
+ const trackedProxyRef = useRef(null);
151
+ const subscribe = useCallback(
152
+ (onStoreChange) => config.subscribe((nextState, prevState) => {
153
+ if (selectorRef.current) {
154
+ onStoreChange();
155
+ return;
156
+ }
157
+ const trackedKeys = trackedKeysRef.current;
158
+ if (trackedKeys.size === 0) {
159
+ onStoreChange();
160
+ return;
161
+ }
162
+ const next = asRecord(nextState);
163
+ const prev = asRecord(prevState);
164
+ for (const key of trackedKeys) {
165
+ if (!Object.is(next[key], prev[key])) {
166
+ onStoreChange();
167
+ return;
168
+ }
169
+ }
170
+ config.onUntrackedChange?.(trackedKeys, next, prev);
171
+ }),
172
+ []
173
+ );
174
+ return useSyncExternalStore(subscribe, readTracked, readUntracked);
175
+ function readTracked() {
176
+ const state = config.readState();
177
+ const activeSelector = selectorRef.current;
178
+ if (activeSelector) return activeSelector(state);
179
+ if (trackedStateRef.current === state && trackedProxyRef.current) {
180
+ return trackedProxyRef.current;
181
+ }
182
+ const trackedKeys = /* @__PURE__ */ new Set();
183
+ const proxyState = new Proxy(state, {
184
+ get(target, prop, receiver) {
185
+ if (typeof prop === "string") {
186
+ trackedKeys.add(prop);
187
+ }
188
+ return Reflect.get(target, prop, receiver);
189
+ }
190
+ });
191
+ trackedKeysRef.current = trackedKeys;
192
+ trackedStateRef.current = state;
193
+ trackedProxyRef.current = proxyState;
194
+ return proxyState;
195
+ }
196
+ function readUntracked() {
197
+ const state = config.readState();
198
+ const activeSelector = selectorRef.current;
199
+ return activeSelector ? activeSelector(state) : state;
200
+ }
201
+ };
202
+ };
203
+
204
+ // src/viewmodel/_abstractions/lanka-vm/ALankaVM.ts
205
+ var ALankaVM = class extends ALankaVMEnvironment {
206
+ /**
207
+ * Turn off when one broad consumer reads most fields, or when an action derives
208
+ * what the screen shows — proxy tracking then costs more than it saves, and in
209
+ * the second case it cannot see the read at all.
210
+ */
211
+ enableAccessTrackingOptimization = true;
212
+ /** Writes state. Available from `createActions` onwards, never before. */
213
+ set;
214
+ /** Reads state. The read a tracked hook cannot see — hence the switch above. */
215
+ get;
216
+ /** Fires a scenario, which every ViewModel bound to it then hears. */
217
+ trigger = (scenario, data) => {
218
+ scenario.trigger(data);
219
+ };
220
+ /** The reactive fields the screen reads. */
221
+ states() {
222
+ return {};
223
+ }
224
+ /** The scenarios this ViewModel listens to, unsubscribed for it on reset. */
225
+ scenarioHandlers() {
226
+ return [];
227
+ }
228
+ /** Store middleware — `persist`, `lankaDevtools` — the last one applied outermost. */
229
+ enhancers() {
230
+ return [];
231
+ }
232
+ /**
233
+ * The protected surface as an object, for the functional style.
234
+ *
235
+ * Assembled INSIDE the class because that is the only place `protected` can be
236
+ * read — a context built from outside could carry only the public half, which is
237
+ * the wrong half. Canon: `skills/parity/SKILL.md` section 3a.
238
+ */
239
+ toStyleContext() {
240
+ return {
241
+ set: this.set,
242
+ get: this.get,
243
+ gateways: this.gateways,
244
+ services: this.services,
245
+ trigger: this.trigger
246
+ };
247
+ }
248
+ /** Builds the hook a screen calls. One store per call. */
249
+ build() {
250
+ lankaLogger.printViewModelLog("START Create VM", this.name);
251
+ const blindSpot = createLankaBlindSpotTrap(
252
+ this.name,
253
+ getLankaFlags().isDevelopment === true
254
+ );
255
+ const bindings = this.scenarioHandlers();
256
+ const stateCreator = (set, get) => {
257
+ this.set = set;
258
+ this.get = blindSpot.observeGet(get);
259
+ this.gateways = this.createGateways();
260
+ this.services = this.createServices();
261
+ 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
+ return {
274
+ ...this.states(),
275
+ ...actions,
276
+ initializeScenario,
277
+ resetScenario
278
+ };
279
+ };
280
+ const enhancedCreator = this.enhancers().reduce(
281
+ (creator, enhance) => enhance(creator),
282
+ stateCreator
283
+ );
284
+ const store = create()(enhancedCreator);
285
+ const registerScenarioViewModel = (viewModel) => {
286
+ if (bindings.length > 0) {
287
+ lankaScenarioBootstrap.registerViewModel(viewModel, this.name);
288
+ }
289
+ };
290
+ if (!this.enableAccessTrackingOptimization) {
291
+ registerScenarioViewModel(store.getState());
292
+ lankaLogger.printViewModelLog("FINISH Create VM", this.name);
293
+ return store;
294
+ }
295
+ const useOptimizedViewModel = createLankaTrackedHook({
296
+ subscribe: (onChange) => store.subscribe(onChange),
297
+ readState: () => store.getState(),
298
+ onUntrackedChange: blindSpot.isArmed ? blindSpot.report : void 0
299
+ });
300
+ useOptimizedViewModel.setState = store.setState;
301
+ useOptimizedViewModel.getState = store.getState;
302
+ useOptimizedViewModel.getInitialState = store.getInitialState;
303
+ useOptimizedViewModel.subscribe = store.subscribe;
304
+ registerScenarioViewModel(useOptimizedViewModel.getState());
305
+ lankaLogger.printViewModelLog("FINISH Create VM", this.name);
306
+ return useOptimizedViewModel;
307
+ }
308
+ };
309
+
310
+ // src/viewmodel/_utils/resolve-lanka-dependency/resolveLankaDependency.ts
311
+ var resolveLankaDependency = (declared) => {
312
+ if (typeof declared === "function") return declared();
313
+ return declared ?? {};
314
+ };
315
+
316
+ // src/viewmodel/_factories/create-lanka-vm/createLankaVM.ts
317
+ function createLankaVM(config) {
318
+ class FunctionalVM extends ALankaVM {
319
+ name = config.name;
320
+ enableAccessTrackingOptimization = config.enableAccessTrackingOptimization ?? true;
321
+ states() {
322
+ return config.states ?? {};
323
+ }
324
+ scenarioHandlers() {
325
+ return config.scenarioHandlers ?? [];
326
+ }
327
+ enhancers() {
328
+ return config.enhancers ?? [];
329
+ }
330
+ createGateways() {
331
+ return resolveLankaDependency(config.gateways);
332
+ }
333
+ createServices() {
334
+ return resolveLankaDependency(config.services);
335
+ }
336
+ createActions() {
337
+ return config.createActions(this.toStyleContext());
338
+ }
339
+ onInit() {
340
+ config.onInit?.(this.toStyleContext());
341
+ }
342
+ onReset() {
343
+ config.onReset?.(this.toStyleContext());
344
+ }
345
+ }
346
+ return new FunctionalVM().build();
347
+ }
348
+
349
+ // src/viewmodel/_internal/create-lazy-lanka-hook/createLazyLankaHook.ts
350
+ var lazySlot = (config) => {
351
+ let store = null;
352
+ return {
353
+ get: () => {
354
+ if (!store) {
355
+ lankaLogger.printViewModelLog(
356
+ `LAZY: Creating ${config.kind} on first access`,
357
+ config.name
358
+ );
359
+ store = config.create();
360
+ }
361
+ return store;
362
+ },
363
+ /**
364
+ * Releases the store. The next access builds a new one.
365
+ *
366
+ * Scenario subscriptions go first, through `resetScenario` — the same path
367
+ * the between-tests reset uses. Dropping the reference without
368
+ * unsubscribing would leave the bus holding handlers that write into a
369
+ * discarded store.
370
+ *
371
+ * On a slot that never built, this does NOTHING: building a store in order
372
+ * to destroy it is work with no result, and it would resurrect a ViewModel
373
+ * a closed screen had just released.
374
+ */
375
+ release: () => {
376
+ if (!store) return;
377
+ store.getState().resetScenario();
378
+ store = null;
379
+ }
380
+ };
381
+ };
382
+ var forwardEveryMember = (slot) => (_target, property) => {
383
+ if (property === "dispose") return slot.release;
384
+ return (...args) => {
385
+ const built = slot.get();
386
+ const member = built[property];
387
+ return typeof member === "function" ? member.apply(built, args) : member;
388
+ };
389
+ };
390
+ var createLazyLankaHook = (config) => {
391
+ const slot = lazySlot(config);
392
+ const hook = (selector) => {
393
+ const built = slot.get();
394
+ return selector ? built(selector) : built();
395
+ };
396
+ return new Proxy(hook, { get: forwardEveryMember(slot) });
397
+ };
398
+
399
+ // src/viewmodel/_factories/create-lazy-lanka-vm/createLazyLankaVM.ts
400
+ function createLazyLankaVM(config) {
401
+ return createLazyLankaHook({
402
+ name: config.name,
403
+ kind: "VM",
404
+ create: () => createLankaVM(config)
405
+ });
406
+ }
407
+
408
+ // src/viewmodel/_abstractions/lanka-stateless-vm/ALankaStatelessVM.ts
409
+ var ALankaStatelessVM = class extends ALankaVMEnvironment {
410
+ /** Writes state. Available from `createActions` onwards, never before. */
411
+ set;
412
+ /** Reads what the actions have written, including the actions themselves. */
413
+ get;
414
+ /** Fires a scenario, which every ViewModel bound to it then hears. */
415
+ trigger = (scenario, data) => {
416
+ scenario.trigger(data);
417
+ };
418
+ /** The scenarios this ViewModel listens to, unsubscribed for it on reset. */
419
+ scenarioHandlers() {
420
+ return [];
421
+ }
422
+ /**
423
+ * The protected surface as an object, for the functional style.
424
+ *
425
+ * Assembled INSIDE the class because that is the only place `protected` can be
426
+ * read. Canon: `skills/parity/SKILL.md` section 3a.
427
+ */
428
+ toStyleContext() {
429
+ return {
430
+ set: this.set,
431
+ get: this.get,
432
+ gateways: this.gateways,
433
+ services: this.services,
434
+ trigger: this.trigger
435
+ };
436
+ }
437
+ /** Builds the hook a screen calls. One ViewModel per call. */
438
+ build() {
439
+ lankaLogger.printViewModelLog("START Create slVM", this.name);
440
+ let state = {};
441
+ this.get = () => state;
442
+ this.set = (partial, replace) => {
443
+ const next = typeof partial === "function" ? partial(state) : partial;
444
+ state = replace ? next : { ...state, ...next };
445
+ };
446
+ this.gateways = this.createGateways();
447
+ this.services = this.createServices();
448
+ const actions = this.createActions();
449
+ const bindings = this.scenarioHandlers();
450
+ const { initializeScenario, resetScenario } = createLankaScenarioBinder({
451
+ name: this.name,
452
+ bindings,
453
+ context: () => this.toStyleContext(),
454
+ onInit: () => {
455
+ this.onInit();
456
+ },
457
+ onReset: () => {
458
+ this.onReset();
459
+ }
460
+ });
461
+ state = { ...state, ...actions, initializeScenario, resetScenario };
462
+ if (bindings.length > 0) {
463
+ lankaScenarioBootstrap.registerViewModel(state, this.name);
464
+ }
465
+ const useStatelessViewModel = ((selector) => selector ? selector(state) : state);
466
+ useStatelessViewModel.getState = () => state;
467
+ lankaLogger.printViewModelLog("FINISH Create slVM", this.name);
468
+ return useStatelessViewModel;
469
+ }
470
+ };
471
+
472
+ // src/viewmodel/_factories/create-stateless-lanka-vm/createStatelessLankaVM.ts
473
+ function createStatelessLankaVM(config) {
474
+ class FunctionalStatelessVM extends ALankaStatelessVM {
475
+ name = config.name;
476
+ scenarioHandlers() {
477
+ return config.scenarioHandlers ?? [];
478
+ }
479
+ createGateways() {
480
+ return resolveLankaDependency(config.gateways);
481
+ }
482
+ createServices() {
483
+ return resolveLankaDependency(config.services);
484
+ }
485
+ createActions() {
486
+ return config.createActions(this.toStyleContext());
487
+ }
488
+ onInit() {
489
+ config.onInit?.(this.toStyleContext());
490
+ }
491
+ onReset() {
492
+ config.onReset?.(this.toStyleContext());
493
+ }
494
+ }
495
+ return new FunctionalStatelessVM().build();
496
+ }
497
+
498
+ // src/viewmodel/_factories/create-lazy-stateless-lanka-vm/createLazyStatelessLankaVM.ts
499
+ function createLazyStatelessLankaVM(config) {
500
+ return createLazyLankaHook({
501
+ name: config.name,
502
+ kind: "slVM",
503
+ create: () => createStatelessLankaVM(config)
504
+ });
505
+ }
506
+
507
+ // src/viewmodel/_factories/create-shared-store-lanka-vm/createSharedStoreLankaVM.ts
508
+ import { useStore } from "zustand";
509
+ function createSharedStoreLankaVM(config) {
510
+ lankaLogger.printViewModelLog("START Create ssVM", config.name);
511
+ let actions = {};
512
+ const gateways = resolveLankaDependency(config.gateways);
513
+ const services = resolveLankaDependency(config.services);
514
+ let lastStoreStateRef = null;
515
+ let lastFullStateRef = null;
516
+ const buildFullState = (storeState) => {
517
+ if (lastStoreStateRef === storeState && lastFullStateRef) {
518
+ return lastFullStateRef;
519
+ }
520
+ lastStoreStateRef = storeState;
521
+ lastFullStateRef = {
522
+ ...storeState,
523
+ ...actions,
524
+ initializeScenario,
525
+ resetScenario
526
+ };
527
+ return lastFullStateRef;
528
+ };
529
+ const getFullState = () => buildFullState(config.store.getState());
530
+ const ctx = {
531
+ set: (partial, replace) => config.store.setState(partial, replace),
532
+ getStore: () => config.store.getState(),
533
+ get: () => getFullState(),
534
+ store: config.store,
535
+ gateways,
536
+ services,
537
+ trigger: (scenario, data) => scenario.trigger(data)
538
+ };
539
+ const { initializeScenario, resetScenario } = createLankaScenarioBinder({
540
+ name: config.name,
541
+ bindings: config.scenarioHandlers,
542
+ context: () => ctx,
543
+ onInit: config.onInit,
544
+ onReset: config.onReset
545
+ });
546
+ actions = config.createActions(ctx);
547
+ lastStoreStateRef = null;
548
+ lastFullStateRef = null;
549
+ const isAccessTrackingEnabled = config.enableAccessTrackingOptimization ?? true;
550
+ const useTrackedSharedStoreViewModel = createLankaTrackedHook({
551
+ subscribe: (onChange) => config.store.subscribe((storeState, prevStoreState) => {
552
+ onChange(buildFullState(storeState), buildFullState(prevStoreState));
553
+ }),
554
+ readState: getFullState
555
+ });
556
+ const useUntrackedSharedStoreViewModel = ((selector) => useStore(config.store.getApi(), (storeState) => {
557
+ const fullState = buildFullState(storeState);
558
+ if (selector) {
559
+ return selector(fullState);
560
+ }
561
+ return fullState;
562
+ }));
563
+ const useSharedStoreViewModel = isAccessTrackingEnabled ? useTrackedSharedStoreViewModel : useUntrackedSharedStoreViewModel;
564
+ useSharedStoreViewModel.getState = () => getFullState();
565
+ useSharedStoreViewModel.getStoreState = () => config.store.getState();
566
+ if (config.scenarioHandlers && config.scenarioHandlers.length > 0) {
567
+ const scenarioViewModel = {
568
+ initializeScenario,
569
+ resetScenario
570
+ };
571
+ lankaScenarioBootstrap.registerViewModel(scenarioViewModel, config.name);
572
+ }
573
+ lankaLogger.printViewModelLog("FINISH Create ssVM", config.name);
574
+ return useSharedStoreViewModel;
575
+ }
576
+
577
+ // src/viewmodel/_factories/create-lazy-shared-store-lanka-vm/createLazySharedStoreLankaVM.ts
578
+ function createLazySharedStoreLankaVM(config) {
579
+ return createLazyLankaHook({
580
+ name: config.name,
581
+ kind: "ssVM",
582
+ create: () => createSharedStoreLankaVM(config)
583
+ });
584
+ }
585
+
586
+ // src/viewmodel/_abstractions/lanka-shared-store-vm/ALankaSharedStoreVM.ts
587
+ var ALankaSharedStoreVM = class extends ALankaVMEnvironment {
588
+ /** The store this ViewModel and its siblings share. */
589
+ store;
590
+ /** Writes into the shared store, which every reader of it hears about. */
591
+ set;
592
+ /** Reads the store's own state, without this ViewModel's actions on top. */
593
+ getStore;
594
+ /** Reads the store's state WITH the actions, which is what a screen sees. */
595
+ get;
596
+ /** Fires a scenario, which every ViewModel bound to it then hears. */
597
+ trigger;
598
+ constructor(store) {
599
+ super();
600
+ this.store = store;
601
+ }
602
+ /**
603
+ * Turn off when one broad consumer reads most fields of the store — proxy
604
+ * tracking then costs more than it saves.
605
+ */
606
+ enableAccessTrackingOptimization = true;
607
+ /** The scenarios this ViewModel listens to, unsubscribed for it on reset. */
608
+ scenarioHandlers() {
609
+ return [];
610
+ }
611
+ /** Builds the hook a screen calls. One ViewModel per call. */
612
+ build() {
613
+ return createSharedStoreLankaVM({
614
+ name: this.name,
615
+ store: this.store,
616
+ enableAccessTrackingOptimization: this.enableAccessTrackingOptimization,
617
+ gateways: () => this.createGateways(),
618
+ services: () => this.createServices(),
619
+ scenarioHandlers: this.scenarioHandlers(),
620
+ createActions: (context) => {
621
+ this.set = context.set;
622
+ this.getStore = context.getStore;
623
+ this.get = context.get;
624
+ this.gateways = context.gateways;
625
+ this.services = context.services;
626
+ this.trigger = context.trigger;
627
+ return this.createActions();
628
+ },
629
+ onInit: () => {
630
+ this.onInit();
631
+ },
632
+ onReset: () => {
633
+ this.onReset();
634
+ }
635
+ });
636
+ }
637
+ };
638
+
639
+ // src/viewmodel/_abstractions/lanka-shared-store/ALankaSharedStore.ts
640
+ import { createStore } from "zustand/vanilla";
641
+ var ALankaSharedStore = class {
642
+ api;
643
+ createInitialState;
644
+ constructor(createInitialState) {
645
+ this.createInitialState = createInitialState;
646
+ this.api = createStore()(() => this.createInitialState());
647
+ }
648
+ getApi() {
649
+ return this.api;
650
+ }
651
+ getState() {
652
+ return this.api.getState();
653
+ }
654
+ setState(partial, replace) {
655
+ const resolved = typeof partial === "function" ? partial(this.api.getState()) : partial;
656
+ if (replace) {
657
+ this.api.setState(resolved, true);
658
+ return;
659
+ }
660
+ this.api.setState(resolved);
661
+ }
662
+ subscribe(listener) {
663
+ return this.api.subscribe(listener);
664
+ }
665
+ /**
666
+ * Back to what the store was built with.
667
+ *
668
+ * Public, and it was not: while this was `protected` only a subclass could
669
+ * reset, so the class style had a capability the functional one could not
670
+ * reach — the asymmetry `skills/parity/SKILL.md` forbids. A shared store needs
671
+ * explicit reset points (see the ladder in `core/README.md`), and the
672
+ * application that owns them is outside the class either way.
673
+ */
674
+ reset() {
675
+ this.api.setState(this.createInitialState(), true);
676
+ }
677
+ };
678
+
679
+ // src/viewmodel/_factories/create-lanka-shared-store/createLankaSharedStore.ts
680
+ var createLankaSharedStore = (createInitialState) => {
681
+ class FunctionalSharedStore extends ALankaSharedStore {
682
+ constructor() {
683
+ super(createInitialState);
684
+ }
685
+ }
686
+ return new FunctionalSharedStore();
687
+ };
688
+ export {
689
+ ALankaSharedStore,
690
+ ALankaSharedStoreVM,
691
+ ALankaStatelessVM,
692
+ ALankaVM,
693
+ createLankaSharedStore,
694
+ createLankaVM,
695
+ createLazyLankaVM,
696
+ createLazySharedStoreLankaVM,
697
+ createLazyStatelessLankaVM,
698
+ createSharedStoreLankaVM,
699
+ createStatelessLankaVM
700
+ };
701
+ //# sourceMappingURL=index.js.map