lambder 11.1.1 → 13.0.1
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/CHANGELOG.md +217 -0
- package/README.md +25 -15
- package/dist/api/LambderApiAnswer.d.ts +7 -0
- package/dist/api/LambderApiDeclarations.d.ts +194 -0
- package/dist/api/LambderApiDeclarations.js +52 -0
- package/dist/api/LambderApiDefinition.d.ts +1 -1
- package/dist/api/LambderApiEnvelope.js +4 -1
- package/dist/api/LambderApiGuards.d.ts +21 -22
- package/dist/api/LambderApiGuards.js +1 -1
- package/dist/api/LambderApiRateLimits.d.ts +19 -19
- package/dist/api/LambderApiRateLimits.js +1 -1
- package/dist/api/LambderApiRefusals.d.ts +22 -0
- package/dist/api/LambderApiRefusals.js +13 -0
- package/dist/api/LambderApiRequest.d.ts +15 -6
- package/dist/api/LambderApiRequest.js +8 -9
- package/dist/build/generateApiFiles.d.ts +60 -0
- package/dist/build/generateApiFiles.js +85 -0
- package/dist/build/generatedTables.d.ts +2 -1
- package/dist/build/generatedTables.js +1 -1
- package/dist/build.d.ts +4 -1
- package/dist/build.js +3 -1
- package/dist/client/LambderCaller.d.ts +36 -23
- package/dist/client/LambderCaller.js +88 -67
- package/dist/client/lambderFetchTransport.js +2 -1
- package/dist/client.d.ts +3 -1
- package/dist/client.js +2 -0
- package/dist/core/Lambder.d.ts +232 -92
- package/dist/core/Lambder.js +400 -103
- package/dist/core/LambderCallSummary.d.ts +43 -0
- package/dist/core/LambderCallSummary.js +10 -0
- package/dist/core/LambderContext.d.ts +15 -2
- package/dist/core/LambderContext.js +32 -9
- package/dist/core/LambderCreateOptions.d.ts +78 -64
- package/dist/core/LambderCreateOptions.js +39 -21
- package/dist/core/LambderInvocationScope.d.ts +25 -0
- package/dist/core/LambderInvocationScope.js +21 -0
- package/dist/core/LambderResponse.d.ts +11 -0
- package/dist/core/LambderResponse.js +12 -0
- package/dist/core/LambderResponseBuilder.js +5 -1
- package/dist/index.d.ts +13 -7
- package/dist/index.js +3 -1
- package/dist/invoke/LambderInvokeCaller.d.ts +48 -31
- package/dist/invoke/LambderInvokeCaller.js +110 -94
- package/dist/invoke/LambderLambdaEvent.d.ts +2 -1
- package/dist/invoke/LambderLambdaEvent.js +8 -3
- package/dist/invoke/lambderHandlerTransport.js +2 -1
- package/dist/mock/LambderMockApp.d.ts +39 -33
- package/dist/mock/LambderMockApp.js +66 -55
- package/dist/mock/LambderMockCreateOptions.d.ts +8 -10
- package/dist/mock/LambderMockTypes.d.ts +44 -7
- package/dist/mock/lambderMockInvokeTransport.d.ts +9 -1
- package/dist/mock/lambderMockInvokeTransport.js +15 -7
- package/dist/mock/lambderMockMswHandler.js +8 -4
- package/dist/mock.d.ts +2 -2
- package/dist/session/LambderSessionManager.d.ts +46 -3
- package/dist/session/LambderSessionManager.js +62 -11
- package/dist/shared/LambderI18n.js +49 -4
- package/dist/shared/contracts/LambderRateLimiter.d.ts +2 -2
- package/dist/shared/contracts/LambderRateLimiter.js +2 -2
- package/dist/shared/transport/LambderApiTransport.d.ts +3 -2
- package/dist/shared/transport/LambderApiTransport.js +2 -3
- package/dist/shared/transport/lambderCookieJarTransport.js +2 -1
- package/dist/shared/util/LambderInvocationScope.d.ts +10 -0
- package/dist/shared/util/LambderInvocationScope.js +5 -0
- package/dist/shared/util/LambderNamedMaps.d.ts +22 -0
- package/dist/shared/util/LambderNamedMaps.js +19 -0
- package/dist/shared/util/LambderTypeUtilities.d.ts +1 -1
- package/dist/shared/wire/LambderApiContract.d.ts +4 -18
- package/dist/shared/wire/LambderApiContract.js +1 -1
- package/dist/shared/wire/LambderApiGroupCalls.d.ts +31 -0
- package/dist/shared/wire/LambderApiGroupCalls.js +68 -0
- package/dist/shared/wire/LambderApiNames.d.ts +55 -0
- package/dist/shared/wire/LambderApiNames.js +81 -0
- package/dist/shared/wire/LambderCallOutcome.d.ts +27 -0
- package/dist/shared/wire/LambderCallOutcome.js +47 -0
- package/dist/shared/wire/LambderInvokeApiId.d.ts +10 -0
- package/dist/shared/wire/LambderInvokeApiId.js +10 -0
- package/dist/shared/wire/LambderUploadSchemas.js +2 -1
- package/dist/stores/LambderDdbRateLimiter.d.ts +24 -16
- package/dist/stores/LambderDdbRateLimiter.js +64 -45
- package/dist/stores/LambderMemoryRateLimiter.d.ts +9 -4
- package/dist/stores/LambderMemoryRateLimiter.js +20 -15
- package/dist/testing/LambderTestApp.d.ts +18 -5
- package/dist/testing/LambderTestApp.js +21 -5
- package/dist/testing/LambderTestVisitor.d.ts +16 -8
- package/dist/testing/LambderTestVisitor.js +33 -25
- package/package.json +1 -1
|
@@ -11,6 +11,8 @@ import { DEFAULT_SESSION_TOKEN_COOKIE_KEY, DEFAULT_SESSION_CSRF_COOKIE_KEY } fro
|
|
|
11
11
|
import { readApiSignature } from '../shared/wire/LambderApiSignatureMap.js';
|
|
12
12
|
import { LambderReloadLoopBreaker, RELOAD_LOOP_WINDOW_MS } from './LambderReloadLoopBreaker.js';
|
|
13
13
|
import { lambderFetchTransport } from './lambderFetchTransport.js';
|
|
14
|
+
import { withApiGroupCalls } from '../shared/wire/LambderApiGroupCalls.js';
|
|
15
|
+
import { splitApiName } from '../shared/wire/LambderApiNames.js';
|
|
14
16
|
/**
|
|
15
17
|
* What keeps a stale bundle from reloading itself forever (see the class):
|
|
16
18
|
* one for the page, shared by every caller it builds, since a reload is the
|
|
@@ -19,74 +21,82 @@ import { lambderFetchTransport } from './lambderFetchTransport.js';
|
|
|
19
21
|
*/
|
|
20
22
|
const pageReloadLoopBreaker = new LambderReloadLoopBreaker();
|
|
21
23
|
/**
|
|
24
|
+
* The caller itself, before the groups: what LambderCaller is, less the
|
|
25
|
+
* endpoints it reaches by group (LambderCallerGroupCalls), which the
|
|
26
|
+
* constructor adds.
|
|
27
|
+
*
|
|
22
28
|
* @typeParam TContract - The API contract, for typed names, payloads and guard inputs.
|
|
23
29
|
* @typeParam TProvidedGuards - Guard names guardInputsProvider covers; those APIs' options argument becomes optional.
|
|
24
30
|
*/
|
|
25
|
-
|
|
26
|
-
apiPath;
|
|
27
|
-
apiVersion;
|
|
28
|
-
apiSignatures;
|
|
29
|
-
timeoutMs;
|
|
31
|
+
class LambderCallerCore {
|
|
32
|
+
#apiPath;
|
|
33
|
+
#apiVersion;
|
|
34
|
+
#apiSignatures;
|
|
35
|
+
#timeoutMs;
|
|
30
36
|
/** The calls currently in flight, in the order they started. */
|
|
31
37
|
fetchTrackerList = [];
|
|
32
38
|
/** Whether any call is in flight. Derived from the list, so the two cannot drift apart. */
|
|
33
39
|
get isLoading() { return this.fetchTrackerList.length > 0; }
|
|
34
|
-
versionExpiredHandler;
|
|
35
|
-
sessionExpiredHandler;
|
|
40
|
+
#versionExpiredHandler;
|
|
41
|
+
#sessionExpiredHandler;
|
|
36
42
|
// Hears every endpoint's refusals, so held at the widest message: the
|
|
37
43
|
// constructor option types it to the contract's codes, and dispatch,
|
|
38
44
|
// generic over one endpoint's message, hands it one of those.
|
|
39
|
-
refusalHandler;
|
|
40
|
-
notAuthorizedHandler;
|
|
41
|
-
errorHandler;
|
|
42
|
-
apiInputValidationErrorHandler;
|
|
43
|
-
logListHandler;
|
|
44
|
-
fetchStartedHandler;
|
|
45
|
-
fetchEndedHandler;
|
|
46
|
-
guardInputsProvider;
|
|
47
|
-
sessionTokenCookieKey = DEFAULT_SESSION_TOKEN_COOKIE_KEY;
|
|
48
|
-
sessionCsrfCookieKey = DEFAULT_SESSION_CSRF_COOKIE_KEY;
|
|
49
|
-
sessionCookieDomain;
|
|
50
|
-
requestCompression;
|
|
51
|
-
transport;
|
|
45
|
+
#refusalHandler;
|
|
46
|
+
#notAuthorizedHandler;
|
|
47
|
+
#errorHandler;
|
|
48
|
+
#apiInputValidationErrorHandler;
|
|
49
|
+
#logListHandler;
|
|
50
|
+
#fetchStartedHandler;
|
|
51
|
+
#fetchEndedHandler;
|
|
52
|
+
#guardInputsProvider;
|
|
53
|
+
#sessionTokenCookieKey = DEFAULT_SESSION_TOKEN_COOKIE_KEY;
|
|
54
|
+
#sessionCsrfCookieKey = DEFAULT_SESSION_CSRF_COOKIE_KEY;
|
|
55
|
+
#sessionCookieDomain;
|
|
56
|
+
#requestCompression;
|
|
57
|
+
#transport;
|
|
52
58
|
constructor(options) {
|
|
53
59
|
// The conditional provider option is resolved per instantiation;
|
|
54
60
|
// inside the class it is read through the plain shape.
|
|
55
61
|
const { apiPath, apiVersion, apiSignatures, isCorsEnabled, timeoutMs, versionExpiredHandler, sessionExpiredHandler, refusalHandler, notAuthorizedHandler, errorHandler, logListHandler, fetchStartedHandler, fetchEndedHandler, apiInputValidationErrorHandler, sessionCookieDomain, requestCompression, guardInputsProvider, transport, } = options;
|
|
56
|
-
this
|
|
57
|
-
this
|
|
58
|
-
this
|
|
59
|
-
this
|
|
60
|
-
this
|
|
62
|
+
this.#apiPath = apiPath;
|
|
63
|
+
this.#apiVersion = apiVersion;
|
|
64
|
+
this.#apiSignatures = apiSignatures;
|
|
65
|
+
this.#timeoutMs = timeoutMs;
|
|
66
|
+
this.#sessionCookieDomain = sessionCookieDomain;
|
|
61
67
|
// `?? false`: unlike the at-rest stores, this one is off unless asked for.
|
|
62
|
-
this
|
|
63
|
-
this
|
|
64
|
-
this
|
|
65
|
-
this
|
|
66
|
-
this
|
|
67
|
-
this
|
|
68
|
-
this
|
|
69
|
-
this
|
|
70
|
-
this
|
|
71
|
-
this
|
|
72
|
-
this
|
|
73
|
-
this
|
|
68
|
+
this.#requestCompression = resolveCompressionOption(requestCompression ?? false, DEFAULT_REQUEST_COMPRESSION_SETTINGS);
|
|
69
|
+
this.#transport = transport ?? lambderFetchTransport({ cors: isCorsEnabled });
|
|
70
|
+
this.#versionExpiredHandler = versionExpiredHandler;
|
|
71
|
+
this.#sessionExpiredHandler = sessionExpiredHandler;
|
|
72
|
+
this.#refusalHandler = refusalHandler;
|
|
73
|
+
this.#notAuthorizedHandler = notAuthorizedHandler;
|
|
74
|
+
this.#errorHandler = errorHandler;
|
|
75
|
+
this.#apiInputValidationErrorHandler = apiInputValidationErrorHandler;
|
|
76
|
+
this.#logListHandler = logListHandler;
|
|
77
|
+
this.#fetchStartedHandler = fetchStartedHandler;
|
|
78
|
+
this.#fetchEndedHandler = fetchEndedHandler;
|
|
79
|
+
this.#guardInputsProvider = guardInputsProvider;
|
|
80
|
+
// Each group of the contract, as a property: caller.orders.place(input)
|
|
81
|
+
// is caller.api("orders.place", input), and .outcome the apiOutcome.
|
|
82
|
+
const byName = this;
|
|
83
|
+
return withApiGroupCalls(this, (apiName, args) => byName.api(apiName, ...args), (apiName, args) => byName.apiOutcome(apiName, ...args));
|
|
74
84
|
}
|
|
75
85
|
;
|
|
76
86
|
setSessionCookieKey(sessionTokenCookieKey, sessionCsrfCookieKey) {
|
|
77
|
-
this
|
|
78
|
-
this
|
|
87
|
+
this.#sessionTokenCookieKey = sessionTokenCookieKey;
|
|
88
|
+
this.#sessionCsrfCookieKey = sessionCsrfCookieKey;
|
|
79
89
|
}
|
|
80
90
|
/** Replaces how calls reach the server: a mock runtime, an in-process handler, a decorated transport. */
|
|
81
91
|
setTransport(transport) {
|
|
82
|
-
this
|
|
92
|
+
this.#transport = transport;
|
|
83
93
|
return this;
|
|
84
94
|
}
|
|
85
|
-
clearSessionCookies() {
|
|
86
|
-
const domainOption = this
|
|
95
|
+
#clearSessionCookies() {
|
|
96
|
+
const domainOption = this.#sessionCookieDomain;
|
|
87
97
|
const hostname = globalThis.location?.hostname ?? "";
|
|
88
98
|
const resolvedDomain = typeof domainOption === "function" ? domainOption(hostname) : domainOption;
|
|
89
|
-
for (const key of [this
|
|
99
|
+
for (const key of [this.#sessionTokenCookieKey, this.#sessionCsrfCookieKey]) {
|
|
90
100
|
// Host-only and domain-scoped cookies are distinct entries; clear both.
|
|
91
101
|
// Only the CSRF cookie is reachable from here: the token cookie is
|
|
92
102
|
// HttpOnly, so its removal is the server's (a Set-Cookie on the
|
|
@@ -97,17 +107,17 @@ export default class LambderCaller {
|
|
|
97
107
|
}
|
|
98
108
|
}
|
|
99
109
|
/** One call, one outcome. Never throws; every failure path resolves to { ok: false }. */
|
|
100
|
-
async dispatch(apiName, payload, options) {
|
|
110
|
+
async #dispatch(apiName, payload, options) {
|
|
101
111
|
// Per-call overrides win over the constructor handlers.
|
|
102
|
-
const versionExpiredHandler = options?.versionExpiredHandler ?? this
|
|
103
|
-
const sessionExpiredHandler = options?.sessionExpiredHandler ?? this
|
|
104
|
-
const refusalHandler = options?.refusalHandler ?? this
|
|
105
|
-
const notAuthorizedHandler = options?.notAuthorizedHandler ?? this
|
|
106
|
-
const errorHandler = options?.errorHandler ?? this
|
|
107
|
-
const apiInputValidationErrorHandler = options?.apiInputValidationErrorHandler ?? this
|
|
108
|
-
const logListHandler = options?.logListHandler ?? this
|
|
109
|
-
const fetchStartedHandler = options?.fetchStartedHandler ?? this
|
|
110
|
-
const fetchEndedHandler = options?.fetchEndedHandler ?? this
|
|
112
|
+
const versionExpiredHandler = options?.versionExpiredHandler ?? this.#versionExpiredHandler;
|
|
113
|
+
const sessionExpiredHandler = options?.sessionExpiredHandler ?? this.#sessionExpiredHandler;
|
|
114
|
+
const refusalHandler = options?.refusalHandler ?? this.#refusalHandler;
|
|
115
|
+
const notAuthorizedHandler = options?.notAuthorizedHandler ?? this.#notAuthorizedHandler;
|
|
116
|
+
const errorHandler = options?.errorHandler ?? this.#errorHandler;
|
|
117
|
+
const apiInputValidationErrorHandler = options?.apiInputValidationErrorHandler ?? this.#apiInputValidationErrorHandler;
|
|
118
|
+
const logListHandler = options?.logListHandler ?? this.#logListHandler;
|
|
119
|
+
const fetchStartedHandler = options?.fetchStartedHandler ?? this.#fetchStartedHandler;
|
|
120
|
+
const fetchEndedHandler = options?.fetchEndedHandler ?? this.#fetchEndedHandler;
|
|
111
121
|
const headers = options?.headers;
|
|
112
122
|
const fetchTracker = { apiName };
|
|
113
123
|
// A key scope sends its current key and is told how the attempt
|
|
@@ -147,7 +157,7 @@ export default class LambderCaller {
|
|
|
147
157
|
};
|
|
148
158
|
// Timeout and abort wiring, shared with LambderInvokeCaller so the two
|
|
149
159
|
// cannot drift on what a late or abandoned call means.
|
|
150
|
-
const abort = createCallAbort({ timeoutMs: options?.timeoutMs ?? this
|
|
160
|
+
const abort = createCallAbort({ timeoutMs: options?.timeoutMs ?? this.#timeoutMs, signal: options?.signal });
|
|
151
161
|
const signal = abort.signal;
|
|
152
162
|
/** Reports a call that was given up on, or null while it still stands. */
|
|
153
163
|
const abandonedOutcome = async (stage) => {
|
|
@@ -161,21 +171,30 @@ export default class LambderCaller {
|
|
|
161
171
|
return outcome;
|
|
162
172
|
};
|
|
163
173
|
try {
|
|
174
|
+
// A name that is not group.action names no endpoint and no path:
|
|
175
|
+
// a caller's mistake, reported as one before anything is sent,
|
|
176
|
+
// rather than as the network failure its transport would throw.
|
|
177
|
+
if (!splitApiName(apiName)) {
|
|
178
|
+
idempotentAttempt.settle(IDEMPOTENT_ATTEMPT_NOT_SENT);
|
|
179
|
+
const error = new Error(`Lambder: "${apiName}" is not an endpoint name. An endpoint is named group.action, both identifiers.`);
|
|
180
|
+
await reportError(error);
|
|
181
|
+
return { ok: false, reason: 'unknown', error };
|
|
182
|
+
}
|
|
164
183
|
this.fetchTrackerList.push(fetchTracker);
|
|
165
184
|
if (fetchStartedHandler)
|
|
166
185
|
await fetchStartedHandler({
|
|
167
186
|
fetchParams: { apiName, payload, headers, },
|
|
168
187
|
activeFetchList: [...this.fetchTrackerList],
|
|
169
188
|
});
|
|
170
|
-
const version = this
|
|
189
|
+
const version = this.#apiVersion;
|
|
171
190
|
// The server's signature for this endpoint, when this build
|
|
172
191
|
// carries the map. A name the map lacks fails the call here, as a
|
|
173
192
|
// provider that threw would: the map predates the endpoint.
|
|
174
|
-
const signature = this
|
|
193
|
+
const signature = this.#apiSignatures ? await readApiSignature(this.#apiSignatures, apiName) : undefined;
|
|
175
194
|
const siteHost = globalThis.location?.hostname ?? "";
|
|
176
195
|
// Provider values underneath, per-call values on top.
|
|
177
|
-
const providedGuardInputs = this
|
|
178
|
-
? await this
|
|
196
|
+
const providedGuardInputs = this.#guardInputsProvider
|
|
197
|
+
? await this.#guardInputsProvider(apiName)
|
|
179
198
|
: undefined;
|
|
180
199
|
const guardInputs = mergeGuardInputs(providedGuardInputs, options?.guardInputs);
|
|
181
200
|
// Compressed when enabled and the payload's JSON reaches the
|
|
@@ -183,7 +202,7 @@ export default class LambderCaller {
|
|
|
183
202
|
// without CompressionStream always sends the payload plainly.
|
|
184
203
|
// Nothing here runs (the extra stringify included) unless
|
|
185
204
|
// compression is actually a possibility for this call.
|
|
186
|
-
const compressionMinBytes = resolveRequestCompressionMinBytes(options?.compressRequest, this
|
|
205
|
+
const compressionMinBytes = resolveRequestCompressionMinBytes(options?.compressRequest, this.#requestCompression);
|
|
187
206
|
const compressedPayload = compressionMinBytes !== null && payload !== undefined && isRequestCompressionAvailable()
|
|
188
207
|
? await compressPayloadGzip(JSON.stringify(payload), compressionMinBytes)
|
|
189
208
|
: null;
|
|
@@ -199,15 +218,15 @@ export default class LambderCaller {
|
|
|
199
218
|
// cookie with the old token. js-cookie reads nothing without a
|
|
200
219
|
// document: the token is "" then, and a transport that carries a
|
|
201
220
|
// cookie jar fills it in from there.
|
|
202
|
-
const token = Cookies.get(this
|
|
221
|
+
const token = Cookies.get(this.#sessionCsrfCookieKey) || "";
|
|
203
222
|
let answer;
|
|
204
223
|
try {
|
|
205
224
|
sent = true;
|
|
206
|
-
answer = await this
|
|
207
|
-
apiPath: this
|
|
225
|
+
answer = await this.#transport({
|
|
226
|
+
apiPath: this.#apiPath,
|
|
208
227
|
apiName, version, token, siteHost,
|
|
209
228
|
...(signature !== undefined ? { signature } : {}),
|
|
210
|
-
csrfCookieKey: this
|
|
229
|
+
csrfCookieKey: this.#sessionCsrfCookieKey,
|
|
211
230
|
...(compressedPayload ? { compressed: compressedPayload } : { payload }),
|
|
212
231
|
...(guardInputs !== undefined ? { guardInputs } : {}),
|
|
213
232
|
...(idempotencyKey !== undefined ? { idempotencyKey } : {}),
|
|
@@ -307,10 +326,10 @@ export default class LambderCaller {
|
|
|
307
326
|
// token it posted and the one it holds, since the page's
|
|
308
327
|
// cookie is not where that session lives.
|
|
309
328
|
const postedToken = answer.csrfTokens?.posted ?? token;
|
|
310
|
-
const heldToken = answer.csrfTokens ? answer.csrfTokens.held() : Cookies.get(this
|
|
329
|
+
const heldToken = answer.csrfTokens ? answer.csrfTokens.held() : Cookies.get(this.#sessionCsrfCookieKey) || "";
|
|
311
330
|
if (heldToken !== "" && heldToken !== postedToken)
|
|
312
331
|
return outcome;
|
|
313
|
-
this
|
|
332
|
+
this.#clearSessionCookies();
|
|
314
333
|
if (sessionExpiredHandler) {
|
|
315
334
|
await sessionExpiredHandler();
|
|
316
335
|
}
|
|
@@ -372,7 +391,7 @@ export default class LambderCaller {
|
|
|
372
391
|
// elements read as unknown from inside; the contract shaped them on
|
|
373
392
|
// the way in, which is where the guarantee belongs.
|
|
374
393
|
const [payload, options] = rest;
|
|
375
|
-
return await this
|
|
394
|
+
return await this.#dispatch(apiName, payload, options);
|
|
376
395
|
}
|
|
377
396
|
;
|
|
378
397
|
/**
|
|
@@ -384,7 +403,9 @@ export default class LambderCaller {
|
|
|
384
403
|
*/
|
|
385
404
|
async api(apiName, ...rest) {
|
|
386
405
|
const [payload, options] = rest;
|
|
387
|
-
const outcome = await this
|
|
406
|
+
const outcome = await this.#dispatch(apiName, payload, options);
|
|
388
407
|
return outcome.ok ? outcome.payload : undefined;
|
|
389
408
|
}
|
|
390
409
|
}
|
|
410
|
+
const LambderCaller = LambderCallerCore;
|
|
411
|
+
export default LambderCaller;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { buildTransportEnvelope, LambderTransportFailure } from "../shared/transport/LambderApiTransport.js";
|
|
2
|
+
import { apiCallPath } from "../shared/wire/LambderApiNames.js";
|
|
2
3
|
/**
|
|
3
4
|
* fetch, with a clearer error for one failure. Outside a page a relative
|
|
4
5
|
* apiPath has nothing to resolve against, so fetch rejects with a URL parse
|
|
@@ -51,7 +52,7 @@ export const lambderFetchTransport = (options = {}) => async (request) => {
|
|
|
51
52
|
// ("text/plain, application/json") rather than give way to it.
|
|
52
53
|
const owned = new Set(['content-type', ...(request.cookies?.length ? ['cookie'] : [])]);
|
|
53
54
|
const callerHeaders = Object.fromEntries(Object.entries(request.headers ?? {}).filter(([name]) => !owned.has(name.toLowerCase())));
|
|
54
|
-
const response = await fetchOrExplain(request.apiPath, {
|
|
55
|
+
const response = await fetchOrExplain(apiCallPath(request.apiPath, request.apiName), {
|
|
55
56
|
method: 'POST', cache: 'no-cache',
|
|
56
57
|
// Cross-origin API hosts need CORS mode and included credentials.
|
|
57
58
|
mode: cors ? 'cors' : 'same-origin',
|
package/dist/client.d.ts
CHANGED
|
@@ -20,7 +20,9 @@ export type { LambderApiSignatureMap } from "./shared/wire/LambderApiSignatureMa
|
|
|
20
20
|
export { RELOAD_LOOP_WINDOW_MS } from "./client/LambderReloadLoopBreaker.js";
|
|
21
21
|
export { compareDottedVersions, isDottedVersion } from "./shared/wire/LambderVersionOrder.js";
|
|
22
22
|
export type { LambderApiAnswerOutcome, LambderApiSuccessOutcome, LambderApiCallFailure, LambderApiValidationFailure, LambderApiEnvelopeFailure, LambderApiHttpAnswer, } from "./shared/wire/LambderApiOutcome.js";
|
|
23
|
-
export type { LambderApiOutcome, LambderApiFailureReason, LambderValidationError, LambderCallOptions, LambderCallerOptions, LambderGuardInputsProvider, LambderProvidedGuardInputs, LambderLogListHandler, } from "./client/LambderCaller.js";
|
|
23
|
+
export type { LambderApiOutcome, LambderApiFailureReason, LambderValidationError, LambderCallOptions, LambderCallerOptions, LambderGuardInputsProvider, LambderProvidedGuardInputs, LambderLogListHandler, LambderCallerEndpoint, LambderCallerGroupCalls, LambderCallerMembers, } from "./client/LambderCaller.js";
|
|
24
|
+
export { apiCallPath, splitApiName, LAMBDER_RESERVED_GROUP_NAMES, LAMBDER_RESERVED_ACTION_NAMES } from "./shared/wire/LambderApiNames.js";
|
|
25
|
+
export type { LambderContractGroupsOf, LambderContractNamesInGroup, LambderContractActionOf } from "./shared/wire/LambderApiGroupCalls.js";
|
|
24
26
|
export { createIdempotencyKey, createIdempotencyKeyScope } from "./shared/wire/LambderIdempotencyKeyScope.js";
|
|
25
27
|
export type { LambderIdempotencyKeyScope } from "./shared/wire/LambderIdempotencyKeyScope.js";
|
|
26
28
|
export { LambderApiRefusal, isLambderApiRefusal, isLambderRefusalCode, refuse, refusalMessageOf, LAMBDER_REFUSAL_CODES } from "./shared/wire/LambderApiRefusal.js";
|
package/dist/client.js
CHANGED
|
@@ -19,6 +19,8 @@ export { resolveApiOutcome } from "./shared/wire/LambderApiOutcome.js";
|
|
|
19
19
|
export { apiNameKeyOf, lookupApiSignature, readApiSignature, API_SIGNATURE_HEX_LENGTH, extensibleEnum } from "./shared/wire/LambderApiSignatureMap.js";
|
|
20
20
|
export { RELOAD_LOOP_WINDOW_MS } from "./client/LambderReloadLoopBreaker.js";
|
|
21
21
|
export { compareDottedVersions, isDottedVersion } from "./shared/wire/LambderVersionOrder.js";
|
|
22
|
+
// How an endpoint is named, and where a call to it goes.
|
|
23
|
+
export { apiCallPath, splitApiName, LAMBDER_RESERVED_GROUP_NAMES, LAMBDER_RESERVED_ACTION_NAMES } from "./shared/wire/LambderApiNames.js";
|
|
22
24
|
export { createIdempotencyKey, createIdempotencyKeyScope } from "./shared/wire/LambderIdempotencyKeyScope.js";
|
|
23
25
|
// Typed API refusals (isomorphic: shared code may throw them from anywhere;
|
|
24
26
|
// in the browser they are plain Errors).
|