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
@@ -0,0 +1,120 @@
1
+ /**
2
+ * The half every engine has: four operations over string values.
3
+ *
4
+ * The clauses each one keeps are on `ILankaStorageAdapter`, together with the
5
+ * ones about how the two halves agree.
6
+ */
7
+ interface ILankaAsyncStorageAdapter {
8
+ setItem(key: string, value: string): Promise<void>;
9
+ getItem(key: string): Promise<string | null>;
10
+ removeItem(key: string): Promise<void>;
11
+ clear(): Promise<void>;
12
+ /**
13
+ * Every key this store holds, when it can say.
14
+ *
15
+ * OPTIONAL, because a store need not be able to: a Cache Storage answers
16
+ * `Request` objects rather than the keys it was given, and a native bridge
17
+ * may answer nothing at all. A required member here would be a compile error
18
+ * in every adapter a consumer had already written.
19
+ *
20
+ * What it is for: `LankaCipher.clear()` removes what IT wrote instead of
21
+ * emptying the whole store — a store that cannot enumerate leaves it no
22
+ * choice but the latter.
23
+ */
24
+ keys?(): Promise<string[]>;
25
+ }
26
+
27
+ /**
28
+ * The half an engine only has when it answers without awaiting.
29
+ *
30
+ * ALL FOUR OR NONE. A caller narrows on three of them and then uses the fourth —
31
+ * an adapter declaring two type-checks and lies at the one call site that has no
32
+ * `await` to fall back to.
33
+ *
34
+ * What it is for: a store read during the first render. `localStorage` and MMKV
35
+ * can answer there; a bridge and a keychain cannot, and a screen that awaits its
36
+ * persisted state renders once without it and again with it — the flash a user
37
+ * reads as a bug.
38
+ */
39
+ interface ILankaSyncStorageAdapter {
40
+ setItemSync(key: string, value: string): void;
41
+ getItemSync(key: string): string | null;
42
+ removeItemSync(key: string): void;
43
+ clearSync(): void;
44
+ }
45
+
46
+ /**
47
+ * What a key-value engine must do to stand behind `@lankajs/storage`.
48
+ *
49
+ * A PORT and nothing else: core declares the shape and ships no engine, because
50
+ * every platform already has one and they have nothing in common but these four
51
+ * operations. The asynchronous half is the whole port; the synchronous half is a
52
+ * capability an engine either has or does not, and a caller asks before using it
53
+ * rather than forcing the type.
54
+ *
55
+ * Implementations: `@lankajs/storage` carries three over what a browser already
56
+ * provides — `LankaWebStorageAdapter`, `LankaCacheStorageAdapter`,
57
+ * `LankaIndexedDbAdapter` — and `modules/storage-adapters/` holds one per engine
58
+ * an application installs. An application is free to write its own; the
59
+ * conformance suite in `@lankajs/tool-testing` is what says whether it is right.
60
+ *
61
+ * ## The clauses an implementation must keep
62
+ *
63
+ * Behaviour, not signatures, is where two honest adapters diverge, so each
64
+ * clause below is a promise and every one of them is an assertion in
65
+ * `lankaStorageAdapterConformance`. Run it; do not read a member's source.
66
+ *
67
+ * 1. Values are STRINGS, and what was written comes back byte for byte — `""`,
68
+ * `"null"` and `"{}"` included. An engine that serialises on the way in and
69
+ * parses on the way out answers a different type than it was given.
70
+ * 2. A missing key answers `null` — never `undefined`, never a throw.
71
+ * 3. `removeItem` of a key that is not there succeeds.
72
+ * 4. `setItem` over an existing key replaces it. Nothing merges.
73
+ * 5. `clear()` empties this adapter's NAMESPACE: afterwards every key it wrote
74
+ * answers `null`.
75
+ * 6. `keys()` is optional. Declared, it answers what this adapter wrote and
76
+ * nothing else — the namespace's keys, not the engine's.
77
+ * 7. An engine that can neither enumerate nor wipe keeps its own index of what
78
+ * it wrote, so clause 5 holds anyway. `expo-secure-store` is the reason this
79
+ * clause exists: it deletes a key it is handed and offers nothing else.
80
+ * 8. The synchronous half is all four methods or none.
81
+ * 9. The two halves see ONE store: a value written synchronously is visible to
82
+ * `getItem`, and the other way round.
83
+ * 10. A value the engine cannot hold FAILS. Truncation surfaces a week later as
84
+ * a token that decrypts to nonsense.
85
+ * 11. A key is used AS GIVEN. A key holding a space, a slash, a colon or a
86
+ * letter outside ASCII names its own value, and `keys()` answers it in the
87
+ * spelling it was written with. An engine that restricts which characters a
88
+ * key may hold — `expo-secure-store` allows only letters, digits, `.`, `-`
89
+ * and `_` — encodes on the way in and decodes on the way out, rather than
90
+ * handing the caller a key it never wrote.
91
+ *
92
+ * ## Why `clear()` is required where `ILankaReadCache.cancel` is optional
93
+ *
94
+ * The port next door made an operation optional when three of four libraries
95
+ * could not do it. This one does the opposite with the same kind of gap, and the
96
+ * difference is what absence COSTS. A cache that cannot cancel finishes a
97
+ * request nobody wants: wasteful, never wrong. A store that cannot clear ends a
98
+ * session with the tokens still in it — which is not waste, it is the failure.
99
+ *
100
+ * So the cost lands on the one adapter with the problem (clause 7) instead of on
101
+ * every caller having to ask whether sign-out worked.
102
+ *
103
+ * ## What this port does not decide
104
+ *
105
+ * Three promises cannot be observed from inside an implementation and belong to
106
+ * whoever wires one:
107
+ *
108
+ * - **The adapter takes its engine, and never constructs it.** `new
109
+ * LankaWebStorageAdapter(localStorage)` is the existing shape and the reason
110
+ * an engine that only exists on a device is testable in node at all.
111
+ * - **An engine that encrypts itself is not encrypted twice.** MMKV with a key
112
+ * and a keychain-backed store are already ciphertext at rest;
113
+ * `LankaEncryptedStorage` over one of them is a second lock on one door.
114
+ * - **On a server the namespace is per REQUEST**, never a module-level
115
+ * singleton — one process serves every reader.
116
+ */
117
+ interface ILankaStorageAdapter extends ILankaAsyncStorageAdapter, Partial<ILankaSyncStorageAdapter> {
118
+ }
119
+
120
+ export type { ILankaAsyncStorageAdapter, ILankaStorageAdapter, ILankaSyncStorageAdapter };
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1,15 +1,16 @@
1
- import { c as ILankaPlugin } from '../createLanka-DI1CSy2Q.js';
2
- import '../createLankaScope-BiFxNQgl.js';
3
- import '../LankaSharedStoreLocator-zS2kLu-S.js';
1
+ import { c as ILankaPlugin } from '../createLanka-x4oSMs7P.js';
2
+ import '../createLankaScope-DomNbEvb.js';
3
+ import '../LankaSharedStoreLocator-jsz_gnUm.js';
4
4
  import '../ALankaSharedStore-B7uepuuk.js';
5
5
  import 'zustand/vanilla';
6
- import '../activeRuntime-B336NU5I.js';
7
- import '../ILankaScenarioVM-DUsI-fSc.js';
8
- import '../LankaScenarioLocator-D86TIwiu.js';
9
- import '../ILankaScenarioMetadata-GoWWNEQL.js';
10
- import '../ALankaGateway-CkW1LbKE.js';
11
- import '../lankaStandardValidator-CL-r-zEV.js';
6
+ import '../activeRuntime-BO-qY0yY.js';
7
+ import '../ILankaScenarioVM-DpKFL3iE.js';
8
+ import '../LankaScenarioLocator-Dki2Es3O.js';
9
+ import '../ILankaScenarioMetadata-Dj4GCqmX.js';
10
+ import '../ALankaGateway-BrVPaZN5.js';
11
+ import '../lankaStandardValidator-BUFnysK0.js';
12
12
  import '@standard-schema/spec';
13
+ import '../ILankaFieldError-D5931-vT.js';
13
14
  import '../ILankaRuntimeConfig-Vl436GWK.js';
14
15
  import '../lankaHttpInFlight-Bk1eIuSx.js';
15
16
  import '../lankaRequestMiddleware-DAC5kCb7.js';
@@ -1,5 +1,6 @@
1
- export { I as ILankaValidator, T as TLankaSchema, a as TLankaValidationResult, l as lankaStandardValidator } from '../lankaStandardValidator-CL-r-zEV.js';
2
- import { L as LankaError } from '../LankaError-xpI-qj35.js';
1
+ export { I as ILankaValidator, T as TLankaSchema, a as TLankaValidationResult, l as lankaStandardValidator } from '../lankaStandardValidator-BUFnysK0.js';
2
+ import { L as LankaError } from '../LankaError-D6RWNyLM.js';
3
+ import { I as ILankaFieldError } from '../ILankaFieldError-D5931-vT.js';
3
4
  import '@standard-schema/spec';
4
5
 
5
6
  /**
@@ -12,10 +13,12 @@ import '@standard-schema/spec';
12
13
  * that told a contract drift from a network failure by `kind` never saw it.
13
14
  *
14
15
  * `name` stays `LankaValidationError` and `status` stays 422: both are what a
15
- * consumer's existing `catch` reads, and the message list is `errors`.
16
+ * consumer's existing `catch` reads, and the message list is `errors`. The same
17
+ * issues arrive a second time as `fields`, with the path in segments, for the
18
+ * consumer that has an input to show each one at.
16
19
  */
17
20
  declare class LankaValidationError extends LankaError {
18
- constructor(message: string, errors?: string[]);
21
+ constructor(message: string, errors?: string[], fields?: readonly ILankaFieldError[]);
19
22
  /** Always present here, unlike the base's optional list. */
20
23
  get errors(): string[];
21
24
  }
@@ -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,8 +1,27 @@
1
- import { StoreApi, StateCreator, UseBoundStore } from 'zustand';
2
- import { a as ILankaScenario, T as TLankaReplayRequest, I as ILankaScenarioVM } from '../ILankaScenarioVM-DUsI-fSc.js';
1
+ import { I as ILankaReadableVM } from '../ILankaReadableVM-BoPzHEPV.js';
2
+ import { a as ILankaScenario, T as TLankaReplayRequest, I as ILankaScenarioVM } from '../ILankaScenarioVM-DpKFL3iE.js';
3
+ import { StoreApi, StateCreator } from 'zustand';
3
4
  import { A as ALankaSharedStore } from '../ALankaSharedStore-B7uepuuk.js';
4
5
  import { StoreApi as StoreApi$1 } from 'zustand/vanilla';
5
6
 
7
+ /**
8
+ * A ViewModel that can also be written to from outside.
9
+ *
10
+ * The reading half plus the two members a host needs: the state a screen starts
11
+ * from, and a way to make server data that first state — which is what
12
+ * `hydrateLankaVM` does and the only reason writing is published at all.
13
+ *
14
+ * A view binding takes `ILankaReadableVM` instead. The narrower type is not
15
+ * politeness: a binding holding this one could write during a render, which is
16
+ * the defect no framework's scheduler survives.
17
+ */
18
+ interface ILankaVM<TState extends object> extends ILankaReadableVM<TState> {
19
+ /** The state the ViewModel was built with, before anything wrote to it. */
20
+ getInitialState(): TState;
21
+ /** Writes state. Partial by default; `replace` swaps the whole object. */
22
+ setState(partial: TState | Partial<TState> | ((state: TState) => TState | Partial<TState>), replace?: false): void;
23
+ }
24
+
6
25
  interface ILankaVMContext<TState, TGateways extends object, TServices extends object> {
7
26
  set: StoreApi<TState>["setState"];
8
27
  get: () => TState;
@@ -122,10 +141,10 @@ interface ILankaVMConfig<State extends object, Actions extends object, TGateways
122
141
  * through a getter — and warns with the ViewModel and key names. "Remember to
123
142
  * set the flag" is not a mechanism.
124
143
  */
125
- declare function createLankaVM<State extends object, Actions extends object>(config: ILankaVMConfig<State, Actions, Record<string, never>, Record<string, never>>): UseBoundStore<StoreApi<State & Actions & ILankaScenarioVM>>;
126
- declare function createLankaVM<State extends object, Actions extends object, Services extends object>(config: ILankaVMConfig<State, Actions, Record<string, never>, Services>): UseBoundStore<StoreApi<State & Actions & ILankaScenarioVM>>;
127
- declare function createLankaVM<State extends object, Actions extends object, TGateways extends object>(config: ILankaVMConfig<State, Actions, TGateways, Record<string, never>>): UseBoundStore<StoreApi<State & Actions & ILankaScenarioVM>>;
128
- declare function createLankaVM<State extends object, Actions extends object, TGateways extends object, Services extends object>(config: ILankaVMConfig<State, Actions, TGateways, Services>): UseBoundStore<StoreApi<State & Actions & ILankaScenarioVM>>;
144
+ declare function createLankaVM<State extends object, Actions extends object>(config: ILankaVMConfig<State, Actions, Record<string, never>, Record<string, never>>): ILankaVM<State & Actions & ILankaScenarioVM>;
145
+ declare function createLankaVM<State extends object, Actions extends object, Services extends object>(config: ILankaVMConfig<State, Actions, Record<string, never>, Services>): ILankaVM<State & Actions & ILankaScenarioVM>;
146
+ declare function createLankaVM<State extends object, Actions extends object, TGateways extends object>(config: ILankaVMConfig<State, Actions, TGateways, Record<string, never>>): ILankaVM<State & Actions & ILankaScenarioVM>;
147
+ declare function createLankaVM<State extends object, Actions extends object, TGateways extends object, Services extends object>(config: ILankaVMConfig<State, Actions, TGateways, Services>): ILankaVM<State & Actions & ILankaScenarioVM>;
129
148
 
130
149
  declare function createLazyLankaVM<State extends object, Actions extends object>(config: ILankaVMConfig<State, Actions, Record<string, never>, Record<string, never>>): TLazyLankaVM<ReturnType<typeof createLankaVM<State, Actions, Record<string, never>, Record<string, never>>>, State & Actions & ILankaScenarioVM>;
131
150
  declare function createLazyLankaVM<State extends object, Actions extends object, Services extends object>(config: ILankaVMConfig<State, Actions, Record<string, never>, Services>): TLazyLankaVM<ReturnType<typeof createLankaVM<State, Actions, Record<string, never>, Services>>, State & Actions & ILankaScenarioVM>;
@@ -166,14 +185,18 @@ type TLankaStatelessVMConfig$1<Actions extends object, TGateways extends object
166
185
  onInit?: (ctx: ILankaStatelessVMContext<Actions & ILankaScenarioVM, TGateways, Services>) => void;
167
186
  onReset?: (ctx: ILankaStatelessVMContext<Actions & ILankaScenarioVM, TGateways, Services>) => void;
168
187
  };
169
- type TLankaStatelessVMHook<Actions extends object> = {
170
- <TSelected = Actions & ILankaScenarioVM>(selector?: (full: Actions & ILankaScenarioVM) => TSelected): TSelected;
171
- getState: () => Actions & ILankaScenarioVM;
172
- };
173
- declare function createStatelessLankaVM<Actions extends object>(config: TLankaStatelessVMConfig$1<Actions, Record<string, never>, Record<string, never>>): TLankaStatelessVMHook<Actions>;
174
- declare function createStatelessLankaVM<Actions extends object, Services extends object>(config: TLankaStatelessVMConfig$1<Actions, Record<string, never>, Services>): TLankaStatelessVMHook<Actions>;
175
- declare function createStatelessLankaVM<Actions extends object, TGateways extends object>(config: TLankaStatelessVMConfig$1<Actions, TGateways, Record<string, never>>): TLankaStatelessVMHook<Actions>;
176
- declare function createStatelessLankaVM<Actions extends object, TGateways extends object, Services extends object>(config: TLankaStatelessVMConfig$1<Actions, TGateways, Services>): TLankaStatelessVMHook<Actions>;
188
+ /**
189
+ * @deprecated since 2.0.0 - use ILankaReadableVM, which is what a stateless
190
+ * ViewModel now IS: `createStatelessLankaVM` answers a readable ViewModel rather
191
+ * than a React hook, so the word "Hook" in this name no longer describes
192
+ * anything. The alias is kept because a published name is never removed, and it
193
+ * resolves to exactly the type the factory returns.
194
+ */
195
+ type TLankaStatelessVMHook<Actions extends object> = ILankaReadableVM<Actions & ILankaScenarioVM>;
196
+ declare function createStatelessLankaVM<Actions extends object>(config: TLankaStatelessVMConfig$1<Actions, Record<string, never>, Record<string, never>>): ILankaReadableVM<Actions & ILankaScenarioVM>;
197
+ declare function createStatelessLankaVM<Actions extends object, Services extends object>(config: TLankaStatelessVMConfig$1<Actions, Record<string, never>, Services>): ILankaReadableVM<Actions & ILankaScenarioVM>;
198
+ declare function createStatelessLankaVM<Actions extends object, TGateways extends object>(config: TLankaStatelessVMConfig$1<Actions, TGateways, Record<string, never>>): ILankaReadableVM<Actions & ILankaScenarioVM>;
199
+ declare function createStatelessLankaVM<Actions extends object, TGateways extends object, Services extends object>(config: TLankaStatelessVMConfig$1<Actions, TGateways, Services>): ILankaReadableVM<Actions & ILankaScenarioVM>;
177
200
 
178
201
  type TLankaStatelessVMConfig<Actions extends object, TGateways extends object = Record<string, never>, Services extends object = Record<string, never>> = Omit<ILankaVMConfig<object, Actions, TGateways, Services>, "states"> & {
179
202
  states?: never;
@@ -197,11 +220,25 @@ interface ILankaSharedStoreVMContext<TStoreState extends object, TFullState exte
197
220
  trigger: <T>(scenario: ILankaScenario<T>, data?: T) => void;
198
221
  }
199
222
 
200
- type TLankaSharedStoreVMHook<TStoreState extends object, TActions extends object> = {
201
- <TSelected = TStoreState & TActions & ILankaScenarioVM>(selector?: (full: TStoreState & TActions & ILankaScenarioVM) => TSelected): TSelected;
202
- getState: () => TStoreState & TActions & ILankaScenarioVM;
223
+ /**
224
+ * A ViewModel over a store somebody else owns.
225
+ *
226
+ * The port, plus the one member the shape genuinely adds: the SLICE, without the
227
+ * actions and scenario members composed onto it. A screen reads `getState`; code
228
+ * that has to reason about what is actually persisted — a hydration, a devtools
229
+ * panel, a second ViewModel over the same store — reads `getStoreState`.
230
+ */
231
+ interface ILankaSharedStoreVM<TStoreState extends object, TActions extends object> extends ILankaReadableVM<TStoreState & TActions & ILankaScenarioVM> {
232
+ /** The shared store's own state, without this ViewModel's actions on top. */
203
233
  getStoreState: () => TStoreState;
204
- };
234
+ }
235
+ /**
236
+ * @deprecated since 2.0.0 - use ILankaSharedStoreVM, which is the same type
237
+ * under a name that is still true: `createSharedStoreLankaVM` answers a readable
238
+ * ViewModel rather than a React hook. The alias is kept because a published name
239
+ * is never removed.
240
+ */
241
+ type TLankaSharedStoreVMHook<TStoreState extends object, TActions extends object> = ILankaSharedStoreVM<TStoreState, TActions>;
205
242
  interface ILankaSharedStoreScenarioBinding<TData, TStoreState extends object, TActions extends object, TStore extends ALankaSharedStore<TStoreState>, TGateways extends object, TServices extends object> {
206
243
  scenario: ILankaScenario<TData>;
207
244
  /**
@@ -235,10 +272,10 @@ interface ILankaSharedStoreVMConfig<TStoreState extends object, TActions extends
235
272
  * Factory for ViewModels backed by an external shared store instance.
236
273
  * Multiple ViewModels can be created on top of the same store instance.
237
274
  */
238
- declare function createSharedStoreLankaVM<StoreState extends object, Actions extends object, Store extends ALankaSharedStore<StoreState>>(config: ILankaSharedStoreVMConfig<StoreState, Actions, Store, Record<string, never>, Record<string, never>>): TLankaSharedStoreVMHook<StoreState, Actions>;
239
- declare function createSharedStoreLankaVM<StoreState extends object, Actions extends object, Store extends ALankaSharedStore<StoreState>, Services extends object>(config: ILankaSharedStoreVMConfig<StoreState, Actions, Store, Record<string, never>, Services>): TLankaSharedStoreVMHook<StoreState, Actions>;
240
- declare function createSharedStoreLankaVM<StoreState extends object, Actions extends object, Store extends ALankaSharedStore<StoreState>, TGateways extends object>(config: ILankaSharedStoreVMConfig<StoreState, Actions, Store, TGateways, Record<string, never>>): TLankaSharedStoreVMHook<StoreState, Actions>;
241
- declare function createSharedStoreLankaVM<StoreState extends object, Actions extends object, Store extends ALankaSharedStore<StoreState>, TGateways extends object, Services extends object>(config: ILankaSharedStoreVMConfig<StoreState, Actions, Store, TGateways, Services>): TLankaSharedStoreVMHook<StoreState, Actions>;
275
+ declare function createSharedStoreLankaVM<StoreState extends object, Actions extends object, Store extends ALankaSharedStore<StoreState>>(config: ILankaSharedStoreVMConfig<StoreState, Actions, Store, Record<string, never>, Record<string, never>>): ILankaSharedStoreVM<StoreState, Actions>;
276
+ declare function createSharedStoreLankaVM<StoreState extends object, Actions extends object, Store extends ALankaSharedStore<StoreState>, Services extends object>(config: ILankaSharedStoreVMConfig<StoreState, Actions, Store, Record<string, never>, Services>): ILankaSharedStoreVM<StoreState, Actions>;
277
+ declare function createSharedStoreLankaVM<StoreState extends object, Actions extends object, Store extends ALankaSharedStore<StoreState>, TGateways extends object>(config: ILankaSharedStoreVMConfig<StoreState, Actions, Store, TGateways, Record<string, never>>): ILankaSharedStoreVM<StoreState, Actions>;
278
+ declare function createSharedStoreLankaVM<StoreState extends object, Actions extends object, Store extends ALankaSharedStore<StoreState>, TGateways extends object, Services extends object>(config: ILankaSharedStoreVMConfig<StoreState, Actions, Store, TGateways, Services>): ILankaSharedStoreVM<StoreState, Actions>;
242
279
 
243
280
  /**
244
281
  * What a lazy shared-store ViewModel is, in the type as well as at runtime.
@@ -257,6 +294,19 @@ declare function createLazySharedStoreLankaVM<StoreState extends object, Actions
257
294
  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>>;
258
295
  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>;
259
296
 
297
+ /**
298
+ * The lifecycle moments a ViewModel took, as the scenario binder receives them.
299
+ *
300
+ * A hook is present only when the ViewModel DECLARED it — overrode the method,
301
+ * or passed it in config. Bootstrap registers a ViewModel for the sake of these
302
+ * calls, so "declared" is what decides registration; a default no-op must not
303
+ * count, or every ViewModel would sit in the scenario registry for nothing.
304
+ */
305
+ interface ILankaVMLifecycleHooks {
306
+ onInit?: () => void;
307
+ onReset?: () => void;
308
+ }
309
+
260
310
  /**
261
311
  * What every ViewModel is given, and the two moments it is told about.
262
312
  *
@@ -281,10 +331,36 @@ declare abstract class ALankaVMEnvironment<TGateways extends object = Record<str
281
331
  protected createGateways(): TGateways;
282
332
  /** Non-gateway collaborators, built once per ViewModel. */
283
333
  protected createServices(): Services;
284
- /** Runs after the scenarios are bound. */
334
+ /**
335
+ * Runs once the scenarios are bound: inside `startLanka()` for a ViewModel built
336
+ * at module level, inside `build()` for one built after bootstrap — in both
337
+ * cases before any screen has read the hook the build returns.
338
+ */
285
339
  protected onInit(): void;
286
- /** Runs when the screen goes away, before the scenarios are unbound. */
340
+ /**
341
+ * Runs when the ViewModel is released — the framework instance disposed, or a
342
+ * lazy ViewModel's `dispose()` — after its scenario subscriptions are gone.
343
+ */
287
344
  protected onReset(): void;
345
+ /**
346
+ * The two moments above as the scenario binder receives them: present only
347
+ * where this ViewModel took them.
348
+ *
349
+ * Framework plumbing, not an extension point — a ViewModel overrides `onInit`
350
+ * and `onReset`, never this. Protected because the three bases call it, and
351
+ * named after `toStyleContext` for the same reason: a derived view of the
352
+ * protected surface, assembled by the framework.
353
+ *
354
+ * Why "took them" decides anything: `onInit` runs inside `initializeScenario`,
355
+ * which bootstrap alone calls, on the ViewModels registered with it.
356
+ * Registration used to follow scenario bindings only, so a ViewModel that
357
+ * overrode `onInit` and bound nothing was never initialised — silently. Now a
358
+ * declared hook registers the ViewModel too, and a default no-op must not
359
+ * count, or every ViewModel would sit in the scenario registry for nothing.
360
+ * The functional bridges declare theirs as own properties over these methods,
361
+ * which is what this comparison sees.
362
+ */
363
+ protected toLifecycleHooks(): ILankaVMLifecycleHooks;
288
364
  }
289
365
 
290
366
  /**
@@ -300,7 +376,9 @@ declare abstract class ALankaVMEnvironment<TGateways extends object = Record<str
300
376
  * value or a thunk, the class supplies by overriding a method of the same name —
301
377
  * `states`, `scenarioHandlers`, `enhancers`, `onInit`, `onReset` — with the two
302
378
  * dependency suppliers named `createGateways` and `createServices`, because
303
- * `gateways` and `services` already name what they answer.
379
+ * `gateways` and `services` already name what they answer. `toLifecycleHooks`
380
+ * is protected too and is not one of these: it is how the framework reads the
381
+ * two hooks, and a ViewModel overrides the hooks, never it.
304
382
  *
305
383
  * ```ts
306
384
  * class TodoVM extends ALankaVM<ITodoState, ITodoActions, ITodoGateways> {
@@ -345,7 +423,7 @@ declare abstract class ALankaVM<State extends object, Actions extends object, TG
345
423
  */
346
424
  protected readonly enableAccessTrackingOptimization: boolean;
347
425
  /** Writes state. Available from `createActions` onwards, never before. */
348
- protected set: StoreApi<State & Actions & ILankaScenarioVM>["setState"];
426
+ protected set: StoreApi$1<State & Actions & ILankaScenarioVM>["setState"];
349
427
  /** Reads state. The read a tracked hook cannot see — hence the switch above. */
350
428
  protected get: () => State & Actions & ILankaScenarioVM;
351
429
  /** Fires a scenario, which every ViewModel bound to it then hears. */
@@ -372,8 +450,16 @@ declare abstract class ALankaVM<State extends object, Actions extends object, TG
372
450
  * the wrong half. Canon: `skills/parity/SKILL.md` section 3a.
373
451
  */
374
452
  protected toStyleContext(): ILankaVMContext<State & Actions & ILankaScenarioVM, TGateways, Services>;
375
- /** Builds the hook a screen calls. One store per call. */
376
- build(): UseBoundStore<StoreApi<State & Actions & ILankaScenarioVM>>;
453
+ /**
454
+ * Builds the ViewModel a screen reads. One store per call.
455
+ *
456
+ * What comes back is a STORE, not a hook: `getState`, `subscribe`, `setState`,
457
+ * plus the name and the tracking flag. A screen reaches it through its
458
+ * framework's binding — `useLankaVM(todoVM)` from `@lankajs/react`,
459
+ * `@lankajs/vue` and the rest of the shelf — and a program with no framework
460
+ * at all reads `getState()` and `subscribe()` directly.
461
+ */
462
+ build(): ILankaVM<State & Actions & ILankaScenarioVM>;
377
463
  }
378
464
 
379
465
  /**
@@ -389,6 +475,9 @@ declare abstract class ALankaVM<State extends object, Actions extends object, TG
389
475
  * or a thunk, the class supplies by overriding a method of the same name, with
390
476
  * the two dependency suppliers named `createGateways` and `createServices`
391
477
  * because `gateways` and `services` already name what they answer.
478
+ * `toLifecycleHooks` is protected too and is not one of these: it is how the
479
+ * framework reads `onInit` and `onReset`, and a ViewModel overrides those,
480
+ * never it.
392
481
  *
393
482
  * ```ts
394
483
  * class SessionVM extends ALankaStatelessVM<ISessionActions, ISessionGateways> {
@@ -438,8 +527,13 @@ declare abstract class ALankaStatelessVM<Actions extends object, TGateways exten
438
527
  * read. Canon: `skills/parity/SKILL.md` section 3a.
439
528
  */
440
529
  protected toStyleContext(): ILankaStatelessVMContext<Actions & ILankaScenarioVM, TGateways, Services>;
441
- /** Builds the hook a screen calls. One ViewModel per call. */
442
- build(): TLankaStatelessVMHook<Actions>;
530
+ /**
531
+ * Builds the ViewModel a screen reads. One per call.
532
+ *
533
+ * What comes back answers the read half of the port and nothing more: there
534
+ * is no store to write to from outside, because there is no state to write.
535
+ */
536
+ build(): ILankaReadableVM<Actions & ILankaScenarioVM>;
443
537
  }
444
538
 
445
539
  /**
@@ -469,7 +563,9 @@ declare abstract class ALankaStatelessVM<Actions extends object, TGateways exten
469
563
  * implementation: the store, the tracked hook and the two memoised state
470
564
  * references are the factory's, and duplicating them here would be the second
471
565
  * implementation the parity canon exists to prevent. What it gives a class-style
472
- * consumer is the same protected surface under the same names.
566
+ * consumer is the same protected surface under the same names — plus
567
+ * `toLifecycleHooks`, the framework's reading of `onInit`/`onReset`, which is
568
+ * not an extension point.
473
569
  *
474
570
  * Canon: `skills/parity/SKILL.md`.
475
571
  */
@@ -513,8 +609,68 @@ declare abstract class ALankaSharedStoreVM<StoreState extends object, Actions ex
513
609
  */
514
610
  declare const createLankaSharedStore: <TState extends object>(createInitialState: () => TState) => ALankaSharedStore<TState>;
515
611
 
612
+ /**
613
+ * Keeps a selection's IDENTITY while nothing in it moved.
614
+ *
615
+ * ```ts
616
+ * const hold = createLankaShallowHold<{ title: string; status: string }>();
617
+ *
618
+ * // in any binding on the shelf
619
+ * const view = useLankaVM(missionVM, (state) => hold({
620
+ * title: state.title,
621
+ * status: state.status,
622
+ * }));
623
+ * ```
624
+ *
625
+ * ## What it is for
626
+ *
627
+ * A selector narrows what a reader depends on, and a selector that BUILDS its
628
+ * answer — `(state) => ({ a: state.a })`, `(state) => rows.filter(…)`, the shape
629
+ * a consumer reaches for first — cannot say so. Every binding compares the new
630
+ * selection with the last one by identity, and a fresh object is never identical
631
+ * to anything, so the reader wakes for every change in the ViewModel including
632
+ * the keys the selector exists to ignore. This is the comparison that makes the
633
+ * selection mean something.
634
+ *
635
+ * A selector answering a PRIMITIVE never needed it, which is what makes the cost
636
+ * quiet: the shape that is free and the shape that repaints on everything look
637
+ * the same on the page.
638
+ *
639
+ * ## Why it is in core and not in a binding
640
+ *
641
+ * It was `useLankaShallow` in `@lankajs/react` and nowhere else, and that made it
642
+ * a CAPABILITY one member of the shelf had and four did not. An idiom is a
643
+ * spelling; this changes which notifications reach a reader, and it encodes a
644
+ * policy — one level deep, own keys, `Object.is` — that five packages inventing
645
+ * separately would answer five ways. `skills/parity/SKILL.md` 3c: a binding that
646
+ * needs more than the port gives it has found something that belongs in core, for
647
+ * everybody.
648
+ *
649
+ * `useLankaShallow` keeps working and is now React's spelling over this. It has
650
+ * to exist there and cannot exist here: a React component re-runs the hook on
651
+ * every render, so the holding has to survive a render while the SELECTOR stays
652
+ * the current one — which is a `useRef`, and a ref is not something core can
653
+ * have.
654
+ *
655
+ * ## Why it holds a value and not a selector
656
+ *
657
+ * `createLankaShallowSelector(selector)` was the other shape and it cannot serve
658
+ * React: the wrapper would be rebuilt whenever the selector's identity moved, and
659
+ * an inline arrow is a new function every render, so the holding would reset
660
+ * before it ever held anything. Taking the VALUE puts the state in the only place
661
+ * every framework can keep it, and the extra line at a call site is the price of
662
+ * one name that works in all five rather than two that each work in some.
663
+ *
664
+ * ## One level, and it says so by failing
665
+ *
666
+ * Deeper would mean walking a state of unknown size on every read, which is the
667
+ * cost a reader took a selector to avoid. A selection with a nested object wants
668
+ * a selector that picks the leaves.
669
+ */
670
+ declare const createLankaShallowHold: <TValue>() => ((next: TValue) => TValue);
671
+
516
672
  type TLankaVMStateCreator<TFullState> = StateCreator<TFullState, TLankaAnyMutators, TLankaAnyMutators>;
517
673
 
518
674
  type TLankaSharedStoreSetState<TState extends object> = (partial: TState | Partial<TState> | ((state: TState) => TState | Partial<TState>), replace?: boolean) => void;
519
675
 
520
- export { ALankaSharedStore, ALankaSharedStoreVM, ALankaStatelessVM, ALankaVM, type ILankaScenarioBinding, type ILankaSharedStoreScenarioBinding, type ILankaSharedStoreVMConfig, type ILankaSharedStoreVMContext, type ILankaStatelessScenarioBinding, type ILankaStatelessVMContext, type ILankaVMConfig, type ILankaVMContext, type TLankaAnyMutators, type TLankaSetState, type TLankaSharedStoreSetState, type TLankaSharedStoreVMHook, type TLankaStatelessVMConfig$1 as TLankaStatelessVMConfig, type TLankaStatelessVMHook, type TLankaVMEnhancer, type TLankaVMStateCreator, type TLazyLankaVM, type TUnknownLankaScenarioBinding, createLankaSharedStore, createLankaVM, createLazyLankaVM, createLazySharedStoreLankaVM, createLazyStatelessLankaVM, createSharedStoreLankaVM, createStatelessLankaVM };
676
+ export { ALankaSharedStore, ALankaSharedStoreVM, ALankaStatelessVM, ALankaVM, ILankaReadableVM, type ILankaScenarioBinding, type ILankaSharedStoreScenarioBinding, type ILankaSharedStoreVM, type ILankaSharedStoreVMConfig, type ILankaSharedStoreVMContext, type ILankaStatelessScenarioBinding, type ILankaStatelessVMContext, type ILankaVM, type ILankaVMConfig, type ILankaVMContext, type TLankaAnyMutators, type TLankaSetState, type TLankaSharedStoreSetState, type TLankaSharedStoreVMHook, type TLankaStatelessVMConfig$1 as TLankaStatelessVMConfig, type TLankaStatelessVMHook, type TLankaVMEnhancer, type TLankaVMStateCreator, type TLazyLankaVM, type TUnknownLankaScenarioBinding, createLankaShallowHold, createLankaSharedStore, createLankaVM, createLazyLankaVM, createLazySharedStoreLankaVM, createLazyStatelessLankaVM, createSharedStoreLankaVM, createStatelessLankaVM };