lanka 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/dist/{ALankaGateway-ExlRGT3D.d.ts → ALankaGateway-CkW1LbKE.d.ts} +50 -4
- package/dist/{ILankaScenarioMetadata-Bu-yggTZ.d.ts → ILankaScenarioMetadata-GoWWNEQL.d.ts} +1 -1
- package/dist/{ILankaScenarioVM-DuCyPoyT.d.ts → ILankaScenarioVM-DUsI-fSc.d.ts} +116 -3
- package/dist/{LankaScenarioLocator-CLkq4MaJ.d.ts → LankaScenarioLocator-D86TIwiu.d.ts} +10 -5
- package/dist/_extend/index.d.ts +4 -4
- package/dist/_extend/index.js +2 -2
- package/dist/_internal/index.d.ts +5 -5
- package/dist/{activeRuntime-DT4gB16d.d.ts → activeRuntime-B336NU5I.d.ts} +2 -2
- package/dist/bootstrap/index.d.ts +13 -144
- package/dist/bootstrap/index.js +4 -4
- package/dist/{chunk-MYZQYOMD.js → chunk-5MAQVBI2.js} +20 -15
- package/dist/chunk-5MAQVBI2.js.map +1 -0
- package/dist/chunk-D5WKKEIR.js +54 -0
- package/dist/chunk-D5WKKEIR.js.map +1 -0
- package/dist/{chunk-63ST2UKP.js → chunk-LMKLLEHA.js} +103 -17
- package/dist/chunk-LMKLLEHA.js.map +1 -0
- package/dist/{chunk-YXI4OQEV.js → chunk-O5ROO7QF.js} +27 -4
- package/dist/chunk-O5ROO7QF.js.map +1 -0
- package/dist/{chunk-HZAIAGWS.js → chunk-UGXSGQPW.js} +6 -2
- package/dist/chunk-UGXSGQPW.js.map +1 -0
- package/dist/createLanka-DI1CSy2Q.d.ts +139 -0
- package/dist/gateway/index.d.ts +77 -67
- package/dist/gateway/index.js +50 -54
- package/dist/gateway/index.js.map +1 -1
- package/dist/index.d.ts +7 -6
- package/dist/index.js +4 -4
- package/dist/locator/index.d.ts +1 -1
- package/dist/scenario/index.d.ts +6 -4
- package/dist/scenario/index.js +2 -2
- package/dist/stream/index.d.ts +386 -0
- package/dist/stream/index.js +287 -0
- package/dist/stream/index.js.map +1 -0
- package/dist/validation/index.js +4 -48
- package/dist/validation/index.js.map +1 -1
- package/dist/viewmodel/index.d.ts +1 -1
- package/dist/viewmodel/index.js +2 -2
- package/package.json +7 -3
- package/skills/lanka-core/SKILL.md +1 -1
- package/skills/lanka-core/reference.md +110 -17
- package/skills/lanka-packages/SKILL.md +1 -1
- package/dist/chunk-63ST2UKP.js.map +0 -1
- package/dist/chunk-HZAIAGWS.js.map +0 -1
- package/dist/chunk-MYZQYOMD.js.map +0 -1
- package/dist/chunk-YXI4OQEV.js.map +0 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**◆ core** · Core
|
|
4
4
|
|
|
5
|
-
>
|
|
5
|
+
> Twelve subsystems, two peer dependencies, five extension points.
|
|
6
6
|
|
|
7
7
|
One, unscoped. Everything depends on it; it depends on nothing.
|
|
8
8
|
|
|
@@ -16,7 +16,8 @@ One, unscoped. Everything depends on it; it depends on nothing.
|
|
|
16
16
|
| --- | --- | --- |
|
|
17
17
|
| `useRequestMiddleware(mw)` | wrap every request | yes |
|
|
18
18
|
| `inFlight.subscribe(fn)` | observe the number of requests on the wire | yes |
|
|
19
|
-
| `lankaEventBus.addMiddleware` | intercept bus events | yes |
|
|
19
|
+
| `lankaEventBus.addMiddleware` | intercept bus events, and stop one | yes |
|
|
20
|
+
| `lankaEventBus.addObserver` | hear what became of each dispatch | yes |
|
|
20
21
|
| `LankaLogger sinks` | where log output goes | yes |
|
|
21
22
|
| `use(plugin)` | register a whole plugin | yes |
|
|
22
23
|
|
|
@@ -46,6 +47,7 @@ the seal on `internal`, and catches a subsystem added as a folder and forgotten
|
|
|
46
47
|
- `src/mock/`
|
|
47
48
|
- `src/errors/`
|
|
48
49
|
- `src/scenario/`
|
|
50
|
+
- `src/stream/`
|
|
49
51
|
- `src/viewmodel/`
|
|
50
52
|
- `src/logger/`
|
|
51
53
|
- `src/internal/` — **not exported.** Refactored without a major.
|
|
@@ -226,7 +228,7 @@ to rename.
|
|
|
226
228
|
|
|
227
229
|
`ViewModel` shortens to `VM` in class, factory and type names: `ALankaVM` and
|
|
228
230
|
`createLankaVM` are written in every screen file — the same ViewModel reached from
|
|
229
|
-
either style, one of the
|
|
231
|
+
either style, one of the roles `skills/parity/SKILL.md` governs.
|
|
230
232
|
|
|
231
233
|
Three names stay bare — `isRecord`, `getStringField`, `generateUuid`. They are pure
|
|
232
234
|
utilities over ordinary values; a collision there is resolved at the import site, which
|
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
import { I as ILankaValidator } from './lankaStandardValidator-CL-r-zEV.js';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* `RequestInit`, with the body a JSON API actually sends.
|
|
5
|
+
*
|
|
6
|
+
* `RequestInit["body"]` is `BodyInit | null` — a string, a `FormData`, a blob,
|
|
7
|
+
* bytes. It is what `fetch` accepts and it is NOT what a gateway method writes:
|
|
8
|
+
* every one of them writes `body: { name, email }`, an object, and lets the
|
|
9
|
+
* transport encode it.
|
|
10
|
+
*
|
|
11
|
+
* The framework's transport has always encoded that object. The TYPE never
|
|
12
|
+
* admitted it, and the cost was not a cast at the call site — it was a fork. A
|
|
13
|
+
* consumer could not write `body: { … }` against `RequestInit`, so they declared
|
|
14
|
+
* their own options type; declaring it made every shipped transport
|
|
15
|
+
* unassignable, because those were fixed to `RequestInit`; so they wrote a
|
|
16
|
+
* transport too, and with it a base URL, a CSRF header and an auth refresh the
|
|
17
|
+
* framework already had. One wrong type produced a re-implementation of three
|
|
18
|
+
* packages.
|
|
19
|
+
*
|
|
20
|
+
* `unknown` rather than a union of the encodable shapes: the transport decides
|
|
21
|
+
* by INSPECTING the value, and a type listing what it accepts would have to be
|
|
22
|
+
* kept in step with that inspection by hand. What is not a `BodyInit` is JSON —
|
|
23
|
+
* that is the whole rule, and it needs no enumeration.
|
|
24
|
+
*/
|
|
25
|
+
type TLankaRequestInit = Omit<RequestInit, "body"> & {
|
|
26
|
+
/**
|
|
27
|
+
* Anything. A `BodyInit` travels as given; everything else is encoded as JSON.
|
|
28
|
+
*/
|
|
29
|
+
body?: unknown;
|
|
30
|
+
};
|
|
31
|
+
|
|
3
32
|
/**
|
|
4
33
|
* Transport options plus what core controls.
|
|
5
34
|
*
|
|
@@ -26,7 +55,7 @@ type TLankaExecuteOptions<TOptions> = TOptions & {
|
|
|
26
55
|
* is how development without a backend works, and a request that ignored it
|
|
27
56
|
* would silently disable mock mode for the gateway holding it.
|
|
28
57
|
*/
|
|
29
|
-
interface ILankaRequest<TOptions =
|
|
58
|
+
interface ILankaRequest<TOptions = TLankaRequestInit> {
|
|
30
59
|
/**
|
|
31
60
|
* Sends the request and returns the value the caller asked for.
|
|
32
61
|
*
|
|
@@ -68,7 +97,15 @@ interface IALankaGatewayConfig<TOptions> {
|
|
|
68
97
|
* reads it.
|
|
69
98
|
*/
|
|
70
99
|
queryParamsHandler?: TLankaQueryBuilder;
|
|
71
|
-
/**
|
|
100
|
+
/**
|
|
101
|
+
* The validator a method checks a response body with. Defaults to
|
|
102
|
+
* `lankaStandardValidator`.
|
|
103
|
+
*
|
|
104
|
+
* Reachable as `this.validationService` in a class and as
|
|
105
|
+
* `validationService` in the functional context. A test hands one that
|
|
106
|
+
* records what it was asked; a migration hands one that accepts a shape the
|
|
107
|
+
* new schema does not yet.
|
|
108
|
+
*/
|
|
72
109
|
validationService?: ILankaValidator;
|
|
73
110
|
/**
|
|
74
111
|
* Enable mock mode. If undefined, reads from infrastructure flags (isMockMode).
|
|
@@ -77,9 +114,18 @@ interface IALankaGatewayConfig<TOptions> {
|
|
|
77
114
|
useMock?: boolean;
|
|
78
115
|
}
|
|
79
116
|
|
|
80
|
-
declare abstract class ALankaGateway<TOptions =
|
|
117
|
+
declare abstract class ALankaGateway<TOptions = TLankaRequestInit> {
|
|
81
118
|
protected requestExecutor: ILankaRequest<TOptions>;
|
|
82
119
|
protected queryParamsHandler: TLankaQueryBuilder;
|
|
120
|
+
/**
|
|
121
|
+
* The validator a method checks a response body with.
|
|
122
|
+
*
|
|
123
|
+
* `config.validationService` when one was given, the Standard Schema port
|
|
124
|
+
* otherwise. It used to be accepted by the config and read by nothing: a
|
|
125
|
+
* consumer handing a test double to the gateway got the real validator and no
|
|
126
|
+
* error, which is the worst kind of ignored option — it looks honoured.
|
|
127
|
+
*/
|
|
128
|
+
protected readonly validationService: ILankaValidator;
|
|
83
129
|
protected readonly useMock: boolean;
|
|
84
130
|
protected readonly basePath: string;
|
|
85
131
|
protected constructor(config: IALankaGatewayConfig<TOptions>);
|
|
@@ -119,4 +165,4 @@ declare abstract class ALankaGateway<TOptions = RequestInit> {
|
|
|
119
165
|
protected setQueryParamsHandler(handler: TLankaQueryBuilder): void;
|
|
120
166
|
}
|
|
121
167
|
|
|
122
|
-
export { ALankaGateway as A, type IALankaGatewayConfig as I, type TLankaExecuteOptions as T, type
|
|
168
|
+
export { ALankaGateway as A, type IALankaGatewayConfig as I, type TLankaExecuteOptions as T, type TLankaRequestInit as a, type ILankaRequest as b, type TLankaQueryBuilder as c, type TLankaQueryParams as d };
|
|
@@ -35,6 +35,17 @@ interface ILankaEventLog {
|
|
|
35
35
|
eventType: string;
|
|
36
36
|
/** ISO timestamp of when the event was dispatched. */
|
|
37
37
|
timestamp: string;
|
|
38
|
+
/**
|
|
39
|
+
* Where this record falls in the order the bus saw events, counting from one.
|
|
40
|
+
*
|
|
41
|
+
* What `getEventLogs()` orders by, and the reason it can: a timestamp has
|
|
42
|
+
* millisecond resolution, and a burst dispatches many events inside one, so
|
|
43
|
+
* two records of one millisecond cannot be told apart by their time.
|
|
44
|
+
*
|
|
45
|
+
* Optional because a record built by anything other than the bus has no
|
|
46
|
+
* place in that order.
|
|
47
|
+
*/
|
|
48
|
+
sequence?: number;
|
|
38
49
|
/** The payload data of the event. */
|
|
39
50
|
data: unknown;
|
|
40
51
|
/** Optional captured stack trace (if enabled). */
|
|
@@ -77,6 +88,60 @@ type TLankaEventBusDecision = "pass" | {
|
|
|
77
88
|
*/
|
|
78
89
|
type TLankaEventBusMiddleware<T> = (eventType: string, data: T) => TLankaEventBusDecision;
|
|
79
90
|
|
|
91
|
+
/**
|
|
92
|
+
* What became of one dispatch, after the whole chain has run.
|
|
93
|
+
*
|
|
94
|
+
* The counterpart of `TLankaEventBusDecision`: a middleware RETURNS a decision,
|
|
95
|
+
* and the bus REPORTS an outcome. The two are deliberately symmetrical, and the
|
|
96
|
+
* asymmetry that matters is that an outcome cannot change anything.
|
|
97
|
+
*/
|
|
98
|
+
interface ILankaEventBusOutcome {
|
|
99
|
+
eventType: string;
|
|
100
|
+
/**
|
|
101
|
+
* Delivered to the subscribers, stopped by a middleware, or refused by the
|
|
102
|
+
* event's own schema.
|
|
103
|
+
*
|
|
104
|
+
* A union rather than an `enum`: a fourth outcome is then additive, which
|
|
105
|
+
* `skills/surface/SKILL.md` §6d.5 is about.
|
|
106
|
+
*/
|
|
107
|
+
outcome: "delivered" | "stopped" | "invalid";
|
|
108
|
+
/**
|
|
109
|
+
* How many subscribers the event had when it was dispatched.
|
|
110
|
+
*
|
|
111
|
+
* The AUDIENCE, not the recipients: "three subscribers never heard it" is the
|
|
112
|
+
* sentence a stopped event needs, and a count that went to zero on a stop
|
|
113
|
+
* could not say it.
|
|
114
|
+
*/
|
|
115
|
+
subscribers: number;
|
|
116
|
+
/** The reason a middleware gave, when one stopped it. */
|
|
117
|
+
stoppedBy?: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Something watching what became of each dispatch.
|
|
122
|
+
*
|
|
123
|
+
* ## Why this is not a middleware
|
|
124
|
+
*
|
|
125
|
+
* A middleware runs in a CHAIN and sees only what happened before it. The first
|
|
126
|
+
* one registered — which is what a diagnostic tool ends up being, since it is
|
|
127
|
+
* installed at bootstrap — cannot see a later middleware's decision to stop an
|
|
128
|
+
* event, so "which middleware stopped it" was a question nothing could answer.
|
|
129
|
+
* `@lankajs/plugin-devtools` carried a `stoppedBy` field that no code path ever
|
|
130
|
+
* filled, and its guide documented it.
|
|
131
|
+
*
|
|
132
|
+
* An observer runs ONCE per dispatch, after the chain, and its return value is
|
|
133
|
+
* ignored — it cannot decide anything. That is what keeps a diagnostic tool
|
|
134
|
+
* incapable of changing what it diagnoses, by construction rather than by
|
|
135
|
+
* discipline.
|
|
136
|
+
*
|
|
137
|
+
* ## Why the parameter is one object
|
|
138
|
+
*
|
|
139
|
+
* `skills/surface/SKILL.md` §6d.9: a callback's parameters are one object, so
|
|
140
|
+
* the day the bus learns to report how long delivery took, nobody's handler
|
|
141
|
+
* silently receives a different third argument.
|
|
142
|
+
*/
|
|
143
|
+
type TLankaEventBusObserver = (outcome: ILankaEventBusOutcome) => void;
|
|
144
|
+
|
|
80
145
|
/**
|
|
81
146
|
* How many recent values to replay to a new subscriber.
|
|
82
147
|
*
|
|
@@ -88,12 +153,26 @@ declare class LankaEventBusInstance {
|
|
|
88
153
|
private registry;
|
|
89
154
|
/** Middleware registered on this instance. */
|
|
90
155
|
private middlewares;
|
|
156
|
+
/**
|
|
157
|
+
* What is watching the OUTCOME of each dispatch — see `TLankaEventBusObserver`.
|
|
158
|
+
*
|
|
159
|
+
* An array rather than a set for one reason: `dispatch` reads `.length` on
|
|
160
|
+
* every event, and that check is what keeps an unobserved bus paying nothing.
|
|
161
|
+
*/
|
|
162
|
+
private observers;
|
|
91
163
|
/** Default maximum number of logs kept per event type. */
|
|
92
164
|
private defaultMaxLogs;
|
|
93
165
|
/** Internal timers for replay batching (avoid multiple replays in the same tick). */
|
|
94
166
|
private pendingReplayTimers;
|
|
95
167
|
/** Whether event logging is enabled. */
|
|
96
168
|
private isEnableLogs;
|
|
169
|
+
/**
|
|
170
|
+
* How many events this bus has logged, ever — the order records are read in.
|
|
171
|
+
*
|
|
172
|
+
* Incremented only while logging is on, which is off by default, so a bus
|
|
173
|
+
* nobody is inspecting pays nothing for it.
|
|
174
|
+
*/
|
|
175
|
+
private dispatchCount;
|
|
97
176
|
/** Configures the bus, e.g. logging. Called once at application start. */
|
|
98
177
|
enableLogs(): void;
|
|
99
178
|
disableLogs(): void;
|
|
@@ -162,7 +241,28 @@ declare class LankaEventBusInstance {
|
|
|
162
241
|
/** Removes previously added middleware. */
|
|
163
242
|
removeMiddleware<T>(mw: TLankaEventBusMiddleware<T>): void;
|
|
164
243
|
/**
|
|
165
|
-
*
|
|
244
|
+
* Watches what became of each dispatch. An extension point — the sixth.
|
|
245
|
+
*
|
|
246
|
+
* An observer never decides: its return value is ignored, and a throw from it
|
|
247
|
+
* is logged rather than surfaced. A diagnostic tool that could stop an event
|
|
248
|
+
* would make "I disabled the inspector and it started working" a sentence
|
|
249
|
+
* somebody says.
|
|
250
|
+
*/
|
|
251
|
+
addObserver(observer: TLankaEventBusObserver): void;
|
|
252
|
+
/** Removes a previously added observer. */
|
|
253
|
+
removeObserver(observer: TLankaEventBusObserver): void;
|
|
254
|
+
/**
|
|
255
|
+
* Log of dispatched events, oldest first, `limit` most recent kept.
|
|
256
|
+
*
|
|
257
|
+
* ## Why the records are sorted rather than concatenated
|
|
258
|
+
*
|
|
259
|
+
* They are held per event TYPE, so that each type keeps its own `maxLogs`
|
|
260
|
+
* and a chatty event cannot push a quiet one's history out. Concatenating
|
|
261
|
+
* those lists puts every record of one type before every record of the next,
|
|
262
|
+
* and `slice(-limit)` then answers the tail of whichever type the registry
|
|
263
|
+
* happened to hold last — not the most recent events at all. On a bus with
|
|
264
|
+
* one event type nothing looked wrong; on a real one the log was a
|
|
265
|
+
* chronology that had never been in chronological order.
|
|
166
266
|
*
|
|
167
267
|
* @param eventType Limit to one type
|
|
168
268
|
* @param limit How many recent records to return
|
|
@@ -172,8 +272,21 @@ declare class LankaEventBusInstance {
|
|
|
172
272
|
clearEvent(eventType: string): void;
|
|
173
273
|
/** Removes every event and any scheduled replays. */
|
|
174
274
|
clearAllEvents(): void;
|
|
175
|
-
/** Full bus reset: events and
|
|
275
|
+
/** Full bus reset: events, middleware and observers. */
|
|
176
276
|
reset(): void;
|
|
277
|
+
/**
|
|
278
|
+
* Tells the observers what became of a dispatch.
|
|
279
|
+
*
|
|
280
|
+
* The empty-list check is the whole reason a bus nobody watches pays nothing
|
|
281
|
+
* for this: `dispatch` is a hot path, and an iteration over an empty array on
|
|
282
|
+
* every event would be a cost paid by every application for a feature almost
|
|
283
|
+
* none of them installs. Measured in `LankaEventBusInstance.bench.ts`.
|
|
284
|
+
*
|
|
285
|
+
* An observer that throws is logged and the next one still runs — the same
|
|
286
|
+
* treatment a subscriber gets, and for the same reason: a diagnostic tool must
|
|
287
|
+
* not be able to break the application it is diagnosing.
|
|
288
|
+
*/
|
|
289
|
+
private report;
|
|
177
290
|
/**
|
|
178
291
|
* Marks the last log record with the reason it was stopped.
|
|
179
292
|
*
|
|
@@ -257,4 +370,4 @@ interface ILankaScenarioVM {
|
|
|
257
370
|
resetScenario(): void;
|
|
258
371
|
}
|
|
259
372
|
|
|
260
|
-
export { type ILankaScenarioVM as I, LankaEventBusInstance as L, type TLankaReplayRequest as T, type ILankaScenario as a, type ILankaEventMetadata as b, type TLankaEventBusMiddleware as c, type
|
|
373
|
+
export { type ILankaScenarioVM as I, LankaEventBusInstance as L, type TLankaReplayRequest as T, type ILankaScenario as a, type ILankaEventMetadata as b, type TLankaEventBusMiddleware as c, type TLankaEventBusObserver as d, type ILankaEventLog as e, type ILankaEventBusOutcome as f, type TLankaEventBusDecision as g };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as ILankaScenario, I as ILankaScenarioVM } from './ILankaScenarioVM-
|
|
2
|
-
import { I as ILankaScenarioMetadata } from './ILankaScenarioMetadata-
|
|
3
|
-
import { A as ALankaGateway } from './ALankaGateway-
|
|
1
|
+
import { a as ILankaScenario, I as ILankaScenarioVM } from './ILankaScenarioVM-DUsI-fSc.js';
|
|
2
|
+
import { I as ILankaScenarioMetadata } from './ILankaScenarioMetadata-GoWWNEQL.js';
|
|
3
|
+
import { A as ALankaGateway } from './ALankaGateway-CkW1LbKE.js';
|
|
4
4
|
import { A as ALankaLocator } from './LankaSharedStoreLocator-zS2kLu-S.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -31,7 +31,8 @@ declare class LankaScenariosRegistry {
|
|
|
31
31
|
*/
|
|
32
32
|
register(scenario: ILankaScenario<unknown>): boolean;
|
|
33
33
|
/**
|
|
34
|
-
*
|
|
34
|
+
* Removes a scenario, calling its `cleanup` if it declared one.
|
|
35
|
+
*
|
|
35
36
|
* @param scenarioName Name of the scenario to unregister
|
|
36
37
|
* @returns true if unregistered successfully, false if not found
|
|
37
38
|
*/
|
|
@@ -61,7 +62,11 @@ declare class LankaScenariosRegistry {
|
|
|
61
62
|
/** Every registered ViewModel. */
|
|
62
63
|
getAllViewModels(): ILankaScenarioVM[];
|
|
63
64
|
/**
|
|
64
|
-
*
|
|
65
|
+
* Removes every scenario, calling each one's `cleanup`.
|
|
66
|
+
*
|
|
67
|
+
* What `dispose()` and the between-tests reset go through: `initialize` ran
|
|
68
|
+
* for every scenario at bootstrap, and this is the one moment its mirror
|
|
69
|
+
* image can run for all of them.
|
|
65
70
|
*/
|
|
66
71
|
clear(): void;
|
|
67
72
|
}
|
package/dist/_extend/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
export { L as LankaGatewayLocator, a as LankaScenarioLocator, b as LankaScenarioVMRegistry, c as LankaScenariosRegistry } from '../LankaScenarioLocator-
|
|
1
|
+
export { L as LankaGatewayLocator, a as LankaScenarioLocator, b as LankaScenarioVMRegistry, c as LankaScenariosRegistry } from '../LankaScenarioLocator-D86TIwiu.js';
|
|
2
2
|
import { a as ILankaLocator } from '../LankaSharedStoreLocator-zS2kLu-S.js';
|
|
3
3
|
export { A as ALankaLocator, L as LankaSharedStoreLocator, b as LankaSingletonLocator } from '../LankaSharedStoreLocator-zS2kLu-S.js';
|
|
4
4
|
export { c as createLankaScope } from '../createLankaScope-BiFxNQgl.js';
|
|
5
5
|
export { c as composeLankaRequestMiddleware } from '../lankaRequestMiddleware-DAC5kCb7.js';
|
|
6
|
-
import '../ILankaScenarioVM-
|
|
7
|
-
import '../ILankaScenarioMetadata-
|
|
8
|
-
import '../ALankaGateway-
|
|
6
|
+
import '../ILankaScenarioVM-DUsI-fSc.js';
|
|
7
|
+
import '../ILankaScenarioMetadata-GoWWNEQL.js';
|
|
8
|
+
import '../ALankaGateway-CkW1LbKE.js';
|
|
9
9
|
import '../lankaStandardValidator-CL-r-zEV.js';
|
|
10
10
|
import '@standard-schema/spec';
|
|
11
11
|
import '../ALankaSharedStore-B7uepuuk.js';
|
package/dist/_extend/index.js
CHANGED
|
@@ -2,11 +2,11 @@ import {
|
|
|
2
2
|
LankaGatewayLocator,
|
|
3
3
|
LankaScenarioLocator,
|
|
4
4
|
createLankaScope
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-5MAQVBI2.js";
|
|
6
6
|
import {
|
|
7
7
|
LankaScenarioVMRegistry,
|
|
8
8
|
LankaScenariosRegistry
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-O5ROO7QF.js";
|
|
10
10
|
import {
|
|
11
11
|
composeLankaRequestMiddleware
|
|
12
12
|
} from "../chunk-YR4MZXMU.js";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export { I as ILankaRuntime, T as TLankaRuntimeResolver, s as setLankaRuntimeResolver } from '../activeRuntime-
|
|
2
|
-
import '../ILankaScenarioVM-
|
|
3
|
-
import '../LankaScenarioLocator-
|
|
4
|
-
import '../ILankaScenarioMetadata-
|
|
5
|
-
import '../ALankaGateway-
|
|
1
|
+
export { I as ILankaRuntime, T as TLankaRuntimeResolver, s as setLankaRuntimeResolver } from '../activeRuntime-B336NU5I.js';
|
|
2
|
+
import '../ILankaScenarioVM-DUsI-fSc.js';
|
|
3
|
+
import '../LankaScenarioLocator-D86TIwiu.js';
|
|
4
|
+
import '../ILankaScenarioMetadata-GoWWNEQL.js';
|
|
5
|
+
import '../ALankaGateway-CkW1LbKE.js';
|
|
6
6
|
import '../lankaStandardValidator-CL-r-zEV.js';
|
|
7
7
|
import '@standard-schema/spec';
|
|
8
8
|
import '../LankaSharedStoreLocator-zS2kLu-S.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { L as LankaEventBusInstance, I as ILankaScenarioVM } from './ILankaScenarioVM-
|
|
2
|
-
import { c as LankaScenariosRegistry, b as LankaScenarioVMRegistry, L as LankaGatewayLocator, a as LankaScenarioLocator } from './LankaScenarioLocator-
|
|
1
|
+
import { L as LankaEventBusInstance, I as ILankaScenarioVM } from './ILankaScenarioVM-DUsI-fSc.js';
|
|
2
|
+
import { c as LankaScenariosRegistry, b as LankaScenarioVMRegistry, L as LankaGatewayLocator, a as LankaScenarioLocator } from './LankaScenarioLocator-D86TIwiu.js';
|
|
3
3
|
import { b as ILankaRuntimeConfig } from './ILankaRuntimeConfig-Vl436GWK.js';
|
|
4
4
|
import { I as ILankaInFlightCounter } from './lankaHttpInFlight-Bk1eIuSx.js';
|
|
5
5
|
import { T as TLankaRequestMiddleware } from './lankaRequestMiddleware-DAC5kCb7.js';
|
|
@@ -1,150 +1,19 @@
|
|
|
1
|
-
import { I as
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
|
|
1
|
+
import { c as ILankaPlugin, e as ILankaServiceConfig, I as ILankaBootstrapConfig, a as ILankaInstance } from '../createLanka-DI1CSy2Q.js';
|
|
2
|
+
export { b as ILankaInstanceConfig, d as ILankaScenarioBootstrapConfig, f as createLanka } from '../createLanka-DI1CSy2Q.js';
|
|
3
|
+
import { I as ILankaFlags, a as ILankaHost } from '../ILankaRuntimeConfig-Vl436GWK.js';
|
|
4
|
+
export { I as ILankaScope } from '../createLankaScope-BiFxNQgl.js';
|
|
5
|
+
import '../activeRuntime-B336NU5I.js';
|
|
6
|
+
import '../ILankaScenarioVM-DUsI-fSc.js';
|
|
7
|
+
import '../LankaScenarioLocator-D86TIwiu.js';
|
|
8
|
+
import '../ILankaScenarioMetadata-GoWWNEQL.js';
|
|
9
|
+
import '../ALankaGateway-CkW1LbKE.js';
|
|
10
|
+
import '../lankaStandardValidator-CL-r-zEV.js';
|
|
11
|
+
import '@standard-schema/spec';
|
|
5
12
|
import '../LankaSharedStoreLocator-zS2kLu-S.js';
|
|
6
13
|
import '../ALankaSharedStore-B7uepuuk.js';
|
|
7
14
|
import 'zustand/vanilla';
|
|
8
|
-
import '../ILankaScenarioVM-DuCyPoyT.js';
|
|
9
|
-
import '../LankaScenarioLocator-CLkq4MaJ.js';
|
|
10
|
-
import '../ILankaScenarioMetadata-Bu-yggTZ.js';
|
|
11
|
-
import '../ALankaGateway-ExlRGT3D.js';
|
|
12
|
-
import '../lankaStandardValidator-CL-r-zEV.js';
|
|
13
|
-
import '@standard-schema/spec';
|
|
14
15
|
import '../lankaHttpInFlight-Bk1eIuSx.js';
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* A plugin is an extension core knows by SHAPE rather than by name.
|
|
18
|
-
*
|
|
19
|
-
* ## Plugin versus module
|
|
20
|
-
*
|
|
21
|
-
* A module is called by the application (`app → module`) and core does not know
|
|
22
|
-
* it exists. A plugin sits on the path core itself walks (`app → core → plugin`).
|
|
23
|
-
* The test question: does core need a hook for this to work? No — then it is a
|
|
24
|
-
* module, and making it a plugin costs more, because the hook has to be supported
|
|
25
|
-
* forever.
|
|
26
|
-
*
|
|
27
|
-
* ## Why `install` receives the instance
|
|
28
|
-
*
|
|
29
|
-
* So a plugin has no private route to the framework. Everything it can do comes
|
|
30
|
-
* from the instance it was given, which means two instances in one process (a
|
|
31
|
-
* test beside the app) do not share its configuration.
|
|
32
|
-
*/
|
|
33
|
-
interface ILankaPlugin {
|
|
34
|
-
/**
|
|
35
|
-
* The name the plugin is recognised by. Registering the same name twice is
|
|
36
|
-
* rejected: two copies of a retry policy would silently double the request
|
|
37
|
-
* count.
|
|
38
|
-
*/
|
|
39
|
-
readonly name: string;
|
|
40
|
-
/**
|
|
41
|
-
* Installation. The returned function removes everything the plugin installed
|
|
42
|
-
* and is called when the plugin is removed and when the instance is disposed.
|
|
43
|
-
*/
|
|
44
|
-
install: (lanka: ILankaInstance) => (() => void) | void;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* A framework instance: all framework state in one object.
|
|
49
|
-
*
|
|
50
|
-
* What instance-scoped state buys: two apps in one process (micro-frontends,
|
|
51
|
-
* Storybook beside the app) no longer share a bus, locator caches and mock mode;
|
|
52
|
-
* SSR does not reuse state between different users' requests; and test isolation
|
|
53
|
-
* rests on construction rather than on a global `beforeEach` reaching into
|
|
54
|
-
* internal registries.
|
|
55
|
-
*
|
|
56
|
-
* **Ambient facades** — `lankaEventBus.dispatch`, `lankaSingletons.foo`,
|
|
57
|
-
* `getLankaFlags()`, `lankaHttpInFlight` — resolve THE ONE active instance
|
|
58
|
-
* (`internal/activeRuntime.ts`). Isolation belongs to whoever holds an instance;
|
|
59
|
-
* a facade cannot offer it.
|
|
60
|
-
*
|
|
61
|
-
* One thing stays at module level deliberately: `ALankaScenario` collects
|
|
62
|
-
* constructed scenarios into a static pool. That is a REGISTRY OF DEFINITIONS,
|
|
63
|
-
* not runtime state — the classes come from one `@lanka_di/Scenarios` barrel and
|
|
64
|
-
* both instances must see the same list. Splitting it would be divergence.
|
|
65
|
-
*/
|
|
66
|
-
interface ILankaInstance extends ILankaRuntime {
|
|
67
|
-
/**
|
|
68
|
-
* Adds a wrapper around every request. Returns a function that removes it.
|
|
69
|
-
*
|
|
70
|
-
* This is how `@lankajs/plugin-http` installs retry, the idempotency key, the
|
|
71
|
-
* CSRF header and auth refresh.
|
|
72
|
-
*/
|
|
73
|
-
useRequestMiddleware(middleware: TLankaRequestMiddleware): () => void;
|
|
74
|
-
/** Default timeout for every request of this instance. */
|
|
75
|
-
setRequestTimeout(timeoutMs: number | undefined): void;
|
|
76
|
-
/** Resolves a service in the root scope — for the instance's whole lifetime. */
|
|
77
|
-
resolve<TInstance>(propertyName: string): TInstance;
|
|
78
|
-
/**
|
|
79
|
-
* Creates a scope: a lifetime shorter than the application's.
|
|
80
|
-
*
|
|
81
|
-
* An object created in a scope goes away with it.
|
|
82
|
-
*/
|
|
83
|
-
createScope(): ILankaScope;
|
|
84
|
-
/**
|
|
85
|
-
* Registers a plugin. Returns a function that removes it.
|
|
86
|
-
*
|
|
87
|
-
* The fifth and last extension point. An extension point declared before
|
|
88
|
-
* anything plugs into it describes an imagined need while costing real
|
|
89
|
-
* support, so this one arrived with the FIRST plugin.
|
|
90
|
-
*/
|
|
91
|
-
use(plugin: ILankaPlugin): () => void;
|
|
92
|
-
/** Runs services and the scenario layer. Idempotent. */
|
|
93
|
-
bootstrap(config?: ILankaBootstrapConfig): Promise<void>;
|
|
94
|
-
/** Has bootstrap already run? */
|
|
95
|
-
isBootstrapped(): boolean;
|
|
96
|
-
/** Makes this instance the one ambient facades resolve to. */
|
|
97
|
-
activate(): void;
|
|
98
|
-
/**
|
|
99
|
-
* Removes subscriptions, clears registries and, if this instance was active,
|
|
100
|
-
* clears the pointer. Returns the framework to its pre-bootstrap state.
|
|
101
|
-
*/
|
|
102
|
-
dispose(): void;
|
|
103
|
-
}
|
|
104
|
-
interface ILankaInstanceConfig {
|
|
105
|
-
host: ILankaHost;
|
|
106
|
-
flags?: ILankaFlags;
|
|
107
|
-
}
|
|
108
|
-
interface ILankaServiceConfig {
|
|
109
|
-
name?: string;
|
|
110
|
-
init: () => void | Promise<void>;
|
|
111
|
-
sync?: boolean;
|
|
112
|
-
priority?: number;
|
|
113
|
-
/**
|
|
114
|
-
* A failure of this service does not abort bootstrap.
|
|
115
|
-
*
|
|
116
|
-
* Without the flag one failed service takes the WHOLE phase with it: the async
|
|
117
|
-
* phase because of `Promise.all`, the sync phase because later tasks never run.
|
|
118
|
-
* Wrapping the failure in a swallowing `try/catch` is worse still — the app
|
|
119
|
-
* starts with a partially executed plan and does not know it.
|
|
120
|
-
*/
|
|
121
|
-
optional?: boolean;
|
|
122
|
-
/**
|
|
123
|
-
* How long to wait for the service. Overrunning counts as a failure.
|
|
124
|
-
*
|
|
125
|
-
* Without a deadline a service that never settles holds bootstrap forever and
|
|
126
|
-
* the app never paints its first screen. Failing is more honest than waiting:
|
|
127
|
-
* an optional service is skipped, a required one names itself.
|
|
128
|
-
*/
|
|
129
|
-
timeoutMs?: number;
|
|
130
|
-
}
|
|
131
|
-
interface ILankaScenarioBootstrapConfig {
|
|
132
|
-
sync?: boolean;
|
|
133
|
-
priority?: number;
|
|
134
|
-
}
|
|
135
|
-
interface ILankaBootstrapConfig {
|
|
136
|
-
services?: ILankaServiceConfig[];
|
|
137
|
-
scenarios?: ILankaScenarioBootstrapConfig;
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* Creates an instance and makes it active.
|
|
141
|
-
*
|
|
142
|
-
* Activating on creation is the deliberate default: there is almost always one
|
|
143
|
-
* app and the facades must work immediately. A second instance takes the
|
|
144
|
-
* pointer — the last one created serves the facades. Callers needing another
|
|
145
|
-
* order call `activate()` explicitly.
|
|
146
|
-
*/
|
|
147
|
-
declare function createLanka(config: ILankaInstanceConfig): ILankaInstance;
|
|
16
|
+
import '../lankaRequestMiddleware-DAC5kCb7.js';
|
|
148
17
|
|
|
149
18
|
/** What `startLanka` takes beyond the host, all of it optional. */
|
|
150
19
|
interface ILankaStartOptions {
|
|
@@ -239,4 +108,4 @@ declare abstract class ALankaPlugin implements ILankaPlugin {
|
|
|
239
108
|
install(lanka: ILankaInstance): () => void;
|
|
240
109
|
}
|
|
241
110
|
|
|
242
|
-
export { ALankaPlugin,
|
|
111
|
+
export { ALankaPlugin, ILankaBootstrapConfig, ILankaInstance, ILankaPlugin, ILankaServiceConfig, type ILankaStartOptions, type TLankaStartConfig, resetActiveLanka, startLanka };
|
package/dist/bootstrap/index.js
CHANGED
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
createLanka,
|
|
4
4
|
resetActiveLanka,
|
|
5
5
|
startLanka
|
|
6
|
-
} from "../chunk-
|
|
7
|
-
import "../chunk-
|
|
8
|
-
import "../chunk-
|
|
9
|
-
import "../chunk-
|
|
6
|
+
} from "../chunk-LMKLLEHA.js";
|
|
7
|
+
import "../chunk-UGXSGQPW.js";
|
|
8
|
+
import "../chunk-5MAQVBI2.js";
|
|
9
|
+
import "../chunk-O5ROO7QF.js";
|
|
10
10
|
import "../chunk-XESL274R.js";
|
|
11
11
|
import "../chunk-UJEC7H6K.js";
|
|
12
12
|
import "../chunk-UE2C76OR.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ALankaScenario,
|
|
3
3
|
LankaScenariosRegistry
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-O5ROO7QF.js";
|
|
5
5
|
import {
|
|
6
6
|
ALankaLocator,
|
|
7
7
|
caseConvert,
|
|
@@ -24,6 +24,23 @@ var LankaGatewayLocator = class extends ALankaLocator {
|
|
|
24
24
|
|
|
25
25
|
// src/locator/scenario/lanka-scenario-locator/LankaScenarioLocator.ts
|
|
26
26
|
import * as ScenariosModule from "@lanka_di/Scenarios";
|
|
27
|
+
var findScenarioClassByInstanceName = (scenarioName) => {
|
|
28
|
+
const pooled = ALankaScenario.getAutoRegisteredScenarios();
|
|
29
|
+
for (const value of Object.values(ScenariosModule)) {
|
|
30
|
+
if (typeof value !== "function" || !value.prototype) continue;
|
|
31
|
+
const Class = value;
|
|
32
|
+
const instance = pooled.find((scenario) => scenario.constructor === Class) ?? construct(Class);
|
|
33
|
+
if (instance?.name === scenarioName) return Class;
|
|
34
|
+
}
|
|
35
|
+
return void 0;
|
|
36
|
+
};
|
|
37
|
+
var construct = (Class) => {
|
|
38
|
+
try {
|
|
39
|
+
return new Class();
|
|
40
|
+
} catch {
|
|
41
|
+
return void 0;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
27
44
|
var LankaScenarioLocator = class extends ALankaLocator {
|
|
28
45
|
/**
|
|
29
46
|
* The registry is read LAZILY rather than in a field initialiser.
|
|
@@ -46,19 +63,7 @@ var LankaScenarioLocator = class extends ALankaLocator {
|
|
|
46
63
|
return ScenarioClass;
|
|
47
64
|
}
|
|
48
65
|
}
|
|
49
|
-
|
|
50
|
-
if (typeof value === "function" && value.prototype) {
|
|
51
|
-
try {
|
|
52
|
-
const instance = new value();
|
|
53
|
-
if (instance.name === scenarioName) {
|
|
54
|
-
return value;
|
|
55
|
-
}
|
|
56
|
-
} catch {
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return void 0;
|
|
66
|
+
return findScenarioClassByInstanceName(scenarioName);
|
|
62
67
|
},
|
|
63
68
|
getInstanceByName: (scenarioName) => {
|
|
64
69
|
const registered = this.registry.getScenarioByName(scenarioName);
|
|
@@ -131,4 +136,4 @@ export {
|
|
|
131
136
|
LankaScenarioLocator,
|
|
132
137
|
createLankaScope
|
|
133
138
|
};
|
|
134
|
-
//# sourceMappingURL=chunk-
|
|
139
|
+
//# sourceMappingURL=chunk-5MAQVBI2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/locator/gateway/lanka-gateway-locator/LankaGatewayLocator.ts","../src/locator/scenario/lanka-scenario-locator/LankaScenarioLocator.ts","../src/locator/_factories/create-lanka-scope/createLankaScope.ts"],"sourcesContent":["import * as GatewaysModule from \"@lanka_di/Gateways\";\nimport type { ALankaGateway } from \"../../../gateway/_abstractions/lanka-gateway/ALankaGateway\";\nimport { ALankaLocator } from \"../../_abstractions/lanka-locator/ALankaLocator\";\nimport { findExportedClass } from \"../../_internal/find-exported-class/findExportedClass\";\n\n/**\n * Resolves a gateway by property name (camelCase) or class name (PascalCase),\n * constructing it lazily and caching the instance.\n *\n * Adding a gateway takes one export line in `@lanka_di/Gateways`: types are\n * inferred and nothing is registered.\n */\nexport class LankaGatewayLocator extends ALankaLocator<ALankaGateway<unknown>> {\n\tconstructor() {\n\t\tsuper({\n\t\t\tfindClassByName: (gatewayName: string) =>\n\t\t\t\tfindExportedClass<ALankaGateway<unknown>>(GatewaysModule, gatewayName),\n\t\t\tnotFoundError: (gatewayName, propertyName) =>\n\t\t\t\t`Gateway \"${gatewayName}\" (accessed as \"${propertyName}\") not found. ` +\n\t\t\t\t`Make sure the gateway class extends ALankaGateway and is exported from lankaGateways.ts.`,\n\t\t});\n\t}\n}\n","import { LankaScenariosRegistry } from \"../../../scenario/_registries/lanka-scenarios-registry/LankaScenariosRegistry\";\nimport { ILankaScenario } from \"../../../scenario/_interfaces/ILankaScenario\";\nimport { ALankaScenario } from \"../../../scenario/_abstractions/lanka-scenario/ALankaScenario\";\nimport { ALankaLocator } from \"../../_abstractions/lanka-locator/ALankaLocator\";\n/**\n * Classes come from the consumer's barrel.\n *\n * Adding one is ONE export line: types are inferred, autocomplete works, and no\n * list has to be maintained.\n */\nimport * as ScenariosModule from \"@lanka_di/Scenarios\";\n\n/**\n * The barrel's class whose INSTANCE carries this name, when the export key does\n * not.\n *\n * A pooled instance answers for its class without constructing another: the\n * pool is never drained, so a lookup that constructed every class on every miss\n * grew it by the whole barrel each time a bad name was asked for. Only a class\n * the pool has never seen is constructed here — once, and it joins the pool.\n */\nconst findScenarioClassByInstanceName = (\n\tscenarioName: string,\n): (new () => ILankaScenario<unknown>) | undefined => {\n\tconst pooled = ALankaScenario.getAutoRegisteredScenarios();\n\n\tfor (const value of Object.values(ScenariosModule)) {\n\t\tif (typeof value !== \"function\" || !value.prototype) continue;\n\n\t\tconst Class = value as new () => ALankaScenario<unknown>;\n\t\tconst instance =\n\t\t\tpooled.find((scenario) => scenario.constructor === Class) ?? construct(Class);\n\t\tif (instance?.name === scenarioName) return Class;\n\t}\n\n\treturn undefined;\n};\n\n/** A new instance, or `undefined` for an export that is not constructible. */\nconst construct = (\n\tClass: new () => ALankaScenario<unknown>,\n): ALankaScenario<unknown> | undefined => {\n\ttry {\n\t\treturn new Class();\n\t} catch {\n\t\treturn undefined;\n\t}\n};\n\n/**\n * Resolves scenarios by property name (camelCase) or scenario name\n * (PascalCase).\n *\n * Looks in the registry, then in the self-registration pool, and failing both\n * constructs the scenario from the consumer's barrel on first use.\n */\nexport class LankaScenarioLocator extends ALankaLocator<ILankaScenario<unknown>> {\n\t/**\n\t * The registry is read LAZILY rather than in a field initialiser.\n\t *\n\t * The locator is constructed INSIDE `createLanka`, before the instance becomes\n\t * active. A field initialiser would ask for the active instance at that moment\n\t * and fail with \"no instance yet\" while the instance being created is on the\n\t * stack. Deferred reading resolves it: by the first scenario resolution an\n\t * active instance exists.\n\t */\n\tprivate get registry(): LankaScenariosRegistry {\n\t\treturn LankaScenariosRegistry.getInstance();\n\t}\n\n\tconstructor() {\n\t\tsuper({\n\t\t\tfindClassByName: (scenarioName: string) => {\n\t\t\t\t// By export key first: an instance name does not always survive minification.\n\t\t\t\tif (scenarioName in ScenariosModule) {\n\t\t\t\t\t// `as unknown` before the guards, exactly as in the singleton locator.\n\t\t\t\t\t// An EMPTY barrel — what the scaffolder writes, and what a new project\n\t\t\t\t\t// has until its first scenario — makes the module's keys `never`, so\n\t\t\t\t\t// an indexed access is `never` and reading `.prototype` off it does\n\t\t\t\t\t// not compile.\n\t\t\t\t\tconst ScenarioClass = ScenariosModule[\n\t\t\t\t\t\tscenarioName as keyof typeof ScenariosModule\n\t\t\t\t\t] as unknown;\n\t\t\t\t\tif (\n\t\t\t\t\t\ttypeof ScenarioClass === \"function\" &&\n\t\t\t\t\t\t\"prototype\" in ScenarioClass &&\n\t\t\t\t\t\tScenarioClass.prototype\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn ScenarioClass as new () => ILankaScenario<unknown>;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Fallback: look up by instance name.\n\t\t\t\treturn findScenarioClassByInstanceName(scenarioName);\n\t\t\t},\n\t\t\tgetInstanceByName: (scenarioName: string) => {\n\t\t\t\t// The registry first.\n\t\t\t\tconst registered = this.registry.getScenarioByName(scenarioName);\n\t\t\t\tif (registered) {\n\t\t\t\t\treturn registered;\n\t\t\t\t}\n\n\t\t\t\t// Then those that registered themselves.\n\t\t\t\tconst autoRegistered = ALankaScenario.getAutoRegisteredScenarios();\n\t\t\t\tconst scenario = autoRegistered.find((s) => s.name === scenarioName);\n\n\t\t\t\tif (scenario) {\n\t\t\t\t\tif (!this.registry.isRegistered(scenarioName)) {\n\t\t\t\t\t\tthis.registry.register(scenario);\n\t\t\t\t\t}\n\t\t\t\t\treturn scenario;\n\t\t\t\t}\n\n\t\t\t\t// Then finding the class by name and constructing it.\n\t\t\t\treturn undefined;\n\t\t\t},\n\t\t\tcreateInstance: (ScenarioClass) => {\n\t\t\t\tconst instance = new ScenarioClass();\n\t\t\t\tconst scenarioName = instance.name;\n\t\t\t\tif (!this.registry.isRegistered(scenarioName)) {\n\t\t\t\t\tthis.registry.register(instance);\n\t\t\t\t}\n\t\t\t\treturn instance;\n\t\t\t},\n\t\t\tnotFoundError: (scenarioName, propertyName) =>\n\t\t\t\t`Scenario \"${scenarioName}\" (accessed as \"${propertyName}\") not found. ` +\n\t\t\t\t`Make sure the scenario class extends ALankaScenario and has name=\"${scenarioName}\".`,\n\t\t});\n\t}\n}\n","import { caseConvert } from \"../../../_internal/case-convert/caseConvert\";\nimport { lankaLogger } from \"../../../logger/lanka-logger/LankaLogger\";\nimport type { LankaSingletonLocator } from \"../../singleton/lanka-singleton-locator/LankaSingletonLocator\";\n\n/**\n * A service lifetime other than \"as long as the application lives\".\n *\n * Everything resolved through `lankaSingletons.*` is a singleton for the life of\n * the app. Per-route, per-session and per-modal lifetimes otherwise rest on\n * discipline: every ViewModel calling `resetScenario()` and writing its own\n * `onReset`, in any of which it can be forgotten.\n *\n * A scope provides the same by construction: an object created in it goes away\n * with it, and nobody has to remember.\n *\n * ## What a scope does NOT do\n *\n * It does not shadow root objects and does not take them on close. It takes only\n * ITS OWN — the ones it created. A scope taking others' would be more dangerous\n * than no scopes at all: closing a screen would break the app.\n */\nexport interface ILankaScope {\n\t/** Resolves a service in this scope, creating it on first use. */\n\tresolve<TInstance>(propertyName: string): TInstance;\n\t/** Disposes everything this scope created and closes it. Idempotent. */\n\tdispose(): void;\n\t/** Whether the scope is closed. */\n\tisDisposed(): boolean;\n}\n\n/** A service may have a `dispose` — or may not. */\ninterface IMaybeDisposable {\n\tdispose?: () => void;\n}\n\nexport function createLankaScope(singletons: LankaSingletonLocator): ILankaScope {\n\tconst instances = new Map<string, unknown>();\n\tlet disposed = false;\n\n\treturn {\n\t\tresolve<TInstance>(propertyName: string): TInstance {\n\t\t\tif (disposed) {\n\t\t\t\t// Resolving from a closed scope is almost always a reference leaked from\n\t\t\t\t// an already-unmounted screen. Silently handing out an object would\n\t\t\t\t// extend the life of what was closed, invisibly.\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`The scope is closed: \"${propertyName}\" can no longer be resolved in it. ` +\n\t\t\t\t\t\t`This is usually a scope reference that outlived the screen that created it.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst className = caseConvert(propertyName, \"pascalCase\");\n\t\t\tif (instances.has(className)) return instances.get(className) as TInstance;\n\n\t\t\t// The class comes from the root locator while the OBJECT is created here:\n\t\t\t// a scope is a different lifetime, not a different set of services.\n\t\t\tconst instance = singletons.createScopedInstance(className, propertyName);\n\t\t\tinstances.set(className, instance);\n\t\t\treturn instance as TInstance;\n\t\t},\n\n\t\tdispose(): void {\n\t\t\tif (disposed) return;\n\t\t\tdisposed = true;\n\n\t\t\tfor (const [name, instance] of instances) {\n\t\t\t\tconst disposable = instance as IMaybeDisposable;\n\t\t\t\tif (typeof disposable.dispose !== \"function\") continue;\n\t\t\t\ttry {\n\t\t\t\t\tdisposable.dispose();\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// One failure must not leave the rest alive: that is exactly what a\n\t\t\t\t\t// scope exists to prevent.\n\t\t\t\t\tlankaLogger.printBootstrapLog(\n\t\t\t\t\t\t`Failed to dispose \"${name}\" in the scope`,\n\t\t\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tinstances.clear();\n\t\t},\n\n\t\tisDisposed: () => disposed,\n\t};\n}\n"],"mappings":"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","names":[]}
|