lanka 1.0.1 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +5 -3
  2. package/dist/{ALankaGateway-ExlRGT3D.d.ts → ALankaGateway-CkW1LbKE.d.ts} +50 -4
  3. package/dist/{ILankaScenarioMetadata-Bu-yggTZ.d.ts → ILankaScenarioMetadata-GoWWNEQL.d.ts} +1 -1
  4. package/dist/{ILankaScenarioVM-DuCyPoyT.d.ts → ILankaScenarioVM-DUsI-fSc.d.ts} +116 -3
  5. package/dist/{LankaError-B1HtuIkw.d.ts → LankaError-xpI-qj35.d.ts} +15 -2
  6. package/dist/{LankaScenarioLocator-CLkq4MaJ.d.ts → LankaScenarioLocator-D86TIwiu.d.ts} +10 -5
  7. package/dist/_extend/index.d.ts +4 -4
  8. package/dist/_extend/index.js +4 -4
  9. package/dist/_internal/index.d.ts +5 -5
  10. package/dist/{activeRuntime-DT4gB16d.d.ts → activeRuntime-B336NU5I.d.ts} +2 -2
  11. package/dist/bootstrap/index.d.ts +13 -144
  12. package/dist/bootstrap/index.js +5 -5
  13. package/dist/{chunk-63ST2UKP.js → chunk-ECM2HZHS.js} +106 -20
  14. package/dist/chunk-ECM2HZHS.js.map +1 -0
  15. package/dist/{chunk-MYZQYOMD.js → chunk-GK3E4CVW.js} +23 -18
  16. package/dist/chunk-GK3E4CVW.js.map +1 -0
  17. package/dist/chunk-GV5DUYST.js +64 -0
  18. package/dist/chunk-GV5DUYST.js.map +1 -0
  19. package/dist/{chunk-YXI4OQEV.js → chunk-O5ROO7QF.js} +27 -4
  20. package/dist/chunk-O5ROO7QF.js.map +1 -0
  21. package/dist/{chunk-HZAIAGWS.js → chunk-UGXSGQPW.js} +6 -2
  22. package/dist/chunk-UGXSGQPW.js.map +1 -0
  23. package/dist/createLanka-DI1CSy2Q.d.ts +139 -0
  24. package/dist/errors/index.d.ts +2 -3
  25. package/dist/gateway/index.d.ts +77 -67
  26. package/dist/gateway/index.js +50 -54
  27. package/dist/gateway/index.js.map +1 -1
  28. package/dist/index.d.ts +8 -8
  29. package/dist/index.js +5 -5
  30. package/dist/locator/index.d.ts +1 -1
  31. package/dist/scenario/index.d.ts +6 -4
  32. package/dist/scenario/index.js +2 -2
  33. package/dist/stream/index.d.ts +386 -0
  34. package/dist/stream/index.js +287 -0
  35. package/dist/stream/index.js.map +1 -0
  36. package/dist/validation/index.d.ts +13 -8
  37. package/dist/validation/index.js +5 -48
  38. package/dist/validation/index.js.map +1 -1
  39. package/dist/viewmodel/index.d.ts +1 -1
  40. package/dist/viewmodel/index.js +2 -2
  41. package/package.json +7 -3
  42. package/skills/lanka-core/SKILL.md +1 -1
  43. package/skills/lanka-core/reference.md +110 -17
  44. package/skills/lanka-packages/SKILL.md +1 -1
  45. package/dist/ILankaApiError-zI_2bNs0.d.ts +0 -16
  46. package/dist/chunk-63ST2UKP.js.map +0 -1
  47. package/dist/chunk-HZAIAGWS.js.map +0 -1
  48. package/dist/chunk-MYZQYOMD.js.map +0 -1
  49. package/dist/chunk-YXI4OQEV.js.map +0 -1
@@ -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":[]}
@@ -3,7 +3,7 @@ import {
3
3
  LankaScenarioVMRegistry,
4
4
  LankaScenariosRegistry,
5
5
  lankaEventBus
6
- } from "./chunk-YXI4OQEV.js";
6
+ } from "./chunk-O5ROO7QF.js";
7
7
  import {
8
8
  lankaLogger
9
9
  } from "./chunk-C2HP7CRD.js";
@@ -29,8 +29,12 @@ var LankaScenarioBootstrap = class {
29
29
  return this.state.bootstrapped;
30
30
  }
31
31
  forceInstantiateAllScenarios() {
32
+ const pooled = new Set(
33
+ ALankaScenario.getAutoRegisteredScenarios().map((scenario) => scenario.constructor)
34
+ );
32
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-HZAIAGWS.js.map
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 };
@@ -1,6 +1,5 @@
1
- import { L as LankaError } from '../LankaError-B1HtuIkw.js';
2
- export { I as ILankaErrorInit, T as TLankaErrorKind } from '../LankaError-B1HtuIkw.js';
3
- export { I as ILankaApiError } from '../ILankaApiError-zI_2bNs0.js';
1
+ import { L as LankaError } from '../LankaError-xpI-qj35.js';
2
+ export { a as ILankaApiError, I as ILankaErrorInit, T as TLankaErrorKind } from '../LankaError-xpI-qj35.js';
4
3
  export { T as TLankaErrorHandler } from '../TLankaErrorHandler-Yfqtdh1M.js';
5
4
 
6
5
  /**
@@ -1,9 +1,9 @@
1
- import { T as TLankaExecuteOptions, I as IALankaGatewayConfig, a as ILankaRequest, b as TLankaQueryBuilder } from '../ALankaGateway-ExlRGT3D.js';
2
- export { A as ALankaGateway, c as TLankaQueryParams } from '../ALankaGateway-ExlRGT3D.js';
1
+ import { T as TLankaExecuteOptions, I as IALankaGatewayConfig, a as TLankaRequestInit, b as ILankaRequest, c as TLankaQueryBuilder } from '../ALankaGateway-CkW1LbKE.js';
2
+ export { A as ALankaGateway, d as TLankaQueryParams } from '../ALankaGateway-CkW1LbKE.js';
3
+ import { I as ILankaValidator } from '../lankaStandardValidator-CL-r-zEV.js';
3
4
  import { T as TLankaErrorHandler } from '../TLankaErrorHandler-Yfqtdh1M.js';
4
5
  export { I as ILankaInFlightCounter, l as lankaHttpInFlight } from '../lankaHttpInFlight-Bk1eIuSx.js';
5
6
  export { I as ILankaRequestContext, T as TLankaRequestMiddleware } from '../lankaRequestMiddleware-DAC5kCb7.js';
6
- import '../lankaStandardValidator-CL-r-zEV.js';
7
7
  import '@standard-schema/spec';
8
8
 
9
9
  /**
@@ -20,6 +20,8 @@ interface ILankaGatewayContext<TOptions> {
20
20
  request: <TReturn = unknown>(path: string, options?: TLankaExecuteOptions<TOptions>, mockHandler?: () => Promise<TReturn>) => Promise<TReturn>;
21
21
  /** Serialises query parameters the way this gateway was configured to. */
22
22
  buildQueryParams: <T extends object>(params: T) => URLSearchParams;
23
+ /** Checks a response body: the validator this gateway was given, or the Standard Schema port. */
24
+ validationService: ILankaValidator;
23
25
  }
24
26
 
25
27
  /** What a gateway is built from, whichever style builds it. */
@@ -47,13 +49,13 @@ declare const createLankaGateway: <TOptions, TMethods extends object>(config: IL
47
49
  * How a gateway request goes on the wire.
48
50
  *
49
51
  * A subclass declares one method, `request()`, and does only its own work there:
50
- * `LankaFetchJsonRequest` returns parsed JSON, `LankaFetchFormDataRequest`
51
- * returns the whole response, a custom transport returns whatever it likes.
52
+ * `LankaFetchJsonRequest` returns parsed JSON, `LankaFetchRequest` returns the
53
+ * whole response, a custom kind returns whatever it likes.
52
54
  *
53
55
  * Overriding `request()` customises the request flow, failure handling, mock
54
56
  * substitution, response transformation and log interception.
55
57
  */
56
- declare abstract class ALankaRequest<TOptions = RequestInit> implements ILankaRequest<TOptions> {
58
+ declare abstract class ALankaRequest<TOptions = TLankaRequestInit> implements ILankaRequest<TOptions> {
57
59
  protected readonly errorHandler?: TLankaErrorHandler;
58
60
  protected readonly useMock: boolean;
59
61
  protected constructor(config: {
@@ -81,10 +83,23 @@ declare abstract class ALankaRequest<TOptions = RequestInit> implements ILankaRe
81
83
  }
82
84
 
83
85
  /**
84
- * Transport layer interface for Gateway.
85
- * Allows to swap between different communication protocols (HTTP Fetch, gRPC, WebSocket, etc.)
86
+ * How bytes travel. The seam a consumer replaces to change the PROTOCOL.
87
+ *
88
+ * `LankaFetchTransport` is the only implementation core ships, and one is the
89
+ * right number: HTTP over `fetch` is what almost every application does, and the
90
+ * things that once justified a second and a third — a JSON `content-type`, a
91
+ * multipart one — turned out to be encodings rather than protocols. An encoding
92
+ * belongs to the CALL, and the shipped transport reads the body to decide it.
93
+ *
94
+ * Implement this for something genuinely different: a native bridge, a socket, an
95
+ * offline queue, a double that never leaves the process. Anything that still ends
96
+ * in `fetch` and only wants to add a header, a credential, a retry or a refresh
97
+ * is POLICY — write a `TLankaRequestMiddleware` and register it with
98
+ * `useRequestMiddleware`, or reach for `@lankajs/plugin-http`, which already has
99
+ * all four. A transport rewritten to carry policy is how an application ends up
100
+ * maintaining its own copy of this package.
86
101
  */
87
- interface ILankaTransport<TOptions = RequestInit> {
102
+ interface ILankaTransport<TOptions = TLankaRequestInit> {
88
103
  /**
89
104
  * Executes a request and returns a Response.
90
105
  * @param resource - Request resource (URL or Request object)
@@ -102,12 +117,17 @@ interface ILankaTransportRequestConfig<TOptions> {
102
117
  /**
103
118
  * The shape every fetch-backed request has: mock, send, check, parse.
104
119
  *
105
- * The three concrete requests differ in exactly two places — which transport
106
- * they default to, and how they turn a successful `Response` into a value. Both
107
- * are parameters of this template, so a fourth kind is a subclass with one
108
- * method rather than a fourth copy of the sequence.
120
+ * The concrete requests differ in ONE place — how they turn a successful
121
+ * `Response` into a value — so a third kind is a subclass with one method rather
122
+ * than a third copy of the sequence.
123
+ *
124
+ * The default transport stays a constructor PARAMETER even though both shipped
125
+ * kinds pass the same one. It is the seam a request kind that is not fetch-backed
126
+ * comes through: this template is the mock/send/check/parse sequence, and nothing
127
+ * in it is about HTTP. Defaulting the parameter here would fix `TOptions` to
128
+ * fetch options for everyone who reuses the sequence.
109
129
  */
110
- declare abstract class ALankaTransportRequest<TOptions = RequestInit> extends ALankaRequest<TOptions> {
130
+ declare abstract class ALankaTransportRequest<TOptions = TLankaRequestInit> extends ALankaRequest<TOptions> {
111
131
  protected readonly transport: ILankaTransport<TOptions>;
112
132
  protected constructor(config: ILankaTransportRequestConfig<TOptions>, createDefaultTransport: () => ILankaTransport<TOptions>);
113
133
  protected request<TReturn>(endpoint: string, options?: TOptions, mockHandler?: () => Promise<TReturn>): Promise<TReturn>;
@@ -131,10 +151,19 @@ declare abstract class ALankaTransportRequest<TOptions = RequestInit> extends AL
131
151
  /**
132
152
  * The raw request: hands the `Response` back untouched.
133
153
  *
134
- * The minimal, extensible case — a caller wanting headers, a stream or a blob
135
- * reads them off the response itself.
154
+ * The minimal, extensible case — a caller wanting headers, a stream, a blob or a
155
+ * `204` reads them off the response itself. Multipart uploads come through here
156
+ * too: the transport encodes by looking at the body, so posting a `FormData` and
157
+ * posting an object are the same call.
158
+ *
159
+ * `TOptions` is CONSTRAINED to fetch options rather than merely defaulted to
160
+ * them. A consumer widening it — their own `interface IRequestOptions extends
161
+ * TLankaRequestInit` — still gets the shipped transport, because the constraint
162
+ * is what lets the framework hand one over without a cast. Unconstrained, the
163
+ * assignment did not typecheck and core cast its way past it; the cast worked
164
+ * here and was unavailable to the consumer, who wrote a transport instead.
136
165
  */
137
- declare class LankaFetchRequest<TOptions = RequestInit> extends ALankaTransportRequest<TOptions> {
166
+ declare class LankaFetchRequest<TOptions extends TLankaRequestInit = TLankaRequestInit> extends ALankaTransportRequest<TOptions> {
138
167
  constructor(config?: ILankaTransportRequestConfig<TOptions>);
139
168
  protected parse<TReturn>(response: Response): Promise<TReturn>;
140
169
  }
@@ -145,7 +174,7 @@ declare class LankaFetchRequest<TOptions = RequestInit> extends ALankaTransportR
145
174
  * One line, and that is the point — the factory IS the class, so a behaviour
146
175
  * cannot exist in one style and not the other.
147
176
  */
148
- declare const createLankaFetchRequest: <TOptions = RequestInit>(config?: ILankaTransportRequestConfig<TOptions>) => LankaFetchRequest<TOptions>;
177
+ declare const createLankaFetchRequest: <TOptions extends TLankaRequestInit = TLankaRequestInit>(config?: ILankaTransportRequestConfig<TOptions>) => LankaFetchRequest<TOptions>;
149
178
 
150
179
  /** The JSON request: parses the body, and refuses a body that is not JSON. */
151
180
  /**
@@ -155,7 +184,7 @@ declare const createLankaFetchRequest: <TOptions = RequestInit>(config?: ILankaT
155
184
  * what lets a test give the same gateway a transport that never leaves the
156
185
  * process. `createLankaFetchJsonRequest()` builds the same class.
157
186
  */
158
- declare class LankaFetchJsonRequest<TOptions = RequestInit> extends ALankaTransportRequest<TOptions> {
187
+ declare class LankaFetchJsonRequest<TOptions extends TLankaRequestInit = TLankaRequestInit> extends ALankaTransportRequest<TOptions> {
159
188
  constructor(config?: ILankaTransportRequestConfig<TOptions>);
160
189
  /**
161
190
  * Parses the body, or names the failure.
@@ -180,58 +209,39 @@ declare class LankaFetchJsonRequest<TOptions = RequestInit> extends ALankaTransp
180
209
  * One line, and that is the point — the factory IS the class, so a behaviour
181
210
  * cannot exist in one style and not the other.
182
211
  */
183
- declare const createLankaFetchJsonRequest: <TOptions = RequestInit>(config?: ILankaTransportRequestConfig<TOptions>) => LankaFetchJsonRequest<TOptions>;
212
+ declare const createLankaFetchJsonRequest: <TOptions extends TLankaRequestInit = TLankaRequestInit>(config?: ILankaTransportRequestConfig<TOptions>) => LankaFetchJsonRequest<TOptions>;
184
213
 
185
214
  /**
186
- * The multipart request: hands the `Response` back untouched.
215
+ * The network seam: `fetch`, plus the two things `fetch` cannot be told.
187
216
  *
188
- * Differs from `LankaFetchRequest` only in its transport — the one that must NOT
189
- * set `content-type`, because the browser writes it with the boundary and a
190
- * hand-set header leaves the body unparseable to the server.
191
- */
192
- declare class LankaFetchFormDataRequest<TOptions = RequestInit> extends ALankaTransportRequest<TOptions> {
193
- constructor(config?: ILankaTransportRequestConfig<TOptions>);
194
- protected parse<TReturn>(response: Response): Promise<TReturn>;
195
- }
196
-
197
- /**
198
- * The functional style of `LankaFetchFormDataRequest`: a multipart body, for an upload.
217
+ * ## Why there is ONE of these
199
218
  *
200
- * One line, and that is the point — the factory IS the class, so a behaviour
201
- * cannot exist in one style and not the other.
202
- */
203
- declare const createLankaFetchFormDataRequest: <TOptions = RequestInit>(config?: ILankaTransportRequestConfig<TOptions>) => LankaFetchFormDataRequest<TOptions>;
204
-
205
- /**
206
- * HTTP Fetch transport implementation.
207
- * Uses native fetch API without any project-specific decorators.
208
- * For project-specific logic (auth, error handling, etc.), use `request` parameter
209
- * in Gateway config or create a custom transport.
210
- */
211
- declare class LankaFetchTransport implements ILankaTransport<RequestInit> {
212
- request(resource: RequestInfo, options?: RequestInit): Promise<Response>;
213
- }
214
-
215
- /**
216
- * HTTP Fetch JSON transport implementation.
217
- * Uses native fetch API with JSON-specific headers.
218
- * Automatically sets Content-Type to application/json for requests with body.
219
- * For project-specific logic (auth, error handling, etc.), use `request` parameter
220
- * in Gateway config or create a custom transport.
221
- */
222
- declare class LankaFetchJsonTransport implements ILankaTransport<RequestInit> {
223
- request(resource: RequestInfo, options?: RequestInit): Promise<Response>;
224
- }
225
-
226
- /**
227
- * HTTP Fetch FormData transport implementation.
228
- * Uses native fetch API optimized for FormData requests.
229
- * Does not set Content-Type header (browser will set it automatically with boundary).
230
- * For project-specific logic (auth, error handling, etc.), use `request` parameter
231
- * in Gateway config or create a custom transport.
219
+ * There were three — a plain one, a JSON one and a multipart one — and the split
220
+ * was wrong at birth. `ILankaTransport` exists so a consumer can change the
221
+ * PROTOCOL: a native bridge, a socket, a double that never leaves the process.
222
+ * The three differed in a `content-type` header. That is not a protocol, it is an
223
+ * encoding, and an encoding is a property of the CALL: a gateway with fourteen
224
+ * JSON endpoints and one upload had no way to say so, because its request kind —
225
+ * and with it its transport — was fixed in its constructor. The application that
226
+ * hit this added a `useFormData` flag to its own options and wrote its own
227
+ * transport to read it.
228
+ *
229
+ * So the encoding is decided here, per call, by looking at the body. A gateway
230
+ * posts `FormData` to one endpoint and an object to the next, and neither it nor
231
+ * the request kind has to know.
232
+ *
233
+ * ## What does NOT belong here
234
+ *
235
+ * The base URL (`ALankaGateway` prefixes `apiBaseUrl`), credentials, static
236
+ * headers, CSRF, retry, auth refresh, idempotency keys and deadlines. Every one
237
+ * of those is policy around a request rather than a way of sending one, and every
238
+ * one is a middleware — `useRequestMiddleware`, which `@lankajs/plugin-http`
239
+ * occupies. A transport that grew them would be a second composition mechanism
240
+ * beside the one core already publishes, and "where does a header get added"
241
+ * would have two answers.
232
242
  */
233
- declare class LankaFetchFormDataTransport implements ILankaTransport<RequestInit> {
234
- request(resource: RequestInfo, options?: RequestInit): Promise<Response>;
243
+ declare class LankaFetchTransport implements ILankaTransport<TLankaRequestInit> {
244
+ request(resource: RequestInfo, options?: TLankaRequestInit): Promise<Response>;
235
245
  }
236
246
 
237
247
  /**
@@ -255,4 +265,4 @@ interface ILankaListQueryParams {
255
265
  filters?: Record<string, unknown>;
256
266
  }
257
267
 
258
- export { ALankaRequest, IALankaGatewayConfig, type ILankaGatewayConfig, type ILankaGatewayContext, type ILankaListQueryParams, ILankaRequest, type ILankaTransport, LankaFetchFormDataRequest, LankaFetchFormDataTransport, LankaFetchJsonRequest, LankaFetchJsonTransport, LankaFetchRequest, LankaFetchTransport, TLankaExecuteOptions, TLankaQueryBuilder, buildLankaQueryParams, createLankaFetchFormDataRequest, createLankaFetchJsonRequest, createLankaFetchRequest, createLankaGateway };
268
+ export { ALankaRequest, IALankaGatewayConfig, type ILankaGatewayConfig, type ILankaGatewayContext, type ILankaListQueryParams, ILankaRequest, type ILankaTransport, LankaFetchJsonRequest, LankaFetchRequest, LankaFetchTransport, TLankaExecuteOptions, TLankaQueryBuilder, TLankaRequestInit, buildLankaQueryParams, createLankaFetchJsonRequest, createLankaFetchRequest, createLankaGateway };
@@ -1,3 +1,6 @@
1
+ import {
2
+ lankaStandardValidator
3
+ } from "../chunk-GV5DUYST.js";
1
4
  import {
2
5
  composeLankaRequestMiddleware
3
6
  } from "../chunk-YR4MZXMU.js";
@@ -181,30 +184,49 @@ var ALankaTransportRequest = class extends ALankaRequest {
181
184
  }
182
185
  };
183
186
 
184
- // src/gateway/transport/lanka-fetch-json-transport/LankaFetchJsonTransport.ts
185
- var LankaFetchJsonTransport = class {
187
+ // src/gateway/lanka-fetch-transport/LankaFetchTransport.ts
188
+ var CONTENT_TYPE = "content-type";
189
+ var JSON_CONTENT_TYPE = "application/json";
190
+ var LankaFetchTransport = class {
186
191
  async request(resource, options) {
187
- if (options?.body && !(options.body instanceof FormData)) {
188
- const headers = new Headers(options.headers);
189
- let body = options.body;
190
- if (typeof body === "object" && !(body instanceof FormData) && !(body instanceof Blob)) {
191
- body = JSON.stringify(body);
192
- }
193
- headers.set("Content-Type", "application/json");
194
- return await fetch(resource, {
195
- ...options,
196
- headers,
197
- body
198
- });
192
+ if (options === void 0 || options.body === void 0 || options.body === null) {
193
+ return await fetch(resource, options);
199
194
  }
200
- return await fetch(resource, options);
195
+ const { body, headers } = options;
196
+ if (isFormData(body)) {
197
+ if (headers === void 0) return await fetch(resource, options);
198
+ const stripped = new Headers(headers);
199
+ stripped.delete(CONTENT_TYPE);
200
+ return await fetch(resource, { ...options, headers: stripped });
201
+ }
202
+ if (isEncodedBody(body)) return await fetch(resource, options);
203
+ return await fetch(resource, {
204
+ ...options,
205
+ headers: withJsonContentType(headers),
206
+ body: JSON.stringify(body)
207
+ });
201
208
  }
202
209
  };
210
+ function withJsonContentType(headers) {
211
+ const result = new Headers(headers);
212
+ if (!result.has(CONTENT_TYPE)) result.set(CONTENT_TYPE, JSON_CONTENT_TYPE);
213
+ return result;
214
+ }
215
+ function isEncodedBody(body) {
216
+ if (typeof body === "string") return true;
217
+ if (typeof URLSearchParams !== "undefined" && body instanceof URLSearchParams) return true;
218
+ if (typeof Blob !== "undefined" && body instanceof Blob) return true;
219
+ if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) return true;
220
+ return typeof ReadableStream !== "undefined" && body instanceof ReadableStream;
221
+ }
222
+ function isFormData(body) {
223
+ return typeof FormData !== "undefined" && body instanceof FormData;
224
+ }
203
225
 
204
226
  // src/gateway/request/lanka-fetch-json-request/LankaFetchJsonRequest.ts
205
227
  var LankaFetchJsonRequest = class extends ALankaTransportRequest {
206
228
  constructor(config = {}) {
207
- super(config, () => new LankaFetchJsonTransport());
229
+ super(config, () => new LankaFetchTransport());
208
230
  }
209
231
  /**
210
232
  * Parses the body, or names the failure.
@@ -269,12 +291,22 @@ var buildLankaQueryParams = (input) => {
269
291
  var ALankaGateway = class {
270
292
  requestExecutor;
271
293
  queryParamsHandler;
294
+ /**
295
+ * The validator a method checks a response body with.
296
+ *
297
+ * `config.validationService` when one was given, the Standard Schema port
298
+ * otherwise. It used to be accepted by the config and read by nothing: a
299
+ * consumer handing a test double to the gateway got the real validator and no
300
+ * error, which is the worst kind of ignored option — it looks honoured.
301
+ */
302
+ validationService;
272
303
  useMock;
273
304
  basePath;
274
305
  constructor(config) {
275
306
  lankaLogger.printGatewayLog("Create gateway", this);
276
307
  const flags = getLankaFlags();
277
308
  this.useMock = config.useMock ?? flags.isMockMode ?? false;
309
+ this.validationService = config.validationService ?? lankaStandardValidator;
278
310
  this.requestExecutor = config.request ?? new LankaFetchJsonRequest();
279
311
  this.basePath = config.basePath ?? "";
280
312
  this.queryParamsHandler = config.queryParamsHandler ?? buildLankaQueryParams;
@@ -363,20 +395,14 @@ var createLankaGateway = (config) => {
363
395
  return config.methods({
364
396
  endpoint: (path) => this.endpoint(path),
365
397
  request: (path, options, mockHandler) => this.request(path, options, mockHandler),
366
- buildQueryParams: (params) => this.buildQueryParams(params)
398
+ buildQueryParams: (params) => this.buildQueryParams(params),
399
+ validationService: this.validationService
367
400
  });
368
401
  }
369
402
  }
370
403
  return new FunctionalGateway(config).build();
371
404
  };
372
405
 
373
- // src/gateway/transport/lanka-fetch-transport/LankaFetchTransport.ts
374
- var LankaFetchTransport = class {
375
- async request(resource, options) {
376
- return await fetch(resource, options);
377
- }
378
- };
379
-
380
406
  // src/gateway/request/lanka-fetch-request/LankaFetchRequest.ts
381
407
  var LankaFetchRequest = class extends ALankaTransportRequest {
382
408
  constructor(config = {}) {
@@ -392,43 +418,13 @@ var createLankaFetchRequest = (config = {}) => new LankaFetchRequest(config);
392
418
 
393
419
  // src/gateway/request/_factories/create-lanka-fetch-json-request/createLankaFetchJsonRequest.ts
394
420
  var createLankaFetchJsonRequest = (config = {}) => new LankaFetchJsonRequest(config);
395
-
396
- // src/gateway/transport/lanka-fetch-form-data-transport/LankaFetchFormDataTransport.ts
397
- var LankaFetchFormDataTransport = class {
398
- async request(resource, options) {
399
- const formDataOptions = { ...options };
400
- if (formDataOptions.body instanceof FormData && formDataOptions.headers) {
401
- const headers = new Headers(formDataOptions.headers);
402
- headers.delete("Content-Type");
403
- formDataOptions.headers = headers;
404
- }
405
- return await fetch(resource, formDataOptions);
406
- }
407
- };
408
-
409
- // src/gateway/request/lanka-fetch-form-data-request/LankaFetchFormDataRequest.ts
410
- var LankaFetchFormDataRequest = class extends ALankaTransportRequest {
411
- constructor(config = {}) {
412
- super(config, () => new LankaFetchFormDataTransport());
413
- }
414
- parse(response) {
415
- return Promise.resolve(response);
416
- }
417
- };
418
-
419
- // src/gateway/request/_factories/create-lanka-fetch-form-data-request/createLankaFetchFormDataRequest.ts
420
- var createLankaFetchFormDataRequest = (config = {}) => new LankaFetchFormDataRequest(config);
421
421
  export {
422
422
  ALankaGateway,
423
423
  ALankaRequest,
424
- LankaFetchFormDataRequest,
425
- LankaFetchFormDataTransport,
426
424
  LankaFetchJsonRequest,
427
- LankaFetchJsonTransport,
428
425
  LankaFetchRequest,
429
426
  LankaFetchTransport,
430
427
  buildLankaQueryParams,
431
- createLankaFetchFormDataRequest,
432
428
  createLankaFetchJsonRequest,
433
429
  createLankaFetchRequest,
434
430
  createLankaGateway,