lanka 1.0.1 → 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-CLkq4MaJ.d.ts → LankaScenarioLocator-D86TIwiu.d.ts} +10 -5
- package/dist/_extend/index.d.ts +4 -4
- package/dist/_extend/index.js +2 -2
- package/dist/_internal/index.d.ts +5 -5
- package/dist/{activeRuntime-DT4gB16d.d.ts → activeRuntime-B336NU5I.d.ts} +2 -2
- package/dist/bootstrap/index.d.ts +13 -144
- package/dist/bootstrap/index.js +4 -4
- package/dist/{chunk-MYZQYOMD.js → chunk-5MAQVBI2.js} +20 -15
- 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-63ST2UKP.js → chunk-LMKLLEHA.js} +103 -17
- package/dist/chunk-LMKLLEHA.js.map +1 -0
- package/dist/{chunk-YXI4OQEV.js → chunk-O5ROO7QF.js} +27 -4
- package/dist/chunk-O5ROO7QF.js.map +1 -0
- package/dist/{chunk-HZAIAGWS.js → chunk-UGXSGQPW.js} +6 -2
- package/dist/chunk-UGXSGQPW.js.map +1 -0
- package/dist/createLanka-DI1CSy2Q.d.ts +139 -0
- package/dist/gateway/index.d.ts +77 -67
- package/dist/gateway/index.js +50 -54
- package/dist/gateway/index.js.map +1 -1
- package/dist/index.d.ts +7 -6
- package/dist/index.js +4 -4
- package/dist/locator/index.d.ts +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 +2 -2
- 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-63ST2UKP.js.map +0 -1
- package/dist/chunk-HZAIAGWS.js.map +0 -1
- package/dist/chunk-MYZQYOMD.js.map +0 -1
- package/dist/chunk-YXI4OQEV.js.map +0 -1
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { I as ILankaScope } from './createLankaScope-BiFxNQgl.js';
|
|
2
|
+
import { I as ILankaRuntime } from './activeRuntime-B336NU5I.js';
|
|
3
|
+
import { a as ILankaHost, I as ILankaFlags } from './ILankaRuntimeConfig-Vl436GWK.js';
|
|
4
|
+
import { T as TLankaRequestMiddleware } from './lankaRequestMiddleware-DAC5kCb7.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A plugin is an extension core knows by SHAPE rather than by name.
|
|
8
|
+
*
|
|
9
|
+
* ## Plugin versus module
|
|
10
|
+
*
|
|
11
|
+
* A module is called by the application (`app → module`) and core does not know
|
|
12
|
+
* it exists. A plugin sits on the path core itself walks (`app → core → plugin`).
|
|
13
|
+
* The test question: does core need a hook for this to work? No — then it is a
|
|
14
|
+
* module, and making it a plugin costs more, because the hook has to be supported
|
|
15
|
+
* forever.
|
|
16
|
+
*
|
|
17
|
+
* ## Why `install` receives the instance
|
|
18
|
+
*
|
|
19
|
+
* So a plugin has no private route to the framework. Everything it can do comes
|
|
20
|
+
* from the instance it was given, which means two instances in one process (a
|
|
21
|
+
* test beside the app) do not share its configuration.
|
|
22
|
+
*/
|
|
23
|
+
interface ILankaPlugin {
|
|
24
|
+
/**
|
|
25
|
+
* The name the plugin is recognised by. Registering the same name twice is
|
|
26
|
+
* rejected: two copies of a retry policy would silently double the request
|
|
27
|
+
* count.
|
|
28
|
+
*/
|
|
29
|
+
readonly name: string;
|
|
30
|
+
/**
|
|
31
|
+
* Installation. The returned function removes everything the plugin installed
|
|
32
|
+
* and is called when the plugin is removed and when the instance is disposed.
|
|
33
|
+
*/
|
|
34
|
+
install: (lanka: ILankaInstance) => (() => void) | void;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* A framework instance: all framework state in one object.
|
|
39
|
+
*
|
|
40
|
+
* What instance-scoped state buys: two apps in one process (micro-frontends,
|
|
41
|
+
* Storybook beside the app) no longer share a bus, locator caches and mock mode;
|
|
42
|
+
* SSR does not reuse state between different users' requests; and test isolation
|
|
43
|
+
* rests on construction rather than on a global `beforeEach` reaching into
|
|
44
|
+
* internal registries.
|
|
45
|
+
*
|
|
46
|
+
* **Ambient facades** — `lankaEventBus.dispatch`, `lankaSingletons.foo`,
|
|
47
|
+
* `getLankaFlags()`, `lankaHttpInFlight` — resolve THE ONE active instance
|
|
48
|
+
* (`internal/activeRuntime.ts`). Isolation belongs to whoever holds an instance;
|
|
49
|
+
* a facade cannot offer it.
|
|
50
|
+
*
|
|
51
|
+
* One thing stays at module level deliberately: `ALankaScenario` collects
|
|
52
|
+
* constructed scenarios into a static pool. That is a REGISTRY OF DEFINITIONS,
|
|
53
|
+
* not runtime state — the classes come from one `@lanka_di/Scenarios` barrel and
|
|
54
|
+
* both instances must see the same list. Splitting it would be divergence.
|
|
55
|
+
*/
|
|
56
|
+
interface ILankaInstance extends ILankaRuntime {
|
|
57
|
+
/**
|
|
58
|
+
* Adds a wrapper around every request. Returns a function that removes it.
|
|
59
|
+
*
|
|
60
|
+
* This is how `@lankajs/plugin-http` installs retry, the idempotency key, the
|
|
61
|
+
* CSRF header and auth refresh.
|
|
62
|
+
*/
|
|
63
|
+
useRequestMiddleware(middleware: TLankaRequestMiddleware): () => void;
|
|
64
|
+
/** Default timeout for every request of this instance. */
|
|
65
|
+
setRequestTimeout(timeoutMs: number | undefined): void;
|
|
66
|
+
/** Resolves a service in the root scope — for the instance's whole lifetime. */
|
|
67
|
+
resolve<TInstance>(propertyName: string): TInstance;
|
|
68
|
+
/**
|
|
69
|
+
* Creates a scope: a lifetime shorter than the application's.
|
|
70
|
+
*
|
|
71
|
+
* An object created in a scope goes away with it.
|
|
72
|
+
*/
|
|
73
|
+
createScope(): ILankaScope;
|
|
74
|
+
/**
|
|
75
|
+
* Registers a plugin. Returns a function that removes it.
|
|
76
|
+
*
|
|
77
|
+
* The fifth and last extension point. An extension point declared before
|
|
78
|
+
* anything plugs into it describes an imagined need while costing real
|
|
79
|
+
* support, so this one arrived with the FIRST plugin.
|
|
80
|
+
*/
|
|
81
|
+
use(plugin: ILankaPlugin): () => void;
|
|
82
|
+
/** Runs services and the scenario layer. Idempotent. */
|
|
83
|
+
bootstrap(config?: ILankaBootstrapConfig): Promise<void>;
|
|
84
|
+
/** Has bootstrap already run? */
|
|
85
|
+
isBootstrapped(): boolean;
|
|
86
|
+
/** Makes this instance the one ambient facades resolve to. */
|
|
87
|
+
activate(): void;
|
|
88
|
+
/**
|
|
89
|
+
* Removes subscriptions, clears registries and, if this instance was active,
|
|
90
|
+
* clears the pointer. Returns the framework to its pre-bootstrap state.
|
|
91
|
+
*/
|
|
92
|
+
dispose(): void;
|
|
93
|
+
}
|
|
94
|
+
interface ILankaInstanceConfig {
|
|
95
|
+
host: ILankaHost;
|
|
96
|
+
flags?: ILankaFlags;
|
|
97
|
+
}
|
|
98
|
+
interface ILankaServiceConfig {
|
|
99
|
+
name?: string;
|
|
100
|
+
init: () => void | Promise<void>;
|
|
101
|
+
sync?: boolean;
|
|
102
|
+
priority?: number;
|
|
103
|
+
/**
|
|
104
|
+
* A failure of this service does not abort bootstrap.
|
|
105
|
+
*
|
|
106
|
+
* Without the flag one failed service takes the WHOLE phase with it: the async
|
|
107
|
+
* phase because of `Promise.all`, the sync phase because later tasks never run.
|
|
108
|
+
* Wrapping the failure in a swallowing `try/catch` is worse still — the app
|
|
109
|
+
* starts with a partially executed plan and does not know it.
|
|
110
|
+
*/
|
|
111
|
+
optional?: boolean;
|
|
112
|
+
/**
|
|
113
|
+
* How long to wait for the service. Overrunning counts as a failure.
|
|
114
|
+
*
|
|
115
|
+
* Without a deadline a service that never settles holds bootstrap forever and
|
|
116
|
+
* the app never paints its first screen. Failing is more honest than waiting:
|
|
117
|
+
* an optional service is skipped, a required one names itself.
|
|
118
|
+
*/
|
|
119
|
+
timeoutMs?: number;
|
|
120
|
+
}
|
|
121
|
+
interface ILankaScenarioBootstrapConfig {
|
|
122
|
+
sync?: boolean;
|
|
123
|
+
priority?: number;
|
|
124
|
+
}
|
|
125
|
+
interface ILankaBootstrapConfig {
|
|
126
|
+
services?: ILankaServiceConfig[];
|
|
127
|
+
scenarios?: ILankaScenarioBootstrapConfig;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Creates an instance and makes it active.
|
|
131
|
+
*
|
|
132
|
+
* Activating on creation is the deliberate default: there is almost always one
|
|
133
|
+
* app and the facades must work immediately. A second instance takes the
|
|
134
|
+
* pointer — the last one created serves the facades. Callers needing another
|
|
135
|
+
* order call `activate()` explicitly.
|
|
136
|
+
*/
|
|
137
|
+
declare function createLanka(config: ILankaInstanceConfig): ILankaInstance;
|
|
138
|
+
|
|
139
|
+
export { type ILankaBootstrapConfig as I, type ILankaInstance as a, type ILankaInstanceConfig as b, type ILankaPlugin as c, type ILankaScenarioBootstrapConfig as d, type ILankaServiceConfig as e, createLanka as f };
|
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;
|
|
@@ -363,20 +395,14 @@ var createLankaGateway = (config) => {
|
|
|
363
395
|
return config.methods({
|
|
364
396
|
endpoint: (path) => this.endpoint(path),
|
|
365
397
|
request: (path, options, mockHandler) => this.request(path, options, mockHandler),
|
|
366
|
-
buildQueryParams: (params) => this.buildQueryParams(params)
|
|
398
|
+
buildQueryParams: (params) => this.buildQueryParams(params),
|
|
399
|
+
validationService: this.validationService
|
|
367
400
|
});
|
|
368
401
|
}
|
|
369
402
|
}
|
|
370
403
|
return new FunctionalGateway(config).build();
|
|
371
404
|
};
|
|
372
405
|
|
|
373
|
-
// src/gateway/transport/lanka-fetch-transport/LankaFetchTransport.ts
|
|
374
|
-
var LankaFetchTransport = class {
|
|
375
|
-
async request(resource, options) {
|
|
376
|
-
return await fetch(resource, options);
|
|
377
|
-
}
|
|
378
|
-
};
|
|
379
|
-
|
|
380
406
|
// src/gateway/request/lanka-fetch-request/LankaFetchRequest.ts
|
|
381
407
|
var LankaFetchRequest = class extends ALankaTransportRequest {
|
|
382
408
|
constructor(config = {}) {
|
|
@@ -392,43 +418,13 @@ var createLankaFetchRequest = (config = {}) => new LankaFetchRequest(config);
|
|
|
392
418
|
|
|
393
419
|
// src/gateway/request/_factories/create-lanka-fetch-json-request/createLankaFetchJsonRequest.ts
|
|
394
420
|
var createLankaFetchJsonRequest = (config = {}) => new LankaFetchJsonRequest(config);
|
|
395
|
-
|
|
396
|
-
// src/gateway/transport/lanka-fetch-form-data-transport/LankaFetchFormDataTransport.ts
|
|
397
|
-
var LankaFetchFormDataTransport = class {
|
|
398
|
-
async request(resource, options) {
|
|
399
|
-
const formDataOptions = { ...options };
|
|
400
|
-
if (formDataOptions.body instanceof FormData && formDataOptions.headers) {
|
|
401
|
-
const headers = new Headers(formDataOptions.headers);
|
|
402
|
-
headers.delete("Content-Type");
|
|
403
|
-
formDataOptions.headers = headers;
|
|
404
|
-
}
|
|
405
|
-
return await fetch(resource, formDataOptions);
|
|
406
|
-
}
|
|
407
|
-
};
|
|
408
|
-
|
|
409
|
-
// src/gateway/request/lanka-fetch-form-data-request/LankaFetchFormDataRequest.ts
|
|
410
|
-
var LankaFetchFormDataRequest = class extends ALankaTransportRequest {
|
|
411
|
-
constructor(config = {}) {
|
|
412
|
-
super(config, () => new LankaFetchFormDataTransport());
|
|
413
|
-
}
|
|
414
|
-
parse(response) {
|
|
415
|
-
return Promise.resolve(response);
|
|
416
|
-
}
|
|
417
|
-
};
|
|
418
|
-
|
|
419
|
-
// src/gateway/request/_factories/create-lanka-fetch-form-data-request/createLankaFetchFormDataRequest.ts
|
|
420
|
-
var createLankaFetchFormDataRequest = (config = {}) => new LankaFetchFormDataRequest(config);
|
|
421
421
|
export {
|
|
422
422
|
ALankaGateway,
|
|
423
423
|
ALankaRequest,
|
|
424
|
-
LankaFetchFormDataRequest,
|
|
425
|
-
LankaFetchFormDataTransport,
|
|
426
424
|
LankaFetchJsonRequest,
|
|
427
|
-
LankaFetchJsonTransport,
|
|
428
425
|
LankaFetchRequest,
|
|
429
426
|
LankaFetchTransport,
|
|
430
427
|
buildLankaQueryParams,
|
|
431
|
-
createLankaFetchFormDataRequest,
|
|
432
428
|
createLankaFetchJsonRequest,
|
|
433
429
|
createLankaFetchRequest,
|
|
434
430
|
createLankaGateway,
|