lanka 2.0.1 → 2.2.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/dist/{ILankaScenario-DQd9ZfUw.d.ts → ILankaScenario-7J3MoKiY.d.ts} +9 -0
- package/dist/{ILankaScenarioMetadata-BsDp0Bzm.d.ts → ILankaScenarioMetadata-DgOxvpwx.d.ts} +1 -1
- package/dist/LankaScenarioVMRegistry-CFzCpIFd.d.ts +46 -0
- package/dist/{LankaScenarioVMRegistry-DySAOaj2.d.ts → LankaScenariosRegistry-DiLN9xwy.d.ts} +3 -46
- package/dist/_extend/index.d.ts +79 -6
- package/dist/_extend/index.js +74 -29
- package/dist/_extend/index.js.map +1 -1
- package/dist/_internal/index.d.ts +5 -4
- package/dist/_internal/index.js +3 -1
- package/dist/_internal/index.js.map +1 -1
- package/dist/{activeRuntime-ByucLPhj.d.ts → activeRuntime-DxB62KN4.d.ts} +8 -4
- package/dist/bootstrap/index.d.ts +8 -7
- package/dist/bootstrap/index.js +9 -9
- package/dist/chunk-3OQ3JZGT.js +90 -0
- package/dist/chunk-3OQ3JZGT.js.map +1 -0
- package/dist/{chunk-FII3PW2G.js → chunk-6TNWBT6T.js} +2 -2
- package/dist/{chunk-RUMUFHSL.js → chunk-BZNDUUTA.js} +2 -2
- package/dist/{chunk-7DQUF2QR.js → chunk-FR2FRJME.js} +2 -2
- package/dist/{chunk-O5EUTNR6.js → chunk-HYT5NSIB.js} +2 -2
- package/dist/{chunk-42CWYVZJ.js → chunk-IB2LRYDK.js} +2 -2
- package/dist/{chunk-MKKMTLOY.js → chunk-JX47U5DC.js} +38 -5
- package/dist/{chunk-MKKMTLOY.js.map → chunk-JX47U5DC.js.map} +1 -1
- package/dist/chunk-MLKTN4OV.js +76 -0
- package/dist/chunk-MLKTN4OV.js.map +1 -0
- package/dist/{chunk-BHQ2SQ7P.js → chunk-NPJSZLTB.js} +2 -2
- package/dist/{chunk-H5TIUKRK.js → chunk-R365QEN2.js} +2 -2
- package/dist/{chunk-NWEHOMTS.js → chunk-RZGBLXXL.js} +3 -3
- package/dist/{chunk-SO7D5E7O.js → chunk-UX4A7TBG.js} +10 -10
- package/dist/chunk-UX4A7TBG.js.map +1 -0
- package/dist/config/index.js +3 -3
- package/dist/{createLanka-DvUGu9Hl.d.ts → createLanka-DxKZkmWe.d.ts} +2 -2
- package/dist/{createLankaScope-CVoV0EwO.d.ts → createLankaScope-DeElRH-E.d.ts} +7 -1
- package/dist/errors/index.js +3 -3
- package/dist/gateway/index.js +7 -7
- package/dist/index.d.ts +8 -7
- package/dist/index.js +10 -10
- package/dist/locator/index.d.ts +3 -1
- package/dist/locator/index.js +2 -2
- package/dist/locator/scenario/lanka-scenario-locator/LankaScenarioLocator.d.ts +1 -1
- package/dist/locator/scenario/lanka-scenario-locator/LankaScenarioLocator.js +5 -5
- package/dist/logger/index.js +3 -3
- package/dist/mock/index.js +3 -3
- package/dist/scenario/index.d.ts +18 -4
- package/dist/scenario/index.js +5 -5
- package/dist/stream/index.d.ts +8 -7
- package/dist/viewmodel/index.d.ts +41 -12
- package/dist/viewmodel/index.js +7 -6
- package/dist/viewmodel/index.js.map +1 -1
- package/package.json +2 -2
- package/skills/lanka-core/SKILL.md +1 -1
- package/skills/lanka-core/reference.md +21 -1
- package/skills/lanka-packages/SKILL.md +1 -1
- package/dist/chunk-SBITVBF7.js +0 -49
- package/dist/chunk-SBITVBF7.js.map +0 -1
- package/dist/chunk-SO7D5E7O.js.map +0 -1
- package/dist/chunk-XGMXT4XZ.js +0 -46
- package/dist/chunk-XGMXT4XZ.js.map +0 -1
- /package/dist/{chunk-FII3PW2G.js.map → chunk-6TNWBT6T.js.map} +0 -0
- /package/dist/{chunk-RUMUFHSL.js.map → chunk-BZNDUUTA.js.map} +0 -0
- /package/dist/{chunk-7DQUF2QR.js.map → chunk-FR2FRJME.js.map} +0 -0
- /package/dist/{chunk-O5EUTNR6.js.map → chunk-HYT5NSIB.js.map} +0 -0
- /package/dist/{chunk-42CWYVZJ.js.map → chunk-IB2LRYDK.js.map} +0 -0
- /package/dist/{chunk-BHQ2SQ7P.js.map → chunk-NPJSZLTB.js.map} +0 -0
- /package/dist/{chunk-H5TIUKRK.js.map → chunk-R365QEN2.js.map} +0 -0
- /package/dist/{chunk-NWEHOMTS.js.map → chunk-RZGBLXXL.js.map} +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { L as LankaEventBusInstance } from './ILankaScenario-
|
|
2
|
-
import { L as LankaScenariosRegistry
|
|
1
|
+
import { L as LankaEventBusInstance } from './ILankaScenario-7J3MoKiY.js';
|
|
2
|
+
import { L as LankaScenariosRegistry } from './LankaScenariosRegistry-DiLN9xwy.js';
|
|
3
|
+
import { L as LankaScenarioVMRegistry } from './LankaScenarioVMRegistry-CFzCpIFd.js';
|
|
3
4
|
import { b as ILankaRuntimeConfig } from './ILankaRuntimeConfig-Vl436GWK.js';
|
|
4
5
|
import { I as ILankaInFlightCounter } from './lankaHttpInFlight-Bk1eIuSx.js';
|
|
5
6
|
import { I as ILankaScenarioVM } from './ILankaScenarioVM-DKwjbIRq.js';
|
|
@@ -20,7 +21,8 @@ import { LankaSharedStoreLocator } from './locator/shared-store/lanka-shared-sto
|
|
|
20
21
|
* static `LankaScenarioBootstrap` — so they ask which instance is active.
|
|
21
22
|
*
|
|
22
23
|
* The module is dependency-free DELIBERATELY: every import here is `import type`
|
|
23
|
-
* and disappears at compile time
|
|
24
|
+
* and disappears at compile time, except `lankaCopies`, which imports nothing
|
|
25
|
+
* itself. Otherwise there would be a cycle
|
|
24
26
|
* `createLanka → locator → gateway → config → createLanka`, and module
|
|
25
27
|
* evaluation order would start deciding what ends up `undefined`.
|
|
26
28
|
*
|
|
@@ -131,5 +133,7 @@ declare function getLankaProcessRuntime(): ILankaRuntime | null;
|
|
|
131
133
|
* and that is the right answer there: a tab is one scope for its whole life.
|
|
132
134
|
*/
|
|
133
135
|
declare function setLankaScopeResolver(resolver: TLankaScopeResolver | null): void;
|
|
136
|
+
/** Whether anything at all knows how to answer "which scope". */
|
|
137
|
+
declare function hasLankaScopeResolver(): boolean;
|
|
134
138
|
|
|
135
|
-
export { type ILankaRuntime as I, type TLankaRuntimeResolver as T, type TLankaScopeResolver as a, setLankaRuntimeResolver as b, setLankaScopeResolver as c, getLankaProcessRuntime as g, setActiveLankaRuntime as s };
|
|
139
|
+
export { type ILankaRuntime as I, type TLankaRuntimeResolver as T, type TLankaScopeResolver as a, setLankaRuntimeResolver as b, setLankaScopeResolver as c, getLankaProcessRuntime as g, hasLankaScopeResolver as h, setActiveLankaRuntime as s };
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { c as ILankaPlugin, e as ILankaServiceConfig, I as ILankaBootstrapConfig, a as ILankaInstance } from '../createLanka-
|
|
2
|
-
export { b as ILankaInstanceConfig, d as ILankaScenarioBootstrapConfig, f as createLanka } from '../createLanka-
|
|
1
|
+
import { c as ILankaPlugin, e as ILankaServiceConfig, I as ILankaBootstrapConfig, a as ILankaInstance } from '../createLanka-DxKZkmWe.js';
|
|
2
|
+
export { b as ILankaInstanceConfig, d as ILankaScenarioBootstrapConfig, f as createLanka } from '../createLanka-DxKZkmWe.js';
|
|
3
3
|
import { I as ILankaFlags, a as ILankaHost } from '../ILankaRuntimeConfig-Vl436GWK.js';
|
|
4
|
-
export { I as ILankaScope } from '../createLankaScope-
|
|
5
|
-
import '../activeRuntime-
|
|
6
|
-
import '../ILankaScenario-
|
|
7
|
-
import '../
|
|
4
|
+
export { I as ILankaScope } from '../createLankaScope-DeElRH-E.js';
|
|
5
|
+
import '../activeRuntime-DxB62KN4.js';
|
|
6
|
+
import '../ILankaScenario-7J3MoKiY.js';
|
|
7
|
+
import '../LankaScenariosRegistry-DiLN9xwy.js';
|
|
8
8
|
import '../ILankaScenarioVM-DKwjbIRq.js';
|
|
9
|
-
import '../ILankaScenarioMetadata-
|
|
9
|
+
import '../ILankaScenarioMetadata-DgOxvpwx.js';
|
|
10
|
+
import '../LankaScenarioVMRegistry-CFzCpIFd.js';
|
|
10
11
|
import '../lankaHttpInFlight-Bk1eIuSx.js';
|
|
11
12
|
import '../lankaRequestMiddleware-DAC5kCb7.js';
|
|
12
13
|
import '../locator/gateway/lanka-gateway-locator/LankaGatewayLocator.js';
|
package/dist/bootstrap/index.js
CHANGED
|
@@ -3,21 +3,21 @@ import {
|
|
|
3
3
|
createLanka,
|
|
4
4
|
resetActiveLanka,
|
|
5
5
|
startLanka
|
|
6
|
-
} from "../chunk-
|
|
7
|
-
import "../chunk-
|
|
6
|
+
} from "../chunk-UX4A7TBG.js";
|
|
7
|
+
import "../chunk-3OQ3JZGT.js";
|
|
8
8
|
import "../chunk-BMF4TM2Z.js";
|
|
9
|
-
import "../chunk-
|
|
10
|
-
import "../chunk-
|
|
11
|
-
import "../chunk-
|
|
9
|
+
import "../chunk-IB2LRYDK.js";
|
|
10
|
+
import "../chunk-JX47U5DC.js";
|
|
11
|
+
import "../chunk-RZGBLXXL.js";
|
|
12
12
|
import "../chunk-XESL274R.js";
|
|
13
|
-
import "../chunk-
|
|
13
|
+
import "../chunk-NPJSZLTB.js";
|
|
14
14
|
import "../chunk-3R2NO47A.js";
|
|
15
15
|
import "../chunk-24MUFFHF.js";
|
|
16
16
|
import "../chunk-JZJ6GXX3.js";
|
|
17
17
|
import "../chunk-N3275IPH.js";
|
|
18
|
-
import "../chunk-
|
|
19
|
-
import "../chunk-
|
|
20
|
-
import "../chunk-
|
|
18
|
+
import "../chunk-6TNWBT6T.js";
|
|
19
|
+
import "../chunk-FR2FRJME.js";
|
|
20
|
+
import "../chunk-MLKTN4OV.js";
|
|
21
21
|
export {
|
|
22
22
|
ALankaPlugin,
|
|
23
23
|
createLanka,
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import {
|
|
2
|
+
caseConvert
|
|
3
|
+
} from "./chunk-N3275IPH.js";
|
|
4
|
+
import {
|
|
5
|
+
lankaLogger
|
|
6
|
+
} from "./chunk-6TNWBT6T.js";
|
|
7
|
+
|
|
8
|
+
// src/viewmodel/_internal/lanka-vm-recipes/lankaVMRecipes.ts
|
|
9
|
+
var lankaVMRecipes = {
|
|
10
|
+
recipes: /* @__PURE__ */ new WeakMap(),
|
|
11
|
+
instances: /* @__PURE__ */ new WeakMap(),
|
|
12
|
+
owned: /* @__PURE__ */ new WeakMap(),
|
|
13
|
+
/** The instances belonging to one scope, created on first use. */
|
|
14
|
+
forScope(scope) {
|
|
15
|
+
const existing = this.instances.get(scope);
|
|
16
|
+
if (existing) return existing;
|
|
17
|
+
const created = /* @__PURE__ */ new WeakMap();
|
|
18
|
+
this.instances.set(scope, created);
|
|
19
|
+
return created;
|
|
20
|
+
},
|
|
21
|
+
/** Records that `scope` built a ViewModel that registered for scenarios. */
|
|
22
|
+
own(scope, viewModel) {
|
|
23
|
+
const set = this.owned.get(scope) ?? /* @__PURE__ */ new Set();
|
|
24
|
+
set.add(viewModel);
|
|
25
|
+
this.owned.set(scope, set);
|
|
26
|
+
},
|
|
27
|
+
/** Everything `scope` owns, handed over once; the scope then holds nothing. */
|
|
28
|
+
release(scope) {
|
|
29
|
+
const set = this.owned.get(scope);
|
|
30
|
+
this.owned.delete(scope);
|
|
31
|
+
this.instances.delete(scope);
|
|
32
|
+
return set ? [...set] : [];
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// src/locator/_factories/create-lanka-scope/createLankaScope.ts
|
|
37
|
+
function createLankaScope(singletons, viewModels) {
|
|
38
|
+
const instances = /* @__PURE__ */ new Map();
|
|
39
|
+
let disposed = false;
|
|
40
|
+
const scope = {
|
|
41
|
+
resolve(propertyName) {
|
|
42
|
+
if (disposed) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
`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.`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
const className = caseConvert(propertyName, "pascalCase");
|
|
48
|
+
if (instances.has(className)) return instances.get(className);
|
|
49
|
+
const instance = singletons.createScopedInstance(className, propertyName);
|
|
50
|
+
instances.set(className, instance);
|
|
51
|
+
return instance;
|
|
52
|
+
},
|
|
53
|
+
dispose() {
|
|
54
|
+
if (disposed) return;
|
|
55
|
+
disposed = true;
|
|
56
|
+
for (const viewModel of lankaVMRecipes.release(scope)) {
|
|
57
|
+
try {
|
|
58
|
+
viewModel.resetScenario();
|
|
59
|
+
} catch (error) {
|
|
60
|
+
lankaLogger.printBootstrapLog(
|
|
61
|
+
"Failed to reset a ViewModel's scenarios in the scope",
|
|
62
|
+
error instanceof Error ? error.message : String(error)
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
viewModels.unregister(viewModel);
|
|
66
|
+
}
|
|
67
|
+
for (const [name, instance] of instances) {
|
|
68
|
+
const disposable = instance;
|
|
69
|
+
if (typeof disposable.dispose !== "function") continue;
|
|
70
|
+
try {
|
|
71
|
+
disposable.dispose();
|
|
72
|
+
} catch (error) {
|
|
73
|
+
lankaLogger.printBootstrapLog(
|
|
74
|
+
`Failed to dispose "${name}" in the scope`,
|
|
75
|
+
error instanceof Error ? error.message : String(error)
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
instances.clear();
|
|
80
|
+
},
|
|
81
|
+
isDisposed: () => disposed
|
|
82
|
+
};
|
|
83
|
+
return scope;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export {
|
|
87
|
+
lankaVMRecipes,
|
|
88
|
+
createLankaScope
|
|
89
|
+
};
|
|
90
|
+
//# sourceMappingURL=chunk-3OQ3JZGT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/viewmodel/_internal/lanka-vm-recipes/lankaVMRecipes.ts","../src/locator/_factories/create-lanka-scope/createLankaScope.ts"],"sourcesContent":["import type { ILankaReadableVM } from \"../../_interfaces/ILankaReadableVM\";\nimport type { ILankaScenarioVM } from \"../../../scenario/_interfaces/ILankaScenarioVM\";\n\n/** What a definition actually holds, kept where a consumer cannot reach it. */\nexport interface ILankaVMRecipe {\n\treadonly name: string;\n\treadonly build: () => ILankaReadableVM<object>;\n}\n\n/**\n * What `defineLankaVM` writes and `resolveLankaVM` reads.\n *\n * One object rather than two exported maps, because a file here holds one\n * runtime identity — and because the two halves are one mechanism: a recipe\n * nobody can resolve and an instance map keyed by nothing are each meaningless\n * alone.\n *\n * ## Weak at both ends, and both ends matter\n *\n * `recipes` is weak so a definition dropped by its module takes its recipe with\n * it. `instances` is weak in the SCOPE so a finished request's ViewModels go\n * when its scope does, and weak in the DEFINITION because the published type\n * invites `defineLankaVM({...})` written inline in a component or a loop — in a\n * browser the scope key is the framework instance and lives as long as the tab,\n * so a strong inner map would hold every inline definition and its ViewModel\n * for the life of the page.\n *\n * ## `owned` is the one strong set, and it is drained\n *\n * A scope handed to `resolveLankaVM` must be able to take its ViewModels OFF the\n * bus when it closes, and a WeakMap cannot be walked. So what a scope built is\n * also kept in a Set it can read — strong, because the scope must find these\n * even when nothing else holds them — and `release` hands the set over and\n * forgets it, so a closed scope keeps nothing alive.\n */\nexport const lankaVMRecipes = {\n\trecipes: new WeakMap<object, ILankaVMRecipe>(),\n\tinstances: new WeakMap<object, WeakMap<object, ILankaReadableVM<object>>>(),\n\towned: new WeakMap<object, Set<ILankaScenarioVM>>(),\n\n\t/** The instances belonging to one scope, created on first use. */\n\tforScope(scope: object): WeakMap<object, ILankaReadableVM<object>> {\n\t\tconst existing = this.instances.get(scope);\n\n\t\tif (existing) return existing;\n\n\t\tconst created = new WeakMap<object, ILankaReadableVM<object>>();\n\n\t\tthis.instances.set(scope, created);\n\n\t\treturn created;\n\t},\n\t/** Records that `scope` built a ViewModel that registered for scenarios. */\n\town(scope: object, viewModel: ILankaScenarioVM): void {\n\t\tconst set = this.owned.get(scope) ?? new Set<ILankaScenarioVM>();\n\t\tset.add(viewModel);\n\t\tthis.owned.set(scope, set);\n\t},\n\t/** Everything `scope` owns, handed over once; the scope then holds nothing. */\n\trelease(scope: object): ILankaScenarioVM[] {\n\t\tconst set = this.owned.get(scope);\n\t\tthis.owned.delete(scope);\n\t\tthis.instances.delete(scope);\n\t\treturn set ? [...set] : [];\n\t},\n};\n","import { caseConvert } from \"../../../_internal/case-convert/caseConvert\";\nimport { lankaLogger } from \"../../../logger/lanka-logger/LankaLogger\";\nimport { lankaVMRecipes } from \"../../../viewmodel/_internal/lanka-vm-recipes/lankaVMRecipes\";\nimport type { LankaScenarioVMRegistry } from \"../../../scenario/_registries/lanka-scenario-vm-registry/LankaScenarioVMRegistry\";\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 *\n * The same rule decides how a ViewModel becomes a scope's own: it is RESOLVED\n * in it, `resolveLankaVM(definition, { scope })`, never captured by having been\n * built while some callback ran. Those leave with the scope too — off the bus\n * and out of the registry.\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(\n\tsingletons: LankaSingletonLocator,\n\tviewModels: LankaScenarioVMRegistry,\n): ILankaScope {\n\tconst instances = new Map<string, unknown>();\n\tlet disposed = false;\n\n\tconst scope: ILankaScope = {\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\t// ViewModels first: a handler may still reach a service below.\n\t\t\tfor (const viewModel of lankaVMRecipes.release(scope)) {\n\t\t\t\ttry {\n\t\t\t\t\tviewModel.resetScenario();\n\t\t\t\t} catch (error) {\n\t\t\t\t\tlankaLogger.printBootstrapLog(\n\t\t\t\t\t\t\"Failed to reset a ViewModel's scenarios 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\tviewModels.unregister(viewModel);\n\t\t\t}\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\treturn scope;\n}\n"],"mappings":";;;;;;;;AAmCO,IAAM,iBAAiB;AAAA,EAC7B,SAAS,oBAAI,QAAgC;AAAA,EAC7C,WAAW,oBAAI,QAA2D;AAAA,EAC1E,OAAO,oBAAI,QAAuC;AAAA;AAAA,EAGlD,SAAS,OAA0D;AAClE,UAAM,WAAW,KAAK,UAAU,IAAI,KAAK;AAEzC,QAAI,SAAU,QAAO;AAErB,UAAM,UAAU,oBAAI,QAA0C;AAE9D,SAAK,UAAU,IAAI,OAAO,OAAO;AAEjC,WAAO;AAAA,EACR;AAAA;AAAA,EAEA,IAAI,OAAe,WAAmC;AACrD,UAAM,MAAM,KAAK,MAAM,IAAI,KAAK,KAAK,oBAAI,IAAsB;AAC/D,QAAI,IAAI,SAAS;AACjB,SAAK,MAAM,IAAI,OAAO,GAAG;AAAA,EAC1B;AAAA;AAAA,EAEA,QAAQ,OAAmC;AAC1C,UAAM,MAAM,KAAK,MAAM,IAAI,KAAK;AAChC,SAAK,MAAM,OAAO,KAAK;AACvB,SAAK,UAAU,OAAO,KAAK;AAC3B,WAAO,MAAM,CAAC,GAAG,GAAG,IAAI,CAAC;AAAA,EAC1B;AACD;;;ACvBO,SAAS,iBACf,YACA,YACc;AACd,QAAM,YAAY,oBAAI,IAAqB;AAC3C,MAAI,WAAW;AAEf,QAAM,QAAqB;AAAA,IAC1B,QAAmB,cAAiC;AACnD,UAAI,UAAU;AAIb,cAAM,IAAI;AAAA,UACT,yBAAyB,YAAY;AAAA,QAEtC;AAAA,MACD;AAEA,YAAM,YAAY,YAAY,cAAc,YAAY;AACxD,UAAI,UAAU,IAAI,SAAS,EAAG,QAAO,UAAU,IAAI,SAAS;AAI5D,YAAM,WAAW,WAAW,qBAAqB,WAAW,YAAY;AACxE,gBAAU,IAAI,WAAW,QAAQ;AACjC,aAAO;AAAA,IACR;AAAA,IAEA,UAAgB;AACf,UAAI,SAAU;AACd,iBAAW;AAGX,iBAAW,aAAa,eAAe,QAAQ,KAAK,GAAG;AACtD,YAAI;AACH,oBAAU,cAAc;AAAA,QACzB,SAAS,OAAO;AACf,sBAAY;AAAA,YACX;AAAA,YACA,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,UACtD;AAAA,QACD;AACA,mBAAW,WAAW,SAAS;AAAA,MAChC;AAEA,iBAAW,CAAC,MAAM,QAAQ,KAAK,WAAW;AACzC,cAAM,aAAa;AACnB,YAAI,OAAO,WAAW,YAAY,WAAY;AAC9C,YAAI;AACH,qBAAW,QAAQ;AAAA,QACpB,SAAS,OAAO;AAGf,sBAAY;AAAA,YACX,sBAAsB,IAAI;AAAA,YAC1B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,UACtD;AAAA,QACD;AAAA,MACD;AAEA,gBAAU,MAAM;AAAA,IACjB;AAAA,IAEA,YAAY,MAAM;AAAA,EACnB;AAEA,SAAO;AACR;","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getLankaFlags
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-FR2FRJME.js";
|
|
4
4
|
|
|
5
5
|
// src/logger/lanka-logger/LankaLogger.ts
|
|
6
6
|
var noop = () => {
|
|
@@ -221,4 +221,4 @@ export {
|
|
|
221
221
|
LankaLogger,
|
|
222
222
|
lankaLogger
|
|
223
223
|
};
|
|
224
|
-
//# sourceMappingURL=chunk-
|
|
224
|
+
//# sourceMappingURL=chunk-6TNWBT6T.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
requireActiveRuntime
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-MLKTN4OV.js";
|
|
4
4
|
|
|
5
5
|
// src/config/get-lanka-host/getLankaHost.ts
|
|
6
6
|
function getLankaHost() {
|
|
@@ -16,4 +16,4 @@ function getLankaHost() {
|
|
|
16
16
|
export {
|
|
17
17
|
getLankaHost
|
|
18
18
|
};
|
|
19
|
-
//# sourceMappingURL=chunk-
|
|
19
|
+
//# sourceMappingURL=chunk-BZNDUUTA.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getActiveRuntime
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-MLKTN4OV.js";
|
|
4
4
|
|
|
5
5
|
// src/config/get-lanka-flags/getLankaFlags.ts
|
|
6
6
|
function getLankaFlags() {
|
|
@@ -10,4 +10,4 @@ function getLankaFlags() {
|
|
|
10
10
|
export {
|
|
11
11
|
getLankaFlags
|
|
12
12
|
};
|
|
13
|
-
//# sourceMappingURL=chunk-
|
|
13
|
+
//# sourceMappingURL=chunk-FR2FRJME.js.map
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from "./chunk-G32H73QY.js";
|
|
8
8
|
import {
|
|
9
9
|
getLankaHost
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-BZNDUUTA.js";
|
|
11
11
|
|
|
12
12
|
// src/errors/_factories/create-lanka-api-error/createLankaApiError.ts
|
|
13
13
|
var createLankaApiError = (status, errors, body) => {
|
|
@@ -49,4 +49,4 @@ export {
|
|
|
49
49
|
createLankaApiError,
|
|
50
50
|
handleLankaApiError
|
|
51
51
|
};
|
|
52
|
-
//# sourceMappingURL=chunk-
|
|
52
|
+
//# sourceMappingURL=chunk-HYT5NSIB.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ALankaScenario,
|
|
3
3
|
LankaScenariosRegistry
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-RZGBLXXL.js";
|
|
5
5
|
import {
|
|
6
6
|
ALankaLocator
|
|
7
7
|
} from "./chunk-N3275IPH.js";
|
|
@@ -119,4 +119,4 @@ var LankaScenarioLocator = class extends ALankaLocator {
|
|
|
119
119
|
export {
|
|
120
120
|
LankaScenarioLocator
|
|
121
121
|
};
|
|
122
|
-
//# sourceMappingURL=chunk-
|
|
122
|
+
//# sourceMappingURL=chunk-IB2LRYDK.js.map
|
|
@@ -3,14 +3,14 @@ import {
|
|
|
3
3
|
LankaScenarioVMRegistry,
|
|
4
4
|
LankaScenariosRegistry,
|
|
5
5
|
lankaEventBus
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-RZGBLXXL.js";
|
|
7
7
|
import {
|
|
8
8
|
lankaLogger
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-6TNWBT6T.js";
|
|
10
10
|
import {
|
|
11
11
|
getActiveRuntime,
|
|
12
12
|
requireActiveRuntime
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-MLKTN4OV.js";
|
|
14
14
|
|
|
15
15
|
// src/scenario/lanka-scenario-bootstrap/LankaScenarioBootstrap.ts
|
|
16
16
|
var LankaScenarioBootstrap = class {
|
|
@@ -163,6 +163,8 @@ var LankaScenarioBootstrap = class {
|
|
|
163
163
|
* suite stays green and the leak is one user seeing another's board.
|
|
164
164
|
*/
|
|
165
165
|
isBuildingScoped = false;
|
|
166
|
+
/** Who is told about each scoped registration while `buildScoped` runs. */
|
|
167
|
+
scopedRegistrations = null;
|
|
166
168
|
/**
|
|
167
169
|
* Runs `build` with its declarations kept OUT of the process-wide list.
|
|
168
170
|
*
|
|
@@ -171,16 +173,47 @@ var LankaScenarioBootstrap = class {
|
|
|
171
173
|
* have to carry a flag through to `registerViewModel`, and eight of them have
|
|
172
174
|
* no business knowing what a scope is.
|
|
173
175
|
*/
|
|
174
|
-
buildScoped(build) {
|
|
176
|
+
buildScoped(build, onRegistered) {
|
|
175
177
|
const before = this.isBuildingScoped;
|
|
178
|
+
const beforeRegistrations = this.scopedRegistrations;
|
|
176
179
|
this.isBuildingScoped = true;
|
|
180
|
+
this.scopedRegistrations = onRegistered ?? null;
|
|
177
181
|
try {
|
|
178
182
|
return build();
|
|
179
183
|
} finally {
|
|
180
184
|
this.isBuildingScoped = before;
|
|
185
|
+
this.scopedRegistrations = beforeRegistrations;
|
|
181
186
|
}
|
|
182
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* The scoped state as it is NOW, as a runner that re-enters it later.
|
|
190
|
+
*
|
|
191
|
+
* For a build that is deferred past the window `buildScoped` opened — a lazy
|
|
192
|
+
* ViewModel builds on its first read, after `resolveLankaVM` has returned.
|
|
193
|
+
* Without it that build would run under whatever state is current at the
|
|
194
|
+
* read: outside every scope, so a scoped ViewModel would join the
|
|
195
|
+
* process-wide list every later instance adopts; or inside SOMEBODY ELSE'S,
|
|
196
|
+
* so a module-level one first read during another scope's build would die
|
|
197
|
+
* with that scope. Captured at creation, it belongs where it was declared.
|
|
198
|
+
*/
|
|
199
|
+
captureScoped() {
|
|
200
|
+
const captured = this.isBuildingScoped;
|
|
201
|
+
const capturedRegistrations = this.scopedRegistrations;
|
|
202
|
+
return (build) => {
|
|
203
|
+
const before = this.isBuildingScoped;
|
|
204
|
+
const beforeRegistrations = this.scopedRegistrations;
|
|
205
|
+
this.isBuildingScoped = captured;
|
|
206
|
+
this.scopedRegistrations = capturedRegistrations;
|
|
207
|
+
try {
|
|
208
|
+
return build();
|
|
209
|
+
} finally {
|
|
210
|
+
this.isBuildingScoped = before;
|
|
211
|
+
this.scopedRegistrations = beforeRegistrations;
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
}
|
|
183
215
|
registerViewModel(viewModel, name) {
|
|
216
|
+
this.scopedRegistrations?.(viewModel);
|
|
184
217
|
if (!this.isBuildingScoped) {
|
|
185
218
|
const isKnown = this.declaredViewModels.some(
|
|
186
219
|
(declared) => declared.viewModel === viewModel
|
|
@@ -247,4 +280,4 @@ var lankaScenarioBootstrap = new LankaScenarioBootstrap();
|
|
|
247
280
|
export {
|
|
248
281
|
lankaScenarioBootstrap
|
|
249
282
|
};
|
|
250
|
-
//# sourceMappingURL=chunk-
|
|
283
|
+
//# sourceMappingURL=chunk-JX47U5DC.js.map
|
|
@@ -1 +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 type { ILankaScenarioResetConfig } from \"../_interfaces/ILankaScenarioResetConfig\";\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\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\t// The list of declared scenario classes comes from the LOCATOR, which is the\n\t\t// one module that reads `@lanka_di/Scenarios`.\n\t\t//\n\t\t// It was read here too until 2.0.1, and that is the whole reason this line\n\t\t// has a comment. The framework reading a consumer barrel puts the module\n\t\t// that does it inside a cycle — barrel → consumer's class → back into the\n\t\t// framework — and this module lives in `lanka/scenario`, which is where a\n\t\t// consumer's scenario class imports `ALankaScenario` from. A bundler orders\n\t\t// the chunks behind that entry as it pleases; with the barrel-reading one\n\t\t// first, the consumer's class extended `undefined` and no application with a\n\t\t// scenario started at all. `lanka/locator` holds the cycle now, and nothing\n\t\t// a barrel exports imports `lanka/locator`.\n\t\t//\n\t\t// Via the runtime rather than an import, because that is the direction that\n\t\t// already exists: this is an ambient facade and reaches its instance exactly\n\t\t// as it reaches `scenarioState` above.\n\t\tconst declared = requireActiveRuntime().locators.scenarios.getDeclaredScenarioClasses();\n\n\t\tfor (const Scenario of declared) {\n\t\t\tif (pooled.has(Scenario)) 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 Scenario();\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// The second adoption. On a server this is the FIRST one that can succeed:\n\t\t// the instance is in its caller's store by now, and creation's attempt was\n\t\t// made before it could be. In a browser it is a no-op — creation adopted\n\t\t// already, and attaching is idempotent.\n\t\tthis.adoptDeclaredViewModels();\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/**\n\t * Registers everything declared into a NEW instance.\n\t *\n\t * Called twice, and the second call is the one that makes this work on a\n\t * server: `createLanka` calls it, and so does `bootstrap()`.\n\t *\n\t * The reason is an order nobody can change. Where \"which instance is active\"\n\t * is answered by a RESOLVER — one instance per request, the shape\n\t * `@lankajs/host` installs — an instance becomes findable only once the CALLER\n\t * has put it in its store, and a caller can only do that after `createLanka`\n\t * has returned. So during creation the answer is honestly \"none\", and asking\n\t * for it threw: every server render of an application with a module-level\n\t * ViewModel failed inside the call that was creating the scope, with a message\n\t * about running outside one.\n\t *\n\t * Skipping is safe precisely because bootstrap adopts again. It is idempotent\n\t * at the other end too: `attachViewModel` returns early for a ViewModel the\n\t * registry already holds.\n\t */\n\tpublic adoptDeclaredViewModels(): void {\n\t\tif (!getActiveRuntime()) return;\n\n\t\tfor (const { viewModel, name } of [...this.declaredViewModels]) {\n\t\t\tthis.attachViewModel(viewModel, name);\n\t\t}\n\t}\n\n\t/**\n\t * Whether the ViewModel being built belongs to ONE scope.\n\t *\n\t * A module-level ViewModel is declared once and adopted by every instance\n\t * ever created, which is what `declaredViewModels` is for and why it is never\n\t * drained. A SCOPED one is the opposite: it belongs to the request that built\n\t * it and to nothing after.\n\t *\n\t * Declaring one would be the defect this flag exists to prevent, and it is\n\t * worth spelling out because nothing would look wrong. The array grows by an\n\t * entry per request for the life of the process, and request N+1 ADOPTS\n\t * request N's ViewModel — subscribing it to N+1's scenarios, so a dispatch\n\t * runs N's handlers against N's gateways and writes into N's store. Every\n\t * suite stays green and the leak is one user seeing another's board.\n\t */\n\tprivate isBuildingScoped = false;\n\n\t/**\n\t * Runs `build` with its declarations kept OUT of the process-wide list.\n\t *\n\t * Called by `resolveLankaVM` and by nothing else. A narrow seam rather than a\n\t * parameter on every factory: the nine ways to build a ViewModel would each\n\t * have to carry a flag through to `registerViewModel`, and eight of them have\n\t * no business knowing what a scope is.\n\t */\n\tpublic buildScoped<TBuilt>(build: () => TBuilt): TBuilt {\n\t\tconst before = this.isBuildingScoped;\n\n\t\tthis.isBuildingScoped = true;\n\n\t\ttry {\n\t\t\treturn build();\n\t\t} finally {\n\t\t\tthis.isBuildingScoped = before;\n\t\t}\n\t}\n\n\tpublic registerViewModel(viewModel: ILankaScenarioVM, name?: string): void {\n\t\tif (!this.isBuildingScoped) {\n\t\t\tconst isKnown = this.declaredViewModels.some(\n\t\t\t\t(declared) => declared.viewModel === viewModel,\n\t\t\t);\n\t\t\tif (!isKnown) this.declaredViewModels.push({ viewModel, name });\n\t\t}\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\t * What it does NOT clear by default is which ViewModels were DECLARED, and\n\t * that default is load-bearing: a module-level ViewModel is built once per\n\t * process, so the declaration is the only thing that lets a second instance\n\t * find it. Drop it and its handlers bind to nothing, silently, for the rest of\n\t * the process — see `declaredViewModels`.\n\t *\n\t * `withDeclarations` is for the case that default gets wrong: a suite that\n\t * builds ViewModels inside test BODIES. Those are declared like any other and\n\t * nothing un-declares them, so the next bootstrap re-adopts every one ever\n\t * built and a finished test's handlers run again — against the gateway that\n\t * test built, which is somebody else's double. Forgetting is not a tombstone:\n\t * a ViewModel declared again afterwards is adopted again.\n\t *\n\t * @param config `withDeclarations` also forgets which ViewModels exist\n\t */\n\tpublic reset(config: ILankaScenarioResetConfig = {}): 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\n\t\tif (config.withDeclarations) this.declaredViewModels.length = 0;\n\t}\n}\n\n/** The one every caller wants. */\nexport const lankaScenarioBootstrap = new LankaScenarioBootstrap();\n"],"mappings":"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","names":[]}
|
|
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 type { ILankaScenarioResetConfig } from \"../_interfaces/ILankaScenarioResetConfig\";\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\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\t// The list of declared scenario classes comes from the LOCATOR, which is the\n\t\t// one module that reads `@lanka_di/Scenarios`.\n\t\t//\n\t\t// It was read here too until 2.0.1, and that is the whole reason this line\n\t\t// has a comment. The framework reading a consumer barrel puts the module\n\t\t// that does it inside a cycle — barrel → consumer's class → back into the\n\t\t// framework — and this module lives in `lanka/scenario`, which is where a\n\t\t// consumer's scenario class imports `ALankaScenario` from. A bundler orders\n\t\t// the chunks behind that entry as it pleases; with the barrel-reading one\n\t\t// first, the consumer's class extended `undefined` and no application with a\n\t\t// scenario started at all. `lanka/locator` holds the cycle now, and nothing\n\t\t// a barrel exports imports `lanka/locator`.\n\t\t//\n\t\t// Via the runtime rather than an import, because that is the direction that\n\t\t// already exists: this is an ambient facade and reaches its instance exactly\n\t\t// as it reaches `scenarioState` above.\n\t\tconst declared = requireActiveRuntime().locators.scenarios.getDeclaredScenarioClasses();\n\n\t\tfor (const Scenario of declared) {\n\t\t\tif (pooled.has(Scenario)) 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 Scenario();\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// The second adoption. On a server this is the FIRST one that can succeed:\n\t\t// the instance is in its caller's store by now, and creation's attempt was\n\t\t// made before it could be. In a browser it is a no-op — creation adopted\n\t\t// already, and attaching is idempotent.\n\t\tthis.adoptDeclaredViewModels();\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/**\n\t * Registers everything declared into a NEW instance.\n\t *\n\t * Called twice, and the second call is the one that makes this work on a\n\t * server: `createLanka` calls it, and so does `bootstrap()`.\n\t *\n\t * The reason is an order nobody can change. Where \"which instance is active\"\n\t * is answered by a RESOLVER — one instance per request, the shape\n\t * `@lankajs/host` installs — an instance becomes findable only once the CALLER\n\t * has put it in its store, and a caller can only do that after `createLanka`\n\t * has returned. So during creation the answer is honestly \"none\", and asking\n\t * for it threw: every server render of an application with a module-level\n\t * ViewModel failed inside the call that was creating the scope, with a message\n\t * about running outside one.\n\t *\n\t * Skipping is safe precisely because bootstrap adopts again. It is idempotent\n\t * at the other end too: `attachViewModel` returns early for a ViewModel the\n\t * registry already holds.\n\t */\n\tpublic adoptDeclaredViewModels(): void {\n\t\tif (!getActiveRuntime()) return;\n\n\t\tfor (const { viewModel, name } of [...this.declaredViewModels]) {\n\t\t\tthis.attachViewModel(viewModel, name);\n\t\t}\n\t}\n\n\t/**\n\t * Whether the ViewModel being built belongs to ONE scope.\n\t *\n\t * A module-level ViewModel is declared once and adopted by every instance\n\t * ever created, which is what `declaredViewModels` is for and why it is never\n\t * drained. A SCOPED one is the opposite: it belongs to the request that built\n\t * it and to nothing after.\n\t *\n\t * Declaring one would be the defect this flag exists to prevent, and it is\n\t * worth spelling out because nothing would look wrong. The array grows by an\n\t * entry per request for the life of the process, and request N+1 ADOPTS\n\t * request N's ViewModel — subscribing it to N+1's scenarios, so a dispatch\n\t * runs N's handlers against N's gateways and writes into N's store. Every\n\t * suite stays green and the leak is one user seeing another's board.\n\t */\n\tprivate isBuildingScoped = false;\n\n\t/** Who is told about each scoped registration while `buildScoped` runs. */\n\tprivate scopedRegistrations: ((viewModel: ILankaScenarioVM) => void) | null = null;\n\n\t/**\n\t * Runs `build` with its declarations kept OUT of the process-wide list.\n\t *\n\t * Called by `resolveLankaVM` and by nothing else. A narrow seam rather than a\n\t * parameter on every factory: the nine ways to build a ViewModel would each\n\t * have to carry a flag through to `registerViewModel`, and eight of them have\n\t * no business knowing what a scope is.\n\t */\n\tpublic buildScoped<TBuilt>(\n\t\tbuild: () => TBuilt,\n\t\tonRegistered?: (viewModel: ILankaScenarioVM) => void,\n\t): TBuilt {\n\t\tconst before = this.isBuildingScoped;\n\t\tconst beforeRegistrations = this.scopedRegistrations;\n\n\t\tthis.isBuildingScoped = true;\n\t\tthis.scopedRegistrations = onRegistered ?? null;\n\n\t\ttry {\n\t\t\treturn build();\n\t\t} finally {\n\t\t\tthis.isBuildingScoped = before;\n\t\t\tthis.scopedRegistrations = beforeRegistrations;\n\t\t}\n\t}\n\n\t/**\n\t * The scoped state as it is NOW, as a runner that re-enters it later.\n\t *\n\t * For a build that is deferred past the window `buildScoped` opened — a lazy\n\t * ViewModel builds on its first read, after `resolveLankaVM` has returned.\n\t * Without it that build would run under whatever state is current at the\n\t * read: outside every scope, so a scoped ViewModel would join the\n\t * process-wide list every later instance adopts; or inside SOMEBODY ELSE'S,\n\t * so a module-level one first read during another scope's build would die\n\t * with that scope. Captured at creation, it belongs where it was declared.\n\t */\n\tpublic captureScoped(): <TBuilt>(build: () => TBuilt) => TBuilt {\n\t\tconst captured = this.isBuildingScoped;\n\t\tconst capturedRegistrations = this.scopedRegistrations;\n\n\t\treturn (build) => {\n\t\t\tconst before = this.isBuildingScoped;\n\t\t\tconst beforeRegistrations = this.scopedRegistrations;\n\n\t\t\tthis.isBuildingScoped = captured;\n\t\t\tthis.scopedRegistrations = capturedRegistrations;\n\n\t\t\ttry {\n\t\t\t\treturn build();\n\t\t\t} finally {\n\t\t\t\tthis.isBuildingScoped = before;\n\t\t\t\tthis.scopedRegistrations = beforeRegistrations;\n\t\t\t}\n\t\t};\n\t}\n\n\tpublic registerViewModel(viewModel: ILankaScenarioVM, name?: string): void {\n\t\t// Told BEFORE the runtime check: a scope owns what it built whether or\n\t\t// not an instance was there to attach it to.\n\t\tthis.scopedRegistrations?.(viewModel);\n\n\t\tif (!this.isBuildingScoped) {\n\t\t\tconst isKnown = this.declaredViewModels.some(\n\t\t\t\t(declared) => declared.viewModel === viewModel,\n\t\t\t);\n\t\t\tif (!isKnown) this.declaredViewModels.push({ viewModel, name });\n\t\t}\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\t * What it does NOT clear by default is which ViewModels were DECLARED, and\n\t * that default is load-bearing: a module-level ViewModel is built once per\n\t * process, so the declaration is the only thing that lets a second instance\n\t * find it. Drop it and its handlers bind to nothing, silently, for the rest of\n\t * the process — see `declaredViewModels`.\n\t *\n\t * `withDeclarations` is for the case that default gets wrong: a suite that\n\t * builds ViewModels inside test BODIES. Those are declared like any other and\n\t * nothing un-declares them, so the next bootstrap re-adopts every one ever\n\t * built and a finished test's handlers run again — against the gateway that\n\t * test built, which is somebody else's double. Forgetting is not a tombstone:\n\t * a ViewModel declared again afterwards is adopted again.\n\t *\n\t * @param config `withDeclarations` also forgets which ViewModels exist\n\t */\n\tpublic reset(config: ILankaScenarioResetConfig = {}): 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\n\t\tif (config.withDeclarations) this.declaredViewModels.length = 0;\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,76 @@
|
|
|
1
|
+
// src/_internal/active-runtime/lankaCopies.ts
|
|
2
|
+
var COPIES = /* @__PURE__ */ Symbol.for("lanka.copies");
|
|
3
|
+
var copies = () => {
|
|
4
|
+
const global = globalThis;
|
|
5
|
+
return global[COPIES] ??= /* @__PURE__ */ new Set();
|
|
6
|
+
};
|
|
7
|
+
var thisCopy = null;
|
|
8
|
+
var warned = false;
|
|
9
|
+
var otherCopies = () => [...copies()].filter((copy) => copy !== thisCopy);
|
|
10
|
+
var anotherIsRunning = () => otherCopies().some((copy) => copy() !== null);
|
|
11
|
+
var warnOnce = () => {
|
|
12
|
+
if (warned) return;
|
|
13
|
+
warned = true;
|
|
14
|
+
console.warn(
|
|
15
|
+
"[lanka] two copies of lanka are on this page. Each copy has its own runtime, so a scenario triggered through one never reaches a ViewModel registered with the other. If the modules on this page are meant to share state, ship ONE lanka: a singleton in Module Federation's `shared`, an import map, or `lanka` external in every module's build. If they are meant to be isolated, this is expected."
|
|
16
|
+
);
|
|
17
|
+
};
|
|
18
|
+
var join = (state) => {
|
|
19
|
+
thisCopy = state;
|
|
20
|
+
copies().add(state);
|
|
21
|
+
if (otherCopies().some((copy) => copy() === true)) warnOnce();
|
|
22
|
+
};
|
|
23
|
+
var noteActivation = (isDevelopment) => {
|
|
24
|
+
if (thisCopy) copies().add(thisCopy);
|
|
25
|
+
if (isDevelopment && anotherIsRunning()) warnOnce();
|
|
26
|
+
};
|
|
27
|
+
var lankaCopies = Object.freeze({ join, noteActivation, anotherIsRunning });
|
|
28
|
+
|
|
29
|
+
// src/_internal/active-runtime/activeRuntime.ts
|
|
30
|
+
var active = null;
|
|
31
|
+
var resolveRuntime = null;
|
|
32
|
+
var resolveScope = null;
|
|
33
|
+
function setActiveLankaRuntime(runtime) {
|
|
34
|
+
active = runtime;
|
|
35
|
+
if (runtime) lankaCopies.noteActivation(runtime.getFlags().isDevelopment === true);
|
|
36
|
+
}
|
|
37
|
+
lankaCopies.join(() => active === null ? null : active.getFlags().isDevelopment === true);
|
|
38
|
+
function setLankaRuntimeResolver(resolver) {
|
|
39
|
+
resolveRuntime = resolver;
|
|
40
|
+
}
|
|
41
|
+
function getLankaProcessRuntime() {
|
|
42
|
+
return active;
|
|
43
|
+
}
|
|
44
|
+
function getActiveRuntime() {
|
|
45
|
+
return resolveRuntime ? resolveRuntime() : active;
|
|
46
|
+
}
|
|
47
|
+
function setLankaScopeResolver(resolver) {
|
|
48
|
+
resolveScope = resolver;
|
|
49
|
+
}
|
|
50
|
+
function hasLankaScopeResolver() {
|
|
51
|
+
return resolveScope !== null;
|
|
52
|
+
}
|
|
53
|
+
function getActiveLankaScope() {
|
|
54
|
+
return resolveScope ? resolveScope() : null;
|
|
55
|
+
}
|
|
56
|
+
function requireActiveRuntime() {
|
|
57
|
+
const runtime = resolveRuntime ? resolveRuntime() : active;
|
|
58
|
+
if (!runtime) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
resolveRuntime ? "lanka has no instance for this call. A runtime resolver is installed and answered with none, which on a server means the code ran outside a request scope \u2014 start one, or do this work inside it." : lankaCopies.anotherIsRunning() ? "lanka used before an instance existed in THIS copy of the package, while another copy of lanka on this page has one. The module calling it bundled its own lanka; ship one \u2014 a singleton in Module Federation's `shared`, an import map, or `lanka` external in its build." : "lanka used before an instance existed. Call createLanka({ host }) and activate it."
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
return runtime;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export {
|
|
67
|
+
setActiveLankaRuntime,
|
|
68
|
+
setLankaRuntimeResolver,
|
|
69
|
+
getLankaProcessRuntime,
|
|
70
|
+
getActiveRuntime,
|
|
71
|
+
setLankaScopeResolver,
|
|
72
|
+
hasLankaScopeResolver,
|
|
73
|
+
getActiveLankaScope,
|
|
74
|
+
requireActiveRuntime
|
|
75
|
+
};
|
|
76
|
+
//# sourceMappingURL=chunk-MLKTN4OV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/_internal/active-runtime/lankaCopies.ts","../src/_internal/active-runtime/activeRuntime.ts"],"sourcesContent":["/**\n * Which COPIES of the package are on this page, and whether any of them runs.\n *\n * A copy is one evaluation of this module — one bundle's `lanka`. Two instances\n * of one copy are the isolation `createLanka` promises; two copies are what a\n * build produces when separately bundled modules each carry their own `lanka`,\n * and then each copy's pointer knows only its own runtime. A scenario triggered\n * through one never reaches a ViewModel registered with the other, and nothing\n * anywhere reports it.\n *\n * The only place two copies can meet is the global object, under a name every\n * copy agrees on without importing anything: `Symbol.for`.\n *\n * Dependency-free for the same reason `activeRuntime.ts` is — it imports nothing\n * and adds no edge to the module graph. That is also what keeps hot reload from\n * reading as a second copy: an update re-evaluates the edited module and its\n * IMPORTERS, and a module that imports nothing is on no such path unless it is\n * the file being edited. A full reload starts a new global object.\n */\n\n/**\n * One copy, as the others see it: `null` while it has no runtime, otherwise\n * whether that runtime runs in development.\n */\nexport type TLankaCopyState = () => boolean | null;\n\nconst COPIES = Symbol.for(\"lanka.copies\");\n\nconst copies = (): Set<TLankaCopyState> => {\n\tconst global = globalThis as Record<symbol, Set<TLankaCopyState> | undefined>;\n\treturn (global[COPIES] ??= new Set<TLankaCopyState>());\n};\n\nlet thisCopy: TLankaCopyState | null = null;\nlet warned = false;\n\nconst otherCopies = (): TLankaCopyState[] => [...copies()].filter((copy) => copy !== thisCopy);\n\n/** Whether a copy of the package other than this one has a runtime. */\nconst anotherIsRunning = (): boolean => otherCopies().some((copy) => copy() !== null);\n\n/**\n * Once per copy, and only in development: a production page with two copies may\n * be two isolated applications on purpose, and the reader who can fix a build\n * is the one running it locally.\n */\nconst warnOnce = (): void => {\n\tif (warned) return;\n\n\twarned = true;\n\tconsole.warn(\n\t\t\"[lanka] two copies of lanka are on this page. Each copy has its own \" +\n\t\t\t\"runtime, so a scenario triggered through one never reaches a ViewModel \" +\n\t\t\t\"registered with the other. If the modules on this page are meant to share \" +\n\t\t\t\"state, ship ONE lanka: a singleton in Module Federation's `shared`, an \" +\n\t\t\t\"import map, or `lanka` external in every module's build. If they are \" +\n\t\t\t\"meant to be isolated, this is expected.\",\n\t);\n};\n\n/**\n * Puts this copy on the page's list, when the copy LOADS.\n *\n * Loading is the one moment a copy that is never started does anything the page\n * can see — and never starting is the commonest form of the accident: the shell\n * started lanka, a module bundled its own and saw no reason to. Whether to warn\n * is the RUNNING copy's call, since this one has no flags yet.\n */\nconst join = (state: TLankaCopyState): void => {\n\tthisCopy = state;\n\tcopies().add(state);\n\n\tif (otherCopies().some((copy) => copy() === true)) warnOnce();\n};\n\n/**\n * Called on every activation, which is not a hot path: `createLanka`, its\n * `bootstrap`, and one instance per request on a server.\n */\nconst noteActivation = (isDevelopment: boolean): void => {\n\t// Re-added, not assumed: whatever cleared the global list — a test between\n\t// cases — must not make a running copy invisible to the next one.\n\tif (thisCopy) copies().add(thisCopy);\n\n\tif (isDevelopment && anotherIsRunning()) warnOnce();\n};\n\n/**\n * The three questions a copy asks of the page, as one table: they share this\n * copy's identity and its one warning, so they are one subject.\n */\nexport const lankaCopies = Object.freeze({ join, noteActivation, anotherIsRunning });\n","import type { LankaEventBusInstance } from \"../../scenario/event-bus/lanka-event-bus-instance/LankaEventBusInstance\";\nimport type { LankaScenariosRegistry } from \"../../scenario/_registries/lanka-scenarios-registry/LankaScenariosRegistry\";\nimport type { LankaScenarioVMRegistry } from \"../../scenario/_registries/lanka-scenario-vm-registry/LankaScenarioVMRegistry\";\nimport type { ILankaRuntimeConfig } from \"../../config/_interfaces/ILankaRuntimeConfig\";\nimport type { ILankaInFlightCounter } from \"../../gateway/inflight/lankaHttpInFlight\";\nimport type { ILankaScenarioVM } from \"../../scenario/_interfaces/ILankaScenarioVM\";\nimport type { TLankaRequestMiddleware } from \"../../gateway/request/lankaRequestMiddleware\";\nimport type { LankaGatewayLocator } from \"../../locator/gateway/lanka-gateway-locator/LankaGatewayLocator\";\nimport type { LankaScenarioLocator } from \"../../locator/scenario/lanka-scenario-locator/LankaScenarioLocator\";\nimport type { LankaSingletonLocator } from \"../../locator/singleton/lanka-singleton-locator/LankaSingletonLocator\";\nimport type { LankaSharedStoreLocator } from \"../../locator/shared-store/lanka-shared-store-locator/LankaSharedStoreLocator\";\nimport { lankaCopies } from \"./lankaCopies\";\n\n/**\n * The pointer to the framework instance that ambient facades resolve to.\n *\n * ## Why this module is separate and imports no value\n *\n * Framework state belongs to an instance (`createLanka`), but the callers of\n * `lankaEventBus.dispatch`, `getLankaFlags()`, `lankaSingletons.foo`, `lankaHttpInFlight` —\n * the ambient facades cannot hold one — a user-extended `ALankaScenario`, the\n * static `LankaScenarioBootstrap` — so they ask which instance is active.\n *\n * The module is dependency-free DELIBERATELY: every import here is `import type`\n * and disappears at compile time, except `lankaCopies`, which imports nothing\n * itself. Otherwise there would be a cycle\n * `createLanka → locator → gateway → config → createLanka`, and module\n * evaluation order would start deciding what ends up `undefined`.\n *\n * This is still a global pointer. Real isolation belongs to whoever holds an\n * instance: their own bus, registries and counter. The facades serve ONE active\n * instance — exactly what an application with one needs.\n */\nexport interface ILankaRuntime {\n\treadonly eventBus: LankaEventBusInstance;\n\treadonly scenarios: LankaScenariosRegistry;\n\treadonly viewModels: LankaScenarioVMRegistry;\n\treadonly inFlight: ILankaInFlightCounter;\n\t/**\n\t * Request wrappers, in registration order: the first wraps them all.\n\t *\n\t * An array, not a set: order carries meaning — retry must sit outside request\n\t * signing, or the second attempt goes out unsigned.\n\t */\n\treadonly requestMiddleware: readonly TLankaRequestMiddleware[];\n\t/** Default response deadline. `undefined` means no limit. */\n\treadonly requestTimeoutMs: number | undefined;\n\treadonly locators: {\n\t\treadonly gateways: LankaGatewayLocator;\n\t\treadonly scenarios: LankaScenarioLocator;\n\t\treadonly singletons: LankaSingletonLocator;\n\t\treadonly sharedStores: LankaSharedStoreLocator;\n\t};\n\treadonly config: ILankaRuntimeConfig;\n\t/**\n\t * Bootstrap state of the scenario layer.\n\t *\n\t * A mutable object rather than contract fields: `LankaScenarioBootstrap` is an\n\t * ambient facade and must write here without holding an instance.\n\t */\n\treadonly scenarioState: {\n\t\tbootstrapped: boolean;\n\t\tinitialized: WeakSet<ILankaScenarioVM>;\n\t};\n\tsetConfig(patch: ILankaRuntimeConfig): void;\n\tgetFlags(): NonNullable<ILankaRuntimeConfig[\"flags\"]>;\n}\n\n/**\n * How \"which instance is active\" is answered.\n *\n * A strategy, because the answer depends on where the code runs. In a browser\n * there is one instance per document and a module-level pointer IS the answer. In\n * a server process there is one instance per REQUEST, and a module-level pointer\n * is the bug: the last request to start would answer for all of them.\n */\nexport type TLankaRuntimeResolver = () => ILankaRuntime | null;\n\n/**\n * How \"which SCOPE is this\" is answered, which is a different question.\n *\n * `TLankaRuntimeResolver` answers which framework INSTANCE serves a call, and on\n * a server that is one per request. This answers which unit of work the call\n * belongs to, and the two are not interchangeable: `runInLankaServerScope`\n * creates its instance INSIDE the scope, and `createLanka` activates every\n * instance it builds — so the process pointer and the scope's runtime are the\n * same object during a request, and nothing downstream can tell \"inside a\n * scope\" from \"after one ended\".\n *\n * That distinction is what a per-scope lifetime needs. Without it a call made\n * outside every request resolves against the LAST request's runtime and is\n * handed the last stranger's state, which is the failure a scope exists to\n * abolish.\n *\n * Core ships no resolver here either, and knows only that the question exists.\n */\nexport type TLankaScopeResolver = () => object | null;\n\nlet active: ILankaRuntime | null = null;\nlet resolveRuntime: TLankaRuntimeResolver | null = null;\nlet resolveScope: TLankaScopeResolver | null = null;\n\nexport function setActiveLankaRuntime(runtime: ILankaRuntime | null): void {\n\tactive = runtime;\n\n\tif (runtime) lankaCopies.noteActivation(runtime.getFlags().isDevelopment === true);\n}\n\n// What another copy of the package asks this one, registered as this copy loads.\nlankaCopies.join(() => (active === null ? null : active.getFlags().isDevelopment === true));\n\n/**\n * Replaces the strategy, or restores the default one with `null`.\n *\n * The installed resolver is the WHOLE answer — the module-level pointer is not\n * consulted behind it. That is deliberate: a fallback would turn \"this ran\n * outside a request\" from a loud error into one request quietly reading another\n * request's instance, which is the failure this seam exists to make impossible.\n *\n * Core ships no resolver and knows of none. What installs one is a package that\n * knows the shape of a request; core knows only that the question has more than\n * one answer.\n */\nexport function setLankaRuntimeResolver(resolver: TLankaRuntimeResolver | null): void {\n\tresolveRuntime = resolver;\n}\n\n/**\n * The PROCESS's own instance, read without asking the strategy.\n *\n * `getActiveRuntime` consults the installed resolver and is the answer for\n * almost everything. This is the one question it cannot answer: what a call\n * would have resolved to if nobody had installed a resolver at all.\n *\n * It exists for a resolver that wants to DEFER. A server's resolver answers from\n * its request scope, and a process that also holds an ambient instance — a\n * worker with a cache, a dev server between reloads, a suite between cases —\n * has a right answer outside every scope that the resolver cannot see. Without\n * this, the first request in such a process makes every later ambient call fail\n * for the life of it.\n *\n * Not a fallback inside `getActiveRuntime`, deliberately: a resolver that wants\n * to defer says so, and one that wants a call outside its scope to fail loudly\n * goes on failing loudly. The choice belongs to whoever knows what a scope is.\n */\nexport function getLankaProcessRuntime(): ILankaRuntime | null {\n\treturn active;\n}\n\nexport function getActiveRuntime(): ILankaRuntime | null {\n\treturn resolveRuntime ? resolveRuntime() : active;\n}\n\n/**\n * Installs the strategy that says which unit of work a call belongs to.\n *\n * Installed by whoever knows what a scope IS — `@lankajs/host` wraps a request\n * in `AsyncLocalStorage` and answers from its store. A browser installs none,\n * and that is the right answer there: a tab is one scope for its whole life.\n */\nexport function setLankaScopeResolver(resolver: TLankaScopeResolver | null): void {\n\tresolveScope = resolver;\n}\n\n/** Whether anything at all knows how to answer \"which scope\". */\nexport function hasLankaScopeResolver(): boolean {\n\treturn resolveScope !== null;\n}\n\n/**\n * The current unit of work, or `null`.\n *\n * `null` means two different things and the caller has to tell them apart with\n * `hasLankaScopeResolver`: with no resolver installed there are no scopes and a\n * process-wide lifetime is correct; with one installed it means this call ran\n * OUTSIDE every scope, which on a server is a mistake worth failing on.\n */\nexport function getActiveLankaScope(): object | null {\n\treturn resolveScope ? resolveScope() : null;\n}\n\n/**\n * The active instance, or a loud failure.\n *\n * No fallback, deliberately, for the same reason `getLankaHost` has none: an\n * instance created silently on the fly would mean subscriptions went to one bus\n * and events to another, and the divergence would surface three layers away.\n */\nexport function requireActiveRuntime(): ILankaRuntime {\n\t// The strategy is read here rather than through `getActiveRuntime()`: this is\n\t// the hottest path in the framework — every ambient facade, every request,\n\t// every dispatch — and a call that only forwards is a call that shows up in\n\t// `perf/`.\n\tconst runtime = resolveRuntime ? resolveRuntime() : active;\n\n\tif (!runtime) {\n\t\tthrow new Error(\n\t\t\tresolveRuntime\n\t\t\t\t? \"lanka has no instance for this call. A runtime resolver is installed and \" +\n\t\t\t\t\t\t\"answered with none, which on a server means the code ran outside a \" +\n\t\t\t\t\t\t\"request scope — start one, or do this work inside it.\"\n\t\t\t\t: lankaCopies.anotherIsRunning()\n\t\t\t\t\t? \"lanka used before an instance existed in THIS copy of the package, \" +\n\t\t\t\t\t\t\"while another copy of lanka on this page has one. The module calling \" +\n\t\t\t\t\t\t\"it bundled its own lanka; ship one — a singleton in Module \" +\n\t\t\t\t\t\t\"Federation's `shared`, an import map, or `lanka` external in its build.\"\n\t\t\t\t\t: \"lanka used before an instance existed. Call createLanka({ host }) and \" +\n\t\t\t\t\t\t\"activate it.\",\n\t\t);\n\t}\n\n\treturn runtime;\n}\n"],"mappings":";AA0BA,IAAM,SAAS,uBAAO,IAAI,cAAc;AAExC,IAAM,SAAS,MAA4B;AAC1C,QAAM,SAAS;AACf,SAAQ,OAAO,MAAM,MAAM,oBAAI,IAAqB;AACrD;AAEA,IAAI,WAAmC;AACvC,IAAI,SAAS;AAEb,IAAM,cAAc,MAAyB,CAAC,GAAG,OAAO,CAAC,EAAE,OAAO,CAAC,SAAS,SAAS,QAAQ;AAG7F,IAAM,mBAAmB,MAAe,YAAY,EAAE,KAAK,CAAC,SAAS,KAAK,MAAM,IAAI;AAOpF,IAAM,WAAW,MAAY;AAC5B,MAAI,OAAQ;AAEZ,WAAS;AACT,UAAQ;AAAA,IACP;AAAA,EAMD;AACD;AAUA,IAAM,OAAO,CAAC,UAAiC;AAC9C,aAAW;AACX,SAAO,EAAE,IAAI,KAAK;AAElB,MAAI,YAAY,EAAE,KAAK,CAAC,SAAS,KAAK,MAAM,IAAI,EAAG,UAAS;AAC7D;AAMA,IAAM,iBAAiB,CAAC,kBAAiC;AAGxD,MAAI,SAAU,QAAO,EAAE,IAAI,QAAQ;AAEnC,MAAI,iBAAiB,iBAAiB,EAAG,UAAS;AACnD;AAMO,IAAM,cAAc,OAAO,OAAO,EAAE,MAAM,gBAAgB,iBAAiB,CAAC;;;ACOnF,IAAI,SAA+B;AACnC,IAAI,iBAA+C;AACnD,IAAI,eAA2C;AAExC,SAAS,sBAAsB,SAAqC;AAC1E,WAAS;AAET,MAAI,QAAS,aAAY,eAAe,QAAQ,SAAS,EAAE,kBAAkB,IAAI;AAClF;AAGA,YAAY,KAAK,MAAO,WAAW,OAAO,OAAO,OAAO,SAAS,EAAE,kBAAkB,IAAK;AAcnF,SAAS,wBAAwB,UAA8C;AACrF,mBAAiB;AAClB;AAoBO,SAAS,yBAA+C;AAC9D,SAAO;AACR;AAEO,SAAS,mBAAyC;AACxD,SAAO,iBAAiB,eAAe,IAAI;AAC5C;AASO,SAAS,sBAAsB,UAA4C;AACjF,iBAAe;AAChB;AAGO,SAAS,wBAAiC;AAChD,SAAO,iBAAiB;AACzB;AAUO,SAAS,sBAAqC;AACpD,SAAO,eAAe,aAAa,IAAI;AACxC;AASO,SAAS,uBAAsC;AAKrD,QAAM,UAAU,iBAAiB,eAAe,IAAI;AAEpD,MAAI,CAAC,SAAS;AACb,UAAM,IAAI;AAAA,MACT,iBACG,2MAGA,YAAY,iBAAiB,IAC5B,oRAIA;AAAA,IAEL;AAAA,EACD;AAEA,SAAO;AACR;","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
requireActiveRuntime
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-MLKTN4OV.js";
|
|
4
4
|
|
|
5
5
|
// src/gateway/inflight/lankaHttpInFlight.ts
|
|
6
6
|
function createInFlightCounter() {
|
|
@@ -36,4 +36,4 @@ export {
|
|
|
36
36
|
createInFlightCounter,
|
|
37
37
|
lankaHttpInFlight
|
|
38
38
|
};
|
|
39
|
-
//# sourceMappingURL=chunk-
|
|
39
|
+
//# sourceMappingURL=chunk-NPJSZLTB.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
requireActiveRuntime
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-MLKTN4OV.js";
|
|
4
4
|
|
|
5
5
|
// src/locator/_factories/create-lanka-locator-proxy/createLankaLocatorProxy.ts
|
|
6
6
|
function createLankaLocatorProxy(config) {
|
|
@@ -56,4 +56,4 @@ export {
|
|
|
56
56
|
lankaSingletons,
|
|
57
57
|
lankaSharedStores
|
|
58
58
|
};
|
|
59
|
-
//# sourceMappingURL=chunk-
|
|
59
|
+
//# sourceMappingURL=chunk-R365QEN2.js.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
lankaLogger
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-6TNWBT6T.js";
|
|
4
4
|
import {
|
|
5
5
|
requireActiveRuntime
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-MLKTN4OV.js";
|
|
7
7
|
|
|
8
8
|
// src/scenario/event-bus/_facades/lanka-event-bus/lankaEventBus.ts
|
|
9
9
|
var bus = () => requireActiveRuntime().eventBus;
|
|
@@ -312,4 +312,4 @@ export {
|
|
|
312
312
|
LankaScenarioVMRegistry,
|
|
313
313
|
LankaScenariosRegistry
|
|
314
314
|
};
|
|
315
|
-
//# sourceMappingURL=chunk-
|
|
315
|
+
//# sourceMappingURL=chunk-RZGBLXXL.js.map
|