lanka 1.2.0 → 1.3.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 +3 -1
- package/dist/{ALankaGateway-CkW1LbKE.d.ts → ALankaGateway-BrVPaZN5.d.ts} +1 -1
- package/dist/ILankaFieldError-D5931-vT.d.ts +34 -0
- package/dist/{ILankaScenarioMetadata-GoWWNEQL.d.ts → ILankaScenarioMetadata-Dj4GCqmX.d.ts} +1 -1
- package/dist/{ILankaScenarioVM-DUsI-fSc.d.ts → ILankaScenarioVM-DpKFL3iE.d.ts} +5 -4
- package/dist/{LankaError-xpI-qj35.d.ts → LankaError-D6RWNyLM.d.ts} +18 -0
- package/dist/{LankaScenarioLocator-D86TIwiu.d.ts → LankaScenarioLocator-CAENLhDO.d.ts} +3 -3
- package/dist/_extend/index.d.ts +6 -5
- package/dist/_internal/index.d.ts +61 -8
- package/dist/_internal/index.js +25 -0
- package/dist/_internal/index.js.map +1 -1
- package/dist/{activeRuntime-B336NU5I.d.ts → activeRuntime-BYd2D6Lx.d.ts} +2 -2
- package/dist/bootstrap/index.d.ts +9 -8
- package/dist/bootstrap/index.js +2 -2
- package/dist/cache/index.d.ts +99 -0
- package/dist/cache/index.js +1 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/{chunk-UGXSGQPW.js → chunk-B7EYIAW7.js} +39 -3
- package/dist/chunk-B7EYIAW7.js.map +1 -0
- package/dist/chunk-CRIRTOLB.js +27 -0
- package/dist/chunk-CRIRTOLB.js.map +1 -0
- package/dist/{chunk-Q7QESSYF.js → chunk-G32H73QY.js} +10 -1
- package/dist/chunk-G32H73QY.js.map +1 -0
- package/dist/chunk-HVSQOMRE.js +69 -0
- package/dist/chunk-HVSQOMRE.js.map +1 -0
- package/dist/{chunk-73IVH3C6.js → chunk-NDCJBO47.js} +2 -2
- package/dist/{chunk-G3I7QIZR.js → chunk-UBGXDTXC.js} +14 -5
- package/dist/chunk-UBGXDTXC.js.map +1 -0
- package/dist/{createLanka-DI1CSy2Q.d.ts → createLanka-NrlvN-WQ.d.ts} +1 -1
- package/dist/errors/index.d.ts +22 -3
- package/dist/errors/index.js +8 -3
- package/dist/errors/index.js.map +1 -1
- package/dist/gateway/index.d.ts +4 -3
- package/dist/gateway/index.js +6 -5
- package/dist/gateway/index.js.map +1 -1
- package/dist/index.d.ts +9 -8
- package/dist/index.js +3 -3
- package/dist/{lankaStandardValidator-CL-r-zEV.d.ts → lankaStandardValidator-BUFnysK0.d.ts} +10 -0
- package/dist/locator/index.d.ts +3 -2
- package/dist/scenario/index.d.ts +56 -6
- package/dist/scenario/index.js +1 -1
- package/dist/storage/index.d.ts +120 -0
- package/dist/storage/index.js +1 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/stream/index.d.ts +8 -7
- package/dist/validation/index.d.ts +7 -4
- package/dist/validation/index.js +5 -3
- package/dist/viewmodel/index.d.ts +51 -5
- package/dist/viewmodel/index.js +75 -47
- package/dist/viewmodel/index.js.map +1 -1
- package/package.json +11 -3
- package/skills/lanka-core/SKILL.md +59 -1
- package/skills/lanka-core/reference.md +312 -6
- package/skills/lanka-packages/SKILL.md +1 -1
- package/dist/chunk-G3I7QIZR.js.map +0 -1
- package/dist/chunk-GV5DUYST.js +0 -64
- package/dist/chunk-GV5DUYST.js.map +0 -1
- package/dist/chunk-Q7QESSYF.js.map +0 -1
- package/dist/chunk-UGXSGQPW.js.map +0 -1
- /package/dist/{chunk-73IVH3C6.js.map → chunk-NDCJBO47.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors/lanka-error/LankaError.ts"],"sourcesContent":["import type { ILankaApiError } from \"../_interfaces/ILankaApiError\";\nimport type { ILankaFieldError } from \"../_interfaces/ILankaFieldError\";\n\n/**\n * How a request ended.\n *\n * Six kinds rather than one, for one reason: each demands something DIFFERENT of\n * the interface. A distinction that implies no different behaviour is\n * superfluous, which is why there is no seventh.\n */\nexport type TLankaErrorKind =\n\t/** Never reached the server: dropped link, DNS failure, no network. Offer a retry. */\n\t| \"network\"\n\t/** Reached it; the response never came. Also a retry, but a different message. */\n\t| \"timeout\"\n\t/** Cancelled by the caller. Nothing to show: the user left. */\n\t| \"aborted\"\n\t/** The server answered with an error status; what to show depends on it. */\n\t| \"http\"\n\t/** A response arrived in the wrong shape. A break, not the user's fault. */\n\t| \"schema\"\n\t/** The server refused deliberately and named the reason. Show what it said. */\n\t| \"domain\";\n\nexport interface ILankaErrorInit {\n\tkind: TLankaErrorKind;\n\tmessage: string;\n\t/** HTTP status, when there was one. */\n\tstatus?: number;\n\t/** The domain failure code — what the application branches on. */\n\tcode?: string;\n\t/** Details: server messages, or paths to the fields that failed validation. */\n\tissues?: readonly string[];\n\t/**\n\t * The failures that have an input to be shown at, with the path in segments.\n\t *\n\t * Beside `issues` rather than instead of it: `issues` is the banner's list and\n\t * is promised; this is the form's, and a failure may have either, both or\n\t * neither. Absent means \"nothing here has an address\", not \"unknown\".\n\t */\n\tfields?: readonly ILankaFieldError[];\n\t/**\n\t * The parsed ERROR response body, when there was one and it was JSON.\n\t *\n\t * Travels with the error because a `Response` is read once: without this field\n\t * a plugin extracting the domain failure code would read an already-drained\n\t * stream and get nothing. Core does not look into the body; it carries it.\n\t */\n\tbody?: unknown;\n\t/** The original error, when this one wraps it. */\n\tcause?: unknown;\n}\n\n/**\n * One failure type for every layer of the framework.\n *\n * Without a tagged kind, an app cannot tell a network failure from a timeout,\n * from a broken schema, from a deliberate domain refusal — so it builds its own\n * error contract ON TOP of the framework, and builds it once per application.\n *\n * Implements `ILankaApiError`: `status` and `errors` are present, so code reading\n * them the old way keeps working.\n *\n * What is NOT here: parsing a particular response body format. Which JSON a\n * backend sends is policy, and policy lives in `@lankajs/plugin-http`. Core knows\n * only that it has a failure and must name its kind.\n */\nexport class LankaError extends Error implements ILankaApiError {\n\treadonly kind: TLankaErrorKind;\n\treadonly status?: number;\n\treadonly code?: string;\n\treadonly issues?: readonly string[];\n\t/**\n\t * The failures with an input to be shown at. See `ILankaErrorInit.fields`.\n\t *\n\t * The array given is the array kept — unlike `errors`, which copies `issues`\n\t * on every read. `readonly` carries the promise, and this is read on the way\n\t * to a form, once, where a copy would buy nothing.\n\t */\n\treadonly fields?: readonly ILankaFieldError[];\n\t/** The parsed error response body; core carries it without reading it. */\n\treadonly body?: unknown;\n\n\tconstructor(init: ILankaErrorInit) {\n\t\tsuper(init.message, init.cause === undefined ? undefined : { cause: init.cause });\n\t\tthis.name = \"LankaError\";\n\t\tthis.kind = init.kind;\n\t\tthis.status = init.status;\n\t\tthis.code = init.code;\n\t\tthis.issues = init.issues;\n\t\tthis.fields = init.fields;\n\t\tthis.body = init.body;\n\n\t\t// The prototype chain is pinned explicitly: without it `instanceof` breaks\n\t\t// for a consumer whose bundler downlevels to ES5, and breaks silently —\n\t\t// `catch` simply fails to recognise its own error.\n\t\tObject.setPrototypeOf(this, LankaError.prototype);\n\t}\n\n\t/** The `ILankaApiError` field. The details are the message list. */\n\tget errors(): string[] | undefined {\n\t\treturn this.issues ? [...this.issues] : undefined;\n\t}\n\n\t/**\n\t * Whether to show this to the user.\n\t *\n\t * A cancelled request is the one failure that must NOT be reported: its cause\n\t * is that the user left before the response, and a toast would catch them on\n\t * another screen.\n\t */\n\tget isSilent(): boolean {\n\t\treturn this.kind === \"aborted\";\n\t}\n\n\t/**\n\t * Recognises our error among others.\n\t *\n\t * Checks the name rather than `instanceof` alone: a consumer may end up with\n\t * two copies of the package — adjacent majors in the dependency tree — and\n\t * then `instanceof` answers `false` for an error that is ours by every other\n\t * sign.\n\t */\n\tstatic is(value: unknown): value is LankaError {\n\t\treturn (\n\t\t\tvalue instanceof LankaError || (value instanceof Error && value.name === \"LankaError\")\n\t\t);\n\t}\n}\n"],"mappings":";AAmEO,IAAM,aAAN,MAAM,oBAAmB,MAAgC;AAAA,EACtD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA;AAAA;AAAA,EAEA;AAAA,EAET,YAAY,MAAuB;AAClC,UAAM,KAAK,SAAS,KAAK,UAAU,SAAY,SAAY,EAAE,OAAO,KAAK,MAAM,CAAC;AAChF,SAAK,OAAO;AACZ,SAAK,OAAO,KAAK;AACjB,SAAK,SAAS,KAAK;AACnB,SAAK,OAAO,KAAK;AACjB,SAAK,SAAS,KAAK;AACnB,SAAK,SAAS,KAAK;AACnB,SAAK,OAAO,KAAK;AAKjB,WAAO,eAAe,MAAM,YAAW,SAAS;AAAA,EACjD;AAAA;AAAA,EAGA,IAAI,SAA+B;AAClC,WAAO,KAAK,SAAS,CAAC,GAAG,KAAK,MAAM,IAAI;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,IAAI,WAAoB;AACvB,WAAO,KAAK,SAAS;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,OAAO,GAAG,OAAqC;AAC9C,WACC,iBAAiB,eAAe,iBAAiB,SAAS,MAAM,SAAS;AAAA,EAE3E;AACD;","names":[]}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import {
|
|
2
|
+
LankaValidationError
|
|
3
|
+
} from "./chunk-CRIRTOLB.js";
|
|
4
|
+
|
|
5
|
+
// src/validation/lanka-standard-validator/lankaStandardValidator.ts
|
|
6
|
+
function readIssuePath(issue) {
|
|
7
|
+
const segments = [];
|
|
8
|
+
for (const segment of issue.path ?? []) {
|
|
9
|
+
const key = typeof segment === "object" && segment !== null && "key" in segment ? segment.key : segment;
|
|
10
|
+
segments.push(typeof key === "number" ? key : String(key));
|
|
11
|
+
}
|
|
12
|
+
return segments;
|
|
13
|
+
}
|
|
14
|
+
function describeIssue(issue) {
|
|
15
|
+
const path = readIssuePath(issue).join(".");
|
|
16
|
+
return path ? `${path}: ${issue.message}` : issue.message;
|
|
17
|
+
}
|
|
18
|
+
function toFieldError(issue) {
|
|
19
|
+
return { path: readIssuePath(issue), message: issue.message };
|
|
20
|
+
}
|
|
21
|
+
function isStandardSchema(schema) {
|
|
22
|
+
if (schema === null || schema === void 0) return false;
|
|
23
|
+
return (typeof schema === "object" || typeof schema === "function") && "~standard" in schema;
|
|
24
|
+
}
|
|
25
|
+
function runSync(schema, data) {
|
|
26
|
+
if (!isStandardSchema(schema)) {
|
|
27
|
+
throw new LankaValidationError(
|
|
28
|
+
"This is not a Standard Schema: the value has no `~standard` property. Either it is not a schema at all, or it belongs to a library that needs its own package from `modules/validators/` \u2014 yup, TypeBox and Effect each do.",
|
|
29
|
+
[]
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
const result = schema["~standard"].validate(data);
|
|
33
|
+
if (result instanceof Promise) {
|
|
34
|
+
throw new LankaValidationError(
|
|
35
|
+
"The schema is asynchronous and the validation port is synchronous. Parse such data by hand: passing it silently would be worse than failing. If this is a yup schema, every one of them is asynchronous \u2014 use `@lankajs/yup`, which validates them synchronously.",
|
|
36
|
+
[]
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
var lankaStandardValidator = Object.freeze({
|
|
42
|
+
validate(schema, data, context) {
|
|
43
|
+
const result = runSync(schema, data);
|
|
44
|
+
if (result.issues) {
|
|
45
|
+
throw new LankaValidationError(
|
|
46
|
+
`Validation failed for ${context}`,
|
|
47
|
+
result.issues.map(describeIssue),
|
|
48
|
+
result.issues.map(toFieldError)
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
return result.value;
|
|
52
|
+
},
|
|
53
|
+
validateSafe(schema, data) {
|
|
54
|
+
const result = runSync(schema, data);
|
|
55
|
+
if (result.issues) {
|
|
56
|
+
return {
|
|
57
|
+
success: false,
|
|
58
|
+
errors: result.issues.map(describeIssue),
|
|
59
|
+
fields: result.issues.map(toFieldError)
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
return { success: true, data: result.value };
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
export {
|
|
67
|
+
lankaStandardValidator
|
|
68
|
+
};
|
|
69
|
+
//# sourceMappingURL=chunk-HVSQOMRE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/validation/lanka-standard-validator/lankaStandardValidator.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { LankaValidationError } from \"../lanka-validation-error/LankaValidationError\";\nimport type { TLankaValidationResult } from \"../_types/TLankaValidationResult\";\nimport type { ILankaFieldError } from \"../../errors/_interfaces/ILankaFieldError\";\n\n/**\n * Response body validation with any Standard Schema implementation.\n *\n * An abstraction typed by one library cannot be implemented by another, which is\n * the whole reason a validation port exists. Standard Schema is the shared\n * interface implemented by zod 4, valibot, arktype and others.\n *\n * `@standard-schema/spec` contains TYPES ONLY and adds no bytes to a build, so it\n * is a regular dependency rather than another peer.\n *\n * No adapter classes: a schema describes itself, and\n * `schema[\"~standard\"].validate(data)` is the whole protocol.\n */\n\n/** A schema the port understands: any Standard Schema implementation. */\nexport type TLankaSchema<TOutput = unknown> = StandardSchemaV1<unknown, TOutput>;\n\nexport interface ILankaValidator {\n\t/** Validates and returns the parsed value, or throws. */\n\tvalidate<TOutput>(schema: TLankaSchema<TOutput>, data: unknown, context: string): TOutput;\n\t/** Validates and returns an outcome, throwing nothing. */\n\tvalidateSafe<TOutput>(\n\t\tschema: TLankaSchema<TOutput>,\n\t\tdata: unknown,\n\t): TLankaValidationResult<TOutput>;\n}\n\n/**\n * An issue's path as segments: `[\"items\", 0, \"id\"]`.\n *\n * Standard Schema allows a segment to be a key or an object carrying one; a\n * form addresses by the key. Numbers stay numbers — an index is not a name —\n * and anything else becomes the string a form library expects.\n *\n * A LOOP rather than `.map`, and neither is a style choice.\n *\n * `.map` preserves an Array SUBCLASS — arktype returns a `ReadonlyPath` carrying\n * a cache of stringified ancestors — so the library's internal came back attached\n * to a path this port promises is segments. It printed identically to a plain\n * array and compared unequal, which is the worst shape a wrong answer can take.\n *\n * `Array.from(path, fn)` fixes that and costs: measured on the zod bench's\n * refusal row, twice each, it took the path from 56 yardsticks to 87-92 — a\n * 1.6x regression on the row a form hits on every keystroke before the last one.\n * Pushing into a literal is both correct and free: 53-55 yardsticks, at or under\n * where the path started. Correctness did not have to be paid for here, and a\n * comment saying \"`Array.from` for correctness\" would have hidden that it was.\n */\nfunction readIssuePath(issue: StandardSchemaV1.Issue): (string | number)[] {\n\tconst segments: (string | number)[] = [];\n\n\tfor (const segment of issue.path ?? []) {\n\t\tconst key =\n\t\t\ttypeof segment === \"object\" && segment !== null && \"key\" in segment\n\t\t\t\t? segment.key\n\t\t\t\t: segment;\n\n\t\tsegments.push(typeof key === \"number\" ? key : String(key));\n\t}\n\n\treturn segments;\n}\n\n/**\n * A field path plus a message, for a banner.\n *\n * The path is assembled WHOLE — `items.0.id`, not `id`. Without the index and the\n * parent the message points nowhere when the list has twenty items.\n */\nfunction describeIssue(issue: StandardSchemaV1.Issue): string {\n\tconst path = readIssuePath(issue).join(\".\");\n\n\treturn path ? `${path}: ${issue.message}` : issue.message;\n}\n\n/** The same issue for a form: the path kept in segments, the message beside it. */\nfunction toFieldError(issue: StandardSchemaV1.Issue): ILankaFieldError {\n\treturn { path: readIssuePath(issue), message: issue.message };\n}\n\n/**\n * Whether the value speaks the protocol at all.\n *\n * A schema may be a FUNCTION — arktype's is callable, with `~standard` on its\n * prototype — so the guard asks for the marker and nothing else. The\n * specification says what a schema CARRIES, never what it is made of, and a\n * guard reading `typeof schema === \"object\"` refuses arktype while the port\n * claims to accept it.\n */\nfunction isStandardSchema(schema: unknown): boolean {\n\tif (schema === null || schema === undefined) return false;\n\n\treturn (typeof schema === \"object\" || typeof schema === \"function\") && \"~standard\" in schema;\n}\n\nfunction runSync<TOutput>(\n\tschema: TLankaSchema<TOutput>,\n\tdata: unknown,\n): StandardSchemaV1.Result<TOutput> {\n\t// An application whose schemas come from two libraries eventually hands one to\n\t// the wrong validator. Unguarded, that read `undefined.validate` and produced\n\t// \"Cannot read properties of undefined\" — an error naming neither the schema,\n\t// nor the port, nor what to do about it.\n\t//\n\t// It throws from `validateSafe` too, deliberately: a refused VALUE is an\n\t// outcome a form renders, while a schema the port cannot read is a wiring\n\t// mistake, and putting it in `errors` would show a programmer's error to a\n\t// user beside an input.\n\tif (!isStandardSchema(schema)) {\n\t\tthrow new LankaValidationError(\n\t\t\t\"This is not a Standard Schema: the value has no `~standard` property. \" +\n\t\t\t\t\"Either it is not a schema at all, or it belongs to a library that \" +\n\t\t\t\t\"needs its own package from `modules/validators/` — yup, TypeBox and \" +\n\t\t\t\t\"Effect each do.\",\n\t\t\t[],\n\t\t);\n\t}\n\n\tconst result = schema[\"~standard\"].validate(data);\n\n\t// Standard Schema allows returning a promise. A synchronous port cannot await\n\t// it, and answering \"fine\" would let UNVALIDATED data through — a check that\n\t// cannot fail reporting success.\n\tif (result instanceof Promise) {\n\t\tthrow new LankaValidationError(\n\t\t\t\"The schema is asynchronous and the validation port is synchronous. Parse \" +\n\t\t\t\t\"such data by hand: passing it silently would be worse than failing. \" +\n\t\t\t\t// The commonest cause by a distance, and the one where \"parse it by\n\t\t\t\t// hand\" is the wrong advice: yup implements Standard Schema with an\n\t\t\t\t// `async validate`, so EVERY yup schema lands here. Naming it turns a\n\t\t\t\t// dead end into an install.\n\t\t\t\t\"If this is a yup schema, every one of them is asynchronous — use \" +\n\t\t\t\t\"`@lankajs/yup`, which validates them synchronously.\",\n\t\t\t[],\n\t\t);\n\t}\n\n\treturn result;\n}\n\nexport const lankaStandardValidator: ILankaValidator = Object.freeze<ILankaValidator>({\n\tvalidate<TOutput>(schema: TLankaSchema<TOutput>, data: unknown, context: string): TOutput {\n\t\tconst result = runSync(schema, data);\n\n\t\tif (result.issues) {\n\t\t\tthrow new LankaValidationError(\n\t\t\t\t`Validation failed for ${context}`,\n\t\t\t\tresult.issues.map(describeIssue),\n\t\t\t\tresult.issues.map(toFieldError),\n\t\t\t);\n\t\t}\n\n\t\treturn result.value;\n\t},\n\n\tvalidateSafe<TOutput>(\n\t\tschema: TLankaSchema<TOutput>,\n\t\tdata: unknown,\n\t): TLankaValidationResult<TOutput> {\n\t\tconst result = runSync(schema, data);\n\n\t\tif (result.issues) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terrors: result.issues.map(describeIssue),\n\t\t\t\tfields: result.issues.map(toFieldError),\n\t\t\t};\n\t\t}\n\n\t\treturn { success: true, data: result.value };\n\t},\n});\n"],"mappings":";;;;;AAqDA,SAAS,cAAc,OAAoD;AAC1E,QAAM,WAAgC,CAAC;AAEvC,aAAW,WAAW,MAAM,QAAQ,CAAC,GAAG;AACvC,UAAM,MACL,OAAO,YAAY,YAAY,YAAY,QAAQ,SAAS,UACzD,QAAQ,MACR;AAEJ,aAAS,KAAK,OAAO,QAAQ,WAAW,MAAM,OAAO,GAAG,CAAC;AAAA,EAC1D;AAEA,SAAO;AACR;AAQA,SAAS,cAAc,OAAuC;AAC7D,QAAM,OAAO,cAAc,KAAK,EAAE,KAAK,GAAG;AAE1C,SAAO,OAAO,GAAG,IAAI,KAAK,MAAM,OAAO,KAAK,MAAM;AACnD;AAGA,SAAS,aAAa,OAAiD;AACtE,SAAO,EAAE,MAAM,cAAc,KAAK,GAAG,SAAS,MAAM,QAAQ;AAC7D;AAWA,SAAS,iBAAiB,QAA0B;AACnD,MAAI,WAAW,QAAQ,WAAW,OAAW,QAAO;AAEpD,UAAQ,OAAO,WAAW,YAAY,OAAO,WAAW,eAAe,eAAe;AACvF;AAEA,SAAS,QACR,QACA,MACmC;AAUnC,MAAI,CAAC,iBAAiB,MAAM,GAAG;AAC9B,UAAM,IAAI;AAAA,MACT;AAAA,MAIA,CAAC;AAAA,IACF;AAAA,EACD;AAEA,QAAM,SAAS,OAAO,WAAW,EAAE,SAAS,IAAI;AAKhD,MAAI,kBAAkB,SAAS;AAC9B,UAAM,IAAI;AAAA,MACT;AAAA,MAQA,CAAC;AAAA,IACF;AAAA,EACD;AAEA,SAAO;AACR;AAEO,IAAM,yBAA0C,OAAO,OAAwB;AAAA,EACrF,SAAkB,QAA+B,MAAe,SAA0B;AACzF,UAAM,SAAS,QAAQ,QAAQ,IAAI;AAEnC,QAAI,OAAO,QAAQ;AAClB,YAAM,IAAI;AAAA,QACT,yBAAyB,OAAO;AAAA,QAChC,OAAO,OAAO,IAAI,aAAa;AAAA,QAC/B,OAAO,OAAO,IAAI,YAAY;AAAA,MAC/B;AAAA,IACD;AAEA,WAAO,OAAO;AAAA,EACf;AAAA,EAEA,aACC,QACA,MACkC;AAClC,UAAM,SAAS,QAAQ,QAAQ,IAAI;AAEnC,QAAI,OAAO,QAAQ;AAClB,aAAO;AAAA,QACN,SAAS;AAAA,QACT,QAAQ,OAAO,OAAO,IAAI,aAAa;AAAA,QACvC,QAAQ,OAAO,OAAO,IAAI,YAAY;AAAA,MACvC;AAAA,IACD;AAEA,WAAO,EAAE,SAAS,MAAM,MAAM,OAAO,MAAM;AAAA,EAC5C;AACD,CAAC;","names":[]}
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "./chunk-J5LA3Q6D.js";
|
|
5
5
|
import {
|
|
6
6
|
LankaError
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-G32H73QY.js";
|
|
8
8
|
import {
|
|
9
9
|
getLankaHost
|
|
10
10
|
} from "./chunk-RKYKK6MN.js";
|
|
@@ -49,4 +49,4 @@ export {
|
|
|
49
49
|
createLankaApiError,
|
|
50
50
|
handleLankaApiError
|
|
51
51
|
};
|
|
52
|
-
//# sourceMappingURL=chunk-
|
|
52
|
+
//# sourceMappingURL=chunk-NDCJBO47.js.map
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import {
|
|
2
|
+
lankaScenarioBootstrap
|
|
3
|
+
} from "./chunk-B7EYIAW7.js";
|
|
1
4
|
import {
|
|
2
5
|
LankaGatewayLocator,
|
|
3
6
|
LankaScenarioLocator,
|
|
4
7
|
createLankaScope
|
|
5
8
|
} from "./chunk-5MAQVBI2.js";
|
|
6
|
-
import {
|
|
7
|
-
lankaScenarioBootstrap
|
|
8
|
-
} from "./chunk-UGXSGQPW.js";
|
|
9
9
|
import {
|
|
10
10
|
LankaScenarioVMRegistry,
|
|
11
11
|
LankaScenariosRegistry
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
} from "./chunk-BGVDPDX4.js";
|
|
30
30
|
|
|
31
31
|
// src/scenario/event-bus/lanka-event-bus-instance/LankaEventBusInstance.ts
|
|
32
|
+
var isThenable = (value) => typeof value?.then === "function";
|
|
32
33
|
function resolveReplayCount(request) {
|
|
33
34
|
if (request === void 0 || request === false) return 0;
|
|
34
35
|
if (request === true || request === "last") return 1;
|
|
@@ -226,7 +227,15 @@ var LankaEventBusInstance = class {
|
|
|
226
227
|
const deliver = () => {
|
|
227
228
|
for (const entry of [...subs]) {
|
|
228
229
|
try {
|
|
229
|
-
entry.callback(data);
|
|
230
|
+
const settled = entry.callback(data);
|
|
231
|
+
if (isThenable(settled)) {
|
|
232
|
+
settled.then(void 0, (err) => {
|
|
233
|
+
lankaLogger.printScenarioLog(
|
|
234
|
+
`Error dispatching event "${eventType}":`,
|
|
235
|
+
err
|
|
236
|
+
);
|
|
237
|
+
});
|
|
238
|
+
}
|
|
230
239
|
} catch (err) {
|
|
231
240
|
lankaLogger.printScenarioLog(`Error dispatching event "${eventType}":`, err);
|
|
232
241
|
}
|
|
@@ -720,4 +729,4 @@ export {
|
|
|
720
729
|
resetActiveLanka,
|
|
721
730
|
ALankaPlugin
|
|
722
731
|
};
|
|
723
|
-
//# sourceMappingURL=chunk-
|
|
732
|
+
//# sourceMappingURL=chunk-UBGXDTXC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/scenario/event-bus/lanka-event-bus-instance/LankaEventBusInstance.ts","../src/bootstrap/_internal/create-lanka-plugin-registry/createLankaPluginRegistry.ts","../src/bootstrap/_factories/create-lanka/createLanka.ts","../src/bootstrap/start-lanka/startLanka.ts","../src/bootstrap/reset-active-lanka/resetActiveLanka.ts","../src/bootstrap/_abstractions/lanka-plugin/ALankaPlugin.ts"],"sourcesContent":["import { ILankaEventMetadata } from \"../../_interfaces/ILankaEventMetadata\";\nimport { ILankaEventLog } from \"../../_interfaces/ILankaEventLog\";\nimport {\n\tTLankaEventBusDecision,\n\tTLankaEventBusMiddleware,\n} from \"../../_types/TLankaEventBusMiddleware\";\nimport { TLankaEventBusObserver } from \"../../_types/TLankaEventBusObserver\";\nimport { ILankaEventBusOutcome } from \"../../_interfaces/ILankaEventBusOutcome\";\nimport { lankaLogger } from \"../../../logger/lanka-logger/LankaLogger\";\n\n/**\n * Whether a handler's return value is something that can still reject.\n *\n * Duck-typed rather than `instanceof Promise`, because the promise a handler\n * returns need not be THIS realm's: a jsdom test, a native module and a bundled\n * polyfill each bring their own, and `instanceof` answers false for all of them.\n * An `instanceof` check here would work everywhere except the places most likely\n * to need it.\n */\nconst isThenable = (value: unknown): value is PromiseLike<unknown> =>\n\ttypeof (value as PromiseLike<unknown> | null)?.then === \"function\";\n\n/** One subscription: the callback plus everything the bus knows about it. */\ntype TSubscriptionEntry<T = unknown> = {\n\tcallback: (data: T) => void;\n\tpriority: number;\n\t/**\n\t * How many recent values this subscriber asks to be replayed on subscribe.\n\t *\n\t * A number rather than a flag: \"the whole buffer\" can be a hundred events in a\n\t * row, which is almost never what \"give me the current state\" means.\n\t */\n\treplayCount: number;\n};\n\n/** State of one registered event type. */\ntype TEventState = {\n\tmeta: ILankaEventMetadata;\n\tlogs: ILankaEventLog[];\n\tbuffer: unknown[];\n\tsubs: TSubscriptionEntry[];\n};\n\n/**\n * How many recent values to replay to a new subscriber.\n *\n * `true` is kept and means \"the last one\".\n */\nexport type TLankaReplayRequest = boolean | \"last\" | number;\n\nfunction resolveReplayCount(request: TLankaReplayRequest | undefined): number {\n\tif (request === undefined || request === false) return 0;\n\tif (request === true || request === \"last\") return 1;\n\treturn Math.max(0, Math.trunc(request));\n}\n\nfunction maxReplayDepth(subs: readonly TSubscriptionEntry[]): number {\n\tlet depth = 0;\n\tfor (const entry of subs) if (entry.replayCount > depth) depth = entry.replayCount;\n\treturn depth;\n}\n\n// -----------------------------------------------------------------------------\n// Bus implementation\n// -----------------------------------------------------------------------------\n\nexport class LankaEventBusInstance {\n\t/** Event registry and per-event state — private to each instance. */\n\tprivate registry: Map<string, TEventState> = new Map();\n\n\t/** Middleware registered on this instance. */\n\tprivate middlewares: TLankaEventBusMiddleware<unknown>[] = [];\n\n\t/**\n\t * What is watching the OUTCOME of each dispatch — see `TLankaEventBusObserver`.\n\t *\n\t * An array rather than a set for one reason: `dispatch` reads `.length` on\n\t * every event, and that check is what keeps an unobserved bus paying nothing.\n\t */\n\tprivate observers: TLankaEventBusObserver[] = [];\n\n\t/** Default maximum number of logs kept per event type. */\n\tprivate defaultMaxLogs = 100;\n\n\t/** Internal timers for replay batching (avoid multiple replays in the same tick). */\n\tprivate pendingReplayTimers: Map<string, ReturnType<typeof setTimeout>> = new Map();\n\n\t/** Whether event logging is enabled. */\n\tprivate isEnableLogs = false;\n\n\t/**\n\t * How many events this bus has logged, ever — the order records are read in.\n\t *\n\t * Incremented only while logging is on, which is off by default, so a bus\n\t * nobody is inspecting pays nothing for it.\n\t */\n\tprivate dispatchCount = 0;\n\n\t// -------------------------------------------------------------------------\n\t// Configuration\n\t// -------------------------------------------------------------------------\n\n\t/** Configures the bus, e.g. logging. Called once at application start. */\n\tpublic enableLogs(): void {\n\t\tthis.isEnableLogs = true;\n\t}\n\tpublic disableLogs(): void {\n\t\tthis.isEnableLogs = false;\n\t}\n\n\t// -------------------------------------------------------------------------\n\t// Internal\n\t// -------------------------------------------------------------------------\n\n\t/**\n\t * Ensures the event is registered, registering it if not.\n\t *\n\t * @param eventType Event type\n\t * @param usedBy Who uses it — for the inspector and for finding an owner\n\t */\n\tprivate ensureEvent(eventType: string, usedBy?: string): TEventState {\n\t\tlet state = this.registry.get(eventType);\n\t\tif (!state) {\n\t\t\tconst meta: ILankaEventMetadata = {\n\t\t\t\tdataType: \"any\",\n\t\t\t\tdescription: `Auto-registered event: ${eventType}`,\n\t\t\t\tusedBy: usedBy ? [usedBy] : [],\n\t\t\t\tpriority: 0,\n\t\t\t\tmaxLogs: this.defaultMaxLogs,\n\t\t\t};\n\t\t\tstate = {\n\t\t\t\tmeta,\n\t\t\t\tlogs: [],\n\t\t\t\tbuffer: [],\n\t\t\t\tsubs: [],\n\t\t\t};\n\t\t\tthis.registry.set(eventType, state);\n\t\t} else if (usedBy) {\n\t\t\tconst meta = state.meta;\n\t\t\tmeta.usedBy = [...new Set([...(meta.usedBy ?? []), usedBy])];\n\t\t}\n\t\treturn state;\n\t}\n\n\t// -------------------------------------------------------------------------\n\t// Public surface\n\t// -------------------------------------------------------------------------\n\n\t/**\n\t * Declares an event explicitly, together with its metadata.\n\t *\n\t * @param eventType Event type\n\t * @param metadata What is known about the event and what it is allowed\n\t */\n\tpublic registerEvent(eventType: string, metadata: ILankaEventMetadata): void {\n\t\t// MERGE, never recreate the state.\n\t\t//\n\t\t// Writing `subs: []` here means a call after anyone subscribed silently\n\t\t// drops every subscriber. Bootstrap order (register before ViewModels\n\t\t// initialise) hides it, but that is a coincidence, not an invariant: any\n\t\t// lazy event registration breaks it, and the only symptom is that nothing\n\t\t// works.\n\t\tconst existing = this.registry.get(eventType);\n\t\tconst meta: ILankaEventMetadata = {\n\t\t\tdataType: metadata.dataType ?? existing?.meta.dataType ?? \"any\",\n\t\t\tdescription:\n\t\t\t\tmetadata.description ??\n\t\t\t\texisting?.meta.description ??\n\t\t\t\t`Registered event: ${eventType}`,\n\t\t\t// `usedBy` lists everyone using the event and exists precisely to be\n\t\t\t// complete. Replacing it would erase half.\n\t\t\tusedBy: [...new Set([...(existing?.meta.usedBy ?? []), ...(metadata.usedBy ?? [])])],\n\t\t\tschema: metadata.schema ?? existing?.meta.schema,\n\t\t\tpriority: metadata.priority ?? existing?.meta.priority ?? 0,\n\t\t\tmaxLogs: metadata.maxLogs ?? existing?.meta.maxLogs ?? this.defaultMaxLogs,\n\t\t\treplay: metadata.replay ?? existing?.meta.replay,\n\t\t};\n\n\t\tif (existing) {\n\t\t\texisting.meta = meta;\n\t\t\treturn;\n\t\t}\n\n\t\tthis.registry.set(eventType, { meta, logs: [], buffer: [], subs: [] });\n\t}\n\n\t/** Every registered event with its metadata. */\n\tpublic getRegisteredEvents() {\n\t\treturn Array.from(this.registry.entries())\n\t\t\t.map(([eventType, state]) => ({\n\t\t\t\teventType,\n\t\t\t\tmetadata: state.meta,\n\t\t\t}))\n\t\t\t.sort((a, b) => a.eventType.localeCompare(b.eventType));\n\t}\n\n\t/** Metadata of one event type. */\n\tpublic getEventInfo(eventType: string): ILankaEventMetadata | null {\n\t\treturn this.registry.get(eventType)?.meta ?? null;\n\t}\n\n\t/** How many live subscriptions this event has. */\n\t/**\n\t * How many values sit in the event's buffer.\n\t *\n\t * Exists for the test: \"the buffer stays empty until someone asks for replay\"\n\t * is a claim about MEMORY, and nothing but asking can check it.\n\t */\n\tpublic getBufferedCount(eventType: string): number {\n\t\treturn this.registry.get(eventType)?.buffer.length ?? 0;\n\t}\n\n\tpublic getSubscriptions(eventType: string): number {\n\t\treturn this.registry.get(eventType)?.subs.length ?? 0;\n\t}\n\n\t/**\n\t * Subscribes a handler to an event type.\n\t *\n\t * @param eventType Event type\n\t * @param callback What to run on dispatch\n\t * @param options Subscription options:\n\t * - `priority` — execution order, higher runs earlier;\n\t * - `replay` — receive what was dispatched before subscribing;\n\t * - `usedBy` — who subscribed, for the inspector.\n\t */\n\tpublic subscribe<T>(\n\t\teventType: string,\n\t\tcallback: (data: T) => void,\n\t\toptions?: {\n\t\t\tpriority?: number;\n\t\t\treplay?: TLankaReplayRequest;\n\t\t\tusedBy?: string;\n\t\t},\n\t): () => void {\n\t\tconst state = this.ensureEvent(eventType, options?.usedBy);\n\t\tconst prio = options?.priority ?? state.meta.priority ?? 0;\n\t\tconst entry: TSubscriptionEntry<unknown> = {\n\t\t\tcallback: callback as unknown as (data: unknown) => void,\n\t\t\tpriority: prio,\n\t\t\treplayCount: resolveReplayCount(options?.replay),\n\t\t};\n\n\t\tconst subs = state.subs;\n\t\tlet low = 0,\n\t\t\thigh = subs.length;\n\t\twhile (low < high) {\n\t\t\tconst mid = (low + high) >>> 1;\n\t\t\tif (subs[mid].priority < prio) high = mid;\n\t\t\telse low = mid + 1;\n\t\t}\n\t\tsubs.splice(low, 0, entry);\n\n\t\tif (options?.replay) this.scheduleBatchedReplay(eventType, state);\n\n\t\t// The returned function removes EXACTLY this record, not the first one with\n\t\t// the same callback: by callback, two subscriptions of one function are\n\t\t// indistinguishable.\n\t\treturn () => {\n\t\t\tconst at = state.subs.indexOf(entry);\n\t\t\tif (at !== -1) state.subs.splice(at, 1);\n\t\t};\n\t}\n\n\t/**\n\t * Removes a handler from an event type.\n\t *\n\t * @param eventType Event type\n\t * @param callback A previously subscribed handler\n\t */\n\tpublic unsubscribe<T>(eventType: string, callback: (data: T) => void): void {\n\t\tconst state = this.registry.get(eventType);\n\t\tif (!state) return;\n\t\tconst subs = state.subs;\n\t\tfor (let i = 0; i < subs.length; i++) {\n\t\t\tif (subs[i].callback === callback) {\n\t\t\t\tsubs.splice(i, 1);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Dispatches an event: the middleware chain first, then subscribers.\n\t *\n\t * @param eventType Event type\n\t * @param data Payload\n\t * @param usedBy Who dispatched — recorded in the metadata\n\t */\n\tpublic dispatch<T>(eventType: string, data?: T, usedBy?: string): void {\n\t\tconst state = this.ensureEvent(eventType, usedBy);\n\t\tconst { meta, subs, buffer } = state;\n\n\t\t// Payload shape check\n\t\tif (meta.schema && data !== undefined && !meta.schema(data)) {\n\t\t\tlankaLogger.printScenarioLog(`Invalid data for event \"${eventType}\":`, data);\n\t\t\tthis.report({ eventType, outcome: \"invalid\", subscribers: subs.length });\n\t\t\treturn;\n\t\t}\n\n\t\tthis.logEvent(state, eventType, data);\n\n\t\tconst deliver = (): void => {\n\t\t\t// Iterate a COPY. A subscriber may unsubscribe inside its own handler —\n\t\t\t// \"waited for the session, then unsubscribed\" is a routine pattern — and\n\t\t\t// `unsubscribe` splices this same array, so iterating the original skips\n\t\t\t// a neighbour silently.\n\t\t\t//\n\t\t\t// The deliberate side effect: subscribing DURING delivery waits for the\n\t\t\t// next event. Otherwise delivery order would depend on subscription\n\t\t\t// timing, which is undefined.\n\t\t\tfor (const entry of [...subs]) {\n\t\t\t\ttry {\n\t\t\t\t\t// A handler is typed `(data) => void`, and TypeScript assigns a\n\t\t\t\t\t// `Promise<void>` to a void return position — so `async () => { await\n\t\t\t\t\t// refetch(); }` is a handler that compiles with nothing to warn about,\n\t\t\t\t\t// and \"refetch when the stream reconnects\" is the common shape rather\n\t\t\t\t\t// than an exotic one.\n\t\t\t\t\t//\n\t\t\t\t\t// Its failure arrives a microtask after this loop has finished, where\n\t\t\t\t\t// the `catch` below cannot reach it: on node's default an unhandled\n\t\t\t\t\t// rejection ends the PROCESS, and ends it inside whatever ran next.\n\t\t\t\t\t// The promise this bus already makes — one subscriber cannot take a\n\t\t\t\t\t// dispatch down with it — has to cover that handler too, or it only\n\t\t\t\t\t// holds for the handlers that happen to be synchronous.\n\t\t\t\t\tconst settled: unknown = entry.callback(data);\n\n\t\t\t\t\tif (isThenable(settled)) {\n\t\t\t\t\t\tsettled.then(undefined, (err: unknown) => {\n\t\t\t\t\t\t\tlankaLogger.printScenarioLog(\n\t\t\t\t\t\t\t\t`Error dispatching event \"${eventType}\":`,\n\t\t\t\t\t\t\t\terr,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} catch (err) {\n\t\t\t\t\tlankaLogger.printScenarioLog(`Error dispatching event \"${eventType}\":`, err);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\t// Every middleware RETURNS a decision. A `next()`-based shape means not\n\t\t// calling it makes the event vanish silently.\n\t\t//\n\t\t// An exception is the same case as \"did not call next\": it also stops\n\t\t// delivery, so it is handled the same way and names itself in the log,\n\t\t// instead of surfacing and breaking whoever dispatched.\n\t\tfor (const middleware of [...this.middlewares]) {\n\t\t\tlet decision: TLankaEventBusDecision;\n\t\t\ttry {\n\t\t\t\tdecision = middleware(eventType, data);\n\t\t\t} catch (err) {\n\t\t\t\tdecision = { stop: err instanceof Error ? err.message : String(err) };\n\t\t\t}\n\n\t\t\tif (decision !== \"pass\") {\n\t\t\t\tthis.markStopped(state, decision.stop);\n\t\t\t\tlankaLogger.printScenarioLog(\n\t\t\t\t\t`Event \"${eventType}\" stopped by middleware:`,\n\t\t\t\t\tdecision.stop,\n\t\t\t\t);\n\t\t\t\t// The one place that knows WHICH middleware stopped it. A middleware\n\t\t\t\t// cannot learn this about a middleware after itself, which is why an\n\t\t\t\t// inspector needed an observer rather than a sixth middleware.\n\t\t\t\tthis.report({\n\t\t\t\t\teventType,\n\t\t\t\t\toutcome: \"stopped\",\n\t\t\t\t\tsubscribers: subs.length,\n\t\t\t\t\tstoppedBy: decision.stop,\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// AFTER the chain, and that ordering is the guarantee.\n\t\t//\n\t\t// The buffer used to be filled before the middlewares ran, so an event the\n\t\t// chain STOPPED still sat in it — and the next subscriber asking for replay\n\t\t// received a payload no subscriber had been allowed to see, with nothing in\n\t\t// the bus log recording that delivery. Middleware is how an application\n\t\t// gates an event (an authorisation check, a privacy filter, a feature\n\t\t// flag); a gate the framework routes around is not a gate.\n\t\t//\n\t\t// The buffer exists ONLY on demand and holds exactly what the greediest\n\t\t// subscriber asked for. Buffering every payload unconditionally holds up to\n\t\t// a hundred payloads per event type until the end of the session — in an app\n\t\t// where user data arrives over SSE, that is personal data kept in memory\n\t\t// with no consumer.\n\t\tconst depth = Math.max(resolveReplayCount(meta.replay), maxReplayDepth(subs));\n\t\tif (depth > 0) {\n\t\t\tbuffer.push(data);\n\t\t\twhile (buffer.length > depth) buffer.shift();\n\t\t} else if (buffer.length > 0) {\n\t\t\t// The last replay asker unsubscribed — nothing left to hold.\n\t\t\tbuffer.length = 0;\n\t\t}\n\n\t\tdeliver();\n\t\tthis.report({ eventType, outcome: \"delivered\", subscribers: subs.length });\n\t}\n\n\t/** Adds middleware to the shared chain. */\n\tpublic addMiddleware<T>(mw: TLankaEventBusMiddleware<T>): void {\n\t\tthis.middlewares.push(mw as unknown as TLankaEventBusMiddleware<unknown>);\n\t}\n\n\t/** Removes previously added middleware. */\n\tpublic removeMiddleware<T>(mw: TLankaEventBusMiddleware<T>): void {\n\t\tconst i = this.middlewares.indexOf(mw as unknown as TLankaEventBusMiddleware<unknown>);\n\t\tif (i !== -1) this.middlewares.splice(i, 1);\n\t}\n\n\t/**\n\t * Watches what became of each dispatch. An extension point — the sixth.\n\t *\n\t * An observer never decides: its return value is ignored, and a throw from it\n\t * is logged rather than surfaced. A diagnostic tool that could stop an event\n\t * would make \"I disabled the inspector and it started working\" a sentence\n\t * somebody says.\n\t */\n\tpublic addObserver(observer: TLankaEventBusObserver): void {\n\t\tthis.observers.push(observer);\n\t}\n\n\t/** Removes a previously added observer. */\n\tpublic removeObserver(observer: TLankaEventBusObserver): void {\n\t\tconst i = this.observers.indexOf(observer);\n\t\tif (i !== -1) this.observers.splice(i, 1);\n\t}\n\n\t/**\n\t * Log of dispatched events, oldest first, `limit` most recent kept.\n\t *\n\t * ## Why the records are sorted rather than concatenated\n\t *\n\t * They are held per event TYPE, so that each type keeps its own `maxLogs`\n\t * and a chatty event cannot push a quiet one's history out. Concatenating\n\t * those lists puts every record of one type before every record of the next,\n\t * and `slice(-limit)` then answers the tail of whichever type the registry\n\t * happened to hold last — not the most recent events at all. On a bus with\n\t * one event type nothing looked wrong; on a real one the log was a\n\t * chronology that had never been in chronological order.\n\t *\n\t * @param eventType Limit to one type\n\t * @param limit How many recent records to return\n\t */\n\tpublic getEventLogs(eventType?: string, limit = 100): ILankaEventLog[] {\n\t\tif (eventType) {\n\t\t\treturn this.registry.get(eventType)?.logs.slice(-limit) ?? [];\n\t\t}\n\t\tconst all: ILankaEventLog[] = [];\n\t\tfor (const { logs } of this.registry.values()) all.push(...logs);\n\t\t// By `sequence`, not by `timestamp`: a timestamp has millisecond\n\t\t// resolution and a burst — an SSE storm, bootstrap — dispatches many\n\t\t// events inside one, so sorting by it would leave those in the order the\n\t\t// concatenation happened to produce, which is the defect itself.\n\t\tall.sort((left, right) => (left.sequence ?? 0) - (right.sequence ?? 0));\n\t\treturn all.slice(-limit);\n\t}\n\n\t/** Removes an event together with its subscriptions, log and buffer. */\n\tpublic clearEvent(eventType: string): void {\n\t\tthis.registry.delete(eventType);\n\t\tconst t = this.pendingReplayTimers.get(eventType);\n\t\tif (t) {\n\t\t\tclearTimeout(t);\n\t\t\tthis.pendingReplayTimers.delete(eventType);\n\t\t}\n\t}\n\n\t/** Removes every event and any scheduled replays. */\n\tpublic clearAllEvents(): void {\n\t\tthis.registry.clear();\n\t\tfor (const t of this.pendingReplayTimers.values()) clearTimeout(t);\n\t\tthis.pendingReplayTimers.clear();\n\t}\n\n\t/** Full bus reset: events, middleware and observers. */\n\tpublic reset(): void {\n\t\tthis.clearAllEvents();\n\t\tthis.middlewares = [];\n\t\tthis.observers = [];\n\t\tthis.isEnableLogs = false;\n\t}\n\n\t// -------------------------------------------------------------------------\n\t// Internal helpers\n\t// -------------------------------------------------------------------------\n\n\t/**\n\t * Tells the observers what became of a dispatch.\n\t *\n\t * The empty-list check is the whole reason a bus nobody watches pays nothing\n\t * for this: `dispatch` is a hot path, and an iteration over an empty array on\n\t * every event would be a cost paid by every application for a feature almost\n\t * none of them installs. Measured in `LankaEventBusInstance.bench.ts`.\n\t *\n\t * An observer that throws is logged and the next one still runs — the same\n\t * treatment a subscriber gets, and for the same reason: a diagnostic tool must\n\t * not be able to break the application it is diagnosing.\n\t */\n\tprivate report(outcome: ILankaEventBusOutcome): void {\n\t\tif (this.observers.length === 0) return;\n\n\t\tfor (const observer of [...this.observers]) {\n\t\t\ttry {\n\t\t\t\tobserver(outcome);\n\t\t\t} catch (err) {\n\t\t\t\tlankaLogger.printScenarioLog(\n\t\t\t\t\t`Event bus observer failed for \"${outcome.eventType}\":`,\n\t\t\t\t\terr,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Marks the last log record with the reason it was stopped.\n\t *\n\t * Writes where `logEvent` just put the event: a stop is not a separate\n\t * occurrence but this one's outcome.\n\t */\n\tprivate markStopped(state: TEventState, reason: string): void {\n\t\tconst last = state.logs.at(-1);\n\t\tif (last) last.stoppedBy = reason;\n\t}\n\n\t/** Add an event log entry for debugging/auditing. */\n\tprivate logEvent(state: TEventState, eventType: string, data: unknown): void {\n\t\tif (this.isEnableLogs) {\n\t\t\tconst cap = state.meta.maxLogs ?? this.defaultMaxLogs;\n\t\t\tconst logs = state.logs;\n\t\t\tthis.dispatchCount += 1;\n\t\t\tconst log: ILankaEventLog = {\n\t\t\t\teventType,\n\t\t\t\ttimestamp: new Date().toISOString(),\n\t\t\t\tdata,\n\t\t\t\tsequence: this.dispatchCount,\n\t\t\t};\n\t\t\tlog.stackTrace = new Error().stack;\n\t\t\tlogs.push(log);\n\t\t\tif (logs.length > cap) logs.shift();\n\t\t}\n\t}\n\n\t/**\n\t * Schedules replay for late subscribers that asked for it.\n\t *\n\t * Replays are batched into one tick: otherwise ten subscriptions to one event\n\t * would make ten separate passes over the same buffer.\n\t */\n\tprivate scheduleBatchedReplay(eventType: string, state: TEventState): void {\n\t\tif (this.pendingReplayTimers.has(eventType)) return;\n\n\t\tconst timer = setTimeout(() => {\n\t\t\tthis.pendingReplayTimers.delete(eventType);\n\t\t\tconst { buffer, subs } = state;\n\t\t\tif (!buffer.length) return;\n\n\t\t\tconst needReplay = subs.filter((s) => s.replayCount > 0);\n\t\t\tif (!needReplay.length) return;\n\n\t\t\tfor (const entry of needReplay) {\n\t\t\t\t// Each gets what IT asked for, not what the buffer happens to hold.\n\t\t\t\tfor (const payload of buffer.slice(-entry.replayCount)) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tentry.callback(payload);\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tlankaLogger.printScenarioLog(\n\t\t\t\t\t\t\t`Error dispatching event \"${eventType}\" (replay):`,\n\t\t\t\t\t\t\terr,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Replay is an event of subscribing, not a property of the subscriber: it\n\t\t\t// does not happen a second time.\n\t\t\tfor (const entry of needReplay) entry.replayCount = 0;\n\t\t}, 0);\n\n\t\tthis.pendingReplayTimers.set(eventType, timer);\n\t}\n}\n","import type { ILankaPlugin } from \"../../ILankaPlugin\";\nimport type { ILankaInstance } from \"../../_factories/create-lanka/createLanka\";\n\nexport interface ILankaPluginRegistry {\n\t/** Installs a plugin and returns the function that removes it. */\n\tuse: (plugin: ILankaPlugin) => () => void;\n\t/** Removes every plugin, newest first is NOT guaranteed — see `removeAll`. */\n\tremoveAll: () => void;\n}\n\n/**\n * The plugins installed on one framework instance.\n *\n * Owns three decisions that are easy to get subtly wrong and impossible to see\n * from the outside: a duplicate is refused, removal is idempotent, and removing\n * all of them iterates a COPY of the values.\n */\nexport const createLankaPluginRegistry = (instance: ILankaInstance): ILankaPluginRegistry => {\n\tconst removers = new Map<string, () => void>();\n\n\treturn {\n\t\tuse(plugin: ILankaPlugin): () => void {\n\t\t\tif (removers.has(plugin.name)) {\n\t\t\t\t// Rejected LOUDLY: two copies of a retry policy double the request\n\t\t\t\t// count, and that shows up only as backend load — much later, and to\n\t\t\t\t// somebody else.\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Plugin \"${plugin.name}\" is already registered on this lanka instance. ` +\n\t\t\t\t\t\t\"Registering twice almost always means two copies of one policy.\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst uninstall = plugin.install(instance);\n\n\t\t\tconst remove = (): void => {\n\t\t\t\t// Idempotent: a caller may hold the remover and also dispose the\n\t\t\t\t// instance, and uninstalling twice is how a plugin's own teardown ends\n\t\t\t\t// up running against a framework that is already gone.\n\t\t\t\tif (!removers.has(plugin.name)) return;\n\t\t\t\tremovers.delete(plugin.name);\n\t\t\t\tuninstall?.();\n\t\t\t};\n\n\t\t\tremovers.set(plugin.name, remove);\n\t\t\treturn remove;\n\t\t},\n\n\t\tremoveAll(): void {\n\t\t\t// A COPY: each remover deletes its own entry, and mutating the map being\n\t\t\t// iterated skips every second plugin.\n\t\t\tfor (const remove of [...removers.values()]) remove();\n\t\t\tremovers.clear();\n\t\t},\n\t};\n};\n","import { LankaEventBusInstance } from \"../../../scenario/event-bus/lanka-event-bus-instance/LankaEventBusInstance\";\nimport { createLankaPluginRegistry } from \"../../_internal/create-lanka-plugin-registry/createLankaPluginRegistry\";\nimport { LankaScenariosRegistry } from \"../../../scenario/_registries/lanka-scenarios-registry/LankaScenariosRegistry\";\nimport { LankaScenarioVMRegistry } from \"../../../scenario/_registries/lanka-scenario-vm-registry/LankaScenarioVMRegistry\";\nimport { lankaScenarioBootstrap } from \"../../../scenario/lanka-scenario-bootstrap/LankaScenarioBootstrap\";\nimport { LankaGatewayLocator } from \"../../../locator/gateway/lanka-gateway-locator/LankaGatewayLocator\";\nimport { LankaScenarioLocator } from \"../../../locator/scenario/lanka-scenario-locator/LankaScenarioLocator\";\nimport { LankaSingletonLocator } from \"../../../locator/singleton/lanka-singleton-locator/LankaSingletonLocator\";\nimport { LankaSharedStoreLocator } from \"../../../locator/shared-store/lanka-shared-store-locator/LankaSharedStoreLocator\";\nimport { createInFlightCounter } from \"../../../gateway/inflight/lankaHttpInFlight\";\nimport { lankaLogger } from \"../../../logger/lanka-logger/LankaLogger\";\nimport {\n\tgetActiveRuntime,\n\tsetActiveRuntime,\n} from \"../../../_internal/active-runtime/activeRuntime\";\nimport { createLankaScope } from \"../../../locator/_factories/create-lanka-scope/createLankaScope\";\nimport type { ILankaScope } from \"../../../locator/_factories/create-lanka-scope/createLankaScope\";\nimport type { ILankaPlugin } from \"../../ILankaPlugin\";\nimport type { ILankaRuntime } from \"../../../_internal/active-runtime/activeRuntime\";\nimport type { ILankaFlags } from \"../../../config/_interfaces/ILankaFlags\";\nimport type { ILankaHost } from \"../../../config/_interfaces/ILankaHost\";\nimport type { ILankaRuntimeConfig } from \"../../../config/_interfaces/ILankaRuntimeConfig\";\nimport type { ILankaScenarioVM } from \"../../../scenario/_interfaces/ILankaScenarioVM\";\nimport type { TLankaRequestMiddleware } from \"../../../gateway/request/lankaRequestMiddleware\";\n\n/**\n * A framework instance: all framework state in one object.\n *\n * What instance-scoped state buys: two apps in one process (micro-frontends,\n * Storybook beside the app) no longer share a bus, locator caches and mock mode;\n * SSR does not reuse state between different users' requests; and test isolation\n * rests on construction rather than on a global `beforeEach` reaching into\n * internal registries.\n *\n * **Ambient facades** — `lankaEventBus.dispatch`, `lankaSingletons.foo`,\n * `getLankaFlags()`, `lankaHttpInFlight` — resolve THE ONE active instance\n * (`internal/activeRuntime.ts`). Isolation belongs to whoever holds an instance;\n * a facade cannot offer it.\n *\n * One thing stays at module level deliberately: `ALankaScenario` collects\n * constructed scenarios into a static pool. That is a REGISTRY OF DEFINITIONS,\n * not runtime state — the classes come from one `@lanka_di/Scenarios` barrel and\n * both instances must see the same list. Splitting it would be divergence.\n */\nexport interface ILankaInstance extends ILankaRuntime {\n\t/**\n\t * Adds a wrapper around every request. Returns a function that removes it.\n\t *\n\t * This is how `@lankajs/plugin-http` installs retry, the idempotency key, the\n\t * CSRF header and auth refresh.\n\t */\n\tuseRequestMiddleware(middleware: TLankaRequestMiddleware): () => void;\n\t/** Default timeout for every request of this instance. */\n\tsetRequestTimeout(timeoutMs: number | undefined): void;\n\t/** Resolves a service in the root scope — for the instance's whole lifetime. */\n\tresolve<TInstance>(propertyName: string): TInstance;\n\t/**\n\t * Creates a scope: a lifetime shorter than the application's.\n\t *\n\t * An object created in a scope goes away with it.\n\t */\n\tcreateScope(): ILankaScope;\n\t/**\n\t * Registers a plugin. Returns a function that removes it.\n\t *\n\t * The fifth and last extension point. An extension point declared before\n\t * anything plugs into it describes an imagined need while costing real\n\t * support, so this one arrived with the FIRST plugin.\n\t */\n\tuse(plugin: ILankaPlugin): () => void;\n\t/** Runs services and the scenario layer. Idempotent. */\n\tbootstrap(config?: ILankaBootstrapConfig): Promise<void>;\n\t/** Has bootstrap already run? */\n\tisBootstrapped(): boolean;\n\t/** Makes this instance the one ambient facades resolve to. */\n\tactivate(): void;\n\t/**\n\t * Removes subscriptions, clears registries and, if this instance was active,\n\t * clears the pointer. Returns the framework to its pre-bootstrap state.\n\t */\n\tdispose(): void;\n}\n\nexport interface ILankaInstanceConfig {\n\thost: ILankaHost;\n\tflags?: ILankaFlags;\n}\n\nexport interface ILankaServiceConfig {\n\tname?: string;\n\tinit: () => void | Promise<void>;\n\tsync?: boolean;\n\tpriority?: number;\n\t/**\n\t * A failure of this service does not abort bootstrap.\n\t *\n\t * Without the flag one failed service takes the WHOLE phase with it: the async\n\t * phase because of `Promise.all`, the sync phase because later tasks never run.\n\t * Wrapping the failure in a swallowing `try/catch` is worse still — the app\n\t * starts with a partially executed plan and does not know it.\n\t */\n\toptional?: boolean;\n\t/**\n\t * How long to wait for the service. Overrunning counts as a failure.\n\t *\n\t * Without a deadline a service that never settles holds bootstrap forever and\n\t * the app never paints its first screen. Failing is more honest than waiting:\n\t * an optional service is skipped, a required one names itself.\n\t */\n\ttimeoutMs?: number;\n}\n\nexport interface ILankaScenarioBootstrapConfig {\n\tsync?: boolean;\n\tpriority?: number;\n}\n\nexport interface ILankaBootstrapConfig {\n\tservices?: ILankaServiceConfig[];\n\tscenarios?: ILankaScenarioBootstrapConfig;\n}\n\n/**\n * Config merge: patch over previous, flags over flags.\n *\n * Lives HERE rather than in `config/lankaConfig` because the rule belongs to\n * whoever holds the config — the instance. Importing it from the config module\n * would pull that module into EVERY test's graph through the test kit, and any\n * test that partially mocks the config module would break the suite with \"No\n * export is defined on the mock\".\n */\nfunction mergeLankaConfig(\n\tprevious: ILankaRuntimeConfig,\n\tpatch: ILankaRuntimeConfig,\n): ILankaRuntimeConfig {\n\treturn {\n\t\t// `host` is read field by field: `setConfig({ host: undefined })` through a\n\t\t// spread would leave the instance with no host, and every request URL would\n\t\t// then carry the string \"undefined\". Flags stay a spread — there both\n\t\t// absent and undefined mean \"not set\", so nothing can be lost.\n\t\thost: patch.host ?? previous.host,\n\t\tflags: { ...previous.flags, ...patch.flags },\n\t};\n}\n\ntype TExecutionTask = {\n\tname: string;\n\tpriority: number;\n\toptional: boolean;\n\ttimeoutMs?: number;\n\texecute: () => void | Promise<void>;\n};\n\n/**\n * Creates an instance and makes it active.\n *\n * Activating on creation is the deliberate default: there is almost always one\n * app and the facades must work immediately. A second instance takes the\n * pointer — the last one created serves the facades. Callers needing another\n * order call `activate()` explicitly.\n */\nexport function createLanka(config: ILankaInstanceConfig): ILankaInstance {\n\tlet runtimeConfig: ILankaRuntimeConfig = mergeLankaConfig(\n\t\t{},\n\t\t{ host: config.host, flags: config.flags },\n\t);\n\tlet bootstrapped = false;\n\tlet bootstrapping: Promise<void> | null = null; // the plan in flight, joined by later callers\n\n\tconst eventBus = new LankaEventBusInstance();\n\tconst scenarios = new LankaScenariosRegistry();\n\tconst viewModels = new LankaScenarioVMRegistry();\n\tconst inFlight = createInFlightCounter();\n\tconst scenarioState = {\n\t\tbootstrapped: false,\n\t\tinitialized: new WeakSet<ILankaScenarioVM>(),\n\t};\n\tconst scopes = new Set<ILankaScope>();\n\tconst requestMiddleware: TLankaRequestMiddleware[] = [];\n\tlet requestTimeoutMs: number | undefined;\n\n\tconst locators = {\n\t\tgateways: new LankaGatewayLocator(),\n\t\tscenarios: new LankaScenarioLocator(),\n\t\tsingletons: new LankaSingletonLocator(),\n\t\tsharedStores: new LankaSharedStoreLocator(),\n\t};\n\n\tconst instance: ILankaInstance = {\n\t\teventBus,\n\t\tscenarios,\n\t\tviewModels,\n\t\tinFlight,\n\t\tlocators,\n\t\tscenarioState,\n\t\trequestMiddleware,\n\t\tget requestTimeoutMs() {\n\t\t\treturn requestTimeoutMs;\n\t\t},\n\t\tuseRequestMiddleware(middleware: TLankaRequestMiddleware): () => void {\n\t\t\trequestMiddleware.push(middleware);\n\t\t\treturn () => {\n\t\t\t\tconst at = requestMiddleware.indexOf(middleware);\n\t\t\t\tif (at !== -1) requestMiddleware.splice(at, 1);\n\t\t\t};\n\t\t},\n\t\tsetRequestTimeout(timeoutMs: number | undefined): void {\n\t\t\trequestTimeoutMs = timeoutMs;\n\t\t},\n\t\tresolve<TInstance>(propertyName: string): TInstance {\n\t\t\treturn locators.singletons.get(propertyName) as TInstance;\n\t\t},\n\t\tuse(plugin: ILankaPlugin): () => void {\n\t\t\treturn plugins.use(plugin);\n\t\t},\n\t\tcreateScope(): ILankaScope {\n\t\t\tconst scope = createLankaScope(locators.singletons);\n\t\t\tscopes.add(scope);\n\t\t\treturn scope;\n\t\t},\n\t\tget config() {\n\t\t\treturn runtimeConfig;\n\t\t},\n\t\tsetConfig(patch: ILankaRuntimeConfig): void {\n\t\t\truntimeConfig = mergeLankaConfig(runtimeConfig, patch);\n\t\t},\n\t\tgetFlags(): ILankaFlags {\n\t\t\treturn runtimeConfig.flags ?? {};\n\t\t},\n\t\tisBootstrapped(): boolean {\n\t\t\treturn bootstrapped;\n\t\t},\n\t\tactivate(): void {\n\t\t\tsetActiveRuntime(instance);\n\t\t},\n\t\tdispose(): void {\n\t\t\tteardown();\n\t\t\trequestMiddleware.length = 0;\n\t\t\trequestTimeoutMs = undefined;\n\t\t\tbootstrapped = false;\n\t\t},\n\t\tbootstrap(bootstrapConfig: ILankaBootstrapConfig = {}): Promise<void> {\n\t\t\tif (bootstrapped) {\n\t\t\t\tlankaLogger.printBootstrapLog(\"LANKA ALREADY BOOTSTRAPPED\");\n\t\t\t\treturn Promise.resolve();\n\t\t\t}\n\t\t\t// One run, shared: the flag flips when the plan has FINISHED, so two\n\t\t\t// callers arriving before that — two routes, StrictMode mounting twice —\n\t\t\t// both read \"not bootstrapped\" and every service ran twice.\n\t\t\tbootstrapping ??= runBootstrap(instance, bootstrapConfig, () => {\n\t\t\t\tbootstrapped = true;\n\t\t\t}).finally(() => {\n\t\t\t\tbootstrapping = null;\n\t\t\t});\n\t\t\treturn bootstrapping;\n\t\t},\n\t};\n\n\t/**\n\t * Tears the instance down, in the ONE order that works.\n\t *\n\t * Scopes close FIRST: they hold objects built from this instance's classes, so\n\t * disposing without them would leave alive exactly what a scope exists to\n\t * remove. Plugins go AFTER scopes and BEFORE the registries: removing them\n\t * earlier would leave the objects being disposed with half a framework — no\n\t * middleware, but still expecting it.\n\t */\n\tfunction teardown(): void {\n\t\tfor (const scope of scopes) scope.dispose();\n\t\tscopes.clear();\n\n\t\tplugins.removeAll();\n\n\t\tviewModels.resetAll();\n\t\tscenarios.clear();\n\t\teventBus.reset();\n\n\t\tlocators.gateways.clearCache();\n\t\tlocators.scenarios.clearCache();\n\t\tlocators.singletons.clearCache();\n\t\tlocators.sharedStores.clearCache();\n\n\t\tscenarioState.bootstrapped = false;\n\t\tscenarioState.initialized = new WeakSet<ILankaScenarioVM>();\n\n\t\tif (getActiveRuntime() === instance) setActiveRuntime(null);\n\t}\n\n\tconst plugins = createLankaPluginRegistry(instance);\n\n\tinstance.activate();\n\t// EVERY new instance adopts the declared ViewModels: a screen file creates\n\t// them at module level, and neither import order nor test number may decide\n\t// whether the framework knows about them.\n\tlankaScenarioBootstrap.adoptDeclaredViewModels();\n\n\treturn instance;\n}\n\nfunction createExecutionPlan(config: ILankaBootstrapConfig) {\n\tconst syncTasks: TExecutionTask[] = [];\n\tconst asyncTasks: TExecutionTask[] = [];\n\tconst postAsyncTasks: TExecutionTask[] = [];\n\n\tconfig.services?.forEach((service, index) => {\n\t\tconst task: TExecutionTask = {\n\t\t\tname: service.name || `Service-${index + 1}`,\n\t\t\tpriority: service.priority ?? 0,\n\t\t\toptional: service.optional ?? false,\n\t\t\ttimeoutMs: service.timeoutMs,\n\t\t\texecute: service.init,\n\t\t};\n\n\t\tif (service.sync) syncTasks.push(task);\n\t\telse asyncTasks.push(task);\n\t});\n\n\t// Field by field rather than a spread over defaults: a caller forwarding its\n\t// own optional config passes `{ sync: undefined }`, and a spread would put that\n\t// over `sync: true` — moving the whole scenario layer out of the sync phase\n\t// because somebody did not set a variable.\n\tconst scenarioSync = config.scenarios?.sync ?? true;\n\tconst scenarioTask: TExecutionTask = {\n\t\tname: \"LankaScenarioBootstrap\",\n\t\tpriority: config.scenarios?.priority ?? 0,\n\t\t// The scenario layer is required: without it nothing subscribed to it\n\t\t// works, and continuing silently would start an app where half the screens\n\t\t// never update.\n\t\toptional: false,\n\t\texecute: () => {\n\t\t\tlankaScenarioBootstrap.bootstrap();\n\t\t},\n\t};\n\n\tif (scenarioSync) syncTasks.push(scenarioTask);\n\telse postAsyncTasks.push(scenarioTask);\n\n\tsyncTasks.sort((a, b) => b.priority - a.priority);\n\n\treturn { syncTasks, asyncTasks, postAsyncTasks };\n}\n\n/**\n * One bootstrap, start to finish: the three phases in the one order that works,\n * then `markDone` — which the instance uses to flip its flag, and only then.\n *\n * Outside `createLanka` because it closes over nothing the instance holds: the\n * flag it must flip arrives as a callback, and everything else it needs it is\n * handed. That keeps the factory readable as the object it builds.\n */\nasync function runBootstrap(\n\tinstance: ILankaInstance,\n\tconfig: ILankaBootstrapConfig,\n\tmarkDone: () => void,\n): Promise<void> {\n\tinstance.activate();\n\tlankaLogger.printBootstrapLog(\"LANKA BOOTSTRAP START\");\n\n\tconst plan = createExecutionPlan(config);\n\tawait runSequential(plan.syncTasks, \"SYNC TASKS\");\n\tawait runParallel(plan.asyncTasks, \"ASYNC TASKS\");\n\tawait runSequential(plan.postAsyncTasks, \"POST-ASYNC TASKS (lankaScenarios)\");\n\n\tmarkDone();\n\tlankaLogger.printBootstrapLog(\"LANKA BOOTSTRAP FINISH\");\n}\n\n/**\n * Runs a task under its deadline.\n *\n * A race against a timer rather than an `AbortSignal`: a service is an arbitrary\n * application function and cannot be cancelled. The deadline bounds the WAIT,\n * not the work — hung work continues in the background, bootstrap stops waiting.\n */\nasync function runWithDeadline(task: TExecutionTask): Promise<void> {\n\tif (task.timeoutMs === undefined) {\n\t\tawait task.execute();\n\t\treturn;\n\t}\n\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\ttry {\n\t\tawait Promise.race([\n\t\t\tPromise.resolve(task.execute()),\n\t\t\tnew Promise<never>((_resolve, reject) => {\n\t\t\t\ttimer = setTimeout(\n\t\t\t\t\t() =>\n\t\t\t\t\t\treject(\n\t\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t\t`Service \"${task.name}\" did not finish within ${String(task.timeoutMs)}ms`,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t),\n\t\t\t\t\ttask.timeoutMs,\n\t\t\t\t);\n\t\t\t}),\n\t\t]);\n\t} finally {\n\t\tif (timer !== undefined) clearTimeout(timer);\n\t}\n}\n\n/**\n * One task's failure: swallow and log, or abort the plan.\n *\n * The TASK's flag decides, not a blanket tolerance in the runner — otherwise\n * `optional` would mean nothing.\n */\nfunction handleTaskFailure(task: TExecutionTask, phaseName: string, error: unknown): void {\n\tlankaLogger.printBootstrapLog(`[${phaseName}] ERROR IN ${task.name}`, formatError(error));\n\tif (!task.optional) throw error;\n\tlankaLogger.printBootstrapLog(`[${phaseName}] SKIPPED (optional): ${task.name}`);\n}\n\nasync function runSequential(tasks: TExecutionTask[], phaseName: string): Promise<void> {\n\tif (tasks.length === 0) return;\n\n\tfor (const task of tasks) {\n\t\ttry {\n\t\t\tlankaLogger.printBootstrapLog(\n\t\t\t\t`[${phaseName}] EXECUTING: ${task.name} (Priority: ${String(task.priority)})`,\n\t\t\t);\n\t\t\tawait runWithDeadline(task);\n\t\t} catch (error) {\n\t\t\thandleTaskFailure(task, phaseName, error);\n\t\t}\n\t}\n}\n\nasync function runParallel(tasks: TExecutionTask[], phaseName: string): Promise<void> {\n\tif (tasks.length === 0) return;\n\n\tlankaLogger.printBootstrapLog(\n\t\t`[${phaseName}] EXECUTING ${String(tasks.length)} TASKS IN PARALLEL`,\n\t);\n\n\t// `allSettled`, not `all`: `all` rejects on the first failure, so an optional\n\t// service would take down required ones that were still running. Outcomes are\n\t// examined after everything has finished.\n\tconst outcomes = await Promise.allSettled(\n\t\ttasks.map(async (task) => {\n\t\t\tlankaLogger.printBootstrapLog(`[${phaseName}] EXECUTING: ${task.name}`);\n\t\t\tawait runWithDeadline(task);\n\t\t}),\n\t);\n\n\tfor (const [index, outcome] of outcomes.entries()) {\n\t\tif (outcome.status === \"rejected\") {\n\t\t\thandleTaskFailure(tasks[index], phaseName, outcome.reason);\n\t\t}\n\t}\n}\n\nfunction formatError(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n","import { createLanka } from \"../_factories/create-lanka/createLanka\";\nimport { createLankaHost } from \"../../config/_factories/create-lanka-host/createLankaHost\";\nimport type { ILankaHost } from \"../../config/_interfaces/ILankaHost\";\nimport type { ILankaInstance } from \"../_factories/create-lanka/createLanka\";\nimport type { TLankaStartConfig } from \"../_types/TLankaStartConfig\";\n\n/** The host as given, or one built from the base URL. */\nconst hostOf = (config: TLankaStartConfig): ILankaHost =>\n\tconfig.host ?? createLankaHost({ apiBaseUrl: config.apiBaseUrl, ...config.messages });\n\n/**\n * A started framework, in one call.\n *\n * ```ts\n * const lanka = await startLanka({ apiBaseUrl: \"https://api.example.com\" });\n * const sameOrigin = await startLanka(); // nothing to configure yet\n * ```\n *\n * ## What it saves, and what it does not hide\n *\n * `createLanka` already activates the instance it returns, and `bootstrap` already\n * brings up the scenario layer — so an application's whole start-up was two calls\n * and a `for` loop over its plugins. This is those, in the order that works, and\n * nothing else: what comes back is the same `ILankaInstance`, with every method\n * it always had.\n *\n * The order matters and is the reason this exists rather than a paragraph in a\n * guide. Plugins install BEFORE bootstrap, because a plugin that adds a\n * bootstrap service after the plan is built adds it to nothing; and bootstrap is\n * awaited, because a screen rendered against a half-started framework fails in\n * the layer it reaches first rather than where the mistake was.\n *\n * An application that needs something between those steps — registering a\n * singleton whose construction reads a service's result, say — writes the two\n * calls out and keeps this one for the next project.\n */\nexport const startLanka = async (config: TLankaStartConfig = {}): Promise<ILankaInstance> => {\n\tconst lanka = createLanka({ host: hostOf(config), flags: config.flags });\n\n\tfor (const plugin of config.plugins ?? []) lanka.use(plugin);\n\n\tawait lanka.bootstrap({ services: config.services, scenarios: config.scenarios });\n\n\treturn lanka;\n};\n","import { getActiveRuntime } from \"../../_internal/active-runtime/activeRuntime\";\nimport type { ILankaScenarioVM } from \"../../scenario/_interfaces/ILankaScenarioVM\";\n\n/**\n * Resets the active instance, if there is one.\n *\n * What the test kit needs between cases. Silent when there is no instance:\n * \"reset nothing\" is not an error, and the setup file runs before EVERY test,\n * including those that never bootstrap the framework.\n */\nexport function resetActiveLanka(): void {\n\tconst active = getActiveRuntime();\n\tif (!active) return;\n\n\tactive.viewModels.resetAll();\n\tactive.scenarios.clear();\n\tactive.eventBus.clearAllEvents();\n\tactive.scenarioState.bootstrapped = false;\n\tactive.scenarioState.initialized = new WeakSet<ILankaScenarioVM>();\n}\n","import type { ILankaInstance } from \"../../_factories/create-lanka/createLanka\";\nimport type { ILankaPlugin } from \"../../ILankaPlugin\";\n\n/**\n * A plugin, written as a class.\n *\n * The functional style is a function returning `{ name, install }`, and it is\n * what the framework's own five plugins use — a plugin is a value handed to\n * `use()`, and the ecosystem writes those as `react()`, `pinia()`,\n * `tsconfigPaths()`.\n *\n * This is the same contract for an application that keeps its plugins as\n * classes: `name` is a field, `install` is a method, and `uninstall` is the\n * teardown the framework calls on removal and on dispose. Nothing above can\n * tell which style wrote it — `use()` sees `ILankaPlugin` either way.\n */\nexport abstract class ALankaPlugin implements ILankaPlugin {\n\tpublic abstract readonly name: string;\n\n\t/** Everything this plugin puts in place. Return nothing if there is nothing to undo. */\n\tprotected abstract onInstall(lanka: ILankaInstance): void;\n\n\t/**\n\t * Removes what `onInstall` put in place.\n\t *\n\t * A default that does nothing rather than an abstract member: an abstract one\n\t * would break every existing subclass the day it was added, and most plugins\n\t * genuinely have nothing to undo.\n\t */\n\tprotected onUninstall(): void {\n\t\t/* Nothing to undo unless a subclass says otherwise. */\n\t}\n\n\tpublic install(lanka: ILankaInstance): () => void {\n\t\tthis.onInstall(lanka);\n\n\t\treturn () => {\n\t\t\tthis.onUninstall();\n\t\t};\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { I as ILankaScope } from './createLankaScope-BiFxNQgl.js';
|
|
2
|
-
import { I as ILankaRuntime } from './activeRuntime-
|
|
2
|
+
import { I as ILankaRuntime } from './activeRuntime-BYd2D6Lx.js';
|
|
3
3
|
import { a as ILankaHost, I as ILankaFlags } from './ILankaRuntimeConfig-Vl436GWK.js';
|
|
4
4
|
import { T as TLankaRequestMiddleware } from './lankaRequestMiddleware-DAC5kCb7.js';
|
|
5
5
|
|
package/dist/errors/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { L as LankaError } from '../LankaError-
|
|
2
|
-
export { a as ILankaApiError, I as ILankaErrorInit, T as TLankaErrorKind } from '../LankaError-
|
|
1
|
+
import { L as LankaError } from '../LankaError-D6RWNyLM.js';
|
|
2
|
+
export { a as ILankaApiError, I as ILankaErrorInit, T as TLankaErrorKind } from '../LankaError-D6RWNyLM.js';
|
|
3
|
+
import { I as ILankaFieldError } from '../ILankaFieldError-D5931-vT.js';
|
|
3
4
|
export { T as TLankaErrorHandler } from '../TLankaErrorHandler-Yfqtdh1M.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -33,4 +34,22 @@ declare const createLankaApiError: (status: number, errors: string[], body?: unk
|
|
|
33
34
|
*/
|
|
34
35
|
declare const handleLankaApiError: (response: Response) => Promise<never>;
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
/**
|
|
38
|
+
* The part of a failure that has an input to be shown at — or nothing.
|
|
39
|
+
*
|
|
40
|
+
* Always a list, never a throw and never `undefined`: the caller is a ViewModel
|
|
41
|
+
* deciding what to hand a form, and it writes `for … of` without a guard,
|
|
42
|
+
* whatever it caught. A foreign error has no fields; neither has a network
|
|
43
|
+
* failure; a schema refusal and a 422 the request policy parsed have some. The
|
|
44
|
+
* empty answer is one frozen list, so a caller that mutates what it was given
|
|
45
|
+
* finds out at once rather than in another caller's screen.
|
|
46
|
+
*
|
|
47
|
+
* What is NOT here: deciding where the REST of the failure goes. Whether a
|
|
48
|
+
* `domain` refusal is the form's root message or the screen's banner differs
|
|
49
|
+
* per screen, so that sorting stays in the ViewModel — the guide shows the
|
|
50
|
+
* shape — the way `lankaFirstOf` publishes the folding of extractors and leaves
|
|
51
|
+
* their order to the application.
|
|
52
|
+
*/
|
|
53
|
+
declare const readLankaFieldErrors: (error: unknown) => readonly ILankaFieldError[];
|
|
54
|
+
|
|
55
|
+
export { ILankaFieldError, LankaError, createLankaApiError, handleLankaApiError, readLankaFieldErrors };
|
package/dist/errors/index.js
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createLankaApiError,
|
|
3
3
|
handleLankaApiError
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-NDCJBO47.js";
|
|
5
5
|
import "../chunk-J5LA3Q6D.js";
|
|
6
6
|
import {
|
|
7
7
|
LankaError
|
|
8
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-G32H73QY.js";
|
|
9
9
|
import "../chunk-RKYKK6MN.js";
|
|
10
10
|
import "../chunk-BGVDPDX4.js";
|
|
11
|
+
|
|
12
|
+
// src/errors/read-lanka-field-errors/readLankaFieldErrors.ts
|
|
13
|
+
var NONE = Object.freeze([]);
|
|
14
|
+
var readLankaFieldErrors = (error) => LankaError.is(error) ? error.fields ?? NONE : NONE;
|
|
11
15
|
export {
|
|
12
16
|
LankaError,
|
|
13
17
|
createLankaApiError,
|
|
14
|
-
handleLankaApiError
|
|
18
|
+
handleLankaApiError,
|
|
19
|
+
readLankaFieldErrors
|
|
15
20
|
};
|
|
16
21
|
//# sourceMappingURL=index.js.map
|
package/dist/errors/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/errors/read-lanka-field-errors/readLankaFieldErrors.ts"],"sourcesContent":["import { LankaError } from \"../lanka-error/LankaError\";\nimport type { ILankaFieldError } from \"../_interfaces/ILankaFieldError\";\n\nconst NONE: readonly ILankaFieldError[] = Object.freeze<ILankaFieldError[]>([]);\n\n/**\n * The part of a failure that has an input to be shown at — or nothing.\n *\n * Always a list, never a throw and never `undefined`: the caller is a ViewModel\n * deciding what to hand a form, and it writes `for … of` without a guard,\n * whatever it caught. A foreign error has no fields; neither has a network\n * failure; a schema refusal and a 422 the request policy parsed have some. The\n * empty answer is one frozen list, so a caller that mutates what it was given\n * finds out at once rather than in another caller's screen.\n *\n * What is NOT here: deciding where the REST of the failure goes. Whether a\n * `domain` refusal is the form's root message or the screen's banner differs\n * per screen, so that sorting stays in the ViewModel — the guide shows the\n * shape — the way `lankaFirstOf` publishes the folding of extractors and leaves\n * their order to the application.\n */\nexport const readLankaFieldErrors = (error: unknown): readonly ILankaFieldError[] =>\n\tLankaError.is(error) ? (error.fields ?? NONE) : NONE;\n"],"mappings":";;;;;;;;;;;;AAGA,IAAM,OAAoC,OAAO,OAA2B,CAAC,CAAC;AAkBvE,IAAM,uBAAuB,CAAC,UACpC,WAAW,GAAG,KAAK,IAAK,MAAM,UAAU,OAAQ;","names":[]}
|
package/dist/gateway/index.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { T as TLankaExecuteOptions, I as IALankaGatewayConfig, a as TLankaRequestInit, b as ILankaRequest, c as TLankaQueryBuilder } from '../ALankaGateway-
|
|
2
|
-
export { A as ALankaGateway, d as TLankaQueryParams } from '../ALankaGateway-
|
|
3
|
-
import { I as ILankaValidator } from '../lankaStandardValidator-
|
|
1
|
+
import { T as TLankaExecuteOptions, I as IALankaGatewayConfig, a as TLankaRequestInit, b as ILankaRequest, c as TLankaQueryBuilder } from '../ALankaGateway-BrVPaZN5.js';
|
|
2
|
+
export { A as ALankaGateway, d as TLankaQueryParams } from '../ALankaGateway-BrVPaZN5.js';
|
|
3
|
+
import { I as ILankaValidator } from '../lankaStandardValidator-BUFnysK0.js';
|
|
4
4
|
import { T as TLankaErrorHandler } from '../TLankaErrorHandler-Yfqtdh1M.js';
|
|
5
5
|
export { I as ILankaInFlightCounter, l as lankaHttpInFlight } from '../lankaHttpInFlight-Bk1eIuSx.js';
|
|
6
6
|
export { I as ILankaRequestContext, T as TLankaRequestMiddleware } from '../lankaRequestMiddleware-DAC5kCb7.js';
|
|
7
7
|
import '@standard-schema/spec';
|
|
8
|
+
import '../ILankaFieldError-D5931-vT.js';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* A gateway's protected surface, handed to whoever builds one by calling.
|
package/dist/gateway/index.js
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
lankaStandardValidator
|
|
3
|
+
} from "../chunk-HVSQOMRE.js";
|
|
4
|
+
import "../chunk-CRIRTOLB.js";
|
|
1
5
|
import {
|
|
2
6
|
composeLankaRequestMiddleware
|
|
3
7
|
} from "../chunk-YR4MZXMU.js";
|
|
4
|
-
import {
|
|
5
|
-
lankaStandardValidator
|
|
6
|
-
} from "../chunk-GV5DUYST.js";
|
|
7
8
|
import {
|
|
8
9
|
lankaHttpInFlight
|
|
9
10
|
} from "../chunk-UJEC7H6K.js";
|
|
10
11
|
import {
|
|
11
12
|
handleLankaApiError
|
|
12
|
-
} from "../chunk-
|
|
13
|
+
} from "../chunk-NDCJBO47.js";
|
|
13
14
|
import "../chunk-J5LA3Q6D.js";
|
|
14
15
|
import {
|
|
15
16
|
LankaError
|
|
16
|
-
} from "../chunk-
|
|
17
|
+
} from "../chunk-G32H73QY.js";
|
|
17
18
|
import {
|
|
18
19
|
getLankaHost
|
|
19
20
|
} from "../chunk-RKYKK6MN.js";
|