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
@@ -10,17 +10,23 @@
10
10
  */
11
11
  import { restoreBytes } from "../shared/wire/LambderCompressionCodec.js";
12
12
  import { bytesToBase64 } from "../shared/util/LambderBase64.js";
13
+ import { LAMBDER_INVOKE_API_ID, LAMBDER_LOCAL_API_ID } from "../shared/wire/LambderInvokeApiId.js";
13
14
  import { buildEnvelopeFields } from "../shared/transport/LambderApiTransport.js";
14
- /** Marks a synthesized request as an invoke, for guards and hooks that want to tell. Not an authorization. */
15
+ /**
16
+ * Marks a synthesized request as an invoke, for guards and hooks that want to
17
+ * tell. Not an authorization: over HTTP it is a header any client can send.
18
+ * The server itself tells an invoke by its requestContext.apiId, which no
19
+ * gateway lets a client write (see LAMBDER_INVOKE_API_ID).
20
+ */
15
21
  export const LAMBDER_INVOKE_HEADER = "x-lambder-invoke";
16
22
  /** The invoking function's name, when the caller runs in Lambda; for the callee's logs. */
17
23
  export const LAMBDER_INVOKED_BY_HEADER = "x-lambder-invoked-by";
18
24
  /** The value of the marker header; a future incompatible event shape would bump it. */
19
25
  export const LAMBDER_INVOKE_PROTOCOL = "1";
20
26
  /**
21
- * The forwarded-address header a gateway writes. This event never writes it:
22
- * the address it asserts travels in requestContext.http.sourceIp, which is
23
- * what resolveClientIp reads and the only channel a callee trusts by default.
27
+ * The forwarded-address header a gateway writes, which an invoke never
28
+ * carries: its address travels as `clientIp` alone (see
29
+ * synthesizeLambdaHttpEvent).
24
30
  */
25
31
  const FORWARDED_FOR_HEADER = "x-forwarded-for";
26
32
  /** The session's cookie pair for a synthesized event: the token rides as a cookie, the CSRF value in the envelope's `token` field. */
@@ -33,22 +39,19 @@ const randomRequestId = () => {
33
39
  };
34
40
  export function synthesizeLambdaHttpEvent(request, options) {
35
41
  // The caller's own headers go on first, so the ones this function owns
36
- // cannot be displaced by them. Forwarding an incoming browser request's
37
- // headers into `headers` is an ordinary gateway-lambda pattern, and with
38
- // the spread last it let that end user overwrite the invoke markers and
39
- // the forwarded address this event is asserting.
42
+ // cannot be displaced by them.
40
43
  const headers = {};
41
44
  for (const [key, value] of Object.entries(request.headers ?? {}))
42
45
  headers[key.toLowerCase()] = value;
43
- // These three the event owns unconditionally, whatever the caller passed:
44
- // a forwarded address the caller did not assert through `clientIp` is not
45
- // this event's to make, and the invoke markers say what this event is. A
46
- // gateway lambda that forwards a browser's headers wholesale would
47
- // otherwise hand a callee that trusts x-forwarded-for an end-user-chosen
48
- // ctx.ip, and let a browser-shaped request claim to be an invoke.
49
- delete headers[FORWARDED_FOR_HEADER];
46
+ // The invoke markers the event owns whatever the caller passed: they say
47
+ // what this event is. Forwarding a browser's headers wholesale is an
48
+ // ordinary gateway-lambda pattern, and without these deletes it would let
49
+ // a browser-shaped request claim to be an invoke, or an invoke name an
50
+ // invoking function that did not send it.
50
51
  delete headers[LAMBDER_INVOKE_HEADER];
51
52
  delete headers[LAMBDER_INVOKED_BY_HEADER];
53
+ if (options.invoke)
54
+ delete headers[FORWARDED_FOR_HEADER];
52
55
  headers.host = request.host;
53
56
  headers["accept-encoding"] = "br, gzip";
54
57
  if (options.invoke) {
@@ -58,7 +61,9 @@ export function synthesizeLambdaHttpEvent(request, options) {
58
61
  headers[LAMBDER_INVOKED_BY_HEADER] = invokedBy;
59
62
  }
60
63
  const isBinary = Buffer.isBuffer(request.body);
61
- if (request.body !== undefined && !headers["content-type"]) {
64
+ if (request.contentType)
65
+ headers["content-type"] = request.contentType;
66
+ else if (request.body !== undefined && !headers["content-type"]) {
62
67
  headers["content-type"] = isBinary ? "application/octet-stream" : "application/json";
63
68
  }
64
69
  const body = request.body === undefined ? undefined : isBinary ? bytesToBase64(request.body) : request.body;
@@ -85,7 +90,7 @@ export function synthesizeLambdaHttpEvent(request, options) {
85
90
  // Cognito identity) describes a deployment this event has none of.
86
91
  requestContext: {
87
92
  accountId: "",
88
- apiId: "lambder-local",
93
+ apiId: options.invoke ? LAMBDER_INVOKE_API_ID : LAMBDER_LOCAL_API_ID,
89
94
  domainName: request.host,
90
95
  httpMethod: request.method,
91
96
  identity: { sourceIp: request.clientIp ?? "", userAgent: "lambder-local" },
@@ -100,21 +105,34 @@ export function synthesizeLambdaHttpEvent(request, options) {
100
105
  isBase64Encoded: isBinary,
101
106
  };
102
107
  }
108
+ // An HTTP API's event, with its path decoded (an encoded slash into a
109
+ // separator, too) the way the gateway delivers it, whatever host the
110
+ // request names. The server reads a gateway's v2 event as encoded when
111
+ // its domain is a Function URL's, so it is told this event is decoded by
112
+ // the apiId, which is Lambder's own either way: a request naming a
113
+ // lambda-url host would otherwise have its path decoded a second time,
114
+ // and `/%2561dmin` would reach `/admin`. A path that does not decode goes
115
+ // as it is.
116
+ let decodedPath = request.path;
117
+ try {
118
+ decodedPath = decodeURIComponent(request.path);
119
+ }
120
+ catch { /* delivered as written */ }
103
121
  return {
104
122
  version: "2.0",
105
123
  routeKey: "$default",
106
- rawPath: request.path,
124
+ rawPath: decodedPath,
107
125
  rawQueryString: new URLSearchParams(request.query ?? {}).toString(),
108
126
  headers,
109
127
  ...(request.cookies?.length ? { cookies: request.cookies } : {}),
110
128
  requestContext: {
111
129
  accountId: "",
112
- apiId: options.invoke ? "lambder-invoke" : "lambder-local",
130
+ apiId: options.invoke ? LAMBDER_INVOKE_API_ID : LAMBDER_LOCAL_API_ID,
113
131
  domainName: request.host,
114
132
  domainPrefix: "",
115
133
  http: {
116
134
  method: request.method,
117
- path: request.path,
135
+ path: decodedPath,
118
136
  protocol: "HTTP/1.1",
119
137
  sourceIp: request.clientIp ?? "",
120
138
  userAgent: options.invoke ? "lambder-invoke" : "lambder-local",
@@ -182,8 +200,8 @@ export const decodeLambdaHttpResult = async (result, maxBodyBytes) => {
182
200
  }
183
201
  else if (encoding && encoding !== "identity") {
184
202
  // "identity" is a legal value meaning no encoding, and a hook or a
185
- // proxy may set it; treating it as unsupported turned every such
186
- // invoke into a protocol failure.
203
+ // proxy may set it, so it passes as a plain body; only other values
204
+ // fail the invoke.
187
205
  throw new Error(`the answer carries an unsupported Content-Encoding "${encoding}"`);
188
206
  }
189
207
  return {
@@ -23,17 +23,16 @@ export type LambderHandlerTransportOptions = {
23
23
  * lambderCookieJarTransport to hold a session across calls.
24
24
  *
25
25
  * A handler that throws (which a Lambder app never does on the HTTP path,
26
- * since render() answers its own last-resort 500) produced no answer at all,
27
- * so the call fails as `protocol` carrying the handler's own error as its
28
- * cause. API Gateway would have turned it into a bare 502, and synthesizing
29
- * one here would throw the error away; keeping it is the point of an
30
- * in-process transport, and there is nowhere in an HTTP answer to put one
31
- * except the user-facing `message` field, which is the wrong channel for an
32
- * internal fault.
26
+ * since render() answers its own last-resort 500) produced no answer, so the
27
+ * call fails as `protocol` with the handler's own error as its cause. API
28
+ * Gateway would turn it into a bare 502, but synthesizing one here would
29
+ * throw the error away, and keeping it is the point of an in-process
30
+ * transport. An HTTP answer's only place for it would be the user-facing
31
+ * `message` field, the wrong channel for an internal fault.
33
32
  *
34
33
  * `request.signal` ends the wait, as the transport contract requires. The
35
- * handler keeps running to completion either way, because a function call in
36
- * this process cannot be cancelled: what a timeout buys here is the caller's
37
- * answer, not the callee's attention.
34
+ * handler still runs to completion, because a function call in this process
35
+ * cannot be cancelled: a timeout buys the caller its answer, not the
36
+ * callee's attention.
38
37
  */
39
38
  export declare const lambderHandlerTransport: (handler: LambderHandler, options?: LambderHandlerTransportOptions) => LambderApiTransport;
@@ -15,18 +15,17 @@ const isAbortError = (err, signal) => signal !== undefined && signal.aborted &&
15
15
  * lambderCookieJarTransport to hold a session across calls.
16
16
  *
17
17
  * A handler that throws (which a Lambder app never does on the HTTP path,
18
- * since render() answers its own last-resort 500) produced no answer at all,
19
- * so the call fails as `protocol` carrying the handler's own error as its
20
- * cause. API Gateway would have turned it into a bare 502, and synthesizing
21
- * one here would throw the error away; keeping it is the point of an
22
- * in-process transport, and there is nowhere in an HTTP answer to put one
23
- * except the user-facing `message` field, which is the wrong channel for an
24
- * internal fault.
18
+ * since render() answers its own last-resort 500) produced no answer, so the
19
+ * call fails as `protocol` with the handler's own error as its cause. API
20
+ * Gateway would turn it into a bare 502, but synthesizing one here would
21
+ * throw the error away, and keeping it is the point of an in-process
22
+ * transport. An HTTP answer's only place for it would be the user-facing
23
+ * `message` field, the wrong channel for an internal fault.
25
24
  *
26
25
  * `request.signal` ends the wait, as the transport contract requires. The
27
- * handler keeps running to completion either way, because a function call in
28
- * this process cannot be cancelled: what a timeout buys here is the caller's
29
- * answer, not the callee's attention.
26
+ * handler still runs to completion, because a function call in this process
27
+ * cannot be cancelled: a timeout buys the caller its answer, not the
28
+ * callee's attention.
30
29
  */
31
30
  export const lambderHandlerTransport = (handler, options = {}) => {
32
31
  const clientIp = options.clientIp ?? LOOPBACK_CLIENT_IP;
@@ -47,6 +46,7 @@ export const lambderHandlerTransport = (handler, options = {}) => {
47
46
  path: target.path,
48
47
  host,
49
48
  headers: request.headers,
49
+ contentType: "application/json",
50
50
  clientIp: request.clientIp ?? clientIp,
51
51
  cookies: request.cookies,
52
52
  body: JSON.stringify(buildTransportEnvelope({ ...request, siteHost: request.siteHost || host })),
@@ -58,18 +58,15 @@ export const lambderHandlerTransport = (handler, options = {}) => {
58
58
  catch (err) {
59
59
  if (isAbortError(err, request.signal))
60
60
  throw err;
61
- // The whole point of this transport is in-process integration
62
- // testing, so the handler's own error is the useful part. A thrown
63
- // handler answered nothing, and a transport failure is the one
64
- // channel that carries a cause: swallowing it into a synthetic 502
65
- // left the caller an outcome.error reading "Request failed: 502"
66
- // and no way to reach what actually threw.
61
+ // A transport failure is the one channel that carries a cause. A
62
+ // synthetic 502 would leave the caller an outcome.error reading
63
+ // "Request failed: 502" and no way to reach what actually threw.
67
64
  throw new LambderTransportFailure("protocol", `the handler threw instead of answering: ${coerceToError(err).message}`, { cause: err });
68
65
  }
69
66
  // Decoding failures are the callee answering with something that is
70
67
  // not an HTTP result, or with more than the ceiling allows. Neither is
71
- // a network failure, and reporting them as one sends whoever is
72
- // debugging an integration test looking at their connection.
68
+ // a network failure, and reporting one as such would send whoever is
69
+ // debugging an integration test to look at their connection.
73
70
  let http;
74
71
  try {
75
72
  http = await decodeLambdaHttpResult(result, maxResponseBytes);
@@ -13,13 +13,13 @@ import type { LambderMockAppOptions, LambderMockIdempotencyOptions, LambderMockT
13
13
  import type { LambderMockCallContext, LambderMockCallRecord, LambderMockEntry, LambderMockEntryInput, LambderMockFailure, LambderMockFailureReason, LambderMockHandler, LambderMockLatency, LambderMockListener, LambderMockPublicNames, LambderMockRateLimitPolicies, LambderMockRegistryCheck, LambderMockRestEntry, LambderMockSessionCallContext, LambderMockSessionNames, LambderMockSlice, LambderMockOverride } from "./LambderMockTypes.js";
14
14
  /**
15
15
  * The mock runtime: the API core (LambderApiPipeline, the same class the
16
- * Lambda server runs) over memory stores, with a registry of typed mock
17
- * handlers where the server has app handlers, and mock guards where it has
18
- * app guards. Everything the protocol does (envelope, refusals, sessions
19
- * and their cookies, guards, rate limits, idempotency, the signature gate)
20
- * happens in the core; this class only resolves a name to an entry, wraps
21
- * the handler's return into the envelope, and adds what a mock needs on
22
- * top: failure injection, latency, a subscription, a call log, reset.
16
+ * Lambda server runs) over memory stores, with typed mock handlers and mock
17
+ * guards where the server has app handlers and guards. Every protocol step
18
+ * (envelope, refusals, sessions and their cookies, guards, rate limits,
19
+ * idempotency, the signature gate) happens in the core; this class resolves
20
+ * a name to an entry, wraps the handler's return into the envelope, and adds
21
+ * what a mock needs: failure injection, latency, a subscription, a call log,
22
+ * reset.
23
23
  *
24
24
  * Create one with initLambderMock<Contract, SessionData>().create(...),
25
25
  * which fixes the contract and session types first so everything else is
@@ -34,12 +34,11 @@ export declare class LambderMockApp<C extends LambderApiContractShape, S = any,
34
34
  readonly tokenCookieKey: string;
35
35
  readonly csrfCookieKey: string;
36
36
  /**
37
- * The client IP a transport request carrying none is read as. Public
38
- * because an adapter has to read the same default the direct transport
39
- * uses: the MSW adapter had a hardcoded "127.0.0.1" of its own, so an app
40
- * that set defaultClientIp saw one address through the transport and
41
- * another through the service worker, and a per-IP rate limit counted two
42
- * clients where there was one.
37
+ * The client IP a transport request carrying none is read as. Public so
38
+ * every adapter reads the same default the direct transport uses: with a
39
+ * default of its own, an app that set defaultClientIp would show one
40
+ * address through the transport and another through the service worker,
41
+ * and a per-IP rate limit would count two clients where there is one.
43
42
  */
44
43
  readonly defaultClientIp: string;
45
44
  /** The host this runtime's cookies belong to (see the cookieHost option). */
@@ -47,9 +46,8 @@ export declare class LambderMockApp<C extends LambderApiContractShape, S = any,
47
46
  /**
48
47
  * The API core, every protocol step of it. Private: the mock's surface is
49
48
  * the app, and a consumer reaching past it would be configuring the
50
- * server's pipeline through a development tool. The three adapters take
51
- * what they need from the app's own methods (handleRequest,
52
- * requestFromTransport), which is why none of them names this.
49
+ * server's pipeline through a development tool. The adapters use the
50
+ * app's own methods (handleRequest, requestFromTransport) instead.
53
51
  */
54
52
  private readonly pipeline;
55
53
  /** The cookie scope signIn plants under, so signOut can name the same one when it clears them. */
@@ -71,11 +69,10 @@ export declare class LambderMockApp<C extends LambderApiContractShape, S = any,
71
69
  * the ones the server runs on a definition, and the mock's own "a session
72
70
  * endpoint needs the sessions option".
73
71
  *
74
- * One place, so no registration path can skip either check. A session
75
- * endpoint on a mock without sessions would otherwise register silently
76
- * and answer the first call with a 500 from inside the pipeline, naming
77
- * the SERVER's option name, for a mistake whose fix is one option at
78
- * create().
72
+ * One place, so no registration path can skip either check. Without the
73
+ * second, a session endpoint on a mock without sessions would register
74
+ * silently and answer its first call with a 500 naming the SERVER's
75
+ * option, for a mistake fixed by one option at create().
79
76
  */
80
77
  private assertEntryRegistration;
81
78
  private buildEntry;
@@ -87,14 +84,13 @@ export declare class LambderMockApp<C extends LambderApiContractShape, S = any,
87
84
  * A public endpoint deliberately left without a mock; a call answers the
88
85
  * notMocked refusal carrying the reason.
89
86
  *
90
- * Public and session have separate builders for the same reason publicApi
91
- * and sessionApi do: the refusal runs through the pipeline so that the
92
- * steps BEFORE dispatch still happen, and the session read is one of them.
93
- * Declaring every not-mocked endpoint public switched that step off, so a
94
- * session endpoint with no session answered "not mocked" where the server
95
- * answers sessionExpired, and the mode on its events and call log was
96
- * wrong too. The mode cannot be recovered at runtime, because the contract
97
- * is a type, so the builder is where it has to be said.
87
+ * Public and session get separate builders, as publicApi and sessionApi
88
+ * do, because the refusal runs through the pipeline so the steps BEFORE
89
+ * dispatch still happen, and the session read is one of them. Were every
90
+ * not-mocked endpoint public, a session endpoint with no session would
91
+ * answer "not mocked" where the server answers sessionExpired, and its
92
+ * events and call log would carry the wrong mode. The contract is a type,
93
+ * so the mode cannot be recovered at runtime: the builder has to say it.
98
94
  */
99
95
  notMocked<K extends LambderMockPublicNames<C>>(name: K, reason: string): LambderMockEntry<C, K>;
100
96
  /** A session endpoint deliberately left without a mock: the session is still read, and refused before the notMocked refusal. */
@@ -107,23 +103,21 @@ export declare class LambderMockApp<C extends LambderApiContractShape, S = any,
107
103
  * mockApp.register(userMocks, billingMocks, mockApp.restNotMocked("not mocked yet"));
108
104
  * ```
109
105
  *
110
- * What it buys is adoption over a contract the mocks do not cover yet:
111
- * register() stays exhaustive by construction, and the endpoints nothing
112
- * claims answer the notMocked refusal carrying this reason instead of
106
+ * It lets mocks be adopted over a contract they do not cover yet:
107
+ * register() stays exhaustive by construction, and endpoints nothing
108
+ * claims answer the notMocked refusal with this reason instead of
113
109
  * apiNotFound, so a screen that reaches one says "not mocked yet" rather
114
110
  * than "unknown error". Strays and duplicates in the explicit slices are
115
- * refused exactly as they are without it, and an entry registered later
116
- * (registerPartial, or a second register) takes the endpoint back from the
117
- * rest.
111
+ * still refused, and an entry registered later (registerPartial, or a
112
+ * second register) takes its endpoint back from the rest.
118
113
  *
119
- * The one thing it cannot do is the session read. A call it answers is
120
- * processed as a public endpoint: the protocol's pre-pass still runs, so a
121
- * stale client still hears versionExpired, but the mode of a name nothing
122
- * registered is not knowable at runtime, the contract being a type. So a
123
- * signed-out call to an unmocked session endpoint is answered "not mocked"
124
- * where the server answers sessionExpired, and the endpoint whose
125
- * signed-out path a test cares about is the one to declare with
126
- * sessionNotMocked instead.
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.
127
121
  */
128
122
  restNotMocked(reason: string): LambderMockRestEntry;
129
123
  private buildNotMockedEntry;
@@ -161,24 +155,20 @@ export declare class LambderMockApp<C extends LambderApiContractShape, S = any,
161
155
  * Whether a call to this name would be answered from the registry, which
162
156
  * is what an adapter asks before passing one on.
163
157
  *
164
- * True for every name once a rest entry is registered, because the rest
165
- * entry is what answers the names nothing else claimed. That is what makes
166
- * a rest entry and the MSW adapter's `onUnmocked: "passthrough"`
167
- * alternatives rather than layers: with one registered, the runtime
168
- * answers everything itself and nothing is handed on to the network.
158
+ * True for every name once a rest entry is registered, since it answers
159
+ * whatever nothing else claimed. That makes a rest entry and the MSW
160
+ * adapter's `onUnmocked: "passthrough"` alternatives rather than layers:
161
+ * with one registered, nothing is handed on to the network.
169
162
  */
170
163
  hasRegisteredEntry(apiName: string): boolean;
171
164
  private entryFor;
172
165
  /**
173
166
  * The entry that answers a name nothing registered, when register() was
174
167
  * given a rest entry: the notMocked refusal carrying its reason, run
175
- * through the pipeline as a public endpoint.
176
- *
177
- * Public because the mode of an unregistered name cannot be recovered at
178
- * runtime, the contract being a type. Everything that precedes dispatch
179
- * still runs (the signature gate, the payload restore); the session read is
180
- * the one step this answer cannot have, which is the fidelity limit
181
- * restNotMocked documents.
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.
182
172
  */
183
173
  private restNotMockedEntry;
184
174
  /** The next call to the endpoint fails this way; several calls queue in order. */
@@ -194,30 +184,24 @@ export declare class LambderMockApp<C extends LambderApiContractShape, S = any,
194
184
  * latency, the call log and its numbering, the cookies its own transports
195
185
  * hold, then onReset, so the app rewinds its own data too.
196
186
  *
197
- * The cookies matter as much as the sessions do: emptying the session
198
- * store while a jar still holds the token for one of them leaves the next
199
- * call carrying a session that no longer exists, which reads as signed in
200
- * until the answer says sessionExpired. So every jar transport() built
201
- * for itself is emptied, and the cookies a "document" transport mirrored
202
- * are expired again.
187
+ * The cookies matter as much as the sessions: a jar still holding the
188
+ * token of an emptied store's session reads as signed in until an answer
189
+ * says sessionExpired. So every jar transport() built for itself is
190
+ * emptied, and the cookies a "document" transport mirrored are expired.
203
191
  *
204
- * The registry survives, being what the runtime was configured with
205
- * rather than what it accumulated. Subscriptions survive too, because
206
- * they are how a test watches the runtime rather than state it is
207
- * testing; a listener muted for throwing is unmuted, so one bad call does
208
- * not silence it for the rest of the run. A session store or a cookie jar
209
- * the app supplied itself survives: the runtime did not create it and
210
- * does not know what else holds it.
192
+ * The registry survives, being configuration rather than accumulated
193
+ * state. Subscriptions survive too, being how a test watches the runtime;
194
+ * a listener muted for throwing is unmuted, so one bad call does not
195
+ * silence it for the rest of the run. A session store or cookie jar the
196
+ * app supplied survives: the runtime did not create it and does not know
197
+ * what else holds it.
211
198
  */
212
199
  reset(): void;
213
200
  /**
214
201
  * The four session members refuse in the mock's own words, naming the
215
- * option a mock is created with.
216
- *
217
- * The pipeline's guard says "Configure the session option at creation",
218
- * which is the SERVER's option name: the mock's is `sessions`, and a
219
- * reader who goes looking for `session` on create() does not find it.
220
- * The registration path was fixed for exactly this one method over.
202
+ * option a mock is created with. The pipeline's guard names the SERVER's
203
+ * option (`session`), which a reader does not find on the mock's create()
204
+ * (`sessions`); assertEntryRegistration does the same for registration.
221
205
  */
222
206
  private assertSessionsConfigured;
223
207
  /** The session manager, for tests that inspect or manipulate sessions directly. Throws when sessions are off. */
@@ -301,9 +285,9 @@ export declare class LambderMockApp<C extends LambderApiContractShape, S = any,
301
285
  /**
302
286
  * Takes a jar an adapter built for itself as the runtime's own, so reset()
303
287
  * empties it with the rest. The MSW adapter's jar holds the session
304
- * cookies of calls that never touch transport(), and a reset that leaves
305
- * it full is the same stale-session bug: the store is empty and the next
306
- * request still carries a token for one of its sessions.
288
+ * cookies of calls that never touch transport(); left full after a reset,
289
+ * the next request would carry a token for a session the emptied store no
290
+ * longer has.
307
291
  */
308
292
  adoptCookieJar(jar: LambderCookieJar): void;
309
293
  /** caller.setTransport(mockApp.transport(options)); returns the transport, its jar on it. */
@@ -341,12 +325,12 @@ export declare const initLambderMock: <C extends LambderApiContractShape, S = an
341
325
  * The mock app, with the guard map and the rate-limit policies inferred
342
326
  * from the options.
343
327
  *
344
- * `const` on each of them is what pins a restatement to the contract, and
345
- * it has one cost: inferring a generic from an object literal switches
346
- * excess-property checking off for the whole literal, nested objects
347
- * included, so a typo inside `rateLimits.policies` or `idempotency`
348
- * compiled and was dropped in silence. `I` exists for the same reason `P`
349
- * does, and LambderMockSurplusKeys puts the error back on the key.
328
+ * `const` on each of them pins a restatement to the contract, at a cost:
329
+ * inferring a generic from an object literal switches excess-property
330
+ * checking off for the whole literal, nested objects included, so a typo
331
+ * inside `rateLimits.policies` or `idempotency` would compile and be
332
+ * dropped. `I` exists for the same reason `P` does, and
333
+ * LambderMockSurplusKeys puts the error back on the key.
350
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
336
  };