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.
- package/LICENSE +21 -0
- package/README.md +242 -0
- package/dist/ALankaGateway-ExlRGT3D.d.ts +122 -0
- package/dist/ALankaSharedStore-B7uepuuk.d.ts +29 -0
- package/dist/ILankaApiError-zI_2bNs0.d.ts +16 -0
- package/dist/ILankaRuntimeConfig-Vl436GWK.d.ts +62 -0
- package/dist/ILankaScenarioMetadata-Bu-yggTZ.d.ts +14 -0
- package/dist/ILankaScenarioVM-DuCyPoyT.d.ts +260 -0
- package/dist/LankaError-B1HtuIkw.d.ts +86 -0
- package/dist/LankaScenarioLocator-BGQHwf3n.d.ts +144 -0
- package/dist/LankaSharedStoreLocator-MvCpav5F.d.ts +126 -0
- package/dist/TLankaErrorHandler-Yfqtdh1M.d.ts +3 -0
- package/dist/_extend/index.d.ts +34 -0
- package/dist/_extend/index.js +36 -0
- package/dist/_extend/index.js.map +1 -0
- package/dist/_internal/index.d.ts +43 -0
- package/dist/_internal/index.js +34 -0
- package/dist/_internal/index.js.map +1 -0
- package/dist/activeRuntime-FcsSJvUg.d.ts +84 -0
- package/dist/bootstrap/index.d.ts +242 -0
- package/dist/bootstrap/index.js +22 -0
- package/dist/bootstrap/index.js.map +1 -0
- package/dist/chunk-73IVH3C6.js +52 -0
- package/dist/chunk-73IVH3C6.js.map +1 -0
- package/dist/chunk-BGVDPDX4.js +29 -0
- package/dist/chunk-BGVDPDX4.js.map +1 -0
- package/dist/chunk-C2HP7CRD.js +224 -0
- package/dist/chunk-C2HP7CRD.js.map +1 -0
- package/dist/chunk-D27MREPB.js +13 -0
- package/dist/chunk-D27MREPB.js.map +1 -0
- package/dist/chunk-DTO27QFR.js +59 -0
- package/dist/chunk-DTO27QFR.js.map +1 -0
- package/dist/chunk-EWVDJYCC.js +296 -0
- package/dist/chunk-EWVDJYCC.js.map +1 -0
- package/dist/chunk-FIR4XTBL.js +136 -0
- package/dist/chunk-FIR4XTBL.js.map +1 -0
- package/dist/chunk-ILQNYQY5.js +200 -0
- package/dist/chunk-ILQNYQY5.js.map +1 -0
- package/dist/chunk-J5LA3Q6D.js +14 -0
- package/dist/chunk-J5LA3Q6D.js.map +1 -0
- package/dist/chunk-MDZ42XRH.js +10 -0
- package/dist/chunk-MDZ42XRH.js.map +1 -0
- package/dist/chunk-Q3SOVBIJ.js +176 -0
- package/dist/chunk-Q3SOVBIJ.js.map +1 -0
- package/dist/chunk-Q7QESSYF.js +49 -0
- package/dist/chunk-Q7QESSYF.js.map +1 -0
- package/dist/chunk-RKYKK6MN.js +19 -0
- package/dist/chunk-RKYKK6MN.js.map +1 -0
- package/dist/chunk-RYFZCAQ3.js +637 -0
- package/dist/chunk-RYFZCAQ3.js.map +1 -0
- package/dist/chunk-UJEC7H6K.js +39 -0
- package/dist/chunk-UJEC7H6K.js.map +1 -0
- package/dist/chunk-XESL274R.js +23 -0
- package/dist/chunk-XESL274R.js.map +1 -0
- package/dist/chunk-YR4MZXMU.js +13 -0
- package/dist/chunk-YR4MZXMU.js.map +1 -0
- package/dist/config/index.d.ts +63 -0
- package/dist/config/index.js +16 -0
- package/dist/config/index.js.map +1 -0
- package/dist/createLankaScope-Bc_vChRs.d.ts +30 -0
- package/dist/errors/index.d.ts +37 -0
- package/dist/errors/index.js +16 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/gateway/index.d.ts +258 -0
- package/dist/gateway/index.js +438 -0
- package/dist/gateway/index.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/lankaHttpInFlight-Bk1eIuSx.d.ts +43 -0
- package/dist/lankaRequestMiddleware-DAC5kCb7.d.ts +62 -0
- package/dist/lankaStandardValidator-CL-r-zEV.d.ts +34 -0
- package/dist/locator/index.d.ts +282 -0
- package/dist/locator/index.js +67 -0
- package/dist/locator/index.js.map +1 -0
- package/dist/logger/index.d.ts +146 -0
- package/dist/logger/index.js +11 -0
- package/dist/logger/index.js.map +1 -0
- package/dist/mock/index.d.ts +16 -0
- package/dist/mock/index.js +58 -0
- package/dist/mock/index.js.map +1 -0
- package/dist/role/index.d.ts +48 -0
- package/dist/role/index.js +7 -0
- package/dist/role/index.js.map +1 -0
- package/dist/scenario/index.d.ts +204 -0
- package/dist/scenario/index.js +28 -0
- package/dist/scenario/index.js.map +1 -0
- package/dist/validation/index.d.ts +18 -0
- package/dist/validation/index.js +53 -0
- package/dist/validation/index.js.map +1 -0
- package/dist/viewmodel/index.d.ts +478 -0
- package/dist/viewmodel/index.js +701 -0
- package/dist/viewmodel/index.js.map +1 -0
- package/package.json +99 -0
- package/skills/lanka-core/SKILL.md +241 -0
- package/skills/lanka-core/reference.md +1090 -0
- package/skills/lanka-packages/SKILL.md +70 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/gateway/request/_abstractions/lanka-request/ALankaRequest.ts","../../src/gateway/request/_abstractions/lanka-transport-request/ALankaTransportRequest.ts","../../src/gateway/transport/lanka-fetch-json-transport/LankaFetchJsonTransport.ts","../../src/gateway/request/lanka-fetch-json-request/LankaFetchJsonRequest.ts","../../src/gateway/_utils/build-lanka-query-params/buildLankaQueryParams.ts","../../src/gateway/_abstractions/lanka-gateway/ALankaGateway.ts","../../src/gateway/_factories/create-lanka-gateway/createLankaGateway.ts","../../src/gateway/transport/lanka-fetch-transport/LankaFetchTransport.ts","../../src/gateway/request/lanka-fetch-request/LankaFetchRequest.ts","../../src/gateway/request/_factories/create-lanka-fetch-request/createLankaFetchRequest.ts","../../src/gateway/request/_factories/create-lanka-fetch-json-request/createLankaFetchJsonRequest.ts","../../src/gateway/transport/lanka-fetch-form-data-transport/LankaFetchFormDataTransport.ts","../../src/gateway/request/lanka-fetch-form-data-request/LankaFetchFormDataRequest.ts","../../src/gateway/request/_factories/create-lanka-fetch-form-data-request/createLankaFetchFormDataRequest.ts"],"sourcesContent":["import type { TLankaErrorHandler } from \"../../../../errors/_types/TLankaErrorHandler\";\nimport type { ILankaRequest } from \"../../../_interfaces/ILankaRequest\";\nimport type { TLankaExecuteOptions } from \"../../../_types/TLankaExecuteOptions\";\nimport { handleLankaApiError } from \"../../../../errors/handle-lanka-api-error/handleLankaApiError\";\nimport { getLankaFlags } from \"../../../../config/get-lanka-flags/getLankaFlags\";\nimport { getLankaHost } from \"../../../../config/get-lanka-host/getLankaHost\";\nimport { lankaHttpInFlight } from \"../../../inflight/lankaHttpInFlight\";\nimport { LankaError } from \"../../../../errors/lanka-error/LankaError\";\nimport { getActiveRuntime } from \"../../../../_internal/active-runtime/activeRuntime\";\nimport { composeLankaRequestMiddleware } from \"../../lankaRequestMiddleware\";\nimport type { ILankaRequestContext } from \"../../lankaRequestMiddleware\";\n\n/**\n * Tags whatever the transport threw with a kind.\n *\n * Here rather than in the transports: `execute` is the single point EVERY\n * request passes through, and tagging in each of the four transports would be\n * four places to forget it.\n *\n * A real `fetch` throws `TypeError` on a broken connection and a `DOMException`\n * named `AbortError` on cancellation; it does not throw on a status code at all.\n *\n * An already-tagged error is NOT re-tagged: a request-policy plugin may report\n * `domain`, and rewriting that to `network` would lose the one thing the kind\n * exists for.\n */\nfunction classifyTransportError(error: unknown, timedOut: boolean): LankaError {\n\tif (LankaError.is(error)) return error;\n\n\t// The name is read off ANYTHING, not only off `Error`.\n\t//\n\t// `DOMException` — how `fetch` reports cancellation — does not extend `Error`\n\t// everywhere: in a browser yes, in jsdom no. An `instanceof Error` check lets\n\t// cancellation past the tagging, and a raw `AbortError` reaches the app with\n\t// neither `kind` nor `status`: retry policy reads it as non-retryable and the\n\t// app as an unknown error, so a cancelled request is shown to the user as a\n\t// failure.\n\tconst name = readErrorName(error);\n\tconst isAbort = name === \"AbortError\" || name === \"TimeoutError\";\n\tif (isAbort) {\n\t\t// Only whoever assembled the lifetime knows who aborted: `AbortSignal` has\n\t\t// one `abort` for everyone. The distinction carries a decision — a timeout\n\t\t// is shown, a cancellation is not.\n\t\tconst timedOutHere = timedOut || name === \"TimeoutError\";\n\t\treturn new LankaError({\n\t\t\tkind: timedOutHere ? \"timeout\" : \"aborted\",\n\t\t\tmessage: timedOutHere ? getLankaHost().timeoutErrorMessage() : readErrorMessage(error),\n\t\t\tcause: error,\n\t\t});\n\t}\n\n\t// `network` ONLY for what looks like a transport failure. A real `fetch`\n\t// throws `TypeError`; everything else comes from code we did not write — the\n\t// app's error handler, a response transformer, a broken plugin — and calling\n\t// that a network failure would invite the user to retry a request that\n\t// arrived and was processed.\n\t//\n\t// Unknown stays unknown and passes through. The framework asserts only what\n\t// it knows.\n\tif (error instanceof TypeError) {\n\t\treturn new LankaError({\n\t\t\tkind: \"network\",\n\t\t\tmessage: getLankaHost().networkErrorMessage(),\n\t\t\tissues: [error.message],\n\t\t\tcause: error,\n\t\t});\n\t}\n\n\treturn error as LankaError;\n}\n\n/** The error name, off `Error`, `DOMException` or anything else carrying one. */\nfunction readErrorName(error: unknown): string | undefined {\n\tif (typeof error !== \"object\" || error === null) return undefined;\n\tconst name: unknown = (error as { name?: unknown }).name;\n\treturn typeof name === \"string\" ? name : undefined;\n}\n\n/**\n * The error text, when there is one.\n *\n * An object without `message` is not stringified: `String({})` yields\n * `[object Object]`, which occupies the message slot and says nothing. Empty is\n * more honest.\n */\nfunction readErrorMessage(error: unknown): string {\n\tif (typeof error === \"string\") return error;\n\tif (typeof error !== \"object\" || error === null) return \"\";\n\tconst message: unknown = (error as { message?: unknown }).message;\n\treturn typeof message === \"string\" ? message : \"\";\n}\n\n/**\n * How a gateway request goes on the wire.\n *\n * A subclass declares one method, `request()`, and does only its own work there:\n * `LankaFetchJsonRequest` returns parsed JSON, `LankaFetchFormDataRequest`\n * returns the whole response, a custom transport returns whatever it likes.\n *\n * Overriding `request()` customises the request flow, failure handling, mock\n * substitution, response transformation and log interception.\n */\nexport abstract class ALankaRequest<TOptions = RequestInit> implements ILankaRequest<TOptions> {\n\tprotected readonly errorHandler?: TLankaErrorHandler;\n\tprotected readonly useMock: boolean;\n\n\tprotected constructor(config: { errorHandler?: TLankaErrorHandler; useMock?: boolean }) {\n\t\tconst flags = getLankaFlags();\n\n\t\t// The default error-body handler lives HERE because the request is the only\n\t\t// thing that sees the `Response`. Put on the gateway it would sit in a field\n\t\t// nobody reads — the request takes the handler from ITS OWN config — and a\n\t\t// consumer passing `errorHandler` to the gateway would get silence.\n\t\t//\n\t\t// The parse is cheap: core reads the body once and takes `message` from it;\n\t\t// backend-specific shapes are parsed by `@lankajs/plugin-http`.\n\t\tthis.errorHandler = config.errorHandler ?? handleLankaApiError;\n\n\t\tthis.useMock = config.useMock ?? flags.isMockMode ?? false;\n\t}\n\n\t/**\n\t * Performs the request and returns its result — a response, JSON or a custom\n\t * type.\n\t *\n\t * @param endpoint Full URL\n\t * @param options Transport-specific options\n\t * @param mockHandler Mock, when there is one\n\t */\n\tprotected abstract request<TReturn = Response>(\n\t\tendpoint: string,\n\t\toptions?: TOptions,\n\t\tmockHandler?: () => Promise<TReturn>,\n\t): Promise<TReturn>;\n\n\t/**\n\t * The single point EVERY gateway request passes through.\n\t *\n\t * Hence the in-flight accounting here: intent prefetch stands down while\n\t * anything else is on the wire. The `finally` matters more than the increment —\n\t * a rejected request that never decremented would disable prefetching for the\n\t * rest of the session.\n\t */\n\tpublic async execute<TReturn = Response>(\n\t\tendpoint: string,\n\t\toptions?: TLankaExecuteOptions<TOptions>,\n\t\tmockHandler?: () => Promise<TReturn>,\n\t): Promise<TReturn> {\n\t\tconst runtime = getActiveRuntime();\n\t\tconst { signal, timeoutMs, ...rest } = (options ?? {}) as TLankaExecuteOptions<TOptions>;\n\t\t// The caller's deadline travels IN THE CONTEXT, not only in the closure:\n\t\t// otherwise a policy assigning deadlines per request class could not tell\n\t\t// \"no deadline given\" from \"given by the caller\" and would override an\n\t\t// explicit request with a blanket default.\n\t\tconst fallbackDeadline = runtime?.requestTimeoutMs;\n\n\t\t// No options passed means none are produced. Destructuring yields `{}` even\n\t\t// from `undefined`, and handing that empty object to the transport would\n\t\t// change the request: \"no options\" and \"empty options\" are different\n\t\t// statements, and the transport is entitled to tell them apart.\n\t\tconst passedOptions: unknown = options === undefined ? undefined : rest;\n\n\t\t/*\n\t\t * The lifetime is assembled PER ATTEMPT, not per call, and that does two\n\t\t * things at once.\n\t\t *\n\t\t * Under retry a shared deadline would start the third attempt with whatever\n\t\t * the first two left, so the retry aborts before reaching the server.\n\t\t *\n\t\t * And it is the only way to let middleware set the deadline: `ctx.timeoutMs`\n\t\t * is read on every attempt, which is how a request-policy plugin assigns a\n\t\t * deadline per request CLASS — a file upload and a list read cannot share\n\t\t * one value.\n\t\t *\n\t\t * Caller cancellation stays end-to-end: one `signal` for all attempts.\n\t\t */\n\t\tlet lastTimedOut = false;\n\n\t\t// Tagging happens INSIDE, around the request itself, not in the outer\n\t\t// catch: middleware must receive an already-tagged error, or a retrying\n\t\t// middleware cannot tell a network failure from a domain rejection and\n\t\t// retries what must not be retried. The outer catch stays as a backstop.\n\t\tconst perform = async (ctx: ILankaRequestContext): Promise<unknown> => {\n\t\t\tconst lifetime = createRequestLifetime(signal, ctx.timeoutMs ?? fallbackDeadline);\n\t\t\ttry {\n\t\t\t\treturn await this.request<TReturn>(\n\t\t\t\t\tctx.endpoint,\n\t\t\t\t\twithSignal(ctx.options, lifetime.signal) as TOptions,\n\t\t\t\t\tmockHandler,\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\tlastTimedOut = lifetime.timedOut();\n\t\t\t\tthrow classifyTransportError(error, lastTimedOut);\n\t\t\t} finally {\n\t\t\t\tlifetime.dispose();\n\t\t\t}\n\t\t};\n\n\t\tconst run = composeLankaRequestMiddleware(runtime?.requestMiddleware ?? [], perform);\n\n\t\tlankaHttpInFlight.begin();\n\t\ttry {\n\t\t\t// The chain runs INSIDE the same guard as the request. A catch placed\n\t\t\t// outside would leave a permanent +1 when a plugin throws, disabling\n\t\t\t// prefetch for the rest of the session — the very defect this `finally`\n\t\t\t// exists to prevent.\n\t\t\treturn (await run({\n\t\t\t\tendpoint,\n\t\t\t\toptions: passedOptions,\n\t\t\t\tattempt: 1,\n\t\t\t\ttimeoutMs,\n\t\t\t})) as TReturn;\n\t\t} catch (error) {\n\t\t\tthrow classifyTransportError(error, lastTimedOut);\n\t\t} finally {\n\t\t\tlankaHttpInFlight.end();\n\t\t}\n\t}\n}\n\ninterface IRequestLifetime {\n\treadonly signal: AbortSignal | undefined;\n\t/** Whether OUR timer aborted the request rather than the caller. */\n\ttimedOut(): boolean;\n\tdispose(): void;\n}\n\n/**\n * Combines the caller's signal and our own timeout into one request lifetime.\n *\n * The outcome looks the same — an interrupted request — but the decisions\n * differ: a timeout is shown and offered for retry, a user cancellation is not\n * shown at all. `AbortSignal` does not distinguish them: one `abort`, whose\n * reason belongs to whoever got there first. Hence the private flag.\n *\n * Not `AbortSignal.timeout` alone: it cannot combine with a foreign signal\n * without `AbortSignal.any`, which older engines lack. Assembling by hand works\n * everywhere and costs one listener.\n */\nfunction createRequestLifetime(\n\texternal: AbortSignal | undefined,\n\ttimeoutMs: number | undefined,\n): IRequestLifetime {\n\tif (!external && !timeoutMs) {\n\t\treturn { signal: undefined, timedOut: () => false, dispose: () => undefined };\n\t}\n\n\tconst controller = new AbortController();\n\tlet expired = false;\n\n\tconst timer =\n\t\ttimeoutMs === undefined\n\t\t\t? undefined\n\t\t\t: setTimeout(() => {\n\t\t\t\t\texpired = true;\n\t\t\t\t\tcontroller.abort(new DOMException(\"Request timed out\", \"TimeoutError\"));\n\t\t\t\t}, timeoutMs);\n\n\tconst onExternalAbort = (): void => {\n\t\tcontroller.abort(external?.reason);\n\t};\n\n\tif (external) {\n\t\tif (external.aborted) onExternalAbort();\n\t\telse external.addEventListener(\"abort\", onExternalAbort, { once: true });\n\t}\n\n\treturn {\n\t\tsignal: controller.signal,\n\t\ttimedOut: () => expired,\n\t\tdispose: () => {\n\t\t\tif (timer !== undefined) clearTimeout(timer);\n\t\t\texternal?.removeEventListener(\"abort\", onExternalAbort);\n\t\t},\n\t};\n}\n\n/**\n * Attaches the signal to the options, inventing nothing.\n *\n * With no options and no signal the transport receives `undefined` — exactly\n * what the caller passed. An empty object instead looks harmless but is a\n * different statement, and the transport is entitled to tell them apart.\n */\nfunction withSignal(options: unknown, signal: AbortSignal | undefined): unknown {\n\tif (signal === undefined) return options;\n\treturn { ...(options ?? {}), signal };\n}\n","import { ALankaRequest } from \"../lanka-request/ALankaRequest\";\nimport { LankaError } from \"../../../../errors/lanka-error/LankaError\";\nimport { getLankaFlags } from \"../../../../config/get-lanka-flags/getLankaFlags\";\nimport { getLankaHost } from \"../../../../config/get-lanka-host/getLankaHost\";\nimport type { ILankaTransport } from \"../../../_interfaces/ILankaTransport\";\nimport type { TLankaErrorHandler } from \"../../../../errors/_types/TLankaErrorHandler\";\n\nexport interface ILankaTransportRequestConfig<TOptions> {\n\ttransport?: ILankaTransport<TOptions>;\n\terrorHandler?: TLankaErrorHandler;\n\tuseMock?: boolean;\n}\n\n/**\n * The shape every fetch-backed request has: mock, send, check, parse.\n *\n * The three concrete requests differ in exactly two places — which transport\n * they default to, and how they turn a successful `Response` into a value. Both\n * are parameters of this template, so a fourth kind is a subclass with one\n * method rather than a fourth copy of the sequence.\n */\nexport abstract class ALankaTransportRequest<\n\tTOptions = RequestInit,\n> extends ALankaRequest<TOptions> {\n\tprotected readonly transport: ILankaTransport<TOptions>;\n\n\tprotected constructor(\n\t\tconfig: ILankaTransportRequestConfig<TOptions>,\n\t\tcreateDefaultTransport: () => ILankaTransport<TOptions>,\n\t) {\n\t\tconst flags = getLankaFlags();\n\n\t\tsuper({\n\t\t\terrorHandler: config.errorHandler,\n\t\t\tuseMock: config.useMock ?? flags.isMockMode ?? false,\n\t\t});\n\n\t\tthis.transport = config.transport ?? createDefaultTransport();\n\t}\n\n\tprotected async request<TReturn>(\n\t\tendpoint: string,\n\t\toptions?: TOptions,\n\t\tmockHandler?: () => Promise<TReturn>,\n\t): Promise<TReturn> {\n\t\tif (this.useMock && mockHandler) {\n\t\t\treturn await mockHandler();\n\t\t}\n\n\t\tconst response = await this.transport.request(endpoint, options);\n\t\tif (!response.ok) return await this.refuse(response);\n\n\t\treturn await this.parse<TReturn>(response);\n\t}\n\n\t/**\n\t * Turns a successful response into the value the caller asked for.\n\t *\n\t * The one step that genuinely differs between request kinds.\n\t */\n\tprotected abstract parse<TReturn>(response: Response): Promise<TReturn>;\n\n\t/**\n\t * Refuses an unsuccessful response, and never returns.\n\t *\n\t * `TLankaErrorHandler` is typed `Promise<never>` — a handler must throw. It is\n\t * still CALLED and then followed by a throw, because a handler that breaks its\n\t * contract and returns would otherwise hand `undefined` back as if the request\n\t * had succeeded, and a non-value must never become a value.\n\t */\n\tprotected async refuse(response: Response): Promise<never> {\n\t\tif (this.errorHandler) {\n\t\t\tawait this.errorHandler(response);\n\t\t}\n\n\t\t// Kind `http`, not a bare Error: the server answered, and answered with a\n\t\t// code. Without the kind this reads as a network failure, and the user is\n\t\t// offered a retry of a request that already got a meaningful answer.\n\t\tthrow new LankaError({\n\t\t\tkind: \"http\",\n\t\t\tmessage: getLankaHost().httpErrorMessage(response.status),\n\t\t\tstatus: response.status,\n\t\t});\n\t}\n}\n","import type { ILankaTransport } from \"../../_interfaces/ILankaTransport\";\n\n/**\n * HTTP Fetch JSON transport implementation.\n * Uses native fetch API with JSON-specific headers.\n * Automatically sets Content-Type to application/json for requests with body.\n * For project-specific logic (auth, error handling, etc.), use `request` parameter\n * in Gateway config or create a custom transport.\n */\nexport class LankaFetchJsonTransport implements ILankaTransport<RequestInit> {\n\tasync request(resource: RequestInfo, options?: RequestInit): Promise<Response> {\n\t\t// Only set Content-Type if body exists and is not FormData\n\t\tif (options?.body && !(options.body instanceof FormData)) {\n\t\t\tconst headers = new Headers(options.headers);\n\n\t\t\t// If body is object, stringify it\n\t\t\tlet body = options.body;\n\t\t\tif (\n\t\t\t\ttypeof body === \"object\" &&\n\t\t\t\t!(body instanceof FormData) &&\n\t\t\t\t!(body instanceof Blob)\n\t\t\t) {\n\t\t\t\tbody = JSON.stringify(body);\n\t\t\t}\n\n\t\t\theaders.set(\"Content-Type\", \"application/json\");\n\n\t\t\treturn await fetch(resource, {\n\t\t\t\t...options,\n\t\t\t\theaders,\n\t\t\t\tbody,\n\t\t\t});\n\t\t}\n\n\t\t// No body or FormData - use options as-is\n\t\treturn await fetch(resource, options);\n\t}\n}\n","import { ALankaTransportRequest } from \"../_abstractions/lanka-transport-request/ALankaTransportRequest\";\nimport type { ILankaTransportRequestConfig } from \"../_abstractions/lanka-transport-request/ALankaTransportRequest\";\nimport type { ILankaTransport } from \"../../_interfaces/ILankaTransport\";\nimport { LankaFetchJsonTransport } from \"../../transport/lanka-fetch-json-transport/LankaFetchJsonTransport\";\nimport { LankaError } from \"../../../errors/lanka-error/LankaError\";\n\n/** The JSON request: parses the body, and refuses a body that is not JSON. */\n/**\n * A request kind that answers a parsed JSON body — what most endpoints return.\n *\n * A gateway holds one, and is handed it rather than constructing it, which is\n * what lets a test give the same gateway a transport that never leaves the\n * process. `createLankaFetchJsonRequest()` builds the same class.\n */\nexport class LankaFetchJsonRequest<\n\tTOptions = RequestInit,\n> extends ALankaTransportRequest<TOptions> {\n\tconstructor(config: ILankaTransportRequestConfig<TOptions> = {}) {\n\t\tsuper(config, () => new LankaFetchJsonTransport() as ILankaTransport<TOptions>);\n\t}\n\n\t/**\n\t * Parses the body, or names the failure.\n\t *\n\t * The content type is read for the ERROR MESSAGE only. Using it to choose\n\t * between two parse paths, where the second answers `{}` for any non-empty\n\t * body it cannot parse, fails silently: the caller's schema is the first thing\n\t * to notice, and the caller's SCREEN is where it shows up.\n\t *\n\t * A measured case: a dev server whose `/api` fell through to the SPA fallback\n\t * answered `200 text/html` with `index.html`. Turned into `{}`, the schema\n\t * refused it and a person read a validator's issue list on the sign-in card. A\n\t * body a JSON transport cannot parse is never a value — it is a misrouted\n\t * request, and saying so names the actual failure.\n\t */\n\tprotected async parse<TReturn>(response: Response): Promise<TReturn> {\n\t\tconst contentType = response.headers.get(\"content-type\");\n\n\t\tconst text = await response.text();\n\t\tif (!text) {\n\t\t\treturn undefined as TReturn;\n\t\t}\n\n\t\ttry {\n\t\t\treturn JSON.parse(text) as TReturn;\n\t\t} catch (error) {\n\t\t\t// `schema`, not `network`: the request arrived, the server answered, and\n\t\t\t// the answer was the wrong shape. Not cosmetic — a network failure\n\t\t\t// invites a retry, while retrying a broken contract is pointless and\n\t\t\t// blaming the user for it more so.\n\t\t\tthrow new LankaError({\n\t\t\t\tkind: \"schema\",\n\t\t\t\tmessage:\n\t\t\t\t\t`Failed to parse JSON response (content-type: ${contentType ?? \"none\"}): ` +\n\t\t\t\t\t`${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\tcause: error,\n\t\t\t});\n\t\t}\n\t}\n}\n","import { TLankaQueryParams } from \"../../_types/TLankaQueryParams\";\nimport { TLankaQueryBuilder } from \"../../_types/TLankaQueryBuilder\";\n\n/**\n * The query string a gateway sends, from the object a method was called with.\n *\n * Arrays become `key[]` repeated, nested objects become `key[inner]`: the shape\n * most JSON APIs read back without being told about it.\n *\n * Plain loops over `Object.keys` rather than `Object.entries` and `forEach`: this\n * runs on every request that carries a filter, and the pair array `entries`\n * builds — two allocations per key, thrown away immediately — is a cost with\n * nothing to show for it.\n */\nexport const buildLankaQueryParams: TLankaQueryBuilder = <T extends Record<string, unknown>>(\n\tinput: T,\n): URLSearchParams => {\n\tconst params = new URLSearchParams();\n\n\tconst append = (key: string, value: TLankaQueryParams): void => {\n\t\tif (value == null) return;\n\n\t\tif (Array.isArray(value)) {\n\t\t\t// The bracketed key once for the whole array, not once per element.\n\t\t\tconst itemKey = `${key}[]`;\n\t\t\tfor (let index = 0; index < value.length; index += 1) append(itemKey, value[index]);\n\n\t\t\treturn;\n\t\t}\n\n\t\tif (typeof value === \"object\") {\n\t\t\tconst inner = value as Record<string, TLankaQueryParams>;\n\t\t\tconst innerKeys = Object.keys(inner);\n\n\t\t\tfor (let index = 0; index < innerKeys.length; index += 1) {\n\t\t\t\tconst innerKey = innerKeys[index];\n\t\t\t\tappend(`${key}[${innerKey}]`, inner[innerKey]);\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\tparams.append(key, String(value));\n\t};\n\n\tconst source = input as Record<string, TLankaQueryParams>;\n\tconst keys = Object.keys(source);\n\n\tfor (let index = 0; index < keys.length; index += 1) append(keys[index], source[keys[index]]);\n\n\treturn params;\n};\n","import type { IALankaGatewayConfig } from \"../../_interfaces/IALankaGatewayConfig\";\nimport { lankaLogger } from \"../../../logger/lanka-logger/LankaLogger\";\nimport { LankaFetchJsonRequest } from \"../../request/lanka-fetch-json-request/LankaFetchJsonRequest\";\nimport type { ILankaRequest } from \"../../_interfaces/ILankaRequest\";\nimport type { TLankaExecuteOptions } from \"../../_types/TLankaExecuteOptions\";\nimport { buildLankaQueryParams } from \"../../_utils/build-lanka-query-params/buildLankaQueryParams\";\nimport { TLankaQueryParams } from \"../../_types/TLankaQueryParams\";\nimport { TLankaQueryBuilder } from \"../../_types/TLankaQueryBuilder\";\nimport { getLankaFlags } from \"../../../config/get-lanka-flags/getLankaFlags\";\nimport { getLankaHost } from \"../../../config/get-lanka-host/getLankaHost\";\n\nexport abstract class ALankaGateway<TOptions = RequestInit> {\n\tprotected requestExecutor: ILankaRequest<TOptions>;\n\tprotected queryParamsHandler: TLankaQueryBuilder;\n\n\tprotected readonly useMock: boolean;\n\tprotected readonly basePath: string;\n\n\tprotected constructor(config: IALankaGatewayConfig<TOptions>) {\n\t\tlankaLogger.printGatewayLog(\"Create gateway\", this);\n\t\tconst flags = getLankaFlags();\n\t\tthis.useMock = config.useMock ?? flags.isMockMode ?? false;\n\n\t\t// A gateway with nothing said about transport talks JSON over `fetch`, which\n\t\t// is what almost every one of them does. Supplying a request is how a gateway\n\t\t// stops being ordinary — a raw `Response`, a multipart upload, a transport\n\t\t// that never leaves the process — and that stays a decision rather than a\n\t\t// line every gateway has to carry to be born.\n\t\tthis.requestExecutor = config.request ?? new LankaFetchJsonRequest<TOptions>();\n\n\t\tthis.basePath = config.basePath ?? \"\";\n\t\tthis.queryParamsHandler = config.queryParamsHandler ?? buildLankaQueryParams;\n\t}\n\n\t/**\n\t * Resolves endpoint for request.\n\t * - Absolute paths (starting with \"/\") are returned as-is\n\t * - Relative paths are joined with basePath\n\t * - Query-only strings like \"?a=1\" are attached to basePath\n\t */\n\tprotected endpoint(path: string = \"\"): string {\n\t\t// An absolute URL is detected BEFORE joining with `basePath`, not after:\n\t\t// otherwise `https://other.host/health` first becomes\n\t\t// `/things/https://other.host/health` and there is nothing left to detect.\n\t\tif (isAbsoluteUrl(path)) return path;\n\n\t\treturn this.withApiBase(this.resolvePath(path));\n\t}\n\n\t/**\n\t * Joins `basePath` and the method path.\n\t */\n\tprivate resolvePath(path: string): string {\n\t\tif (!path) return this.basePath;\n\n\t\tif (path.startsWith(\"/\")) return path;\n\n\t\tif (path.startsWith(\"?\")) return `${this.basePath}${path}`;\n\n\t\tconst left = this.basePath.endsWith(\"/\") ? this.basePath.slice(0, -1) : this.basePath;\n\t\tconst right = path.startsWith(\"/\") ? path.slice(1) : path;\n\t\treturn `${left}/${right}`;\n\t}\n\n\t/**\n\t * Prefixes the API base URL from the host contract.\n\t *\n\t * Here rather than in every consumer: otherwise each consumer knows the URL\n\t * and the framework does not, and a realtime plugin would have to know a\n\t * specific application's build.\n\t *\n\t * Declaring the field and not using it would be worse than not declaring it: a\n\t * declaration nothing is built from is a second truth, free to diverge from\n\t * the first.\n\t *\n\t * An absolute URL never reaches here — `endpoint()` filters it out before the\n\t * join.\n\t */\n\tprivate withApiBase(path: string): string {\n\t\tconst base = withoutTrailingSlashes(getLankaHost().apiBaseUrl);\n\t\tif (!base) return path;\n\t\tif (!path) return base;\n\n\t\treturn path.startsWith(\"/\") ? `${base}${path}` : `${base}/${path}`;\n\t}\n\n\tprotected buildQueryParams<T extends object>(params: T): URLSearchParams {\n\t\treturn this.queryParamsHandler(params as Record<string, TLankaQueryParams>);\n\t}\n\n\tprotected async request<TReturn = unknown>(\n\t\tpath: string,\n\t\toptions?: TLankaExecuteOptions<TOptions>,\n\t\tmockHandler?: () => Promise<TReturn>,\n\t): Promise<TReturn> {\n\t\treturn this.requestExecutor.execute<TReturn>(this.endpoint(path), options, mockHandler);\n\t}\n\n\t/**\n\t * Allows to replace request implementation at runtime (e.g. feature flags / tests).\n\t * If you prefer static customization - override `request()` in a subclass.\n\t */\n\tprotected setRequest(request: ILankaRequest<TOptions>): void {\n\t\tthis.requestExecutor = request;\n\t}\n\n\tprotected setQueryParamsHandler(handler: TLankaQueryBuilder): void {\n\t\tthis.queryParamsHandler = handler;\n\t}\n}\n\n/**\n * A scheme plus `//` — a URL that already knows where it is going.\n *\n * A standalone function rather than a method: it is not about a particular\n * gateway, and `endpoint()` needs it before any joining.\n */\nfunction isAbsoluteUrl(path: string): boolean {\n\t// The cheap half first: a scheme needs `://`, and `includes` answers without\n\t// starting the regex engine. Every relative path an application writes — which\n\t// is nearly all of them — stops on this line.\n\tif (!path.includes(\"://\")) return false;\n\n\treturn /^[a-z][a-z\\d+\\-.]*:\\/\\//i.test(path);\n}\n\n/**\n * The API base without its trailing slashes, remembered between calls.\n *\n * The host answers the same string for the life of an application, and trimming\n * it is a regex replace otherwise run on every endpoint of every request. One\n * entry is enough: there is one active host, and a second framework in the same\n * process simply replaces what is remembered here.\n */\nlet lastRawBase: string | null = null;\nlet lastTrimmedBase = \"\";\n\nfunction withoutTrailingSlashes(base: string): string {\n\tif (base !== lastRawBase) {\n\t\tlastRawBase = base;\n\t\tlastTrimmedBase = base.replace(/\\/+$/, \"\");\n\t}\n\n\treturn lastTrimmedBase;\n}\n","import { ALankaGateway } from \"../../_abstractions/lanka-gateway/ALankaGateway\";\nimport type { IALankaGatewayConfig } from \"../../_interfaces/IALankaGatewayConfig\";\nimport type { ILankaGatewayContext } from \"../../_interfaces/ILankaGatewayContext\";\n\n/** What a gateway is built from, whichever style builds it. */\nexport interface ILankaGatewayConfig<\n\tTOptions,\n\tTMethods extends object,\n> extends IALankaGatewayConfig<TOptions> {\n\t/** The endpoints this gateway offers, written over its own surface. */\n\tmethods: (context: ILankaGatewayContext<TOptions>) => TMethods;\n}\n\n/**\n * A gateway, without writing a class.\n *\n * The bridge below is the whole mechanism, and it lives here rather than on the\n * base for two reasons. The language reads `protected` from inside a deriving\n * class body and nowhere else, so a factory outside the hierarchy could only\n * reach the public half — the wrong one. And a `toStyleContext` ON the base\n * would put `TOptions` in a method's parameter position, making the class\n * invariant in it: every `ALankaGateway<unknown>` the locator holds would stop\n * accepting a gateway typed for `RequestInit`.\n *\n * One implementation: what comes back is an instance of `ALankaGateway`, so a\n * behaviour fix reaches both styles at once.\n */\nexport const createLankaGateway = <TOptions, TMethods extends object>(\n\tconfig: ILankaGatewayConfig<TOptions, TMethods>,\n): TMethods => {\n\tclass FunctionalGateway extends ALankaGateway<TOptions> {\n\t\t// The base keeps a protected constructor — it is abstract, and a consumer\n\t\t// reaching for `new ALankaGateway()` would get an object with no endpoints.\n\t\t// A subclass may widen it, and this one is the subclass.\n\t\tpublic constructor(gatewayConfig: IALankaGatewayConfig<TOptions>) {\n\t\t\tsuper(gatewayConfig);\n\t\t}\n\n\t\tpublic build(): TMethods {\n\t\t\treturn config.methods({\n\t\t\t\tendpoint: (path) => this.endpoint(path),\n\t\t\t\trequest: (path, options, mockHandler) => this.request(path, options, mockHandler),\n\t\t\t\tbuildQueryParams: (params) => this.buildQueryParams(params),\n\t\t\t});\n\t\t}\n\t}\n\n\treturn new FunctionalGateway(config).build();\n};\n","import type { ILankaTransport } from \"../../_interfaces/ILankaTransport\";\n\n/**\n * HTTP Fetch transport implementation.\n * Uses native fetch API without any project-specific decorators.\n * For project-specific logic (auth, error handling, etc.), use `request` parameter\n * in Gateway config or create a custom transport.\n */\nexport class LankaFetchTransport implements ILankaTransport<RequestInit> {\n\tasync request(resource: RequestInfo, options?: RequestInit): Promise<Response> {\n\t\treturn await fetch(resource, options);\n\t}\n}\n","import { ALankaTransportRequest } from \"../_abstractions/lanka-transport-request/ALankaTransportRequest\";\nimport type { ILankaTransportRequestConfig } from \"../_abstractions/lanka-transport-request/ALankaTransportRequest\";\nimport type { ILankaTransport } from \"../../_interfaces/ILankaTransport\";\nimport { LankaFetchTransport } from \"../../transport/lanka-fetch-transport/LankaFetchTransport\";\n\n/**\n * The raw request: hands the `Response` back untouched.\n *\n * The minimal, extensible case — a caller wanting headers, a stream or a blob\n * reads them off the response itself.\n */\nexport class LankaFetchRequest<TOptions = RequestInit> extends ALankaTransportRequest<TOptions> {\n\tconstructor(config: ILankaTransportRequestConfig<TOptions> = {}) {\n\t\tsuper(config, () => new LankaFetchTransport() as ILankaTransport<TOptions>);\n\t}\n\n\tprotected parse<TReturn>(response: Response): Promise<TReturn> {\n\t\treturn Promise.resolve(response as unknown as TReturn);\n\t}\n}\n","import { LankaFetchRequest } from \"../../lanka-fetch-request/LankaFetchRequest\";\nimport type { ILankaTransportRequestConfig } from \"../../_abstractions/lanka-transport-request/ALankaTransportRequest\";\n\n/**\n * The functional style of `LankaFetchRequest`: the raw `Response`, for a download or a stream.\n *\n * One line, and that is the point — the factory IS the class, so a behaviour\n * cannot exist in one style and not the other.\n */\nexport const createLankaFetchRequest = <TOptions = RequestInit>(\n\tconfig: ILankaTransportRequestConfig<TOptions> = {},\n): LankaFetchRequest<TOptions> => new LankaFetchRequest<TOptions>(config);\n","import { LankaFetchJsonRequest } from \"../../lanka-fetch-json-request/LankaFetchJsonRequest\";\nimport type { ILankaTransportRequestConfig } from \"../../_abstractions/lanka-transport-request/ALankaTransportRequest\";\n\n/**\n * The functional style of `LankaFetchJsonRequest`: a JSON body, which is what most endpoints answer.\n *\n * One line, and that is the point — the factory IS the class, so a behaviour\n * cannot exist in one style and not the other.\n */\nexport const createLankaFetchJsonRequest = <TOptions = RequestInit>(\n\tconfig: ILankaTransportRequestConfig<TOptions> = {},\n): LankaFetchJsonRequest<TOptions> => new LankaFetchJsonRequest<TOptions>(config);\n","import type { ILankaTransport } from \"../../_interfaces/ILankaTransport\";\n\n/**\n * HTTP Fetch FormData transport implementation.\n * Uses native fetch API optimized for FormData requests.\n * Does not set Content-Type header (browser will set it automatically with boundary).\n * For project-specific logic (auth, error handling, etc.), use `request` parameter\n * in Gateway config or create a custom transport.\n */\nexport class LankaFetchFormDataTransport implements ILankaTransport<RequestInit> {\n\tasync request(resource: RequestInfo, options?: RequestInit): Promise<Response> {\n\t\tconst formDataOptions: RequestInit = { ...options };\n\n\t\t// Remove Content-Type header if body is FormData (browser will set it with boundary)\n\t\tif (formDataOptions.body instanceof FormData && formDataOptions.headers) {\n\t\t\tconst headers = new Headers(formDataOptions.headers);\n\t\t\theaders.delete(\"Content-Type\");\n\t\t\tformDataOptions.headers = headers;\n\t\t}\n\n\t\treturn await fetch(resource, formDataOptions);\n\t}\n}\n","import { ALankaTransportRequest } from \"../_abstractions/lanka-transport-request/ALankaTransportRequest\";\nimport type { ILankaTransportRequestConfig } from \"../_abstractions/lanka-transport-request/ALankaTransportRequest\";\nimport type { ILankaTransport } from \"../../_interfaces/ILankaTransport\";\nimport { LankaFetchFormDataTransport } from \"../../transport/lanka-fetch-form-data-transport/LankaFetchFormDataTransport\";\n\n/**\n * The multipart request: hands the `Response` back untouched.\n *\n * Differs from `LankaFetchRequest` only in its transport — the one that must NOT\n * set `content-type`, because the browser writes it with the boundary and a\n * hand-set header leaves the body unparseable to the server.\n */\nexport class LankaFetchFormDataRequest<\n\tTOptions = RequestInit,\n> extends ALankaTransportRequest<TOptions> {\n\tconstructor(config: ILankaTransportRequestConfig<TOptions> = {}) {\n\t\tsuper(config, () => new LankaFetchFormDataTransport() as ILankaTransport<TOptions>);\n\t}\n\n\tprotected parse<TReturn>(response: Response): Promise<TReturn> {\n\t\treturn Promise.resolve(response as unknown as TReturn);\n\t}\n}\n","import { LankaFetchFormDataRequest } from \"../../lanka-fetch-form-data-request/LankaFetchFormDataRequest\";\nimport type { ILankaTransportRequestConfig } from \"../../_abstractions/lanka-transport-request/ALankaTransportRequest\";\n\n/**\n * The functional style of `LankaFetchFormDataRequest`: a multipart body, for an upload.\n *\n * One line, and that is the point — the factory IS the class, so a behaviour\n * cannot exist in one style and not the other.\n */\nexport const createLankaFetchFormDataRequest = <TOptions = RequestInit>(\n\tconfig: ILankaTransportRequestConfig<TOptions> = {},\n): LankaFetchFormDataRequest<TOptions> => new LankaFetchFormDataRequest<TOptions>(config);\n"],"mappings":"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","names":[]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export { ALankaPlugin, ILankaBootstrapConfig, ILankaInstance, ILankaInstanceConfig, ILankaPlugin, ILankaScenarioBootstrapConfig, ILankaServiceConfig, ILankaStartOptions, TLankaStartConfig, createLanka, resetActiveLanka, startLanka } from './bootstrap/index.js';
|
|
2
|
+
export { ILankaRoleFactory, ILankaRoleOpening, TLankaRoleOpener, defineLankaRole } from './role/index.js';
|
|
3
|
+
export { ILankaHostConfig, createLankaHost, getLankaFlags, getLankaHost } from './config/index.js';
|
|
4
|
+
export { I as ILankaFlags, a as ILankaHost, b as ILankaRuntimeConfig } from './ILankaRuntimeConfig-Vl436GWK.js';
|
|
5
|
+
export { I as ILankaLocatorConfig } from './LankaSharedStoreLocator-MvCpav5F.js';
|
|
6
|
+
export { lankaLogger } from './logger/index.js';
|
|
7
|
+
export { I as ILankaErrorInit, L as LankaError, T as TLankaErrorKind } from './LankaError-B1HtuIkw.js';
|
|
8
|
+
import './createLankaScope-Bc_vChRs.js';
|
|
9
|
+
import './activeRuntime-FcsSJvUg.js';
|
|
10
|
+
import './ILankaScenarioVM-DuCyPoyT.js';
|
|
11
|
+
import './LankaScenarioLocator-BGQHwf3n.js';
|
|
12
|
+
import './ILankaScenarioMetadata-Bu-yggTZ.js';
|
|
13
|
+
import './ALankaGateway-ExlRGT3D.js';
|
|
14
|
+
import './lankaStandardValidator-CL-r-zEV.js';
|
|
15
|
+
import '@standard-schema/spec';
|
|
16
|
+
import './lankaHttpInFlight-Bk1eIuSx.js';
|
|
17
|
+
import './lankaRequestMiddleware-DAC5kCb7.js';
|
|
18
|
+
import './ALankaSharedStore-B7uepuuk.js';
|
|
19
|
+
import 'zustand/vanilla';
|
|
20
|
+
import './ILankaApiError-zI_2bNs0.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defineLankaRole
|
|
3
|
+
} from "./chunk-MDZ42XRH.js";
|
|
4
|
+
import {
|
|
5
|
+
ALankaPlugin,
|
|
6
|
+
createLanka,
|
|
7
|
+
resetActiveLanka,
|
|
8
|
+
startLanka
|
|
9
|
+
} from "./chunk-RYFZCAQ3.js";
|
|
10
|
+
import "./chunk-Q3SOVBIJ.js";
|
|
11
|
+
import "./chunk-FIR4XTBL.js";
|
|
12
|
+
import "./chunk-EWVDJYCC.js";
|
|
13
|
+
import {
|
|
14
|
+
createLankaHost
|
|
15
|
+
} from "./chunk-XESL274R.js";
|
|
16
|
+
import "./chunk-UJEC7H6K.js";
|
|
17
|
+
import {
|
|
18
|
+
LankaError
|
|
19
|
+
} from "./chunk-Q7QESSYF.js";
|
|
20
|
+
import {
|
|
21
|
+
getLankaHost
|
|
22
|
+
} from "./chunk-RKYKK6MN.js";
|
|
23
|
+
import "./chunk-ILQNYQY5.js";
|
|
24
|
+
import {
|
|
25
|
+
lankaLogger
|
|
26
|
+
} from "./chunk-C2HP7CRD.js";
|
|
27
|
+
import {
|
|
28
|
+
getLankaFlags
|
|
29
|
+
} from "./chunk-D27MREPB.js";
|
|
30
|
+
import "./chunk-BGVDPDX4.js";
|
|
31
|
+
export {
|
|
32
|
+
ALankaPlugin,
|
|
33
|
+
LankaError,
|
|
34
|
+
createLanka,
|
|
35
|
+
createLankaHost,
|
|
36
|
+
defineLankaRole,
|
|
37
|
+
getLankaFlags,
|
|
38
|
+
getLankaHost,
|
|
39
|
+
lankaLogger,
|
|
40
|
+
resetActiveLanka,
|
|
41
|
+
startLanka
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Counts the gateway requests currently on the wire.
|
|
3
|
+
*
|
|
4
|
+
* Exists for ONE consumer: intent prefetch, which must never compete with a
|
|
5
|
+
* request the app is actually waiting for. The priority ladder is
|
|
6
|
+
*
|
|
7
|
+
* SSE > ordinary request > route chunk > prefetch
|
|
8
|
+
*
|
|
9
|
+
* and prefetch is the only participant that can be dropped for free — the screen
|
|
10
|
+
* that needs the data fetches it itself. Without a probe the rule could only be
|
|
11
|
+
* documented, not enforced.
|
|
12
|
+
*
|
|
13
|
+
* Deliberately a plain counter, not a queue or a scheduler: it answers "is
|
|
14
|
+
* anything else in flight right now". It cannot tell a prefetch from an ordinary
|
|
15
|
+
* read — every request goes through the same `ALankaRequest.execute` — so the
|
|
16
|
+
* prefetcher subtracts its OWN in-flight count from this total. Accounting lives
|
|
17
|
+
* at the single choke point, in a `finally`: a rejected request that never
|
|
18
|
+
* decremented would disable prefetching for the rest of the session.
|
|
19
|
+
*/
|
|
20
|
+
interface ILankaInFlightCounter {
|
|
21
|
+
/** Called by `ALankaRequest.execute` before sending. */
|
|
22
|
+
begin(): void;
|
|
23
|
+
/** Called by `ALankaRequest.execute` in `finally`, on success and on failure. */
|
|
24
|
+
end(): void;
|
|
25
|
+
getActiveCount(): number;
|
|
26
|
+
/**
|
|
27
|
+
* Notifies of a change in the count. Returns an unsubscribe function.
|
|
28
|
+
*
|
|
29
|
+
* What the counter exists for: `@lankajs/plugin-prefetch` stands down while
|
|
30
|
+
* another request is on the wire, and polling the counter in a loop would be a
|
|
31
|
+
* poor substitute for a notification.
|
|
32
|
+
*/
|
|
33
|
+
subscribe(listener: (activeCount: number) => void): () => void;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Ambient counter: the active instance's, reachable without holding it.
|
|
37
|
+
*
|
|
38
|
+
* Used by `ALankaRequest`, which every gateway request passes through and which
|
|
39
|
+
* has no instance reference. Instance holders read `lanka.inFlight`.
|
|
40
|
+
*/
|
|
41
|
+
declare const lankaHttpInFlight: ILankaInFlightCounter;
|
|
42
|
+
|
|
43
|
+
export { type ILankaInFlightCounter as I, lankaHttpInFlight as l };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What middleware knows about the request it wraps.
|
|
3
|
+
*
|
|
4
|
+
* `options` is deliberately `unknown`: every transport has its own —
|
|
5
|
+
* `RequestInit` for fetch, something else for the next one. Core does not read
|
|
6
|
+
* them, it passes them along.
|
|
7
|
+
*/
|
|
8
|
+
interface ILankaRequestContext {
|
|
9
|
+
readonly endpoint: string;
|
|
10
|
+
readonly options: unknown;
|
|
11
|
+
/**
|
|
12
|
+
* Which attempt this is, counting from one.
|
|
13
|
+
*
|
|
14
|
+
* In the context rather than in the middleware itself, because a retry may be
|
|
15
|
+
* started by someone other than the one counting: an auth refresh restarts the
|
|
16
|
+
* request, and the retrying middleware must not mistake that for its own
|
|
17
|
+
* second attempt.
|
|
18
|
+
*/
|
|
19
|
+
readonly attempt: number;
|
|
20
|
+
/**
|
|
21
|
+
* This attempt's deadline, when middleware assigned one.
|
|
22
|
+
*
|
|
23
|
+
* Read by CORE before every call into the transport, which is what allows a
|
|
24
|
+
* deadline per request class: a file upload and a list read cannot share one
|
|
25
|
+
* value, and the policy knows that difference while the framework does not.
|
|
26
|
+
*/
|
|
27
|
+
readonly timeoutMs?: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A wrapper around a request.
|
|
31
|
+
*
|
|
32
|
+
* ## A wrapper rather than `onRequest` / `onResponse` / `onError`
|
|
33
|
+
*
|
|
34
|
+
* Three hooks cannot express RETRY. `onError` can replace an error but cannot
|
|
35
|
+
* run the request again — and retry plus auth refresh are the two main abilities
|
|
36
|
+
* a request-policy plugin exists for. A wrapper expresses those and everything
|
|
37
|
+
* the hooks expressed.
|
|
38
|
+
*
|
|
39
|
+
* ## Why the objection to `next()` does not carry over
|
|
40
|
+
*
|
|
41
|
+
* On the event bus, middleware that skips `next` SILENCES the event: subscribers
|
|
42
|
+
* do not run and nothing records it. Here, skipping `next` means returning a
|
|
43
|
+
* value instead of a request — and the caller receives that value, so the
|
|
44
|
+
* decision is visible.
|
|
45
|
+
*
|
|
46
|
+
* ## Order
|
|
47
|
+
*
|
|
48
|
+
* Registered first wraps all the rest: it sees the request before everyone and
|
|
49
|
+
* the response after everyone — ordinary layer semantics, where "outer" means
|
|
50
|
+
* outer.
|
|
51
|
+
*/
|
|
52
|
+
type TLankaRequestMiddleware = (ctx: ILankaRequestContext, next: (ctx: ILankaRequestContext) => Promise<unknown>) => Promise<unknown>;
|
|
53
|
+
/**
|
|
54
|
+
* Folds the chain into one function.
|
|
55
|
+
*
|
|
56
|
+
* An empty chain returns `perform` as-is, with no wrappers and no cost: an app
|
|
57
|
+
* without a request-policy plugin has no middleware at all and must not pay for
|
|
58
|
+
* their absence.
|
|
59
|
+
*/
|
|
60
|
+
declare function composeLankaRequestMiddleware(middleware: readonly TLankaRequestMiddleware[], perform: (ctx: ILankaRequestContext) => Promise<unknown>): (ctx: ILankaRequestContext) => Promise<unknown>;
|
|
61
|
+
|
|
62
|
+
export { type ILankaRequestContext as I, type TLankaRequestMiddleware as T, composeLankaRequestMiddleware as c };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
2
|
+
|
|
3
|
+
type TLankaValidationResult<T> = {
|
|
4
|
+
success: true;
|
|
5
|
+
data: T;
|
|
6
|
+
} | {
|
|
7
|
+
success: false;
|
|
8
|
+
errors: string[];
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Response body validation with any Standard Schema implementation.
|
|
13
|
+
*
|
|
14
|
+
* An abstraction typed by one library cannot be implemented by another, which is
|
|
15
|
+
* the whole reason a validation port exists. Standard Schema is the shared
|
|
16
|
+
* interface implemented by zod 4, valibot, arktype and others.
|
|
17
|
+
*
|
|
18
|
+
* `@standard-schema/spec` contains TYPES ONLY and adds no bytes to a build, so it
|
|
19
|
+
* is a regular dependency rather than another peer.
|
|
20
|
+
*
|
|
21
|
+
* No adapter classes: a schema describes itself, and
|
|
22
|
+
* `schema["~standard"].validate(data)` is the whole protocol.
|
|
23
|
+
*/
|
|
24
|
+
/** A schema the port understands: any Standard Schema implementation. */
|
|
25
|
+
type TLankaSchema<TOutput = unknown> = StandardSchemaV1<unknown, TOutput>;
|
|
26
|
+
interface ILankaValidator {
|
|
27
|
+
/** Validates and returns the parsed value, or throws. */
|
|
28
|
+
validate<TOutput>(schema: TLankaSchema<TOutput>, data: unknown, context: string): TOutput;
|
|
29
|
+
/** Validates and returns an outcome, throwing nothing. */
|
|
30
|
+
validateSafe<TOutput>(schema: TLankaSchema<TOutput>, data: unknown): TLankaValidationResult<TOutput>;
|
|
31
|
+
}
|
|
32
|
+
declare const lankaStandardValidator: ILankaValidator;
|
|
33
|
+
|
|
34
|
+
export { type ILankaValidator as I, type TLankaSchema as T, type TLankaValidationResult as a, lankaStandardValidator as l };
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
export { I as ILankaScope } from '../createLankaScope-Bc_vChRs.js';
|
|
2
|
+
export { a as ILankaLocator, I as ILankaLocatorConfig, c as ILankaSharedStoreLocatorConfig, d as ILankaSingletonLocatorConfig } from '../LankaSharedStoreLocator-MvCpav5F.js';
|
|
3
|
+
import { A as ALankaGateway } from '../ALankaGateway-ExlRGT3D.js';
|
|
4
|
+
import { A as ALankaSharedStore } from '../ALankaSharedStore-B7uepuuk.js';
|
|
5
|
+
import '../lankaStandardValidator-CL-r-zEV.js';
|
|
6
|
+
import '@standard-schema/spec';
|
|
7
|
+
import 'zustand/vanilla';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The singletons this app publishes to `lanka`.
|
|
11
|
+
*
|
|
12
|
+
* Add one export line per class; the framework derives the locator from these
|
|
13
|
+
* exports, so there is nothing else to register.
|
|
14
|
+
*
|
|
15
|
+
* @example export { AnalyticsService } from "../src/...";
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
declare namespace SingletonsModule {
|
|
19
|
+
export { };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The singleton marker: "this class is published by the application in
|
|
24
|
+
* `lankaSingletons`".
|
|
25
|
+
*
|
|
26
|
+
* ## Why a marker when the class is exported anyway
|
|
27
|
+
*
|
|
28
|
+
* The other three locators have one: a gateway extends `ALankaGateway`, a
|
|
29
|
+
* scenario has a `name`, a store extends `ALankaSharedStore`. Without a marker
|
|
30
|
+
* the selection is "anything that is a function with a prototype", so a stray
|
|
31
|
+
* export in the barrel — a helper, a companion class, an accidental re-export —
|
|
32
|
+
* becomes part of the public `lankaSingletons.*`, and a typo in a name is caught
|
|
33
|
+
* at runtime on first use, far from where it was made.
|
|
34
|
+
*
|
|
35
|
+
* ## A base class rather than a field
|
|
36
|
+
*
|
|
37
|
+
* A marker field can be put on anything, an object included, and the type would
|
|
38
|
+
* again stop telling a class from a non-class. Inheritance also gives TypeScript
|
|
39
|
+
* something it can select on in a mapped type.
|
|
40
|
+
*
|
|
41
|
+
* The class is empty deliberately: all it carries is the statement "I am meant
|
|
42
|
+
* for the locator".
|
|
43
|
+
*/
|
|
44
|
+
declare abstract class ALankaSingleton {
|
|
45
|
+
/**
|
|
46
|
+
* A runtime check, for the locator, which cannot see types.
|
|
47
|
+
*
|
|
48
|
+
* Looks at the prototype chain rather than the name: names are lost to
|
|
49
|
+
* minification, and a name check would work only until the build.
|
|
50
|
+
*/
|
|
51
|
+
static is(candidate: unknown): boolean;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Singleton classes, read from the consumer's barrel.
|
|
56
|
+
*
|
|
57
|
+
* Adding one takes two steps and no registration:
|
|
58
|
+
*
|
|
59
|
+
* 1. write the class;
|
|
60
|
+
* 2. add ONE export line to `.lanka_di/lankaSingletons.ts` at the project root.
|
|
61
|
+
*
|
|
62
|
+
* The type below is derived from that module's exports, so the property name
|
|
63
|
+
* and the instance type appear by themselves.
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Turns a PascalCase class name into a camelCase property name: `UserProfile` → `userProfile`.
|
|
68
|
+
*/
|
|
69
|
+
type TPascalToCamelCase$3<S extends string> = S extends `${infer P1}${infer P2}` ? `${Lowercase<P1>}${P2}` : S;
|
|
70
|
+
/** Whether a type is a class constructor. */
|
|
71
|
+
/**
|
|
72
|
+
* A class marked as a singleton.
|
|
73
|
+
*
|
|
74
|
+
* Selecting on "is a constructor" admits anything, so a stray barrel export
|
|
75
|
+
* silently becomes part of the public `lankaSingletons.*` and a typo is caught at
|
|
76
|
+
* runtime on first use.
|
|
77
|
+
*/
|
|
78
|
+
type TIsSingletonClass<T> = T extends new () => infer Instance ? Instance extends ALankaSingleton ? true : false : false;
|
|
79
|
+
/**
|
|
80
|
+
* Builds the singletons type from the barrel's exports.
|
|
81
|
+
*
|
|
82
|
+
* Derived automatically; no list has to be maintained by hand.
|
|
83
|
+
*/
|
|
84
|
+
type TLankaSingletons = {
|
|
85
|
+
[K in keyof typeof SingletonsModule as TIsSingletonClass<(typeof SingletonsModule)[K]> extends true ? TPascalToCamelCase$3<K & string> : never]: (typeof SingletonsModule)[K] extends new () => infer Instance ? Instance : never;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
declare const lankaSingletons: TLankaSingletons;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The shared stores this app publishes to `lanka`.
|
|
92
|
+
*
|
|
93
|
+
* Add one export line per class; the framework derives the locator from these
|
|
94
|
+
* exports, so there is nothing else to register.
|
|
95
|
+
*
|
|
96
|
+
* @example export { UserSharedStore } from "../src/...";
|
|
97
|
+
*/
|
|
98
|
+
|
|
99
|
+
declare namespace SharedStoresModule {
|
|
100
|
+
export { };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Shared-store classes read from the consumer's barrel.
|
|
105
|
+
*
|
|
106
|
+
* Adding one takes two steps and no registration:
|
|
107
|
+
*
|
|
108
|
+
* 1. write a class extending `ALankaSharedStore`;
|
|
109
|
+
* 2. add ONE export line to `.lanka_di/lankaSharedStores.ts` at the project root.
|
|
110
|
+
*
|
|
111
|
+
* The type below is derived from that module's exports, so the property name
|
|
112
|
+
* and the instance type appear by themselves.
|
|
113
|
+
*/
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Turns a PascalCase class name into a camelCase property name: `RoleEditorStore` → `roleEditorStore`.
|
|
117
|
+
*/
|
|
118
|
+
type TPascalToCamelCase$2<S extends string> = S extends `${infer P1}${infer P2}` ? `${Lowercase<P1>}${P2}` : S;
|
|
119
|
+
/** Whether the type is a shared-store class. */
|
|
120
|
+
type TIsSharedStoreClass<T> = T extends new () => infer Instance ? Instance extends ALankaSharedStore<object> ? true : false : false;
|
|
121
|
+
/**
|
|
122
|
+
* Builds the shared-store type from every export of the barrel.
|
|
123
|
+
*
|
|
124
|
+
* Derived automatically; no list has to be maintained by hand.
|
|
125
|
+
*/
|
|
126
|
+
type TLankaSharedStores = {
|
|
127
|
+
[K in keyof typeof SharedStoresModule as TIsSharedStoreClass<(typeof SharedStoresModule)[K]> extends true ? TPascalToCamelCase$2<K & string> : never]: (typeof SharedStoresModule)[K] extends new () => infer Instance ? Instance : never;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Access to every shared store at once.
|
|
132
|
+
*
|
|
133
|
+
* An `ALankaSharedStore` subclass appears here by itself once it is exported from
|
|
134
|
+
* `@lanka_di/SharedStores`. The property is camelCase, the class name PascalCase.
|
|
135
|
+
*/
|
|
136
|
+
declare const lankaSharedStores: TLankaSharedStores;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* The gateways this app publishes to `lanka`.
|
|
140
|
+
*
|
|
141
|
+
* Add one export line per class; the framework derives the locator from these
|
|
142
|
+
* exports, so there is nothing else to register.
|
|
143
|
+
*
|
|
144
|
+
* @example export { UserGateway } from "../src/...";
|
|
145
|
+
*/
|
|
146
|
+
|
|
147
|
+
declare namespace GatewaysModule {
|
|
148
|
+
export { };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Gateway classes read from the consumer's barrel.
|
|
153
|
+
*
|
|
154
|
+
* Adding one takes two steps and no registration:
|
|
155
|
+
*
|
|
156
|
+
* 1. write a class extending `ALankaGateway`;
|
|
157
|
+
* 2. add ONE export line to `.lanka_di/lankaGateways.ts` at the project root.
|
|
158
|
+
*
|
|
159
|
+
* The type below is derived from that module's exports, so the property name
|
|
160
|
+
* and the instance type appear by themselves.
|
|
161
|
+
*/
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Turns a PascalCase class name into a camelCase property name: `UserGateway` → `userGateway`.
|
|
165
|
+
*/
|
|
166
|
+
type TPascalToCamelCase$1<S extends string> = S extends `${infer P1}${infer P2}` ? `${Lowercase<P1>}${P2}` : S;
|
|
167
|
+
/** Whether the type is a gateway class — an `ALankaGateway` subclass. */
|
|
168
|
+
type TIsGatewayClass<T> = T extends new () => infer Instance ? Instance extends ALankaGateway<unknown> ? true : false : false;
|
|
169
|
+
/**
|
|
170
|
+
* Builds the gateway type from every export of the barrel.
|
|
171
|
+
*
|
|
172
|
+
* Derived automatically; no list has to be maintained by hand.
|
|
173
|
+
*/
|
|
174
|
+
type TLankaGateways = {
|
|
175
|
+
[K in keyof typeof GatewaysModule as TIsGatewayClass<(typeof GatewaysModule)[K]> extends true ? TPascalToCamelCase$1<K & string> : never]: (typeof GatewaysModule)[K] extends new () => infer Instance ? Instance : never;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Access to every gateway at once.
|
|
180
|
+
*
|
|
181
|
+
* An `ALankaGateway` subclass appears here by itself. The property is camelCase,
|
|
182
|
+
* the gateway name is PascalCase.
|
|
183
|
+
*
|
|
184
|
+
* ```ts
|
|
185
|
+
* import { lankaGateways } from "lanka";
|
|
186
|
+
*
|
|
187
|
+
* const user = await lankaGateways.userGateway.getById(1);
|
|
188
|
+
* const list = await lankaGateways.companyGateway.getList({ page: 1 });
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
declare const lankaGateways: TLankaGateways;
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* The scenarios this app publishes to `lanka`.
|
|
195
|
+
*
|
|
196
|
+
* Add one export line per class; the framework derives the locator from these
|
|
197
|
+
* exports, so there is nothing else to register.
|
|
198
|
+
*
|
|
199
|
+
* @example export { SessionScenario } from "../src/...";
|
|
200
|
+
*/
|
|
201
|
+
|
|
202
|
+
declare namespace ScenariosModule {
|
|
203
|
+
export { };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Scenario classes read from the consumer's barrel.
|
|
208
|
+
*
|
|
209
|
+
* Adding one takes two steps and no registration:
|
|
210
|
+
*
|
|
211
|
+
* 1. write a class extending `ALankaScenario`;
|
|
212
|
+
* 2. add ONE export line to `.lanka_di/lankaScenarios.ts` at the project root.
|
|
213
|
+
*
|
|
214
|
+
* The type below is derived from that module's exports, so the property name
|
|
215
|
+
* and the instance type appear by themselves.
|
|
216
|
+
*/
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Turns a PascalCase class name into a camelCase property name: `SessionUpdated` → `sessionUpdated`.
|
|
220
|
+
*/
|
|
221
|
+
type TPascalToCamelCase<S extends string> = S extends `${infer P1}${infer P2}` ? `${Lowercase<P1>}${P2}` : S;
|
|
222
|
+
/** Extracts the scenario name from the constructor. */
|
|
223
|
+
type TExtractScenarioName<T> = T extends new () => infer Instance ? Instance extends {
|
|
224
|
+
readonly name: infer N;
|
|
225
|
+
} ? N extends string ? N : never : never : never;
|
|
226
|
+
/** Derives the property name from a scenario constructor. */
|
|
227
|
+
type TScenarioPropertyName<T> = TPascalToCamelCase<TExtractScenarioName<T>>;
|
|
228
|
+
/** Whether the type is a scenario class — a constructor yielding a named instance. */
|
|
229
|
+
type TIsScenarioClass<T> = T extends new () => infer Instance ? Instance extends {
|
|
230
|
+
readonly name: string;
|
|
231
|
+
} ? true : false : false;
|
|
232
|
+
/**
|
|
233
|
+
* Builds the scenario type from every export of the barrel.
|
|
234
|
+
*
|
|
235
|
+
* Derived automatically; no list has to be maintained by hand.
|
|
236
|
+
*/
|
|
237
|
+
type TLankaScenarios = {
|
|
238
|
+
[K in keyof typeof ScenariosModule as TIsScenarioClass<(typeof ScenariosModule)[K]> extends true ? TScenarioPropertyName<(typeof ScenariosModule)[K]> : never]: (typeof ScenariosModule)[K] extends new () => infer Instance ? Instance : never;
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Access to every scenario at once.
|
|
243
|
+
*
|
|
244
|
+
* An `ALankaScenario` subclass appears here by itself. The property is camelCase,
|
|
245
|
+
* the scenario name is PascalCase.
|
|
246
|
+
*
|
|
247
|
+
* ```ts
|
|
248
|
+
* import { lankaScenarios } from "lanka";
|
|
249
|
+
*
|
|
250
|
+
* lankaScenarios.sessionUpdated.trigger({ user });
|
|
251
|
+
* lankaScenarios.usersListRefresh.trigger({ reason: "create" });
|
|
252
|
+
*
|
|
253
|
+
* lankaScenarios.usersListRefresh.subscribe((data) => { ... });
|
|
254
|
+
* ```
|
|
255
|
+
*/
|
|
256
|
+
declare const lankaScenarios: TLankaScenarios;
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* The functional style of `ALankaSingleton`: a singleton declared by a function.
|
|
260
|
+
*
|
|
261
|
+
* A singleton is a role — an application writes many — and this is the same role
|
|
262
|
+
* the class style declares by extending the marker. What comes back IS a subclass
|
|
263
|
+
* of `ALankaSingleton`, so the locator discovers it in the consumer's barrel,
|
|
264
|
+
* builds it on first use and caches it, exactly as it does a hand-written class.
|
|
265
|
+
*
|
|
266
|
+
* ```ts
|
|
267
|
+
* export const PlaygroundClock = createLankaSingleton(() => ({
|
|
268
|
+
* now: () => Date.now(),
|
|
269
|
+
* }));
|
|
270
|
+
* ```
|
|
271
|
+
*
|
|
272
|
+
* The result is exported under a PascalCase name because it is a class: the
|
|
273
|
+
* locator resolves `lankaSingletons.playgroundClock` by that export name, and a
|
|
274
|
+
* camelCase one would read as the instance while being the constructor.
|
|
275
|
+
*
|
|
276
|
+
* The builder runs at first resolution, not here — a singleton declared in a
|
|
277
|
+
* module body must not touch a runtime that does not exist yet. Canon:
|
|
278
|
+
* `skills/parity/SKILL.md`.
|
|
279
|
+
*/
|
|
280
|
+
declare const createLankaSingleton: <TInstance extends object>(build: () => TInstance) => new () => TInstance;
|
|
281
|
+
|
|
282
|
+
export { ALankaSingleton, type TLankaGateways, type TLankaScenarios, type TLankaSharedStores, type TLankaSingletons, createLankaSingleton, lankaGateways, lankaScenarios, lankaSharedStores, lankaSingletons };
|