lambder 8.1.2 → 9.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.
Files changed (86) hide show
  1. package/CHANGELOG.md +208 -0
  2. package/README.md +23 -31
  3. package/dist/api/LambderApiCallContext.d.ts +31 -1
  4. package/dist/api/LambderApiCallContext.js +8 -0
  5. package/dist/api/LambderApiDefinition.d.ts +2 -2
  6. package/dist/api/LambderApiEnvelope.d.ts +1 -1
  7. package/dist/api/LambderApiEnvelope.js +3 -4
  8. package/dist/api/LambderApiGuards.d.ts +2 -17
  9. package/dist/api/LambderApiIdempotency.js +5 -7
  10. package/dist/api/LambderApiRateLimits.d.ts +2 -29
  11. package/dist/build/generatedTables.d.ts +72 -0
  12. package/dist/build/generatedTables.js +99 -0
  13. package/dist/build/writeApiGuardParams.d.ts +60 -0
  14. package/dist/build/writeApiGuardParams.js +85 -0
  15. package/dist/build/writeApiOptions.d.ts +68 -0
  16. package/dist/build/writeApiOptions.js +102 -0
  17. package/dist/build.d.ts +10 -4
  18. package/dist/build.js +7 -4
  19. package/dist/client/LambderCaller.d.ts +0 -4
  20. package/dist/client/LambderCaller.js +1 -9
  21. package/dist/client/LambderUploadRunner.d.ts +7 -7
  22. package/dist/client/LambderUploadRunner.js +12 -21
  23. package/dist/client.d.ts +7 -0
  24. package/dist/client.js +11 -0
  25. package/dist/core/Lambder.d.ts +71 -12
  26. package/dist/core/Lambder.js +116 -38
  27. package/dist/core/LambderContext.d.ts +9 -6
  28. package/dist/core/LambderContext.js +2 -1
  29. package/dist/core/LambderResolver.d.ts +6 -12
  30. package/dist/core/LambderResolver.js +2 -14
  31. package/dist/core/LambderResponseBuilder.d.ts +15 -70
  32. package/dist/core/LambderResponseBuilder.js +15 -99
  33. package/dist/index.d.ts +16 -3
  34. package/dist/index.js +14 -1
  35. package/dist/invoke/LambderInvokeCaller.js +3 -4
  36. package/dist/mock/LambderMockApp.d.ts +34 -17
  37. package/dist/mock/LambderMockApp.js +69 -24
  38. package/dist/mock/LambderMockCreateOptions.d.ts +70 -7
  39. package/dist/mock/LambderMockTypes.d.ts +31 -21
  40. package/dist/mock/lambderMockPoliciesFrom.d.ts +51 -0
  41. package/dist/mock/lambderMockPoliciesFrom.js +46 -0
  42. package/dist/mock.d.ts +3 -0
  43. package/dist/mock.js +3 -0
  44. package/dist/secrets/LambderOneShotSecrets.d.ts +166 -0
  45. package/dist/secrets/LambderOneShotSecrets.js +217 -0
  46. package/dist/session/LambderSessionCrypto.js +6 -16
  47. package/dist/shared/contracts/LambderIdempotencyStore.d.ts +3 -2
  48. package/dist/shared/contracts/LambderOneShotSecretStore.d.ts +122 -0
  49. package/dist/shared/contracts/LambderOneShotSecretStore.js +38 -0
  50. package/dist/shared/util/LambderBackoffTimer.d.ts +82 -0
  51. package/dist/shared/util/LambderBackoffTimer.js +86 -0
  52. package/dist/shared/util/LambderBase64.d.ts +14 -0
  53. package/dist/shared/util/LambderBase64.js +17 -0
  54. package/dist/shared/util/LambderSignedClaims.d.ts +78 -0
  55. package/dist/shared/util/LambderSignedClaims.js +109 -0
  56. package/dist/shared/util/LambderTextDigest.d.ts +19 -5
  57. package/dist/shared/util/LambderTextDigest.js +30 -5
  58. package/dist/shared/util/LambderTypeUtilities.d.ts +18 -0
  59. package/dist/shared/util/assertPlainData.d.ts +9 -0
  60. package/dist/shared/util/assertPlainData.js +41 -0
  61. package/dist/shared/wire/LambderAnswerHeaders.d.ts +3 -2
  62. package/dist/shared/wire/LambderAnswerHeaders.js +3 -2
  63. package/dist/shared/wire/LambderApiContract.d.ts +9 -14
  64. package/dist/shared/wire/LambderApiOptionEntries.d.ts +148 -0
  65. package/dist/shared/wire/LambderApiOptionEntries.js +35 -0
  66. package/dist/shared/wire/LambderApiRefusal.d.ts +3 -4
  67. package/dist/shared/wire/LambderApiRefusal.js +3 -4
  68. package/dist/stores/LambderDdbOneShotSecretStore.d.ts +64 -0
  69. package/dist/stores/LambderDdbOneShotSecretStore.js +266 -0
  70. package/dist/stores/LambderMemoryIdempotencyStore.d.ts +3 -2
  71. package/dist/stores/LambderMemoryIdempotencyStore.js +3 -2
  72. package/dist/stores/LambderMemoryOneShotSecretStore.d.ts +36 -0
  73. package/dist/stores/LambderMemoryOneShotSecretStore.js +93 -0
  74. package/dist/testing/LambderConformanceRunner.d.ts +46 -0
  75. package/dist/testing/LambderConformanceRunner.js +21 -0
  76. package/dist/testing/lambderIdempotencyStoreConformance.d.ts +33 -0
  77. package/dist/testing/lambderIdempotencyStoreConformance.js +237 -0
  78. package/dist/testing/lambderOneShotSecretStoreConformance.d.ts +43 -0
  79. package/dist/testing/lambderOneShotSecretStoreConformance.js +224 -0
  80. package/dist/testing/lambderRateLimiterConformance.d.ts +20 -0
  81. package/dist/testing/lambderRateLimiterConformance.js +72 -0
  82. package/dist/testing/lambderSessionStoreConformance.d.ts +27 -0
  83. package/dist/testing/lambderSessionStoreConformance.js +165 -0
  84. package/dist/testing.d.ts +14 -0
  85. package/dist/testing.js +12 -0
  86. package/package.json +1 -1
@@ -1,12 +1,10 @@
1
- import type { z } from "zod";
2
1
  import type { LambderRenderContext } from "./LambderContext.js";
3
- import { type LambderCookieOptions, type LambderClearCookieOptions } from "../shared/wire/LambderCookie.js";
4
2
  import type { LambderFiles } from "./LambderFiles.js";
5
3
  import { LambderResponse, type LambderHeadersInput } from "./LambderResponse.js";
6
4
  import type { LambderHttpStatusCode } from "../shared/wire/LambderHttpStatus.js";
7
5
  import { LambderSafeHtml } from "../shared/LambderHtml.js";
8
6
  import type { LambderTemplateData } from "./LambderTemplatingEngine.js";
9
- import type { LambderApiResponseConfig, LambderApiNullAnswerConfig } from "../shared/wire/LambderApiContract.js";
7
+ import type { LambderApiResponseConfig } from "../shared/wire/LambderApiContract.js";
10
8
  export type { LambderApiEnvelopeBody, LambderApiResponseConfig } from "../shared/wire/LambderApiContract.js";
11
9
  export type LambderResponseOptions = {
12
10
  statusCode?: LambderHttpStatusCode;
@@ -18,19 +16,6 @@ export type LambderResponseOptions = {
18
16
  /** "auto" (default): ETag on GET/HEAD 200 when globally enabled. true: force. false: never. */
19
17
  etag?: boolean | "auto";
20
18
  };
21
- /**
22
- * The two shapes of an API answer: the output the contract declares, or
23
- * `null` beside a config that says why (a refusal flag, an `errorMessage`, a
24
- * `message`). A bare `res.api(null)` compiles only when the output type
25
- * itself allows null, so a success payload is always the declared output,
26
- * which lets a typed caller (LambderInvokeCaller.api) promise it. Untyped
27
- * resolvers (`TOutput = any`) accept anything. This is the resolver's method
28
- * type; the core's answer type is LambderApiAnswer.
29
- */
30
- export type LambderResolverApiMethod<TOutput, TResult> = {
31
- (payload: TOutput, config?: LambderApiResponseConfig, options?: LambderResponseOptions): TResult;
32
- (payload: null, config: LambderApiNullAnswerConfig, options?: LambderResponseOptions): TResult;
33
- };
34
19
  export type LambderRawResponseInit = {
35
20
  statusCode: LambderHttpStatusCode;
36
21
  headers?: LambderHeadersInput;
@@ -40,39 +25,23 @@ export type LambderRawResponseInit = {
40
25
  compress?: boolean | "auto";
41
26
  etag?: boolean | "auto";
42
27
  };
43
- export default class LambderResponseBuilder<TResponse = any> {
28
+ /**
29
+ * Builds the responses of routes, hooks and error handlers. An API handler
30
+ * never holds one: it returns its output or refuses, and writes headers and
31
+ * cookies through its context (LambderResponseTools).
32
+ */
33
+ export default class LambderResponseBuilder {
44
34
  protected files: LambderFiles | null;
45
35
  protected apiVersion: string | null;
46
36
  protected ctx?: LambderRenderContext;
47
- /** The output schema of the API this builder answers, which every success payload is parsed through; null outside an API handler. */
48
- protected apiOutput: z.ZodType | null;
49
- constructor({ files, apiVersion, ctx, apiOutput }: {
37
+ constructor({ files, apiVersion, ctx }: {
50
38
  files?: LambderFiles | null;
51
39
  apiVersion?: string | null;
52
40
  ctx?: LambderRenderContext;
53
- apiOutput?: z.ZodType;
54
41
  });
55
42
  private buildResponse;
56
43
  /** The instance's file reader, which res.file and res.templateFile need. */
57
44
  private requireFiles;
58
- /** Appends a response header; applied onto the response once the handler has one, in call order. */
59
- addHeader(key: string, value: string): void;
60
- /** Replaces a response header; applied onto the response once the handler has one, in call order. */
61
- setHeader(key: string, value: string | string[]): void;
62
- /**
63
- * Adds a Set-Cookie header. A function-form `domain` is resolved against
64
- * the request hostname. Defaults: Path=/, SameSite=Lax, Secure, not
65
- * HttpOnly, browser-session lifetime.
66
- */
67
- setCookie(name: string, value: string, options?: LambderCookieOptions): void;
68
- /**
69
- * Adds a Set-Cookie header that deletes the cookie. Pass the same
70
- * `domain` and `path` the cookie was set with: a cookie's identity is
71
- * (name, domain, path), and a deletion under a different scope targets a
72
- * different cookie and deletes nothing.
73
- */
74
- clearCookie(name: string, options?: LambderClearCookieOptions): void;
75
- logToApiResponse(input: unknown): void;
76
45
  raw(init: LambderRawResponseInit): LambderResponse;
77
46
  json(data: Record<string, any>, options?: LambderResponseOptions): LambderResponse;
78
47
  text(data: string, options?: LambderResponseOptions): LambderResponse;
@@ -109,37 +78,13 @@ export default class LambderResponseBuilder<TResponse = any> {
109
78
  templateFile(filePath: string, data?: LambderTemplateData, options?: LambderResponseOptions & {
110
79
  htmlVirtualSlots?: boolean;
111
80
  }): Promise<LambderResponse>;
112
- api(payload: TResponse, config?: LambderApiResponseConfig, options?: LambderResponseOptions): LambderResponse;
113
- api(payload: null, config: LambderApiNullAnswerConfig, options?: LambderResponseOptions): LambderResponse;
114
81
  /**
115
- * A payload as the API's output schema declares it. The type system
116
- * accepts a value that carries more than the schema (a row read straight
117
- * from a table is assignable to a narrower object type), and without this
118
- * the extra fields, a password hash included, would reach the client.
119
- * zod strips what the schema does not declare, fills its defaults and
120
- * applies its transforms, so the wire and the idempotency store only see
121
- * the declared shape. A refusal's payload beside an errorMessage or a
122
- * flag is parsed the same way; only null passes as it is. Only an API
123
- * handler's own resolver holds the schema: a hook, a validation handler
124
- * or an error handler answers in shapes of its own, a cached answer in
125
- * its wire form, and is sent as given.
126
- *
127
- * The payload is the schema's input form (what a handler writes before
128
- * the transforms), so a transform runs exactly once. A payload the schema
129
- * rejects is a handler breaking its contract, answered as a crash rather
130
- * than sent (LambderApiOutputValidationError, which an idempotency key
131
- * records as its answer, since the handler has already run).
132
- *
133
- * The parse is synchronous, so an output schema cannot be async: zod
134
- * throws from a synchronous parse that meets an async refinement or
135
- * transform, and a transform may throw of its own accord. Either throw
136
- * becomes the same LambderApiOutputValidationError, carrying what was
137
- * thrown as its cause. Left to escape as it is, it would read as the
138
- * handler crashing before its answer: the idempotency engine would
139
- * release the key's claim and every retry would run the operation again.
82
+ * An API envelope written by hand: what a hook, an input validation
83
+ * handler or a global error handler answers an API call with (a refusal
84
+ * flag, an errorMessage, a crash). The payload goes out as given; an API
85
+ * handler's own output is parsed through its schema by the instance
86
+ * instead. The logList channel is what the request accumulated unless the
87
+ * config names its own.
140
88
  */
141
- private declaredPayload;
142
- /** Same as api() but forces compression of the response body. */
143
- apiBinary(payload: TResponse, config?: LambderApiResponseConfig, options?: LambderResponseOptions): LambderResponse;
144
- apiBinary(payload: null, config: LambderApiNullAnswerConfig, options?: LambderResponseOptions): LambderResponse;
89
+ api(payload: unknown, config?: LambderApiResponseConfig, options?: LambderResponseOptions): LambderResponse;
145
90
  }
@@ -1,18 +1,18 @@
1
- import { serializeCookie, serializeClearCookie } from "../shared/wire/LambderCookie.js";
2
1
  import { LambderResponse } from "./LambderResponse.js";
3
2
  import { buildApiEnvelope } from "../api/LambderApiEnvelope.js";
4
- import { LambderApiOutputValidationError } from "../api/LambderApiOutputValidationError.js";
3
+ /**
4
+ * Builds the responses of routes, hooks and error handlers. An API handler
5
+ * never holds one: it returns its output or refuses, and writes headers and
6
+ * cookies through its context (LambderResponseTools).
7
+ */
5
8
  export default class LambderResponseBuilder {
6
9
  files;
7
10
  apiVersion;
8
11
  ctx;
9
- /** The output schema of the API this builder answers, which every success payload is parsed through; null outside an API handler. */
10
- apiOutput;
11
- constructor({ files, apiVersion, ctx, apiOutput }) {
12
+ constructor({ files, apiVersion, ctx }) {
12
13
  this.files = files ?? null;
13
14
  this.apiVersion = apiVersion ?? null;
14
15
  this.ctx = ctx;
15
- this.apiOutput = apiOutput ?? null;
16
16
  }
17
17
  ;
18
18
  buildResponse(statusCode, contentType, body, options, defaults) {
@@ -37,49 +37,6 @@ export default class LambderResponseBuilder {
37
37
  throw new Error(`Lambder: ${method} requires the files option at creation (e.g. files: new LambderLocalFileSource({ root }))`);
38
38
  return this.files;
39
39
  }
40
- /** Appends a response header; applied onto the response once the handler has one, in call order. */
41
- addHeader(key, value) {
42
- if (!this.ctx)
43
- throw new Error("Lambder: res.addHeader needs the request context, and this response builder was created without one.");
44
- this.ctx.responseHeaders.add(key, value);
45
- }
46
- ;
47
- /** Replaces a response header; applied onto the response once the handler has one, in call order. */
48
- setHeader(key, value) {
49
- if (!this.ctx)
50
- throw new Error("Lambder: res.setHeader needs the request context, and this response builder was created without one.");
51
- this.ctx.responseHeaders.set(key, value);
52
- }
53
- ;
54
- /**
55
- * Adds a Set-Cookie header. A function-form `domain` is resolved against
56
- * the request hostname. Defaults: Path=/, SameSite=Lax, Secure, not
57
- * HttpOnly, browser-session lifetime.
58
- */
59
- setCookie(name, value, options) {
60
- if (!this.ctx)
61
- throw new Error("Lambder: res.setCookie needs the request context, and this response builder was created without one.");
62
- this.addHeader("Set-Cookie", serializeCookie(name, value, options, this.ctx.host));
63
- }
64
- ;
65
- /**
66
- * Adds a Set-Cookie header that deletes the cookie. Pass the same
67
- * `domain` and `path` the cookie was set with: a cookie's identity is
68
- * (name, domain, path), and a deletion under a different scope targets a
69
- * different cookie and deletes nothing.
70
- */
71
- clearCookie(name, options) {
72
- if (!this.ctx)
73
- throw new Error("Lambder: res.clearCookie needs the request context, and this response builder was created without one.");
74
- this.addHeader("Set-Cookie", serializeClearCookie(name, options, this.ctx.host));
75
- }
76
- ;
77
- logToApiResponse(input) {
78
- if (!this.ctx)
79
- throw new Error("Lambder: res.logToApiResponse needs the request context, and this response builder was created without one.");
80
- this.ctx.logList.push(input);
81
- }
82
- ;
83
40
  raw(init) {
84
41
  return new LambderResponse({
85
42
  statusCode: init.statusCode,
@@ -177,58 +134,17 @@ export default class LambderResponseBuilder {
177
134
  return this.buildResponse(200, "text/html; charset=utf-8", template.render(data), options);
178
135
  }
179
136
  ;
180
- api(payload, config = {}, options) {
181
- // The envelope is the core's (one writer for both the server and the
182
- // mock runtime); the logList channel is what this request accumulated
183
- // unless the config names its own.
184
- const envelope = buildApiEnvelope(this.apiVersion, this.declaredPayload(payload), { ...config, logList: config.logList || this.ctx?.logList });
185
- return this.json(envelope, options);
186
- }
187
- ;
188
137
  /**
189
- * A payload as the API's output schema declares it. The type system
190
- * accepts a value that carries more than the schema (a row read straight
191
- * from a table is assignable to a narrower object type), and without this
192
- * the extra fields, a password hash included, would reach the client.
193
- * zod strips what the schema does not declare, fills its defaults and
194
- * applies its transforms, so the wire and the idempotency store only see
195
- * the declared shape. A refusal's payload beside an errorMessage or a
196
- * flag is parsed the same way; only null passes as it is. Only an API
197
- * handler's own resolver holds the schema: a hook, a validation handler
198
- * or an error handler answers in shapes of its own, a cached answer in
199
- * its wire form, and is sent as given.
200
- *
201
- * The payload is the schema's input form (what a handler writes before
202
- * the transforms), so a transform runs exactly once. A payload the schema
203
- * rejects is a handler breaking its contract, answered as a crash rather
204
- * than sent (LambderApiOutputValidationError, which an idempotency key
205
- * records as its answer, since the handler has already run).
206
- *
207
- * The parse is synchronous, so an output schema cannot be async: zod
208
- * throws from a synchronous parse that meets an async refinement or
209
- * transform, and a transform may throw of its own accord. Either throw
210
- * becomes the same LambderApiOutputValidationError, carrying what was
211
- * thrown as its cause. Left to escape as it is, it would read as the
212
- * handler crashing before its answer: the idempotency engine would
213
- * release the key's claim and every retry would run the operation again.
138
+ * An API envelope written by hand: what a hook, an input validation
139
+ * handler or a global error handler answers an API call with (a refusal
140
+ * flag, an errorMessage, a crash). The payload goes out as given; an API
141
+ * handler's own output is parsed through its schema by the instance
142
+ * instead. The logList channel is what the request accumulated unless the
143
+ * config names its own.
214
144
  */
215
- declaredPayload(payload) {
216
- if (!this.apiOutput || payload === null)
217
- return payload;
218
- const apiName = this.ctx?.apiName ?? "?";
219
- let parsed;
220
- try {
221
- parsed = this.apiOutput.safeParse(payload);
222
- }
223
- catch (thrown) {
224
- throw new LambderApiOutputValidationError(apiName, { thrown });
225
- }
226
- if (parsed.success)
227
- return parsed.data;
228
- throw new LambderApiOutputValidationError(apiName, { zodError: parsed.error });
229
- }
230
- apiBinary(payload, config = {}, options) {
231
- return this.api(payload, config, { ...options, compress: true });
145
+ api(payload, config = {}, options) {
146
+ const envelope = buildApiEnvelope(this.apiVersion, payload, { ...config, logList: config.logList || this.ctx?.logList });
147
+ return this.json(envelope, options);
232
148
  }
233
149
  ;
234
150
  }
package/dist/index.d.ts CHANGED
@@ -12,6 +12,11 @@ export { lambderCookieJarTransport } from "./shared/transport/lambderCookieJarTr
12
12
  export type { LambderApiTransport, LambderApiTransportRequest, LambderTransportFailureReason } from "./shared/transport/LambderApiTransport.js";
13
13
  export { LambderCookieJar, parseSetCookie } from "./shared/transport/LambderCookieJar.js";
14
14
  export { LambderExpiringMap, LambderExpiringMapFullError } from "./shared/util/LambderExpiringMap.js";
15
+ export { LambderBackoffTimer } from "./shared/util/LambderBackoffTimer.js";
16
+ export type { LambderBackoffTimerOptions } from "./shared/util/LambderBackoffTimer.js";
17
+ export { LambderSignedClaims, keyedDigest, randomSecret } from "./shared/util/LambderSignedClaims.js";
18
+ export type { LambderSignedClaimsOptions } from "./shared/util/LambderSignedClaims.js";
19
+ export { constantTimeEquals } from "./shared/util/LambderTextDigest.js";
15
20
  export type { LambderStoredCookie } from "./shared/transport/LambderCookieJar.js";
16
21
  export { lambderHandlerTransport } from "./invoke/lambderHandlerTransport.js";
17
22
  export type { LambderHandlerTransportOptions } from "./invoke/lambderHandlerTransport.js";
@@ -25,7 +30,7 @@ export { toHttpAnswer } from "./api/LambderApiAnswer.js";
25
30
  export type { LambderApiAnswer } from "./api/LambderApiAnswer.js";
26
31
  export { LambderAnswerHeaders, getAnswerHeader, setAnswerHeader, addAnswerHeader } from "./shared/wire/LambderAnswerHeaders.js";
27
32
  export { createApiCallContext } from "./api/LambderApiCallContext.js";
28
- export type { LambderApiCallContext, LambderApiCallTrace } from "./api/LambderApiCallContext.js";
33
+ export type { LambderApiCallContext, LambderApiCallTrace, LambderResponseTools } from "./api/LambderApiCallContext.js";
29
34
  export type { LambderApiDefinition } from "./api/LambderApiDefinition.js";
30
35
  export { apiSignatureOf } from "./api/LambderApiSignature.js";
31
36
  export type { LambderApiSignatureEntry } from "./api/LambderApiSignature.js";
@@ -63,7 +68,7 @@ export type { LambderHttpStatusCode } from "./shared/wire/LambderHttpStatus.js";
63
68
  export { html, xml, raw, jsonScript, escapeHtml, renderHtmlValue, LambderSafeHtml, type LambderHtmlValue } from "./shared/LambderHtml.js";
64
69
  export { LambderTemplatingEngine } from "./core/LambderTemplatingEngine.js";
65
70
  export type { LambderTemplateData, LambderTemplatingEngineOptions } from "./core/LambderTemplatingEngine.js";
66
- export type { LambderResponseOptions, LambderRawResponseInit, LambderResolverApiMethod, } from "./core/LambderResponseBuilder.js";
71
+ export type { LambderResponseOptions, LambderRawResponseInit, } from "./core/LambderResponseBuilder.js";
67
72
  export type { LambderRouteMatcher, LambderRouteConditionFn, LambderRouteCondition, LambderPathParamsOf, LambderRoutePath } from "./core/LambderRouting.js";
68
73
  export type { LambderCorsConfig } from "./core/LambderCors.js";
69
74
  export type { LambderCreateOptions, LambderSessionOptions, LambderActionTools, LambderHandler, } from "./core/LambderCreateOptions.js";
@@ -106,6 +111,12 @@ export type { LambderRateLimiter, LambderRateLimitWindow, LambderRateLimitPolicy
106
111
  export { LambderDdbRateLimiter } from "./stores/LambderDdbRateLimiter.js";
107
112
  export type { LambderDdbRateLimiterOptions } from "./stores/LambderDdbRateLimiter.js";
108
113
  export { LambderMemoryRateLimiter } from "./stores/LambderMemoryRateLimiter.js";
114
+ export { LambderOneShotSecrets } from "./secrets/LambderOneShotSecrets.js";
115
+ export type { LambderOneShotSecretKind, LambderOneShotSecretsOptions, LambderOneShotIssueResult, LambderOneShotRedeemResult, LambderOneShotCodeKindNames, LambderOneShotTokenKindNames, } from "./secrets/LambderOneShotSecrets.js";
116
+ export type { LambderOneShotSecretStore, LambderOneShotSecretRecord, LambderOneShotSecretDraft, LambderOneShotSecretShape, LambderOneShotIssueOutcome, } from "./shared/contracts/LambderOneShotSecretStore.js";
117
+ export { LambderDdbOneShotSecretStore } from "./stores/LambderDdbOneShotSecretStore.js";
118
+ export type { LambderDdbOneShotSecretStoreOptions } from "./stores/LambderDdbOneShotSecretStore.js";
119
+ export { LambderMemoryOneShotSecretStore } from "./stores/LambderMemoryOneShotSecretStore.js";
109
120
  export type { LambderIdempotencyStore, LambderIdempotencyBeginResult, LambderIdempotencyDoneRecord, } from "./shared/contracts/LambderIdempotencyStore.js";
110
121
  export { LambderDdbIdempotencyStore } from "./stores/LambderDdbIdempotencyStore.js";
111
122
  export type { LambderDdbIdempotencyStoreOptions } from "./stores/LambderDdbIdempotencyStore.js";
@@ -118,9 +129,11 @@ export type { LambderRateLimitKeyBuilder } from "./api/LambderApiRateLimits.js";
118
129
  export type { LambderContextRateLimit, LambderContextRateLimitCheck, LambderRateLimitCheckResult, LambderChargeablePolicyNames, LambderChargeKeyArgs, LambderRateLimitKeyFn, LambderRateLimitPer, LambderRateLimitBudget, LambderRateLimitChargeAt, LambderApiRateLimitPolicyConfig, LambderApiRateLimitsConfig, LambderAllowedPolicyNames, LambderRateLimitOption, } from "./api/LambderApiRateLimits.js";
119
130
  export type { LambderApiIdempotencyConfig } from "./api/LambderApiIdempotency.js";
120
131
  export type { LambderGuardsOptionValue, LambderRateLimitOverride, LambderRateLimitOptionValue, LambderApiIdempotencyOption, } from "./shared/wire/LambderApiOptionValues.js";
132
+ export { apiGuardParam } from "./shared/wire/LambderApiOptionEntries.js";
133
+ export type { LambderApiOptionEntries, LambderApiOptionEntry, LambderRateLimitPolicyEntry, LambderGuardDeclarationEntry, LambderApisWithGuard, LambderApisGuardedBy, LambderApisWithMode, LambderGuardParamOf, } from "./shared/wire/LambderApiOptionEntries.js";
121
134
  export { createLambderI18n } from "./shared/LambderI18n.js";
122
135
  export type { LambderLanguageMeta, LambderI18nConfig, LambderI18nInstance, LambderI18nTranslator, LambderI18nExtractParams, LambderI18nDictionaryLoader, LambderI18nCodes, LambderI18nKeys, LambderI18nTranslatorFor, } from "./shared/LambderI18n.js";
123
- export type { LambderApiContractShape, LambderApiMode, LambderApiEnvelopeBody, LambderApiResponseConfig, LambderApiNullAnswerConfig, LambderContractEntry, LambderMergeContract, LambderGuardNamesIn, LambderContractMode, LambderContractKeysWithMode, LambderContractKeysWithGuard, LambderJsonOf, LambderJsonOutputOf, LambderContractGuardsOf, LambderContractGuardNames, LambderContractGuardInputsOf, LambderContractGuardInput, LambderContractGuardInputNames, LambderContractRateLimitOf, LambderContractRateLimitNames, LambderContractIdempotencyOf, } from "./shared/wire/LambderApiContract.js";
136
+ export type { LambderApiContractShape, LambderApiMode, LambderApiEnvelopeBody, LambderApiResponseConfig, LambderContractEntry, LambderMergeContract, LambderGuardNamesIn, LambderContractMode, LambderContractKeysWithMode, LambderContractKeysWithGuard, LambderJsonOf, LambderJsonOutputOf, LambderContractGuardsOf, LambderContractGuardNames, LambderContractGuardInputsOf, LambderContractGuardInput, LambderContractGuardInputNames, LambderContractRateLimitOf, LambderContractRateLimitNames, LambderContractIdempotencyOf, } from "./shared/wire/LambderApiContract.js";
124
137
  export type { LambderRenderContext, LambderSessionRenderContext, LambderHttpEvent, LambderHttpEventFormat } from "./core/LambderContext.js";
125
138
  export type { LambderApiAnswerOutcome, LambderApiSuccessOutcome, LambderApiCallFailure, LambderApiValidationFailure, LambderApiEnvelopeFailure, LambderApiHttpAnswer, } from "./shared/wire/LambderApiOutcome.js";
126
139
  export { resolveApiOutcome } from "./shared/wire/LambderApiOutcome.js";
package/dist/index.js CHANGED
@@ -9,6 +9,11 @@ export { buildTransportEnvelope, LambderTransportFailure, isLambderTransportFail
9
9
  export { lambderCookieJarTransport } from "./shared/transport/lambderCookieJarTransport.js";
10
10
  export { LambderCookieJar, parseSetCookie } from "./shared/transport/LambderCookieJar.js";
11
11
  export { LambderExpiringMap, LambderExpiringMapFullError } from "./shared/util/LambderExpiringMap.js";
12
+ // Waiting longer after each failure, once: what the upload runner and an app's own retrying code climb.
13
+ export { LambderBackoffTimer } from "./shared/util/LambderBackoffTimer.js";
14
+ // Signed claims tokens, and the keyed digest and random secret for the secrets an app stores.
15
+ export { LambderSignedClaims, keyedDigest, randomSecret } from "./shared/util/LambderSignedClaims.js";
16
+ export { constantTimeEquals } from "./shared/util/LambderTextDigest.js";
12
17
  export { lambderHandlerTransport } from "./invoke/lambderHandlerTransport.js";
13
18
  export { synthesizeLambdaHttpEvent, decodeLambdaHttpResult, localLambdaContext, LAMBDER_INVOKE_HEADER, LAMBDER_INVOKED_BY_HEADER, LAMBDER_INVOKE_PROTOCOL, } from "./invoke/LambderLambdaEvent.js";
14
19
  // The API core: the request, answer, envelope and pipeline both the server and the mock runtime run
@@ -71,6 +76,11 @@ export { LambderMemoryCache } from "./stores/LambderMemoryCache.js";
71
76
  export { RATE_LIMIT_WINDOWS } from "./shared/contracts/LambderRateLimiter.js";
72
77
  export { LambderDdbRateLimiter } from "./stores/LambderDdbRateLimiter.js";
73
78
  export { LambderMemoryRateLimiter } from "./stores/LambderMemoryRateLimiter.js";
79
+ // One-shot secrets: codes and tokens handed out once and taken back once, the
80
+ // store interface they live in, and the DynamoDB and in-memory stores
81
+ export { LambderOneShotSecrets } from "./secrets/LambderOneShotSecrets.js";
82
+ export { LambderDdbOneShotSecretStore } from "./stores/LambderDdbOneShotSecretStore.js";
83
+ export { LambderMemoryOneShotSecretStore } from "./stores/LambderMemoryOneShotSecretStore.js";
74
84
  export { LambderDdbIdempotencyStore } from "./stores/LambderDdbIdempotencyStore.js";
75
85
  export { LambderMemoryIdempotencyStore } from "./stores/LambderMemoryIdempotencyStore.js";
76
86
  // Declarative per-API policies: guards
@@ -78,6 +88,9 @@ export { lambderGuard, lambderRateLimitKey } from "./core/LambderPolicyBuilders.
78
88
  export { lambderGuardBuilder } from "./api/LambderApiGuards.js";
79
89
  // Declarative per-API policies: rate limits
80
90
  export { lambderRateLimitKeyBuilder, rateLimitRefusal, DEFAULT_RATE_LIMIT_REFUSAL } from "./api/LambderApiRateLimits.js";
91
+ // The declared options as plain data: what apiOptionEntries() reports and
92
+ // writeApiOptions writes, and the readers over the generated tables.
93
+ export { apiGuardParam } from "./shared/wire/LambderApiOptionEntries.js";
81
94
  // Typed translations (standalone, isomorphic)
82
95
  export { createLambderI18n } from "./shared/LambderI18n.js";
83
96
  export { resolveApiOutcome } from "./shared/wire/LambderApiOutcome.js";
@@ -86,5 +99,5 @@ export { createContext, isV2HttpEvent } from "./core/LambderContext.js";
86
99
  export { COMPRESSED_PAYLOAD_GZ_FIELD, COMPRESSED_PAYLOAD_BR_FIELD, COMPRESSED_PAYLOAD_BYTES_FIELD, DEFAULT_REQUEST_COMPRESSION_SETTINGS, DEFAULT_MAX_RESTORED_PAYLOAD_BYTES,
87
100
  // The Brotli twin of the browser's compressPayloadGzip, and its defaults.
88
101
  DEFAULT_INVOKE_REQUEST_COMPRESSION_SETTINGS, compressPayloadBrotli, } from "./shared/wire/LambderRequestPayload.js";
89
- // Cookies (res.setCookie / res.clearCookie build on these; exported for code holding a LambderResponse)
102
+ // Cookies (ctx.setCookie / ctx.clearCookie build on these; exported for code holding a LambderResponse)
90
103
  export { serializeCookie, serializeClearCookie, resolveCookieDomain } from "./shared/wire/LambderCookie.js";
@@ -378,10 +378,9 @@ export default class LambderInvokeCaller {
378
378
  // The answer's Set-Cookie values, so a session the callee rotated or
379
379
  // cleared is visible to whoever is carrying it.
380
380
  const cookies = http.cookies;
381
- // The declared output, by the callee's own typing: res.api(null)
382
- // compiles only for an output that allows null or beside a reason (an
383
- // errorMessage is a failure below; a message-only null is the
384
- // callee's contract to keep).
381
+ // The declared output, by the callee's own typing: a handler returns
382
+ // its output, so a success payload is null only where the output
383
+ // allows null.
385
384
  if (outcome.ok)
386
385
  return { ok: true, payload: (outcome.payload ?? null), response: outcome.response, logList, cookies };
387
386
  const shared = { status: outcome.status, retryAfterSeconds: outcome.retryAfterSeconds, logList, cookies };
@@ -10,6 +10,7 @@ import { LambderMemoryIdempotencyStore } from "../stores/LambderMemoryIdempotenc
10
10
  import { LambderMemorySessionStore } from "../stores/LambderMemorySessionStore.js";
11
11
  import LambderSessionManager, { type LambderCreatedSession } from "../session/LambderSessionManager.js";
12
12
  import type { LambderMockAppOptions, LambderMockIdempotencyOptions, LambderMockTransport, LambderMockTransportOptions } from "./LambderMockCreateOptions.js";
13
+ import type { LambderApiOptionEntry, LambderGuardDeclarationEntry } from "../shared/wire/LambderApiOptionEntries.js";
13
14
  import type { LambderMockCallContext, LambderMockCallRecord, LambderMockEntry, LambderMockEntryInput, LambderMockFailure, LambderMockFailureReason, LambderMockHandler, LambderMockLatency, LambderMockListener, LambderMockPublicNames, LambderMockRateLimitPolicies, LambderMockRegistryCheck, LambderMockRestEntry, LambderMockSessionCallContext, LambderMockSessionNames, LambderMockSlice, LambderMockOverride } from "./LambderMockTypes.js";
14
15
  /**
15
16
  * The mock runtime: the API core (LambderApiPipeline, the same class the
@@ -23,9 +24,10 @@ import type { LambderMockCallContext, LambderMockCallRecord, LambderMockEntry, L
23
24
  *
24
25
  * Create one with initLambderMock<Contract, SessionData>().create(...),
25
26
  * which fixes the contract and session types first so everything else is
26
- * inferred from the options.
27
+ * inferred from the options. `TDerived` is true for a mock created with the
28
+ * generated `apiOptions` table, whose entries are their handlers alone.
27
29
  */
28
- export declare class LambderMockApp<C extends LambderApiContractShape, S = any, G extends Record<string, LambderApiGuard<any, any, any>> = {}> {
30
+ export declare class LambderMockApp<C extends LambderApiContractShape, S = any, G extends Record<string, LambderApiGuard<any, any, any>> = {}, TDerived extends boolean = false> {
29
31
  readonly apiVersion: string | null;
30
32
  /** The memory stores, for assertions and reset; null for a subsystem that is off or backed by a store of yours. */
31
33
  readonly sessionStore: LambderMemorySessionStore<S> | null;
@@ -61,9 +63,11 @@ export declare class LambderMockApp<C extends LambderApiContractShape, S = any,
61
63
  private readonly recorder;
62
64
  /** Registered entries and the overrides over them (see LambderMockEntryRegistry). */
63
65
  private readonly registry;
66
+ /** The server's declared options per API, when create() was given the generated table; the entries' declarations come from here. */
67
+ private readonly apiOptions;
64
68
  /** The jars the runtime owns and what it planted in document.cookie (see LambderMockBrowserCookies). */
65
69
  private readonly browserCookies;
66
- constructor(options: LambderMockAppOptions<C, S, G>);
70
+ constructor(options: LambderMockAppOptions<C, S, G, LambderMockRateLimitPolicies<S>, boolean | LambderMockIdempotencyOptions<S>, {}, Record<string, LambderApiOptionEntry> | undefined>);
67
71
  /**
68
72
  * The registration-time checks every entry goes through, mocked or not:
69
73
  * the ones the server runs on a definition, and the mock's own "a session
@@ -75,11 +79,21 @@ export declare class LambderMockApp<C extends LambderApiContractShape, S = any,
75
79
  * option, for a mistake fixed by one option at create().
76
80
  */
77
81
  private assertEntryRegistration;
82
+ /**
83
+ * The table's entry for an endpoint, when create() was given one: null
84
+ * without a table, and a throw for a name the table does not hold or an
85
+ * entry registered under the other mode. The compiler already refuses
86
+ * both against the contract; this is where a stale table meets a caller
87
+ * the compiler did not see.
88
+ */
89
+ private declaredOptionsOf;
90
+ /** The mode the apiOptions table gives a name, or null without a table or for a name it does not hold. */
91
+ private declaredModeOf;
78
92
  private buildEntry;
79
93
  /** A mock for a public endpoint: a handler, or the handler with the endpoint's declarations restated. */
80
- publicApi<K extends LambderMockPublicNames<C>, TInputSchema extends z.ZodType = z.ZodType>(name: K, entry: LambderMockEntryInput<C, K, S, G, TInputSchema>): LambderMockEntry<C, K>;
94
+ publicApi<K extends LambderMockPublicNames<C>, TInputSchema extends z.ZodType = z.ZodType>(name: K, entry: LambderMockEntryInput<C, K, S, G, TInputSchema, TDerived>): LambderMockEntry<C, K>;
81
95
  /** A mock for a session endpoint: the pipeline fetches the session before the handler runs, and refuses without one. */
82
- sessionApi<K extends LambderMockSessionNames<C>, TInputSchema extends z.ZodType = z.ZodType>(name: K, entry: LambderMockEntryInput<C, K, S, G, TInputSchema>): LambderMockEntry<C, K>;
96
+ sessionApi<K extends LambderMockSessionNames<C>, TInputSchema extends z.ZodType = z.ZodType>(name: K, entry: LambderMockEntryInput<C, K, S, G, TInputSchema, TDerived>): LambderMockEntry<C, K>;
83
97
  /**
84
98
  * A public endpoint deliberately left without a mock; a call answers the
85
99
  * notMocked refusal carrying the reason.
@@ -111,13 +125,15 @@ export declare class LambderMockApp<C extends LambderApiContractShape, S = any,
111
125
  * still refused, and an entry registered later (registerPartial, or a
112
126
  * second register) takes its endpoint back from the rest.
113
127
  *
114
- * What it cannot do is the session read. The mode of an unregistered name
115
- * is not knowable at runtime (the contract is a type), so a call it
116
- * answers is processed as public: the protocol's pre-pass still runs, so
117
- * a stale client still hears versionExpired, but a signed-out call to an
118
- * unmocked session endpoint answers "not mocked" where the server answers
119
- * sessionExpired. Declare an endpoint whose signed-out path a test cares
120
- * about with sessionNotMocked instead.
128
+ * The mode of an unregistered name comes from the apiOptions table, when
129
+ * create() was given one, so an unmocked session endpoint still reads the
130
+ * session and a signed-out call answers sessionExpired as on the server.
131
+ * Without the table the mode is not knowable at runtime (the contract is
132
+ * a type), and a call it answers is processed as public: the protocol's
133
+ * pre-pass still runs, so a stale client still hears versionExpired, but
134
+ * a signed-out call to an unmocked session endpoint answers "not mocked"
135
+ * where the server answers sessionExpired. Declare an endpoint whose
136
+ * signed-out path a test cares about with sessionNotMocked there.
121
137
  */
122
138
  restNotMocked(reason: string): LambderMockRestEntry;
123
139
  private buildNotMockedEntry;
@@ -165,10 +181,11 @@ export declare class LambderMockApp<C extends LambderApiContractShape, S = any,
165
181
  /**
166
182
  * The entry that answers a name nothing registered, when register() was
167
183
  * given a rest entry: the notMocked refusal carrying its reason, run
168
- * through the pipeline as a public endpoint, since the mode of an
169
- * unregistered name cannot be recovered at runtime. Everything before
170
- * dispatch still runs (the signature gate, the payload restore); the
171
- * missing session read is the fidelity limit restNotMocked documents.
184
+ * through the pipeline under the mode the apiOptions table gives the
185
+ * name, and as a public endpoint where there is no table to say (the
186
+ * mode of an unregistered name cannot otherwise be recovered at runtime).
187
+ * Everything before dispatch still runs (the signature gate, the payload
188
+ * restore, and for a session endpoint the session read).
172
189
  */
173
190
  private restNotMockedEntry;
174
191
  /** The next call to the endpoint fails this way; several calls queue in order. */
@@ -332,5 +349,5 @@ export declare const initLambderMock: <C extends LambderApiContractShape, S = an
332
349
  * dropped. `I` exists for the same reason `P` does, and
333
350
  * LambderMockSurplusKeys puts the error back on the key.
334
351
  */
335
- 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>>(options: LambderMockAppOptions<C, S, G, P, I>): LambderMockApp<C, S, G>;
352
+ 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>;
336
353
  };