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.
- package/LICENSE +21 -0
- package/README.md +242 -0
- package/dist/ALankaGateway-ExlRGT3D.d.ts +122 -0
- package/dist/ALankaSharedStore-B7uepuuk.d.ts +29 -0
- package/dist/ILankaApiError-zI_2bNs0.d.ts +16 -0
- package/dist/ILankaRuntimeConfig-Vl436GWK.d.ts +62 -0
- package/dist/ILankaScenarioMetadata-Bu-yggTZ.d.ts +14 -0
- package/dist/ILankaScenarioVM-DuCyPoyT.d.ts +260 -0
- package/dist/LankaError-B1HtuIkw.d.ts +86 -0
- package/dist/LankaScenarioLocator-BGQHwf3n.d.ts +144 -0
- package/dist/LankaSharedStoreLocator-MvCpav5F.d.ts +126 -0
- package/dist/TLankaErrorHandler-Yfqtdh1M.d.ts +3 -0
- package/dist/_extend/index.d.ts +34 -0
- package/dist/_extend/index.js +36 -0
- package/dist/_extend/index.js.map +1 -0
- package/dist/_internal/index.d.ts +43 -0
- package/dist/_internal/index.js +34 -0
- package/dist/_internal/index.js.map +1 -0
- package/dist/activeRuntime-FcsSJvUg.d.ts +84 -0
- package/dist/bootstrap/index.d.ts +242 -0
- package/dist/bootstrap/index.js +22 -0
- package/dist/bootstrap/index.js.map +1 -0
- package/dist/chunk-73IVH3C6.js +52 -0
- package/dist/chunk-73IVH3C6.js.map +1 -0
- package/dist/chunk-BGVDPDX4.js +29 -0
- package/dist/chunk-BGVDPDX4.js.map +1 -0
- package/dist/chunk-C2HP7CRD.js +224 -0
- package/dist/chunk-C2HP7CRD.js.map +1 -0
- package/dist/chunk-D27MREPB.js +13 -0
- package/dist/chunk-D27MREPB.js.map +1 -0
- package/dist/chunk-DTO27QFR.js +59 -0
- package/dist/chunk-DTO27QFR.js.map +1 -0
- package/dist/chunk-EWVDJYCC.js +296 -0
- package/dist/chunk-EWVDJYCC.js.map +1 -0
- package/dist/chunk-FIR4XTBL.js +136 -0
- package/dist/chunk-FIR4XTBL.js.map +1 -0
- package/dist/chunk-ILQNYQY5.js +200 -0
- package/dist/chunk-ILQNYQY5.js.map +1 -0
- package/dist/chunk-J5LA3Q6D.js +14 -0
- package/dist/chunk-J5LA3Q6D.js.map +1 -0
- package/dist/chunk-MDZ42XRH.js +10 -0
- package/dist/chunk-MDZ42XRH.js.map +1 -0
- package/dist/chunk-Q3SOVBIJ.js +176 -0
- package/dist/chunk-Q3SOVBIJ.js.map +1 -0
- package/dist/chunk-Q7QESSYF.js +49 -0
- package/dist/chunk-Q7QESSYF.js.map +1 -0
- package/dist/chunk-RKYKK6MN.js +19 -0
- package/dist/chunk-RKYKK6MN.js.map +1 -0
- package/dist/chunk-RYFZCAQ3.js +637 -0
- package/dist/chunk-RYFZCAQ3.js.map +1 -0
- package/dist/chunk-UJEC7H6K.js +39 -0
- package/dist/chunk-UJEC7H6K.js.map +1 -0
- package/dist/chunk-XESL274R.js +23 -0
- package/dist/chunk-XESL274R.js.map +1 -0
- package/dist/chunk-YR4MZXMU.js +13 -0
- package/dist/chunk-YR4MZXMU.js.map +1 -0
- package/dist/config/index.d.ts +63 -0
- package/dist/config/index.js +16 -0
- package/dist/config/index.js.map +1 -0
- package/dist/createLankaScope-Bc_vChRs.d.ts +30 -0
- package/dist/errors/index.d.ts +37 -0
- package/dist/errors/index.js +16 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/gateway/index.d.ts +258 -0
- package/dist/gateway/index.js +438 -0
- package/dist/gateway/index.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/lankaHttpInFlight-Bk1eIuSx.d.ts +43 -0
- package/dist/lankaRequestMiddleware-DAC5kCb7.d.ts +62 -0
- package/dist/lankaStandardValidator-CL-r-zEV.d.ts +34 -0
- package/dist/locator/index.d.ts +282 -0
- package/dist/locator/index.js +67 -0
- package/dist/locator/index.js.map +1 -0
- package/dist/logger/index.d.ts +146 -0
- package/dist/logger/index.js +11 -0
- package/dist/logger/index.js.map +1 -0
- package/dist/mock/index.d.ts +16 -0
- package/dist/mock/index.js +58 -0
- package/dist/mock/index.js.map +1 -0
- package/dist/role/index.d.ts +48 -0
- package/dist/role/index.js +7 -0
- package/dist/role/index.js.map +1 -0
- package/dist/scenario/index.d.ts +204 -0
- package/dist/scenario/index.js +28 -0
- package/dist/scenario/index.js.map +1 -0
- package/dist/validation/index.d.ts +18 -0
- package/dist/validation/index.js +53 -0
- package/dist/validation/index.js.map +1 -0
- package/dist/viewmodel/index.d.ts +478 -0
- package/dist/viewmodel/index.js +701 -0
- package/dist/viewmodel/index.js.map +1 -0
- package/package.json +99 -0
- package/skills/lanka-core/SKILL.md +241 -0
- package/skills/lanka-core/reference.md +1090 -0
- package/skills/lanka-packages/SKILL.md +70 -0
|
@@ -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","../../tools/testing/_fixtures/.lanka_di/Scenarios.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 { 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\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 type { ILankaScenarioMetadata } from \"../../_interfaces/ILankaScenarioMetadata\";\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 * Unregister a scenario\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\tif (!this.registeredScenarios.has(scenarioName)) {\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\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 * Clears every registered scenario. Required by tests.\n\t */\n\tpublic clear(): void {\n\t\tthis.registeredScenarios.clear();\n\t\tthis.scenarioMetadata.clear();\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","/**\n * The scenarios this app publishes to `lanka`.\n *\n * Add one export line per class; the framework derives the locator from these\n * exports, so there is nothing else to register.\n *\n * @example export { SessionScenario } from \"../src/...\";\n */\nexport {};\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ALankaScenario,
|
|
3
|
+
LankaScenariosRegistry,
|
|
4
|
+
Scenarios_exports
|
|
5
|
+
} from "./chunk-EWVDJYCC.js";
|
|
6
|
+
import {
|
|
7
|
+
ALankaLocator,
|
|
8
|
+
caseConvert,
|
|
9
|
+
findExportedClass
|
|
10
|
+
} from "./chunk-ILQNYQY5.js";
|
|
11
|
+
import {
|
|
12
|
+
lankaLogger
|
|
13
|
+
} from "./chunk-C2HP7CRD.js";
|
|
14
|
+
|
|
15
|
+
// ../tools/testing/_fixtures/.lanka_di/Gateways.ts
|
|
16
|
+
var Gateways_exports = {};
|
|
17
|
+
|
|
18
|
+
// src/locator/gateway/lanka-gateway-locator/LankaGatewayLocator.ts
|
|
19
|
+
var LankaGatewayLocator = class extends ALankaLocator {
|
|
20
|
+
constructor() {
|
|
21
|
+
super({
|
|
22
|
+
findClassByName: (gatewayName) => findExportedClass(Gateways_exports, gatewayName),
|
|
23
|
+
notFoundError: (gatewayName, propertyName) => `Gateway "${gatewayName}" (accessed as "${propertyName}") not found. Make sure the gateway class extends ALankaGateway and is exported from lankaGateways.ts.`
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// src/locator/scenario/lanka-scenario-locator/LankaScenarioLocator.ts
|
|
29
|
+
var LankaScenarioLocator = class extends ALankaLocator {
|
|
30
|
+
/**
|
|
31
|
+
* The registry is read LAZILY rather than in a field initialiser.
|
|
32
|
+
*
|
|
33
|
+
* The locator is constructed INSIDE `createLanka`, before the instance becomes
|
|
34
|
+
* active. A field initialiser would ask for the active instance at that moment
|
|
35
|
+
* and fail with "no instance yet" while the instance being created is on the
|
|
36
|
+
* stack. Deferred reading resolves it: by the first scenario resolution an
|
|
37
|
+
* active instance exists.
|
|
38
|
+
*/
|
|
39
|
+
get registry() {
|
|
40
|
+
return LankaScenariosRegistry.getInstance();
|
|
41
|
+
}
|
|
42
|
+
constructor() {
|
|
43
|
+
super({
|
|
44
|
+
findClassByName: (scenarioName) => {
|
|
45
|
+
if (scenarioName in Scenarios_exports) {
|
|
46
|
+
const ScenarioClass = Scenarios_exports[scenarioName];
|
|
47
|
+
if (typeof ScenarioClass === "function" && "prototype" in ScenarioClass && ScenarioClass.prototype) {
|
|
48
|
+
return ScenarioClass;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
for (const value of Object.values(Scenarios_exports)) {
|
|
52
|
+
if (typeof value === "function" && value.prototype) {
|
|
53
|
+
try {
|
|
54
|
+
const instance = new value();
|
|
55
|
+
if (instance.name === scenarioName) {
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
} catch {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return void 0;
|
|
64
|
+
},
|
|
65
|
+
getInstanceByName: (scenarioName) => {
|
|
66
|
+
const registered = this.registry.getScenarioByName(scenarioName);
|
|
67
|
+
if (registered) {
|
|
68
|
+
return registered;
|
|
69
|
+
}
|
|
70
|
+
const autoRegistered = ALankaScenario.getAutoRegisteredScenarios();
|
|
71
|
+
const scenario = autoRegistered.find((s) => s.name === scenarioName);
|
|
72
|
+
if (scenario) {
|
|
73
|
+
if (!this.registry.isRegistered(scenarioName)) {
|
|
74
|
+
this.registry.register(scenario);
|
|
75
|
+
}
|
|
76
|
+
return scenario;
|
|
77
|
+
}
|
|
78
|
+
return void 0;
|
|
79
|
+
},
|
|
80
|
+
createInstance: (ScenarioClass) => {
|
|
81
|
+
const instance = new ScenarioClass();
|
|
82
|
+
const scenarioName = instance.name;
|
|
83
|
+
if (!this.registry.isRegistered(scenarioName)) {
|
|
84
|
+
this.registry.register(instance);
|
|
85
|
+
}
|
|
86
|
+
return instance;
|
|
87
|
+
},
|
|
88
|
+
notFoundError: (scenarioName, propertyName) => `Scenario "${scenarioName}" (accessed as "${propertyName}") not found. Make sure the scenario class extends ALankaScenario and has name="${scenarioName}".`
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// src/locator/_factories/create-lanka-scope/createLankaScope.ts
|
|
94
|
+
function createLankaScope(singletons) {
|
|
95
|
+
const instances = /* @__PURE__ */ new Map();
|
|
96
|
+
let disposed = false;
|
|
97
|
+
return {
|
|
98
|
+
resolve(propertyName) {
|
|
99
|
+
if (disposed) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`The scope is closed: "${propertyName}" can no longer be resolved in it. This is usually a scope reference that outlived the screen that created it.`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
const className = caseConvert(propertyName, "pascalCase");
|
|
105
|
+
if (instances.has(className)) return instances.get(className);
|
|
106
|
+
const instance = singletons.createScopedInstance(className, propertyName);
|
|
107
|
+
instances.set(className, instance);
|
|
108
|
+
return instance;
|
|
109
|
+
},
|
|
110
|
+
dispose() {
|
|
111
|
+
if (disposed) return;
|
|
112
|
+
disposed = true;
|
|
113
|
+
for (const [name, instance] of instances) {
|
|
114
|
+
const disposable = instance;
|
|
115
|
+
if (typeof disposable.dispose !== "function") continue;
|
|
116
|
+
try {
|
|
117
|
+
disposable.dispose();
|
|
118
|
+
} catch (error) {
|
|
119
|
+
lankaLogger.printBootstrapLog(
|
|
120
|
+
`Failed to dispose "${name}" in the scope`,
|
|
121
|
+
error instanceof Error ? error.message : String(error)
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
instances.clear();
|
|
126
|
+
},
|
|
127
|
+
isDisposed: () => disposed
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export {
|
|
132
|
+
LankaGatewayLocator,
|
|
133
|
+
LankaScenarioLocator,
|
|
134
|
+
createLankaScope
|
|
135
|
+
};
|
|
136
|
+
//# sourceMappingURL=chunk-FIR4XTBL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../tools/testing/_fixtures/.lanka_di/Gateways.ts","../src/locator/gateway/lanka-gateway-locator/LankaGatewayLocator.ts","../src/locator/scenario/lanka-scenario-locator/LankaScenarioLocator.ts","../src/locator/_factories/create-lanka-scope/createLankaScope.ts"],"sourcesContent":["/**\n * The gateways this app publishes to `lanka`.\n *\n * Add one export line per class; the framework derives the locator from these\n * exports, so there is nothing else to register.\n *\n * @example export { UserGateway } from \"../src/...\";\n */\nexport {};\n","import * as GatewaysModule from \"@lanka_di/Gateways\";\nimport type { ALankaGateway } from \"../../../gateway/_abstractions/lanka-gateway/ALankaGateway\";\nimport { ALankaLocator } from \"../../_abstractions/lanka-locator/ALankaLocator\";\nimport { findExportedClass } from \"../../_internal/findExportedClass\";\n\n/**\n * Resolves a gateway by property name (camelCase) or class name (PascalCase),\n * constructing it lazily and caching the instance.\n *\n * Adding a gateway takes one export line in `@lanka_di/Gateways`: types are\n * inferred and nothing is registered.\n */\nexport class LankaGatewayLocator extends ALankaLocator<ALankaGateway<unknown>> {\n\tconstructor() {\n\t\tsuper({\n\t\t\tfindClassByName: (gatewayName: string) =>\n\t\t\t\tfindExportedClass<ALankaGateway<unknown>>(GatewaysModule, gatewayName),\n\t\t\tnotFoundError: (gatewayName, propertyName) =>\n\t\t\t\t`Gateway \"${gatewayName}\" (accessed as \"${propertyName}\") not found. ` +\n\t\t\t\t`Make sure the gateway class extends ALankaGateway and is exported from lankaGateways.ts.`,\n\t\t});\n\t}\n}\n","import { LankaScenariosRegistry } from \"../../../scenario/_registries/lanka-scenarios-registry/LankaScenariosRegistry\";\nimport { ILankaScenario } from \"../../../scenario/_interfaces/ILankaScenario\";\nimport { ALankaScenario } from \"../../../scenario/_abstractions/lanka-scenario/ALankaScenario\";\nimport { ALankaLocator } from \"../../_abstractions/lanka-locator/ALankaLocator\";\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.\n */\nimport * as ScenariosModule from \"@lanka_di/Scenarios\";\n\n/**\n * Resolves scenarios by property name (camelCase) or scenario name\n * (PascalCase).\n *\n * Looks in the registry, then in the self-registration pool, and failing both\n * constructs the scenario from the consumer's barrel on first use.\n */\nexport class LankaScenarioLocator extends ALankaLocator<ILankaScenario<unknown>> {\n\t/**\n\t * The registry is read LAZILY rather than in a field initialiser.\n\t *\n\t * The locator is constructed INSIDE `createLanka`, before the instance becomes\n\t * active. A field initialiser would ask for the active instance at that moment\n\t * and fail with \"no instance yet\" while the instance being created is on the\n\t * stack. Deferred reading resolves it: by the first scenario resolution an\n\t * active instance exists.\n\t */\n\tprivate get registry(): LankaScenariosRegistry {\n\t\treturn LankaScenariosRegistry.getInstance();\n\t}\n\n\tconstructor() {\n\t\tsuper({\n\t\t\tfindClassByName: (scenarioName: string) => {\n\t\t\t\t// By export key first: an instance name does not always survive minification.\n\t\t\t\tif (scenarioName in ScenariosModule) {\n\t\t\t\t\t// `as unknown` before the guards, exactly as in the singleton locator.\n\t\t\t\t\t// An EMPTY barrel — what the scaffolder writes, and what a new project\n\t\t\t\t\t// has until its first scenario — makes the module's keys `never`, so\n\t\t\t\t\t// an indexed access is `never` and reading `.prototype` off it does\n\t\t\t\t\t// not compile.\n\t\t\t\t\tconst ScenarioClass = ScenariosModule[\n\t\t\t\t\t\tscenarioName as keyof typeof ScenariosModule\n\t\t\t\t\t] as unknown;\n\t\t\t\t\tif (\n\t\t\t\t\t\ttypeof ScenarioClass === \"function\" &&\n\t\t\t\t\t\t\"prototype\" in ScenarioClass &&\n\t\t\t\t\t\tScenarioClass.prototype\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn ScenarioClass as new () => ILankaScenario<unknown>;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Fallback: look up by instance name.\n\t\t\t\tfor (const value of Object.values(ScenariosModule)) {\n\t\t\t\t\tif (typeof value === \"function\" && value.prototype) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst instance = new (value as new () => ALankaScenario<unknown>)();\n\t\t\t\t\t\t\tif (instance.name === scenarioName) {\n\t\t\t\t\t\t\t\treturn value as new () => ILankaScenario<unknown>;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// Not constructible — skip.\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn undefined;\n\t\t\t},\n\t\t\tgetInstanceByName: (scenarioName: string) => {\n\t\t\t\t// The registry first.\n\t\t\t\tconst registered = this.registry.getScenarioByName(scenarioName);\n\t\t\t\tif (registered) {\n\t\t\t\t\treturn registered;\n\t\t\t\t}\n\n\t\t\t\t// Then those that registered themselves.\n\t\t\t\tconst autoRegistered = ALankaScenario.getAutoRegisteredScenarios();\n\t\t\t\tconst scenario = autoRegistered.find((s) => s.name === scenarioName);\n\n\t\t\t\tif (scenario) {\n\t\t\t\t\tif (!this.registry.isRegistered(scenarioName)) {\n\t\t\t\t\t\tthis.registry.register(scenario);\n\t\t\t\t\t}\n\t\t\t\t\treturn scenario;\n\t\t\t\t}\n\n\t\t\t\t// Then finding the class by name and constructing it.\n\t\t\t\treturn undefined;\n\t\t\t},\n\t\t\tcreateInstance: (ScenarioClass) => {\n\t\t\t\tconst instance = new ScenarioClass();\n\t\t\t\tconst scenarioName = instance.name;\n\t\t\t\tif (!this.registry.isRegistered(scenarioName)) {\n\t\t\t\t\tthis.registry.register(instance);\n\t\t\t\t}\n\t\t\t\treturn instance;\n\t\t\t},\n\t\t\tnotFoundError: (scenarioName, propertyName) =>\n\t\t\t\t`Scenario \"${scenarioName}\" (accessed as \"${propertyName}\") not found. ` +\n\t\t\t\t`Make sure the scenario class extends ALankaScenario and has name=\"${scenarioName}\".`,\n\t\t});\n\t}\n}\n","import { caseConvert } from \"../../../_internal/case-convert/caseConvert\";\nimport { lankaLogger } from \"../../../logger/lanka-logger/LankaLogger\";\nimport type { LankaSingletonLocator } from \"../../singleton/lanka-singleton-locator/LankaSingletonLocator\";\n\n/**\n * A service lifetime other than \"as long as the application lives\".\n *\n * Everything resolved through `lankaSingletons.*` is a singleton for the life of\n * the app. Per-route, per-session and per-modal lifetimes otherwise rest on\n * discipline: every ViewModel calling `resetScenario()` and writing its own\n * `onReset`, in any of which it can be forgotten.\n *\n * A scope provides the same by construction: an object created in it goes away\n * with it, and nobody has to remember.\n *\n * ## What a scope does NOT do\n *\n * It does not shadow root objects and does not take them on close. It takes only\n * ITS OWN — the ones it created. A scope taking others' would be more dangerous\n * than no scopes at all: closing a screen would break the app.\n */\nexport interface ILankaScope {\n\t/** Resolves a service in this scope, creating it on first use. */\n\tresolve<TInstance>(propertyName: string): TInstance;\n\t/** Disposes everything this scope created and closes it. Idempotent. */\n\tdispose(): void;\n\t/** Whether the scope is closed. */\n\tisDisposed(): boolean;\n}\n\n/** A service may have a `dispose` — or may not. */\ninterface IMaybeDisposable {\n\tdispose?: () => void;\n}\n\nexport function createLankaScope(singletons: LankaSingletonLocator): ILankaScope {\n\tconst instances = new Map<string, unknown>();\n\tlet disposed = false;\n\n\treturn {\n\t\tresolve<TInstance>(propertyName: string): TInstance {\n\t\t\tif (disposed) {\n\t\t\t\t// Resolving from a closed scope is almost always a reference leaked from\n\t\t\t\t// an already-unmounted screen. Silently handing out an object would\n\t\t\t\t// extend the life of what was closed, invisibly.\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`The scope is closed: \"${propertyName}\" can no longer be resolved in it. ` +\n\t\t\t\t\t\t`This is usually a scope reference that outlived the screen that created it.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst className = caseConvert(propertyName, \"pascalCase\");\n\t\t\tif (instances.has(className)) return instances.get(className) as TInstance;\n\n\t\t\t// The class comes from the root locator while the OBJECT is created here:\n\t\t\t// a scope is a different lifetime, not a different set of services.\n\t\t\tconst instance = singletons.createScopedInstance(className, propertyName);\n\t\t\tinstances.set(className, instance);\n\t\t\treturn instance as TInstance;\n\t\t},\n\n\t\tdispose(): void {\n\t\t\tif (disposed) return;\n\t\t\tdisposed = true;\n\n\t\t\tfor (const [name, instance] of instances) {\n\t\t\t\tconst disposable = instance as IMaybeDisposable;\n\t\t\t\tif (typeof disposable.dispose !== \"function\") continue;\n\t\t\t\ttry {\n\t\t\t\t\tdisposable.dispose();\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// One failure must not leave the rest alive: that is exactly what a\n\t\t\t\t\t// scope exists to prevent.\n\t\t\t\t\tlankaLogger.printBootstrapLog(\n\t\t\t\t\t\t`Failed to dispose \"${name}\" in the scope`,\n\t\t\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tinstances.clear();\n\t\t},\n\n\t\tisDisposed: () => disposed,\n\t};\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// src/_internal/case-convert/caseConvert.ts
|
|
2
|
+
function caseConvert(str, toCase) {
|
|
3
|
+
if (!str || typeof str !== "string") return str;
|
|
4
|
+
const words = str.replace(/([A-Z])/g, " $1").trim().split(/\s+|_|-/).filter((word) => word);
|
|
5
|
+
switch (toCase) {
|
|
6
|
+
case "camelCase":
|
|
7
|
+
return words.map(
|
|
8
|
+
(word, index) => index === 0 ? word.toLowerCase() : word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()
|
|
9
|
+
).join("");
|
|
10
|
+
case "kebabCase":
|
|
11
|
+
return words.map((word) => word.toLowerCase()).join("-");
|
|
12
|
+
case "snakeCase":
|
|
13
|
+
return words.map((word) => word.toLowerCase()).join("_");
|
|
14
|
+
case "pascalCase":
|
|
15
|
+
return words.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
|
|
16
|
+
case "titleCase":
|
|
17
|
+
return words.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
|
|
18
|
+
default:
|
|
19
|
+
return str;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// src/locator/_abstractions/lanka-locator/ALankaLocator.ts
|
|
24
|
+
var ALankaLocator = class {
|
|
25
|
+
instanceCache = /* @__PURE__ */ new Map();
|
|
26
|
+
registeredClasses = /* @__PURE__ */ new Map();
|
|
27
|
+
registeredInstances = /* @__PURE__ */ new Map();
|
|
28
|
+
config;
|
|
29
|
+
constructor(config) {
|
|
30
|
+
this.config = {
|
|
31
|
+
...config,
|
|
32
|
+
createInstance: config.createInstance ?? ((Class) => new Class()),
|
|
33
|
+
notFoundError: config.notFoundError ?? ((name, propertyName) => `Instance "${name}" (accessed as "${propertyName}") not found.`)
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* An object by class name.
|
|
38
|
+
*
|
|
39
|
+
* The cache first, then custom resolution when configured, and only then
|
|
40
|
+
* construction.
|
|
41
|
+
*/
|
|
42
|
+
getInstanceByName(instanceName) {
|
|
43
|
+
if (this.instanceCache.has(instanceName)) {
|
|
44
|
+
return this.instanceCache.get(instanceName);
|
|
45
|
+
}
|
|
46
|
+
if (this.registeredInstances.has(instanceName)) {
|
|
47
|
+
return this.registeredInstances.get(instanceName);
|
|
48
|
+
}
|
|
49
|
+
if (this.config.getInstanceByName) {
|
|
50
|
+
const instance = this.config.getInstanceByName(instanceName);
|
|
51
|
+
if (instance) {
|
|
52
|
+
this.instanceCache.set(instanceName, instance);
|
|
53
|
+
return instance;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const Class = this.config.findClassByName(instanceName);
|
|
57
|
+
if (Class) {
|
|
58
|
+
const instance = this.config.createInstance(Class);
|
|
59
|
+
this.instanceCache.set(instanceName, instance);
|
|
60
|
+
return instance;
|
|
61
|
+
}
|
|
62
|
+
return void 0;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* An object by property name (camelCase).
|
|
66
|
+
*
|
|
67
|
+
* Throws when there is none: a silent `undefined` would surface layers later.
|
|
68
|
+
*/
|
|
69
|
+
get(propertyName) {
|
|
70
|
+
const instanceName = caseConvert(propertyName, "pascalCase");
|
|
71
|
+
const instance = this.getInstanceByName(instanceName);
|
|
72
|
+
if (!instance) {
|
|
73
|
+
throw new Error(this.config.notFoundError(instanceName, propertyName));
|
|
74
|
+
}
|
|
75
|
+
return instance;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Clears the instance cache. Required by tests.
|
|
79
|
+
*/
|
|
80
|
+
clearCache() {
|
|
81
|
+
this.instanceCache.clear();
|
|
82
|
+
}
|
|
83
|
+
// ── Registration by hand ─────────────────────────────────────────────────
|
|
84
|
+
//
|
|
85
|
+
// Every locator needs it, for the same two reasons: a test supplies a double,
|
|
86
|
+
// and an application supplies an object it built itself. It lives here rather
|
|
87
|
+
// than in each locator because three copies of four one-line methods are three
|
|
88
|
+
// places for the cache invalidation to be forgotten.
|
|
89
|
+
/**
|
|
90
|
+
* Registers a class by hand, ahead of the consumer's barrel.
|
|
91
|
+
*
|
|
92
|
+
* The cache entry is dropped: what it holds was built from the previous class.
|
|
93
|
+
*/
|
|
94
|
+
register(className, Class) {
|
|
95
|
+
this.registeredClasses.set(className, Class);
|
|
96
|
+
this.instanceCache.delete(className);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Registers an already-built object by hand.
|
|
100
|
+
*
|
|
101
|
+
* It goes straight into the cache: there is nothing left to construct, and a
|
|
102
|
+
* later lookup must not build a rival.
|
|
103
|
+
*/
|
|
104
|
+
registerInstance(className, instance) {
|
|
105
|
+
this.registeredInstances.set(className, instance);
|
|
106
|
+
this.instanceCache.set(className, instance);
|
|
107
|
+
}
|
|
108
|
+
/** Removes a class or object registered by hand, and what was built from it. */
|
|
109
|
+
unregister(className) {
|
|
110
|
+
this.registeredClasses.delete(className);
|
|
111
|
+
this.registeredInstances.delete(className);
|
|
112
|
+
this.instanceCache.delete(className);
|
|
113
|
+
}
|
|
114
|
+
/** Whether this name resolves to anything the locator already holds. */
|
|
115
|
+
isRegistered(className) {
|
|
116
|
+
return this.registeredClasses.has(className) || this.registeredInstances.has(className) || this.instanceCache.has(className);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// src/locator/_internal/findExportedClass.ts
|
|
121
|
+
function findExportedClass(module, exportName) {
|
|
122
|
+
if (!(exportName in module)) return void 0;
|
|
123
|
+
const candidate = module[exportName];
|
|
124
|
+
return typeof candidate === "function" && candidate.prototype ? candidate : void 0;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ../tools/testing/_fixtures/.lanka_di/Singletons.ts
|
|
128
|
+
var Singletons_exports = {};
|
|
129
|
+
|
|
130
|
+
// src/locator/singleton/lanka-singleton-locator/LankaSingletonLocator.ts
|
|
131
|
+
var LankaSingletonLocator = class extends ALankaLocator {
|
|
132
|
+
singletonIndexModule;
|
|
133
|
+
constructor(config) {
|
|
134
|
+
super({
|
|
135
|
+
findClassByName: (className) => {
|
|
136
|
+
const registered = this.registeredClasses.get(className);
|
|
137
|
+
if (registered) return registered;
|
|
138
|
+
return findExportedClass(Singletons_exports, className) ?? (this.singletonIndexModule ? findExportedClass(this.singletonIndexModule, className) : void 0);
|
|
139
|
+
},
|
|
140
|
+
createInstance: (Class) => {
|
|
141
|
+
return new Class();
|
|
142
|
+
},
|
|
143
|
+
notFoundError: (className, propertyName) => `Singleton "${className}" (accessed as "${propertyName}") not found. Make sure the class is exported from @lanka_di/Singletons.ts or registered via registerSingleton method.`
|
|
144
|
+
});
|
|
145
|
+
this.singletonIndexModule = config?.singletonIndexModule;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Creates a NEW service object, bypassing the locator's cache.
|
|
149
|
+
*
|
|
150
|
+
* For scopes: they take the class from here and set the lifetime themselves.
|
|
151
|
+
* The root cache is untouched — otherwise the first resolution inside a scope
|
|
152
|
+
* would replace the application's root object.
|
|
153
|
+
*/
|
|
154
|
+
createScopedInstance(className, propertyName) {
|
|
155
|
+
const Class = this.config.findClassByName(className);
|
|
156
|
+
if (!Class) {
|
|
157
|
+
throw new Error(this.config.notFoundError(className, propertyName));
|
|
158
|
+
}
|
|
159
|
+
return this.config.createInstance(Class);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
// ../tools/testing/_fixtures/.lanka_di/SharedStores.ts
|
|
164
|
+
var SharedStores_exports = {};
|
|
165
|
+
|
|
166
|
+
// src/locator/shared-store/lanka-shared-store-locator/LankaSharedStoreLocator.ts
|
|
167
|
+
var LankaSharedStoreLocator = class extends ALankaLocator {
|
|
168
|
+
sharedStoreIndexModule;
|
|
169
|
+
constructor(config) {
|
|
170
|
+
super({
|
|
171
|
+
findClassByName: (className) => {
|
|
172
|
+
const registeredClass = this.registeredClasses.get(className);
|
|
173
|
+
if (registeredClass) {
|
|
174
|
+
return registeredClass;
|
|
175
|
+
}
|
|
176
|
+
return findExportedClass(
|
|
177
|
+
SharedStores_exports,
|
|
178
|
+
className
|
|
179
|
+
) ?? (this.sharedStoreIndexModule ? findExportedClass(
|
|
180
|
+
this.sharedStoreIndexModule,
|
|
181
|
+
className
|
|
182
|
+
) : void 0);
|
|
183
|
+
},
|
|
184
|
+
createInstance: (Class) => {
|
|
185
|
+
return new Class();
|
|
186
|
+
},
|
|
187
|
+
notFoundError: (className, propertyName) => `SharedStore "${className}" (accessed as "${propertyName}") not found. Make sure the class extends ALankaSharedStore and is exported from @lanka_di/SharedStores.ts or registered via registerSharedStore method.`
|
|
188
|
+
});
|
|
189
|
+
this.sharedStoreIndexModule = config?.sharedStoreIndexModule;
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
export {
|
|
194
|
+
caseConvert,
|
|
195
|
+
ALankaLocator,
|
|
196
|
+
findExportedClass,
|
|
197
|
+
LankaSingletonLocator,
|
|
198
|
+
LankaSharedStoreLocator
|
|
199
|
+
};
|
|
200
|
+
//# sourceMappingURL=chunk-ILQNYQY5.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/findExportedClass.ts","../../tools/testing/_fixtures/.lanka_di/Singletons.ts","../src/locator/singleton/lanka-singleton-locator/LankaSingletonLocator.ts","../../tools/testing/_fixtures/.lanka_di/SharedStores.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 * 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.config.findClassByName(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\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","/**\n * The singletons this app publishes to `lanka`.\n *\n * Add one export line per class; the framework derives the locator from these\n * exports, so there is nothing else to register.\n *\n * @example export { AnalyticsService } from \"../src/...\";\n */\nexport {};\n","import { ALankaLocator } from \"../../_abstractions/lanka-locator/ALankaLocator\";\nimport { findExportedClass } from \"../../_internal/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: hand-registered first, then the application's barrel, then an\n\t\t\t\t// extra module if one was given.\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\tconst registered = this.registeredClasses.get(className);\n\t\t\t\tif (registered) return registered;\n\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\tconst Class = this.config.findClassByName(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","/**\n * The shared stores this app publishes to `lanka`.\n *\n * Add one export line per class; the framework derives the locator from these\n * exports, so there is nothing else to register.\n *\n * @example export { UserSharedStore } from \"../src/...\";\n */\nexport {};\n","import { ALankaLocator } from \"../../_abstractions/lanka-locator/ALankaLocator\";\nimport { findExportedClass } from \"../../_internal/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\tconst registeredClass = this.registeredClasses.get(className);\n\t\t\t\tif (registeredClass) {\n\t\t\t\t\treturn registeredClass;\n\t\t\t\t}\n\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":";AAAO,SAAS,YACf,KACA,QACS;AACT,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAE5C,QAAM,QAAQ,IACZ,QAAQ,YAAY,KAAK,EACzB,KAAK,EACL,MAAM,SAAS,EACf,OAAO,CAAC,SAAS,IAAI;AAEvB,UAAQ,QAAQ;AAAA,IACf,KAAK;AACJ,aAAO,MACL;AAAA,QAAI,CAAC,MAAM,UACX,UAAU,IACP,KAAK,YAAY,IACjB,KAAK,OAAO,CAAC,EAAE,YAAY,IAAI,KAAK,MAAM,CAAC,EAAE,YAAY;AAAA,MAC7D,EACC,KAAK,EAAE;AAAA,IACV,KAAK;AACJ,aAAO,MAAM,IAAI,CAAC,SAAS,KAAK,YAAY,CAAC,EAAE,KAAK,GAAG;AAAA,IACxD,KAAK;AACJ,aAAO,MAAM,IAAI,CAAC,SAAS,KAAK,YAAY,CAAC,EAAE,KAAK,GAAG;AAAA,IACxD,KAAK;AACJ,aAAO,MACL,IAAI,CAAC,SAAS,KAAK,OAAO,CAAC,EAAE,YAAY,IAAI,KAAK,MAAM,CAAC,EAAE,YAAY,CAAC,EACxE,KAAK,EAAE;AAAA,IACV,KAAK;AACJ,aAAO,MACL,IAAI,CAAC,SAAS,KAAK,OAAO,CAAC,EAAE,YAAY,IAAI,KAAK,MAAM,CAAC,EAAE,YAAY,CAAC,EACxE,KAAK,GAAG;AAAA,IACX;AACC,aAAO;AAAA,EACT;AACD;;;ACZO,IAAe,gBAAf,MAA4E;AAAA,EAC/D,gBAAwC,oBAAI,IAAI;AAAA,EAChD,oBAAsD,oBAAI,IAAI;AAAA,EAC9D,sBAA8C,oBAAI,IAAI;AAAA,EACtD;AAAA,EAKT,YAAY,QAAwC;AAI7D,SAAK,SAAS;AAAA,MACb,GAAG;AAAA,MACH,gBAAgB,OAAO,mBAAmB,CAAC,UAAU,IAAI,MAAM;AAAA,MAC/D,eACC,OAAO,kBACN,CAAC,MAAM,iBACP,aAAa,IAAI,mBAAmB,YAAY;AAAA,IACnD;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQU,kBAAkB,cAA6C;AACxE,QAAI,KAAK,cAAc,IAAI,YAAY,GAAG;AACzC,aAAO,KAAK,cAAc,IAAI,YAAY;AAAA,IAC3C;AAIA,QAAI,KAAK,oBAAoB,IAAI,YAAY,GAAG;AAC/C,aAAO,KAAK,oBAAoB,IAAI,YAAY;AAAA,IACjD;AAGA,QAAI,KAAK,OAAO,mBAAmB;AAClC,YAAM,WAAW,KAAK,OAAO,kBAAkB,YAAY;AAC3D,UAAI,UAAU;AACb,aAAK,cAAc,IAAI,cAAc,QAAQ;AAC7C,eAAO;AAAA,MACR;AAAA,IACD;AAGA,UAAM,QAAQ,KAAK,OAAO,gBAAgB,YAAY;AACtD,QAAI,OAAO;AACV,YAAM,WAAW,KAAK,OAAO,eAAe,KAAK;AACjD,WAAK,cAAc,IAAI,cAAc,QAAQ;AAC7C,aAAO;AAAA,IACR;AAEA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOO,IAAI,cAAiC;AAC3C,UAAM,eAAe,YAAY,cAAc,YAAY;AAC3D,UAAM,WAAW,KAAK,kBAAkB,YAAY;AAEpD,QAAI,CAAC,UAAU;AACd,YAAM,IAAI,MAAM,KAAK,OAAO,cAAc,cAAc,YAAY,CAAC;AAAA,IACtE;AAEA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAKO,aAAmB;AACzB,SAAK,cAAc,MAAM;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcO,SAAS,WAAmB,OAAkC;AACpE,SAAK,kBAAkB,IAAI,WAAW,KAAK;AAC3C,SAAK,cAAc,OAAO,SAAS;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQO,iBAAiB,WAAmB,UAA2B;AACrE,SAAK,oBAAoB,IAAI,WAAW,QAAQ;AAChD,SAAK,cAAc,IAAI,WAAW,QAAQ;AAAA,EAC3C;AAAA;AAAA,EAGO,WAAW,WAAyB;AAC1C,SAAK,kBAAkB,OAAO,SAAS;AACvC,SAAK,oBAAoB,OAAO,SAAS;AACzC,SAAK,cAAc,OAAO,SAAS;AAAA,EACpC;AAAA;AAAA,EAGO,aAAa,WAA4B;AAC/C,WACC,KAAK,kBAAkB,IAAI,SAAS,KACpC,KAAK,oBAAoB,IAAI,SAAS,KACtC,KAAK,cAAc,IAAI,SAAS;AAAA,EAElC;AACD;;;AClIO,SAAS,kBACf,QACA,YACoC;AAIpC,MAAI,EAAE,cAAc,QAAS,QAAO;AAEpC,QAAM,YAAa,OAAmC,UAAU;AAMhE,SAAO,OAAO,cAAc,cAAc,UAAU,YAChD,YACD;AACJ;;;ACtCA;;;AC0BO,IAAM,wBAAN,cAAoC,cAAuB;AAAA,EAChD;AAAA,EAEjB,YAAY,QAAuC;AAClD,UAAM;AAAA,MACL,iBAAiB,CAAC,cAAsB;AAOvC,cAAM,aAAa,KAAK,kBAAkB,IAAI,SAAS;AACvD,YAAI,WAAY,QAAO;AAEvB,eACC,kBAA2B,oBAAkB,SAAS,MACrD,KAAK,uBACH,kBAA2B,KAAK,sBAAsB,SAAS,IAC/D;AAAA,MAEL;AAAA,MACA,gBAAgB,CAAC,UAAU;AAC1B,eAAO,IAAI,MAAM;AAAA,MAClB;AAAA,MACA,eAAe,CAAC,WAAW,iBAC1B,cAAc,SAAS,mBAAmB,YAAY;AAAA,IAExD,CAAC;AAED,SAAK,uBAAuB,QAAQ;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASO,qBAAqB,WAAmB,cAA+B;AAC7E,UAAM,QAAQ,KAAK,OAAO,gBAAgB,SAAS;AACnD,QAAI,CAAC,OAAO;AACX,YAAM,IAAI,MAAM,KAAK,OAAO,cAAc,WAAW,YAAY,CAAC;AAAA,IACnE;AACA,WAAO,KAAK,OAAO,eAAe,KAAK;AAAA,EACxC;AACD;;;ACzEA;;;ACmBO,IAAM,0BAAN,cAAsC,cAAoD;AAAA,EAC/E;AAAA,EAEjB,YAAY,QAAyC;AACpD,UAAM;AAAA,MACL,iBAAiB,CAAC,cAAsB;AACvC,cAAM,kBAAkB,KAAK,kBAAkB,IAAI,SAAS;AAC5D,YAAI,iBAAiB;AACpB,iBAAO;AAAA,QACR;AAEA,eACC;AAAA,UACC;AAAA,UACA;AAAA,QACD,MACC,KAAK,yBACH;AAAA,UACA,KAAK;AAAA,UACL;AAAA,QACD,IACC;AAAA,MAEL;AAAA,MACA,gBAAgB,CAAC,UAAU;AAC1B,eAAO,IAAI,MAAM;AAAA,MAClB;AAAA,MACA,eAAe,CAAC,WAAW,iBAC1B,gBAAgB,SAAS,mBAAmB,YAAY;AAAA,IAG1D,CAAC;AAED,SAAK,yBAAyB,QAAQ;AAAA,EACvC;AACD;","names":[]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// src/_internal/_guards/isRecord.ts
|
|
2
|
+
var isRecord = (value) => typeof value === "object" && value !== null;
|
|
3
|
+
|
|
4
|
+
// src/_internal/_guards/getStringField.ts
|
|
5
|
+
var getStringField = (data, key) => {
|
|
6
|
+
const value = data[key];
|
|
7
|
+
return typeof value === "string" ? value : void 0;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export {
|
|
11
|
+
isRecord,
|
|
12
|
+
getStringField
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=chunk-J5LA3Q6D.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/_internal/_guards/isRecord.ts","../src/_internal/_guards/getStringField.ts"],"sourcesContent":["/**\n * Type-guard: narrows `unknown` to `Record<string, unknown>`.\n * Excludes `null`, primitives, and arrays-as-keys (arrays still satisfy\n * `typeof === \"object\"`, but they're rarely the target — callers that need\n * to exclude arrays should add `!Array.isArray(value)`).\n *\n * ABSORBED into this package when the layer was extracted: it lived in an\n * application, and a package cannot import its consumer. An application\n * re-exports this one rather than keeping a copy — two implementations of a\n * three-line guard is how they end up disagreeing about `null`.\n */\nexport const isRecord = (value: unknown): value is Record<string, unknown> =>\n\ttypeof value === \"object\" && value !== null;\n","/**\n * Reads `key` from a record and returns it only if the value is a string.\n * Returns `undefined` for missing or non-string values.\n *\n * Use when narrowing `Record<string, unknown>` payloads (SSE events,\n * error response bodies, etc.) without `as` casts. Does NOT filter\n * empty strings — callers that need non-empty values should check\n * `.trim().length > 0` explicitly.\n *\n * Absorbed with {@link isRecord}, for the same reason.\n */\nexport const getStringField = (data: Record<string, unknown>, key: string): string | undefined => {\n\tconst value = data[key];\n\treturn typeof value === \"string\" ? value : undefined;\n};\n"],"mappings":";AAWO,IAAM,WAAW,CAAC,UACxB,OAAO,UAAU,YAAY,UAAU;;;ACDjC,IAAM,iBAAiB,CAAC,MAA+B,QAAoC;AACjG,QAAM,QAAQ,KAAK,GAAG;AACtB,SAAO,OAAO,UAAU,WAAW,QAAQ;AAC5C;","names":[]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// src/role/define-lanka-role/defineLankaRole.ts
|
|
2
|
+
var defineLankaRole = (open) => ((config) => {
|
|
3
|
+
const opened = open(config);
|
|
4
|
+
return config.build ? config.build(opened.context) : opened.instance;
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
defineLankaRole
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=chunk-MDZ42XRH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/role/define-lanka-role/defineLankaRole.ts"],"sourcesContent":["/** What a role's bridge hands over: the instance, and its protected surface. */\nexport interface ILankaRoleOpening<TInstance, TContext> {\n\tinstance: TInstance;\n\tcontext: TContext;\n}\n\n/**\n * Builds a role's instance and opens its protected surface.\n *\n * Written in the role's own module, because that is the only place the language\n * allows: `protected` is readable from inside a class body deriving from the\n * base and nowhere else. A generic helper cannot reach it, and a base that made\n * it public would put its whole extension surface on every object a consumer\n * writes.\n */\nexport type TLankaRoleOpener<TConfig, TInstance, TContext> = (\n\tconfig: TConfig,\n) => ILankaRoleOpening<TInstance, TContext>;\n\n/**\n * The functional style of one role: call it with hooks, or with nothing.\n *\n * With `build` it answers what the hooks returned — the methods a gateway\n * declares, the actions a ViewModel exposes. Without it, the instance itself,\n * which is what a role whose whole body is data needs.\n */\nexport interface ILankaRoleFactory<TConfig, TInstance, TContext> {\n\t<TResult>(config: TConfig & { build: (context: TContext) => TResult }): TResult;\n\t(config: TConfig): TInstance;\n}\n\n/**\n * Turns a role's class into its factory, over one implementation.\n *\n * The two styles of a role are not two implementations: the factory builds the\n * SAME class and hands its protected surface to the caller as an object. A\n * behaviour fix lands in both styles at once, and a divergence is not something\n * a reviewer has to notice — it cannot be written.\n *\n * It is published because a consumer with a layer of their own — a repository, a\n * command, a presenter — gets both styles for it from one line:\n *\n * ```ts\n * export const createRepository = defineLankaRole(openRepository);\n * ```\n *\n * Canon: `skills/parity/SKILL.md`.\n */\nexport const defineLankaRole = <TConfig, TInstance, TContext>(\n\topen: TLankaRoleOpener<TConfig, TInstance, TContext>,\n): ILankaRoleFactory<TConfig, TInstance, TContext> =>\n\t((config: TConfig & { build?: (context: TContext) => unknown }) => {\n\t\tconst opened = open(config);\n\n\t\treturn config.build ? config.build(opened.context) : opened.instance;\n\t}) as ILankaRoleFactory<TConfig, TInstance, TContext>;\n"],"mappings":";AAgDO,IAAM,kBAAkB,CAC9B,UAEC,CAAC,WAAiE;AAClE,QAAM,SAAS,KAAK,MAAM;AAE1B,SAAO,OAAO,QAAQ,OAAO,MAAM,OAAO,OAAO,IAAI,OAAO;AAC7D;","names":[]}
|