lanka 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/dist/{ALankaGateway-ExlRGT3D.d.ts → ALankaGateway-CkW1LbKE.d.ts} +50 -4
- package/dist/{ILankaScenarioMetadata-Bu-yggTZ.d.ts → ILankaScenarioMetadata-GoWWNEQL.d.ts} +1 -1
- package/dist/{ILankaScenarioVM-DuCyPoyT.d.ts → ILankaScenarioVM-DUsI-fSc.d.ts} +116 -3
- package/dist/{LankaScenarioLocator-BGQHwf3n.d.ts → LankaScenarioLocator-D86TIwiu.d.ts} +11 -6
- package/dist/{LankaSharedStoreLocator-MvCpav5F.d.ts → LankaSharedStoreLocator-zS2kLu-S.d.ts} +21 -0
- package/dist/_extend/index.d.ts +7 -7
- package/dist/_extend/index.js +3 -3
- package/dist/_internal/index.d.ts +6 -6
- package/dist/{activeRuntime-FcsSJvUg.d.ts → activeRuntime-B336NU5I.d.ts} +3 -3
- package/dist/bootstrap/index.d.ts +14 -145
- package/dist/bootstrap/index.js +5 -5
- package/dist/{chunk-FIR4XTBL.js → chunk-5MAQVBI2.js} +27 -24
- package/dist/chunk-5MAQVBI2.js.map +1 -0
- package/dist/chunk-D5WKKEIR.js +54 -0
- package/dist/chunk-D5WKKEIR.js.map +1 -0
- package/dist/{chunk-RYFZCAQ3.js → chunk-LMKLLEHA.js} +111 -25
- package/dist/chunk-LMKLLEHA.js.map +1 -0
- package/dist/{chunk-EWVDJYCC.js → chunk-O5ROO7QF.js} +28 -9
- package/dist/chunk-O5ROO7QF.js.map +1 -0
- package/dist/{chunk-ILQNYQY5.js → chunk-UE2C76OR.js} +32 -18
- package/dist/chunk-UE2C76OR.js.map +1 -0
- package/dist/{chunk-Q3SOVBIJ.js → chunk-UGXSGQPW.js} +8 -4
- package/dist/chunk-UGXSGQPW.js.map +1 -0
- package/dist/createLanka-DI1CSy2Q.d.ts +139 -0
- package/dist/{createLankaScope-Bc_vChRs.d.ts → createLankaScope-BiFxNQgl.d.ts} +1 -1
- package/dist/gateway/index.d.ts +77 -67
- package/dist/gateway/index.js +51 -56
- package/dist/gateway/index.js.map +1 -1
- package/dist/index.d.ts +9 -8
- package/dist/index.js +5 -5
- package/dist/locator/index.d.ts +7 -55
- package/dist/locator/index.js +1 -1
- package/dist/scenario/index.d.ts +6 -4
- package/dist/scenario/index.js +2 -2
- package/dist/stream/index.d.ts +386 -0
- package/dist/stream/index.js +287 -0
- package/dist/stream/index.js.map +1 -0
- package/dist/validation/index.js +4 -48
- package/dist/validation/index.js.map +1 -1
- package/dist/viewmodel/index.d.ts +1 -1
- package/dist/viewmodel/index.js +4 -2
- package/dist/viewmodel/index.js.map +1 -1
- package/package.json +7 -3
- package/skills/lanka-core/SKILL.md +1 -1
- package/skills/lanka-core/reference.md +110 -17
- package/skills/lanka-packages/SKILL.md +1 -1
- package/dist/chunk-EWVDJYCC.js.map +0 -1
- package/dist/chunk-FIR4XTBL.js.map +0 -1
- package/dist/chunk-ILQNYQY5.js.map +0 -1
- package/dist/chunk-Q3SOVBIJ.js.map +0 -1
- package/dist/chunk-RYFZCAQ3.js.map +0 -1
package/dist/gateway/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { T as TLankaExecuteOptions, I as IALankaGatewayConfig, a as
|
|
2
|
-
export { A as ALankaGateway,
|
|
1
|
+
import { T as TLankaExecuteOptions, I as IALankaGatewayConfig, a as TLankaRequestInit, b as ILankaRequest, c as TLankaQueryBuilder } from '../ALankaGateway-CkW1LbKE.js';
|
|
2
|
+
export { A as ALankaGateway, d as TLankaQueryParams } from '../ALankaGateway-CkW1LbKE.js';
|
|
3
|
+
import { I as ILankaValidator } from '../lankaStandardValidator-CL-r-zEV.js';
|
|
3
4
|
import { T as TLankaErrorHandler } from '../TLankaErrorHandler-Yfqtdh1M.js';
|
|
4
5
|
export { I as ILankaInFlightCounter, l as lankaHttpInFlight } from '../lankaHttpInFlight-Bk1eIuSx.js';
|
|
5
6
|
export { I as ILankaRequestContext, T as TLankaRequestMiddleware } from '../lankaRequestMiddleware-DAC5kCb7.js';
|
|
6
|
-
import '../lankaStandardValidator-CL-r-zEV.js';
|
|
7
7
|
import '@standard-schema/spec';
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -20,6 +20,8 @@ interface ILankaGatewayContext<TOptions> {
|
|
|
20
20
|
request: <TReturn = unknown>(path: string, options?: TLankaExecuteOptions<TOptions>, mockHandler?: () => Promise<TReturn>) => Promise<TReturn>;
|
|
21
21
|
/** Serialises query parameters the way this gateway was configured to. */
|
|
22
22
|
buildQueryParams: <T extends object>(params: T) => URLSearchParams;
|
|
23
|
+
/** Checks a response body: the validator this gateway was given, or the Standard Schema port. */
|
|
24
|
+
validationService: ILankaValidator;
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
/** What a gateway is built from, whichever style builds it. */
|
|
@@ -47,13 +49,13 @@ declare const createLankaGateway: <TOptions, TMethods extends object>(config: IL
|
|
|
47
49
|
* How a gateway request goes on the wire.
|
|
48
50
|
*
|
|
49
51
|
* A subclass declares one method, `request()`, and does only its own work there:
|
|
50
|
-
* `LankaFetchJsonRequest` returns parsed JSON, `
|
|
51
|
-
*
|
|
52
|
+
* `LankaFetchJsonRequest` returns parsed JSON, `LankaFetchRequest` returns the
|
|
53
|
+
* whole response, a custom kind returns whatever it likes.
|
|
52
54
|
*
|
|
53
55
|
* Overriding `request()` customises the request flow, failure handling, mock
|
|
54
56
|
* substitution, response transformation and log interception.
|
|
55
57
|
*/
|
|
56
|
-
declare abstract class ALankaRequest<TOptions =
|
|
58
|
+
declare abstract class ALankaRequest<TOptions = TLankaRequestInit> implements ILankaRequest<TOptions> {
|
|
57
59
|
protected readonly errorHandler?: TLankaErrorHandler;
|
|
58
60
|
protected readonly useMock: boolean;
|
|
59
61
|
protected constructor(config: {
|
|
@@ -81,10 +83,23 @@ declare abstract class ALankaRequest<TOptions = RequestInit> implements ILankaRe
|
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
/**
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
+
* How bytes travel. The seam a consumer replaces to change the PROTOCOL.
|
|
87
|
+
*
|
|
88
|
+
* `LankaFetchTransport` is the only implementation core ships, and one is the
|
|
89
|
+
* right number: HTTP over `fetch` is what almost every application does, and the
|
|
90
|
+
* things that once justified a second and a third — a JSON `content-type`, a
|
|
91
|
+
* multipart one — turned out to be encodings rather than protocols. An encoding
|
|
92
|
+
* belongs to the CALL, and the shipped transport reads the body to decide it.
|
|
93
|
+
*
|
|
94
|
+
* Implement this for something genuinely different: a native bridge, a socket, an
|
|
95
|
+
* offline queue, a double that never leaves the process. Anything that still ends
|
|
96
|
+
* in `fetch` and only wants to add a header, a credential, a retry or a refresh
|
|
97
|
+
* is POLICY — write a `TLankaRequestMiddleware` and register it with
|
|
98
|
+
* `useRequestMiddleware`, or reach for `@lankajs/plugin-http`, which already has
|
|
99
|
+
* all four. A transport rewritten to carry policy is how an application ends up
|
|
100
|
+
* maintaining its own copy of this package.
|
|
86
101
|
*/
|
|
87
|
-
interface ILankaTransport<TOptions =
|
|
102
|
+
interface ILankaTransport<TOptions = TLankaRequestInit> {
|
|
88
103
|
/**
|
|
89
104
|
* Executes a request and returns a Response.
|
|
90
105
|
* @param resource - Request resource (URL or Request object)
|
|
@@ -102,12 +117,17 @@ interface ILankaTransportRequestConfig<TOptions> {
|
|
|
102
117
|
/**
|
|
103
118
|
* The shape every fetch-backed request has: mock, send, check, parse.
|
|
104
119
|
*
|
|
105
|
-
* The
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
120
|
+
* The concrete requests differ in ONE place — how they turn a successful
|
|
121
|
+
* `Response` into a value — so a third kind is a subclass with one method rather
|
|
122
|
+
* than a third copy of the sequence.
|
|
123
|
+
*
|
|
124
|
+
* The default transport stays a constructor PARAMETER even though both shipped
|
|
125
|
+
* kinds pass the same one. It is the seam a request kind that is not fetch-backed
|
|
126
|
+
* comes through: this template is the mock/send/check/parse sequence, and nothing
|
|
127
|
+
* in it is about HTTP. Defaulting the parameter here would fix `TOptions` to
|
|
128
|
+
* fetch options for everyone who reuses the sequence.
|
|
109
129
|
*/
|
|
110
|
-
declare abstract class ALankaTransportRequest<TOptions =
|
|
130
|
+
declare abstract class ALankaTransportRequest<TOptions = TLankaRequestInit> extends ALankaRequest<TOptions> {
|
|
111
131
|
protected readonly transport: ILankaTransport<TOptions>;
|
|
112
132
|
protected constructor(config: ILankaTransportRequestConfig<TOptions>, createDefaultTransport: () => ILankaTransport<TOptions>);
|
|
113
133
|
protected request<TReturn>(endpoint: string, options?: TOptions, mockHandler?: () => Promise<TReturn>): Promise<TReturn>;
|
|
@@ -131,10 +151,19 @@ declare abstract class ALankaTransportRequest<TOptions = RequestInit> extends AL
|
|
|
131
151
|
/**
|
|
132
152
|
* The raw request: hands the `Response` back untouched.
|
|
133
153
|
*
|
|
134
|
-
* The minimal, extensible case — a caller wanting headers, a stream or a
|
|
135
|
-
* reads them off the response itself.
|
|
154
|
+
* The minimal, extensible case — a caller wanting headers, a stream, a blob or a
|
|
155
|
+
* `204` reads them off the response itself. Multipart uploads come through here
|
|
156
|
+
* too: the transport encodes by looking at the body, so posting a `FormData` and
|
|
157
|
+
* posting an object are the same call.
|
|
158
|
+
*
|
|
159
|
+
* `TOptions` is CONSTRAINED to fetch options rather than merely defaulted to
|
|
160
|
+
* them. A consumer widening it — their own `interface IRequestOptions extends
|
|
161
|
+
* TLankaRequestInit` — still gets the shipped transport, because the constraint
|
|
162
|
+
* is what lets the framework hand one over without a cast. Unconstrained, the
|
|
163
|
+
* assignment did not typecheck and core cast its way past it; the cast worked
|
|
164
|
+
* here and was unavailable to the consumer, who wrote a transport instead.
|
|
136
165
|
*/
|
|
137
|
-
declare class LankaFetchRequest<TOptions =
|
|
166
|
+
declare class LankaFetchRequest<TOptions extends TLankaRequestInit = TLankaRequestInit> extends ALankaTransportRequest<TOptions> {
|
|
138
167
|
constructor(config?: ILankaTransportRequestConfig<TOptions>);
|
|
139
168
|
protected parse<TReturn>(response: Response): Promise<TReturn>;
|
|
140
169
|
}
|
|
@@ -145,7 +174,7 @@ declare class LankaFetchRequest<TOptions = RequestInit> extends ALankaTransportR
|
|
|
145
174
|
* One line, and that is the point — the factory IS the class, so a behaviour
|
|
146
175
|
* cannot exist in one style and not the other.
|
|
147
176
|
*/
|
|
148
|
-
declare const createLankaFetchRequest: <TOptions =
|
|
177
|
+
declare const createLankaFetchRequest: <TOptions extends TLankaRequestInit = TLankaRequestInit>(config?: ILankaTransportRequestConfig<TOptions>) => LankaFetchRequest<TOptions>;
|
|
149
178
|
|
|
150
179
|
/** The JSON request: parses the body, and refuses a body that is not JSON. */
|
|
151
180
|
/**
|
|
@@ -155,7 +184,7 @@ declare const createLankaFetchRequest: <TOptions = RequestInit>(config?: ILankaT
|
|
|
155
184
|
* what lets a test give the same gateway a transport that never leaves the
|
|
156
185
|
* process. `createLankaFetchJsonRequest()` builds the same class.
|
|
157
186
|
*/
|
|
158
|
-
declare class LankaFetchJsonRequest<TOptions =
|
|
187
|
+
declare class LankaFetchJsonRequest<TOptions extends TLankaRequestInit = TLankaRequestInit> extends ALankaTransportRequest<TOptions> {
|
|
159
188
|
constructor(config?: ILankaTransportRequestConfig<TOptions>);
|
|
160
189
|
/**
|
|
161
190
|
* Parses the body, or names the failure.
|
|
@@ -180,58 +209,39 @@ declare class LankaFetchJsonRequest<TOptions = RequestInit> extends ALankaTransp
|
|
|
180
209
|
* One line, and that is the point — the factory IS the class, so a behaviour
|
|
181
210
|
* cannot exist in one style and not the other.
|
|
182
211
|
*/
|
|
183
|
-
declare const createLankaFetchJsonRequest: <TOptions =
|
|
212
|
+
declare const createLankaFetchJsonRequest: <TOptions extends TLankaRequestInit = TLankaRequestInit>(config?: ILankaTransportRequestConfig<TOptions>) => LankaFetchJsonRequest<TOptions>;
|
|
184
213
|
|
|
185
214
|
/**
|
|
186
|
-
* The
|
|
215
|
+
* The network seam: `fetch`, plus the two things `fetch` cannot be told.
|
|
187
216
|
*
|
|
188
|
-
*
|
|
189
|
-
* set `content-type`, because the browser writes it with the boundary and a
|
|
190
|
-
* hand-set header leaves the body unparseable to the server.
|
|
191
|
-
*/
|
|
192
|
-
declare class LankaFetchFormDataRequest<TOptions = RequestInit> extends ALankaTransportRequest<TOptions> {
|
|
193
|
-
constructor(config?: ILankaTransportRequestConfig<TOptions>);
|
|
194
|
-
protected parse<TReturn>(response: Response): Promise<TReturn>;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* The functional style of `LankaFetchFormDataRequest`: a multipart body, for an upload.
|
|
217
|
+
* ## Why there is ONE of these
|
|
199
218
|
*
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
*
|
|
209
|
-
*
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
request(resource: RequestInfo, options?: RequestInit): Promise<Response>;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* HTTP Fetch FormData transport implementation.
|
|
228
|
-
* Uses native fetch API optimized for FormData requests.
|
|
229
|
-
* Does not set Content-Type header (browser will set it automatically with boundary).
|
|
230
|
-
* For project-specific logic (auth, error handling, etc.), use `request` parameter
|
|
231
|
-
* in Gateway config or create a custom transport.
|
|
219
|
+
* There were three — a plain one, a JSON one and a multipart one — and the split
|
|
220
|
+
* was wrong at birth. `ILankaTransport` exists so a consumer can change the
|
|
221
|
+
* PROTOCOL: a native bridge, a socket, a double that never leaves the process.
|
|
222
|
+
* The three differed in a `content-type` header. That is not a protocol, it is an
|
|
223
|
+
* encoding, and an encoding is a property of the CALL: a gateway with fourteen
|
|
224
|
+
* JSON endpoints and one upload had no way to say so, because its request kind —
|
|
225
|
+
* and with it its transport — was fixed in its constructor. The application that
|
|
226
|
+
* hit this added a `useFormData` flag to its own options and wrote its own
|
|
227
|
+
* transport to read it.
|
|
228
|
+
*
|
|
229
|
+
* So the encoding is decided here, per call, by looking at the body. A gateway
|
|
230
|
+
* posts `FormData` to one endpoint and an object to the next, and neither it nor
|
|
231
|
+
* the request kind has to know.
|
|
232
|
+
*
|
|
233
|
+
* ## What does NOT belong here
|
|
234
|
+
*
|
|
235
|
+
* The base URL (`ALankaGateway` prefixes `apiBaseUrl`), credentials, static
|
|
236
|
+
* headers, CSRF, retry, auth refresh, idempotency keys and deadlines. Every one
|
|
237
|
+
* of those is policy around a request rather than a way of sending one, and every
|
|
238
|
+
* one is a middleware — `useRequestMiddleware`, which `@lankajs/plugin-http`
|
|
239
|
+
* occupies. A transport that grew them would be a second composition mechanism
|
|
240
|
+
* beside the one core already publishes, and "where does a header get added"
|
|
241
|
+
* would have two answers.
|
|
232
242
|
*/
|
|
233
|
-
declare class
|
|
234
|
-
request(resource: RequestInfo, options?:
|
|
243
|
+
declare class LankaFetchTransport implements ILankaTransport<TLankaRequestInit> {
|
|
244
|
+
request(resource: RequestInfo, options?: TLankaRequestInit): Promise<Response>;
|
|
235
245
|
}
|
|
236
246
|
|
|
237
247
|
/**
|
|
@@ -255,4 +265,4 @@ interface ILankaListQueryParams {
|
|
|
255
265
|
filters?: Record<string, unknown>;
|
|
256
266
|
}
|
|
257
267
|
|
|
258
|
-
export { ALankaRequest, IALankaGatewayConfig, type ILankaGatewayConfig, type ILankaGatewayContext, type ILankaListQueryParams, ILankaRequest, type ILankaTransport,
|
|
268
|
+
export { ALankaRequest, IALankaGatewayConfig, type ILankaGatewayConfig, type ILankaGatewayContext, type ILankaListQueryParams, ILankaRequest, type ILankaTransport, LankaFetchJsonRequest, LankaFetchRequest, LankaFetchTransport, TLankaExecuteOptions, TLankaQueryBuilder, TLankaRequestInit, buildLankaQueryParams, createLankaFetchJsonRequest, createLankaFetchRequest, createLankaGateway };
|
package/dist/gateway/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
lankaStandardValidator
|
|
3
|
+
} from "../chunk-D5WKKEIR.js";
|
|
1
4
|
import {
|
|
2
5
|
composeLankaRequestMiddleware
|
|
3
6
|
} from "../chunk-YR4MZXMU.js";
|
|
@@ -181,30 +184,49 @@ var ALankaTransportRequest = class extends ALankaRequest {
|
|
|
181
184
|
}
|
|
182
185
|
};
|
|
183
186
|
|
|
184
|
-
// src/gateway/
|
|
185
|
-
var
|
|
187
|
+
// src/gateway/lanka-fetch-transport/LankaFetchTransport.ts
|
|
188
|
+
var CONTENT_TYPE = "content-type";
|
|
189
|
+
var JSON_CONTENT_TYPE = "application/json";
|
|
190
|
+
var LankaFetchTransport = class {
|
|
186
191
|
async request(resource, options) {
|
|
187
|
-
if (options
|
|
188
|
-
|
|
189
|
-
let body = options.body;
|
|
190
|
-
if (typeof body === "object" && !(body instanceof FormData) && !(body instanceof Blob)) {
|
|
191
|
-
body = JSON.stringify(body);
|
|
192
|
-
}
|
|
193
|
-
headers.set("Content-Type", "application/json");
|
|
194
|
-
return await fetch(resource, {
|
|
195
|
-
...options,
|
|
196
|
-
headers,
|
|
197
|
-
body
|
|
198
|
-
});
|
|
192
|
+
if (options === void 0 || options.body === void 0 || options.body === null) {
|
|
193
|
+
return await fetch(resource, options);
|
|
199
194
|
}
|
|
200
|
-
|
|
195
|
+
const { body, headers } = options;
|
|
196
|
+
if (isFormData(body)) {
|
|
197
|
+
if (headers === void 0) return await fetch(resource, options);
|
|
198
|
+
const stripped = new Headers(headers);
|
|
199
|
+
stripped.delete(CONTENT_TYPE);
|
|
200
|
+
return await fetch(resource, { ...options, headers: stripped });
|
|
201
|
+
}
|
|
202
|
+
if (isEncodedBody(body)) return await fetch(resource, options);
|
|
203
|
+
return await fetch(resource, {
|
|
204
|
+
...options,
|
|
205
|
+
headers: withJsonContentType(headers),
|
|
206
|
+
body: JSON.stringify(body)
|
|
207
|
+
});
|
|
201
208
|
}
|
|
202
209
|
};
|
|
210
|
+
function withJsonContentType(headers) {
|
|
211
|
+
const result = new Headers(headers);
|
|
212
|
+
if (!result.has(CONTENT_TYPE)) result.set(CONTENT_TYPE, JSON_CONTENT_TYPE);
|
|
213
|
+
return result;
|
|
214
|
+
}
|
|
215
|
+
function isEncodedBody(body) {
|
|
216
|
+
if (typeof body === "string") return true;
|
|
217
|
+
if (typeof URLSearchParams !== "undefined" && body instanceof URLSearchParams) return true;
|
|
218
|
+
if (typeof Blob !== "undefined" && body instanceof Blob) return true;
|
|
219
|
+
if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) return true;
|
|
220
|
+
return typeof ReadableStream !== "undefined" && body instanceof ReadableStream;
|
|
221
|
+
}
|
|
222
|
+
function isFormData(body) {
|
|
223
|
+
return typeof FormData !== "undefined" && body instanceof FormData;
|
|
224
|
+
}
|
|
203
225
|
|
|
204
226
|
// src/gateway/request/lanka-fetch-json-request/LankaFetchJsonRequest.ts
|
|
205
227
|
var LankaFetchJsonRequest = class extends ALankaTransportRequest {
|
|
206
228
|
constructor(config = {}) {
|
|
207
|
-
super(config, () => new
|
|
229
|
+
super(config, () => new LankaFetchTransport());
|
|
208
230
|
}
|
|
209
231
|
/**
|
|
210
232
|
* Parses the body, or names the failure.
|
|
@@ -269,12 +291,22 @@ var buildLankaQueryParams = (input) => {
|
|
|
269
291
|
var ALankaGateway = class {
|
|
270
292
|
requestExecutor;
|
|
271
293
|
queryParamsHandler;
|
|
294
|
+
/**
|
|
295
|
+
* The validator a method checks a response body with.
|
|
296
|
+
*
|
|
297
|
+
* `config.validationService` when one was given, the Standard Schema port
|
|
298
|
+
* otherwise. It used to be accepted by the config and read by nothing: a
|
|
299
|
+
* consumer handing a test double to the gateway got the real validator and no
|
|
300
|
+
* error, which is the worst kind of ignored option — it looks honoured.
|
|
301
|
+
*/
|
|
302
|
+
validationService;
|
|
272
303
|
useMock;
|
|
273
304
|
basePath;
|
|
274
305
|
constructor(config) {
|
|
275
306
|
lankaLogger.printGatewayLog("Create gateway", this);
|
|
276
307
|
const flags = getLankaFlags();
|
|
277
308
|
this.useMock = config.useMock ?? flags.isMockMode ?? false;
|
|
309
|
+
this.validationService = config.validationService ?? lankaStandardValidator;
|
|
278
310
|
this.requestExecutor = config.request ?? new LankaFetchJsonRequest();
|
|
279
311
|
this.basePath = config.basePath ?? "";
|
|
280
312
|
this.queryParamsHandler = config.queryParamsHandler ?? buildLankaQueryParams;
|
|
@@ -297,8 +329,7 @@ var ALankaGateway = class {
|
|
|
297
329
|
if (path.startsWith("/")) return path;
|
|
298
330
|
if (path.startsWith("?")) return `${this.basePath}${path}`;
|
|
299
331
|
const left = this.basePath.endsWith("/") ? this.basePath.slice(0, -1) : this.basePath;
|
|
300
|
-
|
|
301
|
-
return `${left}/${right}`;
|
|
332
|
+
return `${left}/${path}`;
|
|
302
333
|
}
|
|
303
334
|
/**
|
|
304
335
|
* Prefixes the API base URL from the host contract.
|
|
@@ -364,20 +395,14 @@ var createLankaGateway = (config) => {
|
|
|
364
395
|
return config.methods({
|
|
365
396
|
endpoint: (path) => this.endpoint(path),
|
|
366
397
|
request: (path, options, mockHandler) => this.request(path, options, mockHandler),
|
|
367
|
-
buildQueryParams: (params) => this.buildQueryParams(params)
|
|
398
|
+
buildQueryParams: (params) => this.buildQueryParams(params),
|
|
399
|
+
validationService: this.validationService
|
|
368
400
|
});
|
|
369
401
|
}
|
|
370
402
|
}
|
|
371
403
|
return new FunctionalGateway(config).build();
|
|
372
404
|
};
|
|
373
405
|
|
|
374
|
-
// src/gateway/transport/lanka-fetch-transport/LankaFetchTransport.ts
|
|
375
|
-
var LankaFetchTransport = class {
|
|
376
|
-
async request(resource, options) {
|
|
377
|
-
return await fetch(resource, options);
|
|
378
|
-
}
|
|
379
|
-
};
|
|
380
|
-
|
|
381
406
|
// src/gateway/request/lanka-fetch-request/LankaFetchRequest.ts
|
|
382
407
|
var LankaFetchRequest = class extends ALankaTransportRequest {
|
|
383
408
|
constructor(config = {}) {
|
|
@@ -393,43 +418,13 @@ var createLankaFetchRequest = (config = {}) => new LankaFetchRequest(config);
|
|
|
393
418
|
|
|
394
419
|
// src/gateway/request/_factories/create-lanka-fetch-json-request/createLankaFetchJsonRequest.ts
|
|
395
420
|
var createLankaFetchJsonRequest = (config = {}) => new LankaFetchJsonRequest(config);
|
|
396
|
-
|
|
397
|
-
// src/gateway/transport/lanka-fetch-form-data-transport/LankaFetchFormDataTransport.ts
|
|
398
|
-
var LankaFetchFormDataTransport = class {
|
|
399
|
-
async request(resource, options) {
|
|
400
|
-
const formDataOptions = { ...options };
|
|
401
|
-
if (formDataOptions.body instanceof FormData && formDataOptions.headers) {
|
|
402
|
-
const headers = new Headers(formDataOptions.headers);
|
|
403
|
-
headers.delete("Content-Type");
|
|
404
|
-
formDataOptions.headers = headers;
|
|
405
|
-
}
|
|
406
|
-
return await fetch(resource, formDataOptions);
|
|
407
|
-
}
|
|
408
|
-
};
|
|
409
|
-
|
|
410
|
-
// src/gateway/request/lanka-fetch-form-data-request/LankaFetchFormDataRequest.ts
|
|
411
|
-
var LankaFetchFormDataRequest = class extends ALankaTransportRequest {
|
|
412
|
-
constructor(config = {}) {
|
|
413
|
-
super(config, () => new LankaFetchFormDataTransport());
|
|
414
|
-
}
|
|
415
|
-
parse(response) {
|
|
416
|
-
return Promise.resolve(response);
|
|
417
|
-
}
|
|
418
|
-
};
|
|
419
|
-
|
|
420
|
-
// src/gateway/request/_factories/create-lanka-fetch-form-data-request/createLankaFetchFormDataRequest.ts
|
|
421
|
-
var createLankaFetchFormDataRequest = (config = {}) => new LankaFetchFormDataRequest(config);
|
|
422
421
|
export {
|
|
423
422
|
ALankaGateway,
|
|
424
423
|
ALankaRequest,
|
|
425
|
-
LankaFetchFormDataRequest,
|
|
426
|
-
LankaFetchFormDataTransport,
|
|
427
424
|
LankaFetchJsonRequest,
|
|
428
|
-
LankaFetchJsonTransport,
|
|
429
425
|
LankaFetchRequest,
|
|
430
426
|
LankaFetchTransport,
|
|
431
427
|
buildLankaQueryParams,
|
|
432
|
-
createLankaFetchFormDataRequest,
|
|
433
428
|
createLankaFetchJsonRequest,
|
|
434
429
|
createLankaFetchRequest,
|
|
435
430
|
createLankaGateway,
|
|
@@ -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/transport/lanka-fetch-json-transport/LankaFetchJsonTransport.ts","../../src/gateway/request/lanka-fetch-json-request/LankaFetchJsonRequest.ts","../../src/gateway/_utils/build-lanka-query-params/buildLankaQueryParams.ts","../../src/gateway/_abstractions/lanka-gateway/ALankaGateway.ts","../../src/gateway/_factories/create-lanka-gateway/createLankaGateway.ts","../../src/gateway/transport/lanka-fetch-transport/LankaFetchTransport.ts","../../src/gateway/request/lanka-fetch-request/LankaFetchRequest.ts","../../src/gateway/request/_factories/create-lanka-fetch-request/createLankaFetchRequest.ts","../../src/gateway/request/_factories/create-lanka-fetch-json-request/createLankaFetchJsonRequest.ts","../../src/gateway/transport/lanka-fetch-form-data-transport/LankaFetchFormDataTransport.ts","../../src/gateway/request/lanka-fetch-form-data-request/LankaFetchFormDataRequest.ts","../../src/gateway/request/_factories/create-lanka-fetch-form-data-request/createLankaFetchFormDataRequest.ts"],"sourcesContent":["import type { TLankaErrorHandler } from \"../../../../errors/_types/TLankaErrorHandler\";\nimport type { ILankaRequest } from \"../../../_interfaces/ILankaRequest\";\nimport type { TLankaExecuteOptions } from \"../../../_types/TLankaExecuteOptions\";\nimport { handleLankaApiError } from \"../../../../errors/handle-lanka-api-error/handleLankaApiError\";\nimport { getLankaFlags } from \"../../../../config/get-lanka-flags/getLankaFlags\";\nimport { getLankaHost } from \"../../../../config/get-lanka-host/getLankaHost\";\nimport { lankaHttpInFlight } from \"../../../inflight/lankaHttpInFlight\";\nimport { LankaError } from \"../../../../errors/lanka-error/LankaError\";\nimport { getActiveRuntime } from \"../../../../_internal/active-runtime/activeRuntime\";\nimport { composeLankaRequestMiddleware } from \"../../lankaRequestMiddleware\";\nimport type { ILankaRequestContext } from \"../../lankaRequestMiddleware\";\n\n/**\n * Tags whatever the transport threw with a kind.\n *\n * Here rather than in the transports: `execute` is the single point EVERY\n * request passes through, and tagging in each of the four transports would be\n * four places to forget it.\n *\n * A real `fetch` throws `TypeError` on a broken connection and a `DOMException`\n * named `AbortError` on cancellation; it does not throw on a status code at all.\n *\n * An already-tagged error is NOT re-tagged: a request-policy plugin may report\n * `domain`, and rewriting that to `network` would lose the one thing the kind\n * exists for.\n */\nfunction classifyTransportError(error: unknown, timedOut: boolean): LankaError {\n\tif (LankaError.is(error)) return error;\n\n\t// The name is read off ANYTHING, not only off `Error`.\n\t//\n\t// `DOMException` — how `fetch` reports cancellation — does not extend `Error`\n\t// everywhere: in a browser yes, in jsdom no. An `instanceof Error` check lets\n\t// cancellation past the tagging, and a raw `AbortError` reaches the app with\n\t// neither `kind` nor `status`: retry policy reads it as non-retryable and the\n\t// app as an unknown error, so a cancelled request is shown to the user as a\n\t// failure.\n\tconst name = readErrorName(error);\n\tconst isAbort = name === \"AbortError\" || name === \"TimeoutError\";\n\tif (isAbort) {\n\t\t// Only whoever assembled the lifetime knows who aborted: `AbortSignal` has\n\t\t// one `abort` for everyone. The distinction carries a decision — a timeout\n\t\t// is shown, a cancellation is not.\n\t\tconst timedOutHere = timedOut || name === \"TimeoutError\";\n\t\treturn new LankaError({\n\t\t\tkind: timedOutHere ? \"timeout\" : \"aborted\",\n\t\t\tmessage: timedOutHere ? getLankaHost().timeoutErrorMessage() : readErrorMessage(error),\n\t\t\tcause: error,\n\t\t});\n\t}\n\n\t// `network` ONLY for what looks like a transport failure. A real `fetch`\n\t// throws `TypeError`; everything else comes from code we did not write — the\n\t// app's error handler, a response transformer, a broken plugin — and calling\n\t// that a network failure would invite the user to retry a request that\n\t// arrived and was processed.\n\t//\n\t// Unknown stays unknown and passes through. The framework asserts only what\n\t// it knows.\n\tif (error instanceof TypeError) {\n\t\treturn new LankaError({\n\t\t\tkind: \"network\",\n\t\t\tmessage: getLankaHost().networkErrorMessage(),\n\t\t\tissues: [error.message],\n\t\t\tcause: error,\n\t\t});\n\t}\n\n\treturn error as LankaError;\n}\n\n/** The error name, off `Error`, `DOMException` or anything else carrying one. */\nfunction readErrorName(error: unknown): string | undefined {\n\tif (typeof error !== \"object\" || error === null) return undefined;\n\tconst name: unknown = (error as { name?: unknown }).name;\n\treturn typeof name === \"string\" ? name : undefined;\n}\n\n/**\n * The error text, when there is one.\n *\n * An object without `message` is not stringified: `String({})` yields\n * `[object Object]`, which occupies the message slot and says nothing. Empty is\n * more honest.\n */\nfunction readErrorMessage(error: unknown): string {\n\tif (typeof error === \"string\") return error;\n\tif (typeof error !== \"object\" || error === null) return \"\";\n\tconst message: unknown = (error as { message?: unknown }).message;\n\treturn typeof message === \"string\" ? message : \"\";\n}\n\n/**\n * How a gateway request goes on the wire.\n *\n * A subclass declares one method, `request()`, and does only its own work there:\n * `LankaFetchJsonRequest` returns parsed JSON, `LankaFetchFormDataRequest`\n * returns the whole response, a custom transport returns whatever it likes.\n *\n * Overriding `request()` customises the request flow, failure handling, mock\n * substitution, response transformation and log interception.\n */\nexport abstract class ALankaRequest<TOptions = RequestInit> implements ILankaRequest<TOptions> {\n\tprotected readonly errorHandler?: TLankaErrorHandler;\n\tprotected readonly useMock: boolean;\n\n\tprotected constructor(config: { errorHandler?: TLankaErrorHandler; useMock?: boolean }) {\n\t\tconst flags = getLankaFlags();\n\n\t\t// The default error-body handler lives HERE because the request is the only\n\t\t// thing that sees the `Response`. Put on the gateway it would sit in a field\n\t\t// nobody reads — the request takes the handler from ITS OWN config — and a\n\t\t// consumer passing `errorHandler` to the gateway would get silence.\n\t\t//\n\t\t// The parse is cheap: core reads the body once and takes `message` from it;\n\t\t// backend-specific shapes are parsed by `@lankajs/plugin-http`.\n\t\tthis.errorHandler = config.errorHandler ?? handleLankaApiError;\n\n\t\tthis.useMock = config.useMock ?? flags.isMockMode ?? false;\n\t}\n\n\t/**\n\t * Performs the request and returns its result — a response, JSON or a custom\n\t * type.\n\t *\n\t * @param endpoint Full URL\n\t * @param options Transport-specific options\n\t * @param mockHandler Mock, when there is one\n\t */\n\tprotected abstract request<TReturn = Response>(\n\t\tendpoint: string,\n\t\toptions?: TOptions,\n\t\tmockHandler?: () => Promise<TReturn>,\n\t): Promise<TReturn>;\n\n\t/**\n\t * The single point EVERY gateway request passes through.\n\t *\n\t * Hence the in-flight accounting here: intent prefetch stands down while\n\t * anything else is on the wire. The `finally` matters more than the increment —\n\t * a rejected request that never decremented would disable prefetching for the\n\t * rest of the session.\n\t */\n\tpublic async execute<TReturn = Response>(\n\t\tendpoint: string,\n\t\toptions?: TLankaExecuteOptions<TOptions>,\n\t\tmockHandler?: () => Promise<TReturn>,\n\t): Promise<TReturn> {\n\t\tconst runtime = getActiveRuntime();\n\t\tconst { signal, timeoutMs, ...rest } = (options ?? {}) as TLankaExecuteOptions<TOptions>;\n\t\t// The caller's deadline travels IN THE CONTEXT, not only in the closure:\n\t\t// otherwise a policy assigning deadlines per request class could not tell\n\t\t// \"no deadline given\" from \"given by the caller\" and would override an\n\t\t// explicit request with a blanket default.\n\t\tconst fallbackDeadline = runtime?.requestTimeoutMs;\n\n\t\t// No options passed means none are produced. Destructuring yields `{}` even\n\t\t// from `undefined`, and handing that empty object to the transport would\n\t\t// change the request: \"no options\" and \"empty options\" are different\n\t\t// statements, and the transport is entitled to tell them apart.\n\t\tconst passedOptions: unknown = options === undefined ? undefined : rest;\n\n\t\t/*\n\t\t * The lifetime is assembled PER ATTEMPT, not per call, and that does two\n\t\t * things at once.\n\t\t *\n\t\t * Under retry a shared deadline would start the third attempt with whatever\n\t\t * the first two left, so the retry aborts before reaching the server.\n\t\t *\n\t\t * And it is the only way to let middleware set the deadline: `ctx.timeoutMs`\n\t\t * is read on every attempt, which is how a request-policy plugin assigns a\n\t\t * deadline per request CLASS — a file upload and a list read cannot share\n\t\t * one value.\n\t\t *\n\t\t * Caller cancellation stays end-to-end: one `signal` for all attempts.\n\t\t */\n\t\tlet lastTimedOut = false;\n\n\t\t// Tagging happens INSIDE, around the request itself, not in the outer\n\t\t// catch: middleware must receive an already-tagged error, or a retrying\n\t\t// middleware cannot tell a network failure from a domain rejection and\n\t\t// retries what must not be retried. The outer catch stays as a backstop.\n\t\tconst perform = async (ctx: ILankaRequestContext): Promise<unknown> => {\n\t\t\tconst lifetime = createRequestLifetime(signal, ctx.timeoutMs ?? fallbackDeadline);\n\t\t\ttry {\n\t\t\t\treturn await this.request<TReturn>(\n\t\t\t\t\tctx.endpoint,\n\t\t\t\t\twithSignal(ctx.options, lifetime.signal) as TOptions,\n\t\t\t\t\tmockHandler,\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\tlastTimedOut = lifetime.timedOut();\n\t\t\t\tthrow classifyTransportError(error, lastTimedOut);\n\t\t\t} finally {\n\t\t\t\tlifetime.dispose();\n\t\t\t}\n\t\t};\n\n\t\tconst run = composeLankaRequestMiddleware(runtime?.requestMiddleware ?? [], perform);\n\n\t\tlankaHttpInFlight.begin();\n\t\ttry {\n\t\t\t// The chain runs INSIDE the same guard as the request. A catch placed\n\t\t\t// outside would leave a permanent +1 when a plugin throws, disabling\n\t\t\t// prefetch for the rest of the session — the very defect this `finally`\n\t\t\t// exists to prevent.\n\t\t\treturn (await run({\n\t\t\t\tendpoint,\n\t\t\t\toptions: passedOptions,\n\t\t\t\tattempt: 1,\n\t\t\t\ttimeoutMs,\n\t\t\t})) as TReturn;\n\t\t} catch (error) {\n\t\t\tthrow classifyTransportError(error, lastTimedOut);\n\t\t} finally {\n\t\t\tlankaHttpInFlight.end();\n\t\t}\n\t}\n}\n\ninterface IRequestLifetime {\n\treadonly signal: AbortSignal | undefined;\n\t/** Whether OUR timer aborted the request rather than the caller. */\n\ttimedOut(): boolean;\n\tdispose(): void;\n}\n\n/**\n * Combines the caller's signal and our own timeout into one request lifetime.\n *\n * The outcome looks the same — an interrupted request — but the decisions\n * differ: a timeout is shown and offered for retry, a user cancellation is not\n * shown at all. `AbortSignal` does not distinguish them: one `abort`, whose\n * reason belongs to whoever got there first. Hence the private flag.\n *\n * Not `AbortSignal.timeout` alone: it cannot combine with a foreign signal\n * without `AbortSignal.any`, which older engines lack. Assembling by hand works\n * everywhere and costs one listener.\n */\nfunction createRequestLifetime(\n\texternal: AbortSignal | undefined,\n\ttimeoutMs: number | undefined,\n): IRequestLifetime {\n\tif (!external && !timeoutMs) {\n\t\treturn { signal: undefined, timedOut: () => false, dispose: () => undefined };\n\t}\n\n\tconst controller = new AbortController();\n\tlet expired = false;\n\n\tconst timer =\n\t\ttimeoutMs === undefined\n\t\t\t? undefined\n\t\t\t: setTimeout(() => {\n\t\t\t\t\texpired = true;\n\t\t\t\t\tcontroller.abort(new DOMException(\"Request timed out\", \"TimeoutError\"));\n\t\t\t\t}, timeoutMs);\n\n\tconst onExternalAbort = (): void => {\n\t\tcontroller.abort(external?.reason);\n\t};\n\n\tif (external) {\n\t\tif (external.aborted) onExternalAbort();\n\t\telse external.addEventListener(\"abort\", onExternalAbort, { once: true });\n\t}\n\n\treturn {\n\t\tsignal: controller.signal,\n\t\ttimedOut: () => expired,\n\t\tdispose: () => {\n\t\t\tif (timer !== undefined) clearTimeout(timer);\n\t\t\texternal?.removeEventListener(\"abort\", onExternalAbort);\n\t\t},\n\t};\n}\n\n/**\n * Attaches the signal to the options, inventing nothing.\n *\n * With no options and no signal the transport receives `undefined` — exactly\n * what the caller passed. An empty object instead looks harmless but is a\n * different statement, and the transport is entitled to tell them apart.\n */\nfunction withSignal(options: unknown, signal: AbortSignal | undefined): unknown {\n\tif (signal === undefined) return options;\n\treturn { ...(options ?? {}), signal };\n}\n","import { ALankaRequest } from \"../lanka-request/ALankaRequest\";\nimport { LankaError } from \"../../../../errors/lanka-error/LankaError\";\nimport { getLankaFlags } from \"../../../../config/get-lanka-flags/getLankaFlags\";\nimport { getLankaHost } from \"../../../../config/get-lanka-host/getLankaHost\";\nimport type { ILankaTransport } from \"../../../_interfaces/ILankaTransport\";\nimport type { TLankaErrorHandler } from \"../../../../errors/_types/TLankaErrorHandler\";\n\nexport interface ILankaTransportRequestConfig<TOptions> {\n\ttransport?: ILankaTransport<TOptions>;\n\terrorHandler?: TLankaErrorHandler;\n\tuseMock?: boolean;\n}\n\n/**\n * The shape every fetch-backed request has: mock, send, check, parse.\n *\n * The three concrete requests differ in exactly two places — which transport\n * they default to, and how they turn a successful `Response` into a value. Both\n * are parameters of this template, so a fourth kind is a subclass with one\n * method rather than a fourth copy of the sequence.\n */\nexport abstract class ALankaTransportRequest<\n\tTOptions = RequestInit,\n> extends ALankaRequest<TOptions> {\n\tprotected readonly transport: ILankaTransport<TOptions>;\n\n\tprotected constructor(\n\t\tconfig: ILankaTransportRequestConfig<TOptions>,\n\t\tcreateDefaultTransport: () => ILankaTransport<TOptions>,\n\t) {\n\t\tconst flags = getLankaFlags();\n\n\t\tsuper({\n\t\t\terrorHandler: config.errorHandler,\n\t\t\tuseMock: config.useMock ?? flags.isMockMode ?? false,\n\t\t});\n\n\t\tthis.transport = config.transport ?? createDefaultTransport();\n\t}\n\n\tprotected async request<TReturn>(\n\t\tendpoint: string,\n\t\toptions?: TOptions,\n\t\tmockHandler?: () => Promise<TReturn>,\n\t): Promise<TReturn> {\n\t\tif (this.useMock && mockHandler) {\n\t\t\treturn await mockHandler();\n\t\t}\n\n\t\tconst response = await this.transport.request(endpoint, options);\n\t\tif (!response.ok) return await this.refuse(response);\n\n\t\treturn await this.parse<TReturn>(response);\n\t}\n\n\t/**\n\t * Turns a successful response into the value the caller asked for.\n\t *\n\t * The one step that genuinely differs between request kinds.\n\t */\n\tprotected abstract parse<TReturn>(response: Response): Promise<TReturn>;\n\n\t/**\n\t * Refuses an unsuccessful response, and never returns.\n\t *\n\t * `TLankaErrorHandler` is typed `Promise<never>` — a handler must throw. It is\n\t * still CALLED and then followed by a throw, because a handler that breaks its\n\t * contract and returns would otherwise hand `undefined` back as if the request\n\t * had succeeded, and a non-value must never become a value.\n\t */\n\tprotected async refuse(response: Response): Promise<never> {\n\t\tif (this.errorHandler) {\n\t\t\tawait this.errorHandler(response);\n\t\t}\n\n\t\t// Kind `http`, not a bare Error: the server answered, and answered with a\n\t\t// code. Without the kind this reads as a network failure, and the user is\n\t\t// offered a retry of a request that already got a meaningful answer.\n\t\tthrow new LankaError({\n\t\t\tkind: \"http\",\n\t\t\tmessage: getLankaHost().httpErrorMessage(response.status),\n\t\t\tstatus: response.status,\n\t\t});\n\t}\n}\n","import type { ILankaTransport } from \"../../_interfaces/ILankaTransport\";\n\n/**\n * HTTP Fetch JSON transport implementation.\n * Uses native fetch API with JSON-specific headers.\n * Automatically sets Content-Type to application/json for requests with body.\n * For project-specific logic (auth, error handling, etc.), use `request` parameter\n * in Gateway config or create a custom transport.\n */\nexport class LankaFetchJsonTransport implements ILankaTransport<RequestInit> {\n\tasync request(resource: RequestInfo, options?: RequestInit): Promise<Response> {\n\t\t// Only set Content-Type if body exists and is not FormData\n\t\tif (options?.body && !(options.body instanceof FormData)) {\n\t\t\tconst headers = new Headers(options.headers);\n\n\t\t\t// If body is object, stringify it\n\t\t\tlet body = options.body;\n\t\t\tif (\n\t\t\t\ttypeof body === \"object\" &&\n\t\t\t\t!(body instanceof FormData) &&\n\t\t\t\t!(body instanceof Blob)\n\t\t\t) {\n\t\t\t\tbody = JSON.stringify(body);\n\t\t\t}\n\n\t\t\theaders.set(\"Content-Type\", \"application/json\");\n\n\t\t\treturn await fetch(resource, {\n\t\t\t\t...options,\n\t\t\t\theaders,\n\t\t\t\tbody,\n\t\t\t});\n\t\t}\n\n\t\t// No body or FormData - use options as-is\n\t\treturn await fetch(resource, options);\n\t}\n}\n","import { ALankaTransportRequest } from \"../_abstractions/lanka-transport-request/ALankaTransportRequest\";\nimport type { ILankaTransportRequestConfig } from \"../_abstractions/lanka-transport-request/ALankaTransportRequest\";\nimport type { ILankaTransport } from \"../../_interfaces/ILankaTransport\";\nimport { LankaFetchJsonTransport } from \"../../transport/lanka-fetch-json-transport/LankaFetchJsonTransport\";\nimport { LankaError } from \"../../../errors/lanka-error/LankaError\";\n\n/** The JSON request: parses the body, and refuses a body that is not JSON. */\n/**\n * A request kind that answers a parsed JSON body — what most endpoints return.\n *\n * A gateway holds one, and is handed it rather than constructing it, which is\n * what lets a test give the same gateway a transport that never leaves the\n * process. `createLankaFetchJsonRequest()` builds the same class.\n */\nexport class LankaFetchJsonRequest<\n\tTOptions = RequestInit,\n> extends ALankaTransportRequest<TOptions> {\n\tconstructor(config: ILankaTransportRequestConfig<TOptions> = {}) {\n\t\tsuper(config, () => new LankaFetchJsonTransport() as ILankaTransport<TOptions>);\n\t}\n\n\t/**\n\t * Parses the body, or names the failure.\n\t *\n\t * The content type is read for the ERROR MESSAGE only. Using it to choose\n\t * between two parse paths, where the second answers `{}` for any non-empty\n\t * body it cannot parse, fails silently: the caller's schema is the first thing\n\t * to notice, and the caller's SCREEN is where it shows up.\n\t *\n\t * A measured case: a dev server whose `/api` fell through to the SPA fallback\n\t * answered `200 text/html` with `index.html`. Turned into `{}`, the schema\n\t * refused it and a person read a validator's issue list on the sign-in card. A\n\t * body a JSON transport cannot parse is never a value — it is a misrouted\n\t * request, and saying so names the actual failure.\n\t */\n\tprotected async parse<TReturn>(response: Response): Promise<TReturn> {\n\t\tconst contentType = response.headers.get(\"content-type\");\n\n\t\tconst text = await response.text();\n\t\tif (!text) {\n\t\t\treturn undefined as TReturn;\n\t\t}\n\n\t\ttry {\n\t\t\treturn JSON.parse(text) as TReturn;\n\t\t} catch (error) {\n\t\t\t// `schema`, not `network`: the request arrived, the server answered, and\n\t\t\t// the answer was the wrong shape. Not cosmetic — a network failure\n\t\t\t// invites a retry, while retrying a broken contract is pointless and\n\t\t\t// blaming the user for it more so.\n\t\t\tthrow new LankaError({\n\t\t\t\tkind: \"schema\",\n\t\t\t\tmessage:\n\t\t\t\t\t`Failed to parse JSON response (content-type: ${contentType ?? \"none\"}): ` +\n\t\t\t\t\t`${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\tcause: error,\n\t\t\t});\n\t\t}\n\t}\n}\n","import { TLankaQueryParams } from \"../../_types/TLankaQueryParams\";\nimport { TLankaQueryBuilder } from \"../../_types/TLankaQueryBuilder\";\n\n/**\n * The query string a gateway sends, from the object a method was called with.\n *\n * Arrays become `key[]` repeated, nested objects become `key[inner]`: the shape\n * most JSON APIs read back without being told about it.\n *\n * Plain loops over `Object.keys` rather than `Object.entries` and `forEach`: this\n * runs on every request that carries a filter, and the pair array `entries`\n * builds — two allocations per key, thrown away immediately — is a cost with\n * nothing to show for it.\n */\nexport const buildLankaQueryParams: TLankaQueryBuilder = <T extends Record<string, unknown>>(\n\tinput: T,\n): URLSearchParams => {\n\tconst params = new URLSearchParams();\n\n\tconst append = (key: string, value: TLankaQueryParams): void => {\n\t\tif (value == null) return;\n\n\t\tif (Array.isArray(value)) {\n\t\t\t// The bracketed key once for the whole array, not once per element.\n\t\t\tconst itemKey = `${key}[]`;\n\t\t\tfor (let index = 0; index < value.length; index += 1) append(itemKey, value[index]);\n\n\t\t\treturn;\n\t\t}\n\n\t\tif (typeof value === \"object\") {\n\t\t\tconst inner = value as Record<string, TLankaQueryParams>;\n\t\t\tconst innerKeys = Object.keys(inner);\n\n\t\t\tfor (let index = 0; index < innerKeys.length; index += 1) {\n\t\t\t\tconst innerKey = innerKeys[index];\n\t\t\t\tappend(`${key}[${innerKey}]`, inner[innerKey]);\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\tparams.append(key, String(value));\n\t};\n\n\tconst source = input as Record<string, TLankaQueryParams>;\n\tconst keys = Object.keys(source);\n\n\tfor (let index = 0; index < keys.length; index += 1) append(keys[index], source[keys[index]]);\n\n\treturn params;\n};\n","import type { IALankaGatewayConfig } from \"../../_interfaces/IALankaGatewayConfig\";\nimport { lankaLogger } from \"../../../logger/lanka-logger/LankaLogger\";\nimport { LankaFetchJsonRequest } from \"../../request/lanka-fetch-json-request/LankaFetchJsonRequest\";\nimport type { ILankaRequest } from \"../../_interfaces/ILankaRequest\";\nimport type { TLankaExecuteOptions } from \"../../_types/TLankaExecuteOptions\";\nimport { buildLankaQueryParams } from \"../../_utils/build-lanka-query-params/buildLankaQueryParams\";\nimport { TLankaQueryParams } from \"../../_types/TLankaQueryParams\";\nimport { TLankaQueryBuilder } from \"../../_types/TLankaQueryBuilder\";\nimport { getLankaFlags } from \"../../../config/get-lanka-flags/getLankaFlags\";\nimport { getLankaHost } from \"../../../config/get-lanka-host/getLankaHost\";\n\nexport abstract class ALankaGateway<TOptions = RequestInit> {\n\tprotected requestExecutor: ILankaRequest<TOptions>;\n\tprotected queryParamsHandler: TLankaQueryBuilder;\n\n\tprotected readonly useMock: boolean;\n\tprotected readonly basePath: string;\n\n\tprotected constructor(config: IALankaGatewayConfig<TOptions>) {\n\t\tlankaLogger.printGatewayLog(\"Create gateway\", this);\n\t\tconst flags = getLankaFlags();\n\t\tthis.useMock = config.useMock ?? flags.isMockMode ?? false;\n\n\t\t// A gateway with nothing said about transport talks JSON over `fetch`, which\n\t\t// is what almost every one of them does. Supplying a request is how a gateway\n\t\t// stops being ordinary — a raw `Response`, a multipart upload, a transport\n\t\t// that never leaves the process — and that stays a decision rather than a\n\t\t// line every gateway has to carry to be born.\n\t\tthis.requestExecutor = config.request ?? new LankaFetchJsonRequest<TOptions>();\n\n\t\tthis.basePath = config.basePath ?? \"\";\n\t\tthis.queryParamsHandler = config.queryParamsHandler ?? buildLankaQueryParams;\n\t}\n\n\t/**\n\t * Resolves endpoint for request.\n\t * - Absolute paths (starting with \"/\") are returned as-is\n\t * - Relative paths are joined with basePath\n\t * - Query-only strings like \"?a=1\" are attached to basePath\n\t */\n\tprotected endpoint(path: string = \"\"): string {\n\t\t// An absolute URL is detected BEFORE joining with `basePath`, not after:\n\t\t// otherwise `https://other.host/health` first becomes\n\t\t// `/things/https://other.host/health` and there is nothing left to detect.\n\t\tif (isAbsoluteUrl(path)) return path;\n\n\t\treturn this.withApiBase(this.resolvePath(path));\n\t}\n\n\t/**\n\t * Joins `basePath` and the method path.\n\t */\n\tprivate resolvePath(path: string): string {\n\t\tif (!path) return this.basePath;\n\n\t\tif (path.startsWith(\"/\")) return path;\n\n\t\tif (path.startsWith(\"?\")) return `${this.basePath}${path}`;\n\n\t\tconst left = this.basePath.endsWith(\"/\") ? this.basePath.slice(0, -1) : this.basePath;\n\t\tconst right = path.startsWith(\"/\") ? path.slice(1) : path;\n\t\treturn `${left}/${right}`;\n\t}\n\n\t/**\n\t * Prefixes the API base URL from the host contract.\n\t *\n\t * Here rather than in every consumer: otherwise each consumer knows the URL\n\t * and the framework does not, and a realtime plugin would have to know a\n\t * specific application's build.\n\t *\n\t * Declaring the field and not using it would be worse than not declaring it: a\n\t * declaration nothing is built from is a second truth, free to diverge from\n\t * the first.\n\t *\n\t * An absolute URL never reaches here — `endpoint()` filters it out before the\n\t * join.\n\t */\n\tprivate withApiBase(path: string): string {\n\t\tconst base = withoutTrailingSlashes(getLankaHost().apiBaseUrl);\n\t\tif (!base) return path;\n\t\tif (!path) return base;\n\n\t\treturn path.startsWith(\"/\") ? `${base}${path}` : `${base}/${path}`;\n\t}\n\n\tprotected buildQueryParams<T extends object>(params: T): URLSearchParams {\n\t\treturn this.queryParamsHandler(params as Record<string, TLankaQueryParams>);\n\t}\n\n\tprotected async request<TReturn = unknown>(\n\t\tpath: string,\n\t\toptions?: TLankaExecuteOptions<TOptions>,\n\t\tmockHandler?: () => Promise<TReturn>,\n\t): Promise<TReturn> {\n\t\treturn this.requestExecutor.execute<TReturn>(this.endpoint(path), options, mockHandler);\n\t}\n\n\t/**\n\t * Allows to replace request implementation at runtime (e.g. feature flags / tests).\n\t * If you prefer static customization - override `request()` in a subclass.\n\t */\n\tprotected setRequest(request: ILankaRequest<TOptions>): void {\n\t\tthis.requestExecutor = request;\n\t}\n\n\tprotected setQueryParamsHandler(handler: TLankaQueryBuilder): void {\n\t\tthis.queryParamsHandler = handler;\n\t}\n}\n\n/**\n * A scheme plus `//` — a URL that already knows where it is going.\n *\n * A standalone function rather than a method: it is not about a particular\n * gateway, and `endpoint()` needs it before any joining.\n */\nfunction isAbsoluteUrl(path: string): boolean {\n\t// The cheap half first: a scheme needs `://`, and `includes` answers without\n\t// starting the regex engine. Every relative path an application writes — which\n\t// is nearly all of them — stops on this line.\n\tif (!path.includes(\"://\")) return false;\n\n\treturn /^[a-z][a-z\\d+\\-.]*:\\/\\//i.test(path);\n}\n\n/**\n * The API base without its trailing slashes, remembered between calls.\n *\n * The host answers the same string for the life of an application, and trimming\n * it is a regex replace otherwise run on every endpoint of every request. One\n * entry is enough: there is one active host, and a second framework in the same\n * process simply replaces what is remembered here.\n */\nlet lastRawBase: string | null = null;\nlet lastTrimmedBase = \"\";\n\nfunction withoutTrailingSlashes(base: string): string {\n\tif (base !== lastRawBase) {\n\t\tlastRawBase = base;\n\t\tlastTrimmedBase = base.replace(/\\/+$/, \"\");\n\t}\n\n\treturn lastTrimmedBase;\n}\n","import { ALankaGateway } from \"../../_abstractions/lanka-gateway/ALankaGateway\";\nimport type { IALankaGatewayConfig } from \"../../_interfaces/IALankaGatewayConfig\";\nimport type { ILankaGatewayContext } from \"../../_interfaces/ILankaGatewayContext\";\n\n/** What a gateway is built from, whichever style builds it. */\nexport interface ILankaGatewayConfig<\n\tTOptions,\n\tTMethods extends object,\n> extends IALankaGatewayConfig<TOptions> {\n\t/** The endpoints this gateway offers, written over its own surface. */\n\tmethods: (context: ILankaGatewayContext<TOptions>) => TMethods;\n}\n\n/**\n * A gateway, without writing a class.\n *\n * The bridge below is the whole mechanism, and it lives here rather than on the\n * base for two reasons. The language reads `protected` from inside a deriving\n * class body and nowhere else, so a factory outside the hierarchy could only\n * reach the public half — the wrong one. And a `toStyleContext` ON the base\n * would put `TOptions` in a method's parameter position, making the class\n * invariant in it: every `ALankaGateway<unknown>` the locator holds would stop\n * accepting a gateway typed for `RequestInit`.\n *\n * One implementation: what comes back is an instance of `ALankaGateway`, so a\n * behaviour fix reaches both styles at once.\n */\nexport const createLankaGateway = <TOptions, TMethods extends object>(\n\tconfig: ILankaGatewayConfig<TOptions, TMethods>,\n): TMethods => {\n\tclass FunctionalGateway extends ALankaGateway<TOptions> {\n\t\t// The base keeps a protected constructor — it is abstract, and a consumer\n\t\t// reaching for `new ALankaGateway()` would get an object with no endpoints.\n\t\t// A subclass may widen it, and this one is the subclass.\n\t\tpublic constructor(gatewayConfig: IALankaGatewayConfig<TOptions>) {\n\t\t\tsuper(gatewayConfig);\n\t\t}\n\n\t\tpublic build(): TMethods {\n\t\t\treturn config.methods({\n\t\t\t\tendpoint: (path) => this.endpoint(path),\n\t\t\t\trequest: (path, options, mockHandler) => this.request(path, options, mockHandler),\n\t\t\t\tbuildQueryParams: (params) => this.buildQueryParams(params),\n\t\t\t});\n\t\t}\n\t}\n\n\treturn new FunctionalGateway(config).build();\n};\n","import type { ILankaTransport } from \"../../_interfaces/ILankaTransport\";\n\n/**\n * HTTP Fetch transport implementation.\n * Uses native fetch API without any project-specific decorators.\n * For project-specific logic (auth, error handling, etc.), use `request` parameter\n * in Gateway config or create a custom transport.\n */\nexport class LankaFetchTransport implements ILankaTransport<RequestInit> {\n\tasync request(resource: RequestInfo, options?: RequestInit): Promise<Response> {\n\t\treturn await fetch(resource, options);\n\t}\n}\n","import { ALankaTransportRequest } from \"../_abstractions/lanka-transport-request/ALankaTransportRequest\";\nimport type { ILankaTransportRequestConfig } from \"../_abstractions/lanka-transport-request/ALankaTransportRequest\";\nimport type { ILankaTransport } from \"../../_interfaces/ILankaTransport\";\nimport { LankaFetchTransport } from \"../../transport/lanka-fetch-transport/LankaFetchTransport\";\n\n/**\n * The raw request: hands the `Response` back untouched.\n *\n * The minimal, extensible case — a caller wanting headers, a stream or a blob\n * reads them off the response itself.\n */\nexport class LankaFetchRequest<TOptions = RequestInit> extends ALankaTransportRequest<TOptions> {\n\tconstructor(config: ILankaTransportRequestConfig<TOptions> = {}) {\n\t\tsuper(config, () => new LankaFetchTransport() as ILankaTransport<TOptions>);\n\t}\n\n\tprotected parse<TReturn>(response: Response): Promise<TReturn> {\n\t\treturn Promise.resolve(response as unknown as TReturn);\n\t}\n}\n","import { LankaFetchRequest } from \"../../lanka-fetch-request/LankaFetchRequest\";\nimport type { ILankaTransportRequestConfig } from \"../../_abstractions/lanka-transport-request/ALankaTransportRequest\";\n\n/**\n * The functional style of `LankaFetchRequest`: the raw `Response`, for a download or a stream.\n *\n * One line, and that is the point — the factory IS the class, so a behaviour\n * cannot exist in one style and not the other.\n */\nexport const createLankaFetchRequest = <TOptions = RequestInit>(\n\tconfig: ILankaTransportRequestConfig<TOptions> = {},\n): LankaFetchRequest<TOptions> => new LankaFetchRequest<TOptions>(config);\n","import { LankaFetchJsonRequest } from \"../../lanka-fetch-json-request/LankaFetchJsonRequest\";\nimport type { ILankaTransportRequestConfig } from \"../../_abstractions/lanka-transport-request/ALankaTransportRequest\";\n\n/**\n * The functional style of `LankaFetchJsonRequest`: a JSON body, which is what most endpoints answer.\n *\n * One line, and that is the point — the factory IS the class, so a behaviour\n * cannot exist in one style and not the other.\n */\nexport const createLankaFetchJsonRequest = <TOptions = RequestInit>(\n\tconfig: ILankaTransportRequestConfig<TOptions> = {},\n): LankaFetchJsonRequest<TOptions> => new LankaFetchJsonRequest<TOptions>(config);\n","import type { ILankaTransport } from \"../../_interfaces/ILankaTransport\";\n\n/**\n * HTTP Fetch FormData transport implementation.\n * Uses native fetch API optimized for FormData requests.\n * Does not set Content-Type header (browser will set it automatically with boundary).\n * For project-specific logic (auth, error handling, etc.), use `request` parameter\n * in Gateway config or create a custom transport.\n */\nexport class LankaFetchFormDataTransport implements ILankaTransport<RequestInit> {\n\tasync request(resource: RequestInfo, options?: RequestInit): Promise<Response> {\n\t\tconst formDataOptions: RequestInit = { ...options };\n\n\t\t// Remove Content-Type header if body is FormData (browser will set it with boundary)\n\t\tif (formDataOptions.body instanceof FormData && formDataOptions.headers) {\n\t\t\tconst headers = new Headers(formDataOptions.headers);\n\t\t\theaders.delete(\"Content-Type\");\n\t\t\tformDataOptions.headers = headers;\n\t\t}\n\n\t\treturn await fetch(resource, formDataOptions);\n\t}\n}\n","import { ALankaTransportRequest } from \"../_abstractions/lanka-transport-request/ALankaTransportRequest\";\nimport type { ILankaTransportRequestConfig } from \"../_abstractions/lanka-transport-request/ALankaTransportRequest\";\nimport type { ILankaTransport } from \"../../_interfaces/ILankaTransport\";\nimport { LankaFetchFormDataTransport } from \"../../transport/lanka-fetch-form-data-transport/LankaFetchFormDataTransport\";\n\n/**\n * The multipart request: hands the `Response` back untouched.\n *\n * Differs from `LankaFetchRequest` only in its transport — the one that must NOT\n * set `content-type`, because the browser writes it with the boundary and a\n * hand-set header leaves the body unparseable to the server.\n */\nexport class LankaFetchFormDataRequest<\n\tTOptions = RequestInit,\n> extends ALankaTransportRequest<TOptions> {\n\tconstructor(config: ILankaTransportRequestConfig<TOptions> = {}) {\n\t\tsuper(config, () => new LankaFetchFormDataTransport() as ILankaTransport<TOptions>);\n\t}\n\n\tprotected parse<TReturn>(response: Response): Promise<TReturn> {\n\t\treturn Promise.resolve(response as unknown as TReturn);\n\t}\n}\n","import { LankaFetchFormDataRequest } from \"../../lanka-fetch-form-data-request/LankaFetchFormDataRequest\";\nimport type { ILankaTransportRequestConfig } from \"../../_abstractions/lanka-transport-request/ALankaTransportRequest\";\n\n/**\n * The functional style of `LankaFetchFormDataRequest`: a multipart body, for an upload.\n *\n * One line, and that is the point — the factory IS the class, so a behaviour\n * cannot exist in one style and not the other.\n */\nexport const createLankaFetchFormDataRequest = <TOptions = RequestInit>(\n\tconfig: ILankaTransportRequestConfig<TOptions> = {},\n): LankaFetchFormDataRequest<TOptions> => new LankaFetchFormDataRequest<TOptions>(config);\n"],"mappings":"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","names":[]}
|
|
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":[]}
|