lanka 1.0.0 → 1.1.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.
- package/README.md +5 -3
- package/dist/{ALankaGateway-ExlRGT3D.d.ts → ALankaGateway-CkW1LbKE.d.ts} +50 -4
- package/dist/{ILankaScenarioMetadata-Bu-yggTZ.d.ts → ILankaScenarioMetadata-GoWWNEQL.d.ts} +1 -1
- package/dist/{ILankaScenarioVM-DuCyPoyT.d.ts → ILankaScenarioVM-DUsI-fSc.d.ts} +116 -3
- package/dist/{LankaScenarioLocator-BGQHwf3n.d.ts → LankaScenarioLocator-D86TIwiu.d.ts} +11 -6
- package/dist/{LankaSharedStoreLocator-MvCpav5F.d.ts → LankaSharedStoreLocator-zS2kLu-S.d.ts} +21 -0
- package/dist/_extend/index.d.ts +7 -7
- package/dist/_extend/index.js +3 -3
- package/dist/_internal/index.d.ts +6 -6
- package/dist/{activeRuntime-FcsSJvUg.d.ts → activeRuntime-B336NU5I.d.ts} +3 -3
- package/dist/bootstrap/index.d.ts +14 -145
- package/dist/bootstrap/index.js +5 -5
- package/dist/{chunk-FIR4XTBL.js → chunk-5MAQVBI2.js} +27 -24
- package/dist/chunk-5MAQVBI2.js.map +1 -0
- package/dist/chunk-D5WKKEIR.js +54 -0
- package/dist/chunk-D5WKKEIR.js.map +1 -0
- package/dist/{chunk-RYFZCAQ3.js → chunk-LMKLLEHA.js} +111 -25
- package/dist/chunk-LMKLLEHA.js.map +1 -0
- package/dist/{chunk-EWVDJYCC.js → chunk-O5ROO7QF.js} +28 -9
- package/dist/chunk-O5ROO7QF.js.map +1 -0
- package/dist/{chunk-ILQNYQY5.js → chunk-UE2C76OR.js} +32 -18
- package/dist/chunk-UE2C76OR.js.map +1 -0
- package/dist/{chunk-Q3SOVBIJ.js → chunk-UGXSGQPW.js} +8 -4
- package/dist/chunk-UGXSGQPW.js.map +1 -0
- package/dist/createLanka-DI1CSy2Q.d.ts +139 -0
- package/dist/{createLankaScope-Bc_vChRs.d.ts → createLankaScope-BiFxNQgl.d.ts} +1 -1
- package/dist/gateway/index.d.ts +77 -67
- package/dist/gateway/index.js +51 -56
- package/dist/gateway/index.js.map +1 -1
- package/dist/index.d.ts +9 -8
- package/dist/index.js +5 -5
- package/dist/locator/index.d.ts +7 -55
- package/dist/locator/index.js +1 -1
- package/dist/scenario/index.d.ts +6 -4
- package/dist/scenario/index.js +2 -2
- package/dist/stream/index.d.ts +386 -0
- package/dist/stream/index.js +287 -0
- package/dist/stream/index.js.map +1 -0
- package/dist/validation/index.js +4 -48
- package/dist/validation/index.js.map +1 -1
- package/dist/viewmodel/index.d.ts +1 -1
- package/dist/viewmodel/index.js +4 -2
- package/dist/viewmodel/index.js.map +1 -1
- package/package.json +7 -3
- package/skills/lanka-core/SKILL.md +1 -1
- package/skills/lanka-core/reference.md +110 -17
- package/skills/lanka-packages/SKILL.md +1 -1
- package/dist/chunk-EWVDJYCC.js.map +0 -1
- package/dist/chunk-FIR4XTBL.js.map +0 -1
- package/dist/chunk-ILQNYQY5.js.map +0 -1
- package/dist/chunk-Q3SOVBIJ.js.map +0 -1
- package/dist/chunk-RYFZCAQ3.js.map +0 -1
|
@@ -33,6 +33,12 @@ var lankaEventBus = Object.freeze({
|
|
|
33
33
|
removeMiddleware: (middleware) => {
|
|
34
34
|
bus().removeMiddleware(middleware);
|
|
35
35
|
},
|
|
36
|
+
addObserver: (observer) => {
|
|
37
|
+
bus().addObserver(observer);
|
|
38
|
+
},
|
|
39
|
+
removeObserver: (observer) => {
|
|
40
|
+
bus().removeObserver(observer);
|
|
41
|
+
},
|
|
36
42
|
getEventLogs: (eventType, limit = 100) => bus().getEventLogs(eventType, limit),
|
|
37
43
|
clearEvent: (eventType) => {
|
|
38
44
|
bus().clearEvent(eventType);
|
|
@@ -109,6 +115,14 @@ var LankaScenarioVMRegistry = class {
|
|
|
109
115
|
};
|
|
110
116
|
|
|
111
117
|
// src/scenario/_registries/lanka-scenarios-registry/LankaScenariosRegistry.ts
|
|
118
|
+
var release = (scenario) => {
|
|
119
|
+
if (!scenario.cleanup) return;
|
|
120
|
+
try {
|
|
121
|
+
scenario.cleanup();
|
|
122
|
+
} catch (error) {
|
|
123
|
+
lankaLogger.printScenarioLog(`Error cleaning up scenario "${scenario.name}":`, error);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
112
126
|
var LankaScenariosRegistry = class {
|
|
113
127
|
registeredScenarios = /* @__PURE__ */ new Map();
|
|
114
128
|
scenarioMetadata = /* @__PURE__ */ new Map();
|
|
@@ -147,12 +161,14 @@ var LankaScenariosRegistry = class {
|
|
|
147
161
|
return true;
|
|
148
162
|
}
|
|
149
163
|
/**
|
|
150
|
-
*
|
|
164
|
+
* Removes a scenario, calling its `cleanup` if it declared one.
|
|
165
|
+
*
|
|
151
166
|
* @param scenarioName Name of the scenario to unregister
|
|
152
167
|
* @returns true if unregistered successfully, false if not found
|
|
153
168
|
*/
|
|
154
169
|
unregister(scenarioName) {
|
|
155
|
-
|
|
170
|
+
const scenario = this.registeredScenarios.get(scenarioName);
|
|
171
|
+
if (!scenario) {
|
|
156
172
|
return false;
|
|
157
173
|
}
|
|
158
174
|
const metadata = this.scenarioMetadata.get(scenarioName);
|
|
@@ -160,6 +176,7 @@ var LankaScenariosRegistry = class {
|
|
|
160
176
|
metadata.isRegistered = false;
|
|
161
177
|
}
|
|
162
178
|
this.registeredScenarios.delete(scenarioName);
|
|
179
|
+
release(scenario);
|
|
163
180
|
return true;
|
|
164
181
|
}
|
|
165
182
|
/**
|
|
@@ -206,11 +223,17 @@ var LankaScenariosRegistry = class {
|
|
|
206
223
|
return LankaScenarioVMRegistry.getInstance().getAllViewModels();
|
|
207
224
|
}
|
|
208
225
|
/**
|
|
209
|
-
*
|
|
226
|
+
* Removes every scenario, calling each one's `cleanup`.
|
|
227
|
+
*
|
|
228
|
+
* What `dispose()` and the between-tests reset go through: `initialize` ran
|
|
229
|
+
* for every scenario at bootstrap, and this is the one moment its mirror
|
|
230
|
+
* image can run for all of them.
|
|
210
231
|
*/
|
|
211
232
|
clear() {
|
|
233
|
+
const scenarios = [...this.registeredScenarios.values()];
|
|
212
234
|
this.registeredScenarios.clear();
|
|
213
235
|
this.scenarioMetadata.clear();
|
|
236
|
+
for (const scenario of scenarios) release(scenario);
|
|
214
237
|
}
|
|
215
238
|
};
|
|
216
239
|
|
|
@@ -283,14 +306,10 @@ var ALankaScenario = class _ALankaScenario {
|
|
|
283
306
|
}
|
|
284
307
|
};
|
|
285
308
|
|
|
286
|
-
// ../tools/testing/_fixtures/.lanka_di/Scenarios.ts
|
|
287
|
-
var Scenarios_exports = {};
|
|
288
|
-
|
|
289
309
|
export {
|
|
290
310
|
lankaEventBus,
|
|
291
311
|
ALankaScenario,
|
|
292
312
|
LankaScenarioVMRegistry,
|
|
293
|
-
LankaScenariosRegistry
|
|
294
|
-
Scenarios_exports
|
|
313
|
+
LankaScenariosRegistry
|
|
295
314
|
};
|
|
296
|
-
//# sourceMappingURL=chunk-
|
|
315
|
+
//# sourceMappingURL=chunk-O5ROO7QF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/scenario/event-bus/_facades/lanka-event-bus/lankaEventBus.ts","../src/scenario/_registries/lanka-scenario-vm-registry/LankaScenarioVMRegistry.ts","../src/scenario/_registries/lanka-scenarios-registry/LankaScenariosRegistry.ts","../src/scenario/_abstractions/lanka-scenario/ALankaScenario.ts"],"sourcesContent":["import { requireActiveRuntime } from \"../../../../_internal/active-runtime/activeRuntime\";\nimport type { ILankaEventLog } from \"../../../_interfaces/ILankaEventLog\";\nimport type { ILankaEventMetadata } from \"../../../_interfaces/ILankaEventMetadata\";\nimport type { TLankaEventBusMiddleware } from \"../../../_types/TLankaEventBusMiddleware\";\nimport type { TLankaEventBusObserver } from \"../../../_types/TLankaEventBusObserver\";\nimport type { LankaEventBusInstance } from \"../../lanka-event-bus-instance/LankaEventBusInstance\";\nimport type { TLankaReplayRequest } from \"../../lanka-event-bus-instance/LankaEventBusInstance\";\n\n/**\n * Ambient bus: the active instance's, reachable without holding it.\n *\n * Every member is one line of delegation and that is all this does. It exists\n * for callers that cannot hold an instance — a user-extended `ALankaScenario`,\n * the scenario bootstrap.\n *\n * An object rather than a class with a default instance, and the difference is\n * not cosmetic: this owns no state, so a second one would delegate to the same\n * runtime and be the same object under another name. Where an ambient DOES own\n * state — storage — the class is real and a second instance means something.\n *\n * Isolation belongs to the instance holder: `lanka.eventBus.dispatch(…)`. This\n * cannot offer it — by definition it serves the ONE active instance, so two\n * instances in one process share nothing through it.\n */\nconst bus = (): LankaEventBusInstance => requireActiveRuntime().eventBus;\n\nexport const lankaEventBus = Object.freeze({\n\tenableLogs: (): void => {\n\t\tbus().enableLogs();\n\t},\n\n\tdisableLogs: (): void => {\n\t\tbus().disableLogs();\n\t},\n\n\tregisterEvent: (eventType: string, metadata: ILankaEventMetadata): void => {\n\t\tbus().registerEvent(eventType, metadata);\n\t},\n\n\tgetRegisteredEvents: () => bus().getRegisteredEvents(),\n\n\tgetEventInfo: (eventType: string): ILankaEventMetadata | null => bus().getEventInfo(eventType),\n\n\tgetSubscriptions: (eventType: string): number => bus().getSubscriptions(eventType),\n\n\tsubscribe: <T>(\n\t\teventType: string,\n\t\tcallback: (data: T) => void,\n\t\toptions?: { priority?: number; replay?: TLankaReplayRequest; usedBy?: string },\n\t): (() => void) => bus().subscribe(eventType, callback, options),\n\n\tunsubscribe: <T>(eventType: string, callback: (data: T) => void): void => {\n\t\tbus().unsubscribe(eventType, callback);\n\t},\n\n\tdispatch: <T>(eventType: string, data?: T, usedBy?: string): void => {\n\t\tbus().dispatch(eventType, data, usedBy);\n\t},\n\n\taddMiddleware: <T>(middleware: TLankaEventBusMiddleware<T>): void => {\n\t\tbus().addMiddleware(middleware);\n\t},\n\n\tremoveMiddleware: <T>(middleware: TLankaEventBusMiddleware<T>): void => {\n\t\tbus().removeMiddleware(middleware);\n\t},\n\n\taddObserver: (observer: TLankaEventBusObserver): void => {\n\t\tbus().addObserver(observer);\n\t},\n\n\tremoveObserver: (observer: TLankaEventBusObserver): void => {\n\t\tbus().removeObserver(observer);\n\t},\n\n\tgetEventLogs: (eventType?: string, limit = 100): ILankaEventLog[] =>\n\t\tbus().getEventLogs(eventType, limit),\n\n\tclearEvent: (eventType: string): void => {\n\t\tbus().clearEvent(eventType);\n\t},\n\n\tclearAllEvents: (): void => {\n\t\tbus().clearAllEvents();\n\t},\n\n\treset: (): void => {\n\t\tbus().reset();\n\t},\n});\n","import { requireActiveRuntime } from \"../../../_internal/active-runtime/activeRuntime\";\nimport { ILankaScenarioVM } from \"../../_interfaces/ILankaScenarioVM\";\n\n/**\n * The registry of ViewModels that use scenarios.\n *\n * They arrive by themselves: the factory registers a ViewModel when it declares\n * scenario handlers.\n */\nexport class LankaScenarioVMRegistry {\n\tprivate registeredViewModels: Set<ILankaScenarioVM> = new Set();\n\n\t/**\n\t * Public: the registry belongs to a framework instance rather than to the\n\t * module.\n\t */\n\tpublic constructor() {}\n\n\t/**\n\t * The active instance's registry, for callers that cannot hold one — the\n\t * static `LankaScenarioBootstrap`. Instance holders read `lanka.viewModels`.\n\t */\n\tpublic static getInstance(): LankaScenarioVMRegistry {\n\t\treturn requireActiveRuntime().viewModels;\n\t}\n\n\t/** Whether this ViewModel is already registered. */\n\tpublic isRegistered(viewModel: ILankaScenarioVM): boolean {\n\t\treturn this.registeredViewModels.has(viewModel);\n\t}\n\n\t/**\n\t * Registers a ViewModel.\n\t *\n\t * @returns `false` when it was already registered\n\t */\n\tpublic register(viewModel: ILankaScenarioVM): boolean {\n\t\tif (this.registeredViewModels.has(viewModel)) {\n\t\t\treturn false;\n\t\t}\n\t\tthis.registeredViewModels.add(viewModel);\n\t\treturn true;\n\t}\n\n\t/** Removes a ViewModel from the registry. */\n\tpublic unregister(viewModel: ILankaScenarioVM): void {\n\t\tthis.registeredViewModels.delete(viewModel);\n\t}\n\n\t/** Every registered ViewModel. */\n\tpublic getAllViewModels(): ILankaScenarioVM[] {\n\t\treturn Array.from(this.registeredViewModels);\n\t}\n\n\t/**\n\t * Clears every registered ViewModel. Required by tests.\n\t */\n\tpublic clear(): void {\n\t\tthis.registeredViewModels.clear();\n\t}\n\n\t/**\n\t * Unsubscribes every registered ViewModel from its scenarios and clears the\n\t * registry.\n\t *\n\t * Without it tests are not isolated: a subscription leaks from test to test.\n\t */\n\tpublic resetAll(): void {\n\t\tthis.registeredViewModels.forEach((vm) => {\n\t\t\ttry {\n\t\t\t\tvm.resetScenario();\n\t\t\t} catch {\n\t\t\t\t// One failing to reset is no reason to fail the rest.\n\t\t\t}\n\t\t});\n\t\tthis.clear();\n\t}\n}\n","import { requireActiveRuntime } from \"../../../_internal/active-runtime/activeRuntime\";\nimport { ILankaScenario } from \"../../_interfaces/ILankaScenario\";\nimport { ALankaScenario } from \"../../_abstractions/lanka-scenario/ALankaScenario\";\nimport { ILankaScenarioVM } from \"../../_interfaces/ILankaScenarioVM\";\nimport { LankaScenarioVMRegistry } from \"../lanka-scenario-vm-registry/LankaScenarioVMRegistry\";\nimport { lankaLogger } from \"../../../logger/lanka-logger/LankaLogger\";\nimport type { ILankaScenarioMetadata } from \"../../_interfaces/ILankaScenarioMetadata\";\n\n/**\n * Runs a scenario's `cleanup`, if it declared one, and contains what it throws.\n *\n * `ILankaScenario` promises `cleanup` is \"called when the scenario is removed\n * from the registry\", and for a long time nothing called it: a scenario whose\n * `initialize` opened something had no way to close it, and the promise read\n * as kept. Contained, because a removal runs over MANY scenarios — on dispose,\n * on the between-tests reset — and one failing to let go is no reason to leave\n * the rest registered.\n */\nconst release = (scenario: ILankaScenario<unknown>): void => {\n\tif (!scenario.cleanup) return;\n\ttry {\n\t\tscenario.cleanup();\n\t} catch (error) {\n\t\tlankaLogger.printScenarioLog(`Error cleaning up scenario \"${scenario.name}\":`, error);\n\t}\n};\n\n/**\n * The scenario registry: both those that registered themselves and those\n * registered by hand.\n *\n * `ALankaScenario` subclasses arrive here automatically unless they opted out.\n */\nexport class LankaScenariosRegistry {\n\tprivate registeredScenarios: Map<string, ILankaScenario<unknown>> = new Map();\n\tprivate scenarioMetadata: Map<string, ILankaScenarioMetadata> = new Map();\n\n\t/**\n\t * Public: the registry belongs to a framework instance rather than to the\n\t * module.\n\t */\n\tpublic constructor() {}\n\n\t/**\n\t * The active instance's registry, for callers that cannot hold one:\n\t * `ALankaScenario` (a base class the consumer extends) and the static\n\t * `LankaScenarioBootstrap`. Instance holders read `lanka.scenarios`.\n\t */\n\tpublic static getInstance(): LankaScenariosRegistry {\n\t\treturn requireActiveRuntime().scenarios;\n\t}\n\n\t/**\n\t * Registers a scenario by hand.\n\t *\n\t * @param scenario What to register\n\t * @returns `false` when it was already registered\n\t */\n\tpublic register(scenario: ILankaScenario<unknown>): boolean {\n\t\tif (this.registeredScenarios.has(scenario.name)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tthis.registeredScenarios.set(scenario.name, scenario);\n\t\tthis.scenarioMetadata.set(scenario.name, {\n\t\t\tscenario,\n\t\t\tname: scenario.name,\n\t\t\teventType: scenario.eventType,\n\t\t\tdataTypeName: scenario.dataTypeName,\n\t\t\tisRegistered: true,\n\t\t});\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Removes a scenario, calling its `cleanup` if it declared one.\n\t *\n\t * @param scenarioName Name of the scenario to unregister\n\t * @returns true if unregistered successfully, false if not found\n\t */\n\tpublic unregister(scenarioName: string): boolean {\n\t\tconst scenario = this.registeredScenarios.get(scenarioName);\n\t\tif (!scenario) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst metadata = this.scenarioMetadata.get(scenarioName);\n\t\tif (metadata) {\n\t\t\tmetadata.isRegistered = false;\n\t\t}\n\n\t\tthis.registeredScenarios.delete(scenarioName);\n\t\trelease(scenario);\n\t\treturn true;\n\t}\n\n\t/**\n\t * Get metadata for a specific scenario\n\t * @param scenarioName Name of the scenario\n\t * @returns Scenario metadata or undefined if not found\n\t */\n\tpublic getMetadata(scenarioName: string): ILankaScenarioMetadata | undefined {\n\t\treturn this.scenarioMetadata.get(scenarioName);\n\t}\n\n\t/** Every registered scenario. */\n\tpublic getAllScenarios(): ILankaScenario<unknown>[] {\n\t\treturn Array.from(this.registeredScenarios.values());\n\t}\n\n\t/** Metadata of every scenario. */\n\tpublic getAllMetadata(): ILankaScenarioMetadata[] {\n\t\treturn Array.from(this.scenarioMetadata.values());\n\t}\n\n\t/** Whether a scenario with this name is registered. */\n\tpublic isRegistered(scenarioName: string): boolean {\n\t\treturn this.registeredScenarios.has(scenarioName);\n\t}\n\n\t/**\n\t * Collects every scenario that registered itself at construction.\n\t */\n\tpublic collectAutoRegisteredScenarios(): void {\n\t\t// Take those that registered themselves at construction.\n\t\tconst autoRegistered = ALankaScenario.getAutoRegisteredScenarios();\n\t\tautoRegistered.forEach((scenario) => {\n\t\t\tif (!this.registeredScenarios.has(scenario.name)) {\n\t\t\t\tthis.register(scenario);\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * A scenario by name.\n\t *\n\t * @returns `undefined` when there is none\n\t */\n\tpublic getScenarioByName(scenarioName: string): ILankaScenario<unknown> | undefined {\n\t\treturn this.registeredScenarios.get(scenarioName);\n\t}\n\n\t/** Every registered ViewModel. */\n\tpublic getAllViewModels(): ILankaScenarioVM[] {\n\t\treturn LankaScenarioVMRegistry.getInstance().getAllViewModels();\n\t}\n\n\t/**\n\t * Removes every scenario, calling each one's `cleanup`.\n\t *\n\t * What `dispose()` and the between-tests reset go through: `initialize` ran\n\t * for every scenario at bootstrap, and this is the one moment its mirror\n\t * image can run for all of them.\n\t */\n\tpublic clear(): void {\n\t\tconst scenarios = [...this.registeredScenarios.values()];\n\t\tthis.registeredScenarios.clear();\n\t\tthis.scenarioMetadata.clear();\n\t\tfor (const scenario of scenarios) release(scenario);\n\t}\n}\n","import type { TLankaReplayRequest } from \"../../event-bus/lanka-event-bus-instance/LankaEventBusInstance\";\nimport { ILankaScenario } from \"../../_interfaces/ILankaScenario\";\nimport { LankaScenariosRegistry } from \"../../_registries/lanka-scenarios-registry/LankaScenariosRegistry\";\nimport { lankaLogger } from \"../../../logger/lanka-logger/LankaLogger\";\nimport { lankaEventBus } from \"../../event-bus/_facades/lanka-event-bus/lankaEventBus\";\n\n/**\n * The base of a scenario — a named unit of coordination over the event bus.\n *\n * A subclass declares `name`, `eventType` and `dataTypeName`, calls `trigger()`\n * when the event happens, and ViewModels subscribe to it and unsubscribe.\n *\n * Scenarios register THEMSELVES unless the subclass sets the static\n * `skipAutoRegistration`.\n *\n * ```ts\n * export class SessionUpdated extends ALankaScenario<TSession> {\n * readonly name = \"SessionUpdated\";\n * readonly eventType = \"session.updated\";\n * readonly dataTypeName = \"TSession\";\n * }\n * ```\n */\nexport abstract class ALankaScenario<TData = void> implements ILankaScenario<TData> {\n\tabstract readonly name: string;\n\tabstract readonly eventType: string;\n\tabstract readonly dataTypeName: string;\n\n\t/** A subclass sets this to opt out of self-registration. */\n\tstatic skipAutoRegistration?: boolean;\n\n\t/**\n\t * The self-registration pool.\n\t *\n\t * Class-level deliberately: a registry of DEFINITIONS, not runtime state. The\n\t * classes come from one barrel and both framework instances must see the same\n\t * list — splitting it would be divergence, not isolation.\n\t */\n\tprivate static autoRegisteredScenarios: Set<ALankaScenario<unknown>> = new Set();\n\n\t/** Registers the scenario itself unless the subclass opted out. */\n\tconstructor() {\n\t\t// The constructor comes from the actual class, that is the subclass.\n\t\tconst constructor = this.constructor as typeof ALankaScenario;\n\n\t\t// Register unless opted out.\n\t\tif (!constructor.skipAutoRegistration) {\n\t\t\tALankaScenario.autoRegisteredScenarios.add(this);\n\t\t}\n\t}\n\n\t/**\n\t * Every scenario that registered itself.\n\t *\n\t * @internal\n\t */\n\tpublic static getAutoRegisteredScenarios(): ALankaScenario<unknown>[] {\n\t\treturn Array.from(ALankaScenario.autoRegisteredScenarios);\n\t}\n\n\t/**\n\t * Clears the auto-registration pool. Required by tests.\n\t * @internal\n\t */\n\tpublic static clearAutoRegisteredScenarios(): void {\n\t\tALankaScenario.autoRegisteredScenarios.clear();\n\t}\n\n\t/**\n\t * Registers the scenario by hand.\n\t *\n\t * @returns `false` when it was already registered\n\t */\n\tpublic register(): boolean {\n\t\treturn LankaScenariosRegistry.getInstance().register(this);\n\t}\n\n\t/**\n\t * Removes the scenario from the registry.\n\t *\n\t * @returns `false` when it was not there\n\t */\n\tpublic unregister(): boolean {\n\t\treturn LankaScenariosRegistry.getInstance().unregister(this.name);\n\t}\n\n\ttrigger(data?: TData): void {\n\t\tlankaLogger.printScenarioLog(\"TRIGGER Scenario\", this.name, data);\n\t\tlankaEventBus.dispatch(this.eventType, data, this.name);\n\t}\n\n\t/**\n\t * Subscribes a handler and returns an unsubscribe function.\n\t *\n\t * Without the return, unsubscribing is possible only by callback identity, and\n\t * every ViewModel factory has to keep a map of references for it.\n\t */\n\tsubscribe(\n\t\tcallback: (data?: TData) => void,\n\t\toptions?: {\n\t\t\tpriority?: number;\n\t\t\treplay?: TLankaReplayRequest;\n\t\t\tusedBy?: string;\n\t\t},\n\t): () => void {\n\t\tlankaLogger.printScenarioLog(\"SUBSCRIBE Scenario\", this.name, this.dataTypeName);\n\t\treturn lankaEventBus.subscribe(this.eventType, callback, {\n\t\t\t...options,\n\t\t\tusedBy: options?.usedBy ?? this.name,\n\t\t});\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
@@ -33,6 +33,29 @@ var ALankaLocator = class {
|
|
|
33
33
|
notFoundError: config.notFoundError ?? ((name, propertyName) => `Instance "${name}" (accessed as "${propertyName}") not found.`)
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* The class a name resolves to: hand-registered FIRST, then the consumer's
|
|
38
|
+
* barrel.
|
|
39
|
+
*
|
|
40
|
+
* One owner for both callers, and that is the whole point of it existing.
|
|
41
|
+
* `registeredClasses` used to be read inside each locator's own
|
|
42
|
+
* `findClassByName`, and only two of the four did it — singletons and shared
|
|
43
|
+
* stores worked, gateways and scenarios silently dropped the class. Moving the
|
|
44
|
+
* read into `getInstanceByName` fixed those two and broke a third path:
|
|
45
|
+
* `LankaSingletonLocator.createScopedInstance` calls `findClassByName`
|
|
46
|
+
* directly, so a registered class stopped resolving inside a scope. Ten tests
|
|
47
|
+
* said so.
|
|
48
|
+
*
|
|
49
|
+
* Both callers go through here now. A fourth caller added later gets the
|
|
50
|
+
* precedence for free instead of having to remember it.
|
|
51
|
+
*
|
|
52
|
+
* The precedence itself matches `registeredInstances` below: what a caller
|
|
53
|
+
* handed over wins over what the barrel happens to export under that name,
|
|
54
|
+
* because the caller is usually a test substituting a double.
|
|
55
|
+
*/
|
|
56
|
+
classFor(instanceName) {
|
|
57
|
+
return this.registeredClasses.get(instanceName) ?? this.config.findClassByName(instanceName);
|
|
58
|
+
}
|
|
36
59
|
/**
|
|
37
60
|
* An object by class name.
|
|
38
61
|
*
|
|
@@ -53,7 +76,7 @@ var ALankaLocator = class {
|
|
|
53
76
|
return instance;
|
|
54
77
|
}
|
|
55
78
|
}
|
|
56
|
-
const Class = this.
|
|
79
|
+
const Class = this.classFor(instanceName);
|
|
57
80
|
if (Class) {
|
|
58
81
|
const instance = this.config.createInstance(Class);
|
|
59
82
|
this.instanceCache.set(instanceName, instance);
|
|
@@ -117,25 +140,22 @@ var ALankaLocator = class {
|
|
|
117
140
|
}
|
|
118
141
|
};
|
|
119
142
|
|
|
120
|
-
// src/locator/_internal/findExportedClass.ts
|
|
143
|
+
// src/locator/_internal/find-exported-class/findExportedClass.ts
|
|
121
144
|
function findExportedClass(module, exportName) {
|
|
122
145
|
if (!(exportName in module)) return void 0;
|
|
146
|
+
if (!Object.hasOwn(module, exportName)) return void 0;
|
|
123
147
|
const candidate = module[exportName];
|
|
124
148
|
return typeof candidate === "function" && candidate.prototype ? candidate : void 0;
|
|
125
149
|
}
|
|
126
150
|
|
|
127
|
-
// ../tools/testing/_fixtures/.lanka_di/Singletons.ts
|
|
128
|
-
var Singletons_exports = {};
|
|
129
|
-
|
|
130
151
|
// src/locator/singleton/lanka-singleton-locator/LankaSingletonLocator.ts
|
|
152
|
+
import * as SingletonsModule from "@lanka_di/Singletons";
|
|
131
153
|
var LankaSingletonLocator = class extends ALankaLocator {
|
|
132
154
|
singletonIndexModule;
|
|
133
155
|
constructor(config) {
|
|
134
156
|
super({
|
|
135
157
|
findClassByName: (className) => {
|
|
136
|
-
|
|
137
|
-
if (registered) return registered;
|
|
138
|
-
return findExportedClass(Singletons_exports, className) ?? (this.singletonIndexModule ? findExportedClass(this.singletonIndexModule, className) : void 0);
|
|
158
|
+
return findExportedClass(SingletonsModule, className) ?? (this.singletonIndexModule ? findExportedClass(this.singletonIndexModule, className) : void 0);
|
|
139
159
|
},
|
|
140
160
|
createInstance: (Class) => {
|
|
141
161
|
return new Class();
|
|
@@ -152,7 +172,7 @@ var LankaSingletonLocator = class extends ALankaLocator {
|
|
|
152
172
|
* would replace the application's root object.
|
|
153
173
|
*/
|
|
154
174
|
createScopedInstance(className, propertyName) {
|
|
155
|
-
const Class = this.
|
|
175
|
+
const Class = this.classFor(className);
|
|
156
176
|
if (!Class) {
|
|
157
177
|
throw new Error(this.config.notFoundError(className, propertyName));
|
|
158
178
|
}
|
|
@@ -160,21 +180,15 @@ var LankaSingletonLocator = class extends ALankaLocator {
|
|
|
160
180
|
}
|
|
161
181
|
};
|
|
162
182
|
|
|
163
|
-
// ../tools/testing/_fixtures/.lanka_di/SharedStores.ts
|
|
164
|
-
var SharedStores_exports = {};
|
|
165
|
-
|
|
166
183
|
// src/locator/shared-store/lanka-shared-store-locator/LankaSharedStoreLocator.ts
|
|
184
|
+
import * as SharedStoresModule from "@lanka_di/SharedStores";
|
|
167
185
|
var LankaSharedStoreLocator = class extends ALankaLocator {
|
|
168
186
|
sharedStoreIndexModule;
|
|
169
187
|
constructor(config) {
|
|
170
188
|
super({
|
|
171
189
|
findClassByName: (className) => {
|
|
172
|
-
const registeredClass = this.registeredClasses.get(className);
|
|
173
|
-
if (registeredClass) {
|
|
174
|
-
return registeredClass;
|
|
175
|
-
}
|
|
176
190
|
return findExportedClass(
|
|
177
|
-
|
|
191
|
+
SharedStoresModule,
|
|
178
192
|
className
|
|
179
193
|
) ?? (this.sharedStoreIndexModule ? findExportedClass(
|
|
180
194
|
this.sharedStoreIndexModule,
|
|
@@ -197,4 +211,4 @@ export {
|
|
|
197
211
|
LankaSingletonLocator,
|
|
198
212
|
LankaSharedStoreLocator
|
|
199
213
|
};
|
|
200
|
-
//# sourceMappingURL=chunk-
|
|
214
|
+
//# sourceMappingURL=chunk-UE2C76OR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/_internal/case-convert/caseConvert.ts","../src/locator/_abstractions/lanka-locator/ALankaLocator.ts","../src/locator/_internal/find-exported-class/findExportedClass.ts","../src/locator/singleton/lanka-singleton-locator/LankaSingletonLocator.ts","../src/locator/shared-store/lanka-shared-store-locator/LankaSharedStoreLocator.ts"],"sourcesContent":["export function caseConvert(\n\tstr: string,\n\ttoCase: \"camelCase\" | \"kebabCase\" | \"snakeCase\" | \"pascalCase\" | \"titleCase\",\n): string {\n\tif (!str || typeof str !== \"string\") return str;\n\n\tconst words = str\n\t\t.replace(/([A-Z])/g, \" $1\")\n\t\t.trim()\n\t\t.split(/\\s+|_|-/)\n\t\t.filter((word) => word);\n\n\tswitch (toCase) {\n\t\tcase \"camelCase\":\n\t\t\treturn words\n\t\t\t\t.map((word, index) =>\n\t\t\t\t\tindex === 0\n\t\t\t\t\t\t? word.toLowerCase()\n\t\t\t\t\t\t: word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(),\n\t\t\t\t)\n\t\t\t\t.join(\"\");\n\t\tcase \"kebabCase\":\n\t\t\treturn words.map((word) => word.toLowerCase()).join(\"-\");\n\t\tcase \"snakeCase\":\n\t\t\treturn words.map((word) => word.toLowerCase()).join(\"_\");\n\t\tcase \"pascalCase\":\n\t\t\treturn words\n\t\t\t\t.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n\t\t\t\t.join(\"\");\n\t\tcase \"titleCase\":\n\t\t\treturn words\n\t\t\t\t.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n\t\t\t\t.join(\" \");\n\t\tdefault:\n\t\t\treturn str;\n\t}\n}\n","import type { ILankaLocator } from \"../../_interfaces/ILankaLocator\";\nimport { caseConvert } from \"../../../_internal/case-convert/caseConvert\";\n\n/**\n * Base locator configuration.\n */\nexport interface ILankaLocatorConfig<TInstance> {\n\t/** How to find a constructor by class name. */\n\tfindClassByName: (name: string) => (new () => TInstance) | undefined;\n\t/** How to create an object from a constructor. */\n\tcreateInstance?: (Class: new () => TInstance) => TInstance;\n\t/** Custom resolution by name, when the ordinary one is not enough. */\n\tgetInstanceByName?: (name: string) => TInstance | undefined;\n\t/** What to say when there is no such object. */\n\tnotFoundError?: (name: string, propertyName: string) => string;\n}\n\n/**\n * The locator base: resolves objects by property name (camelCase), translating\n * it into a class name (PascalCase).\n *\n * Handles what every locator shares — name translation, construction on first\n * use from the barrel's exports, and caching what was constructed.\n */\nexport abstract class ALankaLocator<TInstance> implements ILankaLocator<TInstance> {\n\tprotected readonly instanceCache: Map<string, TInstance> = new Map();\n\tprotected readonly registeredClasses: Map<string, new () => TInstance> = new Map();\n\tprotected readonly registeredInstances: Map<string, TInstance> = new Map();\n\tprotected readonly config: ILankaLocatorConfig<TInstance> & {\n\t\tcreateInstance: (Class: new () => TInstance) => TInstance;\n\t\tnotFoundError: (name: string, propertyName: string) => string;\n\t};\n\n\tprotected constructor(config: ILankaLocatorConfig<TInstance>) {\n\t\t// The defaults are applied AFTER the spread, so an explicit `undefined` —\n\t\t// which a caller forwarding its own optional config passes — falls back\n\t\t// instead of removing the only thing that can construct an instance.\n\t\tthis.config = {\n\t\t\t...config,\n\t\t\tcreateInstance: config.createInstance ?? ((Class) => new Class()),\n\t\t\tnotFoundError:\n\t\t\t\tconfig.notFoundError ??\n\t\t\t\t((name, propertyName) =>\n\t\t\t\t\t`Instance \"${name}\" (accessed as \"${propertyName}\") not found.`),\n\t\t};\n\t}\n\n\t/**\n\t * The class a name resolves to: hand-registered FIRST, then the consumer's\n\t * barrel.\n\t *\n\t * One owner for both callers, and that is the whole point of it existing.\n\t * `registeredClasses` used to be read inside each locator's own\n\t * `findClassByName`, and only two of the four did it — singletons and shared\n\t * stores worked, gateways and scenarios silently dropped the class. Moving the\n\t * read into `getInstanceByName` fixed those two and broke a third path:\n\t * `LankaSingletonLocator.createScopedInstance` calls `findClassByName`\n\t * directly, so a registered class stopped resolving inside a scope. Ten tests\n\t * said so.\n\t *\n\t * Both callers go through here now. A fourth caller added later gets the\n\t * precedence for free instead of having to remember it.\n\t *\n\t * The precedence itself matches `registeredInstances` below: what a caller\n\t * handed over wins over what the barrel happens to export under that name,\n\t * because the caller is usually a test substituting a double.\n\t */\n\tprotected classFor(instanceName: string): (new () => TInstance) | undefined {\n\t\treturn (\n\t\t\tthis.registeredClasses.get(instanceName) ?? this.config.findClassByName(instanceName)\n\t\t);\n\t}\n\n\t/**\n\t * An object by class name.\n\t *\n\t * The cache first, then custom resolution when configured, and only then\n\t * construction.\n\t */\n\tprotected getInstanceByName(instanceName: string): TInstance | undefined {\n\t\tif (this.instanceCache.has(instanceName)) {\n\t\t\treturn this.instanceCache.get(instanceName);\n\t\t}\n\n\t\t// What was handed to us already built. Ahead of the barrel, and ahead of\n\t\t// custom resolution: a test's double must win over what the barrel exports.\n\t\tif (this.registeredInstances.has(instanceName)) {\n\t\t\treturn this.registeredInstances.get(instanceName);\n\t\t}\n\n\t\t// Custom resolution when configured.\n\t\tif (this.config.getInstanceByName) {\n\t\t\tconst instance = this.config.getInstanceByName(instanceName);\n\t\t\tif (instance) {\n\t\t\t\tthis.instanceCache.set(instanceName, instance);\n\t\t\t\treturn instance;\n\t\t\t}\n\t\t}\n\n\t\t// Otherwise find the class and construct.\n\t\tconst Class = this.classFor(instanceName);\n\t\tif (Class) {\n\t\t\tconst instance = this.config.createInstance(Class);\n\t\t\tthis.instanceCache.set(instanceName, instance);\n\t\t\treturn instance;\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * An object by property name (camelCase).\n\t *\n\t * Throws when there is none: a silent `undefined` would surface layers later.\n\t */\n\tpublic get(propertyName: string): TInstance {\n\t\tconst instanceName = caseConvert(propertyName, \"pascalCase\");\n\t\tconst instance = this.getInstanceByName(instanceName);\n\n\t\tif (!instance) {\n\t\t\tthrow new Error(this.config.notFoundError(instanceName, propertyName));\n\t\t}\n\n\t\treturn instance;\n\t}\n\n\t/**\n\t * Clears the instance cache. Required by tests.\n\t */\n\tpublic clearCache(): void {\n\t\tthis.instanceCache.clear();\n\t}\n\n\t// ── Registration by hand ─────────────────────────────────────────────────\n\t//\n\t// Every locator needs it, for the same two reasons: a test supplies a double,\n\t// and an application supplies an object it built itself. It lives here rather\n\t// than in each locator because three copies of four one-line methods are three\n\t// places for the cache invalidation to be forgotten.\n\n\t/**\n\t * Registers a class by hand, ahead of the consumer's barrel.\n\t *\n\t * The cache entry is dropped: what it holds was built from the previous class.\n\t */\n\tpublic register(className: string, Class: new () => TInstance): void {\n\t\tthis.registeredClasses.set(className, Class);\n\t\tthis.instanceCache.delete(className);\n\t}\n\n\t/**\n\t * Registers an already-built object by hand.\n\t *\n\t * It goes straight into the cache: there is nothing left to construct, and a\n\t * later lookup must not build a rival.\n\t */\n\tpublic registerInstance(className: string, instance: TInstance): void {\n\t\tthis.registeredInstances.set(className, instance);\n\t\tthis.instanceCache.set(className, instance);\n\t}\n\n\t/** Removes a class or object registered by hand, and what was built from it. */\n\tpublic unregister(className: string): void {\n\t\tthis.registeredClasses.delete(className);\n\t\tthis.registeredInstances.delete(className);\n\t\tthis.instanceCache.delete(className);\n\t}\n\n\t/** Whether this name resolves to anything the locator already holds. */\n\tpublic isRegistered(className: string): boolean {\n\t\treturn (\n\t\t\tthis.registeredClasses.has(className) ||\n\t\t\tthis.registeredInstances.has(className) ||\n\t\t\tthis.instanceCache.has(className)\n\t\t);\n\t}\n}\n","/**\n * Finds a class in a module's ES namespace by name.\n *\n * An ES namespace is an ordinary object and `module[name]` is enough. Walking\n * `Reflect.ownKeys`, reading property descriptors and wrapping field access in\n * `try/catch` guards against what does not happen.\n *\n * ## Why a fallback pass over `Class.name` is worse than useless\n *\n * It is written for the case where the export name was lost to minification. But\n * `Class.name`.\n *\n * minification loses `Class.name` too — so the fallback looks for something a\n * built bundle no longer has, creating an impression of protection where there\n * is none.\n *\n * Resolution still works when the export name differs from the class name, which\n * `locator.contract.test.ts` pins: the lookup is by EXPORT KEY, and that is what\n * the consumer writes in the barrel.\n */\nexport function findExportedClass<TInstance>(\n\tmodule: object,\n\texportName: string,\n): (new () => TInstance) | undefined {\n\t// `in` first, then indexing. An ES namespace answers `undefined` for a missing\n\t// key, but a mocked module does not: vitest returns a proxy that THROWS on an\n\t// unknown key.\n\tif (!(exportName in module)) return undefined;\n\n\t// OWN properties only, and `in` alone was not enough. It walks the prototype\n\t// chain, so `\"constructor\" in module` is true for any plain object and\n\t// `module.constructor` is `Object` — a function WITH a prototype, which both\n\t// checks below accept. The locator would have handed back a class the barrel\n\t// never exported. `toString` and its siblings only escaped by luck: built-in\n\t// methods have no `.prototype`.\n\t//\n\t// It is asked SECOND, never first: `in` is what tolerates a vitest module\n\t// proxy, and this narrowing is only ever consulted for a key that already\n\t// exists. A real ES namespace has a null prototype, so nothing changes there.\n\tif (!Object.hasOwn(module, exportName)) return undefined;\n\n\tconst candidate = (module as Record<string, unknown>)[exportName];\n\n\t// `typeof === \"function\"` would also admit a plain function: it has a\n\t// `prototype` too. They cannot be told apart before calling, and that is a\n\t// deliberate boundary — the locator trusts the barrel, and `@lankajs/tool-di`\n\t// verifies the barrel.\n\treturn typeof candidate === \"function\" && candidate.prototype\n\t\t? (candidate as new () => TInstance)\n\t\t: undefined;\n}\n","import { ALankaLocator } from \"../../_abstractions/lanka-locator/ALankaLocator\";\nimport { findExportedClass } from \"../../_internal/find-exported-class/findExportedClass\";\n/**\n * Classes come from the consumer's barrel.\n *\n * Adding one is ONE export line: types are inferred, autocomplete works, and no\n * list has to be maintained. A list maintained by hand is a list that eventually\n * falls behind the code.\n */\nimport * as SingletonsModule from \"@lanka_di/Singletons\";\n\n/**\n * LankaSingletonLocator configuration.\n */\nexport interface ILankaSingletonLocatorConfig {\n\t/** The module holding singleton classes — the consumer's barrel. */\n\tsingletonIndexModule?: Record<string, unknown>;\n}\n\n/**\n * Resolves singletons by property name (camelCase) or class name (PascalCase),\n * constructing them on first use and caching them.\n *\n * A class arrives either from the consumer's barrel or registered by hand — the\n * latter for tests and for objects that arrive already built.\n */\nexport class LankaSingletonLocator extends ALankaLocator<unknown> {\n\tprivate readonly singletonIndexModule?: Record<string, unknown>;\n\n\tconstructor(config?: ILankaSingletonLocatorConfig) {\n\t\tsuper({\n\t\t\tfindClassByName: (className: string) => {\n\t\t\t\t// Order: the application's barrel, then an extra module if one was\n\t\t\t\t// given. Hand-registered classes are read by `ALankaLocator` itself,\n\t\t\t\t// ahead of this callback — they used to be read here, and the copy is\n\t\t\t\t// what let the gateway and scenario locators forget them entirely.\n\t\t\t\t//\n\t\t\t\t// Each source is checked ONCE, by export key. A fallback pass over\n\t\t\t\t// `Class.name` looks for something a built bundle no longer has:\n\t\t\t\t// minification loses class names exactly as it loses export names.\n\t\t\t\treturn (\n\t\t\t\t\tfindExportedClass<unknown>(SingletonsModule, className) ??\n\t\t\t\t\t(this.singletonIndexModule\n\t\t\t\t\t\t? findExportedClass<unknown>(this.singletonIndexModule, className)\n\t\t\t\t\t\t: undefined)\n\t\t\t\t);\n\t\t\t},\n\t\t\tcreateInstance: (Class) => {\n\t\t\t\treturn new Class();\n\t\t\t},\n\t\t\tnotFoundError: (className, propertyName) =>\n\t\t\t\t`Singleton \"${className}\" (accessed as \"${propertyName}\") not found. ` +\n\t\t\t\t`Make sure the class is exported from @lanka_di/Singletons.ts or registered via registerSingleton method.`,\n\t\t});\n\n\t\tthis.singletonIndexModule = config?.singletonIndexModule;\n\t}\n\n\t/**\n\t * Creates a NEW service object, bypassing the locator's cache.\n\t *\n\t * For scopes: they take the class from here and set the lifetime themselves.\n\t * The root cache is untouched — otherwise the first resolution inside a scope\n\t * would replace the application's root object.\n\t */\n\tpublic createScopedInstance(className: string, propertyName: string): unknown {\n\t\t// `classFor`, not `config.findClassByName`: a hand-registered class must\n\t\t// resolve inside a scope exactly as it does at the root.\n\t\tconst Class = this.classFor(className);\n\t\tif (!Class) {\n\t\t\tthrow new Error(this.config.notFoundError(className, propertyName));\n\t\t}\n\t\treturn this.config.createInstance(Class);\n\t}\n}\n","import { ALankaLocator } from \"../../_abstractions/lanka-locator/ALankaLocator\";\nimport { findExportedClass } from \"../../_internal/find-exported-class/findExportedClass\";\nimport { ALankaSharedStore } from \"../../../viewmodel/_abstractions/lanka-shared-store/ALankaSharedStore\";\nimport * as SharedStoresModule from \"@lanka_di/SharedStores\";\n\ntype TSharedStoreState = object;\n\n/**\n * LankaSharedStoreLocator configuration.\n */\nexport interface ILankaSharedStoreLocatorConfig {\n\t/** The module holding shared store classes — the consumer's barrel. */\n\tsharedStoreIndexModule?: Record<string, unknown>;\n}\n\n/**\n * Resolves shared stores by property name (camelCase) or class name\n * (PascalCase), constructing them on first use and caching them.\n */\nexport class LankaSharedStoreLocator extends ALankaLocator<ALankaSharedStore<TSharedStoreState>> {\n\tprivate readonly sharedStoreIndexModule?: Record<string, unknown>;\n\n\tconstructor(config?: ILankaSharedStoreLocatorConfig) {\n\t\tsuper({\n\t\t\tfindClassByName: (className: string) => {\n\t\t\t\t// Hand-registered classes are read by `ALankaLocator` itself, ahead of\n\t\t\t\t// this callback. This is only the barrel.\n\t\t\t\treturn (\n\t\t\t\t\tfindExportedClass<ALankaSharedStore<TSharedStoreState>>(\n\t\t\t\t\t\tSharedStoresModule,\n\t\t\t\t\t\tclassName,\n\t\t\t\t\t) ??\n\t\t\t\t\t(this.sharedStoreIndexModule\n\t\t\t\t\t\t? findExportedClass<ALankaSharedStore<TSharedStoreState>>(\n\t\t\t\t\t\t\t\tthis.sharedStoreIndexModule,\n\t\t\t\t\t\t\t\tclassName,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t: undefined)\n\t\t\t\t);\n\t\t\t},\n\t\t\tcreateInstance: (Class) => {\n\t\t\t\treturn new Class();\n\t\t\t},\n\t\t\tnotFoundError: (className, propertyName) =>\n\t\t\t\t`SharedStore \"${className}\" (accessed as \"${propertyName}\") not found. ` +\n\t\t\t\t`Make sure the class extends ALankaSharedStore and is exported from @lanka_di/SharedStores.ts ` +\n\t\t\t\t`or registered via registerSharedStore method.`,\n\t\t});\n\n\t\tthis.sharedStoreIndexModule = config?.sharedStoreIndexModule;\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
@@ -2,9 +2,8 @@ import {
|
|
|
2
2
|
ALankaScenario,
|
|
3
3
|
LankaScenarioVMRegistry,
|
|
4
4
|
LankaScenariosRegistry,
|
|
5
|
-
Scenarios_exports,
|
|
6
5
|
lankaEventBus
|
|
7
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-O5ROO7QF.js";
|
|
8
7
|
import {
|
|
9
8
|
lankaLogger
|
|
10
9
|
} from "./chunk-C2HP7CRD.js";
|
|
@@ -14,6 +13,7 @@ import {
|
|
|
14
13
|
} from "./chunk-BGVDPDX4.js";
|
|
15
14
|
|
|
16
15
|
// src/scenario/lanka-scenario-bootstrap/LankaScenarioBootstrap.ts
|
|
16
|
+
import * as ScenariosModule from "@lanka_di/Scenarios";
|
|
17
17
|
var LankaScenarioBootstrap = class {
|
|
18
18
|
/**
|
|
19
19
|
* The bootstrapped flag and the set of initialised VMs live on the INSTANCE.
|
|
@@ -29,8 +29,12 @@ var LankaScenarioBootstrap = class {
|
|
|
29
29
|
return this.state.bootstrapped;
|
|
30
30
|
}
|
|
31
31
|
forceInstantiateAllScenarios() {
|
|
32
|
-
|
|
32
|
+
const pooled = new Set(
|
|
33
|
+
ALankaScenario.getAutoRegisteredScenarios().map((scenario) => scenario.constructor)
|
|
34
|
+
);
|
|
35
|
+
for (const exported of Object.values(ScenariosModule)) {
|
|
33
36
|
if (typeof exported !== "function") continue;
|
|
37
|
+
if (pooled.has(exported)) continue;
|
|
34
38
|
try {
|
|
35
39
|
new exported();
|
|
36
40
|
} catch {
|
|
@@ -173,4 +177,4 @@ var lankaScenarioBootstrap = new LankaScenarioBootstrap();
|
|
|
173
177
|
export {
|
|
174
178
|
lankaScenarioBootstrap
|
|
175
179
|
};
|
|
176
|
-
//# sourceMappingURL=chunk-
|
|
180
|
+
//# sourceMappingURL=chunk-UGXSGQPW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/scenario/lanka-scenario-bootstrap/LankaScenarioBootstrap.ts"],"sourcesContent":["import type { ILankaScenario } from \"../_interfaces/ILankaScenario\";\nimport type { ILankaScenarioVM } from \"../_interfaces/ILankaScenarioVM\";\nimport { LankaScenariosRegistry } from \"../_registries/lanka-scenarios-registry/LankaScenariosRegistry\";\nimport { LankaScenarioVMRegistry } from \"../_registries/lanka-scenario-vm-registry/LankaScenarioVMRegistry\";\nimport { ALankaScenario } from \"../_abstractions/lanka-scenario/ALankaScenario\";\nimport { lankaLogger } from \"../../logger/lanka-logger/LankaLogger\";\n\n/**\n * Brings up the scenario layer — deliberately React-free.\n *\n * Two jobs: declare every scenario's events on the bus as early as possible,\n * before the first ViewModel exists; and let a ViewModel raise its own\n * subscriptions at construction.\n *\n * Called from the application entry point before ViewModels are imported.\n */\n\n// The barrel of every scenario class: what keeps bootstrap in step with the list.\nimport * as ScenariosModule from \"@lanka_di/Scenarios\";\nimport { lankaEventBus } from \"../event-bus/_facades/lanka-event-bus/lankaEventBus\";\nimport {\n\tgetActiveRuntime,\n\trequireActiveRuntime,\n} from \"../../_internal/active-runtime/activeRuntime\";\n\n/**\n * Bootstrapping the scenario layer, for callers with no instance in hand.\n *\n * An ordinary class with an ordinary instance: the declared-ViewModel list below\n * is deliberately shared across instances — every test builds a new framework and\n * module-level ViewModels are declared once — so the single instance IS the\n * shared list, and a second one would be a second list nobody adopts.\n */\nexport class LankaScenarioBootstrap {\n\t/**\n\t * The bootstrapped flag and the set of initialised VMs live on the INSTANCE.\n\t *\n\t * As static fields, a second framework in the same process would exit\n\t * `bootstrap()` on someone else's flag — never starting at all, with no symptom\n\t * except that no scenario works.\n\t */\n\tprivate get state() {\n\t\treturn requireActiveRuntime().scenarioState;\n\t}\n\n\tpublic isScenarioLayerBootstrapped(): boolean {\n\t\treturn this.state.bootstrapped;\n\t}\n\n\tprivate forceInstantiateAllScenarios(): void {\n\t\t// Every class the pool already holds an instance of. Constructing it again\n\t\t// would add a second instance to a pool nothing ever drains: each bootstrap\n\t\t// — one per framework instance, one per test — grew it by the whole barrel,\n\t\t// and both `collectAutoRegisteredScenarios` and the locator's fallback walk\n\t\t// it in full.\n\t\tconst pooled = new Set(\n\t\t\tALankaScenario.getAutoRegisteredScenarios().map((scenario) => scenario.constructor),\n\t\t);\n\n\t\tfor (const exported of Object.values(ScenariosModule)) {\n\t\t\t// Only class constructors are relevant.\n\t\t\tif (typeof exported !== \"function\") continue;\n\t\t\tif (pooled.has(exported)) continue;\n\n\t\t\ttry {\n\t\t\t\t// Constructing an ALankaScenario subclass puts it into the\n\t\t\t\t// self-registration pool — see ALankaScenario.\n\t\t\t\tnew (exported as unknown as new () => unknown)();\n\t\t\t} catch {\n\t\t\t\t// Not every export is constructible; skip those.\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate registerScenarioEvents(scenarios: ILankaScenario<unknown>[]): void {\n\t\tconst registeredEventTypes = new Set<string>();\n\n\t\tscenarios.forEach((scenario) => {\n\t\t\tif (!registeredEventTypes.has(scenario.eventType)) {\n\t\t\t\tregisteredEventTypes.add(scenario.eventType);\n\n\t\t\t\tlankaLogger.printScenarioLog(\"REGISTER SCENARIO EVENT\", scenario.name);\n\t\t\t\tlankaEventBus.registerEvent(scenario.eventType, {\n\t\t\t\t\tdataType: scenario.dataTypeName,\n\t\t\t\t\tdescription: `Scenario: ${scenario.name}`,\n\t\t\t\t\tusedBy: [scenario.name],\n\t\t\t\t\tpriority: 0,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (scenario.initialize) {\n\t\t\t\tlankaLogger.printScenarioLog(\"INITIALIZE SCENARIO\", scenario.name);\n\t\t\t\tscenario.initialize();\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate initializeAlreadyCreatedViewModels(): void {\n\t\tconst viewModels = LankaScenarioVMRegistry.getInstance().getAllViewModels();\n\t\tviewModels.forEach((vm) => {\n\t\t\tif (!this.state.initialized.has(vm)) {\n\t\t\t\tthis.state.initialized.add(vm);\n\t\t\t\tvm.initializeScenario();\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Brings up the scenario layer.\n\t *\n\t * Constructs the scenario classes so they can be collected, declares their\n\t * events on the bus, calls `initialize()` where present, and initialises\n\t * ViewModels that already exist — which happens in tests and under unusual\n\t * import order.\n\t *\n\t * Idempotent.\n\t */\n\tpublic bootstrap(): void {\n\t\tif (this.state.bootstrapped) return;\n\n\t\tlankaLogger.printScenarioLog(\"BOOTSTRAP SCENARIOS START\");\n\n\t\t// Instances must exist, or the registry has nothing to collect.\n\t\tthis.forceInstantiateAllScenarios();\n\n\t\tconst registry = LankaScenariosRegistry.getInstance();\n\t\tregistry.collectAutoRegisteredScenarios();\n\n\t\tconst scenarios = registry.getAllScenarios();\n\t\tthis.registerScenarioEvents(scenarios);\n\n\t\tthis.state.bootstrapped = true;\n\n\t\t// Some ViewModels may predate bootstrap — in tests, or under unusual import\n\t\t// order; initialise them now.\n\t\tthis.initializeAlreadyCreatedViewModels();\n\n\t\tlankaLogger.printScenarioLog(\"BOOTSTRAP SCENARIOS FINISH\");\n\t}\n\n\t/**\n\t * Called by a ViewModel factory when the ViewModel declares scenario handlers.\n\t *\n\t * Registers the ViewModel and, if the layer is already up, initialises it\n\t * immediately. Duplicate registration and duplicate initialisation are\n\t * impossible.\n\t *\n\t * @param viewModel What to register\n\t * @param name Name for the log\n\t */\n\t/**\n\t * ViewModels DECLARED in this process.\n\t *\n\t * Module-level deliberately, the same case as the scenario pool: a registry of\n\t * DEFINITIONS, not runtime state. Splitting it between instances would be\n\t * divergence, not isolation — the classes come from the same modules and both\n\t * instances must see one list.\n\t *\n\t * Two reasons it exists:\n\t *\n\t * 1. Declaring a ViewModel is a declaration, not work. A screen file creates it\n\t * at module level, and import order decides whether that happens before or\n\t * after `createLanka()`. Requiring a live instance at declaration time\n\t * requires an import order the consumer does not control.\n\t * 2. Instances come one after another: every test creates its own. The list is\n\t * NOT drained by the first — otherwise the second instance would know no\n\t * module-level ViewModel and its `dispose()` would not remove their\n\t * subscriptions, so a test would receive events the previous one subscribed\n\t * to.\n\t */\n\tprivate readonly declaredViewModels: { viewModel: ILankaScenarioVM; name?: string }[] = [];\n\n\t/** Registers everything declared into a NEW instance. Called by `createLanka`. */\n\tpublic adoptDeclaredViewModels(): void {\n\t\tfor (const { viewModel, name } of [...this.declaredViewModels]) {\n\t\t\tthis.attachViewModel(viewModel, name);\n\t\t}\n\t}\n\n\tpublic registerViewModel(viewModel: ILankaScenarioVM, name?: string): void {\n\t\tconst isKnown = this.declaredViewModels.some(\n\t\t\t(declared) => declared.viewModel === viewModel,\n\t\t);\n\t\tif (!isKnown) this.declaredViewModels.push({ viewModel, name });\n\n\t\t// No instance yet: the declaration came before bootstrap, and whoever\n\t\t// bootstraps will pick it up.\n\t\tif (!getActiveRuntime()) return;\n\n\t\tthis.attachViewModel(viewModel, name);\n\t}\n\n\tprivate attachViewModel(viewModel: ILankaScenarioVM, name?: string): void {\n\t\tconst registry = LankaScenarioVMRegistry.getInstance();\n\n\t\t// Already registered — nothing to do.\n\t\tif (registry.isRegistered(viewModel)) {\n\t\t\tif (name) {\n\t\t\t\tlankaLogger.printViewModelLog(\"VM Already Registered (skipped)\", name);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Register it.\n\t\tregistry.register(viewModel);\n\n\t\tif (name) {\n\t\t\tlankaLogger.printViewModelLog(\"Register Scenario VM\", name);\n\t\t}\n\n\t\t// The layer is already up — initialise it now rather than at the next\n\t\t// bootstrap.\n\t\tif (this.state.bootstrapped) {\n\t\t\tif (!this.state.initialized.has(viewModel)) {\n\t\t\t\tthis.state.initialized.add(viewModel);\n\t\t\t\tviewModel.initializeScenario();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Resets the whole scenario layer. Without it tests are not isolated.\n\t *\n\t * Clears the self-registration list, the scenario registry with its metadata,\n\t * the ViewModel registry together with all their subscriptions, and the bus —\n\t * events and\n\t * middleware.\n\t */\n\tpublic reset(): void {\n\t\tALankaScenario.clearAutoRegisteredScenarios();\n\t\tLankaScenariosRegistry.getInstance().clear();\n\t\tLankaScenarioVMRegistry.getInstance().resetAll();\n\t\tlankaEventBus.clearAllEvents();\n\t\tthis.state.bootstrapped = false;\n\t\tthis.state.initialized = new WeakSet<ILankaScenarioVM>();\n\t}\n}\n\n/** The one every caller wants. */\nexport const lankaScenarioBootstrap = new LankaScenarioBootstrap();\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { I as ILankaScope } from './createLankaScope-BiFxNQgl.js';
|
|
2
|
+
import { I as ILankaRuntime } from './activeRuntime-B336NU5I.js';
|
|
3
|
+
import { a as ILankaHost, I as ILankaFlags } from './ILankaRuntimeConfig-Vl436GWK.js';
|
|
4
|
+
import { T as TLankaRequestMiddleware } from './lankaRequestMiddleware-DAC5kCb7.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A plugin is an extension core knows by SHAPE rather than by name.
|
|
8
|
+
*
|
|
9
|
+
* ## Plugin versus module
|
|
10
|
+
*
|
|
11
|
+
* A module is called by the application (`app → module`) and core does not know
|
|
12
|
+
* it exists. A plugin sits on the path core itself walks (`app → core → plugin`).
|
|
13
|
+
* The test question: does core need a hook for this to work? No — then it is a
|
|
14
|
+
* module, and making it a plugin costs more, because the hook has to be supported
|
|
15
|
+
* forever.
|
|
16
|
+
*
|
|
17
|
+
* ## Why `install` receives the instance
|
|
18
|
+
*
|
|
19
|
+
* So a plugin has no private route to the framework. Everything it can do comes
|
|
20
|
+
* from the instance it was given, which means two instances in one process (a
|
|
21
|
+
* test beside the app) do not share its configuration.
|
|
22
|
+
*/
|
|
23
|
+
interface ILankaPlugin {
|
|
24
|
+
/**
|
|
25
|
+
* The name the plugin is recognised by. Registering the same name twice is
|
|
26
|
+
* rejected: two copies of a retry policy would silently double the request
|
|
27
|
+
* count.
|
|
28
|
+
*/
|
|
29
|
+
readonly name: string;
|
|
30
|
+
/**
|
|
31
|
+
* Installation. The returned function removes everything the plugin installed
|
|
32
|
+
* and is called when the plugin is removed and when the instance is disposed.
|
|
33
|
+
*/
|
|
34
|
+
install: (lanka: ILankaInstance) => (() => void) | void;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* A framework instance: all framework state in one object.
|
|
39
|
+
*
|
|
40
|
+
* What instance-scoped state buys: two apps in one process (micro-frontends,
|
|
41
|
+
* Storybook beside the app) no longer share a bus, locator caches and mock mode;
|
|
42
|
+
* SSR does not reuse state between different users' requests; and test isolation
|
|
43
|
+
* rests on construction rather than on a global `beforeEach` reaching into
|
|
44
|
+
* internal registries.
|
|
45
|
+
*
|
|
46
|
+
* **Ambient facades** — `lankaEventBus.dispatch`, `lankaSingletons.foo`,
|
|
47
|
+
* `getLankaFlags()`, `lankaHttpInFlight` — resolve THE ONE active instance
|
|
48
|
+
* (`internal/activeRuntime.ts`). Isolation belongs to whoever holds an instance;
|
|
49
|
+
* a facade cannot offer it.
|
|
50
|
+
*
|
|
51
|
+
* One thing stays at module level deliberately: `ALankaScenario` collects
|
|
52
|
+
* constructed scenarios into a static pool. That is a REGISTRY OF DEFINITIONS,
|
|
53
|
+
* not runtime state — the classes come from one `@lanka_di/Scenarios` barrel and
|
|
54
|
+
* both instances must see the same list. Splitting it would be divergence.
|
|
55
|
+
*/
|
|
56
|
+
interface ILankaInstance extends ILankaRuntime {
|
|
57
|
+
/**
|
|
58
|
+
* Adds a wrapper around every request. Returns a function that removes it.
|
|
59
|
+
*
|
|
60
|
+
* This is how `@lankajs/plugin-http` installs retry, the idempotency key, the
|
|
61
|
+
* CSRF header and auth refresh.
|
|
62
|
+
*/
|
|
63
|
+
useRequestMiddleware(middleware: TLankaRequestMiddleware): () => void;
|
|
64
|
+
/** Default timeout for every request of this instance. */
|
|
65
|
+
setRequestTimeout(timeoutMs: number | undefined): void;
|
|
66
|
+
/** Resolves a service in the root scope — for the instance's whole lifetime. */
|
|
67
|
+
resolve<TInstance>(propertyName: string): TInstance;
|
|
68
|
+
/**
|
|
69
|
+
* Creates a scope: a lifetime shorter than the application's.
|
|
70
|
+
*
|
|
71
|
+
* An object created in a scope goes away with it.
|
|
72
|
+
*/
|
|
73
|
+
createScope(): ILankaScope;
|
|
74
|
+
/**
|
|
75
|
+
* Registers a plugin. Returns a function that removes it.
|
|
76
|
+
*
|
|
77
|
+
* The fifth and last extension point. An extension point declared before
|
|
78
|
+
* anything plugs into it describes an imagined need while costing real
|
|
79
|
+
* support, so this one arrived with the FIRST plugin.
|
|
80
|
+
*/
|
|
81
|
+
use(plugin: ILankaPlugin): () => void;
|
|
82
|
+
/** Runs services and the scenario layer. Idempotent. */
|
|
83
|
+
bootstrap(config?: ILankaBootstrapConfig): Promise<void>;
|
|
84
|
+
/** Has bootstrap already run? */
|
|
85
|
+
isBootstrapped(): boolean;
|
|
86
|
+
/** Makes this instance the one ambient facades resolve to. */
|
|
87
|
+
activate(): void;
|
|
88
|
+
/**
|
|
89
|
+
* Removes subscriptions, clears registries and, if this instance was active,
|
|
90
|
+
* clears the pointer. Returns the framework to its pre-bootstrap state.
|
|
91
|
+
*/
|
|
92
|
+
dispose(): void;
|
|
93
|
+
}
|
|
94
|
+
interface ILankaInstanceConfig {
|
|
95
|
+
host: ILankaHost;
|
|
96
|
+
flags?: ILankaFlags;
|
|
97
|
+
}
|
|
98
|
+
interface ILankaServiceConfig {
|
|
99
|
+
name?: string;
|
|
100
|
+
init: () => void | Promise<void>;
|
|
101
|
+
sync?: boolean;
|
|
102
|
+
priority?: number;
|
|
103
|
+
/**
|
|
104
|
+
* A failure of this service does not abort bootstrap.
|
|
105
|
+
*
|
|
106
|
+
* Without the flag one failed service takes the WHOLE phase with it: the async
|
|
107
|
+
* phase because of `Promise.all`, the sync phase because later tasks never run.
|
|
108
|
+
* Wrapping the failure in a swallowing `try/catch` is worse still — the app
|
|
109
|
+
* starts with a partially executed plan and does not know it.
|
|
110
|
+
*/
|
|
111
|
+
optional?: boolean;
|
|
112
|
+
/**
|
|
113
|
+
* How long to wait for the service. Overrunning counts as a failure.
|
|
114
|
+
*
|
|
115
|
+
* Without a deadline a service that never settles holds bootstrap forever and
|
|
116
|
+
* the app never paints its first screen. Failing is more honest than waiting:
|
|
117
|
+
* an optional service is skipped, a required one names itself.
|
|
118
|
+
*/
|
|
119
|
+
timeoutMs?: number;
|
|
120
|
+
}
|
|
121
|
+
interface ILankaScenarioBootstrapConfig {
|
|
122
|
+
sync?: boolean;
|
|
123
|
+
priority?: number;
|
|
124
|
+
}
|
|
125
|
+
interface ILankaBootstrapConfig {
|
|
126
|
+
services?: ILankaServiceConfig[];
|
|
127
|
+
scenarios?: ILankaScenarioBootstrapConfig;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Creates an instance and makes it active.
|
|
131
|
+
*
|
|
132
|
+
* Activating on creation is the deliberate default: there is almost always one
|
|
133
|
+
* app and the facades must work immediately. A second instance takes the
|
|
134
|
+
* pointer — the last one created serves the facades. Callers needing another
|
|
135
|
+
* order call `activate()` explicitly.
|
|
136
|
+
*/
|
|
137
|
+
declare function createLanka(config: ILankaInstanceConfig): ILankaInstance;
|
|
138
|
+
|
|
139
|
+
export { type ILankaBootstrapConfig as I, type ILankaInstance as a, type ILankaInstanceConfig as b, type ILankaPlugin as c, type ILankaScenarioBootstrapConfig as d, type ILankaServiceConfig as e, createLanka as f };
|