lanka 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +242 -0
- package/dist/ALankaGateway-ExlRGT3D.d.ts +122 -0
- package/dist/ALankaSharedStore-B7uepuuk.d.ts +29 -0
- package/dist/ILankaApiError-zI_2bNs0.d.ts +16 -0
- package/dist/ILankaRuntimeConfig-Vl436GWK.d.ts +62 -0
- package/dist/ILankaScenarioMetadata-Bu-yggTZ.d.ts +14 -0
- package/dist/ILankaScenarioVM-DuCyPoyT.d.ts +260 -0
- package/dist/LankaError-B1HtuIkw.d.ts +86 -0
- package/dist/LankaScenarioLocator-BGQHwf3n.d.ts +144 -0
- package/dist/LankaSharedStoreLocator-MvCpav5F.d.ts +126 -0
- package/dist/TLankaErrorHandler-Yfqtdh1M.d.ts +3 -0
- package/dist/_extend/index.d.ts +34 -0
- package/dist/_extend/index.js +36 -0
- package/dist/_extend/index.js.map +1 -0
- package/dist/_internal/index.d.ts +43 -0
- package/dist/_internal/index.js +34 -0
- package/dist/_internal/index.js.map +1 -0
- package/dist/activeRuntime-FcsSJvUg.d.ts +84 -0
- package/dist/bootstrap/index.d.ts +242 -0
- package/dist/bootstrap/index.js +22 -0
- package/dist/bootstrap/index.js.map +1 -0
- package/dist/chunk-73IVH3C6.js +52 -0
- package/dist/chunk-73IVH3C6.js.map +1 -0
- package/dist/chunk-BGVDPDX4.js +29 -0
- package/dist/chunk-BGVDPDX4.js.map +1 -0
- package/dist/chunk-C2HP7CRD.js +224 -0
- package/dist/chunk-C2HP7CRD.js.map +1 -0
- package/dist/chunk-D27MREPB.js +13 -0
- package/dist/chunk-D27MREPB.js.map +1 -0
- package/dist/chunk-DTO27QFR.js +59 -0
- package/dist/chunk-DTO27QFR.js.map +1 -0
- package/dist/chunk-EWVDJYCC.js +296 -0
- package/dist/chunk-EWVDJYCC.js.map +1 -0
- package/dist/chunk-FIR4XTBL.js +136 -0
- package/dist/chunk-FIR4XTBL.js.map +1 -0
- package/dist/chunk-ILQNYQY5.js +200 -0
- package/dist/chunk-ILQNYQY5.js.map +1 -0
- package/dist/chunk-J5LA3Q6D.js +14 -0
- package/dist/chunk-J5LA3Q6D.js.map +1 -0
- package/dist/chunk-MDZ42XRH.js +10 -0
- package/dist/chunk-MDZ42XRH.js.map +1 -0
- package/dist/chunk-Q3SOVBIJ.js +176 -0
- package/dist/chunk-Q3SOVBIJ.js.map +1 -0
- package/dist/chunk-Q7QESSYF.js +49 -0
- package/dist/chunk-Q7QESSYF.js.map +1 -0
- package/dist/chunk-RKYKK6MN.js +19 -0
- package/dist/chunk-RKYKK6MN.js.map +1 -0
- package/dist/chunk-RYFZCAQ3.js +637 -0
- package/dist/chunk-RYFZCAQ3.js.map +1 -0
- package/dist/chunk-UJEC7H6K.js +39 -0
- package/dist/chunk-UJEC7H6K.js.map +1 -0
- package/dist/chunk-XESL274R.js +23 -0
- package/dist/chunk-XESL274R.js.map +1 -0
- package/dist/chunk-YR4MZXMU.js +13 -0
- package/dist/chunk-YR4MZXMU.js.map +1 -0
- package/dist/config/index.d.ts +63 -0
- package/dist/config/index.js +16 -0
- package/dist/config/index.js.map +1 -0
- package/dist/createLankaScope-Bc_vChRs.d.ts +30 -0
- package/dist/errors/index.d.ts +37 -0
- package/dist/errors/index.js +16 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/gateway/index.d.ts +258 -0
- package/dist/gateway/index.js +438 -0
- package/dist/gateway/index.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/lankaHttpInFlight-Bk1eIuSx.d.ts +43 -0
- package/dist/lankaRequestMiddleware-DAC5kCb7.d.ts +62 -0
- package/dist/lankaStandardValidator-CL-r-zEV.d.ts +34 -0
- package/dist/locator/index.d.ts +282 -0
- package/dist/locator/index.js +67 -0
- package/dist/locator/index.js.map +1 -0
- package/dist/logger/index.d.ts +146 -0
- package/dist/logger/index.js +11 -0
- package/dist/logger/index.js.map +1 -0
- package/dist/mock/index.d.ts +16 -0
- package/dist/mock/index.js +58 -0
- package/dist/mock/index.js.map +1 -0
- package/dist/role/index.d.ts +48 -0
- package/dist/role/index.js +7 -0
- package/dist/role/index.js.map +1 -0
- package/dist/scenario/index.d.ts +204 -0
- package/dist/scenario/index.js +28 -0
- package/dist/scenario/index.js.map +1 -0
- package/dist/validation/index.d.ts +18 -0
- package/dist/validation/index.js +53 -0
- package/dist/validation/index.js.map +1 -0
- package/dist/viewmodel/index.d.ts +478 -0
- package/dist/viewmodel/index.js +701 -0
- package/dist/viewmodel/index.js.map +1 -0
- package/package.json +99 -0
- package/skills/lanka-core/SKILL.md +241 -0
- package/skills/lanka-core/reference.md +1090 -0
- package/skills/lanka-packages/SKILL.md +70 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { T as TLankaExecuteOptions, I as IALankaGatewayConfig, a as ILankaRequest, b as TLankaQueryBuilder } from '../ALankaGateway-ExlRGT3D.js';
|
|
2
|
+
export { A as ALankaGateway, c as TLankaQueryParams } from '../ALankaGateway-ExlRGT3D.js';
|
|
3
|
+
import { T as TLankaErrorHandler } from '../TLankaErrorHandler-Yfqtdh1M.js';
|
|
4
|
+
export { I as ILankaInFlightCounter, l as lankaHttpInFlight } from '../lankaHttpInFlight-Bk1eIuSx.js';
|
|
5
|
+
export { I as ILankaRequestContext, T as TLankaRequestMiddleware } from '../lankaRequestMiddleware-DAC5kCb7.js';
|
|
6
|
+
import '../lankaStandardValidator-CL-r-zEV.js';
|
|
7
|
+
import '@standard-schema/spec';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A gateway's protected surface, handed to whoever builds one by calling.
|
|
11
|
+
*
|
|
12
|
+
* The names match `ALankaGateway`'s protected members exactly, and that is the
|
|
13
|
+
* parity contract: a consumer who switches styles moves the same call from
|
|
14
|
+
* `this.endpoint(...)` to `endpoint(...)` and changes nothing else.
|
|
15
|
+
*/
|
|
16
|
+
interface ILankaGatewayContext<TOptions> {
|
|
17
|
+
/** The full URL for a path: base API, then this gateway's own base path. */
|
|
18
|
+
endpoint: (path?: string) => string;
|
|
19
|
+
/** Sends through this gateway's request, mock handler included. */
|
|
20
|
+
request: <TReturn = unknown>(path: string, options?: TLankaExecuteOptions<TOptions>, mockHandler?: () => Promise<TReturn>) => Promise<TReturn>;
|
|
21
|
+
/** Serialises query parameters the way this gateway was configured to. */
|
|
22
|
+
buildQueryParams: <T extends object>(params: T) => URLSearchParams;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** What a gateway is built from, whichever style builds it. */
|
|
26
|
+
interface ILankaGatewayConfig<TOptions, TMethods extends object> extends IALankaGatewayConfig<TOptions> {
|
|
27
|
+
/** The endpoints this gateway offers, written over its own surface. */
|
|
28
|
+
methods: (context: ILankaGatewayContext<TOptions>) => TMethods;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* A gateway, without writing a class.
|
|
32
|
+
*
|
|
33
|
+
* The bridge below is the whole mechanism, and it lives here rather than on the
|
|
34
|
+
* base for two reasons. The language reads `protected` from inside a deriving
|
|
35
|
+
* class body and nowhere else, so a factory outside the hierarchy could only
|
|
36
|
+
* reach the public half — the wrong one. And a `toStyleContext` ON the base
|
|
37
|
+
* would put `TOptions` in a method's parameter position, making the class
|
|
38
|
+
* invariant in it: every `ALankaGateway<unknown>` the locator holds would stop
|
|
39
|
+
* accepting a gateway typed for `RequestInit`.
|
|
40
|
+
*
|
|
41
|
+
* One implementation: what comes back is an instance of `ALankaGateway`, so a
|
|
42
|
+
* behaviour fix reaches both styles at once.
|
|
43
|
+
*/
|
|
44
|
+
declare const createLankaGateway: <TOptions, TMethods extends object>(config: ILankaGatewayConfig<TOptions, TMethods>) => TMethods;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* How a gateway request goes on the wire.
|
|
48
|
+
*
|
|
49
|
+
* A subclass declares one method, `request()`, and does only its own work there:
|
|
50
|
+
* `LankaFetchJsonRequest` returns parsed JSON, `LankaFetchFormDataRequest`
|
|
51
|
+
* returns the whole response, a custom transport returns whatever it likes.
|
|
52
|
+
*
|
|
53
|
+
* Overriding `request()` customises the request flow, failure handling, mock
|
|
54
|
+
* substitution, response transformation and log interception.
|
|
55
|
+
*/
|
|
56
|
+
declare abstract class ALankaRequest<TOptions = RequestInit> implements ILankaRequest<TOptions> {
|
|
57
|
+
protected readonly errorHandler?: TLankaErrorHandler;
|
|
58
|
+
protected readonly useMock: boolean;
|
|
59
|
+
protected constructor(config: {
|
|
60
|
+
errorHandler?: TLankaErrorHandler;
|
|
61
|
+
useMock?: boolean;
|
|
62
|
+
});
|
|
63
|
+
/**
|
|
64
|
+
* Performs the request and returns its result — a response, JSON or a custom
|
|
65
|
+
* type.
|
|
66
|
+
*
|
|
67
|
+
* @param endpoint Full URL
|
|
68
|
+
* @param options Transport-specific options
|
|
69
|
+
* @param mockHandler Mock, when there is one
|
|
70
|
+
*/
|
|
71
|
+
protected abstract request<TReturn = Response>(endpoint: string, options?: TOptions, mockHandler?: () => Promise<TReturn>): Promise<TReturn>;
|
|
72
|
+
/**
|
|
73
|
+
* The single point EVERY gateway request passes through.
|
|
74
|
+
*
|
|
75
|
+
* Hence the in-flight accounting here: intent prefetch stands down while
|
|
76
|
+
* anything else is on the wire. The `finally` matters more than the increment —
|
|
77
|
+
* a rejected request that never decremented would disable prefetching for the
|
|
78
|
+
* rest of the session.
|
|
79
|
+
*/
|
|
80
|
+
execute<TReturn = Response>(endpoint: string, options?: TLankaExecuteOptions<TOptions>, mockHandler?: () => Promise<TReturn>): Promise<TReturn>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Transport layer interface for Gateway.
|
|
85
|
+
* Allows to swap between different communication protocols (HTTP Fetch, gRPC, WebSocket, etc.)
|
|
86
|
+
*/
|
|
87
|
+
interface ILankaTransport<TOptions = RequestInit> {
|
|
88
|
+
/**
|
|
89
|
+
* Executes a request and returns a Response.
|
|
90
|
+
* @param resource - Request resource (URL or Request object)
|
|
91
|
+
* @param options - Request options specific to transport implementation
|
|
92
|
+
* @returns Promise resolving to Response
|
|
93
|
+
*/
|
|
94
|
+
request(resource: RequestInfo, options?: TOptions): Promise<Response>;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
interface ILankaTransportRequestConfig<TOptions> {
|
|
98
|
+
transport?: ILankaTransport<TOptions>;
|
|
99
|
+
errorHandler?: TLankaErrorHandler;
|
|
100
|
+
useMock?: boolean;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The shape every fetch-backed request has: mock, send, check, parse.
|
|
104
|
+
*
|
|
105
|
+
* The three concrete requests differ in exactly two places — which transport
|
|
106
|
+
* they default to, and how they turn a successful `Response` into a value. Both
|
|
107
|
+
* are parameters of this template, so a fourth kind is a subclass with one
|
|
108
|
+
* method rather than a fourth copy of the sequence.
|
|
109
|
+
*/
|
|
110
|
+
declare abstract class ALankaTransportRequest<TOptions = RequestInit> extends ALankaRequest<TOptions> {
|
|
111
|
+
protected readonly transport: ILankaTransport<TOptions>;
|
|
112
|
+
protected constructor(config: ILankaTransportRequestConfig<TOptions>, createDefaultTransport: () => ILankaTransport<TOptions>);
|
|
113
|
+
protected request<TReturn>(endpoint: string, options?: TOptions, mockHandler?: () => Promise<TReturn>): Promise<TReturn>;
|
|
114
|
+
/**
|
|
115
|
+
* Turns a successful response into the value the caller asked for.
|
|
116
|
+
*
|
|
117
|
+
* The one step that genuinely differs between request kinds.
|
|
118
|
+
*/
|
|
119
|
+
protected abstract parse<TReturn>(response: Response): Promise<TReturn>;
|
|
120
|
+
/**
|
|
121
|
+
* Refuses an unsuccessful response, and never returns.
|
|
122
|
+
*
|
|
123
|
+
* `TLankaErrorHandler` is typed `Promise<never>` — a handler must throw. It is
|
|
124
|
+
* still CALLED and then followed by a throw, because a handler that breaks its
|
|
125
|
+
* contract and returns would otherwise hand `undefined` back as if the request
|
|
126
|
+
* had succeeded, and a non-value must never become a value.
|
|
127
|
+
*/
|
|
128
|
+
protected refuse(response: Response): Promise<never>;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* The raw request: hands the `Response` back untouched.
|
|
133
|
+
*
|
|
134
|
+
* The minimal, extensible case — a caller wanting headers, a stream or a blob
|
|
135
|
+
* reads them off the response itself.
|
|
136
|
+
*/
|
|
137
|
+
declare class LankaFetchRequest<TOptions = RequestInit> extends ALankaTransportRequest<TOptions> {
|
|
138
|
+
constructor(config?: ILankaTransportRequestConfig<TOptions>);
|
|
139
|
+
protected parse<TReturn>(response: Response): Promise<TReturn>;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* The functional style of `LankaFetchRequest`: the raw `Response`, for a download or a stream.
|
|
144
|
+
*
|
|
145
|
+
* One line, and that is the point — the factory IS the class, so a behaviour
|
|
146
|
+
* cannot exist in one style and not the other.
|
|
147
|
+
*/
|
|
148
|
+
declare const createLankaFetchRequest: <TOptions = RequestInit>(config?: ILankaTransportRequestConfig<TOptions>) => LankaFetchRequest<TOptions>;
|
|
149
|
+
|
|
150
|
+
/** The JSON request: parses the body, and refuses a body that is not JSON. */
|
|
151
|
+
/**
|
|
152
|
+
* A request kind that answers a parsed JSON body — what most endpoints return.
|
|
153
|
+
*
|
|
154
|
+
* A gateway holds one, and is handed it rather than constructing it, which is
|
|
155
|
+
* what lets a test give the same gateway a transport that never leaves the
|
|
156
|
+
* process. `createLankaFetchJsonRequest()` builds the same class.
|
|
157
|
+
*/
|
|
158
|
+
declare class LankaFetchJsonRequest<TOptions = RequestInit> extends ALankaTransportRequest<TOptions> {
|
|
159
|
+
constructor(config?: ILankaTransportRequestConfig<TOptions>);
|
|
160
|
+
/**
|
|
161
|
+
* Parses the body, or names the failure.
|
|
162
|
+
*
|
|
163
|
+
* The content type is read for the ERROR MESSAGE only. Using it to choose
|
|
164
|
+
* between two parse paths, where the second answers `{}` for any non-empty
|
|
165
|
+
* body it cannot parse, fails silently: the caller's schema is the first thing
|
|
166
|
+
* to notice, and the caller's SCREEN is where it shows up.
|
|
167
|
+
*
|
|
168
|
+
* A measured case: a dev server whose `/api` fell through to the SPA fallback
|
|
169
|
+
* answered `200 text/html` with `index.html`. Turned into `{}`, the schema
|
|
170
|
+
* refused it and a person read a validator's issue list on the sign-in card. A
|
|
171
|
+
* body a JSON transport cannot parse is never a value — it is a misrouted
|
|
172
|
+
* request, and saying so names the actual failure.
|
|
173
|
+
*/
|
|
174
|
+
protected parse<TReturn>(response: Response): Promise<TReturn>;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* The functional style of `LankaFetchJsonRequest`: a JSON body, which is what most endpoints answer.
|
|
179
|
+
*
|
|
180
|
+
* One line, and that is the point — the factory IS the class, so a behaviour
|
|
181
|
+
* cannot exist in one style and not the other.
|
|
182
|
+
*/
|
|
183
|
+
declare const createLankaFetchJsonRequest: <TOptions = RequestInit>(config?: ILankaTransportRequestConfig<TOptions>) => LankaFetchJsonRequest<TOptions>;
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* The multipart request: hands the `Response` back untouched.
|
|
187
|
+
*
|
|
188
|
+
* Differs from `LankaFetchRequest` only in its transport — the one that must NOT
|
|
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.
|
|
199
|
+
*
|
|
200
|
+
* One line, and that is the point — the factory IS the class, so a behaviour
|
|
201
|
+
* cannot exist in one style and not the other.
|
|
202
|
+
*/
|
|
203
|
+
declare const createLankaFetchFormDataRequest: <TOptions = RequestInit>(config?: ILankaTransportRequestConfig<TOptions>) => LankaFetchFormDataRequest<TOptions>;
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* HTTP Fetch transport implementation.
|
|
207
|
+
* Uses native fetch API without any project-specific decorators.
|
|
208
|
+
* For project-specific logic (auth, error handling, etc.), use `request` parameter
|
|
209
|
+
* in Gateway config or create a custom transport.
|
|
210
|
+
*/
|
|
211
|
+
declare class LankaFetchTransport implements ILankaTransport<RequestInit> {
|
|
212
|
+
request(resource: RequestInfo, options?: RequestInit): Promise<Response>;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* HTTP Fetch JSON transport implementation.
|
|
217
|
+
* Uses native fetch API with JSON-specific headers.
|
|
218
|
+
* Automatically sets Content-Type to application/json for requests with body.
|
|
219
|
+
* For project-specific logic (auth, error handling, etc.), use `request` parameter
|
|
220
|
+
* in Gateway config or create a custom transport.
|
|
221
|
+
*/
|
|
222
|
+
declare class LankaFetchJsonTransport implements ILankaTransport<RequestInit> {
|
|
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.
|
|
232
|
+
*/
|
|
233
|
+
declare class LankaFetchFormDataTransport implements ILankaTransport<RequestInit> {
|
|
234
|
+
request(resource: RequestInfo, options?: RequestInit): Promise<Response>;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* The query string a gateway sends, from the object a method was called with.
|
|
239
|
+
*
|
|
240
|
+
* Arrays become `key[]` repeated, nested objects become `key[inner]`: the shape
|
|
241
|
+
* most JSON APIs read back without being told about it.
|
|
242
|
+
*
|
|
243
|
+
* Plain loops over `Object.keys` rather than `Object.entries` and `forEach`: this
|
|
244
|
+
* runs on every request that carries a filter, and the pair array `entries`
|
|
245
|
+
* builds — two allocations per key, thrown away immediately — is a cost with
|
|
246
|
+
* nothing to show for it.
|
|
247
|
+
*/
|
|
248
|
+
declare const buildLankaQueryParams: TLankaQueryBuilder;
|
|
249
|
+
|
|
250
|
+
interface ILankaListQueryParams {
|
|
251
|
+
page: number;
|
|
252
|
+
limit: number;
|
|
253
|
+
sortField?: string | null;
|
|
254
|
+
sortOrder?: string | null;
|
|
255
|
+
filters?: Record<string, unknown>;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export { ALankaRequest, IALankaGatewayConfig, type ILankaGatewayConfig, type ILankaGatewayContext, type ILankaListQueryParams, ILankaRequest, type ILankaTransport, LankaFetchFormDataRequest, LankaFetchFormDataTransport, LankaFetchJsonRequest, LankaFetchJsonTransport, LankaFetchRequest, LankaFetchTransport, TLankaExecuteOptions, TLankaQueryBuilder, buildLankaQueryParams, createLankaFetchFormDataRequest, createLankaFetchJsonRequest, createLankaFetchRequest, createLankaGateway };
|
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
import {
|
|
2
|
+
composeLankaRequestMiddleware
|
|
3
|
+
} from "../chunk-YR4MZXMU.js";
|
|
4
|
+
import {
|
|
5
|
+
lankaHttpInFlight
|
|
6
|
+
} from "../chunk-UJEC7H6K.js";
|
|
7
|
+
import {
|
|
8
|
+
handleLankaApiError
|
|
9
|
+
} from "../chunk-73IVH3C6.js";
|
|
10
|
+
import "../chunk-J5LA3Q6D.js";
|
|
11
|
+
import {
|
|
12
|
+
LankaError
|
|
13
|
+
} from "../chunk-Q7QESSYF.js";
|
|
14
|
+
import {
|
|
15
|
+
getLankaHost
|
|
16
|
+
} from "../chunk-RKYKK6MN.js";
|
|
17
|
+
import {
|
|
18
|
+
lankaLogger
|
|
19
|
+
} from "../chunk-C2HP7CRD.js";
|
|
20
|
+
import {
|
|
21
|
+
getLankaFlags
|
|
22
|
+
} from "../chunk-D27MREPB.js";
|
|
23
|
+
import {
|
|
24
|
+
getActiveRuntime
|
|
25
|
+
} from "../chunk-BGVDPDX4.js";
|
|
26
|
+
|
|
27
|
+
// src/gateway/request/_abstractions/lanka-request/ALankaRequest.ts
|
|
28
|
+
function classifyTransportError(error, timedOut) {
|
|
29
|
+
if (LankaError.is(error)) return error;
|
|
30
|
+
const name = readErrorName(error);
|
|
31
|
+
const isAbort = name === "AbortError" || name === "TimeoutError";
|
|
32
|
+
if (isAbort) {
|
|
33
|
+
const timedOutHere = timedOut || name === "TimeoutError";
|
|
34
|
+
return new LankaError({
|
|
35
|
+
kind: timedOutHere ? "timeout" : "aborted",
|
|
36
|
+
message: timedOutHere ? getLankaHost().timeoutErrorMessage() : readErrorMessage(error),
|
|
37
|
+
cause: error
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
if (error instanceof TypeError) {
|
|
41
|
+
return new LankaError({
|
|
42
|
+
kind: "network",
|
|
43
|
+
message: getLankaHost().networkErrorMessage(),
|
|
44
|
+
issues: [error.message],
|
|
45
|
+
cause: error
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return error;
|
|
49
|
+
}
|
|
50
|
+
function readErrorName(error) {
|
|
51
|
+
if (typeof error !== "object" || error === null) return void 0;
|
|
52
|
+
const name = error.name;
|
|
53
|
+
return typeof name === "string" ? name : void 0;
|
|
54
|
+
}
|
|
55
|
+
function readErrorMessage(error) {
|
|
56
|
+
if (typeof error === "string") return error;
|
|
57
|
+
if (typeof error !== "object" || error === null) return "";
|
|
58
|
+
const message = error.message;
|
|
59
|
+
return typeof message === "string" ? message : "";
|
|
60
|
+
}
|
|
61
|
+
var ALankaRequest = class {
|
|
62
|
+
errorHandler;
|
|
63
|
+
useMock;
|
|
64
|
+
constructor(config) {
|
|
65
|
+
const flags = getLankaFlags();
|
|
66
|
+
this.errorHandler = config.errorHandler ?? handleLankaApiError;
|
|
67
|
+
this.useMock = config.useMock ?? flags.isMockMode ?? false;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The single point EVERY gateway request passes through.
|
|
71
|
+
*
|
|
72
|
+
* Hence the in-flight accounting here: intent prefetch stands down while
|
|
73
|
+
* anything else is on the wire. The `finally` matters more than the increment —
|
|
74
|
+
* a rejected request that never decremented would disable prefetching for the
|
|
75
|
+
* rest of the session.
|
|
76
|
+
*/
|
|
77
|
+
async execute(endpoint, options, mockHandler) {
|
|
78
|
+
const runtime = getActiveRuntime();
|
|
79
|
+
const { signal, timeoutMs, ...rest } = options ?? {};
|
|
80
|
+
const fallbackDeadline = runtime?.requestTimeoutMs;
|
|
81
|
+
const passedOptions = options === void 0 ? void 0 : rest;
|
|
82
|
+
let lastTimedOut = false;
|
|
83
|
+
const perform = async (ctx) => {
|
|
84
|
+
const lifetime = createRequestLifetime(signal, ctx.timeoutMs ?? fallbackDeadline);
|
|
85
|
+
try {
|
|
86
|
+
return await this.request(
|
|
87
|
+
ctx.endpoint,
|
|
88
|
+
withSignal(ctx.options, lifetime.signal),
|
|
89
|
+
mockHandler
|
|
90
|
+
);
|
|
91
|
+
} catch (error) {
|
|
92
|
+
lastTimedOut = lifetime.timedOut();
|
|
93
|
+
throw classifyTransportError(error, lastTimedOut);
|
|
94
|
+
} finally {
|
|
95
|
+
lifetime.dispose();
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
const run = composeLankaRequestMiddleware(runtime?.requestMiddleware ?? [], perform);
|
|
99
|
+
lankaHttpInFlight.begin();
|
|
100
|
+
try {
|
|
101
|
+
return await run({
|
|
102
|
+
endpoint,
|
|
103
|
+
options: passedOptions,
|
|
104
|
+
attempt: 1,
|
|
105
|
+
timeoutMs
|
|
106
|
+
});
|
|
107
|
+
} catch (error) {
|
|
108
|
+
throw classifyTransportError(error, lastTimedOut);
|
|
109
|
+
} finally {
|
|
110
|
+
lankaHttpInFlight.end();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
function createRequestLifetime(external, timeoutMs) {
|
|
115
|
+
if (!external && !timeoutMs) {
|
|
116
|
+
return { signal: void 0, timedOut: () => false, dispose: () => void 0 };
|
|
117
|
+
}
|
|
118
|
+
const controller = new AbortController();
|
|
119
|
+
let expired = false;
|
|
120
|
+
const timer = timeoutMs === void 0 ? void 0 : setTimeout(() => {
|
|
121
|
+
expired = true;
|
|
122
|
+
controller.abort(new DOMException("Request timed out", "TimeoutError"));
|
|
123
|
+
}, timeoutMs);
|
|
124
|
+
const onExternalAbort = () => {
|
|
125
|
+
controller.abort(external?.reason);
|
|
126
|
+
};
|
|
127
|
+
if (external) {
|
|
128
|
+
if (external.aborted) onExternalAbort();
|
|
129
|
+
else external.addEventListener("abort", onExternalAbort, { once: true });
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
signal: controller.signal,
|
|
133
|
+
timedOut: () => expired,
|
|
134
|
+
dispose: () => {
|
|
135
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
136
|
+
external?.removeEventListener("abort", onExternalAbort);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function withSignal(options, signal) {
|
|
141
|
+
if (signal === void 0) return options;
|
|
142
|
+
return { ...options ?? {}, signal };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// src/gateway/request/_abstractions/lanka-transport-request/ALankaTransportRequest.ts
|
|
146
|
+
var ALankaTransportRequest = class extends ALankaRequest {
|
|
147
|
+
transport;
|
|
148
|
+
constructor(config, createDefaultTransport) {
|
|
149
|
+
const flags = getLankaFlags();
|
|
150
|
+
super({
|
|
151
|
+
errorHandler: config.errorHandler,
|
|
152
|
+
useMock: config.useMock ?? flags.isMockMode ?? false
|
|
153
|
+
});
|
|
154
|
+
this.transport = config.transport ?? createDefaultTransport();
|
|
155
|
+
}
|
|
156
|
+
async request(endpoint, options, mockHandler) {
|
|
157
|
+
if (this.useMock && mockHandler) {
|
|
158
|
+
return await mockHandler();
|
|
159
|
+
}
|
|
160
|
+
const response = await this.transport.request(endpoint, options);
|
|
161
|
+
if (!response.ok) return await this.refuse(response);
|
|
162
|
+
return await this.parse(response);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Refuses an unsuccessful response, and never returns.
|
|
166
|
+
*
|
|
167
|
+
* `TLankaErrorHandler` is typed `Promise<never>` — a handler must throw. It is
|
|
168
|
+
* still CALLED and then followed by a throw, because a handler that breaks its
|
|
169
|
+
* contract and returns would otherwise hand `undefined` back as if the request
|
|
170
|
+
* had succeeded, and a non-value must never become a value.
|
|
171
|
+
*/
|
|
172
|
+
async refuse(response) {
|
|
173
|
+
if (this.errorHandler) {
|
|
174
|
+
await this.errorHandler(response);
|
|
175
|
+
}
|
|
176
|
+
throw new LankaError({
|
|
177
|
+
kind: "http",
|
|
178
|
+
message: getLankaHost().httpErrorMessage(response.status),
|
|
179
|
+
status: response.status
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// src/gateway/transport/lanka-fetch-json-transport/LankaFetchJsonTransport.ts
|
|
185
|
+
var LankaFetchJsonTransport = class {
|
|
186
|
+
async request(resource, options) {
|
|
187
|
+
if (options?.body && !(options.body instanceof FormData)) {
|
|
188
|
+
const headers = new Headers(options.headers);
|
|
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
|
+
});
|
|
199
|
+
}
|
|
200
|
+
return await fetch(resource, options);
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
// src/gateway/request/lanka-fetch-json-request/LankaFetchJsonRequest.ts
|
|
205
|
+
var LankaFetchJsonRequest = class extends ALankaTransportRequest {
|
|
206
|
+
constructor(config = {}) {
|
|
207
|
+
super(config, () => new LankaFetchJsonTransport());
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Parses the body, or names the failure.
|
|
211
|
+
*
|
|
212
|
+
* The content type is read for the ERROR MESSAGE only. Using it to choose
|
|
213
|
+
* between two parse paths, where the second answers `{}` for any non-empty
|
|
214
|
+
* body it cannot parse, fails silently: the caller's schema is the first thing
|
|
215
|
+
* to notice, and the caller's SCREEN is where it shows up.
|
|
216
|
+
*
|
|
217
|
+
* A measured case: a dev server whose `/api` fell through to the SPA fallback
|
|
218
|
+
* answered `200 text/html` with `index.html`. Turned into `{}`, the schema
|
|
219
|
+
* refused it and a person read a validator's issue list on the sign-in card. A
|
|
220
|
+
* body a JSON transport cannot parse is never a value — it is a misrouted
|
|
221
|
+
* request, and saying so names the actual failure.
|
|
222
|
+
*/
|
|
223
|
+
async parse(response) {
|
|
224
|
+
const contentType = response.headers.get("content-type");
|
|
225
|
+
const text = await response.text();
|
|
226
|
+
if (!text) {
|
|
227
|
+
return void 0;
|
|
228
|
+
}
|
|
229
|
+
try {
|
|
230
|
+
return JSON.parse(text);
|
|
231
|
+
} catch (error) {
|
|
232
|
+
throw new LankaError({
|
|
233
|
+
kind: "schema",
|
|
234
|
+
message: `Failed to parse JSON response (content-type: ${contentType ?? "none"}): ${error instanceof Error ? error.message : String(error)}`,
|
|
235
|
+
cause: error
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
// src/gateway/_utils/build-lanka-query-params/buildLankaQueryParams.ts
|
|
242
|
+
var buildLankaQueryParams = (input) => {
|
|
243
|
+
const params = new URLSearchParams();
|
|
244
|
+
const append = (key, value) => {
|
|
245
|
+
if (value == null) return;
|
|
246
|
+
if (Array.isArray(value)) {
|
|
247
|
+
const itemKey = `${key}[]`;
|
|
248
|
+
for (let index = 0; index < value.length; index += 1) append(itemKey, value[index]);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
if (typeof value === "object") {
|
|
252
|
+
const inner = value;
|
|
253
|
+
const innerKeys = Object.keys(inner);
|
|
254
|
+
for (let index = 0; index < innerKeys.length; index += 1) {
|
|
255
|
+
const innerKey = innerKeys[index];
|
|
256
|
+
append(`${key}[${innerKey}]`, inner[innerKey]);
|
|
257
|
+
}
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
params.append(key, String(value));
|
|
261
|
+
};
|
|
262
|
+
const source = input;
|
|
263
|
+
const keys = Object.keys(source);
|
|
264
|
+
for (let index = 0; index < keys.length; index += 1) append(keys[index], source[keys[index]]);
|
|
265
|
+
return params;
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
// src/gateway/_abstractions/lanka-gateway/ALankaGateway.ts
|
|
269
|
+
var ALankaGateway = class {
|
|
270
|
+
requestExecutor;
|
|
271
|
+
queryParamsHandler;
|
|
272
|
+
useMock;
|
|
273
|
+
basePath;
|
|
274
|
+
constructor(config) {
|
|
275
|
+
lankaLogger.printGatewayLog("Create gateway", this);
|
|
276
|
+
const flags = getLankaFlags();
|
|
277
|
+
this.useMock = config.useMock ?? flags.isMockMode ?? false;
|
|
278
|
+
this.requestExecutor = config.request ?? new LankaFetchJsonRequest();
|
|
279
|
+
this.basePath = config.basePath ?? "";
|
|
280
|
+
this.queryParamsHandler = config.queryParamsHandler ?? buildLankaQueryParams;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Resolves endpoint for request.
|
|
284
|
+
* - Absolute paths (starting with "/") are returned as-is
|
|
285
|
+
* - Relative paths are joined with basePath
|
|
286
|
+
* - Query-only strings like "?a=1" are attached to basePath
|
|
287
|
+
*/
|
|
288
|
+
endpoint(path = "") {
|
|
289
|
+
if (isAbsoluteUrl(path)) return path;
|
|
290
|
+
return this.withApiBase(this.resolvePath(path));
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Joins `basePath` and the method path.
|
|
294
|
+
*/
|
|
295
|
+
resolvePath(path) {
|
|
296
|
+
if (!path) return this.basePath;
|
|
297
|
+
if (path.startsWith("/")) return path;
|
|
298
|
+
if (path.startsWith("?")) return `${this.basePath}${path}`;
|
|
299
|
+
const left = this.basePath.endsWith("/") ? this.basePath.slice(0, -1) : this.basePath;
|
|
300
|
+
const right = path.startsWith("/") ? path.slice(1) : path;
|
|
301
|
+
return `${left}/${right}`;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Prefixes the API base URL from the host contract.
|
|
305
|
+
*
|
|
306
|
+
* Here rather than in every consumer: otherwise each consumer knows the URL
|
|
307
|
+
* and the framework does not, and a realtime plugin would have to know a
|
|
308
|
+
* specific application's build.
|
|
309
|
+
*
|
|
310
|
+
* Declaring the field and not using it would be worse than not declaring it: a
|
|
311
|
+
* declaration nothing is built from is a second truth, free to diverge from
|
|
312
|
+
* the first.
|
|
313
|
+
*
|
|
314
|
+
* An absolute URL never reaches here — `endpoint()` filters it out before the
|
|
315
|
+
* join.
|
|
316
|
+
*/
|
|
317
|
+
withApiBase(path) {
|
|
318
|
+
const base = withoutTrailingSlashes(getLankaHost().apiBaseUrl);
|
|
319
|
+
if (!base) return path;
|
|
320
|
+
if (!path) return base;
|
|
321
|
+
return path.startsWith("/") ? `${base}${path}` : `${base}/${path}`;
|
|
322
|
+
}
|
|
323
|
+
buildQueryParams(params) {
|
|
324
|
+
return this.queryParamsHandler(params);
|
|
325
|
+
}
|
|
326
|
+
async request(path, options, mockHandler) {
|
|
327
|
+
return this.requestExecutor.execute(this.endpoint(path), options, mockHandler);
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Allows to replace request implementation at runtime (e.g. feature flags / tests).
|
|
331
|
+
* If you prefer static customization - override `request()` in a subclass.
|
|
332
|
+
*/
|
|
333
|
+
setRequest(request) {
|
|
334
|
+
this.requestExecutor = request;
|
|
335
|
+
}
|
|
336
|
+
setQueryParamsHandler(handler) {
|
|
337
|
+
this.queryParamsHandler = handler;
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
function isAbsoluteUrl(path) {
|
|
341
|
+
if (!path.includes("://")) return false;
|
|
342
|
+
return /^[a-z][a-z\d+\-.]*:\/\//i.test(path);
|
|
343
|
+
}
|
|
344
|
+
var lastRawBase = null;
|
|
345
|
+
var lastTrimmedBase = "";
|
|
346
|
+
function withoutTrailingSlashes(base) {
|
|
347
|
+
if (base !== lastRawBase) {
|
|
348
|
+
lastRawBase = base;
|
|
349
|
+
lastTrimmedBase = base.replace(/\/+$/, "");
|
|
350
|
+
}
|
|
351
|
+
return lastTrimmedBase;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// src/gateway/_factories/create-lanka-gateway/createLankaGateway.ts
|
|
355
|
+
var createLankaGateway = (config) => {
|
|
356
|
+
class FunctionalGateway extends ALankaGateway {
|
|
357
|
+
// The base keeps a protected constructor — it is abstract, and a consumer
|
|
358
|
+
// reaching for `new ALankaGateway()` would get an object with no endpoints.
|
|
359
|
+
// A subclass may widen it, and this one is the subclass.
|
|
360
|
+
constructor(gatewayConfig) {
|
|
361
|
+
super(gatewayConfig);
|
|
362
|
+
}
|
|
363
|
+
build() {
|
|
364
|
+
return config.methods({
|
|
365
|
+
endpoint: (path) => this.endpoint(path),
|
|
366
|
+
request: (path, options, mockHandler) => this.request(path, options, mockHandler),
|
|
367
|
+
buildQueryParams: (params) => this.buildQueryParams(params)
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return new FunctionalGateway(config).build();
|
|
372
|
+
};
|
|
373
|
+
|
|
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
|
+
// src/gateway/request/lanka-fetch-request/LankaFetchRequest.ts
|
|
382
|
+
var LankaFetchRequest = class extends ALankaTransportRequest {
|
|
383
|
+
constructor(config = {}) {
|
|
384
|
+
super(config, () => new LankaFetchTransport());
|
|
385
|
+
}
|
|
386
|
+
parse(response) {
|
|
387
|
+
return Promise.resolve(response);
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
// src/gateway/request/_factories/create-lanka-fetch-request/createLankaFetchRequest.ts
|
|
392
|
+
var createLankaFetchRequest = (config = {}) => new LankaFetchRequest(config);
|
|
393
|
+
|
|
394
|
+
// src/gateway/request/_factories/create-lanka-fetch-json-request/createLankaFetchJsonRequest.ts
|
|
395
|
+
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
|
+
export {
|
|
423
|
+
ALankaGateway,
|
|
424
|
+
ALankaRequest,
|
|
425
|
+
LankaFetchFormDataRequest,
|
|
426
|
+
LankaFetchFormDataTransport,
|
|
427
|
+
LankaFetchJsonRequest,
|
|
428
|
+
LankaFetchJsonTransport,
|
|
429
|
+
LankaFetchRequest,
|
|
430
|
+
LankaFetchTransport,
|
|
431
|
+
buildLankaQueryParams,
|
|
432
|
+
createLankaFetchFormDataRequest,
|
|
433
|
+
createLankaFetchJsonRequest,
|
|
434
|
+
createLankaFetchRequest,
|
|
435
|
+
createLankaGateway,
|
|
436
|
+
lankaHttpInFlight
|
|
437
|
+
};
|
|
438
|
+
//# sourceMappingURL=index.js.map
|