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
|
@@ -72,9 +72,11 @@ export type LambderGuardRefusals<TRefusals extends readonly string[] = readonly
|
|
|
72
72
|
*
|
|
73
73
|
* Orthogonally, a guard may also:
|
|
74
74
|
*
|
|
75
|
-
* - declare `session: true`: the guard needs ctx.session, so it
|
|
76
|
-
*
|
|
77
|
-
*
|
|
75
|
+
* - declare `session: true`: the guard needs ctx.session, so it makes every
|
|
76
|
+
* endpoint declaring it a session endpoint (the session is read before the
|
|
77
|
+
* guards run, a call without one is answered sessionExpired, and the
|
|
78
|
+
* endpoint's handler receives the session-typed context), and it receives
|
|
79
|
+
* the session-typed context itself.
|
|
78
80
|
* - take a PARAMETER: annotate a 3rd handler argument
|
|
79
81
|
* (`(ctx, payload, param: YourType) => ...`) and APIs pass the value in
|
|
80
82
|
* their declaration: `guards: { yourGuard: paramValue }`. The value is
|
|
@@ -272,19 +274,16 @@ export type LambderGuardMetaMap<TGuards> = {
|
|
|
272
274
|
[K in keyof TGuards]: LambderGuardMeta<TGuards[K]>;
|
|
273
275
|
};
|
|
274
276
|
/**
|
|
275
|
-
* Guard names an API may declare: every guard the instance holds, session
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
* known, by
|
|
277
|
+
* Guard names an API may declare: every guard the instance holds, a session
|
|
278
|
+
* guard included, which makes the API a session one (LambderApiModeOf).
|
|
279
|
+
* Whether the API's input carries the fields an apiInput guard reads is
|
|
280
|
+
* asked of the names it declared, once that input is known, by
|
|
281
|
+
* LambderGuardNamesInputLacks.
|
|
279
282
|
*/
|
|
280
|
-
export type LambderAllowedGuardNames<TGuards
|
|
281
|
-
[K in keyof TGuards]: TGuards[K] extends {
|
|
282
|
-
session: true;
|
|
283
|
-
} ? (TIncludeSession extends true ? K : never) : K;
|
|
284
|
-
}[keyof TGuards] & string;
|
|
283
|
+
export type LambderAllowedGuardNames<TGuards> = keyof TGuards & string;
|
|
285
284
|
/**
|
|
286
285
|
* The apiInput guards among `TNames` whose fields the API's payload does not
|
|
287
|
-
* carry.
|
|
286
|
+
* carry. defineApi asks this once the API's input is known, of
|
|
288
287
|
* the names its guards option holds; see LambderPayloadSliceCheck.
|
|
289
288
|
*
|
|
290
289
|
* The payload is compared whole, not member by member: a bare `TPayload
|
|
@@ -298,14 +297,14 @@ export type LambderGuardNamesInputLacks<TGuards, TNames, TPayload> = {
|
|
|
298
297
|
} ? ([TPayload] extends [R] ? never : K) : never;
|
|
299
298
|
}[TNames & keyof TGuards] & string;
|
|
300
299
|
/** The allowed guard names whose handler takes no param (usable in the string/array forms). */
|
|
301
|
-
export type LambderParamlessGuardNames<TGuards
|
|
302
|
-
[K in LambderAllowedGuardNames<TGuards
|
|
300
|
+
export type LambderParamlessGuardNames<TGuards> = {
|
|
301
|
+
[K in LambderAllowedGuardNames<TGuards> & keyof TGuards]: TGuards[K] extends {
|
|
303
302
|
param: undefined;
|
|
304
303
|
} ? K & string : never;
|
|
305
|
-
}[LambderAllowedGuardNames<TGuards
|
|
304
|
+
}[LambderAllowedGuardNames<TGuards> & keyof TGuards];
|
|
306
305
|
/** The map form's full shape: every declarable guard name, each carrying its own param type. */
|
|
307
|
-
type LambderGuardsMap<TGuards
|
|
308
|
-
readonly [K in LambderAllowedGuardNames<TGuards
|
|
306
|
+
type LambderGuardsMap<TGuards> = {
|
|
307
|
+
readonly [K in LambderAllowedGuardNames<TGuards> & keyof TGuards]?: TGuards[K] extends {
|
|
309
308
|
param: undefined;
|
|
310
309
|
} ? true : TGuards[K] extends {
|
|
311
310
|
param: infer P;
|
|
@@ -324,10 +323,10 @@ type LambderGuardsMap<TGuards, TIncludeSession extends boolean> = {
|
|
|
324
323
|
* compiler builds it once per instance rather than once per API; see
|
|
325
324
|
* LambderPayloadSliceCheck for why, and for where the input is asked.
|
|
326
325
|
*/
|
|
327
|
-
export type LambderGuardsOption<TGuards
|
|
328
|
-
LambderParamlessGuardNames<TGuards
|
|
329
|
-
...LambderParamlessGuardNames<TGuards
|
|
330
|
-
] | LambderNonEmptyOptionMap<LambderGuardsMap<TGuards
|
|
326
|
+
export type LambderGuardsOption<TGuards> = LambderParamlessGuardNames<TGuards> | readonly [
|
|
327
|
+
LambderParamlessGuardNames<TGuards>,
|
|
328
|
+
...LambderParamlessGuardNames<TGuards>[]
|
|
329
|
+
] | LambderNonEmptyOptionMap<LambderGuardsMap<TGuards>>;
|
|
331
330
|
/**
|
|
332
331
|
* The typed ctx.guardData an API's handler sees: declared guards that return
|
|
333
332
|
* a value, keyed by name. Check-only (void) guards never appear.
|
|
@@ -101,7 +101,7 @@ export class LambderApiGuardsEngine {
|
|
|
101
101
|
throw new Error(`Lambder: API "${apiName}" references unknown guard "${name}". Declare it in the guards option at creation.`);
|
|
102
102
|
}
|
|
103
103
|
if (guardDef.session && mode !== "session") {
|
|
104
|
-
throw new Error(`Lambder: API "${apiName}" uses guard "${name}" (session: true)
|
|
104
|
+
throw new Error(`Lambder: API "${apiName}" uses guard "${name}" (session: true) but is registered as public. An endpoint declaring a session guard is a session endpoint.`);
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
}
|
|
@@ -116,7 +116,7 @@ export type LambderApiRateLimitPolicyConfig<TCtx = any> = LambderRateLimitPolicy
|
|
|
116
116
|
export type LambderApiRateLimitsConfig<TPolicies extends Record<string, LambderApiRateLimitPolicyConfig<any>>> = {
|
|
117
117
|
/** Your limiter instance (LambderDdbRateLimiter, LambderMemoryRateLimiter, or your own); its table and keyPrefix apply as configured on it. */
|
|
118
118
|
limiter: LambderRateLimiter;
|
|
119
|
-
/** Named policies referenced (typed) from
|
|
119
|
+
/** Named policies referenced (typed) from defineApi. */
|
|
120
120
|
policies: TPolicies;
|
|
121
121
|
/**
|
|
122
122
|
* Let the request through when the limiter itself fails (the table is
|
|
@@ -144,23 +144,23 @@ export type LambderApiRateLimitsConfig<TPolicies extends Record<string, LambderA
|
|
|
144
144
|
* without one, and a union holding undefined for a policy typed as the
|
|
145
145
|
* general LambderApiRateLimitPolicyConfig, where only registration can tell.
|
|
146
146
|
*/
|
|
147
|
-
type LambderPolicyPerOf<TPolicy> = "per" extends keyof TPolicy ? TPolicy["per" & keyof TPolicy] : undefined;
|
|
147
|
+
export type LambderPolicyPerOf<TPolicy> = "per" extends keyof TPolicy ? TPolicy["per" & keyof TPolicy] : undefined;
|
|
148
148
|
/**
|
|
149
|
-
* Policy names an API may reference:
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
* here and
|
|
153
|
-
*
|
|
154
|
-
*
|
|
149
|
+
* Policy names an API may reference: never a policy without `per`, whose key
|
|
150
|
+
* only the code that charges it knows. A policy whose type does not settle
|
|
151
|
+
* its `per` is allowed here and checked at registration. A session-keyed
|
|
152
|
+
* policy is allowed here too, and refused of an API none of whose guards
|
|
153
|
+
* needs a session by LambderPublicSessionPolicyCheck, once its guards are
|
|
154
|
+
* known. Whether the API's input carries the fields an apiInput-keyed policy
|
|
155
|
+
* reads is asked of the names it declared, once that input is known, by
|
|
156
|
+
* LambderPolicyNamesInputLacks.
|
|
155
157
|
*/
|
|
156
|
-
export type LambderAllowedPolicyNames<TPolicies
|
|
157
|
-
[K in keyof TPolicies]: [
|
|
158
|
-
LambderPolicyPerOf<TPolicies[K]>
|
|
159
|
-
] extends [undefined] ? never : [LambderPolicyPerOf<TPolicies[K]>] extends ["session"] ? (TIncludeSession extends true ? K : never) : K;
|
|
158
|
+
export type LambderAllowedPolicyNames<TPolicies> = {
|
|
159
|
+
[K in keyof TPolicies]: [LambderPolicyPerOf<TPolicies[K]>] extends [undefined] ? never : K;
|
|
160
160
|
}[keyof TPolicies] & string;
|
|
161
161
|
/**
|
|
162
162
|
* The apiInput-keyed policies among `TNames` whose key fields the API's
|
|
163
|
-
* payload does not carry.
|
|
163
|
+
* payload does not carry. defineApi asks this once the API's
|
|
164
164
|
* input is known, of the names its rateLimit option holds; see
|
|
165
165
|
* LambderPayloadSliceCheck.
|
|
166
166
|
*
|
|
@@ -235,8 +235,8 @@ type LambderRateLimitOverrideFor<TPolicy> = TPolicy extends {
|
|
|
235
235
|
budget: "perPolicy";
|
|
236
236
|
} ? Pick<LambderRateLimitOverride, "refusal"> : LambderRateLimitOverride;
|
|
237
237
|
/** The map form's full shape: every referable policy name, each carrying its own override. */
|
|
238
|
-
type LambderRateLimitMap<TPolicies
|
|
239
|
-
readonly [K in LambderAllowedPolicyNames<TPolicies
|
|
238
|
+
type LambderRateLimitMap<TPolicies> = {
|
|
239
|
+
readonly [K in LambderAllowedPolicyNames<TPolicies> & keyof TPolicies]?: true | LambderRateLimitOverrideFor<TPolicies[K]>;
|
|
240
240
|
};
|
|
241
241
|
/**
|
|
242
242
|
* The per-API `rateLimit` option: one policy name, a non-empty ordered list
|
|
@@ -251,10 +251,10 @@ type LambderRateLimitMap<TPolicies, TIncludeSession extends boolean> = {
|
|
|
251
251
|
* It depends on the instance alone, never on the API's input, as
|
|
252
252
|
* LambderGuardsOption does; see LambderPayloadSliceCheck.
|
|
253
253
|
*/
|
|
254
|
-
export type LambderRateLimitOption<TPolicies
|
|
255
|
-
LambderAllowedPolicyNames<TPolicies
|
|
256
|
-
...LambderAllowedPolicyNames<TPolicies
|
|
257
|
-
] | LambderNonEmptyOptionMap<LambderRateLimitMap<TPolicies
|
|
254
|
+
export type LambderRateLimitOption<TPolicies> = LambderAllowedPolicyNames<TPolicies> | readonly [
|
|
255
|
+
LambderAllowedPolicyNames<TPolicies>,
|
|
256
|
+
...LambderAllowedPolicyNames<TPolicies>[]
|
|
257
|
+
] | LambderNonEmptyOptionMap<LambderRateLimitMap<TPolicies>>;
|
|
258
258
|
/**
|
|
259
259
|
* When in a call a policy is checked.
|
|
260
260
|
*
|
|
@@ -209,7 +209,7 @@ export class LambderApiRateLimitsEngine {
|
|
|
209
209
|
throw new Error(`Lambder: API "${apiName}" references rate-limit policy "${name}", which declares no per: its key is the one a handler passes to ctx.rateLimit("${name}", key), so the request alone cannot be counted against it.`);
|
|
210
210
|
}
|
|
211
211
|
if (policy.per === "session" && mode !== "session") {
|
|
212
|
-
throw new Error(`Lambder: API "${apiName}" uses rate-limit policy "${name}" (per "session"), which
|
|
212
|
+
throw new Error(`Lambder: API "${apiName}" uses rate-limit policy "${name}" (per "session"), which counts per session, and none of its guards needs a session.`);
|
|
213
213
|
}
|
|
214
214
|
if (override)
|
|
215
215
|
assertWindowLimits(`API "${apiName}" override of rate-limit policy "${name}"`, override);
|
|
@@ -3,6 +3,7 @@ import type { LambderJsonOf } from "../shared/wire/LambderApiContract.js";
|
|
|
3
3
|
import type { LambderRefusalStatusCode } from "../shared/wire/LambderHttpStatus.js";
|
|
4
4
|
import type { LambderNoExtraKeys } from "../shared/util/LambderTypeUtilities.js";
|
|
5
5
|
import { LambderApiRefusal } from "../shared/wire/LambderApiRefusal.js";
|
|
6
|
+
import { type LambderMergedNamedMaps, type LambderNamedMapsOption, type LambderNoRepeatedNames } from "../shared/util/LambderNamedMaps.js";
|
|
6
7
|
/**
|
|
7
8
|
* One code of an app's refusal vocabulary: the schema of the data it carries,
|
|
8
9
|
* when it carries data, and how every refusal with the code leaves the
|
|
@@ -69,6 +70,27 @@ export type LambderRefusalVocabularyChecks<TVocabulary> = {
|
|
|
69
70
|
readonly "lambder: a refusal code may not start with lambder/, the prefix of the framework's own codes": never;
|
|
70
71
|
} : LambderNoExtraKeys<TVocabulary[TCode], LambderRefusalDeclaration> & LambderRefusalDataCheck<TVocabulary[TCode]>;
|
|
71
72
|
};
|
|
73
|
+
/**
|
|
74
|
+
* The vocabulary as declareRefusals() takes it: one map of codes, or a list of
|
|
75
|
+
* them, so each part of an app declares its own codes beside its APIs. A
|
|
76
|
+
* code two maps declare is refused.
|
|
77
|
+
*/
|
|
78
|
+
export type LambderRefusalVocabularyOption = LambderNamedMapsOption<LambderRefusalVocabulary>;
|
|
79
|
+
/** The one vocabulary a declareRefusals() option declares: a list's maps merged, a lone map as it is. */
|
|
80
|
+
export type LambderMergedRefusalVocabulary<TOption> = LambderMergedNamedMaps<TOption> extends infer TMerged extends LambderRefusalVocabulary ? TMerged : never;
|
|
81
|
+
/** A declareRefusals() option checked code by code, in each map of a list, and a list declaring no code twice. */
|
|
82
|
+
export type LambderRefusalVocabularyOptionChecks<TOption> = TOption extends readonly unknown[] ? {
|
|
83
|
+
[TIndex in keyof TOption]: LambderRefusalVocabularyChecks<TOption[TIndex]>;
|
|
84
|
+
} & LambderNoRepeatedNames<TOption> : LambderRefusalVocabularyChecks<TOption>;
|
|
85
|
+
/**
|
|
86
|
+
* A declareRefusals() option as the one vocabulary it declares, checked
|
|
87
|
+
* (see readRefusalVocabulary) and keyed. `declarer` names the init in the
|
|
88
|
+
* errors.
|
|
89
|
+
*/
|
|
90
|
+
export declare const declaredRefusalVocabulary: (option: LambderRefusalVocabularyOption, declarer: string) => {
|
|
91
|
+
refusals: LambderRefusalVocabulary;
|
|
92
|
+
vocabulary: Map<string, LambderRefusalDeclaration>;
|
|
93
|
+
};
|
|
72
94
|
/** One code's declaration as checkedRefusal reads it: how a refusal with the code leaves, and its schema. */
|
|
73
95
|
export declare const allowedRefusalOf: (declaration: LambderRefusalDeclaration) => LambderApiAllowedRefusal;
|
|
74
96
|
/**
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import { LambderApiRefusal, isLambderRefusalCode } from "../shared/wire/LambderApiRefusal.js";
|
|
2
2
|
import { describePayloadKind, isObjectPayload } from "../shared/wire/LambderObjectPayload.js";
|
|
3
|
+
import { mergeNamedMaps } from "../shared/util/LambderNamedMaps.js";
|
|
4
|
+
/**
|
|
5
|
+
* A declareRefusals() option as the one vocabulary it declares, checked
|
|
6
|
+
* (see readRefusalVocabulary) and keyed. `declarer` names the init in the
|
|
7
|
+
* errors.
|
|
8
|
+
*/
|
|
9
|
+
export const declaredRefusalVocabulary = (option, declarer) => {
|
|
10
|
+
if (option === null || typeof option !== "object") {
|
|
11
|
+
throw new Error(`${declarer}: declareRefusals() takes the vocabulary, an object of codes or a list of them.`);
|
|
12
|
+
}
|
|
13
|
+
const refusals = mergeNamedMaps(option, "refusal code", "declareRefusals()");
|
|
14
|
+
return { refusals, vocabulary: readRefusalVocabulary(refusals) };
|
|
15
|
+
};
|
|
3
16
|
/** One code's declaration as checkedRefusal reads it: how a refusal with the code leaves, and its schema. */
|
|
4
17
|
export const allowedRefusalOf = (declaration) => {
|
|
5
18
|
const leaves = {
|
|
@@ -18,7 +18,15 @@ export declare const lowercaseHeaderNames: (headers: Record<string, string | und
|
|
|
18
18
|
* transport request, and from here on nothing in the pipeline knows which.
|
|
19
19
|
*/
|
|
20
20
|
export type LambderApiRequest = {
|
|
21
|
+
/** The endpoint called: `group.action`, read off the path the call was posted to (`{apiPath}/{group}/{action}`). */
|
|
21
22
|
apiName: string;
|
|
23
|
+
/**
|
|
24
|
+
* Set on a call posted to apiPath itself with the endpoint named in the
|
|
25
|
+
* body, the way callers did before endpoints had paths: such a caller is a
|
|
26
|
+
* page loaded from an older build, and it is answered that its version
|
|
27
|
+
* expired, which reloads it.
|
|
28
|
+
*/
|
|
29
|
+
retiredPath?: true;
|
|
22
30
|
/** The caller's apiVersion, informational; null when it sent none. */
|
|
23
31
|
version: string | null;
|
|
24
32
|
/** The signature the caller carries for this endpoint (see LambderApiSignatureMap), for the signature gate; null when it sent none. */
|
|
@@ -79,13 +87,14 @@ export type LambderApiRequestInfo = {
|
|
|
79
87
|
*/
|
|
80
88
|
export declare const isApiCallContentType: (lowercasedHeaders: Record<string, string | undefined>) => boolean;
|
|
81
89
|
/**
|
|
82
|
-
* Reads the posted envelope
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
* message they already give.
|
|
90
|
+
* Reads the posted envelope of a call to `apiName` into a request; the name
|
|
91
|
+
* comes from where the call was posted, never from the body. Everything is
|
|
92
|
+
* taken as posted: a malformed idempotencyKey or guardInputs value is the
|
|
93
|
+
* engines' to refuse, with the client-facing message they already give.
|
|
87
94
|
*/
|
|
88
|
-
export declare const readApiEnvelope: (post: Record<string, unknown> | null | undefined, info: LambderApiRequestInfo
|
|
95
|
+
export declare const readApiEnvelope: (post: Record<string, unknown> | null | undefined, info: LambderApiRequestInfo, apiName: string, flags?: {
|
|
96
|
+
retiredPath?: true;
|
|
97
|
+
}) => LambderApiRequest;
|
|
89
98
|
/** Outcome of restoring a compressed request payload; the message is client-facing. */
|
|
90
99
|
export type LambderRestorePayloadResult = {
|
|
91
100
|
ok: true;
|
|
@@ -43,20 +43,19 @@ export const lowercaseHeaderNames = (headers) => {
|
|
|
43
43
|
*/
|
|
44
44
|
export const isApiCallContentType = (lowercasedHeaders) => (lowercasedHeaders["content-type"] ?? "").split(";")[0].trim().toLowerCase() === "application/json";
|
|
45
45
|
/**
|
|
46
|
-
* Reads the posted envelope
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* message they already give.
|
|
46
|
+
* Reads the posted envelope of a call to `apiName` into a request; the name
|
|
47
|
+
* comes from where the call was posted, never from the body. Everything is
|
|
48
|
+
* taken as posted: a malformed idempotencyKey or guardInputs value is the
|
|
49
|
+
* engines' to refuse, with the client-facing message they already give.
|
|
51
50
|
*/
|
|
52
|
-
export const readApiEnvelope = (post, info) => {
|
|
53
|
-
|
|
54
|
-
return null;
|
|
51
|
+
export const readApiEnvelope = (post, info, apiName, flags = {}) => {
|
|
52
|
+
post ??= {};
|
|
55
53
|
const hasGzip = post[COMPRESSED_PAYLOAD_GZ_FIELD] !== undefined;
|
|
56
54
|
const hasBrotli = post[COMPRESSED_PAYLOAD_BR_FIELD] !== undefined;
|
|
57
55
|
const guardInputs = post.guardInputs;
|
|
58
56
|
return {
|
|
59
|
-
apiName
|
|
57
|
+
apiName,
|
|
58
|
+
...(flags.retiredPath ? { retiredPath: true } : {}),
|
|
60
59
|
version: typeof post.version === "string" ? post.version : null,
|
|
61
60
|
signature: typeof post.signature === "string" ? post.signature : null,
|
|
62
61
|
token: typeof post.token === "string" ? post.token : "",
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { LambderModuleLocation } from "./moduleLocation.js";
|
|
2
|
+
import { type LambderApiContractFileOptions } from "./writeApiContract.js";
|
|
3
|
+
import { type LambderApiSignatureFileOptions } from "./writeApiSignatures.js";
|
|
4
|
+
import { type LambderApiOptionsFileOptions } from "./writeApiOptions.js";
|
|
5
|
+
import { type LambderApiGuardParamsFileOptions } from "./writeApiGuardParams.js";
|
|
6
|
+
/** What a file's writer takes beyond where the instance is, which the app gives once for all its files. */
|
|
7
|
+
type LambderAppFile<TOptions> = Omit<TOptions, "module" | "exportName" | "tsconfig" | "check">;
|
|
8
|
+
/** One app: where its instance is, and which files it is written to. Each file is optional. */
|
|
9
|
+
export type LambderApiFilesApp = {
|
|
10
|
+
/** The module that exports the instance, usually the server's entry: a path relative to the working directory, or a file URL. */
|
|
11
|
+
module: LambderModuleLocation;
|
|
12
|
+
/** The export that holds the instance. Default: "default". */
|
|
13
|
+
exportName?: string;
|
|
14
|
+
/** The tsconfig.json the module compiles under, which the contract is read through. Default: the nearest one at or above the module. */
|
|
15
|
+
tsconfig?: string;
|
|
16
|
+
/** The contract a client compiles against, as plain types (writeApiContract). */
|
|
17
|
+
contract?: LambderAppFile<LambderApiContractFileOptions>;
|
|
18
|
+
/** The signature file both sides ship (writeApiSignatures). */
|
|
19
|
+
signatures?: LambderAppFile<LambderApiSignatureFileOptions>;
|
|
20
|
+
/** The declared options as plain data, for a mock and tests (writeApiOptions). */
|
|
21
|
+
options?: LambderAppFile<LambderApiOptionsFileOptions>;
|
|
22
|
+
/** One file per guard whose parameters a browser needs (writeApiGuardParams). */
|
|
23
|
+
guardParams?: readonly LambderAppFile<LambderApiGuardParamsFileOptions>[];
|
|
24
|
+
};
|
|
25
|
+
/** The files generateApiFiles writes: every app's, by a name the output lines use. */
|
|
26
|
+
export type LambderApiFilesConfig = {
|
|
27
|
+
apps: Record<string, LambderApiFilesApp>;
|
|
28
|
+
};
|
|
29
|
+
export type LambderApiFilesResult = {
|
|
30
|
+
/** False when a check found a file stale, or a writer failed. */
|
|
31
|
+
ok: boolean;
|
|
32
|
+
/** What happened, per app and file, as lines to print. */
|
|
33
|
+
lines: string[];
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Writes every file the config names, or with `check` writes nothing and
|
|
37
|
+
* answers whether each one on disk is current. Paths are relative to the
|
|
38
|
+
* working directory, as each writer takes them.
|
|
39
|
+
*
|
|
40
|
+
* ```ts
|
|
41
|
+
* import { generateApiFiles } from "lambder/build";
|
|
42
|
+
*
|
|
43
|
+
* const result = await generateApiFiles({
|
|
44
|
+
* apps: {
|
|
45
|
+
* server: {
|
|
46
|
+
* module: "backend/index.ts",
|
|
47
|
+
* exportName: "lambder",
|
|
48
|
+
* contract: { file: "shared/generated/apiContract.generated.ts" },
|
|
49
|
+
* signatures: { file: "shared/generated/apiSignatures.generated.ts" },
|
|
50
|
+
* },
|
|
51
|
+
* },
|
|
52
|
+
* }, { check: process.argv.includes("--check") });
|
|
53
|
+
* console.log(result.lines.join("\n"));
|
|
54
|
+
* process.exit(result.ok ? 0 : 1);
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export declare const generateApiFiles: (config: LambderApiFilesConfig, options?: {
|
|
58
|
+
check?: boolean;
|
|
59
|
+
}) => Promise<LambderApiFilesResult>;
|
|
60
|
+
export {};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { writeApiContract } from "./writeApiContract.js";
|
|
2
|
+
import { writeApiSignatures } from "./writeApiSignatures.js";
|
|
3
|
+
import { writeApiOptions } from "./writeApiOptions.js";
|
|
4
|
+
import { writeApiGuardParams } from "./writeApiGuardParams.js";
|
|
5
|
+
const APP_KEYS = ["module", "exportName", "tsconfig", "contract", "signatures", "options", "guardParams"];
|
|
6
|
+
const FILE_KINDS = ["contract", "signatures", "options", "guardParams"];
|
|
7
|
+
/** A config that names apps, each with its module and at least one file, and no key the generator does not read. */
|
|
8
|
+
function assertConfig(config) {
|
|
9
|
+
const apps = config?.apps;
|
|
10
|
+
if (apps === null || typeof apps !== "object" || Array.isArray(apps) || Object.keys(apps).length === 0) {
|
|
11
|
+
throw new Error("generateApiFiles: the config names no apps. Give `apps`, one entry per Lambder instance: { module, exportName, and the files it is written to }.");
|
|
12
|
+
}
|
|
13
|
+
for (const [name, app] of Object.entries(apps)) {
|
|
14
|
+
if (app === null || typeof app !== "object")
|
|
15
|
+
throw new Error(`generateApiFiles: the app "${name}" is not an object.`);
|
|
16
|
+
const unknownKeys = Object.keys(app).filter((key) => !APP_KEYS.includes(key));
|
|
17
|
+
if (unknownKeys.length)
|
|
18
|
+
throw new Error(`generateApiFiles: the app "${name}" has ${unknownKeys.map((key) => `"${key}"`).join(", ")}, which the generator does not read (it reads ${APP_KEYS.join(", ")}).`);
|
|
19
|
+
if (!app.module)
|
|
20
|
+
throw new Error(`generateApiFiles: the app "${name}" names no module to read its instance from.`);
|
|
21
|
+
if (!FILE_KINDS.some((kind) => app[kind] !== undefined)) {
|
|
22
|
+
throw new Error(`generateApiFiles: the app "${name}" is written to no file; give it a contract, signatures, options or guardParams.`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/** A writer's run as lines: its own on success or a stale check, the reason when it threw. */
|
|
27
|
+
const settle = async (label, write) => {
|
|
28
|
+
try {
|
|
29
|
+
return await write();
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
const reasons = [];
|
|
33
|
+
for (let cause = err; cause instanceof Error && reasons.length < 4; cause = cause.cause)
|
|
34
|
+
reasons.push(cause.message);
|
|
35
|
+
return { ok: false, lines: [`✗ ${label} failed: ${reasons.join(": ") || String(err)}`] };
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Writes every file the config names, or with `check` writes nothing and
|
|
40
|
+
* answers whether each one on disk is current. Paths are relative to the
|
|
41
|
+
* working directory, as each writer takes them.
|
|
42
|
+
*
|
|
43
|
+
* ```ts
|
|
44
|
+
* import { generateApiFiles } from "lambder/build";
|
|
45
|
+
*
|
|
46
|
+
* const result = await generateApiFiles({
|
|
47
|
+
* apps: {
|
|
48
|
+
* server: {
|
|
49
|
+
* module: "backend/index.ts",
|
|
50
|
+
* exportName: "lambder",
|
|
51
|
+
* contract: { file: "shared/generated/apiContract.generated.ts" },
|
|
52
|
+
* signatures: { file: "shared/generated/apiSignatures.generated.ts" },
|
|
53
|
+
* },
|
|
54
|
+
* },
|
|
55
|
+
* }, { check: process.argv.includes("--check") });
|
|
56
|
+
* console.log(result.lines.join("\n"));
|
|
57
|
+
* process.exit(result.ok ? 0 : 1);
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export const generateApiFiles = async (config, options = {}) => {
|
|
61
|
+
assertConfig(config);
|
|
62
|
+
const check = options.check ?? false;
|
|
63
|
+
const apps = Object.entries(config.apps);
|
|
64
|
+
const runs = [];
|
|
65
|
+
// The contracts first: they compile the app's sources and run none of
|
|
66
|
+
// them, so a module that fails to load still gets its contract checked.
|
|
67
|
+
for (const [name, app] of apps) {
|
|
68
|
+
if (!app.contract)
|
|
69
|
+
continue;
|
|
70
|
+
runs.push(await settle(`${name}: the contract`, () => writeApiContract({ ...app.contract, module: app.module, exportName: app.exportName, tsconfig: app.tsconfig, check })));
|
|
71
|
+
}
|
|
72
|
+
// Then what is read off the instance itself, which importing the module
|
|
73
|
+
// builds once for all three.
|
|
74
|
+
for (const [name, app] of apps) {
|
|
75
|
+
const instance = { module: app.module, exportName: app.exportName, check };
|
|
76
|
+
if (app.signatures)
|
|
77
|
+
runs.push(await settle(`${name}: the signatures`, () => writeApiSignatures({ ...app.signatures, ...instance })));
|
|
78
|
+
if (app.options)
|
|
79
|
+
runs.push(await settle(`${name}: the options`, () => writeApiOptions({ ...app.options, ...instance })));
|
|
80
|
+
for (const guardFile of app.guardParams ?? []) {
|
|
81
|
+
runs.push(await settle(`${name}: the "${guardFile.guard}" guard parameters`, () => writeApiGuardParams({ ...guardFile, ...instance })));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return { ok: runs.every((run) => run.ok), lines: runs.flatMap((run) => run.lines) };
|
|
85
|
+
};
|
|
@@ -2,7 +2,8 @@ import type { LambderApiOptionEntries } from "../shared/wire/LambderApiOptionEnt
|
|
|
2
2
|
import { type LambderModuleLocation } from "./moduleLocation.js";
|
|
3
3
|
/** What the generators read the options from: a Lambder instance, or anything else that reports them the same way. */
|
|
4
4
|
export type LambderApiOptionsSource = {
|
|
5
|
-
|
|
5
|
+
/** Asynchronous, since a Lambder instance loads its lazy groups before it can report every endpoint. */
|
|
6
|
+
apiOptionEntries(): Promise<LambderApiOptionEntries> | LambderApiOptionEntries;
|
|
6
7
|
};
|
|
7
8
|
/** Which names of one table moved: entries that changed, entries the file did not have, entries the file had and the instance no longer reports. */
|
|
8
9
|
export type LambderNameChanges = {
|
|
@@ -16,7 +16,7 @@ export const loadApiOptionEntries = async (location, generator) => {
|
|
|
16
16
|
if (typeof source?.apiOptionEntries !== "function") {
|
|
17
17
|
throw new Error(`${moduleUrl} has no export "${exportName}" that reports API options: name the export holding the instance in exportName`);
|
|
18
18
|
}
|
|
19
|
-
return source.apiOptionEntries();
|
|
19
|
+
return await source.apiOptionEntries();
|
|
20
20
|
};
|
|
21
21
|
/** One table of a generated file, read back as data, or null for a file that does not hold it as written. */
|
|
22
22
|
export const readGeneratedTable = (contents, exportName) => {
|
package/dist/build.d.ts
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* ships: the signature file both sides read, the declared options as plain
|
|
6
6
|
* data, and one guard's parameters as the least a browser needs of them, all
|
|
7
7
|
* from the app's own instance; and the contract a client compiles against,
|
|
8
|
-
* from the server's sources
|
|
8
|
+
* from the server's sources; and generateApiFiles, every one of them for
|
|
9
|
+
* every app a script names, in one call. Node-only and imported by nothing else
|
|
9
10
|
* in the package, so no deployment or bundle carries it.
|
|
10
11
|
*/
|
|
11
12
|
export { writeApiSignatures } from "./build/writeApiSignatures.js";
|
|
@@ -17,4 +18,6 @@ export type { LambderApiOptionsFileOptions, LambderApiOptionsFileResult, } from
|
|
|
17
18
|
export { writeApiGuardParams } from "./build/writeApiGuardParams.js";
|
|
18
19
|
export type { LambderApiGuardParamsFileOptions, LambderApiGuardParamsFileResult, } from "./build/writeApiGuardParams.js";
|
|
19
20
|
export type { LambderApiOptionsSource, LambderNameChanges } from "./build/generatedTables.js";
|
|
21
|
+
export { generateApiFiles } from "./build/generateApiFiles.js";
|
|
22
|
+
export type { LambderApiFilesConfig, LambderApiFilesApp, LambderApiFilesResult } from "./build/generateApiFiles.js";
|
|
20
23
|
export type { LambderModuleLocation } from "./build/moduleLocation.js";
|
package/dist/build.js
CHANGED
|
@@ -5,10 +5,12 @@
|
|
|
5
5
|
* ships: the signature file both sides read, the declared options as plain
|
|
6
6
|
* data, and one guard's parameters as the least a browser needs of them, all
|
|
7
7
|
* from the app's own instance; and the contract a client compiles against,
|
|
8
|
-
* from the server's sources
|
|
8
|
+
* from the server's sources; and generateApiFiles, every one of them for
|
|
9
|
+
* every app a script names, in one call. Node-only and imported by nothing else
|
|
9
10
|
* in the package, so no deployment or bundle carries it.
|
|
10
11
|
*/
|
|
11
12
|
export { writeApiSignatures } from "./build/writeApiSignatures.js";
|
|
12
13
|
export { writeApiContract } from "./build/writeApiContract.js";
|
|
13
14
|
export { writeApiOptions } from "./build/writeApiOptions.js";
|
|
14
15
|
export { writeApiGuardParams } from "./build/writeApiGuardParams.js";
|
|
16
|
+
export { generateApiFiles } from "./build/generateApiFiles.js";
|
|
@@ -5,6 +5,7 @@ import { type LambderApiOutcome, type LambderValidationError } from '../shared/w
|
|
|
5
5
|
import { type LambderCallArgs, type LambderContractOutputOf, type LambderGuardInputsProviderOption, type LambderSharedCallOptions } from '../shared/wire/LambderCallOptions.js';
|
|
6
6
|
import { type LambderApiTransport } from '../shared/transport/LambderApiTransport.js';
|
|
7
7
|
import { type LambderApiSignatureMap } from '../shared/wire/LambderApiSignatureMap.js';
|
|
8
|
+
import { type LambderContractActionOf, type LambderContractGroupsOf, type LambderContractNamesInGroup } from '../shared/wire/LambderApiGroupCalls.js';
|
|
8
9
|
export type { LambderApiOutcome, LambderApiFailureReason, LambderValidationError } from '../shared/wire/LambderApiOutcome.js';
|
|
9
10
|
export type { LambderProvidedGuardInputs, LambderGuardInputsProvider } from '../shared/wire/LambderCallOptions.js';
|
|
10
11
|
/** A handler told that something happened, with nothing to hand it. */
|
|
@@ -110,40 +111,23 @@ type LambderCallerBaseOptions<TMessage extends LambderUncheckedRefusalMessage =
|
|
|
110
111
|
/** Constructor options: the base options plus guardInputsProvider, mandatory once TProvided names guards. */
|
|
111
112
|
export type LambderCallerOptions<TContract, TProvided extends string = never> = LambderCallerBaseOptions<LambderContractAnyRefusalMessage<TContract>> & LambderGuardInputsProviderOption<TContract, TProvided>;
|
|
112
113
|
/**
|
|
114
|
+
* The caller itself, before the groups: what LambderCaller is, less the
|
|
115
|
+
* endpoints it reaches by group (LambderCallerGroupCalls), which the
|
|
116
|
+
* constructor adds.
|
|
117
|
+
*
|
|
113
118
|
* @typeParam TContract - The API contract, for typed names, payloads and guard inputs.
|
|
114
119
|
* @typeParam TProvidedGuards - Guard names guardInputsProvider covers; those APIs' options argument becomes optional.
|
|
115
120
|
*/
|
|
116
|
-
|
|
117
|
-
private
|
|
118
|
-
private apiVersion?;
|
|
119
|
-
private apiSignatures?;
|
|
120
|
-
private timeoutMs?;
|
|
121
|
+
declare class LambderCallerCore<TContract extends LambderApiContractShape = any, TProvidedGuards extends string = never> {
|
|
122
|
+
#private;
|
|
121
123
|
/** The calls currently in flight, in the order they started. */
|
|
122
124
|
fetchTrackerList: FetchTracker[];
|
|
123
125
|
/** Whether any call is in flight. Derived from the list, so the two cannot drift apart. */
|
|
124
126
|
get isLoading(): boolean;
|
|
125
|
-
private versionExpiredHandler?;
|
|
126
|
-
private sessionExpiredHandler?;
|
|
127
|
-
private refusalHandler?;
|
|
128
|
-
private notAuthorizedHandler?;
|
|
129
|
-
private errorHandler?;
|
|
130
|
-
private apiInputValidationErrorHandler?;
|
|
131
|
-
private logListHandler?;
|
|
132
|
-
private fetchStartedHandler?;
|
|
133
|
-
private fetchEndedHandler?;
|
|
134
|
-
private guardInputsProvider?;
|
|
135
|
-
private sessionTokenCookieKey;
|
|
136
|
-
private sessionCsrfCookieKey;
|
|
137
|
-
private sessionCookieDomain?;
|
|
138
|
-
private requestCompression;
|
|
139
|
-
private transport;
|
|
140
127
|
constructor(options: LambderCallerOptions<TContract, TProvidedGuards>);
|
|
141
128
|
setSessionCookieKey(sessionTokenCookieKey: string, sessionCsrfCookieKey: string): void;
|
|
142
129
|
/** Replaces how calls reach the server: a mock runtime, an in-process handler, a decorated transport. */
|
|
143
130
|
setTransport(transport: LambderApiTransport): this;
|
|
144
|
-
private clearSessionCookies;
|
|
145
|
-
/** One call, one outcome. Never throws; every failure path resolves to { ok: false }. */
|
|
146
|
-
private dispatch;
|
|
147
131
|
/**
|
|
148
132
|
* Full-fidelity call: resolves to a discriminated LambderApiOutcome
|
|
149
133
|
* instead of collapsing every failure to undefined. Never throws. A
|
|
@@ -164,3 +148,32 @@ export default class LambderCaller<TContract extends LambderApiContractShape = a
|
|
|
164
148
|
*/
|
|
165
149
|
api<TApiName extends keyof TContract & string = string>(apiName: TApiName, ...rest: LambderCallArgs<TContract, TApiName, TProvidedGuards, LambderCallOptions<LambderContractRefusalMessage<TContract, TApiName>>>): Promise<LambderContractOutputOf<TContract, TApiName> | undefined>;
|
|
166
150
|
}
|
|
151
|
+
/**
|
|
152
|
+
* One endpoint as a caller hands it out on its group: called, it is `api`
|
|
153
|
+
* for that endpoint (the output, or undefined on a failure); `.outcome` is
|
|
154
|
+
* `apiOutcome` (the full outcome, never throwing).
|
|
155
|
+
*/
|
|
156
|
+
export type LambderCallerEndpoint<TContract, TName extends keyof TContract & string, TProvidedGuards extends string> = {
|
|
157
|
+
(...args: LambderCallArgs<TContract, TName, TProvidedGuards, LambderCallOptions<LambderContractRefusalMessage<TContract, TName>>>): Promise<LambderContractOutputOf<TContract, TName> | undefined>;
|
|
158
|
+
outcome(...args: LambderCallArgs<TContract, TName, TProvidedGuards, LambderCallOptions<LambderContractRefusalMessage<TContract, TName>>>): Promise<LambderApiOutcome<LambderContractOutputOf<TContract, TName>, LambderContractRefusalMessage<TContract, TName>>>;
|
|
159
|
+
};
|
|
160
|
+
/** Every endpoint of a contract, by group: `caller.orders.place(input)`. */
|
|
161
|
+
export type LambderCallerGroupCalls<TContract, TProvidedGuards extends string> = {
|
|
162
|
+
readonly [TGroup in LambderContractGroupsOf<TContract>]: {
|
|
163
|
+
readonly [TName in LambderContractNamesInGroup<TContract, TGroup> as LambderContractActionOf<TName>]: LambderCallerEndpoint<TContract, TName, TProvidedGuards>;
|
|
164
|
+
};
|
|
165
|
+
};
|
|
166
|
+
/**
|
|
167
|
+
* A typed client of a Lambder app: `caller.orders.place(input)` for the
|
|
168
|
+
* endpoint `orders.place`, `caller.orders.place.outcome(input)` for its full
|
|
169
|
+
* outcome, and `caller.api("orders.place", input)` for code that has the
|
|
170
|
+
* name as a value.
|
|
171
|
+
*/
|
|
172
|
+
type LambderCaller<TContract extends LambderApiContractShape = any, TProvidedGuards extends string = never> = LambderCallerCore<TContract, TProvidedGuards> & LambderCallerGroupCalls<TContract, TProvidedGuards>;
|
|
173
|
+
/** The caller's own members, without the groups: what a wrapper of a caller (the test visitor) types its `api` and `apiOutcome` by. */
|
|
174
|
+
export type LambderCallerMembers<TContract extends LambderApiContractShape = any, TProvidedGuards extends string = never> = LambderCallerCore<TContract, TProvidedGuards>;
|
|
175
|
+
declare const LambderCaller: {
|
|
176
|
+
new <TContract extends LambderApiContractShape = any, TProvidedGuards extends string = never>(options: LambderCallerOptions<TContract, TProvidedGuards>): LambderCaller<TContract, TProvidedGuards>;
|
|
177
|
+
readonly prototype: LambderCallerCore<any, any>;
|
|
178
|
+
};
|
|
179
|
+
export default LambderCaller;
|