lambder 7.3.1 → 8.0.2

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 (207) hide show
  1. package/CHANGELOG.md +933 -3
  2. package/README.md +41 -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/freshProcessVerifier.d.ts +13 -0
  27. package/dist/build/freshProcessVerifier.js +19 -0
  28. package/dist/build/writeApiSignatures.d.ts +109 -0
  29. package/dist/build/writeApiSignatures.js +222 -0
  30. package/dist/build.d.ts +9 -0
  31. package/dist/build.js +8 -0
  32. package/dist/client/LambderCaller.d.ts +13 -44
  33. package/dist/client/LambderCaller.js +77 -84
  34. package/dist/client/LambderReloadLoopBreaker.d.ts +56 -26
  35. package/dist/client/LambderReloadLoopBreaker.js +90 -46
  36. package/dist/client/lambderFetchTransport.d.ts +4 -1
  37. package/dist/client/lambderFetchTransport.js +52 -28
  38. package/dist/client.d.ts +5 -3
  39. package/dist/client.js +2 -1
  40. package/dist/core/Lambder.d.ts +140 -75
  41. package/dist/core/Lambder.js +347 -227
  42. package/dist/core/LambderContext.d.ts +82 -15
  43. package/dist/core/LambderContext.js +107 -20
  44. package/dist/core/LambderCors.d.ts +21 -3
  45. package/dist/core/LambderCors.js +35 -16
  46. package/dist/core/LambderCrashHandling.d.ts +40 -0
  47. package/dist/core/LambderCrashHandling.js +97 -0
  48. package/dist/core/LambderCreateOptions.d.ts +151 -75
  49. package/dist/core/LambderCreateOptions.js +16 -23
  50. package/dist/core/LambderFiles.d.ts +21 -7
  51. package/dist/core/LambderFiles.js +62 -34
  52. package/dist/core/LambderIndexHtml.js +12 -11
  53. package/dist/core/LambderPolicyBuilders.d.ts +17 -5
  54. package/dist/core/LambderPolicyBuilders.js +17 -5
  55. package/dist/core/LambderPublicFiles.d.ts +11 -5
  56. package/dist/core/LambderPublicFiles.js +32 -4
  57. package/dist/core/LambderRequestPath.d.ts +43 -0
  58. package/dist/core/LambderRequestPath.js +63 -0
  59. package/dist/core/LambderResponse.d.ts +26 -5
  60. package/dist/core/LambderResponse.js +157 -70
  61. package/dist/core/LambderResponseBuilder.d.ts +49 -4
  62. package/dist/core/LambderResponseBuilder.js +64 -3
  63. package/dist/core/LambderRouting.d.ts +2 -3
  64. package/dist/core/LambderRouting.js +22 -7
  65. package/dist/core/LambderTemplatingEngine.js +211 -32
  66. package/dist/index.d.ts +15 -8
  67. package/dist/index.js +5 -4
  68. package/dist/invoke/LambderInvokeCaller.d.ts +37 -42
  69. package/dist/invoke/LambderInvokeCaller.js +76 -66
  70. package/dist/invoke/LambderInvokeOutcome.d.ts +27 -26
  71. package/dist/invoke/LambderInvokeOutcome.js +9 -22
  72. package/dist/invoke/LambderLambdaEvent.d.ts +29 -9
  73. package/dist/invoke/LambderLambdaEvent.js +40 -22
  74. package/dist/invoke/lambderHandlerTransport.d.ts +9 -10
  75. package/dist/invoke/lambderHandlerTransport.js +15 -18
  76. package/dist/mock/LambderMockApp.d.ts +67 -83
  77. package/dist/mock/LambderMockApp.js +167 -153
  78. package/dist/mock/LambderMockBrowserCookies.d.ts +24 -28
  79. package/dist/mock/LambderMockBrowserCookies.js +24 -28
  80. package/dist/mock/LambderMockCallRecorder.d.ts +15 -22
  81. package/dist/mock/LambderMockCallRecorder.js +19 -28
  82. package/dist/mock/LambderMockCreateOptions.d.ts +42 -24
  83. package/dist/mock/LambderMockEntryRegistry.d.ts +11 -12
  84. package/dist/mock/LambderMockEntryRegistry.js +24 -29
  85. package/dist/mock/LambderMockFailureInjector.d.ts +3 -6
  86. package/dist/mock/LambderMockFailureInjector.js +3 -6
  87. package/dist/mock/LambderMockTypes.d.ts +78 -108
  88. package/dist/mock/lambderMockInvokeTransport.d.ts +11 -13
  89. package/dist/mock/lambderMockInvokeTransport.js +11 -10
  90. package/dist/mock/lambderMockMswHandler.d.ts +33 -29
  91. package/dist/mock/lambderMockMswHandler.js +50 -39
  92. package/dist/mock.d.ts +1 -1
  93. package/dist/mock.js +2 -3
  94. package/dist/session/LambderSessionController.d.ts +108 -89
  95. package/dist/session/LambderSessionController.js +187 -168
  96. package/dist/session/LambderSessionCrypto.d.ts +16 -7
  97. package/dist/session/LambderSessionCrypto.js +26 -12
  98. package/dist/session/LambderSessionManager.d.ts +124 -46
  99. package/dist/session/LambderSessionManager.js +262 -137
  100. package/dist/shared/LambderHtml.d.ts +42 -3
  101. package/dist/shared/LambderHtml.js +127 -7
  102. package/dist/shared/LambderHtmlPositions.d.ts +173 -0
  103. package/dist/shared/LambderHtmlPositions.js +652 -0
  104. package/dist/shared/LambderI18n.d.ts +10 -11
  105. package/dist/shared/LambderI18n.js +33 -21
  106. package/dist/shared/contracts/LambderCache.d.ts +66 -0
  107. package/dist/shared/contracts/LambderCache.js +11 -0
  108. package/dist/shared/contracts/LambderFileSource.d.ts +6 -6
  109. package/dist/shared/contracts/LambderFileSource.js +5 -8
  110. package/dist/shared/contracts/LambderIdempotencyStore.d.ts +51 -22
  111. package/dist/shared/contracts/LambderIdempotencyStore.js +4 -5
  112. package/dist/shared/contracts/LambderRateLimiter.d.ts +27 -15
  113. package/dist/shared/contracts/LambderRateLimiter.js +4 -5
  114. package/dist/shared/contracts/LambderSessionStore.d.ts +65 -26
  115. package/dist/shared/contracts/LambderSessionStore.js +5 -6
  116. package/dist/shared/transport/LambderApiTransport.d.ts +27 -27
  117. package/dist/shared/transport/LambderApiTransport.js +7 -7
  118. package/dist/shared/transport/LambderCookieJar.d.ts +28 -35
  119. package/dist/shared/transport/LambderCookieJar.js +54 -66
  120. package/dist/shared/transport/lambderCookieJarTransport.d.ts +11 -13
  121. package/dist/shared/transport/lambderCookieJarTransport.js +24 -23
  122. package/dist/shared/util/LambderCallAbort.d.ts +5 -5
  123. package/dist/shared/util/LambderCallAbort.js +5 -5
  124. package/dist/shared/util/LambderClientIp.d.ts +27 -11
  125. package/dist/shared/util/LambderClientIp.js +96 -13
  126. package/dist/shared/util/LambderExpiringMap.d.ts +35 -49
  127. package/dist/shared/util/LambderExpiringMap.js +41 -57
  128. package/dist/shared/util/LambderNodeModules.js +6 -7
  129. package/dist/shared/util/LambderOptionChecks.d.ts +4 -4
  130. package/dist/shared/util/LambderOptionChecks.js +4 -4
  131. package/dist/shared/util/LambderResponseBrand.d.ts +5 -5
  132. package/dist/shared/util/LambderResponseBrand.js +5 -5
  133. package/dist/shared/util/LambderTypeUtilities.d.ts +7 -8
  134. package/dist/shared/util/LambderTypeUtilities.js +3 -3
  135. package/dist/shared/util/boundKeyField.d.ts +20 -0
  136. package/dist/shared/util/boundKeyField.js +34 -0
  137. package/dist/shared/util/canonicalJson.d.ts +11 -0
  138. package/dist/shared/util/canonicalJson.js +28 -0
  139. package/dist/shared/util/joinKeyFields.d.ts +20 -0
  140. package/dist/shared/util/joinKeyFields.js +22 -0
  141. package/dist/shared/wire/LambderAnswerHeaders.d.ts +12 -16
  142. package/dist/shared/wire/LambderAnswerHeaders.js +12 -16
  143. package/dist/shared/wire/LambderApiContract.d.ts +107 -32
  144. package/dist/shared/wire/LambderApiOutcome.d.ts +43 -31
  145. package/dist/shared/wire/LambderApiOutcome.js +48 -23
  146. package/dist/shared/wire/LambderApiRefusal.d.ts +39 -27
  147. package/dist/shared/wire/LambderApiRefusal.js +36 -7
  148. package/dist/shared/wire/LambderApiSignature.d.ts +18 -22
  149. package/dist/shared/wire/LambderApiSignature.js +16 -19
  150. package/dist/shared/wire/LambderCallOptions.d.ts +38 -47
  151. package/dist/shared/wire/LambderCallOptions.js +9 -11
  152. package/dist/shared/wire/LambderCompressionCodec.d.ts +29 -34
  153. package/dist/shared/wire/LambderCompressionCodec.js +31 -36
  154. package/dist/shared/wire/LambderCompressionOption.d.ts +9 -9
  155. package/dist/shared/wire/LambderCompressionOption.js +9 -9
  156. package/dist/shared/wire/LambderCrashDetail.d.ts +12 -15
  157. package/dist/shared/wire/LambderCrashDetail.js +12 -15
  158. package/dist/shared/wire/LambderDefaultApiPath.d.ts +6 -0
  159. package/dist/shared/wire/LambderDefaultApiPath.js +6 -0
  160. package/dist/shared/wire/LambderHttpStatus.d.ts +6 -7
  161. package/dist/shared/wire/LambderIdempotencyKeyScope.d.ts +89 -0
  162. package/dist/shared/wire/LambderIdempotencyKeyScope.js +146 -0
  163. package/dist/shared/wire/LambderInvokeApiId.d.ts +27 -0
  164. package/dist/shared/wire/LambderInvokeApiId.js +27 -0
  165. package/dist/shared/wire/LambderOutcomeAssertions.d.ts +6 -7
  166. package/dist/shared/wire/LambderOutcomeAssertions.js +6 -7
  167. package/dist/shared/wire/LambderRequestPayload.d.ts +18 -20
  168. package/dist/shared/wire/LambderRequestPayload.js +4 -6
  169. package/dist/stores/LambderCacheFiller.d.ts +48 -0
  170. package/dist/stores/LambderCacheFiller.js +119 -0
  171. package/dist/stores/LambderCacheKeys.d.ts +26 -0
  172. package/dist/stores/LambderCacheKeys.js +54 -0
  173. package/dist/stores/LambderCacheValues.d.ts +45 -0
  174. package/dist/stores/LambderCacheValues.js +74 -0
  175. package/dist/stores/LambderDdbCache.d.ts +121 -56
  176. package/dist/stores/LambderDdbCache.js +528 -225
  177. package/dist/stores/LambderDdbIdempotencyStore.d.ts +33 -22
  178. package/dist/stores/LambderDdbIdempotencyStore.js +75 -50
  179. package/dist/stores/LambderDdbRateLimiter.d.ts +76 -20
  180. package/dist/stores/LambderDdbRateLimiter.js +151 -39
  181. package/dist/stores/LambderDdbSdk.d.ts +43 -31
  182. package/dist/stores/LambderDdbSdk.js +79 -33
  183. package/dist/stores/LambderDdbSessionStore.d.ts +27 -14
  184. package/dist/stores/LambderDdbSessionStore.js +119 -47
  185. package/dist/stores/LambderHttpFileSource.d.ts +15 -6
  186. package/dist/stores/LambderHttpFileSource.js +15 -13
  187. package/dist/stores/LambderMemoryCache.d.ts +49 -0
  188. package/dist/stores/LambderMemoryCache.js +113 -0
  189. package/dist/stores/LambderMemoryIdempotencyStore.d.ts +13 -12
  190. package/dist/stores/LambderMemoryIdempotencyStore.js +31 -30
  191. package/dist/stores/LambderMemoryRateLimiter.d.ts +8 -9
  192. package/dist/stores/LambderMemoryRateLimiter.js +14 -13
  193. package/dist/stores/LambderMemorySessionStore.d.ts +14 -11
  194. package/dist/stores/LambderMemorySessionStore.js +38 -19
  195. package/dist/stores/LambderS3FileSource.d.ts +21 -6
  196. package/dist/stores/LambderS3FileSource.js +12 -7
  197. package/dist/testing/LambderTestApp.d.ts +21 -23
  198. package/dist/testing/LambderTestApp.js +22 -24
  199. package/dist/testing/LambderTestVisitor.d.ts +10 -12
  200. package/dist/testing/LambderTestVisitor.js +15 -15
  201. package/dist/testing.d.ts +1 -0
  202. package/dist/testing.js +1 -0
  203. package/package.json +12 -3
  204. package/dist/api/LambderApiPolicyEngine.d.ts +0 -47
  205. package/dist/api/LambderApiPolicyEngine.js +0 -85
  206. package/dist/shared/util/LambderKeyFields.d.ts +0 -32
  207. package/dist/shared/util/LambderKeyFields.js +0 -34
@@ -100,10 +100,9 @@ export declare class LambderTestVisitor<TContract extends LambderApiContractShap
100
100
  * The full outcome, never throwing: LambderCaller.apiOutcome, through
101
101
  * this visitor. Pair it with assertApiSuccess / assertApiFailure.
102
102
  *
103
- * One thing is added to what the caller hands back. When the app crashed
104
- * answering the call, the outcome is the `server` failure any client
105
- * would get, whose error says "Request failed: 500" and nothing else;
106
- * here that error's `cause` is what the app actually threw, stack
103
+ * When the app crashed answering the call, the outcome is the `server`
104
+ * failure any client would get, whose error says only "Request failed:
105
+ * 500"; here that error's `cause` is what the app actually threw, stack
107
106
  * included, so a failing test points at the line in the handler.
108
107
  */
109
108
  readonly apiOutcome: LambderCaller<TContract, TProvidedGuards>["apiOutcome"];
@@ -117,10 +116,9 @@ export declare class LambderTestVisitor<TContract extends LambderApiContractShap
117
116
  });
118
117
  /**
119
118
  * This visitor's cookies, to inspect or clear; every call and request
120
- * reads and fills them. Emptied by the test app's reset(), which a
121
- * visitor notices here, the next time anything asks for its cookies: a
122
- * jar still holding the token of an emptied store would read as signed
123
- * in until an answer said otherwise.
119
+ * reads and fills them. The test app's reset() empties them, noticed here
120
+ * the next time anything asks: a jar still holding the token of an
121
+ * emptied store would read as signed in until an answer said otherwise.
124
122
  */
125
123
  get jar(): LambderCookieJar;
126
124
  /**
@@ -144,10 +142,10 @@ export declare class LambderTestVisitor<TContract extends LambderApiContractShap
144
142
  * tokens too, as LambderMockApp.signIn does.
145
143
  *
146
144
  * Throws when the cookies do not stick. An app that scopes its session
147
- * cookie to a domain (`cookie: { domain: ".example.com" }`) writes one
148
- * this visitor's host is not under, a browser on that host would drop it,
149
- * and so does the jar; left silent, every session call after it answers
150
- * sessionExpired with nothing to say why.
145
+ * cookie to a domain (`cookie: { domain: ".example.com" }`) this visitor's
146
+ * host is not under writes a cookie a browser on that host would drop, and
147
+ * the jar drops it too; left silent, every later session call would
148
+ * answer sessionExpired with nothing to say why.
151
149
  */
152
150
  signIn(sessionKey: string, data: TSessionData, options?: {
153
151
  ttlSeconds?: number;
@@ -31,10 +31,9 @@ export class LambderTestVisitor {
31
31
  * The full outcome, never throwing: LambderCaller.apiOutcome, through
32
32
  * this visitor. Pair it with assertApiSuccess / assertApiFailure.
33
33
  *
34
- * One thing is added to what the caller hands back. When the app crashed
35
- * answering the call, the outcome is the `server` failure any client
36
- * would get, whose error says "Request failed: 500" and nothing else;
37
- * here that error's `cause` is what the app actually threw, stack
34
+ * When the app crashed answering the call, the outcome is the `server`
35
+ * failure any client would get, whose error says only "Request failed:
36
+ * 500"; here that error's `cause` is what the app actually threw, stack
38
37
  * included, so a failing test points at the line in the handler.
39
38
  */
40
39
  apiOutcome;
@@ -53,17 +52,19 @@ export class LambderTestVisitor {
53
52
  const withVisitorHeaders = (request) => handlerTransport({ ...request, headers: { ...this.headers, ...request.headers } });
54
53
  this.caller = new LambderCaller({
55
54
  apiPath: wiring.apiPath,
56
- isCorsEnabled: false,
57
55
  apiVersion: options.apiVersion,
58
56
  apiSignatures: options.apiSignatures,
59
57
  guardInputsProvider: options.guardInputsProvider,
60
58
  logListHandler: () => { },
61
59
  // The jar is read per call rather than captured, so a call made
62
60
  // after the test app was reset carries none of the old cookies.
63
- transport: (request) => lambderCookieJarTransport(withVisitorHeaders, { jar: this.jar, host: this.host })(request),
61
+ // The visitor's session lives in that jar, so a CSRF token the
62
+ // caller read from a page's document.cookie (a test with a DOM)
63
+ // is dropped and the jar's own is posted.
64
+ transport: (request) => lambderCookieJarTransport(withVisitorHeaders, { jar: this.jar, host: this.host })({ ...request, token: "" }),
64
65
  });
65
66
  // The caller names the CSRF cookie the jar transport fills its token
66
- // from, so an app that renamed its session cookies has to be matched
67
+ // from, so an app with custom session cookie names has to be matched
67
68
  // here or every session call would post an empty token.
68
69
  if (wiring.sessionCookieNames) {
69
70
  this.caller.setSessionCookieKey(wiring.sessionCookieNames.tokenCookieKey, wiring.sessionCookieNames.csrfCookieKey);
@@ -78,10 +79,9 @@ export class LambderTestVisitor {
78
79
  }
79
80
  /**
80
81
  * This visitor's cookies, to inspect or clear; every call and request
81
- * reads and fills them. Emptied by the test app's reset(), which a
82
- * visitor notices here, the next time anything asks for its cookies: a
83
- * jar still holding the token of an emptied store would read as signed
84
- * in until an answer said otherwise.
82
+ * reads and fills them. The test app's reset() empties them, noticed here
83
+ * the next time anything asks: a jar still holding the token of an
84
+ * emptied store would read as signed in until an answer said otherwise.
85
85
  */
86
86
  get jar() {
87
87
  const resetCount = this.wiring.resetCount();
@@ -134,10 +134,10 @@ export class LambderTestVisitor {
134
134
  * tokens too, as LambderMockApp.signIn does.
135
135
  *
136
136
  * Throws when the cookies do not stick. An app that scopes its session
137
- * cookie to a domain (`cookie: { domain: ".example.com" }`) writes one
138
- * this visitor's host is not under, a browser on that host would drop it,
139
- * and so does the jar; left silent, every session call after it answers
140
- * sessionExpired with nothing to say why.
137
+ * cookie to a domain (`cookie: { domain: ".example.com" }`) this visitor's
138
+ * host is not under writes a cookie a browser on that host would drop, and
139
+ * the jar drops it too; left silent, every later session call would
140
+ * answer sessionExpired with nothing to say why.
141
141
  */
142
142
  async signIn(sessionKey, data, options = {}) {
143
143
  if (!this.wiring.sessionCookieNames)
package/dist/testing.d.ts CHANGED
@@ -18,6 +18,7 @@ export type { LambderExpectedFailure } from "./shared/wire/LambderOutcomeAsserti
18
18
  export { LambderMemorySessionStore } from "./stores/LambderMemorySessionStore.js";
19
19
  export { LambderMemoryRateLimiter } from "./stores/LambderMemoryRateLimiter.js";
20
20
  export { LambderMemoryIdempotencyStore } from "./stores/LambderMemoryIdempotencyStore.js";
21
+ export { LambderMemoryCache } from "./stores/LambderMemoryCache.js";
21
22
  export { LambderLocalFileSource } from "./stores/LambderLocalFileSource.js";
22
23
  export { LambderCookieJar } from "./shared/transport/LambderCookieJar.js";
23
24
  export { LAMBDER_REFUSAL_CODES } from "./shared/wire/LambderApiRefusal.js";
package/dist/testing.js CHANGED
@@ -18,6 +18,7 @@ export { assertApiSuccess, assertApiFailure } from "./shared/wire/LambderOutcome
18
18
  export { LambderMemorySessionStore } from "./stores/LambderMemorySessionStore.js";
19
19
  export { LambderMemoryRateLimiter } from "./stores/LambderMemoryRateLimiter.js";
20
20
  export { LambderMemoryIdempotencyStore } from "./stores/LambderMemoryIdempotencyStore.js";
21
+ export { LambderMemoryCache } from "./stores/LambderMemoryCache.js";
21
22
  export { LambderLocalFileSource } from "./stores/LambderLocalFileSource.js";
22
23
  export { LambderCookieJar } from "./shared/transport/LambderCookieJar.js";
23
24
  export { LAMBDER_REFUSAL_CODES } from "./shared/wire/LambderApiRefusal.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lambder",
3
- "version": "7.3.1",
3
+ "version": "8.0.2",
4
4
  "sideEffects": false,
5
5
  "description": "Opinionated serverless web framework for TypeScript on AWS Lambda: type-safe APIs from Zod schemas, DynamoDB sessions, and declarative rate limits, authorization guards and idempotency.",
6
6
  "keywords": [
@@ -53,6 +53,10 @@
53
53
  "types": "./dist/testing.d.ts",
54
54
  "default": "./dist/testing.js"
55
55
  },
56
+ "./build": {
57
+ "types": "./dist/build.d.ts",
58
+ "default": "./dist/build.js"
59
+ },
56
60
  "./package.json": "./package.json"
57
61
  },
58
62
  "typesVersions": {
@@ -65,15 +69,20 @@
65
69
  ],
66
70
  "testing": [
67
71
  "./dist/testing.d.ts"
72
+ ],
73
+ "build": [
74
+ "./dist/build.d.ts"
68
75
  ]
69
76
  }
70
77
  },
71
78
  "browser": {
72
79
  "async_hooks": false,
80
+ "child_process": false,
73
81
  "fs": false,
74
82
  "path": false,
75
83
  "zlib": false,
76
- "crypto": false
84
+ "crypto": false,
85
+ "url": false
77
86
  },
78
87
  "files": [
79
88
  "dist",
@@ -101,7 +110,7 @@
101
110
  "tough-cookie": "^6.0.2"
102
111
  },
103
112
  "peerDependencies": {
104
- "@aws-sdk/client-dynamodb": "^3.574.0",
113
+ "@aws-sdk/client-dynamodb": "^3.868.0",
105
114
  "@aws-sdk/client-lambda": "^3.574.0",
106
115
  "@aws-sdk/client-s3": "^3.574.0",
107
116
  "@aws-sdk/lib-dynamodb": "^3.574.0",
@@ -1,47 +0,0 @@
1
- import type { LambderApiRequest } from "./LambderApiRequest.js";
2
- import type { LambderApiCallContext, LambderApiCallTrace } from "./LambderApiCallContext.js";
3
- import type { LambderApiAnswer } from "./LambderApiAnswer.js";
4
- import type { LambderApiDefinition } from "./LambderApiDefinition.js";
5
- import { type LambderApiGuard } from "./LambderApiGuards.js";
6
- import { type LambderApiRateLimitPolicyConfig, type LambderApiRateLimitsConfig } from "./LambderApiRateLimits.js";
7
- import { type LambderApiIdempotencyConfig } from "./LambderApiIdempotency.js";
8
- import type { LambderRateLimiter } from "../shared/contracts/LambderRateLimiter.js";
9
- import type { LambderIdempotencyStore } from "../shared/contracts/LambderIdempotencyStore.js";
10
- import { LAMBDER_BACKEND_SWAP } from "../shared/util/LambderTestingDoors.js";
11
- /**
12
- * Runtime side of the declarative API options: composes the three policy
13
- * subsystems (rate limits in ./LambderApiRateLimits.ts, guards in
14
- * ./LambderApiGuards.ts, idempotency in ./LambderApiIdempotency.ts), asserts
15
- * registrations against them at startup, and executes them around handlers
16
- * at request time. Owned by LambderApiPipeline; apps interact through the
17
- * create() options (rateLimits, guards, idempotency) and the per-API
18
- * options.
19
- */
20
- export declare class LambderApiPolicyEngine {
21
- private rateLimits;
22
- private guards;
23
- private idempotency;
24
- /** True once any of the three subsystems was configured. */
25
- get isConfigured(): boolean;
26
- configureRateLimits(config: LambderApiRateLimitsConfig<Record<string, LambderApiRateLimitPolicyConfig>>): void;
27
- configureGuards(guards: Record<string, LambderApiGuard<any, any, any>>): void;
28
- configureIdempotency(config: LambderApiIdempotencyConfig): void;
29
- /** The backend swap, handed on to the two subsystems that hold a store. Each answers whether it had a place for one. */
30
- [LAMBDER_BACKEND_SWAP](backends: {
31
- rateLimiter?: LambderRateLimiter;
32
- idempotencyStore?: LambderIdempotencyStore;
33
- }): {
34
- rateLimits: boolean;
35
- idempotency: boolean;
36
- };
37
- /** Startup validation of one API's declarative options. */
38
- assertRegistration(definition: LambderApiDefinition): void;
39
- /** The rate-limit policies that can be checked before the session is read: see LambderApiRateLimitsEngine.run. */
40
- runSessionlessRateLimits(request: LambderApiRequest, ctx: LambderApiCallContext, definition: LambderApiDefinition): Promise<void>;
41
- /** The remaining rate limits, then guards, in declared order. Refusals throw; the trace records each guard as it runs. */
42
- runPreflight(request: LambderApiRequest, ctx: LambderApiCallContext, definition: LambderApiDefinition, trace: LambderApiCallTrace): Promise<void>;
43
- /** Idempotency replay fast path, run before the preflight: see LambderApiIdempotencyEngine.findReplay. */
44
- findReplay(request: LambderApiRequest, ctx: LambderApiCallContext, definition: LambderApiDefinition, trace: LambderApiCallTrace): Promise<LambderApiAnswer | null>;
45
- /** Idempotency claim/replay wrapper around handler execution: see LambderApiIdempotencyEngine.withIdempotency. */
46
- withIdempotency(request: LambderApiRequest, ctx: LambderApiCallContext, definition: LambderApiDefinition, trace: LambderApiCallTrace, exec: () => Promise<LambderApiAnswer>): Promise<LambderApiAnswer>;
47
- }
@@ -1,85 +0,0 @@
1
- import { LambderApiGuardsEngine } from "./LambderApiGuards.js";
2
- import { LambderApiRateLimitsEngine } from "./LambderApiRateLimits.js";
3
- import { LambderApiIdempotencyEngine } from "./LambderApiIdempotency.js";
4
- import { LAMBDER_BACKEND_SWAP } from "../shared/util/LambderTestingDoors.js";
5
- /** An API that asks for idempotency: declared, and not the explicit `false` opt-out. */
6
- const usesIdempotency = (definition) => definition.idempotency !== undefined && definition.idempotency !== false;
7
- /**
8
- * Runtime side of the declarative API options: composes the three policy
9
- * subsystems (rate limits in ./LambderApiRateLimits.ts, guards in
10
- * ./LambderApiGuards.ts, idempotency in ./LambderApiIdempotency.ts), asserts
11
- * registrations against them at startup, and executes them around handlers
12
- * at request time. Owned by LambderApiPipeline; apps interact through the
13
- * create() options (rateLimits, guards, idempotency) and the per-API
14
- * options.
15
- */
16
- export class LambderApiPolicyEngine {
17
- rateLimits = new LambderApiRateLimitsEngine();
18
- guards = new LambderApiGuardsEngine();
19
- idempotency = new LambderApiIdempotencyEngine();
20
- /** True once any of the three subsystems was configured. */
21
- get isConfigured() {
22
- return this.rateLimits.isConfigured || this.guards.isConfigured || this.idempotency.isConfigured;
23
- }
24
- configureRateLimits(config) {
25
- this.rateLimits.configure(config);
26
- }
27
- configureGuards(guards) {
28
- this.guards.configure(guards);
29
- }
30
- configureIdempotency(config) {
31
- this.idempotency.configure(config);
32
- }
33
- /** The backend swap, handed on to the two subsystems that hold a store. Each answers whether it had a place for one. */
34
- [LAMBDER_BACKEND_SWAP](backends) {
35
- return {
36
- rateLimits: backends.rateLimiter ? this.rateLimits[LAMBDER_BACKEND_SWAP](backends.rateLimiter) : false,
37
- idempotency: backends.idempotencyStore ? this.idempotency[LAMBDER_BACKEND_SWAP](backends.idempotencyStore) : false,
38
- };
39
- }
40
- /** Startup validation of one API's declarative options. */
41
- assertRegistration(definition) {
42
- const { name, mode } = definition;
43
- // Each subsystem reports its own absence. One combined message would
44
- // tell an API that declares guards on an instance with no guards map
45
- // that none of the three was configured, which reads as a question
46
- // about all three when only one of them is missing.
47
- if (definition.rateLimit !== undefined && !this.rateLimits.isConfigured) {
48
- throw new Error(`Lambder: API "${name}" declares rateLimit but no rateLimits option was configured at creation.`);
49
- }
50
- if (definition.guards !== undefined && !this.guards.isConfigured) {
51
- throw new Error(`Lambder: API "${name}" declares guards but no guards option was configured at creation.`);
52
- }
53
- this.rateLimits.assertRegistration(name, mode, definition.rateLimit);
54
- this.guards.assertRegistration(name, mode, definition.guards);
55
- // `idempotency: false` is an explicit opt-out, not a use: it asks for
56
- // nothing and so needs no store behind it.
57
- if (usesIdempotency(definition)) {
58
- if (!this.idempotency.isConfigured) {
59
- throw new Error(`Lambder: API "${name}" declares idempotency but no idempotency store was configured at creation.`);
60
- }
61
- this.idempotency.assertRegistration(name, definition.idempotency);
62
- }
63
- }
64
- /** The rate-limit policies that can be checked before the session is read: see LambderApiRateLimitsEngine.run. */
65
- async runSessionlessRateLimits(request, ctx, definition) {
66
- await this.rateLimits.run(definition.name, request, ctx, definition.rateLimit, "beforeSession");
67
- }
68
- /** The remaining rate limits, then guards, in declared order. Refusals throw; the trace records each guard as it runs. */
69
- async runPreflight(request, ctx, definition, trace) {
70
- await this.rateLimits.run(definition.name, request, ctx, definition.rateLimit, "afterSession");
71
- await this.guards.run(request, ctx, definition.guards, trace);
72
- }
73
- /** Idempotency replay fast path, run before the preflight: see LambderApiIdempotencyEngine.findReplay. */
74
- async findReplay(request, ctx, definition, trace) {
75
- if (!definition.idempotency)
76
- return null;
77
- return await this.idempotency.findReplay(definition.name, request, ctx, trace);
78
- }
79
- /** Idempotency claim/replay wrapper around handler execution: see LambderApiIdempotencyEngine.withIdempotency. */
80
- async withIdempotency(request, ctx, definition, trace, exec) {
81
- if (!definition.idempotency)
82
- return await exec();
83
- return await this.idempotency.withIdempotency(definition.name, request, ctx, definition.idempotency, trace, exec);
84
- }
85
- }
@@ -1,32 +0,0 @@
1
- /**
2
- * Joining the fields of a tracker or scope key.
3
- *
4
- * Both the rate limiter and the idempotency engine build a key by joining a
5
- * few fields with a separator, and at least one field in each is caller data:
6
- * a rate-limit key returned by a policy handler, an idempotency key posted
7
- * with the request. A plain join lets two different field lists produce one
8
- * string, so two callers land on one counter or one caller reads another's
9
- * stored answer.
10
- *
11
- * Escaping the caller's use of the separator rather than rejecting or
12
- * reserving it: the caller chose the character for its own reasons, and a
13
- * limit that refuses a legal key is a bug of its own.
14
- *
15
- * The join is one-way. Nothing here reads a key back apart, because nothing
16
- * needs to: a key is looked up, counted against or compared, never taken to
17
- * pieces. The escaping is there so that distinct field lists cannot collide,
18
- * not so that the fields can be recovered.
19
- *
20
- * LambderDdbCache's sort-key encoding looks similar and is deliberately the
21
- * other thing: a reversible escape, because a cached entry's sort key is
22
- * handed back to the caller by listSortKeys and therefore has to decode to
23
- * exactly what was written. Two schemes, two jobs; neither should be reached
24
- * for in the other's place.
25
- *
26
- * Written once because the two engines had drifted, one escaping and one not.
27
- */
28
- /**
29
- * The fields joined into one key, each escaped, so no two distinct field
30
- * lists can produce the same string.
31
- */
32
- export declare const joinKeyFields: (...fields: string[]) => string;
@@ -1,34 +0,0 @@
1
- /**
2
- * Joining the fields of a tracker or scope key.
3
- *
4
- * Both the rate limiter and the idempotency engine build a key by joining a
5
- * few fields with a separator, and at least one field in each is caller data:
6
- * a rate-limit key returned by a policy handler, an idempotency key posted
7
- * with the request. A plain join lets two different field lists produce one
8
- * string, so two callers land on one counter or one caller reads another's
9
- * stored answer.
10
- *
11
- * Escaping the caller's use of the separator rather than rejecting or
12
- * reserving it: the caller chose the character for its own reasons, and a
13
- * limit that refuses a legal key is a bug of its own.
14
- *
15
- * The join is one-way. Nothing here reads a key back apart, because nothing
16
- * needs to: a key is looked up, counted against or compared, never taken to
17
- * pieces. The escaping is there so that distinct field lists cannot collide,
18
- * not so that the fields can be recovered.
19
- *
20
- * LambderDdbCache's sort-key encoding looks similar and is deliberately the
21
- * other thing: a reversible escape, because a cached entry's sort key is
22
- * handed back to the caller by listSortKeys and therefore has to decode to
23
- * exactly what was written. Two schemes, two jobs; neither should be reached
24
- * for in the other's place.
25
- *
26
- * Written once because the two engines had drifted, one escaping and one not.
27
- */
28
- /** One field, with the separator and its own escape character escaped. */
29
- const escapeKeyField = (value) => value.replace(/\\/g, "\\\\").replace(/\|/g, "\\|");
30
- /**
31
- * The fields joined into one key, each escaped, so no two distinct field
32
- * lists can produce the same string.
33
- */
34
- export const joinKeyFields = (...fields) => fields.map(escapeKeyField).join("|");