lanka 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +242 -0
  3. package/dist/ALankaGateway-ExlRGT3D.d.ts +122 -0
  4. package/dist/ALankaSharedStore-B7uepuuk.d.ts +29 -0
  5. package/dist/ILankaApiError-zI_2bNs0.d.ts +16 -0
  6. package/dist/ILankaRuntimeConfig-Vl436GWK.d.ts +62 -0
  7. package/dist/ILankaScenarioMetadata-Bu-yggTZ.d.ts +14 -0
  8. package/dist/ILankaScenarioVM-DuCyPoyT.d.ts +260 -0
  9. package/dist/LankaError-B1HtuIkw.d.ts +86 -0
  10. package/dist/LankaScenarioLocator-BGQHwf3n.d.ts +144 -0
  11. package/dist/LankaSharedStoreLocator-MvCpav5F.d.ts +126 -0
  12. package/dist/TLankaErrorHandler-Yfqtdh1M.d.ts +3 -0
  13. package/dist/_extend/index.d.ts +34 -0
  14. package/dist/_extend/index.js +36 -0
  15. package/dist/_extend/index.js.map +1 -0
  16. package/dist/_internal/index.d.ts +43 -0
  17. package/dist/_internal/index.js +34 -0
  18. package/dist/_internal/index.js.map +1 -0
  19. package/dist/activeRuntime-FcsSJvUg.d.ts +84 -0
  20. package/dist/bootstrap/index.d.ts +242 -0
  21. package/dist/bootstrap/index.js +22 -0
  22. package/dist/bootstrap/index.js.map +1 -0
  23. package/dist/chunk-73IVH3C6.js +52 -0
  24. package/dist/chunk-73IVH3C6.js.map +1 -0
  25. package/dist/chunk-BGVDPDX4.js +29 -0
  26. package/dist/chunk-BGVDPDX4.js.map +1 -0
  27. package/dist/chunk-C2HP7CRD.js +224 -0
  28. package/dist/chunk-C2HP7CRD.js.map +1 -0
  29. package/dist/chunk-D27MREPB.js +13 -0
  30. package/dist/chunk-D27MREPB.js.map +1 -0
  31. package/dist/chunk-DTO27QFR.js +59 -0
  32. package/dist/chunk-DTO27QFR.js.map +1 -0
  33. package/dist/chunk-EWVDJYCC.js +296 -0
  34. package/dist/chunk-EWVDJYCC.js.map +1 -0
  35. package/dist/chunk-FIR4XTBL.js +136 -0
  36. package/dist/chunk-FIR4XTBL.js.map +1 -0
  37. package/dist/chunk-ILQNYQY5.js +200 -0
  38. package/dist/chunk-ILQNYQY5.js.map +1 -0
  39. package/dist/chunk-J5LA3Q6D.js +14 -0
  40. package/dist/chunk-J5LA3Q6D.js.map +1 -0
  41. package/dist/chunk-MDZ42XRH.js +10 -0
  42. package/dist/chunk-MDZ42XRH.js.map +1 -0
  43. package/dist/chunk-Q3SOVBIJ.js +176 -0
  44. package/dist/chunk-Q3SOVBIJ.js.map +1 -0
  45. package/dist/chunk-Q7QESSYF.js +49 -0
  46. package/dist/chunk-Q7QESSYF.js.map +1 -0
  47. package/dist/chunk-RKYKK6MN.js +19 -0
  48. package/dist/chunk-RKYKK6MN.js.map +1 -0
  49. package/dist/chunk-RYFZCAQ3.js +637 -0
  50. package/dist/chunk-RYFZCAQ3.js.map +1 -0
  51. package/dist/chunk-UJEC7H6K.js +39 -0
  52. package/dist/chunk-UJEC7H6K.js.map +1 -0
  53. package/dist/chunk-XESL274R.js +23 -0
  54. package/dist/chunk-XESL274R.js.map +1 -0
  55. package/dist/chunk-YR4MZXMU.js +13 -0
  56. package/dist/chunk-YR4MZXMU.js.map +1 -0
  57. package/dist/config/index.d.ts +63 -0
  58. package/dist/config/index.js +16 -0
  59. package/dist/config/index.js.map +1 -0
  60. package/dist/createLankaScope-Bc_vChRs.d.ts +30 -0
  61. package/dist/errors/index.d.ts +37 -0
  62. package/dist/errors/index.js +16 -0
  63. package/dist/errors/index.js.map +1 -0
  64. package/dist/gateway/index.d.ts +258 -0
  65. package/dist/gateway/index.js +438 -0
  66. package/dist/gateway/index.js.map +1 -0
  67. package/dist/index.d.ts +20 -0
  68. package/dist/index.js +43 -0
  69. package/dist/index.js.map +1 -0
  70. package/dist/lankaHttpInFlight-Bk1eIuSx.d.ts +43 -0
  71. package/dist/lankaRequestMiddleware-DAC5kCb7.d.ts +62 -0
  72. package/dist/lankaStandardValidator-CL-r-zEV.d.ts +34 -0
  73. package/dist/locator/index.d.ts +282 -0
  74. package/dist/locator/index.js +67 -0
  75. package/dist/locator/index.js.map +1 -0
  76. package/dist/logger/index.d.ts +146 -0
  77. package/dist/logger/index.js +11 -0
  78. package/dist/logger/index.js.map +1 -0
  79. package/dist/mock/index.d.ts +16 -0
  80. package/dist/mock/index.js +58 -0
  81. package/dist/mock/index.js.map +1 -0
  82. package/dist/role/index.d.ts +48 -0
  83. package/dist/role/index.js +7 -0
  84. package/dist/role/index.js.map +1 -0
  85. package/dist/scenario/index.d.ts +204 -0
  86. package/dist/scenario/index.js +28 -0
  87. package/dist/scenario/index.js.map +1 -0
  88. package/dist/validation/index.d.ts +18 -0
  89. package/dist/validation/index.js +53 -0
  90. package/dist/validation/index.js.map +1 -0
  91. package/dist/viewmodel/index.d.ts +478 -0
  92. package/dist/viewmodel/index.js +701 -0
  93. package/dist/viewmodel/index.js.map +1 -0
  94. package/package.json +99 -0
  95. package/skills/lanka-core/SKILL.md +241 -0
  96. package/skills/lanka-core/reference.md +1090 -0
  97. package/skills/lanka-packages/SKILL.md +70 -0
@@ -0,0 +1,637 @@
1
+ import {
2
+ lankaScenarioBootstrap
3
+ } from "./chunk-Q3SOVBIJ.js";
4
+ import {
5
+ LankaGatewayLocator,
6
+ LankaScenarioLocator,
7
+ createLankaScope
8
+ } from "./chunk-FIR4XTBL.js";
9
+ import {
10
+ LankaScenarioVMRegistry,
11
+ LankaScenariosRegistry
12
+ } from "./chunk-EWVDJYCC.js";
13
+ import {
14
+ createLankaHost
15
+ } from "./chunk-XESL274R.js";
16
+ import {
17
+ createInFlightCounter
18
+ } from "./chunk-UJEC7H6K.js";
19
+ import {
20
+ LankaSharedStoreLocator,
21
+ LankaSingletonLocator
22
+ } from "./chunk-ILQNYQY5.js";
23
+ import {
24
+ lankaLogger
25
+ } from "./chunk-C2HP7CRD.js";
26
+ import {
27
+ getActiveRuntime,
28
+ setActiveRuntime
29
+ } from "./chunk-BGVDPDX4.js";
30
+
31
+ // src/scenario/event-bus/lanka-event-bus-instance/LankaEventBusInstance.ts
32
+ function resolveReplayCount(request) {
33
+ if (request === void 0 || request === false) return 0;
34
+ if (request === true || request === "last") return 1;
35
+ return Math.max(0, Math.trunc(request));
36
+ }
37
+ function maxReplayDepth(subs) {
38
+ let depth = 0;
39
+ for (const entry of subs) if (entry.replayCount > depth) depth = entry.replayCount;
40
+ return depth;
41
+ }
42
+ var LankaEventBusInstance = class {
43
+ /** Event registry and per-event state — private to each instance. */
44
+ registry = /* @__PURE__ */ new Map();
45
+ /** Middleware registered on this instance. */
46
+ middlewares = [];
47
+ /** Default maximum number of logs kept per event type. */
48
+ defaultMaxLogs = 100;
49
+ /** Internal timers for replay batching (avoid multiple replays in the same tick). */
50
+ pendingReplayTimers = /* @__PURE__ */ new Map();
51
+ /** Whether event logging is enabled. */
52
+ isEnableLogs = false;
53
+ // -------------------------------------------------------------------------
54
+ // Configuration
55
+ // -------------------------------------------------------------------------
56
+ /** Configures the bus, e.g. logging. Called once at application start. */
57
+ enableLogs() {
58
+ this.isEnableLogs = true;
59
+ }
60
+ disableLogs() {
61
+ this.isEnableLogs = false;
62
+ }
63
+ // -------------------------------------------------------------------------
64
+ // Internal
65
+ // -------------------------------------------------------------------------
66
+ /**
67
+ * Ensures the event is registered, registering it if not.
68
+ *
69
+ * @param eventType Event type
70
+ * @param usedBy Who uses it — for the inspector and for finding an owner
71
+ */
72
+ ensureEvent(eventType, usedBy) {
73
+ let state = this.registry.get(eventType);
74
+ if (!state) {
75
+ const meta = {
76
+ dataType: "any",
77
+ description: `Auto-registered event: ${eventType}`,
78
+ usedBy: usedBy ? [usedBy] : [],
79
+ priority: 0,
80
+ maxLogs: this.defaultMaxLogs
81
+ };
82
+ state = {
83
+ meta,
84
+ logs: [],
85
+ buffer: [],
86
+ subs: []
87
+ };
88
+ this.registry.set(eventType, state);
89
+ } else if (usedBy) {
90
+ const meta = state.meta;
91
+ meta.usedBy = [.../* @__PURE__ */ new Set([...meta.usedBy ?? [], usedBy])];
92
+ }
93
+ return state;
94
+ }
95
+ // -------------------------------------------------------------------------
96
+ // Public surface
97
+ // -------------------------------------------------------------------------
98
+ /**
99
+ * Declares an event explicitly, together with its metadata.
100
+ *
101
+ * @param eventType Event type
102
+ * @param metadata What is known about the event and what it is allowed
103
+ */
104
+ registerEvent(eventType, metadata) {
105
+ const existing = this.registry.get(eventType);
106
+ const meta = {
107
+ dataType: metadata.dataType ?? existing?.meta.dataType ?? "any",
108
+ description: metadata.description ?? existing?.meta.description ?? `Registered event: ${eventType}`,
109
+ // `usedBy` lists everyone using the event and exists precisely to be
110
+ // complete. Replacing it would erase half.
111
+ usedBy: [.../* @__PURE__ */ new Set([...existing?.meta.usedBy ?? [], ...metadata.usedBy ?? []])],
112
+ schema: metadata.schema ?? existing?.meta.schema,
113
+ priority: metadata.priority ?? existing?.meta.priority ?? 0,
114
+ maxLogs: metadata.maxLogs ?? existing?.meta.maxLogs ?? this.defaultMaxLogs,
115
+ replay: metadata.replay ?? existing?.meta.replay
116
+ };
117
+ if (existing) {
118
+ existing.meta = meta;
119
+ return;
120
+ }
121
+ this.registry.set(eventType, { meta, logs: [], buffer: [], subs: [] });
122
+ }
123
+ /** Every registered event with its metadata. */
124
+ getRegisteredEvents() {
125
+ return Array.from(this.registry.entries()).map(([eventType, state]) => ({
126
+ eventType,
127
+ metadata: state.meta
128
+ })).sort((a, b) => a.eventType.localeCompare(b.eventType));
129
+ }
130
+ /** Metadata of one event type. */
131
+ getEventInfo(eventType) {
132
+ return this.registry.get(eventType)?.meta ?? null;
133
+ }
134
+ /** How many live subscriptions this event has. */
135
+ /**
136
+ * How many values sit in the event's buffer.
137
+ *
138
+ * Exists for the test: "the buffer stays empty until someone asks for replay"
139
+ * is a claim about MEMORY, and nothing but asking can check it.
140
+ */
141
+ getBufferedCount(eventType) {
142
+ return this.registry.get(eventType)?.buffer.length ?? 0;
143
+ }
144
+ getSubscriptions(eventType) {
145
+ return this.registry.get(eventType)?.subs.length ?? 0;
146
+ }
147
+ /**
148
+ * Subscribes a handler to an event type.
149
+ *
150
+ * @param eventType Event type
151
+ * @param callback What to run on dispatch
152
+ * @param options Subscription options:
153
+ * - `priority` — execution order, higher runs earlier;
154
+ * - `replay` — receive what was dispatched before subscribing;
155
+ * - `usedBy` — who subscribed, for the inspector.
156
+ */
157
+ subscribe(eventType, callback, options) {
158
+ const state = this.ensureEvent(eventType, options?.usedBy);
159
+ const prio = options?.priority ?? state.meta.priority ?? 0;
160
+ const entry = {
161
+ callback,
162
+ priority: prio,
163
+ replayCount: resolveReplayCount(options?.replay)
164
+ };
165
+ const subs = state.subs;
166
+ let low = 0, high = subs.length;
167
+ while (low < high) {
168
+ const mid = low + high >>> 1;
169
+ if (subs[mid].priority < prio) high = mid;
170
+ else low = mid + 1;
171
+ }
172
+ subs.splice(low, 0, entry);
173
+ if (options?.replay) this.scheduleBatchedReplay(eventType, state);
174
+ return () => {
175
+ const at = state.subs.indexOf(entry);
176
+ if (at !== -1) state.subs.splice(at, 1);
177
+ };
178
+ }
179
+ /**
180
+ * Removes a handler from an event type.
181
+ *
182
+ * @param eventType Event type
183
+ * @param callback A previously subscribed handler
184
+ */
185
+ unsubscribe(eventType, callback) {
186
+ const state = this.registry.get(eventType);
187
+ if (!state) return;
188
+ const subs = state.subs;
189
+ for (let i = 0; i < subs.length; i++) {
190
+ if (subs[i].callback === callback) {
191
+ subs.splice(i, 1);
192
+ break;
193
+ }
194
+ }
195
+ }
196
+ /**
197
+ * Dispatches an event: the middleware chain first, then subscribers.
198
+ *
199
+ * @param eventType Event type
200
+ * @param data Payload
201
+ * @param usedBy Who dispatched — recorded in the metadata
202
+ */
203
+ dispatch(eventType, data, usedBy) {
204
+ const state = this.ensureEvent(eventType, usedBy);
205
+ const { meta, subs, buffer } = state;
206
+ if (meta.schema && data !== void 0 && !meta.schema(data)) {
207
+ lankaLogger.printScenarioLog(`Invalid data for event "${eventType}":`, data);
208
+ return;
209
+ }
210
+ this.logEvent(state, eventType, data);
211
+ const depth = Math.max(resolveReplayCount(meta.replay), maxReplayDepth(subs));
212
+ if (depth > 0) {
213
+ buffer.push(data);
214
+ while (buffer.length > depth) buffer.shift();
215
+ } else if (buffer.length > 0) {
216
+ buffer.length = 0;
217
+ }
218
+ const deliver = () => {
219
+ for (const entry of [...subs]) {
220
+ try {
221
+ entry.callback(data);
222
+ } catch (err) {
223
+ lankaLogger.printScenarioLog(`Error dispatching event "${eventType}":`, err);
224
+ }
225
+ }
226
+ };
227
+ for (const middleware of [...this.middlewares]) {
228
+ let decision;
229
+ try {
230
+ decision = middleware(eventType, data);
231
+ } catch (err) {
232
+ decision = { stop: err instanceof Error ? err.message : String(err) };
233
+ }
234
+ if (decision !== "pass") {
235
+ this.markStopped(state, decision.stop);
236
+ lankaLogger.printScenarioLog(
237
+ `Event "${eventType}" stopped by middleware:`,
238
+ decision.stop
239
+ );
240
+ return;
241
+ }
242
+ }
243
+ deliver();
244
+ }
245
+ /** Adds middleware to the shared chain. */
246
+ addMiddleware(mw) {
247
+ this.middlewares.push(mw);
248
+ }
249
+ /** Removes previously added middleware. */
250
+ removeMiddleware(mw) {
251
+ const i = this.middlewares.indexOf(mw);
252
+ if (i !== -1) this.middlewares.splice(i, 1);
253
+ }
254
+ /**
255
+ * Log of dispatched events.
256
+ *
257
+ * @param eventType Limit to one type
258
+ * @param limit How many recent records to return
259
+ */
260
+ getEventLogs(eventType, limit = 100) {
261
+ if (eventType) {
262
+ return this.registry.get(eventType)?.logs.slice(-limit) ?? [];
263
+ }
264
+ const all = [];
265
+ for (const { logs } of this.registry.values()) all.push(...logs);
266
+ return all.slice(-limit);
267
+ }
268
+ /** Removes an event together with its subscriptions, log and buffer. */
269
+ clearEvent(eventType) {
270
+ this.registry.delete(eventType);
271
+ const t = this.pendingReplayTimers.get(eventType);
272
+ if (t) {
273
+ clearTimeout(t);
274
+ this.pendingReplayTimers.delete(eventType);
275
+ }
276
+ }
277
+ /** Removes every event and any scheduled replays. */
278
+ clearAllEvents() {
279
+ this.registry.clear();
280
+ for (const t of this.pendingReplayTimers.values()) clearTimeout(t);
281
+ this.pendingReplayTimers.clear();
282
+ }
283
+ /** Full bus reset: events and middleware. */
284
+ reset() {
285
+ this.clearAllEvents();
286
+ this.middlewares = [];
287
+ this.isEnableLogs = false;
288
+ }
289
+ // -------------------------------------------------------------------------
290
+ // Internal helpers
291
+ // -------------------------------------------------------------------------
292
+ /**
293
+ * Marks the last log record with the reason it was stopped.
294
+ *
295
+ * Writes where `logEvent` just put the event: a stop is not a separate
296
+ * occurrence but this one's outcome.
297
+ */
298
+ markStopped(state, reason) {
299
+ const last = state.logs.at(-1);
300
+ if (last) last.stoppedBy = reason;
301
+ }
302
+ /** Add an event log entry for debugging/auditing. */
303
+ logEvent(state, eventType, data) {
304
+ if (this.isEnableLogs) {
305
+ const cap = state.meta.maxLogs ?? this.defaultMaxLogs;
306
+ const logs = state.logs;
307
+ const log = {
308
+ eventType,
309
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
310
+ data
311
+ };
312
+ log.stackTrace = new Error().stack;
313
+ logs.push(log);
314
+ if (logs.length > cap) logs.shift();
315
+ }
316
+ }
317
+ /**
318
+ * Schedules replay for late subscribers that asked for it.
319
+ *
320
+ * Replays are batched into one tick: otherwise ten subscriptions to one event
321
+ * would make ten separate passes over the same buffer.
322
+ */
323
+ scheduleBatchedReplay(eventType, state) {
324
+ if (this.pendingReplayTimers.has(eventType)) return;
325
+ const timer = setTimeout(() => {
326
+ this.pendingReplayTimers.delete(eventType);
327
+ const { buffer, subs } = state;
328
+ if (!buffer.length) return;
329
+ const needReplay = subs.filter((s) => s.replayCount > 0);
330
+ if (!needReplay.length) return;
331
+ for (const entry of needReplay) {
332
+ for (const payload of buffer.slice(-entry.replayCount)) {
333
+ try {
334
+ entry.callback(payload);
335
+ } catch (err) {
336
+ lankaLogger.printScenarioLog(
337
+ `Error dispatching event "${eventType}" (replay):`,
338
+ err
339
+ );
340
+ }
341
+ }
342
+ }
343
+ for (const entry of needReplay) entry.replayCount = 0;
344
+ }, 0);
345
+ this.pendingReplayTimers.set(eventType, timer);
346
+ }
347
+ };
348
+
349
+ // src/bootstrap/_internal/create-lanka-plugin-registry/createLankaPluginRegistry.ts
350
+ var createLankaPluginRegistry = (instance) => {
351
+ const removers = /* @__PURE__ */ new Map();
352
+ return {
353
+ use(plugin) {
354
+ if (removers.has(plugin.name)) {
355
+ throw new Error(
356
+ `Plugin "${plugin.name}" is already registered on this lanka instance. Registering twice almost always means two copies of one policy.`
357
+ );
358
+ }
359
+ const uninstall = plugin.install(instance);
360
+ const remove = () => {
361
+ if (!removers.has(plugin.name)) return;
362
+ removers.delete(plugin.name);
363
+ uninstall?.();
364
+ };
365
+ removers.set(plugin.name, remove);
366
+ return remove;
367
+ },
368
+ removeAll() {
369
+ for (const remove of [...removers.values()]) remove();
370
+ removers.clear();
371
+ }
372
+ };
373
+ };
374
+
375
+ // src/bootstrap/_factories/create-lanka/createLanka.ts
376
+ function mergeLankaConfig(previous, patch) {
377
+ return {
378
+ // `host` is read field by field: `setConfig({ host: undefined })` through a
379
+ // spread would leave the instance with no host, and every request URL would
380
+ // then carry the string "undefined". Flags stay a spread — there both
381
+ // absent and undefined mean "not set", so nothing can be lost.
382
+ host: patch.host ?? previous.host,
383
+ flags: { ...previous.flags, ...patch.flags }
384
+ };
385
+ }
386
+ function createLanka(config) {
387
+ let runtimeConfig = mergeLankaConfig(
388
+ {},
389
+ { host: config.host, flags: config.flags }
390
+ );
391
+ let bootstrapped = false;
392
+ const eventBus = new LankaEventBusInstance();
393
+ const scenarios = new LankaScenariosRegistry();
394
+ const viewModels = new LankaScenarioVMRegistry();
395
+ const inFlight = createInFlightCounter();
396
+ const scenarioState = {
397
+ bootstrapped: false,
398
+ initialized: /* @__PURE__ */ new WeakSet()
399
+ };
400
+ const scopes = /* @__PURE__ */ new Set();
401
+ const requestMiddleware = [];
402
+ let requestTimeoutMs;
403
+ const locators = {
404
+ gateways: new LankaGatewayLocator(),
405
+ scenarios: new LankaScenarioLocator(),
406
+ singletons: new LankaSingletonLocator(),
407
+ sharedStores: new LankaSharedStoreLocator()
408
+ };
409
+ const instance = {
410
+ eventBus,
411
+ scenarios,
412
+ viewModels,
413
+ inFlight,
414
+ locators,
415
+ scenarioState,
416
+ requestMiddleware,
417
+ get requestTimeoutMs() {
418
+ return requestTimeoutMs;
419
+ },
420
+ useRequestMiddleware(middleware) {
421
+ requestMiddleware.push(middleware);
422
+ return () => {
423
+ const at = requestMiddleware.indexOf(middleware);
424
+ if (at !== -1) requestMiddleware.splice(at, 1);
425
+ };
426
+ },
427
+ setRequestTimeout(timeoutMs) {
428
+ requestTimeoutMs = timeoutMs;
429
+ },
430
+ resolve(propertyName) {
431
+ return locators.singletons.get(propertyName);
432
+ },
433
+ use(plugin) {
434
+ return plugins.use(plugin);
435
+ },
436
+ createScope() {
437
+ const scope = createLankaScope(locators.singletons);
438
+ scopes.add(scope);
439
+ return scope;
440
+ },
441
+ get config() {
442
+ return runtimeConfig;
443
+ },
444
+ setConfig(patch) {
445
+ runtimeConfig = mergeLankaConfig(runtimeConfig, patch);
446
+ },
447
+ getFlags() {
448
+ return runtimeConfig.flags ?? {};
449
+ },
450
+ isBootstrapped() {
451
+ return bootstrapped;
452
+ },
453
+ activate() {
454
+ setActiveRuntime(instance);
455
+ },
456
+ dispose() {
457
+ teardown();
458
+ requestMiddleware.length = 0;
459
+ requestTimeoutMs = void 0;
460
+ bootstrapped = false;
461
+ },
462
+ async bootstrap(bootstrapConfig = {}) {
463
+ if (bootstrapped) {
464
+ lankaLogger.printBootstrapLog("LANKA ALREADY BOOTSTRAPPED");
465
+ return;
466
+ }
467
+ instance.activate();
468
+ lankaLogger.printBootstrapLog("LANKA BOOTSTRAP START");
469
+ const plan = createExecutionPlan(bootstrapConfig);
470
+ await runSequential(plan.syncTasks, "SYNC TASKS");
471
+ await runParallel(plan.asyncTasks, "ASYNC TASKS");
472
+ await runSequential(plan.postAsyncTasks, "POST-ASYNC TASKS (lankaScenarios)");
473
+ bootstrapped = true;
474
+ lankaLogger.printBootstrapLog("LANKA BOOTSTRAP FINISH");
475
+ }
476
+ };
477
+ function teardown() {
478
+ for (const scope of scopes) scope.dispose();
479
+ scopes.clear();
480
+ plugins.removeAll();
481
+ viewModels.resetAll();
482
+ scenarios.clear();
483
+ eventBus.reset();
484
+ locators.gateways.clearCache();
485
+ locators.scenarios.clearCache();
486
+ locators.singletons.clearCache();
487
+ locators.sharedStores.clearCache();
488
+ scenarioState.bootstrapped = false;
489
+ scenarioState.initialized = /* @__PURE__ */ new WeakSet();
490
+ if (getActiveRuntime() === instance) setActiveRuntime(null);
491
+ }
492
+ const plugins = createLankaPluginRegistry(instance);
493
+ instance.activate();
494
+ lankaScenarioBootstrap.adoptDeclaredViewModels();
495
+ return instance;
496
+ }
497
+ function createExecutionPlan(config) {
498
+ const syncTasks = [];
499
+ const asyncTasks = [];
500
+ const postAsyncTasks = [];
501
+ config.services?.forEach((service, index) => {
502
+ const task = {
503
+ name: service.name || `Service-${index + 1}`,
504
+ priority: service.priority ?? 0,
505
+ optional: service.optional ?? false,
506
+ timeoutMs: service.timeoutMs,
507
+ execute: service.init
508
+ };
509
+ if (service.sync) syncTasks.push(task);
510
+ else asyncTasks.push(task);
511
+ });
512
+ const scenarioSync = config.scenarios?.sync ?? true;
513
+ const scenarioTask = {
514
+ name: "LankaScenarioBootstrap",
515
+ priority: config.scenarios?.priority ?? 0,
516
+ // The scenario layer is required: without it nothing subscribed to it
517
+ // works, and continuing silently would start an app where half the screens
518
+ // never update.
519
+ optional: false,
520
+ execute: () => {
521
+ lankaScenarioBootstrap.bootstrap();
522
+ }
523
+ };
524
+ if (scenarioSync) syncTasks.push(scenarioTask);
525
+ else postAsyncTasks.push(scenarioTask);
526
+ syncTasks.sort((a, b) => b.priority - a.priority);
527
+ return { syncTasks, asyncTasks, postAsyncTasks };
528
+ }
529
+ async function runWithDeadline(task) {
530
+ if (task.timeoutMs === void 0) {
531
+ await task.execute();
532
+ return;
533
+ }
534
+ let timer;
535
+ try {
536
+ await Promise.race([
537
+ Promise.resolve(task.execute()),
538
+ new Promise((_resolve, reject) => {
539
+ timer = setTimeout(
540
+ () => reject(
541
+ new Error(
542
+ `Service "${task.name}" did not finish within ${String(task.timeoutMs)}ms`
543
+ )
544
+ ),
545
+ task.timeoutMs
546
+ );
547
+ })
548
+ ]);
549
+ } finally {
550
+ if (timer !== void 0) clearTimeout(timer);
551
+ }
552
+ }
553
+ function handleTaskFailure(task, phaseName, error) {
554
+ lankaLogger.printBootstrapLog(`[${phaseName}] ERROR IN ${task.name}`, formatError(error));
555
+ if (!task.optional) throw error;
556
+ lankaLogger.printBootstrapLog(`[${phaseName}] SKIPPED (optional): ${task.name}`);
557
+ }
558
+ async function runSequential(tasks, phaseName) {
559
+ if (tasks.length === 0) return;
560
+ for (const task of tasks) {
561
+ try {
562
+ lankaLogger.printBootstrapLog(
563
+ `[${phaseName}] EXECUTING: ${task.name} (Priority: ${String(task.priority)})`
564
+ );
565
+ await runWithDeadline(task);
566
+ } catch (error) {
567
+ handleTaskFailure(task, phaseName, error);
568
+ }
569
+ }
570
+ }
571
+ async function runParallel(tasks, phaseName) {
572
+ if (tasks.length === 0) return;
573
+ lankaLogger.printBootstrapLog(
574
+ `[${phaseName}] EXECUTING ${String(tasks.length)} TASKS IN PARALLEL`
575
+ );
576
+ const outcomes = await Promise.allSettled(
577
+ tasks.map(async (task) => {
578
+ lankaLogger.printBootstrapLog(`[${phaseName}] EXECUTING: ${task.name}`);
579
+ await runWithDeadline(task);
580
+ })
581
+ );
582
+ for (const [index, outcome] of outcomes.entries()) {
583
+ if (outcome.status === "rejected") {
584
+ handleTaskFailure(tasks[index], phaseName, outcome.reason);
585
+ }
586
+ }
587
+ }
588
+ function formatError(error) {
589
+ return error instanceof Error ? error.message : String(error);
590
+ }
591
+
592
+ // src/bootstrap/start-lanka/startLanka.ts
593
+ var hostOf = (config) => config.host ?? createLankaHost({ apiBaseUrl: config.apiBaseUrl, ...config.messages });
594
+ var startLanka = async (config = {}) => {
595
+ const lanka = createLanka({ host: hostOf(config), flags: config.flags });
596
+ for (const plugin of config.plugins ?? []) lanka.use(plugin);
597
+ await lanka.bootstrap({ services: config.services, scenarios: config.scenarios });
598
+ return lanka;
599
+ };
600
+
601
+ // src/bootstrap/reset-active-lanka/resetActiveLanka.ts
602
+ function resetActiveLanka() {
603
+ const active = getActiveRuntime();
604
+ if (!active) return;
605
+ active.viewModels.resetAll();
606
+ active.scenarios.clear();
607
+ active.eventBus.clearAllEvents();
608
+ active.scenarioState.bootstrapped = false;
609
+ active.scenarioState.initialized = /* @__PURE__ */ new WeakSet();
610
+ }
611
+
612
+ // src/bootstrap/_abstractions/lanka-plugin/ALankaPlugin.ts
613
+ var ALankaPlugin = class {
614
+ /**
615
+ * Removes what `onInstall` put in place.
616
+ *
617
+ * A default that does nothing rather than an abstract member: an abstract one
618
+ * would break every existing subclass the day it was added, and most plugins
619
+ * genuinely have nothing to undo.
620
+ */
621
+ onUninstall() {
622
+ }
623
+ install(lanka) {
624
+ this.onInstall(lanka);
625
+ return () => {
626
+ this.onUninstall();
627
+ };
628
+ }
629
+ };
630
+
631
+ export {
632
+ createLanka,
633
+ startLanka,
634
+ resetActiveLanka,
635
+ ALankaPlugin
636
+ };
637
+ //# sourceMappingURL=chunk-RYFZCAQ3.js.map