lambder 7.3.1 → 8.1.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 (237) hide show
  1. package/CHANGELOG.md +1047 -3
  2. package/README.md +46 -21
  3. package/dist/api/LambderApiAnswer.d.ts +18 -22
  4. package/dist/api/LambderApiAnswer.js +6 -7
  5. package/dist/api/LambderApiCallContext.d.ts +21 -8
  6. package/dist/api/LambderApiCallContext.js +22 -4
  7. package/dist/api/LambderApiDefinition.d.ts +4 -3
  8. package/dist/api/LambderApiEnvelope.d.ts +14 -9
  9. package/dist/api/LambderApiEnvelope.js +33 -34
  10. package/dist/api/LambderApiGuards.d.ts +78 -51
  11. package/dist/api/LambderApiGuards.js +34 -36
  12. package/dist/api/LambderApiIdempotency.d.ts +68 -62
  13. package/dist/api/LambderApiIdempotency.js +214 -151
  14. package/dist/api/LambderApiOutputValidationError.d.ts +32 -0
  15. package/dist/api/LambderApiOutputValidationError.js +50 -0
  16. package/dist/api/LambderApiPipeline.d.ts +47 -38
  17. package/dist/api/LambderApiPipeline.js +122 -63
  18. package/dist/api/LambderApiRateLimits.d.ts +201 -54
  19. package/dist/api/LambderApiRateLimits.js +185 -108
  20. package/dist/api/LambderApiRequest.d.ts +27 -21
  21. package/dist/api/LambderApiRequest.js +26 -19
  22. package/dist/api/LambderApiSignature.d.ts +12 -15
  23. package/dist/api/LambderApiSignature.js +28 -51
  24. package/dist/api/LambderApiValidationRefusal.d.ts +9 -9
  25. package/dist/api/LambderApiValidationRefusal.js +10 -10
  26. package/dist/build/ContractTypePrinter.d.ts +85 -0
  27. package/dist/build/ContractTypePrinter.js +402 -0
  28. package/dist/build/freshProcessVerifier.d.ts +13 -0
  29. package/dist/build/freshProcessVerifier.js +19 -0
  30. package/dist/build/moduleLocation.d.ts +11 -0
  31. package/dist/build/moduleLocation.js +6 -0
  32. package/dist/build/writeApiContract.d.ts +78 -0
  33. package/dist/build/writeApiContract.js +302 -0
  34. package/dist/build/writeApiSignatures.d.ts +114 -0
  35. package/dist/build/writeApiSignatures.js +217 -0
  36. package/dist/build/writeFileAtomically.d.ts +8 -0
  37. package/dist/build/writeFileAtomically.js +22 -0
  38. package/dist/build.d.ts +14 -0
  39. package/dist/build.js +11 -0
  40. package/dist/client/LambderCaller.d.ts +13 -44
  41. package/dist/client/LambderCaller.js +77 -84
  42. package/dist/client/LambderReloadLoopBreaker.d.ts +56 -26
  43. package/dist/client/LambderReloadLoopBreaker.js +90 -46
  44. package/dist/client/LambderUploadRunner.d.ts +96 -0
  45. package/dist/client/LambderUploadRunner.js +234 -0
  46. package/dist/client/lambderFetchTransport.d.ts +4 -1
  47. package/dist/client/lambderFetchTransport.js +52 -28
  48. package/dist/client.d.ts +9 -3
  49. package/dist/client.js +6 -1
  50. package/dist/core/Lambder.d.ts +143 -79
  51. package/dist/core/Lambder.js +350 -231
  52. package/dist/core/LambderContext.d.ts +82 -15
  53. package/dist/core/LambderContext.js +107 -20
  54. package/dist/core/LambderCors.d.ts +21 -3
  55. package/dist/core/LambderCors.js +35 -16
  56. package/dist/core/LambderCrashHandling.d.ts +40 -0
  57. package/dist/core/LambderCrashHandling.js +97 -0
  58. package/dist/core/LambderCreateOptions.d.ts +151 -75
  59. package/dist/core/LambderCreateOptions.js +16 -23
  60. package/dist/core/LambderFiles.d.ts +21 -7
  61. package/dist/core/LambderFiles.js +62 -34
  62. package/dist/core/LambderIndexHtml.js +12 -11
  63. package/dist/core/LambderPolicyBuilders.d.ts +17 -5
  64. package/dist/core/LambderPolicyBuilders.js +17 -5
  65. package/dist/core/LambderPublicFiles.d.ts +11 -5
  66. package/dist/core/LambderPublicFiles.js +32 -4
  67. package/dist/core/LambderRequestPath.d.ts +43 -0
  68. package/dist/core/LambderRequestPath.js +63 -0
  69. package/dist/core/LambderResponse.d.ts +26 -5
  70. package/dist/core/LambderResponse.js +157 -70
  71. package/dist/core/LambderResponseBuilder.d.ts +49 -4
  72. package/dist/core/LambderResponseBuilder.js +64 -3
  73. package/dist/core/LambderRouting.d.ts +2 -3
  74. package/dist/core/LambderRouting.js +22 -7
  75. package/dist/core/LambderTemplatingEngine.js +211 -32
  76. package/dist/index.d.ts +25 -8
  77. package/dist/index.js +13 -4
  78. package/dist/invoke/LambderInvokeCaller.d.ts +37 -42
  79. package/dist/invoke/LambderInvokeCaller.js +76 -66
  80. package/dist/invoke/LambderInvokeOutcome.d.ts +27 -26
  81. package/dist/invoke/LambderInvokeOutcome.js +9 -22
  82. package/dist/invoke/LambderLambdaEvent.d.ts +29 -9
  83. package/dist/invoke/LambderLambdaEvent.js +40 -22
  84. package/dist/invoke/lambderHandlerTransport.d.ts +9 -10
  85. package/dist/invoke/lambderHandlerTransport.js +15 -18
  86. package/dist/mock/LambderMockApp.d.ts +67 -83
  87. package/dist/mock/LambderMockApp.js +167 -153
  88. package/dist/mock/LambderMockBrowserCookies.d.ts +24 -28
  89. package/dist/mock/LambderMockBrowserCookies.js +24 -28
  90. package/dist/mock/LambderMockCallRecorder.d.ts +15 -22
  91. package/dist/mock/LambderMockCallRecorder.js +19 -28
  92. package/dist/mock/LambderMockCreateOptions.d.ts +42 -24
  93. package/dist/mock/LambderMockEntryRegistry.d.ts +11 -12
  94. package/dist/mock/LambderMockEntryRegistry.js +24 -29
  95. package/dist/mock/LambderMockFailureInjector.d.ts +3 -6
  96. package/dist/mock/LambderMockFailureInjector.js +3 -6
  97. package/dist/mock/LambderMockTypes.d.ts +78 -108
  98. package/dist/mock/lambderMockInvokeTransport.d.ts +11 -13
  99. package/dist/mock/lambderMockInvokeTransport.js +11 -10
  100. package/dist/mock/lambderMockMswHandler.d.ts +43 -33
  101. package/dist/mock/lambderMockMswHandler.js +50 -39
  102. package/dist/mock/lambderMockUploadMswHandler.d.ts +26 -0
  103. package/dist/mock/lambderMockUploadMswHandler.js +28 -0
  104. package/dist/mock.d.ts +4 -1
  105. package/dist/mock.js +6 -3
  106. package/dist/session/LambderSessionController.d.ts +108 -89
  107. package/dist/session/LambderSessionController.js +187 -168
  108. package/dist/session/LambderSessionCrypto.d.ts +16 -7
  109. package/dist/session/LambderSessionCrypto.js +26 -12
  110. package/dist/session/LambderSessionManager.d.ts +124 -46
  111. package/dist/session/LambderSessionManager.js +262 -137
  112. package/dist/shared/LambderHtml.d.ts +42 -3
  113. package/dist/shared/LambderHtml.js +127 -7
  114. package/dist/shared/LambderHtmlPositions.d.ts +173 -0
  115. package/dist/shared/LambderHtmlPositions.js +652 -0
  116. package/dist/shared/LambderI18n.d.ts +10 -11
  117. package/dist/shared/LambderI18n.js +33 -21
  118. package/dist/shared/contracts/LambderCache.d.ts +66 -0
  119. package/dist/shared/contracts/LambderCache.js +11 -0
  120. package/dist/shared/contracts/LambderFileSource.d.ts +6 -6
  121. package/dist/shared/contracts/LambderFileSource.js +5 -8
  122. package/dist/shared/contracts/LambderIdempotencyStore.d.ts +51 -22
  123. package/dist/shared/contracts/LambderIdempotencyStore.js +4 -5
  124. package/dist/shared/contracts/LambderRateLimiter.d.ts +27 -15
  125. package/dist/shared/contracts/LambderRateLimiter.js +4 -5
  126. package/dist/shared/contracts/LambderSessionStore.d.ts +65 -26
  127. package/dist/shared/contracts/LambderSessionStore.js +5 -6
  128. package/dist/shared/contracts/LambderUploadBucket.d.ts +154 -0
  129. package/dist/shared/contracts/LambderUploadBucket.js +74 -0
  130. package/dist/shared/transport/LambderApiTransport.d.ts +27 -27
  131. package/dist/shared/transport/LambderApiTransport.js +7 -7
  132. package/dist/shared/transport/LambderCookieJar.d.ts +28 -35
  133. package/dist/shared/transport/LambderCookieJar.js +54 -66
  134. package/dist/shared/transport/lambderCookieJarTransport.d.ts +11 -13
  135. package/dist/shared/transport/lambderCookieJarTransport.js +24 -23
  136. package/dist/shared/util/LambderCallAbort.d.ts +5 -5
  137. package/dist/shared/util/LambderCallAbort.js +5 -5
  138. package/dist/shared/util/LambderClientIp.d.ts +27 -11
  139. package/dist/shared/util/LambderClientIp.js +96 -13
  140. package/dist/shared/util/LambderContentDisposition.d.ts +10 -0
  141. package/dist/shared/util/LambderContentDisposition.js +13 -0
  142. package/dist/shared/util/LambderExpiringMap.d.ts +35 -49
  143. package/dist/shared/util/LambderExpiringMap.js +41 -57
  144. package/dist/shared/util/LambderNodeModules.js +6 -7
  145. package/dist/shared/util/LambderOptionChecks.d.ts +4 -4
  146. package/dist/shared/util/LambderOptionChecks.js +4 -4
  147. package/dist/shared/util/LambderResponseBrand.d.ts +5 -5
  148. package/dist/shared/util/LambderResponseBrand.js +5 -5
  149. package/dist/shared/util/LambderTextDigest.d.ts +7 -5
  150. package/dist/shared/util/LambderTextDigest.js +11 -5
  151. package/dist/shared/util/LambderTypeUtilities.d.ts +7 -8
  152. package/dist/shared/util/LambderTypeUtilities.js +3 -3
  153. package/dist/shared/util/boundKeyField.d.ts +20 -0
  154. package/dist/shared/util/boundKeyField.js +34 -0
  155. package/dist/shared/util/canonicalJson.d.ts +11 -0
  156. package/dist/shared/util/canonicalJson.js +28 -0
  157. package/dist/shared/util/joinKeyFields.d.ts +20 -0
  158. package/dist/shared/util/joinKeyFields.js +22 -0
  159. package/dist/shared/wire/LambderAnswerHeaders.d.ts +12 -16
  160. package/dist/shared/wire/LambderAnswerHeaders.js +12 -16
  161. package/dist/shared/wire/LambderApiContract.d.ts +98 -53
  162. package/dist/shared/wire/LambderApiOutcome.d.ts +43 -31
  163. package/dist/shared/wire/LambderApiOutcome.js +48 -23
  164. package/dist/shared/wire/LambderApiRefusal.d.ts +45 -27
  165. package/dist/shared/wire/LambderApiRefusal.js +42 -7
  166. package/dist/shared/wire/LambderApiSignature.d.ts +18 -22
  167. package/dist/shared/wire/LambderApiSignature.js +16 -19
  168. package/dist/shared/wire/LambderCallOptions.d.ts +38 -47
  169. package/dist/shared/wire/LambderCallOptions.js +9 -11
  170. package/dist/shared/wire/LambderCompressionCodec.d.ts +29 -34
  171. package/dist/shared/wire/LambderCompressionCodec.js +31 -36
  172. package/dist/shared/wire/LambderCompressionOption.d.ts +9 -9
  173. package/dist/shared/wire/LambderCompressionOption.js +9 -9
  174. package/dist/shared/wire/LambderCrashDetail.d.ts +12 -15
  175. package/dist/shared/wire/LambderCrashDetail.js +12 -15
  176. package/dist/shared/wire/LambderDefaultApiPath.d.ts +6 -0
  177. package/dist/shared/wire/LambderDefaultApiPath.js +6 -0
  178. package/dist/shared/wire/LambderHttpStatus.d.ts +6 -7
  179. package/dist/shared/wire/LambderIdempotencyKeyScope.d.ts +89 -0
  180. package/dist/shared/wire/LambderIdempotencyKeyScope.js +146 -0
  181. package/dist/shared/wire/LambderInvokeApiId.d.ts +27 -0
  182. package/dist/shared/wire/LambderInvokeApiId.js +27 -0
  183. package/dist/shared/wire/LambderOutcomeAssertions.d.ts +6 -7
  184. package/dist/shared/wire/LambderOutcomeAssertions.js +6 -7
  185. package/dist/shared/wire/LambderRequestPayload.d.ts +18 -20
  186. package/dist/shared/wire/LambderRequestPayload.js +4 -6
  187. package/dist/shared/wire/LambderUploadObjectFields.d.ts +10 -0
  188. package/dist/shared/wire/LambderUploadObjectFields.js +24 -0
  189. package/dist/shared/wire/LambderUploadRefusal.d.ts +9 -0
  190. package/dist/shared/wire/LambderUploadRefusal.js +18 -0
  191. package/dist/shared/wire/LambderUploadSchemas.d.ts +12 -0
  192. package/dist/shared/wire/LambderUploadSchemas.js +30 -0
  193. package/dist/stores/LambderCacheFiller.d.ts +48 -0
  194. package/dist/stores/LambderCacheFiller.js +119 -0
  195. package/dist/stores/LambderCacheKeys.d.ts +26 -0
  196. package/dist/stores/LambderCacheKeys.js +54 -0
  197. package/dist/stores/LambderCacheValues.d.ts +45 -0
  198. package/dist/stores/LambderCacheValues.js +74 -0
  199. package/dist/stores/LambderDdbCache.d.ts +121 -56
  200. package/dist/stores/LambderDdbCache.js +528 -225
  201. package/dist/stores/LambderDdbIdempotencyStore.d.ts +33 -22
  202. package/dist/stores/LambderDdbIdempotencyStore.js +75 -50
  203. package/dist/stores/LambderDdbRateLimiter.d.ts +76 -20
  204. package/dist/stores/LambderDdbRateLimiter.js +151 -39
  205. package/dist/stores/LambderDdbSdk.d.ts +43 -31
  206. package/dist/stores/LambderDdbSdk.js +80 -38
  207. package/dist/stores/LambderDdbSessionStore.d.ts +27 -14
  208. package/dist/stores/LambderDdbSessionStore.js +119 -47
  209. package/dist/stores/LambderHttpFileSource.d.ts +15 -6
  210. package/dist/stores/LambderHttpFileSource.js +15 -13
  211. package/dist/stores/LambderMemoryCache.d.ts +49 -0
  212. package/dist/stores/LambderMemoryCache.js +113 -0
  213. package/dist/stores/LambderMemoryIdempotencyStore.d.ts +13 -12
  214. package/dist/stores/LambderMemoryIdempotencyStore.js +31 -30
  215. package/dist/stores/LambderMemoryRateLimiter.d.ts +8 -9
  216. package/dist/stores/LambderMemoryRateLimiter.js +14 -13
  217. package/dist/stores/LambderMemorySessionStore.d.ts +14 -11
  218. package/dist/stores/LambderMemorySessionStore.js +38 -19
  219. package/dist/stores/LambderMemoryUploadBucket.d.ts +99 -0
  220. package/dist/stores/LambderMemoryUploadBucket.js +219 -0
  221. package/dist/stores/LambderS3FileSource.d.ts +21 -6
  222. package/dist/stores/LambderS3FileSource.js +12 -7
  223. package/dist/stores/LambderS3UploadBucket.d.ts +73 -0
  224. package/dist/stores/LambderS3UploadBucket.js +144 -0
  225. package/dist/stores/LambderSdkInstallHint.d.ts +11 -0
  226. package/dist/stores/LambderSdkInstallHint.js +14 -0
  227. package/dist/testing/LambderTestApp.d.ts +23 -25
  228. package/dist/testing/LambderTestApp.js +22 -24
  229. package/dist/testing/LambderTestVisitor.d.ts +10 -12
  230. package/dist/testing/LambderTestVisitor.js +15 -15
  231. package/dist/testing.d.ts +3 -0
  232. package/dist/testing.js +2 -0
  233. package/package.json +26 -3
  234. package/dist/api/LambderApiPolicyEngine.d.ts +0 -47
  235. package/dist/api/LambderApiPolicyEngine.js +0 -85
  236. package/dist/shared/util/LambderKeyFields.d.ts +0 -32
  237. package/dist/shared/util/LambderKeyFields.js +0 -34
@@ -8,7 +8,7 @@
8
8
  import type { z } from "zod";
9
9
  import type { LambderApiMode, LambderContractGuardInput, LambderContractGuardInputNames, LambderContractGuardInputsOf, LambderContractGuardNames, LambderContractGuardsOf, LambderContractIdempotencyOf, LambderContractKeysWithMode, LambderContractMode, LambderContractRateLimitOf } from "../shared/wire/LambderApiContract.js";
10
10
  import type { LambderApiGuard, LambderGuardDataOf, LambderGuardMetaMap } from "../api/LambderApiGuards.js";
11
- import type { LambderApiRateLimitPolicyConfig } from "../api/LambderApiRateLimits.js";
11
+ import type { LambderApiRateLimitPolicyConfig, LambderContextRateLimit, LambderContextRateLimitCheck } from "../api/LambderApiRateLimits.js";
12
12
  import type { LambderApiCallContext } from "../api/LambderApiCallContext.js";
13
13
  import type { LambderApiRequest } from "../api/LambderApiRequest.js";
14
14
  import type { LambderHttpStatusCode } from "../shared/wire/LambderHttpStatus.js";
@@ -28,21 +28,16 @@ export type LambderMockOutputOf<C, K extends keyof C> = C[K] extends {
28
28
  * `never`, so a typo is an error on the key itself.
29
29
  *
30
30
  * Needed once per nesting level: inferring a generic from an object literal
31
- * (`const G`, `const P` on create()) switches excess-property checking off for
32
- * the WHOLE literal, nested objects included, so `idempotency: { failOpn:
33
- * false }` compiled, was dropped in silence, and left the runtime on the
34
- * default. Intersected into a nested position rather than applied to the
35
- * option type as a whole, because the type variable has to stay naked
36
- * somewhere for the literal to be inferred from at all.
31
+ * (`const G`, `const P` on create()) switches excess-property checking off
32
+ * for the WHOLE literal, so `idempotency: { failOpn: false }` would compile
33
+ * and be dropped. Intersected into a nested position because the type
34
+ * variable must stay naked somewhere for the literal to be inferred at all.
35
+ * `unknown` for a non-object (`idempotency: true`), since mapping Boolean's
36
+ * prototype keys to `never` would refuse the boolean form.
37
37
  *
38
- * `unknown` for anything that is not an object (`idempotency: true`), which
39
- * intersects away: `keyof boolean` is Boolean's own prototype members, and
40
- * mapping those to `never` would refuse the boolean form outright.
41
- *
42
- * The server's create() has the same construction in `LambderNoExtraKeys`
43
- * (core/Lambder.ts). It is written twice on purpose: `lambder/mock` is
44
- * browser-safe by its import graph, and reaching into core/ for a type alias
45
- * would pull the server's whole type graph (aws-lambda included) back into it.
38
+ * Duplicates `LambderNoExtraKeys` (core/LambderCreateOptions.ts) on purpose: importing it
39
+ * would pull the server's type graph (aws-lambda included) into the
40
+ * browser-safe `lambder/mock`.
46
41
  */
47
42
  export type LambderMockSurplusKeys<TOptions, TShape> = [
48
43
  TOptions
@@ -55,8 +50,16 @@ export type LambderMockSurplusKeys<TOptions, TShape> = [
55
50
  export type LambderMockCallContext<S = any> = LambderApiCallContext<S> & {
56
51
  apiName: string;
57
52
  request: LambderApiRequest;
58
- /** Create, rotate, refresh and end sessions, exactly as a server handler does through getSessionController(ctx). */
59
- sessions: LambderSessionController<S>;
53
+ /** Create, rotate, refresh and end sessions, exactly as a server handler does through its own ctx.sessionController. */
54
+ sessionController: LambderSessionController<S>;
55
+ /**
56
+ * Charges a named policy from code, as a server handler does through its
57
+ * own ctx.rateLimit: a 429 refusal when it is over. The name is any
58
+ * string and the key optional here; the charge checks both.
59
+ */
60
+ rateLimit: LambderContextRateLimit<Record<string, LambderApiRateLimitPolicyConfig>>;
61
+ /** The same count, answered instead of thrown, as ctx.isRateLimited on the server. */
62
+ isRateLimited: LambderContextRateLimitCheck<Record<string, LambderApiRateLimitPolicyConfig>>;
60
63
  signal: AbortSignal;
61
64
  /**
62
65
  * The envelope fields that travel beside the payload, the mock's stand-in
@@ -88,18 +91,17 @@ export type LambderMockContext<C, K extends keyof C, S, G> = Omit<LambderMockCal
88
91
  };
89
92
  /**
90
93
  * The guard map a mock app must declare: one guard per name any endpoint of
91
- * the contract declares, and for every guard the contract knows in
92
- * guardInput mode, a `guardInput` schema whose output is what the server
93
- * inferred. A guard a public endpoint names may not require a session, since
94
- * an entry naming one cannot be registered. A missing name, a schema that
95
- * parses to something else, or a session guard where the contract has a
96
- * public endpoint fails at the `guards` option.
94
+ * the contract declares, plus, for each guardInput-mode guard, a `guardInput`
95
+ * schema whose input is what the contract says a client sends. A guard a
96
+ * public endpoint names may not require a session. A missing name, a schema
97
+ * that parses to something else, or such a session guard fails at the
98
+ * `guards` option.
97
99
  */
98
100
  export type LambderMockGuards<C, S = any> = {
99
101
  [N in LambderContractGuardNames<C>]: LambderApiGuard<any, any, any, LambderMockCallContext<S>, LambderMockSessionCallContext<S>>;
100
102
  } & {
101
103
  [N in LambderContractGuardInputNames<C>]: {
102
- guardInput: z.ZodType<LambderContractGuardInput<C, N>, any>;
104
+ guardInput: z.ZodType<unknown, LambderContractGuardInput<C, N>>;
103
105
  };
104
106
  } & {
105
107
  [N in LambderContractGuardNames<C, "public">]: {
@@ -110,14 +112,10 @@ export type LambderMockHandler<C, K extends keyof C, S, G> = (ctx: LambderMockCo
110
112
  /**
111
113
  * The guards field of an entry: required whenever the contract declares any
112
114
  * guard for the endpoint, and type-equal to the server's own declaration.
113
- *
114
- * Keyed on the guards themselves rather than on guardInputs, because the
115
- * restatement is the only thing that tells the runtime which guards to run:
116
- * a guard the entry leaves out simply does not run, and the mock answers 200
117
- * where the server answers notAuthorized. The guards that carry no
118
- * guardInput are exactly the "may this role call it" ones (session-only,
119
- * param-only), so keying on guardInputs made the authorization checks the
120
- * droppable half.
115
+ * The restatement is what tells the runtime which guards to run, so a guard
116
+ * left out would let the mock answer 200 where the server answers
117
+ * notAuthorized. Keyed on the guards rather than on guardInputs, since the
118
+ * guards without a guardInput are the "may this role call it" ones.
121
119
  */
122
120
  type LambderMockGuardsField<C, K extends keyof C> = [
123
121
  LambderContractGuardsOf<C, K>
@@ -128,10 +126,9 @@ type LambderMockGuardsField<C, K extends keyof C> = [
128
126
  };
129
127
  /**
130
128
  * The rate-limit field of an entry: required whenever the contract declares
131
- * one, absent otherwise. Same reasoning as LambderMockGuardsField, and the
132
- * argument transfers word for word: the restatement is the only thing that
133
- * tells the runtime to apply the limit, so an entry that leaves it out
134
- * answers 200 where the server answers 429.
129
+ * one, absent otherwise. As with LambderMockGuardsField, the restatement is
130
+ * the only thing that tells the runtime to apply the limit, so an entry that
131
+ * left it out would answer 200 where the server answers 429.
135
132
  */
136
133
  type LambderMockRateLimitField<C, K extends keyof C> = [
137
134
  LambderContractRateLimitOf<C, K>
@@ -157,56 +154,47 @@ type LambderMockIdempotencyField<C, K extends keyof C> = [
157
154
  * What override() hands back: call restore() to put the original handler
158
155
  * back.
159
156
  *
160
- * Restore and nothing else. The handle also carried a `[Symbol.dispose]`
161
- * member, for `using`, and that member is declared in `lib: ESNext` alone: a
162
- * consumer on `lib: ES2022` (a Vue app's own setting, and the only consumer
163
- * this package has) got TS2550 "Property 'dispose' does not exist on type
164
- * 'SymbolConstructor'" out of the published .d.ts, from importing the entry at
165
- * all, whenever skipLibCheck was off. A scoped override is a try/finally,
166
- * which needs no lib.
157
+ * Deliberately no `[Symbol.dispose]` for `using`: it is declared only in
158
+ * `lib: ESNext`, so a consumer on `lib: ES2022` with skipLibCheck off would
159
+ * get TS2550 from the published .d.ts just by importing the entry. A scoped
160
+ * override is a try/finally, which needs no lib.
167
161
  */
168
162
  export type LambderMockOverride = {
169
163
  restore(): void;
170
164
  };
171
165
  /**
172
- * What an entry's `input` schema must parse to: the endpoint's contract
173
- * input, in both directions.
174
- *
175
- * One direction is not enough, and the missing one is the direction the drift
176
- * actually travels in. `z.ZodType<Input>` is covariant in its output, so a
177
- * schema parsing to a SUBTYPE of the contract input passed: an extra required
178
- * field, or a literal where the contract says string. Such a schema refuses
179
- * payloads the server accepts, and the mock then answers 422 to a call that
180
- * works against the real backend, which is the exact failure the schema was
181
- * added to reproduce, produced by the thing added to reproduce it. Requiring
182
- * assignability the other way as well makes the schema's output the contract's
183
- * input and nothing else.
166
+ * What an entry's `input` schema must take and give: it takes exactly the
167
+ * endpoint's contract input (the form a client posts), in both directions,
168
+ * and what it parses to still reads as that input, which is how the mock's
169
+ * handler is typed. `z.ZodType<Input>` alone is covariant, so a schema
170
+ * taking a SUBTYPE (an extra required field, a literal where the contract
171
+ * says string) would pass, and the mock would then answer 422 to payloads
172
+ * the server accepts. A default passes (the parsed field is simply there);
173
+ * a transform that changes a field's type does not, since the handler would
174
+ * read it as the posted type.
184
175
  *
185
176
  * Intersected onto the schema rather than mapped to `never`, so the compiler
186
- * quotes the reason at the `input` property. `z.any()` passes in both
187
- * directions, which is the one deliberate escape hatch; `z.unknown()` does
188
- * not.
177
+ * quotes the reason at the `input` property. `z.any()` passes both ways as
178
+ * the one deliberate escape hatch; `z.unknown()` does not.
189
179
  */
190
180
  type LambderMockInputPin<C, K extends keyof C, TSchema extends z.ZodType> = [
191
- z.output<TSchema>
192
- ] extends [LambderMockInputOf<C, K>] ? ([LambderMockInputOf<C, K>] extends [z.output<TSchema>] ? unknown : {
193
- "LambderMockApp: this input schema parses to less than the endpoint takes (an extra required field, or a narrower type), so the mock would answer 422 to payloads the server accepts": LambderMockInputOf<C, K>;
181
+ z.input<TSchema>
182
+ ] extends [LambderMockInputOf<C, K>] ? ([LambderMockInputOf<C, K>] extends [z.input<TSchema>] ? ([z.output<TSchema>] extends [LambderMockInputOf<C, K>] ? unknown : {
183
+ "LambderMockApp: this input schema transforms the payload into a type the mock handler is not typed for (it reads the payload as the endpoint's contract input)": LambderMockInputOf<C, K>;
194
184
  }) : {
195
- "LambderMockApp: this input schema parses to something else than the endpoint's contract input": LambderMockInputOf<C, K>;
185
+ "LambderMockApp: this input schema takes less than the endpoint takes (an extra required field, or a narrower type), so the mock would answer 422 to payloads the server accepts": LambderMockInputOf<C, K>;
186
+ }) : {
187
+ "LambderMockApp: this input schema takes something else than the endpoint's contract input": LambderMockInputOf<C, K>;
196
188
  };
197
189
  /** An entry written in full: the declarations restated and pinned, plus the handler. */
198
190
  export type LambderMockEntryOptions<C, K extends keyof C, S, G, TInputSchema extends z.ZodType = z.ZodType> = LambderMockGuardsField<C, K> & LambderMockRateLimitField<C, K> & LambderMockIdempotencyField<C, K> & {
199
191
  /**
200
- * A schema to validate the posted payload against, which makes the mock
201
- * answer 422 exactly as the server would. Optional, and deliberately the
202
- * mock's own: the contract is a type, so the server's schemas do not exist
203
- * at runtime on this side, and importing them would put the whole endpoint
204
- * surface into the browser bundle. Restate the shape for the endpoints
205
- * whose rejection path a test needs to exercise; leave it off and a bad
206
- * payload reaches the handler, as it does today.
207
- *
208
- * Pinned to the contract's input all the same: the schema is the mock's,
209
- * but what it parses to is the server's, in both directions (see
192
+ * A schema to validate the posted payload against, so the mock answers
193
+ * 422 exactly as the server would. Optional, and the mock's own: the
194
+ * contract is type-only, and importing the server's schemas would put the
195
+ * whole endpoint surface into the browser bundle. Restate it for endpoints
196
+ * whose rejection path a test exercises; without it a bad payload reaches
197
+ * the handler. What it parses to is pinned to the contract's input (see
210
198
  * LambderMockInputPin).
211
199
  */
212
200
  input?: TInputSchema & LambderMockInputPin<C, K, TInputSchema>;
@@ -215,14 +203,9 @@ export type LambderMockEntryOptions<C, K extends keyof C, S, G, TInputSchema ext
215
203
  /**
216
204
  * What publicApi/sessionApi accept: a bare handler only for an endpoint the
217
205
  * contract declares nothing for, the full options otherwise, so the form that
218
- * cannot carry a restatement is unavailable exactly where one is owed.
219
- *
220
- * All three declarations, not guards alone. The three fields above make each
221
- * restatement required INSIDE the options form, and the bare handler is the
222
- * form that has no fields at all, so keying this on guards left every
223
- * guardless endpoint free to drop its rate limit and its idempotency again:
224
- * the handler ran twice for one key and a perMin limit never answered 429,
225
- * which is the whole of what those two fields exist to prevent.
206
+ * cannot carry a restatement is unavailable exactly where one is owed. Keyed
207
+ * on all three declarations: keyed on guards alone, a guardless endpoint
208
+ * could drop its rate limit and idempotency through the bare form.
226
209
  */
227
210
  export type LambderMockEntryInput<C, K extends keyof C, S, G, TInputSchema extends z.ZodType = z.ZodType> = [
228
211
  LambderContractGuardsOf<C, K> | LambderContractRateLimitOf<C, K> | LambderContractIdempotencyOf<C, K>
@@ -244,13 +227,9 @@ export type LambderMockSlice<C, K extends keyof C & string> = {
244
227
  };
245
228
  /**
246
229
  * What restNotMocked(reason) hands register(): "every endpoint the slices
247
- * beside me leave out is not mocked, for this reason".
248
- *
249
- * A one-field object rather than a slice, because it names no endpoint: it
250
- * answers the names nothing else claimed, and which those are is only known
251
- * once the other arguments have been read. Every check below filters it out
252
- * before it reads a name, so the field it does carry is neither a stray nor
253
- * half of a duplicate; the one clause it changes is completeness.
230
+ * beside me leave out is not mocked, for this reason". A one-field object
231
+ * rather than a slice, because it names no endpoint; every check below
232
+ * filters it out before reading names, so it changes only completeness.
254
233
  */
255
234
  export type LambderMockRestEntry = {
256
235
  readonly restNotMockedReason: string;
@@ -259,15 +238,11 @@ export type LambderMockRestEntry = {
259
238
  * The names one slice holds; distributes over a union of slices.
260
239
  *
261
240
  * A slice typed with an index signature (`Record<string, LambderMockEntry>`,
262
- * or a list built in a loop) holds `string` as its key type, which would
263
- * subtract every name from the missing list and pass the completeness check
264
- * while registering almost nothing. Such a slice names nothing the compiler
265
- * can check, so it contributes nothing here and LambderMockUncheckableSlices
266
- * reports it for what it is.
267
- *
268
- * The rest entry contributes nothing either, and for the opposite reason: the
269
- * one key it carries is not an endpoint name, so reading it would report
270
- * "restNotMockedReason" as a stray, and two rest entries as a duplicate of it.
241
+ * a list built in a loop) has `string` as its key type, which would pass the
242
+ * completeness check while registering almost nothing, so it contributes
243
+ * nothing here and LambderMockUncheckableSlices reports it. The rest entry
244
+ * contributes nothing either: its one key is not an endpoint name, and
245
+ * reading it would report "restNotMockedReason" as a stray or a duplicate.
271
246
  */
272
247
  type LambderMockSliceNames<S> = S extends unknown ? (S extends LambderMockRestEntry ? never : (string extends keyof S ? never : keyof S & string)) : never;
273
248
  /**
@@ -288,10 +263,9 @@ type LambderMockHasRestEntry<Slices extends readonly unknown[]> = [
288
263
  * The endpoints register() would leave unanswered: the ones no slice covers,
289
264
  * unless a rest entry stands for them.
290
265
  *
291
- * The completeness clause and nothing else. An endpoint the rest entry answers
292
- * is still an endpoint with no mock of its own, which is what
293
- * LambderMockMissingNames says and why that one keeps its meaning; what the
294
- * rest entry changes is whether leaving it out is a mistake.
266
+ * Separate from LambderMockMissingNames, because an endpoint the rest entry
267
+ * answers still has no mock of its own; the rest entry only changes whether
268
+ * leaving it out is a mistake.
295
269
  */
296
270
  type LambderMockUncoveredNames<C, Slices extends readonly unknown[]> = LambderMockHasRestEntry<Slices> extends true ? never : LambderMockMissingNames<C, Slices>;
297
271
  /** Names the slices carry that the contract does not declare. */
@@ -300,14 +274,10 @@ export type LambderMockStrayNames<C, Slices extends readonly unknown[]> = Exclud
300
274
  export type LambderMockDuplicateNames<Slices extends readonly unknown[]> = Slices extends readonly [infer Head, ...infer Tail] ? (LambderMockSliceNames<Head> & LambderMockSliceNames<Tail[number]>) | LambderMockDuplicateNames<Tail> : never;
301
275
  /**
302
276
  * True for a slice list whose length the compiler does not know: an array
303
- * type rather than a tuple, `length: number`.
304
- *
305
- * Every check below is written over a tuple, and an array type quietly
306
- * disables all of them: the overlap and index-signature walks fall to their
307
- * `never` base case on the first step, and completeness reduces to "the
308
- * element type mentions these names", which one element satisfies as well as
309
- * twenty. `const slices = [userMocks, orderMocks]` spread into register() is
310
- * exactly that type, so the array form is refused rather than passed.
277
+ * type rather than a tuple. Every check here walks a tuple, and an array
278
+ * type quietly disables them all (completeness reduces to "the element type
279
+ * mentions these names"), so the array form, such as a spread
280
+ * `const slices = [userMocks, orderMocks]`, is refused.
311
281
  */
312
282
  type LambderMockUncountableSlices<Slices extends readonly unknown[]> = number extends Slices["length"] ? true : false;
313
283
  /**
@@ -4,14 +4,12 @@ import { type LambderApiRequest } from "../api/LambderApiRequest.js";
4
4
  * The fields of an invoke's synthesized event this transport reads, declared
5
5
  * structurally rather than imported as APIGatewayProxyEventV2.
6
6
  *
7
- * `lambder/mock` is browser-safe, and its type graph is part of that claim: a
8
- * type-only import of the invoke caller pulled `aws-lambda` and
9
- * `@aws-sdk/client-lambda` into the .d.ts graph of the mock entry, so a
10
- * frontend compiling without `skipLibCheck` and without those @types got
11
- * errors from inside node_modules for a module it never loads. Every field a
12
- * real event carries is optional here and no field is required, so a real
13
- * event is assignable and the returned function still fits
14
- * LambderInvokeTransport wherever a caller expects one.
7
+ * `lambder/mock` is browser-safe, type graph included: a type-only import of
8
+ * the invoke caller would pull `aws-lambda` and `@aws-sdk/client-lambda` into
9
+ * the mock entry's .d.ts graph, and a frontend compiling without
10
+ * `skipLibCheck` or those @types would get errors from a module it never
11
+ * loads. Every field is optional, so a real event is assignable and the
12
+ * returned function still fits LambderInvokeTransport.
15
13
  */
16
14
  export type LambderMockInvokeEvent = {
17
15
  body?: string | undefined;
@@ -37,11 +35,11 @@ export type LambderMockInvokeResult = {
37
35
  };
38
36
  };
39
37
  /**
40
- * The mock app as the callee of a LambderInvokeCaller: the invoke's
41
- * synthesized event is read the way the callee's createContext would read
42
- * it, and the answer goes back as the Lambda response object the caller
43
- * decodes. So a server test can point its typed invoke caller at a mock of
44
- * the function it depends on, with the same registry a browser test uses.
38
+ * The mock app as the callee of a LambderInvokeCaller: the synthesized event
39
+ * is read the way the callee's createContext would read it, and the answer
40
+ * goes back as the Lambda response object the caller decodes. A server test
41
+ * can then point its typed invoke caller at a mock of the function it
42
+ * depends on, with the same registry a browser test uses.
45
43
  */
46
44
  export declare const lambderMockInvokeTransport: (mockApp: {
47
45
  handleRequest(request: LambderApiRequest): Promise<LambderApiAnswer>;
@@ -1,13 +1,13 @@
1
- import { readApiEnvelope, cookieValuesByName, lowercaseHeaderNames } from "../api/LambderApiRequest.js";
1
+ import { readApiEnvelope, cookieValuesByName, isApiCallContentType, lowercaseHeaderNames } from "../api/LambderApiRequest.js";
2
2
  import { getAnswerHeader } from "../shared/wire/LambderAnswerHeaders.js";
3
3
  import { base64ToText } from "../shared/util/LambderBase64.js";
4
4
  import { normalizeClientIp } from "../shared/util/LambderClientIp.js";
5
5
  /**
6
- * The mock app as the callee of a LambderInvokeCaller: the invoke's
7
- * synthesized event is read the way the callee's createContext would read
8
- * it, and the answer goes back as the Lambda response object the caller
9
- * decodes. So a server test can point its typed invoke caller at a mock of
10
- * the function it depends on, with the same registry a browser test uses.
6
+ * The mock app as the callee of a LambderInvokeCaller: the synthesized event
7
+ * is read the way the callee's createContext would read it, and the answer
8
+ * goes back as the Lambda response object the caller decodes. A server test
9
+ * can then point its typed invoke caller at a mock of the function it
10
+ * depends on, with the same registry a browser test uses.
11
11
  */
12
12
  export const lambderMockInvokeTransport = (mockApp) => async (event, { signal }) => {
13
13
  const rawBody = event.body ?? "";
@@ -21,10 +21,11 @@ export const lambderMockInvokeTransport = (mockApp) => async (event, { signal })
21
21
  }
22
22
  const headers = lowercaseHeaderNames(event.headers);
23
23
  const cookies = cookieValuesByName(event.cookies ?? []);
24
- // The address the synthesized event carries in sourceIp, as the server's
25
- // createContext reads it; no forwarding header is trusted here either,
26
- // so a per-IP limit keys the same address under both adapters.
27
- const request = readApiEnvelope(post, {
24
+ // A POST of another type is no API call on the server either. The
25
+ // address is the one the synthesized event carries in sourceIp, as the
26
+ // server's createContext reads it; no forwarding header is trusted here
27
+ // either, so a per-IP limit keys the same address under both adapters.
28
+ const request = isApiCallContentType(headers) && readApiEnvelope(post, {
28
29
  headers, cookies,
29
30
  ip: normalizeClientIp(event.requestContext?.http?.sourceIp ?? ""),
30
31
  host: headers.host || event.requestContext?.domainName || "lambder-invoke",
@@ -1,22 +1,25 @@
1
1
  import { type LambderApiRequest } from "../api/LambderApiRequest.js";
2
2
  import type { LambderApiAnswer } from "../api/LambderApiAnswer.js";
3
3
  import { LambderCookieJar } from "../shared/transport/LambderCookieJar.js";
4
+ /** What a resolver of Lambder's adapters is: msw's, answering a Response, or `undefined` to hand the request on. */
5
+ type LambderMswResolver = (info: {
6
+ request: Request;
7
+ }) => Promise<Response | undefined>;
4
8
  /**
5
- * The parts of the msw module the adapter uses: `import * as msw from "msw"`.
9
+ * The parts of the msw module Lambder's adapters use, `import * as msw from
10
+ * "msw"`: `http.post` for the API (lambderMockMswHandler) and `http.all` for
11
+ * an upload bucket's storage (lambderMockUploadMswHandler).
6
12
  *
7
- * Written so the real package satisfies it, which is the whole point of a
8
- * structural declaration and was not true of the previous one. msw's resolver
9
- * answers a Response, or `undefined` to hand the request back (its
10
- * AsyncResponseResolverReturnType), and a resolver declared to return
11
- * `Promise<unknown>` is not assignable to that, so `http.post` did not fit
12
- * here and the handler this returned did not fit `setupWorker`. Both errors
13
- * landed on the documented five-line wiring.
13
+ * Written so the real package satisfies it. msw's resolver answers a
14
+ * Response, or `undefined` to hand the request back (its
15
+ * AsyncResponseResolverReturnType); a resolver declared to return
16
+ * `Promise<unknown>` is not assignable to that, so `http.post` would not fit
17
+ * here and the returned handler would not fit `setupWorker`.
14
18
  */
15
19
  export type LambderMswModule = {
16
20
  http: {
17
- post: (path: string, resolver: (info: {
18
- request: Request;
19
- }) => Promise<Response | undefined>) => unknown;
21
+ post: (path: string, resolver: LambderMswResolver) => unknown;
22
+ all: (path: string, resolver: LambderMswResolver) => unknown;
20
23
  };
21
24
  HttpResponse: {
22
25
  new (body?: BodyInit | null, init?: ResponseInit): Response;
@@ -43,10 +46,10 @@ export type LambderMockMswTarget = {
43
46
  /**
44
47
  * The host the runtime's cookies belong to, which this adapter's jar is
45
48
  * scoped by. The runtime's value, not the request URL's: signIn plants at
46
- * the app's cookieHost and the direct transport's jar sends from there, so
47
- * an adapter scoping by whatever host the page is served from held the
48
- * session cookies at a host it never sent them to, and every session call
49
- * behind the worker answered sessionExpired with a full jar.
49
+ * the app's cookieHost and the direct transport's jar sends from there.
50
+ * Scoped by the page's host instead, the jar would hold session cookies
51
+ * at a host it never sends them to, and every session call behind the
52
+ * worker would answer sessionExpired with a full jar.
50
53
  */
51
54
  readonly cookieHost: string;
52
55
  };
@@ -54,25 +57,31 @@ export type LambderMockMswTarget = {
54
57
  * ONE MSW request handler for the whole API path, over the mock app: the
55
58
  * opt-in that makes mocked calls appear in the browser's network panel as
56
59
  * genuine requests, with real method, status, timing and bodies. The request
57
- * is read the way the server reads it, its headers and Cookie header
58
- * included.
60
+ * is read the way the server reads it, headers included, with the cookies a
61
+ * browser would send.
59
62
  *
60
- * Session cookies are held in a jar here rather than by the browser, because
61
- * the browser will not hold them: a response a service worker synthesizes
62
- * never reaches the cookie store, and MSW's own jar comma-joins the
63
- * Set-Cookie headers before parsing them, which loses every cookie after the
64
- * first. So the answer's cookies go into the jar, the next request carries
65
- * them back, and the ones a page's scripts may see are mirrored into
66
- * document.cookie. The Set-Cookie headers still travel on the response, where
67
- * the network panel shows them.
63
+ * Session cookies are held in a jar here, because the browser will not hold
64
+ * them: a response a service worker synthesizes never reaches the cookie
65
+ * store, and MSW's own jar comma-joins the Set-Cookie headers before parsing
66
+ * them, losing every cookie after the first. So the answer's cookies go into
67
+ * the jar, the next request carries them back, and the ones a page's scripts
68
+ * may see are mirrored into document.cookie. The Set-Cookie headers still
69
+ * travel on the response, where the network panel shows them.
70
+ *
71
+ * A request's cookies are therefore the jar's and document.cookie's, never
72
+ * its Cookie header: MSW fills that from its own store, which captures the
73
+ * HttpOnly session cookie off those Set-Cookie headers and keeps it in
74
+ * localStorage across reloads. Reading it would send a second session after
75
+ * a user switch (every call answering sessionExpired), keep a cleared jar
76
+ * signed in, and put the raw token into request events.
68
77
  *
69
78
  * Lambder never depends on msw: the app installs it and passes the module
70
79
  * in. An injected network failure answers MSW's network error. A POST whose
71
80
  * body is not an API envelope is left to other handlers.
72
81
  *
73
82
  * Generic over the module so the handler keeps msw's own handler type, which
74
- * is what `setupWorker(...)` and `setupServer(...)` take. Returning `unknown`
75
- * made the documented wiring an error at the consumer.
83
+ * is what `setupWorker(...)` and `setupServer(...)` take; typed `unknown`,
84
+ * the documented wiring would be a type error at the consumer.
76
85
  */
77
86
  export declare const lambderMockMswHandler: <M extends LambderMswModule>(mockApp: LambderMockMswTarget, options: {
78
87
  msw: M;
@@ -86,14 +95,15 @@ export declare const lambderMockMswHandler: <M extends LambderMswModule>(mockApp
86
95
  * a partially mocked app runs in while its remaining endpoints still
87
96
  * come from a real backend.
88
97
  *
89
- * This and `mockApp.restNotMocked(reason)` are the two answers to the
90
- * same question, and the rest entry wins: it leaves the runtime with
91
- * an entry for every name, so nothing is ever unmocked here and a call
92
- * that would have gone to the network is answered notMocked instead.
93
- * Pick the rest entry for an app with no backend to reach, and this
94
- * for one whose remaining endpoints are served by a real one.
98
+ * This and `mockApp.restNotMocked(reason)` answer the same question,
99
+ * and the rest entry wins: it gives the runtime an entry for every
100
+ * name, so nothing is unmocked here and a call is answered notMocked
101
+ * rather than passed on. Pick the rest entry for an app with no
102
+ * backend to reach, and this for one whose remaining endpoints are
103
+ * served by a real one.
95
104
  */
96
105
  onUnmocked?: "refuse" | "passthrough";
97
106
  /** The client IP its calls are read as arriving from. Default: the runtime's own defaultClientIp. */
98
107
  clientIp?: string;
99
108
  }) => ReturnType<M["http"]["post"]>;
109
+ export {};