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,53 @@
1
+ // src/validation/lanka-validation-error/LankaValidationError.ts
2
+ var LankaValidationError = class extends Error {
3
+ status;
4
+ errors;
5
+ constructor(message, errors = []) {
6
+ super(message);
7
+ this.name = "LankaValidationError";
8
+ this.status = 422;
9
+ this.errors = errors.length > 0 ? errors : [message];
10
+ }
11
+ };
12
+
13
+ // src/validation/lanka-standard-validator/lankaStandardValidator.ts
14
+ function describeIssue(issue) {
15
+ const path = (issue.path ?? []).map(
16
+ (segment) => typeof segment === "object" && segment !== null && "key" in segment ? String(segment.key) : String(segment)
17
+ ).join(".");
18
+ return path ? `${path}: ${issue.message}` : issue.message;
19
+ }
20
+ function runSync(schema, data) {
21
+ const result = schema["~standard"].validate(data);
22
+ if (result instanceof Promise) {
23
+ throw new LankaValidationError(
24
+ "The schema is asynchronous and the validation port is synchronous. Parse such data by hand: passing it silently would be worse than failing.",
25
+ []
26
+ );
27
+ }
28
+ return result;
29
+ }
30
+ var lankaStandardValidator = Object.freeze({
31
+ validate(schema, data, context) {
32
+ const result = runSync(schema, data);
33
+ if (result.issues) {
34
+ throw new LankaValidationError(
35
+ `Validation failed for ${context}`,
36
+ result.issues.map(describeIssue)
37
+ );
38
+ }
39
+ return result.value;
40
+ },
41
+ validateSafe(schema, data) {
42
+ const result = runSync(schema, data);
43
+ if (result.issues) {
44
+ return { success: false, errors: result.issues.map(describeIssue) };
45
+ }
46
+ return { success: true, data: result.value };
47
+ }
48
+ });
49
+ export {
50
+ LankaValidationError,
51
+ lankaStandardValidator
52
+ };
53
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/validation/lanka-validation-error/LankaValidationError.ts","../../src/validation/lanka-standard-validator/lankaStandardValidator.ts"],"sourcesContent":["import { ILankaApiError } from \"../../errors/_interfaces/ILankaApiError\";\n\n/**\n * What a schema refused, as an error a screen can catch by type.\n *\n * The one case where a class is the answer by itself: `instanceof` needs a\n * prototype. It carries the field errors a form renders, so a caller branches on\n * the type rather than parsing a message.\n */\nexport class LankaValidationError extends Error implements ILankaApiError {\n\tstatus: number;\n\terrors: string[];\n\n\tconstructor(message: string, errors: string[] = []) {\n\t\tsuper(message);\n\t\tthis.name = \"LankaValidationError\";\n\t\tthis.status = 422;\n\t\tthis.errors = errors.length > 0 ? errors : [message];\n\t}\n}\n","import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { LankaValidationError } from \"../lanka-validation-error/LankaValidationError\";\nimport type { TLankaValidationResult } from \"../_types/TLankaValidationResult\";\n\n/**\n * Response body validation with any Standard Schema implementation.\n *\n * An abstraction typed by one library cannot be implemented by another, which is\n * the whole reason a validation port exists. Standard Schema is the shared\n * interface implemented by zod 4, valibot, arktype and others.\n *\n * `@standard-schema/spec` contains TYPES ONLY and adds no bytes to a build, so it\n * is a regular dependency rather than another peer.\n *\n * No adapter classes: a schema describes itself, and\n * `schema[\"~standard\"].validate(data)` is the whole protocol.\n */\n\n/** A schema the port understands: any Standard Schema implementation. */\nexport type TLankaSchema<TOutput = unknown> = StandardSchemaV1<unknown, TOutput>;\n\nexport interface ILankaValidator {\n\t/** Validates and returns the parsed value, or throws. */\n\tvalidate<TOutput>(schema: TLankaSchema<TOutput>, data: unknown, context: string): TOutput;\n\t/** Validates and returns an outcome, throwing nothing. */\n\tvalidateSafe<TOutput>(\n\t\tschema: TLankaSchema<TOutput>,\n\t\tdata: unknown,\n\t): TLankaValidationResult<TOutput>;\n}\n\n/**\n * A field path plus a message.\n *\n * The path is assembled WHOLE — `items.0.id`, not `id`. Without the index and the\n * parent the message points nowhere when the list has twenty items.\n */\nfunction describeIssue(issue: StandardSchemaV1.Issue): string {\n\tconst path = (issue.path ?? [])\n\t\t.map((segment) =>\n\t\t\ttypeof segment === \"object\" && segment !== null && \"key\" in segment\n\t\t\t\t? String(segment.key)\n\t\t\t\t: String(segment),\n\t\t)\n\t\t.join(\".\");\n\n\treturn path ? `${path}: ${issue.message}` : issue.message;\n}\n\nfunction runSync<TOutput>(\n\tschema: TLankaSchema<TOutput>,\n\tdata: unknown,\n): StandardSchemaV1.Result<TOutput> {\n\tconst result = schema[\"~standard\"].validate(data);\n\n\t// Standard Schema allows returning a promise. A synchronous port cannot await\n\t// it, and answering \"fine\" would let UNVALIDATED data through — a check that\n\t// cannot fail reporting success.\n\tif (result instanceof Promise) {\n\t\tthrow new LankaValidationError(\n\t\t\t\"The schema is asynchronous and the validation port is synchronous. Parse \" +\n\t\t\t\t\"such data by hand: passing it silently would be worse than failing.\",\n\t\t\t[],\n\t\t);\n\t}\n\n\treturn result;\n}\n\nexport const lankaStandardValidator: ILankaValidator = Object.freeze<ILankaValidator>({\n\tvalidate<TOutput>(schema: TLankaSchema<TOutput>, data: unknown, context: string): TOutput {\n\t\tconst result = runSync(schema, data);\n\n\t\tif (result.issues) {\n\t\t\tthrow new LankaValidationError(\n\t\t\t\t`Validation failed for ${context}`,\n\t\t\t\tresult.issues.map(describeIssue),\n\t\t\t);\n\t\t}\n\n\t\treturn result.value;\n\t},\n\n\tvalidateSafe<TOutput>(\n\t\tschema: TLankaSchema<TOutput>,\n\t\tdata: unknown,\n\t): TLankaValidationResult<TOutput> {\n\t\tconst result = runSync(schema, data);\n\n\t\tif (result.issues) {\n\t\t\treturn { success: false, errors: result.issues.map(describeIssue) };\n\t\t}\n\n\t\treturn { success: true, data: result.value };\n\t},\n});\n"],"mappings":";AASO,IAAM,uBAAN,cAAmC,MAAgC;AAAA,EACzE;AAAA,EACA;AAAA,EAEA,YAAY,SAAiB,SAAmB,CAAC,GAAG;AACnD,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,SAAS,OAAO,SAAS,IAAI,SAAS,CAAC,OAAO;AAAA,EACpD;AACD;;;ACkBA,SAAS,cAAc,OAAuC;AAC7D,QAAM,QAAQ,MAAM,QAAQ,CAAC,GAC3B;AAAA,IAAI,CAAC,YACL,OAAO,YAAY,YAAY,YAAY,QAAQ,SAAS,UACzD,OAAO,QAAQ,GAAG,IAClB,OAAO,OAAO;AAAA,EAClB,EACC,KAAK,GAAG;AAEV,SAAO,OAAO,GAAG,IAAI,KAAK,MAAM,OAAO,KAAK,MAAM;AACnD;AAEA,SAAS,QACR,QACA,MACmC;AACnC,QAAM,SAAS,OAAO,WAAW,EAAE,SAAS,IAAI;AAKhD,MAAI,kBAAkB,SAAS;AAC9B,UAAM,IAAI;AAAA,MACT;AAAA,MAEA,CAAC;AAAA,IACF;AAAA,EACD;AAEA,SAAO;AACR;AAEO,IAAM,yBAA0C,OAAO,OAAwB;AAAA,EACrF,SAAkB,QAA+B,MAAe,SAA0B;AACzF,UAAM,SAAS,QAAQ,QAAQ,IAAI;AAEnC,QAAI,OAAO,QAAQ;AAClB,YAAM,IAAI;AAAA,QACT,yBAAyB,OAAO;AAAA,QAChC,OAAO,OAAO,IAAI,aAAa;AAAA,MAChC;AAAA,IACD;AAEA,WAAO,OAAO;AAAA,EACf;AAAA,EAEA,aACC,QACA,MACkC;AAClC,UAAM,SAAS,QAAQ,QAAQ,IAAI;AAEnC,QAAI,OAAO,QAAQ;AAClB,aAAO,EAAE,SAAS,OAAO,QAAQ,OAAO,OAAO,IAAI,aAAa,EAAE;AAAA,IACnE;AAEA,WAAO,EAAE,SAAS,MAAM,MAAM,OAAO,MAAM;AAAA,EAC5C;AACD,CAAC;","names":[]}
@@ -0,0 +1,478 @@
1
+ import { StoreApi, StateCreator, UseBoundStore } from 'zustand';
2
+ import { a as ILankaScenario, T as TLankaReplayRequest, I as ILankaScenarioVM } from '../ILankaScenarioVM-DuCyPoyT.js';
3
+ import { A as ALankaSharedStore } from '../ALankaSharedStore-B7uepuuk.js';
4
+ import { StoreApi as StoreApi$1 } from 'zustand/vanilla';
5
+
6
+ interface ILankaVMContext<TState, TGateways extends object, TServices extends object> {
7
+ set: StoreApi<TState>["setState"];
8
+ get: () => TState;
9
+ gateways: TGateways;
10
+ services: TServices;
11
+ trigger: <T>(scenario: ILankaScenario<T>, data?: T) => void;
12
+ }
13
+
14
+ /**
15
+ * A scenario handler, accepted bivariantly.
16
+ *
17
+ * The method-shorthand indirection is deliberate and cannot be simplified: under
18
+ * `strictFunctionTypes` an arrow-typed parameter is contravariant, so a handler
19
+ * declared for a specific payload would not satisfy the bus, which calls every
20
+ * subscriber as `(data?: unknown) => void`. A method position is checked
21
+ * bivariantly, which is what makes the two assignable.
22
+ *
23
+ * Written once here because it appeared identically in three binding types, and
24
+ * a reader meeting `bivarianceHack` for the first time deserves to find the
25
+ * reason rather than the trick.
26
+ */
27
+ type TLankaScenarioHandler<TData> = {
28
+ bivarianceHack(data?: TData): void;
29
+ }["bivarianceHack"];
30
+
31
+ interface ILankaScenarioBinding<TData, TState extends object, TGateways extends object, TServices extends object> {
32
+ scenario: ILankaScenario<TData>;
33
+ /**
34
+ * Bivariant handler allows passing stricter callback signatures
35
+ * (e.g. data?: TFeatureGroupsListRefreshEventData) while the
36
+ * lankaEventBus expects (data?: unknown) => void.
37
+ */
38
+ handler: (ctx: ILankaVMContext<TState, TGateways, TServices>) => TLankaScenarioHandler<TData>;
39
+ options?: {
40
+ priority?: number;
41
+ replay?: TLankaReplayRequest;
42
+ usedBy?: string;
43
+ };
44
+ }
45
+
46
+ type TUnknownLankaScenarioBinding<TState extends object, TGateways extends object, TServices extends object> = ILankaScenarioBinding<unknown, TState & ILankaScenarioVM, TGateways, TServices>;
47
+
48
+ type TLankaAnyMutators = any;
49
+
50
+ type TLankaVMEnhancer<TState> = (creator: StateCreator<TState, TLankaAnyMutators, TLankaAnyMutators>) => StateCreator<TState, TLankaAnyMutators, TLankaAnyMutators>;
51
+
52
+ interface ILankaVMConfig<State extends object, Actions extends object, TGateways extends object = Record<string, never>, Services extends object = Record<string, never>> {
53
+ name: string;
54
+ /**
55
+ * Enables access-tracking optimization for no-selector hook usage.
56
+ * Disable when the ViewModel is consumed by a single broad consumer
57
+ * that reads most fields, because proxy tracking overhead may outweigh gains.
58
+ */
59
+ enableAccessTrackingOptimization?: boolean;
60
+ states?: State;
61
+ createActions: (ctx: ILankaVMContext<State & Actions & ILankaScenarioVM, TGateways, Services>) => Actions;
62
+ scenarioHandlers?: TUnknownLankaScenarioBinding<State & Actions, TGateways, Services>[];
63
+ /** Non-gateway services. */
64
+ services?: Services | (() => Services);
65
+ /** TGateways (data layer) separated from services. */
66
+ gateways?: TGateways | (() => TGateways);
67
+ enhancers?: TLankaVMEnhancer<State & Actions & ILankaScenarioVM>[];
68
+ onInit?: (ctx: ILankaVMContext<State & Actions & ILankaScenarioVM, TGateways, Services>) => void;
69
+ onReset?: (ctx: ILankaVMContext<State & Actions & ILankaScenarioVM, TGateways, Services>) => void;
70
+ }
71
+
72
+ /**
73
+ * The functional style of `ALankaVM`: a ViewModel declared as an options object.
74
+ *
75
+ * Everything it can do, the class can do, because this IS the class — the hooks a
76
+ * subclass overrides arrive here as config fields of the same names, and the
77
+ * protected surface arrives as the `ctx` every hook is handed. What a ViewModel is
78
+ * and how it binds scenarios is documented once, on `ALankaVM`.
79
+ *
80
+ * ⚠️ ACCESS-TRACKING BLIND SPOT. A consumer re-renders only for state keys it READ
81
+ * off the returned proxy. An action that DERIVES a value (`getSomeView()`) reads the
82
+ * store through `get()`, which the proxy never sees — so a component whose only link
83
+ * to a state key is such a getter will never re-render when that key changes.
84
+ *
85
+ * Set `enableAccessTrackingOptimization: false` on such a ViewModel. Do NOT patch it
86
+ * in the view by destructuring the underlying keys for their side effect only: that
87
+ * reads as dead code, so a refactor, an unused-variable cleanup or a lint autofix
88
+ * removes it and the screen silently freezes again. `MeetingReportViewModel` carries
89
+ * the worked example (its report toggles froze exactly that way, twice).
90
+ *
91
+ * In development the mismatch ANNOUNCES ITSELF: the framework sees that a key
92
+ * changed, that no re-render will follow, and that the component reads that key
93
+ * through a getter — and warns with the ViewModel and key names. "Remember to
94
+ * set the flag" is not a mechanism.
95
+ */
96
+ declare function createLankaVM<State extends object, Actions extends object>(config: ILankaVMConfig<State, Actions, Record<string, never>, Record<string, never>>): UseBoundStore<StoreApi<State & Actions & ILankaScenarioVM>>;
97
+ 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>>;
98
+ 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>>;
99
+ 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>>;
100
+
101
+ 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>;
102
+ 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>;
103
+ declare function createLazyLankaVM<State extends object, Actions extends object, TGateways extends object>(config: ILankaVMConfig<State, Actions, TGateways, Record<string, never>>): TLazyLankaVM<ReturnType<typeof createLankaVM<State, Actions, TGateways, Record<string, never>>>, State & Actions & ILankaScenarioVM>;
104
+ declare function createLazyLankaVM<State extends object, Actions extends object, TGateways extends object, Services extends object>(config: ILankaVMConfig<State, Actions, TGateways, Services>): TLazyLankaVM<ReturnType<typeof createLankaVM<State, Actions, TGateways, Services>>, State & Actions & ILankaScenarioVM>;
105
+ /**
106
+ * A lazy ViewModel hook: everything an ordinary store does, plus `dispose`.
107
+ */
108
+ type TLazyLankaVM<TStore, TFullState> = TStore & {
109
+ getState: () => TFullState;
110
+ dispose: () => void;
111
+ };
112
+
113
+ type TLankaSetState<TState> = (partial: TState | Partial<TState> | ((state: TState) => TState | Partial<TState>), replace?: boolean) => void;
114
+ interface ILankaStatelessVMContext<TState, TGateways extends object, TServices extends object> {
115
+ set: TLankaSetState<TState>;
116
+ get: () => TState;
117
+ gateways: TGateways;
118
+ services: TServices;
119
+ trigger: <T>(scenario: ILankaScenario<T>, data?: T) => void;
120
+ }
121
+ interface ILankaStatelessScenarioBinding<TData, TState extends object, TGateways extends object, TServices extends object> {
122
+ scenario: ILankaScenario<TData>;
123
+ handler: (ctx: ILankaStatelessVMContext<TState, TGateways, TServices>) => TLankaScenarioHandler<TData>;
124
+ options?: {
125
+ priority?: number;
126
+ replay?: TLankaReplayRequest;
127
+ usedBy?: string;
128
+ };
129
+ }
130
+ type TLankaStatelessVMConfig$1<Actions extends object, TGateways extends object = Record<string, never>, Services extends object = Record<string, never>> = {
131
+ name: string;
132
+ createActions: (ctx: ILankaStatelessVMContext<Actions & ILankaScenarioVM, TGateways, Services>) => Actions;
133
+ scenarioHandlers?: ILankaStatelessScenarioBinding<unknown, Actions & ILankaScenarioVM, TGateways, Services>[];
134
+ services?: Services | (() => Services);
135
+ gateways?: TGateways | (() => TGateways);
136
+ onInit?: (ctx: ILankaStatelessVMContext<Actions & ILankaScenarioVM, TGateways, Services>) => void;
137
+ onReset?: (ctx: ILankaStatelessVMContext<Actions & ILankaScenarioVM, TGateways, Services>) => void;
138
+ };
139
+ type TLankaStatelessVMHook<Actions extends object> = {
140
+ <TSelected = Actions & ILankaScenarioVM>(selector?: (full: Actions & ILankaScenarioVM) => TSelected): TSelected;
141
+ getState: () => Actions & ILankaScenarioVM;
142
+ };
143
+ declare function createStatelessLankaVM<Actions extends object>(config: TLankaStatelessVMConfig$1<Actions, Record<string, never>, Record<string, never>>): TLankaStatelessVMHook<Actions>;
144
+ declare function createStatelessLankaVM<Actions extends object, Services extends object>(config: TLankaStatelessVMConfig$1<Actions, Record<string, never>, Services>): TLankaStatelessVMHook<Actions>;
145
+ declare function createStatelessLankaVM<Actions extends object, TGateways extends object>(config: TLankaStatelessVMConfig$1<Actions, TGateways, Record<string, never>>): TLankaStatelessVMHook<Actions>;
146
+ declare function createStatelessLankaVM<Actions extends object, TGateways extends object, Services extends object>(config: TLankaStatelessVMConfig$1<Actions, TGateways, Services>): TLankaStatelessVMHook<Actions>;
147
+
148
+ type TLankaStatelessVMConfig<Actions extends object, TGateways extends object = Record<string, never>, Services extends object = Record<string, never>> = Omit<ILankaVMConfig<object, Actions, TGateways, Services>, "states"> & {
149
+ states?: never;
150
+ };
151
+ type TLazyStatelessReturn<Actions extends object, TGateways extends object, Services extends object> = ReturnType<typeof createStatelessLankaVM<Actions, TGateways, Services>> & {
152
+ getState: () => Actions & ILankaScenarioVM;
153
+ dispose: () => void;
154
+ };
155
+ declare function createLazyStatelessLankaVM<Actions extends object>(config: TLankaStatelessVMConfig<Actions, Record<string, never>, Record<string, never>>): TLazyStatelessReturn<Actions, Record<string, never>, Record<string, never>>;
156
+ declare function createLazyStatelessLankaVM<Actions extends object, Services extends object>(config: TLankaStatelessVMConfig<Actions, Record<string, never>, Services>): TLazyStatelessReturn<Actions, Record<string, never>, Services>;
157
+ declare function createLazyStatelessLankaVM<Actions extends object, TGateways extends object>(config: TLankaStatelessVMConfig<Actions, TGateways, Record<string, never>>): TLazyStatelessReturn<Actions, TGateways, Record<string, never>>;
158
+ declare function createLazyStatelessLankaVM<Actions extends object, TGateways extends object, Services extends object>(config: TLankaStatelessVMConfig<Actions, TGateways, Services>): TLazyStatelessReturn<Actions, TGateways, Services>;
159
+
160
+ interface ILankaSharedStoreVMContext<TStoreState extends object, TFullState extends object, TStore extends ALankaSharedStore<TStoreState>, TGateways extends object, TServices extends object> {
161
+ set: StoreApi$1<TStoreState>["setState"];
162
+ getStore: () => TStoreState;
163
+ get: () => TFullState;
164
+ store: TStore;
165
+ gateways: TGateways;
166
+ services: TServices;
167
+ trigger: <T>(scenario: ILankaScenario<T>, data?: T) => void;
168
+ }
169
+
170
+ type TLankaSharedStoreVMHook<TStoreState extends object, TActions extends object> = {
171
+ <TSelected = TStoreState & TActions & ILankaScenarioVM>(selector?: (full: TStoreState & TActions & ILankaScenarioVM) => TSelected): TSelected;
172
+ getState: () => TStoreState & TActions & ILankaScenarioVM;
173
+ getStoreState: () => TStoreState;
174
+ };
175
+ interface ILankaSharedStoreScenarioBinding<TData, TStoreState extends object, TActions extends object, TStore extends ALankaSharedStore<TStoreState>, TGateways extends object, TServices extends object> {
176
+ scenario: ILankaScenario<TData>;
177
+ /**
178
+ * Bivariant handler allows stricter callback signatures while lankaEventBus expects unknown.
179
+ */
180
+ handler: (ctx: ILankaSharedStoreVMContext<TStoreState, TStoreState & TActions & ILankaScenarioVM, TStore, TGateways, TServices>) => TLankaScenarioHandler<TData>;
181
+ options?: {
182
+ priority?: number;
183
+ replay?: TLankaReplayRequest;
184
+ usedBy?: string;
185
+ };
186
+ }
187
+ interface ILankaSharedStoreVMConfig<TStoreState extends object, TActions extends object, TStore extends ALankaSharedStore<TStoreState>, TGateways extends object = Record<string, never>, TServices extends object = Record<string, never>> {
188
+ name: string;
189
+ /**
190
+ * Enables access-tracking optimization for no-selector hook usage.
191
+ * Disable when one broad consumer reads most store fields and updates are frequent.
192
+ */
193
+ enableAccessTrackingOptimization?: boolean;
194
+ store: TStore;
195
+ createActions: (ctx: ILankaSharedStoreVMContext<TStoreState, TStoreState & TActions & ILankaScenarioVM, TStore, TGateways, TServices>) => TActions;
196
+ scenarioHandlers?: ILankaSharedStoreScenarioBinding<unknown, TStoreState, TActions, TStore, TGateways, TServices>[];
197
+ services?: TServices | (() => TServices);
198
+ gateways?: TGateways | (() => TGateways);
199
+ onInit?: (ctx: ILankaSharedStoreVMContext<TStoreState, TStoreState & TActions & ILankaScenarioVM, TStore, TGateways, TServices>) => void;
200
+ onReset?: (ctx: ILankaSharedStoreVMContext<TStoreState, TStoreState & TActions & ILankaScenarioVM, TStore, TGateways, TServices>) => void;
201
+ }
202
+
203
+ /**
204
+ * Factory for ViewModels backed by an external shared store instance.
205
+ * Multiple ViewModels can be created on top of the same store instance.
206
+ */
207
+ 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>;
208
+ 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>;
209
+ 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>;
210
+ 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>;
211
+
212
+ /**
213
+ * What a lazy shared-store ViewModel is, in the type as well as at runtime.
214
+ *
215
+ * The overloads used to promise the eager factory's return and nothing else, so
216
+ * `dispose` and `getStoreState` existed on the object and not in the type: a
217
+ * consumer releasing a closed screen's ViewModel got a compile error for calling
218
+ * something that was there.
219
+ */
220
+ type TLazySharedStoreReturn<StoreState extends object, Actions extends object, Store extends ALankaSharedStore<StoreState>, TGateways extends object, Services extends object> = ReturnType<typeof createSharedStoreLankaVM<StoreState, Actions, Store, TGateways, Services>> & {
221
+ getState: () => StoreState & Actions & ILankaScenarioVM;
222
+ getStoreState: () => StoreState;
223
+ dispose: () => void;
224
+ };
225
+ declare function createLazySharedStoreLankaVM<StoreState extends object, Actions extends object, Store extends ALankaSharedStore<StoreState>>(config: ILankaSharedStoreVMConfig<StoreState, Actions, Store, Record<string, never>, Record<string, never>>): TLazySharedStoreReturn<StoreState, Actions, Store, Record<string, never>, Record<string, never>>;
226
+ 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
+ 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
+
229
+ /**
230
+ * What every ViewModel is given, and the two moments it is told about.
231
+ *
232
+ * The three ViewModel shapes — stateful, stateless, over a shared store — differ
233
+ * in where their state lives and in nothing else about this: each is handed a
234
+ * data layer and a set of collaborators, and each is told when its scenarios are
235
+ * bound and when they are about to be unbound.
236
+ *
237
+ * Stated once because a fifth hook added to two of the three is exactly the
238
+ * divergence `skills/parity/SKILL.md` is written against, and three copies of a
239
+ * default is how that starts.
240
+ *
241
+ * It is not a role and nothing extends it directly: the three bases do, and a
242
+ * consumer extends one of them.
243
+ */
244
+ declare abstract class ALankaVMEnvironment<TGateways extends object = Record<string, never>, Services extends object = Record<string, never>> {
245
+ /** What `createGateways` answered: the data layer, kept apart from services. */
246
+ protected gateways: TGateways;
247
+ /** What `createServices` answered: everything that is not a gateway. */
248
+ protected services: Services;
249
+ /** The data layer, built once per ViewModel. */
250
+ protected createGateways(): TGateways;
251
+ /** Non-gateway collaborators, built once per ViewModel. */
252
+ protected createServices(): Services;
253
+ /** Runs after the scenarios are bound. */
254
+ protected onInit(): void;
255
+ /** Runs when the screen goes away, before the scenarios are unbound. */
256
+ protected onReset(): void;
257
+ }
258
+
259
+ /**
260
+ * A ViewModel written as a class: the state a screen reads, and the only place a
261
+ * gateway is called from.
262
+ *
263
+ * This is the implementation of the role, and `createLankaVM` is the same thing
264
+ * reached the other way — a subclass built from an options object. Neither style
265
+ * can do what the other cannot, because there is nothing here to diverge from.
266
+ *
267
+ * The protected surface IS the functional context, member for member:
268
+ * `set`, `get`, `gateways`, `services`, `trigger`. What the config supplies as a
269
+ * value or a thunk, the class supplies by overriding a method of the same name —
270
+ * `states`, `scenarioHandlers`, `enhancers`, `onInit`, `onReset` — with the two
271
+ * dependency suppliers named `createGateways` and `createServices`, because
272
+ * `gateways` and `services` already name what they answer.
273
+ *
274
+ * ```ts
275
+ * class TodoVM extends ALankaVM<ITodoState, ITodoActions, ITodoGateways> {
276
+ * protected readonly name = "TodoVM";
277
+ *
278
+ * protected states(): ITodoState {
279
+ * return { todos: [], isLoading: false };
280
+ * }
281
+ *
282
+ * protected createGateways(): ITodoGateways {
283
+ * return { todo: new TodoGateway() };
284
+ * }
285
+ *
286
+ * protected createActions(): ITodoActions {
287
+ * return {
288
+ * load: async () => {
289
+ * this.set({ isLoading: true });
290
+ * this.set({ todos: await this.gateways.todo.list(), isLoading: false });
291
+ * },
292
+ * };
293
+ * }
294
+ * }
295
+ *
296
+ * export const useTodoVM = new TodoVM().build();
297
+ * ```
298
+ *
299
+ * The access-tracking blind spot the functional style documents is the same one
300
+ * here, and `enableAccessTrackingOptimization` is the same switch. A consumer
301
+ * re-renders only for state keys it READ off the returned proxy; an action that
302
+ * DERIVES a value reads the store through `get`, which the proxy never sees, so a
303
+ * component whose only link to a key is such a getter never re-renders for it. In
304
+ * development the mismatch announces itself by name rather than by a frozen
305
+ * screen. Canon: `skills/parity/SKILL.md`.
306
+ */
307
+ declare abstract class ALankaVM<State extends object, Actions extends object, TGateways extends object = Record<string, never>, Services extends object = Record<string, never>> extends ALankaVMEnvironment<TGateways, Services> {
308
+ /** Names the ViewModel in the logs, the scenario registry and the blind-spot warning. */
309
+ protected abstract readonly name: string;
310
+ /**
311
+ * Turn off when one broad consumer reads most fields, or when an action derives
312
+ * what the screen shows — proxy tracking then costs more than it saves, and in
313
+ * the second case it cannot see the read at all.
314
+ */
315
+ protected readonly enableAccessTrackingOptimization: boolean;
316
+ /** Writes state. Available from `createActions` onwards, never before. */
317
+ protected set: StoreApi<State & Actions & ILankaScenarioVM>["setState"];
318
+ /** Reads state. The read a tracked hook cannot see — hence the switch above. */
319
+ protected get: () => State & Actions & ILankaScenarioVM;
320
+ /** Fires a scenario, which every ViewModel bound to it then hears. */
321
+ protected trigger: <TData>(scenario: ILankaScenario<TData>, data?: TData) => void;
322
+ /** The reactive fields the screen reads. */
323
+ protected states(): State;
324
+ /** The scenarios this ViewModel listens to, unsubscribed for it on reset. */
325
+ protected scenarioHandlers(): TUnknownLankaScenarioBinding<State & Actions, TGateways, Services>[];
326
+ /** Store middleware — `persist`, `lankaDevtools` — the last one applied outermost. */
327
+ protected enhancers(): TLankaVMEnhancer<State & Actions & ILankaScenarioVM>[];
328
+ /** The actions the screen calls. Written against `this.set` and `this.get`. */
329
+ protected abstract createActions(): Actions;
330
+ /**
331
+ * The protected surface as an object, for the functional style.
332
+ *
333
+ * Assembled INSIDE the class because that is the only place `protected` can be
334
+ * read — a context built from outside could carry only the public half, which is
335
+ * the wrong half. Canon: `skills/parity/SKILL.md` section 3a.
336
+ */
337
+ protected toStyleContext(): ILankaVMContext<State & Actions & ILankaScenarioVM, TGateways, Services>;
338
+ /** Builds the hook a screen calls. One store per call. */
339
+ build(): UseBoundStore<StoreApi<State & Actions & ILankaScenarioVM>>;
340
+ }
341
+
342
+ /**
343
+ * A ViewModel that holds no reactive state: actions, and what they orchestrate.
344
+ *
345
+ * The second rung of the ladder in `core/README.md` written as a class. A screen
346
+ * that reads nothing and only DOES things — a sign-out, a share sheet, a form
347
+ * whose fields live in the form library — pays for a zustand store it never
348
+ * reads; this is the same role without one.
349
+ *
350
+ * The protected surface IS the functional context, member for member: `set`,
351
+ * `get`, `gateways`, `services`, `trigger`. What the config supplies as a value
352
+ * or a thunk, the class supplies by overriding a method of the same name, with
353
+ * the two dependency suppliers named `createGateways` and `createServices`
354
+ * because `gateways` and `services` already name what they answer.
355
+ *
356
+ * ```ts
357
+ * class SessionVM extends ALankaStatelessVM<ISessionActions, ISessionGateways> {
358
+ * protected readonly name = "SessionVM";
359
+ *
360
+ * protected createGateways(): ISessionGateways {
361
+ * return { session: new SessionGateway() };
362
+ * }
363
+ *
364
+ * protected createActions(): ISessionActions {
365
+ * return {
366
+ * signOut: async () => {
367
+ * await this.gateways.session.signOut();
368
+ * this.trigger(sessionEnded);
369
+ * },
370
+ * };
371
+ * }
372
+ * }
373
+ *
374
+ * export const useSessionVM = new SessionVM().build();
375
+ * ```
376
+ *
377
+ * Canon: `skills/parity/SKILL.md`.
378
+ */
379
+ declare abstract class ALankaStatelessVM<Actions extends object, TGateways extends object = Record<string, never>, Services extends object = Record<string, never>> extends ALankaVMEnvironment<TGateways, Services> {
380
+ /** Names the ViewModel in the logs, the scenario registry and any warning. */
381
+ protected abstract readonly name: string;
382
+ /** Writes state. Available from `createActions` onwards, never before. */
383
+ protected set: TLankaSetState<Actions & ILankaScenarioVM>;
384
+ /** Reads what the actions have written, including the actions themselves. */
385
+ protected get: () => Actions & ILankaScenarioVM;
386
+ /** Fires a scenario, which every ViewModel bound to it then hears. */
387
+ 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>[];
390
+ /** The actions the screen calls. Written against `this.set` and `this.get`. */
391
+ protected abstract createActions(): Actions;
392
+ /**
393
+ * The protected surface as an object, for the functional style.
394
+ *
395
+ * Assembled INSIDE the class because that is the only place `protected` can be
396
+ * read. Canon: `skills/parity/SKILL.md` section 3a.
397
+ */
398
+ protected toStyleContext(): ILankaStatelessVMContext<Actions & ILankaScenarioVM, TGateways, Services>;
399
+ /** Builds the hook a screen calls. One ViewModel per call. */
400
+ build(): TLankaStatelessVMHook<Actions>;
401
+ }
402
+
403
+ /**
404
+ * A ViewModel over a store several ViewModels share, written as a class.
405
+ *
406
+ * The third rung of the ladder in `core/README.md`: reach for it only when two
407
+ * ViewModels must CO-EDIT one state — a list and the badge that counts it, a
408
+ * form and the header that says it is dirty. What the class adds over the
409
+ * stateful base is where the state lives: in the store it is given, so `set`
410
+ * writes there and `getStore` reads it.
411
+ *
412
+ * ```ts
413
+ * class BadgeVM extends ALankaSharedStoreVM<ISelection, IBadgeActions, TodoStore> {
414
+ * protected readonly name = "BadgeVM";
415
+ *
416
+ * public constructor(store: TodoStore) {
417
+ * super(store);
418
+ * }
419
+ *
420
+ * protected createActions(): IBadgeActions {
421
+ * return { clear: () => this.set({ selectedId: null }) };
422
+ * }
423
+ * }
424
+ * ```
425
+ *
426
+ * Unlike its two siblings this one is a thin adapter rather than the
427
+ * implementation: the store, the tracked hook and the two memoised state
428
+ * references are the factory's, and duplicating them here would be the second
429
+ * implementation the parity canon exists to prevent. What it gives a class-style
430
+ * consumer is the same protected surface under the same names.
431
+ *
432
+ * Canon: `skills/parity/SKILL.md`.
433
+ */
434
+ declare abstract class ALankaSharedStoreVM<StoreState extends object, Actions extends object, Store extends ALankaSharedStore<StoreState>, TGateways extends object = Record<string, never>, Services extends object = Record<string, never>> extends ALankaVMEnvironment<TGateways, Services> {
435
+ /** Names the ViewModel in the logs and in the scenario registry. */
436
+ protected abstract readonly name: string;
437
+ /** The store this ViewModel and its siblings share. */
438
+ protected readonly store: Store;
439
+ /** Writes into the shared store, which every reader of it hears about. */
440
+ protected set: ILankaSharedStoreVMContext<StoreState, StoreState & Actions & ILankaScenarioVM, Store, TGateways, Services>["set"];
441
+ /** Reads the store's own state, without this ViewModel's actions on top. */
442
+ protected getStore: () => StoreState;
443
+ /** Reads the store's state WITH the actions, which is what a screen sees. */
444
+ protected get: () => StoreState & Actions & ILankaScenarioVM;
445
+ /** Fires a scenario, which every ViewModel bound to it then hears. */
446
+ protected trigger: ILankaSharedStoreVMContext<StoreState, StoreState & Actions & ILankaScenarioVM, Store, TGateways, Services>["trigger"];
447
+ constructor(store: Store);
448
+ /**
449
+ * Turn off when one broad consumer reads most fields of the store — proxy
450
+ * tracking then costs more than it saves.
451
+ */
452
+ protected readonly enableAccessTrackingOptimization: boolean;
453
+ /** The scenarios this ViewModel listens to, unsubscribed for it on reset. */
454
+ protected scenarioHandlers(): ILankaSharedStoreScenarioBinding<unknown, StoreState, Actions, Store, TGateways, Services>[];
455
+ /** The actions the screen calls. Written against `this.set` and `this.get`. */
456
+ protected abstract createActions(): Actions;
457
+ /** Builds the hook a screen calls. One ViewModel per call. */
458
+ build(): TLankaSharedStoreVMHook<StoreState, Actions>;
459
+ }
460
+
461
+ /**
462
+ * A shared store, without writing a class whose body is one function.
463
+ *
464
+ * A shared store is the third rung of the ladder in `core/README.md`: reach for
465
+ * it only when several ViewModels must CO-EDIT one state. Most stores have no
466
+ * behaviour of their own — the state and how to build it fresh is the whole
467
+ * declaration — and this is the shape for those.
468
+ *
469
+ * One implementation: what comes back is an `ALankaSharedStore`, so a reset, a
470
+ * subscription and the zustand api behave identically either way.
471
+ */
472
+ declare const createLankaSharedStore: <TState extends object>(createInitialState: () => TState) => ALankaSharedStore<TState>;
473
+
474
+ type TLankaVMStateCreator<TFullState> = StateCreator<TFullState, TLankaAnyMutators, TLankaAnyMutators>;
475
+
476
+ type TLankaSharedStoreSetState<TState extends object> = (partial: TState | Partial<TState> | ((state: TState) => TState | Partial<TState>), replace?: boolean) => void;
477
+
478
+ 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 };