lambder 15.1.1 → 16.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.
@@ -19,15 +19,15 @@ import { type LambderRefusalVocabulary } from "../api/LambderApiRefusals.js";
19
19
  import type { LambderNoExtraKeys } from "../shared/util/LambderTypeUtilities.js";
20
20
  import type { LambderGuardNamesIn, LambderJsonOf } from "../shared/wire/LambderApiContract.js";
21
21
  import type { LambderRateLimitMessage } from "../shared/wire/LambderApiOptionValues.js";
22
- import type { MaybePromise } from "../shared/util/LambderTypeUtilities.js";
23
- export type LambderRouteHandler = (ctx: LambderRenderContext, resolver: LambderResolver) => MaybePromise<LambderResponse>;
24
- export type LambderSessionRouteHandler<SessionData = any> = (ctx: LambderSessionRenderContext<any, SessionData>, resolver: LambderResolver) => MaybePromise<LambderResponse>;
22
+ import type { LambderMaybePromise } from "../shared/util/LambderTypeUtilities.js";
23
+ export type LambderRouteHandler = (ctx: LambderRenderContext, resolver: LambderResolver) => LambderMaybePromise<LambderResponse>;
24
+ export type LambderSessionRouteHandler<SessionData = any> = (ctx: LambderSessionRenderContext<any, SessionData>, resolver: LambderResolver) => LambderMaybePromise<LambderResponse>;
25
25
  export type LambderHookEvent = "created" | "beforeRender" | "afterRender" | "fallback";
26
26
  /** Return the (possibly replaced) ctx to continue, a LambderResponse to short-circuit, or an Error to fail. */
27
- export type LambderBeforeRenderHook = (ctx: LambderRenderContext, resolver: LambderResolver) => MaybePromise<LambderRenderContext | LambderResponse | Error>;
28
- export type LambderAfterRenderHook = (ctx: LambderRenderContext, resolver: LambderResolver, response: LambderResponse) => MaybePromise<LambderResponse | Error>;
27
+ export type LambderBeforeRenderHook = (ctx: LambderRenderContext, resolver: LambderResolver) => LambderMaybePromise<LambderRenderContext | LambderResponse | Error>;
28
+ export type LambderAfterRenderHook = (ctx: LambderRenderContext, resolver: LambderResolver, response: LambderResponse) => LambderMaybePromise<LambderResponse | Error>;
29
29
  export type LambderFallbackHook = (ctx: LambderRenderContext, resolver: LambderResolver) => void | Promise<void>;
30
- export type LambderGlobalErrorHandler = (err: Error, ctx: LambderRenderContext | null, response: LambderResponseBuilder) => MaybePromise<LambderResponse>;
30
+ export type LambderGlobalErrorHandler = (err: Error, ctx: LambderRenderContext | null, response: LambderResponseBuilder) => LambderMaybePromise<LambderResponse>;
31
31
  /**
32
32
  * Where a crash happened.
33
33
  *
@@ -68,7 +68,7 @@ export type LambderCrashSite = {
68
68
  * password in the body, the session record): forward the fields a crash
69
69
  * needs rather than the whole context to an error tracker.
70
70
  */
71
- export type LambderCrashReporter = (error: Error, site: LambderCrashSite) => MaybePromise<void>;
71
+ export type LambderCrashReporter = (error: Error, site: LambderCrashSite) => LambderMaybePromise<void>;
72
72
  /** The `crashes` option of create(). */
73
73
  export type LambderCrashOptions = {
74
74
  /** Told every crash on every path; see LambderCrashReporter. Default: none, and the framework's own 500 logs the crash to the console instead. */
@@ -88,10 +88,10 @@ export type LambderCrashOptions = {
88
88
  * writes its own answer, and describeCrash is there for it. A reveal
89
89
  * that throws counts as no. Default: nobody.
90
90
  */
91
- reveal?: (ctx: LambderRenderContext) => MaybePromise<boolean>;
91
+ reveal?: (ctx: LambderRenderContext) => LambderMaybePromise<boolean>;
92
92
  };
93
- export type LambderFallbackHandler = (ctx: LambderRenderContext, resolver: LambderResolver) => MaybePromise<LambderResponse>;
94
- export type LambderInputValidationHandler = (ctx: LambderRenderContext, resolver: LambderResolver, zodError: z.ZodError) => MaybePromise<LambderResponse>;
93
+ export type LambderFallbackHandler = (ctx: LambderRenderContext, resolver: LambderResolver) => LambderMaybePromise<LambderResponse>;
94
+ export type LambderInputValidationHandler = (ctx: LambderRenderContext, resolver: LambderResolver, zodError: z.ZodError) => LambderMaybePromise<LambderResponse>;
95
95
  /**
96
96
  * Second argument of an addAction handler. Discriminated on `ctx`: HTTP
97
97
  * invocations get the full context and a resolver, non-HTTP invocations get
@@ -107,7 +107,7 @@ export type LambderActionTools = {
107
107
  lambdaContext: Context;
108
108
  };
109
109
  export type LambderActionFilter = (event: unknown, ctx: LambderRenderContext | null) => boolean;
110
- export type LambderActionHandler<TEvent = unknown> = (event: TEvent, tools: LambderActionTools) => MaybePromise<unknown>;
110
+ export type LambderActionHandler<TEvent = unknown> = (event: TEvent, tools: LambderActionTools) => LambderMaybePromise<unknown>;
111
111
  /** Overloaded handler type returned by getHandler(): HTTP events get a typed response, others dispatch to actions. */
112
112
  export type LambderHandler = {
113
113
  (event: LambderHttpEvent, context: Context): Promise<LambderHttpResponse>;
@@ -170,8 +170,12 @@ export type LambderGuardsMap<TSessionData = any> = Record<string, LambderApiGuar
170
170
  export type LambderRateLimitPoliciesMap = Record<string, LambderApiRateLimitPolicyConfig<LambderRenderContext>>;
171
171
  /** The `rateLimits` option of create(): the engine's config, with `policies` as one map or a list of maps. */
172
172
  export type LambderCreateRateLimitsOption = Omit<LambderApiRateLimitsConfig<LambderRateLimitPoliciesMap>, "policies"> & {
173
- /** Named policies referenced (typed) from defineApi: one map, or a list of maps merged in order. */
174
- policies: LambderNamedMapsOption<LambderRateLimitPoliciesMap>;
173
+ /**
174
+ * Named policies referenced (typed) from defineApi: one map, or a list of
175
+ * maps merged in order. Left out where every policy is declared by the
176
+ * app's modules (defineApiModule), which count on this limiter.
177
+ */
178
+ policies?: LambderNamedMapsOption<LambderRateLimitPoliciesMap>;
175
179
  };
176
180
  /** The options as the instance holds them: the guards and the rate-limit policies merged into one map each. */
177
181
  export type LambderMergedCreateOptions<TSessionData = any> = Omit<LambderCreateOptions<TSessionData>, "guards" | "rateLimits"> & {
@@ -475,7 +479,7 @@ type LambderGuardRefusalsKnown<TGuard, TRefusals> = TGuard extends {
475
479
  refusals: readonly (keyof NonNullable<TRefusals> & string)[];
476
480
  } : unknown : unknown;
477
481
  /** Every policy in a map, or in each map of a list, held to the policy shape, its message included; a list also declares no name twice. */
478
- type LambderRateLimitPolicyChecks<TPolicies> = TPolicies extends readonly unknown[] ? {
482
+ export type LambderRateLimitPolicyChecks<TPolicies> = TPolicies extends readonly unknown[] ? {
479
483
  [TIndex in keyof TPolicies]: LambderRateLimitPolicyChecks<TPolicies[TIndex]>;
480
484
  } & LambderNoRepeatedNames<TPolicies> : {
481
485
  [TPolicy in keyof TPolicies]: LambderNoExtraKeys<TPolicies[TPolicy], LambderApiRateLimitPolicyConfig<LambderRenderContext>> & (TPolicies[TPolicy] extends {
@@ -485,7 +489,7 @@ type LambderRateLimitPolicyChecks<TPolicies> = TPolicies extends readonly unknow
485
489
  } : unknown);
486
490
  };
487
491
  /** Every guard in a map, or in each map of a list, held to the guard shape and its refusals to the vocabulary; a list also declares no name twice. */
488
- type LambderGuardChecks<TSessionData, TGuards, TRefusals> = TGuards extends readonly unknown[] ? {
492
+ export type LambderGuardChecks<TSessionData, TGuards, TRefusals> = TGuards extends readonly unknown[] ? {
489
493
  [TIndex in keyof TGuards]: LambderGuardChecks<TSessionData, TGuards[TIndex], TRefusals>;
490
494
  } & LambderNoRepeatedNames<TGuards> : {
491
495
  [TGuard in keyof TGuards]: LambderNoExtraKeys<TGuards[TGuard], LambderGuardsMap<TSessionData>[string]> & LambderGuardRefusalsKnown<TGuards[TGuard], TRefusals>;
@@ -510,7 +514,7 @@ export type LambderNestedOptionChecks<TSessionData, TOptions extends LambderCrea
510
514
  crashes?: LambderNoExtraKeys<NonNullable<TOptions["crashes"]>, LambderOptionShape<TSessionData, "crashes">>;
511
515
  originProof?: LambderNoExtraKeys<NonNullable<TOptions["originProof"]>, LambderOptionShape<TSessionData, "originProof">>;
512
516
  rateLimits?: LambderNoExtraKeys<NonNullable<TOptions["rateLimits"]>, LambderOptionShape<TSessionData, "rateLimits">> & {
513
- policies?: LambderRateLimitPolicyChecks<NonNullable<TOptions["rateLimits"]>["policies"]>;
517
+ policies?: LambderRateLimitPolicyChecks<NonNullable<NonNullable<TOptions["rateLimits"]>["policies"]>>;
514
518
  };
515
519
  guards?: LambderGuardChecks<TSessionData, NonNullable<TOptions["guards"]>, TRefusals>;
516
520
  files?: TOptions["files"] extends {
package/dist/index.d.ts CHANGED
@@ -1,8 +1,12 @@
1
1
  import Lambder from './core/Lambder.js';
2
2
  export default Lambder;
3
3
  export { initLambder } from './core/Lambder.js';
4
- export type { LambderCreatedHook } from './core/Lambder.js';
5
- export type { LambderAppTypes, LambderPlainAppTypes, LambderApiDeclaration, LambderApiDeclarations, LambderApiGroup, LambderLazyApiGroup, LambderRegistrableApiGroup, LambderApiModeOf, LambderEntryOf, LambderContractOfGroups, } from './api/LambderApiDeclarations.js';
4
+ export type { LambderCreatedHook, LambderInitCreateOptions } from './core/Lambder.js';
5
+ export type { LambderDefineApi, LambderDefineApiGroup, LambderDefineLazyApiGroup, LambderApiHandlerContext, } from './core/LambderApiBuilders.js';
6
+ export { LambderApiModule } from './core/LambderApiModule.js';
7
+ export type { LambderApiModuleOptions, LambderApiModuleOptionChecks, LambderModuleAppTypes } from './core/LambderApiModule.js';
8
+ export type { LambderApiModuleDeclarations } from './api/LambderApiModules.js';
9
+ export type { LambderAppTypes, LambderPlainAppTypes, LambderApiDeclaration, LambderApiDeclarations, LambderApiGroup, LambderLazyApiGroup, LambderRegistrableApiGroup, LambderApiModeOf, LambderEntryOf, LambderContractOfGroups, LambderRegisteredGroupsCheck, LambderActionNamesCheck, LambderGroupNameCheck, LambderGroupPartsCheck, LambderMergedParts, LambderPublicSessionPolicyCheck, LambderSessionModeCheck, } from './api/LambderApiDeclarations.js';
6
10
  export { apiCallPath, apiNameOfCallPath, splitApiName, isGroupName, isActionName, LAMBDER_API_NAME_SEGMENT, LAMBDER_CALLER_MEMBER_NAMES, LAMBDER_RESERVED_GROUP_NAMES, LAMBDER_RESERVED_ACTION_NAMES, } from './shared/wire/LambderApiNames.js';
7
11
  export type { LambderReservedGroupName, LambderReservedActionName } from './shared/wire/LambderApiNames.js';
8
12
  export type { LambderContractGroupsOf, LambderContractNamesInGroup, LambderContractActionOf } from './shared/wire/LambderApiGroupCalls.js';
@@ -77,7 +81,7 @@ export type { LambderTemplateData, LambderTemplatingEngineOptions } from "./core
77
81
  export type { LambderResponseOptions, LambderRawResponseInit, LambderTemplateFileOptions, } from "./core/LambderResponseBuilder.js";
78
82
  export type { LambderRouteMatcher, LambderRouteConditionFn, LambderRouteCondition, LambderPathParamsOf, LambderRoutePath } from "./core/LambderRouting.js";
79
83
  export type { LambderCorsConfig } from "./core/LambderCors.js";
80
- export type { LambderCreateOptions, LambderSessionOptions, LambderActionTools, LambderHandler, } from "./core/LambderCreateOptions.js";
84
+ export type { LambderCreateOptions, LambderSessionOptions, LambderActionTools, LambderHandler, LambderGivenOption, LambderNamedMapsOption, LambderMergedNamedMaps, LambderNestedOptionChecks, LambderObjectOutputCheck, LambderPayloadSliceCheck, LambderRequirableGuardsField, LambderSessionEnabledInstance, } from "./core/LambderCreateOptions.js";
81
85
  export type { LambderIndexHtmlOptions } from "./core/LambderIndexHtml.js";
82
86
  export type { LambderRouteHandler, LambderSessionRouteHandler, LambderActionHandler, LambderActionFilter, LambderHookEvent, LambderBeforeRenderHook, LambderAfterRenderHook, LambderFallbackHook, LambderGlobalErrorHandler, LambderFallbackHandler, LambderInputValidationHandler, LambderCrashOptions, LambderCrashReporter, LambderCrashSite, } from "./core/LambderCreateOptions.js";
83
87
  export type { LambderPublicFilesOptions } from "./core/LambderPublicFiles.js";
@@ -136,10 +140,11 @@ export { lambderGuard, lambderRateLimitKey } from "./core/LambderPolicyBuilders.
136
140
  export { lambderGuardBuilder } from "./api/LambderApiGuards.js";
137
141
  export type { LambderGuardBuilder, LambderApiGuard, LambderGuardMeta, LambderGuardMetaMap, LambderGuardRunAt, LambderAllowedGuardNames, LambderGuardNamesInputLacks, LambderParamlessGuardNames, LambderGuardsOption, LambderGuardDataOf, LambderGuardInputsOf, LambderGuardRefusals, LambderGuardRefusalNamesOf, } from "./api/LambderApiGuards.js";
138
142
  export { LambderApiRefusalValidationError } from "./api/LambderApiRefusals.js";
139
- export type { LambderRefusalDeclaration, LambderRefusalVocabulary, LambderRefusalVocabularyOption, LambderMergedRefusalVocabulary, LambderApiAllowedRefusal, LambderApiAllowedRefusals, LambderEndpointRefusals, LambderRefusalsOption, LambderRefusalNamesIn, LambderHandlerRefusalsOf, LambderWireRefusalsOf, } from "./api/LambderApiRefusals.js";
143
+ export type { LambderRefusalDeclaration, LambderRefusalVocabulary, LambderRefusalVocabularyOption, LambderRefusalVocabularyOptionChecks, LambderMergedRefusalVocabulary, LambderApiAllowedRefusal, LambderApiAllowedRefusals, LambderEndpointRefusals, LambderRefusalsOption, LambderRefusalNamesIn, LambderHandlerRefusalsOf, LambderWireRefusalsOf, } from "./api/LambderApiRefusals.js";
140
144
  export { lambderRateLimitKeyBuilder, rateLimitRefusal, DEFAULT_RATE_LIMIT_REFUSAL } from "./api/LambderApiRateLimits.js";
141
145
  export type { LambderRateLimitKeyBuilder } from "./api/LambderApiRateLimits.js";
142
- export type { LambderContextRateLimit, LambderContextRateLimitCheck, LambderRateLimitCheckResult, LambderChargeablePolicyNames, LambderChargeKeyArgs, LambderRateLimitKeyFn, LambderRateLimitPer, LambderRateLimitBudget, LambderRateLimitChargeAt, LambderApiRateLimitPolicyConfig, LambderApiRateLimitsConfig, LambderAllowedPolicyNames, LambderPolicyNamesInputLacks, LambderRateLimitOption, } from "./api/LambderApiRateLimits.js";
146
+ export type { LambderContextRateLimit, LambderContextRateLimitCheck, LambderRateLimitCheckResult, LambderChargeablePolicyNames, LambderChargeKeyArgs, LambderRateLimitKeyFn, LambderRateLimitPer, LambderRateLimitBudget, LambderRateLimitChargeAt, LambderApiRateLimitPolicyConfig, LambderApiRateLimitsConfig, LambderAllowedPolicyNames, LambderPolicyNamesInputLacks, LambderRateLimitOption, LambderPolicyPerOf, } from "./api/LambderApiRateLimits.js";
147
+ export type { LambderMaybePromise, LambderNoExtraKeys } from "./shared/util/LambderTypeUtilities.js";
143
148
  export type { LambderApiIdempotencyConfig } from "./api/LambderApiIdempotency.js";
144
149
  export type { LambderGuardsOptionValue, LambderRateLimitMessage, LambderRateLimitOverride, LambderRateLimitOptionValue, LambderRefusalsOptionValue, LambderApiIdempotencyOption, } from "./shared/wire/LambderApiOptionValues.js";
145
150
  export { apiGuardParam, apiGuardParamExportName, apisWithGuard } from "./shared/wire/LambderApiOptionEntries.js";
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import Lambder from './core/Lambder.js';
2
2
  export default Lambder;
3
3
  export { initLambder } from './core/Lambder.js';
4
+ // Modules: guards and rate-limit policies a part of an app declares for its own endpoints.
5
+ export { LambderApiModule } from './core/LambderApiModule.js';
4
6
  // How an endpoint is named, and where a call to it goes.
5
7
  export { apiCallPath, apiNameOfCallPath, splitApiName, isGroupName, isActionName, LAMBDER_API_NAME_SEGMENT, LAMBDER_CALLER_MEMBER_NAMES, LAMBDER_RESERVED_GROUP_NAMES, LAMBDER_RESERVED_ACTION_NAMES, } from './shared/wire/LambderApiNames.js';
6
8
  export { default as LambderCaller } from "./client/LambderCaller.js";
@@ -0,0 +1,70 @@
1
+ import type { z } from "zod";
2
+ import type { LambderApiContractShape } from "../shared/wire/LambderApiContract.js";
3
+ import type { LambderApiGuard } from "../api/LambderApiGuards.js";
4
+ import type { LambderRefusalVocabulary } from "../api/LambderApiRefusals.js";
5
+ import type { LambderMockGuardsAgree, LambderMockGuardShapes } from "./LambderMockCreateOptions.js";
6
+ import type { LambderMockCallContext, LambderMockEntry, LambderMockEntryGuardsCheck, LambderMockEntryInput, LambderMockGuardMapChecks, LambderMockHandler, LambderMockNotMockedGuardsCheck, LambderMockNotMockedInput, LambderMockOverride, LambderMockSessionCallContext, LambderMockSlice } from "./LambderMockTypes.js";
7
+ /**
8
+ * What a mock app's defineApiModule() takes: the mock guards a part of the
9
+ * app declares for its own endpoints, standing in for the guards the
10
+ * server's module declares (its defineApiModule), as create()'s guards stand
11
+ * in for the server instance's. One map.
12
+ *
13
+ * No rate-limit policies: a mock takes every policy at create(), from the
14
+ * generated table the server writes (lambderMockPoliciesFrom), the module's
15
+ * among them, so none is restated by hand here.
16
+ */
17
+ export type LambderMockApiModuleOptions<S = any> = {
18
+ /** The module's mock guards, one per guard the server's module declares, built with the mock init's guard(). */
19
+ guards: Record<string, LambderApiGuard<any, any, any, LambderMockCallContext<S>, LambderMockSessionCallContext<S>>>;
20
+ };
21
+ /** A module's map held to declaring names of its own: one the mock app's create() declares is a compile error, and the property name is the message. */
22
+ type LambderMockModuleNamesCheck<MG, TAppNames> = [
23
+ Extract<keyof MG, TAppNames>
24
+ ] extends [never] ? unknown : {
25
+ readonly "lambder: the mock app declares these guards already; a module declares guards of its own": Extract<keyof MG, TAppNames>;
26
+ };
27
+ /**
28
+ * A module's mock guards held to what create()'s are held to: the contract,
29
+ * the generated guard declarations when the app was given them, surplus
30
+ * keys, and names of their own.
31
+ */
32
+ export type LambderMockApiModuleChecks<C, S, G, D, TOptions extends LambderMockApiModuleOptions<S>> = {
33
+ guards: LambderMockGuardMapChecks<C, S, TOptions["guards"]> & LambderMockGuardShapes<S, TOptions["guards"]> & LambderMockGuardsAgree<TOptions["guards"], D> & LambderMockModuleNamesCheck<TOptions["guards"], keyof G>;
34
+ };
35
+ /**
36
+ * What a mock module asks of the app that made it: the app's own entry
37
+ * builders, given the module's declarations. The constructor's parameter,
38
+ * which the app's defineApiModule() passes; an app does not build one.
39
+ */
40
+ export type LambderMockApiModuleDoor = {
41
+ api: (name: string, entry: unknown) => LambderMockEntry<any, any>;
42
+ notMocked: (name: string, input: unknown) => LambderMockEntry<any, any>;
43
+ apiSlice: (...entries: readonly LambderMockEntry<any, any>[]) => unknown;
44
+ override: (name: string, handler: unknown) => LambderMockOverride;
45
+ };
46
+ /**
47
+ * A part of a mock app with mock guards of its own: the app's entry builders,
48
+ * typed with the module's guards beside the app's. Made by the mock app's
49
+ * defineApiModule(); its entries are registered as any are (register,
50
+ * registerPartial), and registering them adds the module's guards to the
51
+ * app, refusing a name the app or another module declares. The mock twin of
52
+ * the server's LambderApiModule, so a module's mock guards are declared with
53
+ * the module's mocks, and the app's own setup imports none of them.
54
+ *
55
+ * `G` is the app's guards and the module's together, which is what the
56
+ * entries' handlers read guardData by.
57
+ */
58
+ export declare class LambderMockApiModule<C extends LambderApiContractShape, S = any, G extends Record<string, LambderApiGuard<any, any, any>> = {}, TDerived extends boolean = false, TVocabulary extends LambderRefusalVocabulary = never, TCodesRequired extends boolean = false> {
59
+ private readonly door;
60
+ constructor(door: LambderMockApiModuleDoor);
61
+ /** A mock for one of the module's endpoints, as the app's api() builds one: its guards may be the app's and the module's own. */
62
+ api<K extends keyof C & string, TInputSchema extends z.ZodType = z.ZodType>(name: K & LambderMockEntryGuardsCheck<C, K, G, TDerived>, entry: LambderMockEntryInput<C, K, S, G, TInputSchema, TDerived, TVocabulary, TCodesRequired>): LambderMockEntry<C, K>;
63
+ /** One of the module's endpoints deliberately left without a mock, as the app's notMocked() builds one; its restated guards may name the module's. */
64
+ notMocked<K extends keyof C & string>(name: K & LambderMockNotMockedGuardsCheck<C, K, G, TDerived>, input: LambderMockNotMockedInput<C, K, TDerived>): LambderMockEntry<C, K>;
65
+ /** The entries of one slice, keyed by name, as the app's apiSlice() gathers them. */
66
+ apiSlice<const E extends readonly LambderMockEntry<C, keyof C & string>[]>(...entries: E): LambderMockSlice<C, E[number]["name"]>;
67
+ /** Replaces one of the module's registered handlers until restored, as the app's override() does, its guardData typed with the module's guards. */
68
+ override<K extends keyof C & string>(name: K, handler: LambderMockHandler<C, K, S, G, TVocabulary, TCodesRequired>): LambderMockOverride;
69
+ }
70
+ export {};
@@ -0,0 +1,34 @@
1
+ /**
2
+ * A part of a mock app with mock guards of its own: the app's entry builders,
3
+ * typed with the module's guards beside the app's. Made by the mock app's
4
+ * defineApiModule(); its entries are registered as any are (register,
5
+ * registerPartial), and registering them adds the module's guards to the
6
+ * app, refusing a name the app or another module declares. The mock twin of
7
+ * the server's LambderApiModule, so a module's mock guards are declared with
8
+ * the module's mocks, and the app's own setup imports none of them.
9
+ *
10
+ * `G` is the app's guards and the module's together, which is what the
11
+ * entries' handlers read guardData by.
12
+ */
13
+ export class LambderMockApiModule {
14
+ door;
15
+ constructor(door) {
16
+ this.door = door;
17
+ }
18
+ /** A mock for one of the module's endpoints, as the app's api() builds one: its guards may be the app's and the module's own. */
19
+ api(name, entry) {
20
+ return this.door.api(name, entry);
21
+ }
22
+ /** One of the module's endpoints deliberately left without a mock, as the app's notMocked() builds one; its restated guards may name the module's. */
23
+ notMocked(name, input) {
24
+ return this.door.notMocked(name, input);
25
+ }
26
+ /** The entries of one slice, keyed by name, as the app's apiSlice() gathers them. */
27
+ apiSlice(...entries) {
28
+ return this.door.apiSlice(...entries);
29
+ }
30
+ /** Replaces one of the module's registered handlers until restored, as the app's override() does, its guardData typed with the module's guards. */
31
+ override(name, handler) {
32
+ return this.door.override(name, handler);
33
+ }
34
+ }
@@ -7,6 +7,8 @@ import { type LambderDeclaredVocabulary, type LambderHandlerRefusalsOf, type Lam
7
7
  import { type LambderApiTransport, type LambderApiTransportRequest } from "../shared/transport/LambderApiTransport.js";
8
8
  import { LambderCookieJar } from "../shared/transport/LambderCookieJar.js";
9
9
  import { type LambderApiGuard } from "../api/LambderApiGuards.js";
10
+ import type { LambderNoExtraKeys } from "../shared/util/LambderTypeUtilities.js";
11
+ import { LambderMockApiModule, type LambderMockApiModuleChecks, type LambderMockApiModuleOptions } from "./LambderMockApiModule.js";
10
12
  import { LAMBDER_MOCK_ADAPTER_DOOR, type LambderMockAdapterDoor } from "./LambderMockAdapterDoor.js";
11
13
  import { LambderMemoryRateLimiter } from "../stores/LambderMemoryRateLimiter.js";
12
14
  import { LambderMemoryIdempotencyStore } from "../stores/LambderMemoryIdempotencyStore.js";
@@ -14,7 +16,7 @@ import { LambderMemorySessionStore } from "../stores/LambderMemorySessionStore.j
14
16
  import LambderSessionManager, { type LambderCreatedSession } from "../session/LambderSessionManager.js";
15
17
  import type { LambderMockAppOptions, LambderMockIdempotencyOptions, LambderMockTransport, LambderMockTransportOptions } from "./LambderMockCreateOptions.js";
16
18
  import type { LambderApiOptionEntry, LambderGuardDeclarationEntry } from "../shared/wire/LambderApiOptionEntries.js";
17
- import type { LambderMockCallContext, LambderMockCallRecord, LambderMockEntry, LambderMockEntryInput, LambderMockFailure, LambderMockFailureReason, LambderMockHandler, LambderMockLatency, LambderMockListener, LambderMockNotMockedInput, LambderMockRateLimitPolicies, LambderMockRegistryCheck, LambderMockRestEntry, LambderMockSessionCallContext, LambderMockSlice, LambderMockOverride, LambderMockRefusalsOf } from "./LambderMockTypes.js";
19
+ import type { LambderMockCallContext, LambderMockCallRecord, LambderMockEntry, LambderMockEntryGuardsCheck, LambderMockEntryInput, LambderMockFailure, LambderMockFailureReason, LambderMockHandler, LambderMockLatency, LambderMockListener, LambderMockNotMockedGuardsCheck, LambderMockNotMockedInput, LambderMockRateLimitPolicies, LambderMockRegistryCheck, LambderMockRestEntry, LambderMockSessionCallContext, LambderMockSlice, LambderMockOverride, LambderMockRefusalsOf } from "./LambderMockTypes.js";
18
20
  /**
19
21
  * The mock runtime: the API core (LambderApiPipeline, the same class the
20
22
  * Lambda server runs) over memory stores, with typed mock handlers and mock
@@ -31,8 +33,10 @@ import type { LambderMockCallContext, LambderMockCallRecord, LambderMockEntry, L
31
33
  * generated `apiOptions` table, whose entries are their handlers alone.
32
34
  * `TVocabulary` and `TCodesRequired` are the refusal vocabulary the init
33
35
  * declared (declareRefusals), which types every handler's ctx.refuse.
36
+ * `D` is the generated guard declarations create() was given, which a
37
+ * module's mock guards are held to as create()'s are.
34
38
  */
35
- export declare class LambderMockApp<C extends LambderApiContractShape, S = any, G extends Record<string, LambderApiGuard<any, any, any>> = {}, TDerived extends boolean = false, TVocabulary extends LambderRefusalVocabulary = never, TCodesRequired extends boolean = false> {
39
+ export declare class LambderMockApp<C extends LambderApiContractShape, S = any, G extends Record<string, LambderApiGuard<any, any, any>> = {}, TDerived extends boolean = false, TVocabulary extends LambderRefusalVocabulary = never, TCodesRequired extends boolean = false, D extends Record<string, LambderGuardDeclarationEntry> = {}> {
36
40
  readonly apiVersion: string | null;
37
41
  /** The memory stores, for assertions and reset; null for a subsystem that is off or backed by a store of yours. */
38
42
  readonly sessionStore: LambderMemorySessionStore<S> | null;
@@ -87,8 +91,8 @@ export declare class LambderMockApp<C extends LambderApiContractShape, S = any,
87
91
  private readonly apiOptions;
88
92
  /** The server's schemas, when create() was given the generated table: every entry's input is validated, and every answer parsed, through them. */
89
93
  private readonly apiSchemas;
90
- /** The mock's own guards as create() was given them: which of them need a session, for an entry's mode without the apiOptions table. */
91
- private readonly mockGuards;
94
+ /** The mock's own guards as create() was given them, and every registered module's beside them: which of them need a session, for an entry's mode without the apiOptions table. */
95
+ private mockGuards;
92
96
  /** The server's guard declarations, when create() was given the generated table: the refusal codes a declared guard adds to an entry. */
93
97
  private readonly guardDeclarations;
94
98
  /** The server's refusal vocabulary, when create() was given the generated table: whether each code an entry may refuse with carries data. */
@@ -100,6 +104,15 @@ export declare class LambderMockApp<C extends LambderApiContractShape, S = any,
100
104
  constructor(options: LambderMockAppOptions<C, S, G, LambderMockRateLimitPolicies<S>, boolean | LambderMockIdempotencyOptions<S>, {}, Record<string, LambderApiOptionEntry> | undefined>,
101
105
  /** The vocabulary the init declared, or null: what initLambderMock().declareRefusals() passes on. */
102
106
  declared?: LambderDeclaredVocabulary<TVocabulary, TCodesRequired> | null);
107
+ /**
108
+ * A mock guard stands in for the server's guard of its name, so it
109
+ * declares the codes that guard declares: its own refusals type its
110
+ * ctx.refuse and are what checkApiRefusals holds it to. The compiler
111
+ * compares literal lists at the guards option and at a module's; this
112
+ * catches a list typed as any string and a caller the compiler did not
113
+ * see.
114
+ */
115
+ private assertGuardRefusalsAgree;
103
116
  /**
104
117
  * The registration-time checks every entry goes through, mocked or not:
105
118
  * the ones the server runs on a definition, and the mock's own "a session
@@ -145,6 +158,11 @@ export declare class LambderMockApp<C extends LambderApiContractShape, S = any,
145
158
  * and defaulted as on the server). Either alone where only one is given.
146
159
  */
147
160
  private entryInputOf;
161
+ /**
162
+ * An entry built and checked, by the app's api() or by a module's, whose
163
+ * declarations (`module`) its guards may name beside the app's own; the
164
+ * entry carries the module, which registering it adds.
165
+ */
148
166
  private buildEntry;
149
167
  /**
150
168
  * A mock for an endpoint: a handler, or the handler with the endpoint's
@@ -152,7 +170,7 @@ export declare class LambderMockApp<C extends LambderApiContractShape, S = any,
152
170
  * session endpoint's session is fetched before the handler runs, and a
153
171
  * call without one is refused as on the server.
154
172
  */
155
- api<K extends keyof C & string, TInputSchema extends z.ZodType = z.ZodType>(name: K, entry: LambderMockEntryInput<C, K, S, G, TInputSchema, TDerived, TVocabulary, TCodesRequired>): LambderMockEntry<C, K>;
173
+ api<K extends keyof C & string, TInputSchema extends z.ZodType = z.ZodType>(name: K & LambderMockEntryGuardsCheck<C, K, G, TDerived>, entry: LambderMockEntryInput<C, K, S, G, TInputSchema, TDerived, TVocabulary, TCodesRequired>): LambderMockEntry<C, K>;
156
174
  /**
157
175
  * An endpoint deliberately left without a mock; a call answers the
158
176
  * notMocked refusal carrying the reason. The refusal runs through the
@@ -162,7 +180,9 @@ export declare class LambderMockApp<C extends LambderApiContractShape, S = any,
162
180
  * apiOptions table a session endpoint restates its guards to say so,
163
181
  * `notMocked(name, { reason, guards })`; no guard runs.
164
182
  */
165
- notMocked<K extends keyof C & string>(name: K, input: LambderMockNotMockedInput<C, K, TDerived>): LambderMockEntry<C, K>;
183
+ notMocked<K extends keyof C & string>(name: K & LambderMockNotMockedGuardsCheck<C, K, G, TDerived>, input: LambderMockNotMockedInput<C, K, TDerived>): LambderMockEntry<C, K>;
184
+ /** A notMocked entry, by the app's notMocked() or by a module's, whose guards give its mode beside the app's own. */
185
+ private buildNotMockedEntry;
166
186
  /**
167
187
  * "Everything I did not register is not mocked, for this reason", as an
168
188
  * argument to the same register() call:
@@ -190,8 +210,33 @@ export declare class LambderMockApp<C extends LambderApiContractShape, S = any,
190
210
  * a test cares about that path.
191
211
  */
192
212
  restNotMocked(reason: string): LambderMockRestEntry;
213
+ /**
214
+ * Defines a module: the mock guards a part of the app declares with its
215
+ * own entries, standing in for the guards the server's module declares
216
+ * (its defineApiModule). Returns the module's entry builders, whose
217
+ * entries may name the app's guards and the module's own; registering
218
+ * them adds the module's guards to the app. So the app's create() need
219
+ * not import every module's mock guards.
220
+ *
221
+ * ```ts
222
+ * export const giftCardMocks = mockApp.defineApiModule({ guards: { cardHolder: mock.guard({ ... }) } });
223
+ * giftCardMocks.apiSlice(giftCardMocks.api("giftCards.redeem", async () => ({ redeemed: true })));
224
+ * ```
225
+ *
226
+ * Each guard is held to what create()'s are held to (the contract, the
227
+ * generated guardDeclarations, surplus keys); a name the app's create()
228
+ * declares is refused here, at compile time and at the call, and a name
229
+ * two modules declare when the second is registered.
230
+ */
231
+ defineApiModule<const TOptions extends LambderMockApiModuleOptions<S>>(options: LambderNoExtraKeys<TOptions, LambderMockApiModuleOptions<S>> & LambderMockApiModuleChecks<C, S, G, D, TOptions>): LambderMockApiModule<C, S, G & TOptions["guards"], TDerived, TVocabulary, TCodesRequired>;
193
232
  /** The entries of one module as a slice, keyed by name. Two entries for one endpoint is an error here. */
194
233
  apiSlice<const E extends readonly LambderMockEntry<C, keyof C & string>[]>(...entries: E): LambderMockSlice<C, E[number]["name"]>;
234
+ /**
235
+ * Registers slices, adding the modules their entries were built with: a
236
+ * module's mock guards are checked against the app's and every other
237
+ * module's first, and taken only once the registry took the slices, so a
238
+ * refused registration leaves the app as it was.
239
+ */
195
240
  private addSlices;
196
241
  /**
197
242
  * Registers the whole contract: every endpoint in exactly one slice, or
@@ -389,7 +434,7 @@ export declare const initLambderMock: <C extends LambderApiContractShape, S = an
389
434
  * dropped. `I` exists for the same reason `P` does, and
390
435
  * LambderMockSurplusKeys puts the error back on the key.
391
436
  */
392
- create<const G extends Record<string, LambderApiGuard<any, any, any, LambderMockCallContext<S>, LambderMockSessionCallContext<S>>> = {}, const P extends LambderMockRateLimitPolicies<S> = {}, I extends boolean | LambderMockIdempotencyOptions<S> = boolean | LambderMockIdempotencyOptions<S>, const D extends Record<string, LambderGuardDeclarationEntry> = {}, const A extends Record<string, LambderApiOptionEntry> | undefined = undefined>(options: LambderMockAppOptions<C, S, G, P, I, D, A>): LambderMockApp<C, S, G, [A] extends [undefined] ? false : true, LambderMergedRefusalVocabulary<TVocabulary>, TRequireCodes>;
437
+ create<const G extends Record<string, LambderApiGuard<any, any, any, LambderMockCallContext<S>, LambderMockSessionCallContext<S>>> = {}, const P extends LambderMockRateLimitPolicies<S> = {}, I extends boolean | LambderMockIdempotencyOptions<S> = boolean | LambderMockIdempotencyOptions<S>, const D extends Record<string, LambderGuardDeclarationEntry> = {}, const A extends Record<string, LambderApiOptionEntry> | undefined = undefined>(options: LambderMockAppOptions<C, S, G, P, I, D, A>): LambderMockApp<C, S, G, [A] extends [undefined] ? false : true, LambderMergedRefusalVocabulary<TVocabulary>, TRequireCodes, D>;
393
438
  };
394
439
  /**
395
440
  * Builds a mock guard: the server guard's shape, the handler seeing the
@@ -425,5 +470,5 @@ export declare const initLambderMock: <C extends LambderApiContractShape, S = an
425
470
  * dropped. `I` exists for the same reason `P` does, and
426
471
  * LambderMockSurplusKeys puts the error back on the key.
427
472
  */
428
- create<const G extends Record<string, LambderApiGuard<any, any, any, LambderMockCallContext<S>, LambderMockSessionCallContext<S>>> = {}, const P extends LambderMockRateLimitPolicies<S> = {}, I extends boolean | LambderMockIdempotencyOptions<S> = boolean | LambderMockIdempotencyOptions<S>, const D extends Record<string, LambderGuardDeclarationEntry> = {}, const A extends Record<string, LambderApiOptionEntry> | undefined = undefined>(options: LambderMockAppOptions<C, S, G, P, I, D, A>): LambderMockApp<C, S, G, [A] extends [undefined] ? false : true, never, false>;
473
+ create<const G extends Record<string, LambderApiGuard<any, any, any, LambderMockCallContext<S>, LambderMockSessionCallContext<S>>> = {}, const P extends LambderMockRateLimitPolicies<S> = {}, I extends boolean | LambderMockIdempotencyOptions<S> = boolean | LambderMockIdempotencyOptions<S>, const D extends Record<string, LambderGuardDeclarationEntry> = {}, const A extends Record<string, LambderApiOptionEntry> | undefined = undefined>(options: LambderMockAppOptions<C, S, G, P, I, D, A>): LambderMockApp<C, S, G, [A] extends [undefined] ? false : true, never, false, D>;
429
474
  };