lanka 2.2.1 → 2.2.2
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 +2 -2
- package/dist/{ALankaGateway-BrVPaZN5.d.ts → ALankaGateway-CzBISB0L.d.ts} +0 -15
- package/dist/_extend/index.d.ts +1 -1
- package/dist/_extend/index.js +9 -9
- package/dist/_internal/index.d.ts +1 -1
- package/dist/bootstrap/index.d.ts +1 -1
- package/dist/bootstrap/index.js +3 -3
- package/dist/{chunk-532WVMAF.js → chunk-JLBSXJRX.js} +6 -6
- package/dist/gateway/index.d.ts +2 -2
- package/dist/gateway/index.js +13 -21
- package/dist/gateway/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +6 -6
- package/dist/locator/gateway/lanka-gateway-locator/LankaGatewayLocator.d.ts +1 -1
- package/dist/locator/index.d.ts +1 -1
- package/dist/stream/index.d.ts +1 -1
- package/package.json +1 -1
- package/skills/lanka-core/SKILL.md +1 -1
- package/skills/lanka-core/reference.md +8 -1
- package/skills/lanka-packages/SKILL.md +1 -1
- /package/dist/{chunk-532WVMAF.js.map → chunk-JLBSXJRX.js.map} +0 -0
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ One, unscoped. Everything depends on it; it depends on nothing.
|
|
|
8
8
|
|
|
9
9
|
**Runs in:** the browser, node and React Native — everywhere.
|
|
10
10
|
|
|
11
|
-
**How to use it:** [GUIDE.md](
|
|
11
|
+
**How to use it:** [GUIDE.md](https://github.com/lankajs/lanka/blob/main/core/GUIDE.md) — the user guide, with examples. **How to change it:** [SKILL.md](https://github.com/lankajs/lanka/blob/main/core/SKILL.md).
|
|
12
12
|
|
|
13
13
|
## Extension points core declares
|
|
14
14
|
|
|
@@ -243,4 +243,4 @@ belong to the consumer and the folder already says whose they are.
|
|
|
243
243
|
|
|
244
244
|
---
|
|
245
245
|
|
|
246
|
-
Repository map: [
|
|
246
|
+
Repository map: [the root README](https://github.com/lankajs/lanka/blob/main/README.md)
|
|
@@ -140,21 +140,6 @@ declare abstract class ALankaGateway<TOptions = TLankaRequestInit> {
|
|
|
140
140
|
* Joins `basePath` and the method path.
|
|
141
141
|
*/
|
|
142
142
|
private resolvePath;
|
|
143
|
-
/**
|
|
144
|
-
* Prefixes the API base URL from the host contract.
|
|
145
|
-
*
|
|
146
|
-
* Here rather than in every consumer: otherwise each consumer knows the URL
|
|
147
|
-
* and the framework does not, and a realtime plugin would have to know a
|
|
148
|
-
* specific application's build.
|
|
149
|
-
*
|
|
150
|
-
* Declaring the field and not using it would be worse than not declaring it: a
|
|
151
|
-
* declaration nothing is built from is a second truth, free to diverge from
|
|
152
|
-
* the first.
|
|
153
|
-
*
|
|
154
|
-
* An absolute URL never reaches here — `endpoint()` filters it out before the
|
|
155
|
-
* join.
|
|
156
|
-
*/
|
|
157
|
-
private withApiBase;
|
|
158
143
|
protected buildQueryParams<T extends object>(params: T): URLSearchParams;
|
|
159
144
|
protected request<TReturn = unknown>(path: string, options?: TLankaExecuteOptions<TOptions>, mockHandler?: () => Promise<TReturn>): Promise<TReturn>;
|
|
160
145
|
/**
|
package/dist/_extend/index.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ import { I as ILankaReadableVM } from '../ILankaReadableVM-BoPzHEPV.js';
|
|
|
13
13
|
import '../ILankaScenario-7J3MoKiY.js';
|
|
14
14
|
import '../ILankaScenarioVM-DKwjbIRq.js';
|
|
15
15
|
import '../ILankaScenarioMetadata-DgOxvpwx.js';
|
|
16
|
-
import '../ALankaGateway-
|
|
16
|
+
import '../ALankaGateway-CzBISB0L.js';
|
|
17
17
|
import '../lankaStandardValidator-BUFnysK0.js';
|
|
18
18
|
import '@standard-schema/spec';
|
|
19
19
|
import '../ILankaFieldError-D5931-vT.js';
|
package/dist/_extend/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createLankaLocatorProxy
|
|
3
|
+
} from "../chunk-2ICKGQXS.js";
|
|
1
4
|
import {
|
|
2
5
|
lankaBlindSpotRegistry
|
|
3
6
|
} from "../chunk-UDP6IXDS.js";
|
|
@@ -5,19 +8,19 @@ import {
|
|
|
5
8
|
createLankaScope,
|
|
6
9
|
lankaVMRecipes
|
|
7
10
|
} from "../chunk-M4BK6GBV.js";
|
|
11
|
+
import {
|
|
12
|
+
LankaSharedStoreLocator
|
|
13
|
+
} from "../chunk-3R2NO47A.js";
|
|
8
14
|
import {
|
|
9
15
|
LankaGatewayLocator
|
|
10
16
|
} from "../chunk-BMF4TM2Z.js";
|
|
11
|
-
import {
|
|
12
|
-
LankaScenarioLocator
|
|
13
|
-
} from "../chunk-EH5WZSRJ.js";
|
|
14
17
|
import {
|
|
15
18
|
LankaSingletonLocator
|
|
16
19
|
} from "../chunk-24MUFFHF.js";
|
|
17
|
-
import {
|
|
18
|
-
LankaSharedStoreLocator
|
|
19
|
-
} from "../chunk-3R2NO47A.js";
|
|
20
20
|
import "../chunk-JZJ6GXX3.js";
|
|
21
|
+
import {
|
|
22
|
+
LankaScenarioLocator
|
|
23
|
+
} from "../chunk-EH5WZSRJ.js";
|
|
21
24
|
import {
|
|
22
25
|
ALankaLocator
|
|
23
26
|
} from "../chunk-N3275IPH.js";
|
|
@@ -33,9 +36,6 @@ import {
|
|
|
33
36
|
} from "../chunk-YR4MZXMU.js";
|
|
34
37
|
import "../chunk-AMLUKB6A.js";
|
|
35
38
|
import "../chunk-SP5OUTWF.js";
|
|
36
|
-
import {
|
|
37
|
-
createLankaLocatorProxy
|
|
38
|
-
} from "../chunk-2ICKGQXS.js";
|
|
39
39
|
import {
|
|
40
40
|
getActiveLankaScope,
|
|
41
41
|
hasLankaScopeResolver,
|
|
@@ -9,7 +9,7 @@ import '../ILankaRuntimeConfig-Vl436GWK.js';
|
|
|
9
9
|
import '../lankaHttpInFlight-Bk1eIuSx.js';
|
|
10
10
|
import '../lankaRequestMiddleware-DAC5kCb7.js';
|
|
11
11
|
import '../locator/gateway/lanka-gateway-locator/LankaGatewayLocator.js';
|
|
12
|
-
import '../ALankaGateway-
|
|
12
|
+
import '../ALankaGateway-CzBISB0L.js';
|
|
13
13
|
import '../ALankaLocator-BUCCz0Q5.js';
|
|
14
14
|
import '@standard-schema/spec';
|
|
15
15
|
import '../ILankaFieldError-D5931-vT.js';
|
|
@@ -11,7 +11,7 @@ import '../LankaScenarioVMRegistry-CFzCpIFd.js';
|
|
|
11
11
|
import '../lankaHttpInFlight-Bk1eIuSx.js';
|
|
12
12
|
import '../lankaRequestMiddleware-DAC5kCb7.js';
|
|
13
13
|
import '../locator/gateway/lanka-gateway-locator/LankaGatewayLocator.js';
|
|
14
|
-
import '../ALankaGateway-
|
|
14
|
+
import '../ALankaGateway-CzBISB0L.js';
|
|
15
15
|
import '../lankaStandardValidator-BUFnysK0.js';
|
|
16
16
|
import '@standard-schema/spec';
|
|
17
17
|
import '../ILankaFieldError-D5931-vT.js';
|
package/dist/bootstrap/index.js
CHANGED
|
@@ -3,13 +3,13 @@ import {
|
|
|
3
3
|
createLanka,
|
|
4
4
|
resetActiveLanka,
|
|
5
5
|
startLanka
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-JLBSXJRX.js";
|
|
7
7
|
import "../chunk-M4BK6GBV.js";
|
|
8
|
+
import "../chunk-3R2NO47A.js";
|
|
8
9
|
import "../chunk-BMF4TM2Z.js";
|
|
9
|
-
import "../chunk-EH5WZSRJ.js";
|
|
10
10
|
import "../chunk-24MUFFHF.js";
|
|
11
|
-
import "../chunk-3R2NO47A.js";
|
|
12
11
|
import "../chunk-JZJ6GXX3.js";
|
|
12
|
+
import "../chunk-EH5WZSRJ.js";
|
|
13
13
|
import "../chunk-N3275IPH.js";
|
|
14
14
|
import "../chunk-4VFVACSL.js";
|
|
15
15
|
import "../chunk-L27FF2IH.js";
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createLankaScope
|
|
3
3
|
} from "./chunk-M4BK6GBV.js";
|
|
4
|
+
import {
|
|
5
|
+
LankaSharedStoreLocator
|
|
6
|
+
} from "./chunk-3R2NO47A.js";
|
|
4
7
|
import {
|
|
5
8
|
LankaGatewayLocator
|
|
6
9
|
} from "./chunk-BMF4TM2Z.js";
|
|
7
|
-
import {
|
|
8
|
-
LankaScenarioLocator
|
|
9
|
-
} from "./chunk-EH5WZSRJ.js";
|
|
10
10
|
import {
|
|
11
11
|
LankaSingletonLocator
|
|
12
12
|
} from "./chunk-24MUFFHF.js";
|
|
13
13
|
import {
|
|
14
|
-
|
|
15
|
-
} from "./chunk-
|
|
14
|
+
LankaScenarioLocator
|
|
15
|
+
} from "./chunk-EH5WZSRJ.js";
|
|
16
16
|
import {
|
|
17
17
|
lankaScenarioBootstrap
|
|
18
18
|
} from "./chunk-4VFVACSL.js";
|
|
@@ -735,4 +735,4 @@ export {
|
|
|
735
735
|
resetActiveLanka,
|
|
736
736
|
ALankaPlugin
|
|
737
737
|
};
|
|
738
|
-
//# sourceMappingURL=chunk-
|
|
738
|
+
//# sourceMappingURL=chunk-JLBSXJRX.js.map
|
package/dist/gateway/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
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-
|
|
1
|
+
import { T as TLankaExecuteOptions, I as IALankaGatewayConfig, a as TLankaRequestInit, b as ILankaRequest, c as TLankaQueryBuilder } from '../ALankaGateway-CzBISB0L.js';
|
|
2
|
+
export { A as ALankaGateway, d as TLankaQueryParams } from '../ALankaGateway-CzBISB0L.js';
|
|
3
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';
|
package/dist/gateway/index.js
CHANGED
|
@@ -320,7 +320,9 @@ var ALankaGateway = class {
|
|
|
320
320
|
*/
|
|
321
321
|
endpoint(path = "") {
|
|
322
322
|
if (isAbsoluteUrl(path)) return path;
|
|
323
|
-
|
|
323
|
+
const base = withoutTrailingSlashes(getLankaHost().apiBaseUrl);
|
|
324
|
+
if (base && isUnder(path, base)) return path;
|
|
325
|
+
return withApiBase(base, this.resolvePath(path));
|
|
324
326
|
}
|
|
325
327
|
/**
|
|
326
328
|
* Joins `basePath` and the method path.
|
|
@@ -332,26 +334,6 @@ var ALankaGateway = class {
|
|
|
332
334
|
const left = this.basePath.endsWith("/") ? this.basePath.slice(0, -1) : this.basePath;
|
|
333
335
|
return `${left}/${path}`;
|
|
334
336
|
}
|
|
335
|
-
/**
|
|
336
|
-
* Prefixes the API base URL from the host contract.
|
|
337
|
-
*
|
|
338
|
-
* Here rather than in every consumer: otherwise each consumer knows the URL
|
|
339
|
-
* and the framework does not, and a realtime plugin would have to know a
|
|
340
|
-
* specific application's build.
|
|
341
|
-
*
|
|
342
|
-
* Declaring the field and not using it would be worse than not declaring it: a
|
|
343
|
-
* declaration nothing is built from is a second truth, free to diverge from
|
|
344
|
-
* the first.
|
|
345
|
-
*
|
|
346
|
-
* An absolute URL never reaches here — `endpoint()` filters it out before the
|
|
347
|
-
* join.
|
|
348
|
-
*/
|
|
349
|
-
withApiBase(path) {
|
|
350
|
-
const base = withoutTrailingSlashes(getLankaHost().apiBaseUrl);
|
|
351
|
-
if (!base) return path;
|
|
352
|
-
if (!path) return base;
|
|
353
|
-
return path.startsWith("/") ? `${base}${path}` : `${base}/${path}`;
|
|
354
|
-
}
|
|
355
337
|
buildQueryParams(params) {
|
|
356
338
|
return this.queryParamsHandler(params);
|
|
357
339
|
}
|
|
@@ -373,6 +355,16 @@ function isAbsoluteUrl(path) {
|
|
|
373
355
|
if (!path.includes("://")) return false;
|
|
374
356
|
return /^[a-z][a-z\d+\-.]*:\/\//i.test(path);
|
|
375
357
|
}
|
|
358
|
+
function withApiBase(base, path) {
|
|
359
|
+
if (!base) return path;
|
|
360
|
+
if (!path) return base;
|
|
361
|
+
return path.startsWith("/") ? `${base}${path}` : `${base}/${path}`;
|
|
362
|
+
}
|
|
363
|
+
function isUnder(path, base) {
|
|
364
|
+
if (!path.startsWith(base)) return false;
|
|
365
|
+
const next = path.charAt(base.length);
|
|
366
|
+
return next === "" || next === "/" || next === "?";
|
|
367
|
+
}
|
|
376
368
|
var lastRawBase = null;
|
|
377
369
|
var lastTrimmedBase = "";
|
|
378
370
|
function withoutTrailingSlashes(base) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/gateway/request/_abstractions/lanka-request/ALankaRequest.ts","../../src/gateway/request/_abstractions/lanka-transport-request/ALankaTransportRequest.ts","../../src/gateway/lanka-fetch-transport/LankaFetchTransport.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/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"],"sourcesContent":["import type { TLankaRequestInit } from \"../../../_types/TLankaRequestInit\";\nimport 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, `LankaFetchRequest` returns the\n * whole response, a custom kind 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<\n\tTOptions = TLankaRequestInit,\n> 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 { TLankaRequestInit } from \"../../../_types/TLankaRequestInit\";\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 concrete requests differ in ONE place — how they turn a successful\n * `Response` into a value — so a third kind is a subclass with one method rather\n * than a third copy of the sequence.\n *\n * The default transport stays a constructor PARAMETER even though both shipped\n * kinds pass the same one. It is the seam a request kind that is not fetch-backed\n * comes through: this template is the mock/send/check/parse sequence, and nothing\n * in it is about HTTP. Defaulting the parameter here would fix `TOptions` to\n * fetch options for everyone who reuses the sequence.\n */\nexport abstract class ALankaTransportRequest<\n\tTOptions = TLankaRequestInit,\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\";\nimport type { TLankaRequestInit } from \"../_types/TLankaRequestInit\";\n\n/** What an unencoded body is sent as, and the header that says so. */\nconst CONTENT_TYPE = \"content-type\";\nconst JSON_CONTENT_TYPE = \"application/json\";\n\n/**\n * The network seam: `fetch`, plus the two things `fetch` cannot be told.\n *\n * ## Why there is ONE of these\n *\n * There were three — a plain one, a JSON one and a multipart one — and the split\n * was wrong at birth. `ILankaTransport` exists so a consumer can change the\n * PROTOCOL: a native bridge, a socket, a double that never leaves the process.\n * The three differed in a `content-type` header. That is not a protocol, it is an\n * encoding, and an encoding is a property of the CALL: a gateway with fourteen\n * JSON endpoints and one upload had no way to say so, because its request kind —\n * and with it its transport — was fixed in its constructor. The application that\n * hit this added a `useFormData` flag to its own options and wrote its own\n * transport to read it.\n *\n * So the encoding is decided here, per call, by looking at the body. A gateway\n * posts `FormData` to one endpoint and an object to the next, and neither it nor\n * the request kind has to know.\n *\n * ## What does NOT belong here\n *\n * The base URL (`ALankaGateway` prefixes `apiBaseUrl`), credentials, static\n * headers, CSRF, retry, auth refresh, idempotency keys and deadlines. Every one\n * of those is policy around a request rather than a way of sending one, and every\n * one is a middleware — `useRequestMiddleware`, which `@lankajs/plugin-http`\n * occupies. A transport that grew them would be a second composition mechanism\n * beside the one core already publishes, and \"where does a header get added\"\n * would have two answers.\n */\nexport class LankaFetchTransport implements ILankaTransport<TLankaRequestInit> {\n\tasync request(resource: RequestInfo, options?: TLankaRequestInit): Promise<Response> {\n\t\t// Nothing to encode: the object the caller assembled reaches `fetch`\n\t\t// unchanged, `undefined` included. \"No options\" and \"empty options\" are\n\t\t// different statements and `fetch` is entitled to tell them apart.\n\t\tif (options === undefined || options.body === undefined || options.body === null) {\n\t\t\treturn await fetch(resource, options as RequestInit | undefined);\n\t\t}\n\n\t\tconst { body, headers } = options;\n\n\t\t// Multipart: the header is REMOVED, not set. The boundary is generated with\n\t\t// the body, and a hand-written `content-type` carries none — the server then\n\t\t// reads zero fields out of a request that looks correct.\n\t\tif (isFormData(body)) {\n\t\t\tif (headers === undefined) return await fetch(resource, options as RequestInit);\n\n\t\t\tconst stripped = new Headers(headers);\n\t\t\tstripped.delete(CONTENT_TYPE);\n\t\t\treturn await fetch(resource, { ...options, headers: stripped } as RequestInit);\n\t\t}\n\n\t\t// Already something `fetch` understands: untouched.\n\t\tif (isEncodedBody(body)) return await fetch(resource, options as RequestInit);\n\n\t\treturn await fetch(resource, {\n\t\t\t...options,\n\t\t\theaders: withJsonContentType(headers),\n\t\t\tbody: JSON.stringify(body),\n\t\t});\n\t}\n}\n\n/**\n * The header set, with `content-type` filled in only if the caller left it out.\n *\n * Not overwritten: `application/merge-patch+json` and `application/ld+json` are\n * JSON that a server distinguishes, and a transport that flattened them to\n * `application/json` would turn a PATCH into a request the server refuses — for\n * a header the caller had already written correctly.\n */\nfunction withJsonContentType(headers: HeadersInit | undefined): Headers {\n\tconst result = new Headers(headers);\n\tif (!result.has(CONTENT_TYPE)) result.set(CONTENT_TYPE, JSON_CONTENT_TYPE);\n\treturn result;\n}\n\n/**\n * Whether `fetch` can already send this body.\n *\n * Every global here is guarded by `typeof`: core runs in node and React Native\n * as well as a browser, and `ReadableStream` in particular is absent on engines\n * this framework supports. An unguarded `instanceof` against a missing global is\n * a `ReferenceError` on the first request, from a transport, in production.\n *\n * Cheapest and most common first. A string — an already-serialised body — stops\n * on line one.\n */\nfunction isEncodedBody(body: unknown): body is BodyInit {\n\tif (typeof body === \"string\") return true;\n\tif (typeof URLSearchParams !== \"undefined\" && body instanceof URLSearchParams) return true;\n\tif (typeof Blob !== \"undefined\" && body instanceof Blob) return true;\n\tif (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) return true;\n\treturn typeof ReadableStream !== \"undefined\" && body instanceof ReadableStream;\n}\n\n/**\n * Multipart, checked separately because it is the one body whose header is\n * removed rather than added.\n */\nfunction isFormData(body: unknown): body is FormData {\n\treturn typeof FormData !== \"undefined\" && body instanceof FormData;\n}\n","import { ALankaTransportRequest } from \"../_abstractions/lanka-transport-request/ALankaTransportRequest\";\nimport type { ILankaTransportRequestConfig } from \"../_abstractions/lanka-transport-request/ALankaTransportRequest\";\nimport type { TLankaRequestInit } from \"../../_types/TLankaRequestInit\";\nimport { LankaFetchTransport } from \"../../lanka-fetch-transport/LankaFetchTransport\";\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 extends TLankaRequestInit = TLankaRequestInit,\n> extends ALankaTransportRequest<TOptions> {\n\tconstructor(config: ILankaTransportRequestConfig<TOptions> = {}) {\n\t\tsuper(config, () => new LankaFetchTransport());\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 { TLankaRequestInit } from \"../../_types/TLankaRequestInit\";\nimport 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\";\nimport { lankaStandardValidator } from \"../../../validation/lanka-standard-validator/lankaStandardValidator\";\nimport type { ILankaValidator } from \"../../../validation/lanka-standard-validator/lankaStandardValidator\";\n\nexport abstract class ALankaGateway<TOptions = TLankaRequestInit> {\n\tprotected requestExecutor: ILankaRequest<TOptions>;\n\tprotected queryParamsHandler: TLankaQueryBuilder;\n\n\t/**\n\t * The validator a method checks a response body with.\n\t *\n\t * `config.validationService` when one was given, the Standard Schema port\n\t * otherwise. It used to be accepted by the config and read by nothing: a\n\t * consumer handing a test double to the gateway got the real validator and no\n\t * error, which is the worst kind of ignored option — it looks honoured.\n\t */\n\tprotected readonly validationService: ILankaValidator;\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\t\tthis.validationService = config.validationService ?? lankaStandardValidator;\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\t// The cast covers the FRAMEWORK picking its own fallback, and nothing a\n\t\t// consumer does. `TOptions` is unconstrained here on purpose — a gateway may\n\t\t// front a request that never speaks HTTP, and the port promises exactly that\n\t\t// — so the JSON default cannot be proven to fit a `TOptions` nobody has\n\t\t// named yet. A consumer whose options are not fetch-shaped supplies\n\t\t// `request`, and this line never runs for them.\n\t\tthis.requestExecutor = config.request ?? new LankaFetchJsonRequest();\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\t// No leading-slash case here: the check above already returned for one, so\n\t\t// stripping it again was a branch no input could take — uncoverable by\n\t\t// construction, and it counted against the coverage floor that gates this\n\t\t// package.\n\t\tconst left = this.basePath.endsWith(\"/\") ? this.basePath.slice(0, -1) : this.basePath;\n\t\treturn `${left}/${path}`;\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\tvalidationService: this.validationService,\n\t\t\t});\n\t\t}\n\t}\n\n\treturn new FunctionalGateway(config).build();\n};\n","import { ALankaTransportRequest } from \"../_abstractions/lanka-transport-request/ALankaTransportRequest\";\nimport type { ILankaTransportRequestConfig } from \"../_abstractions/lanka-transport-request/ALankaTransportRequest\";\nimport type { TLankaRequestInit } from \"../../_types/TLankaRequestInit\";\nimport { LankaFetchTransport } from \"../../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, a blob or a\n * `204` reads them off the response itself. Multipart uploads come through here\n * too: the transport encodes by looking at the body, so posting a `FormData` and\n * posting an object are the same call.\n *\n * `TOptions` is CONSTRAINED to fetch options rather than merely defaulted to\n * them. A consumer widening it — their own `interface IRequestOptions extends\n * TLankaRequestInit` — still gets the shipped transport, because the constraint\n * is what lets the framework hand one over without a cast. Unconstrained, the\n * assignment did not typecheck and core cast its way past it; the cast worked\n * here and was unavailable to the consumer, who wrote a transport instead.\n */\nexport class LankaFetchRequest<\n\tTOptions extends TLankaRequestInit = TLankaRequestInit,\n> extends ALankaTransportRequest<TOptions> {\n\tconstructor(config: ILankaTransportRequestConfig<TOptions> = {}) {\n\t\tsuper(config, () => new LankaFetchTransport());\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\";\nimport type { TLankaRequestInit } from \"../../../_types/TLankaRequestInit\";\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 extends TLankaRequestInit = TLankaRequestInit>(\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\";\nimport type { TLankaRequestInit } from \"../../../_types/TLankaRequestInit\";\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 extends TLankaRequestInit = TLankaRequestInit>(\n\tconfig: ILankaTransportRequestConfig<TOptions> = {},\n): LankaFetchJsonRequest<TOptions> => new LankaFetchJsonRequest<TOptions>(config);\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/gateway/request/_abstractions/lanka-request/ALankaRequest.ts","../../src/gateway/request/_abstractions/lanka-transport-request/ALankaTransportRequest.ts","../../src/gateway/lanka-fetch-transport/LankaFetchTransport.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/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"],"sourcesContent":["import type { TLankaRequestInit } from \"../../../_types/TLankaRequestInit\";\nimport 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, `LankaFetchRequest` returns the\n * whole response, a custom kind 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<\n\tTOptions = TLankaRequestInit,\n> 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 { TLankaRequestInit } from \"../../../_types/TLankaRequestInit\";\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 concrete requests differ in ONE place — how they turn a successful\n * `Response` into a value — so a third kind is a subclass with one method rather\n * than a third copy of the sequence.\n *\n * The default transport stays a constructor PARAMETER even though both shipped\n * kinds pass the same one. It is the seam a request kind that is not fetch-backed\n * comes through: this template is the mock/send/check/parse sequence, and nothing\n * in it is about HTTP. Defaulting the parameter here would fix `TOptions` to\n * fetch options for everyone who reuses the sequence.\n */\nexport abstract class ALankaTransportRequest<\n\tTOptions = TLankaRequestInit,\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\";\nimport type { TLankaRequestInit } from \"../_types/TLankaRequestInit\";\n\n/** What an unencoded body is sent as, and the header that says so. */\nconst CONTENT_TYPE = \"content-type\";\nconst JSON_CONTENT_TYPE = \"application/json\";\n\n/**\n * The network seam: `fetch`, plus the two things `fetch` cannot be told.\n *\n * ## Why there is ONE of these\n *\n * There were three — a plain one, a JSON one and a multipart one — and the split\n * was wrong at birth. `ILankaTransport` exists so a consumer can change the\n * PROTOCOL: a native bridge, a socket, a double that never leaves the process.\n * The three differed in a `content-type` header. That is not a protocol, it is an\n * encoding, and an encoding is a property of the CALL: a gateway with fourteen\n * JSON endpoints and one upload had no way to say so, because its request kind —\n * and with it its transport — was fixed in its constructor. The application that\n * hit this added a `useFormData` flag to its own options and wrote its own\n * transport to read it.\n *\n * So the encoding is decided here, per call, by looking at the body. A gateway\n * posts `FormData` to one endpoint and an object to the next, and neither it nor\n * the request kind has to know.\n *\n * ## What does NOT belong here\n *\n * The base URL (`ALankaGateway` prefixes `apiBaseUrl`), credentials, static\n * headers, CSRF, retry, auth refresh, idempotency keys and deadlines. Every one\n * of those is policy around a request rather than a way of sending one, and every\n * one is a middleware — `useRequestMiddleware`, which `@lankajs/plugin-http`\n * occupies. A transport that grew them would be a second composition mechanism\n * beside the one core already publishes, and \"where does a header get added\"\n * would have two answers.\n */\nexport class LankaFetchTransport implements ILankaTransport<TLankaRequestInit> {\n\tasync request(resource: RequestInfo, options?: TLankaRequestInit): Promise<Response> {\n\t\t// Nothing to encode: the object the caller assembled reaches `fetch`\n\t\t// unchanged, `undefined` included. \"No options\" and \"empty options\" are\n\t\t// different statements and `fetch` is entitled to tell them apart.\n\t\tif (options === undefined || options.body === undefined || options.body === null) {\n\t\t\treturn await fetch(resource, options as RequestInit | undefined);\n\t\t}\n\n\t\tconst { body, headers } = options;\n\n\t\t// Multipart: the header is REMOVED, not set. The boundary is generated with\n\t\t// the body, and a hand-written `content-type` carries none — the server then\n\t\t// reads zero fields out of a request that looks correct.\n\t\tif (isFormData(body)) {\n\t\t\tif (headers === undefined) return await fetch(resource, options as RequestInit);\n\n\t\t\tconst stripped = new Headers(headers);\n\t\t\tstripped.delete(CONTENT_TYPE);\n\t\t\treturn await fetch(resource, { ...options, headers: stripped } as RequestInit);\n\t\t}\n\n\t\t// Already something `fetch` understands: untouched.\n\t\tif (isEncodedBody(body)) return await fetch(resource, options as RequestInit);\n\n\t\treturn await fetch(resource, {\n\t\t\t...options,\n\t\t\theaders: withJsonContentType(headers),\n\t\t\tbody: JSON.stringify(body),\n\t\t});\n\t}\n}\n\n/**\n * The header set, with `content-type` filled in only if the caller left it out.\n *\n * Not overwritten: `application/merge-patch+json` and `application/ld+json` are\n * JSON that a server distinguishes, and a transport that flattened them to\n * `application/json` would turn a PATCH into a request the server refuses — for\n * a header the caller had already written correctly.\n */\nfunction withJsonContentType(headers: HeadersInit | undefined): Headers {\n\tconst result = new Headers(headers);\n\tif (!result.has(CONTENT_TYPE)) result.set(CONTENT_TYPE, JSON_CONTENT_TYPE);\n\treturn result;\n}\n\n/**\n * Whether `fetch` can already send this body.\n *\n * Every global here is guarded by `typeof`: core runs in node and React Native\n * as well as a browser, and `ReadableStream` in particular is absent on engines\n * this framework supports. An unguarded `instanceof` against a missing global is\n * a `ReferenceError` on the first request, from a transport, in production.\n *\n * Cheapest and most common first. A string — an already-serialised body — stops\n * on line one.\n */\nfunction isEncodedBody(body: unknown): body is BodyInit {\n\tif (typeof body === \"string\") return true;\n\tif (typeof URLSearchParams !== \"undefined\" && body instanceof URLSearchParams) return true;\n\tif (typeof Blob !== \"undefined\" && body instanceof Blob) return true;\n\tif (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) return true;\n\treturn typeof ReadableStream !== \"undefined\" && body instanceof ReadableStream;\n}\n\n/**\n * Multipart, checked separately because it is the one body whose header is\n * removed rather than added.\n */\nfunction isFormData(body: unknown): body is FormData {\n\treturn typeof FormData !== \"undefined\" && body instanceof FormData;\n}\n","import { ALankaTransportRequest } from \"../_abstractions/lanka-transport-request/ALankaTransportRequest\";\nimport type { ILankaTransportRequestConfig } from \"../_abstractions/lanka-transport-request/ALankaTransportRequest\";\nimport type { TLankaRequestInit } from \"../../_types/TLankaRequestInit\";\nimport { LankaFetchTransport } from \"../../lanka-fetch-transport/LankaFetchTransport\";\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 extends TLankaRequestInit = TLankaRequestInit,\n> extends ALankaTransportRequest<TOptions> {\n\tconstructor(config: ILankaTransportRequestConfig<TOptions> = {}) {\n\t\tsuper(config, () => new LankaFetchTransport());\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 { TLankaRequestInit } from \"../../_types/TLankaRequestInit\";\nimport 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\";\nimport { lankaStandardValidator } from \"../../../validation/lanka-standard-validator/lankaStandardValidator\";\nimport type { ILankaValidator } from \"../../../validation/lanka-standard-validator/lankaStandardValidator\";\n\nexport abstract class ALankaGateway<TOptions = TLankaRequestInit> {\n\tprotected requestExecutor: ILankaRequest<TOptions>;\n\tprotected queryParamsHandler: TLankaQueryBuilder;\n\n\t/**\n\t * The validator a method checks a response body with.\n\t *\n\t * `config.validationService` when one was given, the Standard Schema port\n\t * otherwise. It used to be accepted by the config and read by nothing: a\n\t * consumer handing a test double to the gateway got the real validator and no\n\t * error, which is the worst kind of ignored option — it looks honoured.\n\t */\n\tprotected readonly validationService: ILankaValidator;\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\t\tthis.validationService = config.validationService ?? lankaStandardValidator;\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\t// The cast covers the FRAMEWORK picking its own fallback, and nothing a\n\t\t// consumer does. `TOptions` is unconstrained here on purpose — a gateway may\n\t\t// front a request that never speaks HTTP, and the port promises exactly that\n\t\t// — so the JSON default cannot be proven to fit a `TOptions` nobody has\n\t\t// named yet. A consumer whose options are not fetch-shaped supplies\n\t\t// `request`, and this line never runs for them.\n\t\tthis.requestExecutor = config.request ?? new LankaFetchJsonRequest();\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\t// A path already under the base is one this method produced, and it is\n\t\t// returned as it is: `endpoint()` is idempotent. `request()` resolves\n\t\t// through `endpoint()`, and every gateway in the guide hands it\n\t\t// `this.endpoint(…)`, so each call resolves twice. An absolute base hides\n\t\t// that — the first pass makes an absolute URL — and a RELATIVE one (`/api`)\n\t\t// sent every request to `/api/api/…` (issue #7).\n\t\t//\n\t\t// Decided on the INPUT, before `basePath` is joined, and not on the joined\n\t\t// result: a gateway whose `basePath` happens to start with the base's\n\t\t// segment then resolves exactly as it always did, and only an argument that\n\t\t// already carries the base is left alone. The one reading given up: a path\n\t\t// written as `/api/x` under a base of `/api` means `/api/x`.\n\t\tconst base = withoutTrailingSlashes(getLankaHost().apiBaseUrl);\n\t\tif (base && isUnder(path, base)) return path;\n\n\t\treturn withApiBase(base, 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\t// No leading-slash case here: the check above already returned for one, so\n\t\t// stripping it again was a branch no input could take — uncoverable by\n\t\t// construction, and it counted against the coverage floor that gates this\n\t\t// package.\n\t\tconst left = this.basePath.endsWith(\"/\") ? this.basePath.slice(0, -1) : this.basePath;\n\t\treturn `${left}/${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 * Prefixes the API base URL from the host contract.\n *\n * Here rather than in every consumer: otherwise each consumer knows the URL\n * and the framework does not, and a realtime plugin would have to know a\n * specific application's build.\n *\n * Declaring the field and not using it would be worse than not declaring it: a\n * declaration nothing is built from is a second truth, free to diverge from\n * the first.\n *\n * An absolute URL never reaches here — `endpoint()` filters it out before the\n * join. The base is passed in rather than read: `endpoint()` has already read\n * it, and the host is not asked twice per request.\n */\nfunction withApiBase(base: string, path: string): string {\n\tif (!base) return path;\n\tif (!path) return base;\n\n\treturn path.startsWith(\"/\") ? `${base}${path}` : `${base}/${path}`;\n}\n\n/**\n * The base itself, or a path below it — compared as a whole segment.\n *\n * `/api-keys` starts with `/api` and is not under it; a bare `startsWith` would\n * take it for already-prefixed and drop the base from a real request.\n */\nfunction isUnder(path: string, base: string): boolean {\n\tif (!path.startsWith(base)) return false;\n\n\tconst next = path.charAt(base.length);\n\treturn next === \"\" || next === \"/\" || next === \"?\";\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\tvalidationService: this.validationService,\n\t\t\t});\n\t\t}\n\t}\n\n\treturn new FunctionalGateway(config).build();\n};\n","import { ALankaTransportRequest } from \"../_abstractions/lanka-transport-request/ALankaTransportRequest\";\nimport type { ILankaTransportRequestConfig } from \"../_abstractions/lanka-transport-request/ALankaTransportRequest\";\nimport type { TLankaRequestInit } from \"../../_types/TLankaRequestInit\";\nimport { LankaFetchTransport } from \"../../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, a blob or a\n * `204` reads them off the response itself. Multipart uploads come through here\n * too: the transport encodes by looking at the body, so posting a `FormData` and\n * posting an object are the same call.\n *\n * `TOptions` is CONSTRAINED to fetch options rather than merely defaulted to\n * them. A consumer widening it — their own `interface IRequestOptions extends\n * TLankaRequestInit` — still gets the shipped transport, because the constraint\n * is what lets the framework hand one over without a cast. Unconstrained, the\n * assignment did not typecheck and core cast its way past it; the cast worked\n * here and was unavailable to the consumer, who wrote a transport instead.\n */\nexport class LankaFetchRequest<\n\tTOptions extends TLankaRequestInit = TLankaRequestInit,\n> extends ALankaTransportRequest<TOptions> {\n\tconstructor(config: ILankaTransportRequestConfig<TOptions> = {}) {\n\t\tsuper(config, () => new LankaFetchTransport());\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\";\nimport type { TLankaRequestInit } from \"../../../_types/TLankaRequestInit\";\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 extends TLankaRequestInit = TLankaRequestInit>(\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\";\nimport type { TLankaRequestInit } from \"../../../_types/TLankaRequestInit\";\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 extends TLankaRequestInit = TLankaRequestInit>(\n\tconfig: ILankaTransportRequestConfig<TOptions> = {},\n): LankaFetchJsonRequest<TOptions> => new LankaFetchJsonRequest<TOptions>(config);\n"],"mappings":"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","names":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ import './ILankaScenarioMetadata-DgOxvpwx.js';
|
|
|
17
17
|
import './lankaHttpInFlight-Bk1eIuSx.js';
|
|
18
18
|
import './lankaRequestMiddleware-DAC5kCb7.js';
|
|
19
19
|
import './locator/gateway/lanka-gateway-locator/LankaGatewayLocator.js';
|
|
20
|
-
import './ALankaGateway-
|
|
20
|
+
import './ALankaGateway-CzBISB0L.js';
|
|
21
21
|
import './lankaStandardValidator-BUFnysK0.js';
|
|
22
22
|
import '@standard-schema/spec';
|
|
23
23
|
import './ILankaFieldError-D5931-vT.js';
|
package/dist/index.js
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
defineLankaRole
|
|
3
|
-
} from "./chunk-MDZ42XRH.js";
|
|
4
1
|
import {
|
|
5
2
|
ALankaPlugin,
|
|
6
3
|
createLanka,
|
|
7
4
|
resetActiveLanka,
|
|
8
5
|
startLanka
|
|
9
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-JLBSXJRX.js";
|
|
10
7
|
import "./chunk-M4BK6GBV.js";
|
|
8
|
+
import "./chunk-3R2NO47A.js";
|
|
11
9
|
import "./chunk-BMF4TM2Z.js";
|
|
12
|
-
import "./chunk-EH5WZSRJ.js";
|
|
13
10
|
import "./chunk-24MUFFHF.js";
|
|
14
|
-
import "./chunk-3R2NO47A.js";
|
|
15
11
|
import "./chunk-JZJ6GXX3.js";
|
|
12
|
+
import "./chunk-EH5WZSRJ.js";
|
|
16
13
|
import "./chunk-N3275IPH.js";
|
|
17
14
|
import "./chunk-4VFVACSL.js";
|
|
18
15
|
import "./chunk-L27FF2IH.js";
|
|
@@ -33,6 +30,9 @@ import {
|
|
|
33
30
|
getLankaFlags
|
|
34
31
|
} from "./chunk-SP5OUTWF.js";
|
|
35
32
|
import "./chunk-XTVEHYIZ.js";
|
|
33
|
+
import {
|
|
34
|
+
defineLankaRole
|
|
35
|
+
} from "./chunk-MDZ42XRH.js";
|
|
36
36
|
export {
|
|
37
37
|
ALankaPlugin,
|
|
38
38
|
LankaError,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as ALankaGateway } from '../../../ALankaGateway-
|
|
1
|
+
import { A as ALankaGateway } from '../../../ALankaGateway-CzBISB0L.js';
|
|
2
2
|
import { A as ALankaLocator } from '../../../ALankaLocator-BUCCz0Q5.js';
|
|
3
3
|
import '../../../lankaStandardValidator-BUFnysK0.js';
|
|
4
4
|
import '@standard-schema/spec';
|
package/dist/locator/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { I as ILankaScope } from '../createLankaScope-DeElRH-E.js';
|
|
2
2
|
export { a as ILankaLocator, I as ILankaLocatorConfig } from '../ALankaLocator-BUCCz0Q5.js';
|
|
3
3
|
import * as GatewaysModule from '@lanka_di/Gateways';
|
|
4
|
-
import { A as ALankaGateway } from '../ALankaGateway-
|
|
4
|
+
import { A as ALankaGateway } from '../ALankaGateway-CzBISB0L.js';
|
|
5
5
|
import * as ScenariosModule from '@lanka_di/Scenarios';
|
|
6
6
|
import * as SingletonsModule from '@lanka_di/Singletons';
|
|
7
7
|
export { ILankaSingletonLocatorConfig } from './singleton/lanka-singleton-locator/LankaSingletonLocator.js';
|
package/dist/stream/index.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ import '../ILankaRuntimeConfig-Vl436GWK.js';
|
|
|
12
12
|
import '../lankaHttpInFlight-Bk1eIuSx.js';
|
|
13
13
|
import '../lankaRequestMiddleware-DAC5kCb7.js';
|
|
14
14
|
import '../locator/gateway/lanka-gateway-locator/LankaGatewayLocator.js';
|
|
15
|
-
import '../ALankaGateway-
|
|
15
|
+
import '../ALankaGateway-CzBISB0L.js';
|
|
16
16
|
import '../lankaStandardValidator-BUFnysK0.js';
|
|
17
17
|
import '@standard-schema/spec';
|
|
18
18
|
import '../ILankaFieldError-D5931-vT.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<!-- Generated from core/GUIDE.md by scripts/skills.mjs. Edit the guide. -->
|
|
2
2
|
|
|
3
|
-
> **`lanka@2.2.
|
|
3
|
+
> **`lanka@2.2.2`** — this document describes that version.
|
|
4
4
|
>
|
|
5
5
|
> Install: `npm install lanka zustand` (the peers are not optional; only npm adds a missing one for you).
|
|
6
6
|
>
|
|
@@ -387,6 +387,13 @@ the framework reaches both at once.
|
|
|
387
387
|
`endpoint(path)` prefixes `apiBaseUrl` and the gateway's `basePath`, normalising
|
|
388
388
|
slashes. Pass an absolute URL and it is used as given.
|
|
389
389
|
|
|
390
|
+
`request(path)` resolves its path through `endpoint()` itself, so
|
|
391
|
+
`request("7")` and `request(endpoint("7"))` reach the same URL. That holds
|
|
392
|
+
because `endpoint()` is idempotent: a path already under `apiBaseUrl` is not
|
|
393
|
+
prefixed again, so a relative base such as `/api` gives `/api/todos`, not
|
|
394
|
+
`/api/api/todos`. The one reading this costs is that a path you write as
|
|
395
|
+
`/api/x` under a base of `/api` means `/api/x`.
|
|
396
|
+
|
|
390
397
|
### Requests and transports
|
|
391
398
|
|
|
392
399
|
A **request kind** decides what a response _is_; the **transport** decides how
|
|
File without changes
|