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
@@ -0,0 +1,154 @@
1
+ /** What an app accepts for one kind of upload, declared once and read by both sides. */
2
+ export type LambderUploadRule = {
3
+ /** The largest file accepted, in bytes. */
4
+ maxBytes: number;
5
+ /** The accepted content types, exact, with no wildcards: `["application/pdf"]`. */
6
+ mimeTypes: readonly string[];
7
+ };
8
+ /** What the browser says about a file before any of its bytes move. */
9
+ export type LambderUploadFileFacts = {
10
+ fileName: string;
11
+ mimeType: string;
12
+ byteSize: number;
13
+ /** SHA-256 of the file's bytes as base64, the form storage checks an upload against: 43 characters and one pad. */
14
+ sha256Base64: string;
15
+ };
16
+ /** Everything the browser needs to post one file to storage, and until when. */
17
+ export type LambderUploadTicket = {
18
+ uploadUrl: string;
19
+ /** Sent as form fields ahead of the file, which storage wants last. */
20
+ formFields: Record<string, string>;
21
+ /** Epoch milliseconds after which storage refuses the ticket. */
22
+ expiresAt: number;
23
+ };
24
+ /** What a bucket holds under a key, compared with what the browser said it would upload. */
25
+ export type LambderUploadVerdict = {
26
+ verified: true;
27
+ }
28
+ /** `objectMissing`: nothing was posted. `factsMismatch`: something else sits under the key. */
29
+ | {
30
+ verified: false;
31
+ reason: "objectMissing" | "factsMismatch";
32
+ };
33
+ /** How a browser presents an object it reads: in place (a PDF in its viewer) or saved as a file, under a name. */
34
+ export type LambderUploadContentDisposition = {
35
+ disposition: "inline" | "attachment";
36
+ /** The name the file is saved or shown under; any characters, encoded for the header. Without one, the browser takes the key's last segment. */
37
+ fileName?: string;
38
+ };
39
+ /**
40
+ * What storage keeps beside an object's bytes. A ticket pins every one of
41
+ * these in its signed policy, so the browser posts them unchanged.
42
+ */
43
+ export type LambderUploadObjectOptions = {
44
+ /**
45
+ * The object's tags, at most ten. They are how an object gets a time to
46
+ * live: S3 has no expiry per object, and a lifecycle rule keyed on a tag
47
+ * (`retention: "30d"` expiring after 30 days) deletes what carries it.
48
+ * Keys up to 128 characters, values up to 256.
49
+ */
50
+ tags?: Record<string, string>;
51
+ /** User metadata, kept as `x-amz-meta-<name>` and returned with every read of the object. Names are lowercased; printable ASCII, 2 KB in all. */
52
+ metadata?: Record<string, string>;
53
+ /** The Cache-Control every read of the object answers with, for one served through a CDN. */
54
+ cacheControl?: string;
55
+ /** How a browser presents the object by default; a download link can say otherwise. */
56
+ contentDisposition?: LambderUploadContentDisposition;
57
+ };
58
+ /** The longest a ticket or a download link may live: S3's limit for a signature, seven days. */
59
+ export declare const UPLOAD_SIGNATURE_MAX_SECONDS: number;
60
+ /** Throws unless a lifetime is a positive number of seconds within S3's seven days. */
61
+ export declare const assertSignatureLifetime: (seconds: number, name: string) => void;
62
+ /** Throws when object options break a limit S3 holds them to, so the mistake shows where the app wrote it rather than as a refused post. */
63
+ export declare const assertObjectOptions: (options: LambderUploadObjectOptions | undefined) => void;
64
+ /** What a rule holds against a file, known before any of it is sent. */
65
+ export type LambderUploadRuleVerdict = "fileEmpty" | "fileTypeRejected" | "fileTooLarge";
66
+ /** A rule's verdict on a file, or null when it may be uploaded: the check the browser makes before hashing and the bucket makes before signing. */
67
+ export declare const checkUploadRule: (rule: LambderUploadRule, file: {
68
+ mimeType: string;
69
+ byteSize: number;
70
+ }) => LambderUploadRuleVerdict | null;
71
+ /**
72
+ * Throws when an object key would not pin the key a ticket writes to: S3
73
+ * substitutes the uploaded file's own name for `${filename}` in a presigned
74
+ * POST's key, so a key holding it lets the browser choose where under it the
75
+ * file lands. A key is the app's, built from its own ids, so this is a
76
+ * programming error rather than a refusal.
77
+ */
78
+ export declare const assertPinnedObjectKey: (objectKey: string) => void;
79
+ /**
80
+ * Object storage a browser uploads to directly, with tickets the server
81
+ * signs, and that the server reads, writes and deletes through for the rest
82
+ * of an object's life. One instance per bucket. LambderS3UploadBucket in
83
+ * production; LambderMemoryUploadBucket in tests and the mock runtime.
84
+ *
85
+ * The object key is always chosen by the app from its own ids. It never
86
+ * comes from the browser, and a ticket pins it, so a ticket cannot be used to
87
+ * write anywhere else.
88
+ */
89
+ export interface LambderUploadBucket {
90
+ /**
91
+ * Signs a ticket for exactly the file the browser described, or refuses
92
+ * (a LambderApiRefusal, code `lambder/upload-empty`,
93
+ * `lambder/upload-type-rejected` or `lambder/upload-too-large`) when the
94
+ * rule does not accept it. Storage then enforces every fact: the post
95
+ * fails unless the body has that byte size, that content type and that
96
+ * SHA-256, so what verifies later is what was described here. `object`
97
+ * is what the stored object carries besides, pinned the same way, and
98
+ * `lifetimeSeconds` overrides the bucket's ticket lifetime for this one.
99
+ */
100
+ issueUploadTicket(options: {
101
+ objectKey: string;
102
+ fileFacts: LambderUploadFileFacts;
103
+ uploadRule: LambderUploadRule;
104
+ lifetimeSeconds?: number;
105
+ object?: LambderUploadObjectOptions;
106
+ }): Promise<LambderUploadTicket>;
107
+ /**
108
+ * Asks storage what it holds under the key. The browser saying "done"
109
+ * proves nothing, so an app calls this before its record counts as
110
+ * uploaded.
111
+ */
112
+ verifyUploadedObject(options: {
113
+ objectKey: string;
114
+ fileFacts: Pick<LambderUploadFileFacts, "byteSize" | "sha256Base64">;
115
+ }): Promise<LambderUploadVerdict>;
116
+ /**
117
+ * A link the browser can read the object with (a preview, a download)
118
+ * until it expires: `lifetimeSeconds`, or the bucket's link lifetime.
119
+ * `contentDisposition` decides for this link whether the browser shows
120
+ * the file or saves it, and under what name.
121
+ */
122
+ issueDownloadUrl(options: {
123
+ objectKey: string;
124
+ lifetimeSeconds?: number;
125
+ contentDisposition?: LambderUploadContentDisposition;
126
+ }): Promise<string>;
127
+ /** The object's bytes, for work the server does on a file itself. Throws when the key holds nothing. */
128
+ readObject(objectKey: string): Promise<Uint8Array>;
129
+ /**
130
+ * Stores bytes the server produced, with their SHA-256 checked by storage
131
+ * on the way in, as a ticket checks a browser's upload. Pass the digest
132
+ * when it is already known; otherwise it is computed. `object` is what
133
+ * the object carries besides, as for a ticket.
134
+ */
135
+ writeObject(options: {
136
+ objectKey: string;
137
+ body: Uint8Array;
138
+ mimeType: string;
139
+ sha256Base64?: string;
140
+ object?: LambderUploadObjectOptions;
141
+ }): Promise<void>;
142
+ /**
143
+ * A second object with the same bytes, made inside storage: nothing is
144
+ * read into the function, so a file of any size copies in one call. For
145
+ * records that must each own their file, so deleting one never takes the
146
+ * other's. The copy carries the source's type, metadata and tags.
147
+ */
148
+ copyObject(options: {
149
+ fromObjectKey: string;
150
+ toObjectKey: string;
151
+ }): Promise<void>;
152
+ /** Removes the object. Deleting a key that holds nothing is not an error. */
153
+ deleteObject(objectKey: string): Promise<void>;
154
+ }
@@ -0,0 +1,74 @@
1
+ /*
2
+ * Direct uploads: a file that travels from the browser straight to object
3
+ * storage, never through the app's function.
4
+ *
5
+ * An API payload tops out near a few megabytes once a file is base64, and a
6
+ * Lambda's request body at six, so anything larger (a scanned lease, a
7
+ * signed PDF, a video) is posted by the browser to the bucket itself, with a
8
+ * ticket the server signed beforehand. The ticket pins everything about the
9
+ * upload: the key, the exact byte size, the content type and the SHA-256 of
10
+ * the bytes, all enforced by the storage, so the browser can only ever store
11
+ * the one file it described.
12
+ *
13
+ * The conversation is the same three steps whatever the app stores: the
14
+ * browser describes the file (LambderUploadFileFacts), the app's endpoint
15
+ * answers with a ticket (LambderUploadTicket), and after the post the app's
16
+ * confirm endpoint asks the bucket what arrived before its record counts as
17
+ * uploaded. The server half is a LambderUploadBucket, the browser half is
18
+ * LambderUploadRunner.
19
+ *
20
+ * This module is the vocabulary both halves share and the contract a bucket
21
+ * implements, and it imports nothing: the zod schemas an endpoint declares
22
+ * its input and output with are in wire/LambderUploadSchemas.ts.
23
+ */
24
+ /** The longest a ticket or a download link may live: S3's limit for a signature, seven days. */
25
+ export const UPLOAD_SIGNATURE_MAX_SECONDS = 7 * 24 * 60 * 60;
26
+ /** Throws unless a lifetime is a positive number of seconds within S3's seven days. */
27
+ export const assertSignatureLifetime = (seconds, name) => {
28
+ if (!(Number.isFinite(seconds) && seconds > 0 && seconds <= UPLOAD_SIGNATURE_MAX_SECONDS)) {
29
+ throw new RangeError(`${name} must be a number of seconds above 0 and at most ${UPLOAD_SIGNATURE_MAX_SECONDS} (seven days): ${seconds}`);
30
+ }
31
+ };
32
+ /** Throws when object options break a limit S3 holds them to, so the mistake shows where the app wrote it rather than as a refused post. */
33
+ export const assertObjectOptions = (options) => {
34
+ const tags = Object.entries(options?.tags ?? {});
35
+ if (tags.length > 10)
36
+ throw new RangeError(`An object carries at most 10 tags: ${tags.length}`);
37
+ for (const [key, value] of tags) {
38
+ if (!key || key.length > 128 || value.length > 256)
39
+ throw new RangeError(`A tag's key is 1 to 128 characters and its value at most 256: ${key}`);
40
+ }
41
+ let metadataBytes = 0;
42
+ for (const [name, value] of Object.entries(options?.metadata ?? {})) {
43
+ if (!/^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(name))
44
+ throw new RangeError(`A metadata name is letters, digits, "-" and "_": ${name}`);
45
+ if (!/^[\x20-\x7e]*$/.test(value))
46
+ throw new RangeError(`A metadata value is printable ASCII: ${name}`);
47
+ metadataBytes += name.length + value.length;
48
+ }
49
+ if (metadataBytes > 2048)
50
+ throw new RangeError(`An object's metadata is at most 2 KB: ${metadataBytes} bytes`);
51
+ };
52
+ /** A rule's verdict on a file, or null when it may be uploaded: the check the browser makes before hashing and the bucket makes before signing. */
53
+ export const checkUploadRule = (rule, file) => {
54
+ if (file.byteSize <= 0)
55
+ return "fileEmpty";
56
+ if (!rule.mimeTypes.includes(file.mimeType))
57
+ return "fileTypeRejected";
58
+ if (file.byteSize > rule.maxBytes)
59
+ return "fileTooLarge";
60
+ return null;
61
+ };
62
+ /**
63
+ * Throws when an object key would not pin the key a ticket writes to: S3
64
+ * substitutes the uploaded file's own name for `${filename}` in a presigned
65
+ * POST's key, so a key holding it lets the browser choose where under it the
66
+ * file lands. A key is the app's, built from its own ids, so this is a
67
+ * programming error rather than a refusal.
68
+ */
69
+ export const assertPinnedObjectKey = (objectKey) => {
70
+ if (!objectKey)
71
+ throw new Error("An upload's object key is empty");
72
+ if (objectKey.includes("${filename}"))
73
+ throw new Error(`An upload's object key may not hold \${filename}, which storage replaces with the uploaded file's name: ${objectKey}`);
74
+ };
@@ -16,9 +16,9 @@ export type LambderApiTransportRequest = {
16
16
  token: string;
17
17
  /**
18
18
  * The cookie name the caller reads that token from. A transport that
19
- * fills the token in itself (the cookie-jar decorator, where there is no
20
- * document to read) needs the same name, and taking it from the caller is
21
- * what keeps the two from being configured apart.
19
+ * fills the token in itself (the cookie-jar decorator, with no document
20
+ * to read) needs the same name; taking it from the caller keeps the two
21
+ * from being configured apart.
22
22
  */
23
23
  csrfCookieKey?: string;
24
24
  siteHost: string;
@@ -36,10 +36,9 @@ export type LambderApiTransportRequest = {
36
36
  };
37
37
  /**
38
38
  * Why a transport could not deliver a call. `network` is the default reading
39
- * of a rejection: nothing came back. `protocol` says the call reached the
40
- * callee and no answer came of it, either because what came back was not one
41
- * or because the callee threw instead of answering, which is a server or
42
- * wiring fault and should not be reported to a developer as flaky
39
+ * of a rejection: nothing came back. `protocol` means the call reached the
40
+ * callee but produced no answer (what came back was not one, or the callee
41
+ * threw): a server or wiring fault, not to be reported as flaky
43
42
  * connectivity. `timeout` belongs to the caller, which knows whether its own
44
43
  * abort fired.
45
44
  */
@@ -60,27 +59,28 @@ export declare class LambderTransportFailure extends Error {
60
59
  export declare const isLambderTransportFailure: (err: unknown) => err is LambderTransportFailure;
61
60
  /**
62
61
  * Delivers one call and hands back the answer in the accessor form
63
- * resolveApiOutcome() reads. What a transport owes its caller, since nothing
64
- * but this contract stands between a call and a wrong outcome:
62
+ * resolveApiOutcome() reads. What a transport owes its caller:
65
63
  *
66
64
  * - **Any HTTP status is an answer.** A 4xx or 5xx resolves, status and body
67
65
  * included, because resolveApiOutcome() is the one place that reads what a
68
- * status means. A transport that rejects on a status throws away the
69
- * envelope a refusal, a validation failure or a crash arrived in.
66
+ * status means. Rejecting on a status would throw away the envelope a
67
+ * refusal, a validation failure or a crash arrived in.
70
68
  * - **A rejection is a transport failure.** The caller reports it as
71
- * `network` unless the transport threw a LambderTransportFailure naming
72
- * another reason, or `timeout` when the caller's own abort fired. That is
73
- * the channel for the real cause too: a LambderTransportFailure keeps it as
74
- * `cause`, where the caller's `outcome.error` carries it.
69
+ * `network`, as `timeout` when its own abort fired, or as the reason a
70
+ * thrown LambderTransportFailure names. That failure's `cause` carries the
71
+ * real error through to the caller's `outcome.error`.
75
72
  * - **`request.signal` must be honoured**, by rejecting as soon as it aborts.
76
- * It is the only thing that makes the caller's `timeoutMs` and its per-call
77
- * `signal` mean anything: a transport that ignores it leaves a call waiting
78
- * for as long as the callee takes, whatever the caller asked for. Work
79
- * already begun need not be cancellable (an in-process handler is not); the
80
- * obligation is to stop waiting, not to stop the callee.
73
+ * Without that, the caller's `timeoutMs` and per-call `signal` mean
74
+ * nothing and a call waits as long as the callee takes. Work already begun
75
+ * need not be cancellable (an in-process handler is not); the obligation
76
+ * is to stop waiting, not to stop the callee.
81
77
  * - **Timeouts and retries belong to the caller.** A transport starts no
82
78
  * clock of its own and retries nothing, so one call is one delivery
83
79
  * attempt and an idempotency key means what it says.
80
+ * - **A transport that keeps the session's cookies itself reports its CSRF
81
+ * tokens** on the answer (`csrfTokens`), the one it posted and a read of the
82
+ * one it holds, since the caller otherwise judges a sessionExpired by
83
+ * document.cookie, which such a transport never writes.
84
84
  *
85
85
  * Four transports ship: fetch (lambderFetchTransport, the browser default),
86
86
  * an in-process Lambder handler (lambderHandlerTransport, for tests), the
@@ -89,16 +89,16 @@ export declare const isLambderTransportFailure: (err: unknown) => err is Lambder
89
89
  */
90
90
  export type LambderApiTransport = (request: LambderApiTransportRequest) => Promise<LambderApiHttpAnswer>;
91
91
  /**
92
- * The fields of the request envelope, in the order they go on the wire: the
93
- * one statement of what a call sends, for every sender there is.
92
+ * The fields of the request envelope, in wire order: the one statement of
93
+ * what a call sends, for every sender.
94
94
  *
95
95
  * Two senders write it. A transport hands the payload over as a value
96
96
  * (buildTransportEnvelope, below); LambderInvokeCaller has already serialized
97
- * its payload to decide whether to compress it, and splices that JSON onto the
98
- * end rather than parsing and stringifying it a second time
99
- * (buildEnvelopeJson, in invoke/LambderLambdaEvent.ts). The splice sits on top
100
- * of this function precisely so that a new envelope field cannot be added to
101
- * one sender and missed by the other, which nothing on the wire would catch.
97
+ * its payload to decide on compression, and splices that JSON onto the end
98
+ * rather than parsing and stringifying it again (buildEnvelopeJson, in
99
+ * invoke/LambderLambdaEvent.ts). The splice builds on this function so a new
100
+ * envelope field cannot reach one sender and miss the other, which nothing on
101
+ * the wire would catch.
102
102
  */
103
103
  export declare const buildEnvelopeFields: (fields: {
104
104
  apiName: string;
@@ -15,16 +15,16 @@ export class LambderTransportFailure extends Error {
15
15
  /** Brand-based type guard, so a duplicate install of the package still matches. */
16
16
  export const isLambderTransportFailure = (err) => err instanceof Error && err.isLambderTransportFailure === true;
17
17
  /**
18
- * The fields of the request envelope, in the order they go on the wire: the
19
- * one statement of what a call sends, for every sender there is.
18
+ * The fields of the request envelope, in wire order: the one statement of
19
+ * what a call sends, for every sender.
20
20
  *
21
21
  * Two senders write it. A transport hands the payload over as a value
22
22
  * (buildTransportEnvelope, below); LambderInvokeCaller has already serialized
23
- * its payload to decide whether to compress it, and splices that JSON onto the
24
- * end rather than parsing and stringifying it a second time
25
- * (buildEnvelopeJson, in invoke/LambderLambdaEvent.ts). The splice sits on top
26
- * of this function precisely so that a new envelope field cannot be added to
27
- * one sender and missed by the other, which nothing on the wire would catch.
23
+ * its payload to decide on compression, and splices that JSON onto the end
24
+ * rather than parsing and stringifying it again (buildEnvelopeJson, in
25
+ * invoke/LambderLambdaEvent.ts). The splice builds on this function so a new
26
+ * envelope field cannot reach one sender and miss the other, which nothing on
27
+ * the wire would catch.
28
28
  */
29
29
  export const buildEnvelopeFields = (fields) => ({
30
30
  apiName: fields.apiName,
@@ -36,28 +36,23 @@ export declare const parseSetCookie: (header: string, now: number, requestPath?:
36
36
  * in-process handler transport in a Node test, and the mock runtime's direct
37
37
  * transport. It stores what an answer's Set-Cookie headers set, honours their
38
38
  * expiry and deletion, and hands back the Cookie pairs the next request should
39
- * carry. One jar is one browser; two jars are two.
39
+ * carry. One jar is one browser.
40
40
  *
41
- * The rules themselves are tough-cookie's, which is the reference
42
- * implementation of RFC 6265 and carries the public suffix list: domain and
43
- * path matching, default-path, Max-Age against Expires, Secure, HttpOnly, and
44
- * the __Host-/__Secure- prefixes. That list is the part worth importing rather
45
- * than writing. A hand-rolled check can tell that `Domain=com` is a registry
46
- * suffix by counting labels, and cannot tell that `co.uk` is one, so a
47
- * hand-rolled jar either trusts `Domain=co.uk` or bans every two-label domain.
41
+ * The rules (domain and path matching, default-path, Max-Age against Expires,
42
+ * Secure, HttpOnly, the __Host-/__Secure- prefixes) are tough-cookie's, the
43
+ * reference RFC 6265 implementation, imported for its public suffix list:
44
+ * counting labels can tell `Domain=com` is a registry suffix but not `co.uk`,
45
+ * so a hand-rolled jar either trusts `Domain=co.uk` or bans every two-label
46
+ * domain.
48
47
  *
49
- * What stays Lambder's is the shape of the questions a transport asks: whole
50
- * Set-Cookie header lists in (storeSetCookies), `name=value` pairs out
51
- * (cookiePairs), and a target given as a host and path rather than a URL,
52
- * since a transport that never speaks HTTP has no URL to give. A field the
53
- * caller omits is one it could not know, and an unknown field matches
54
- * anything: a jar pointed at a single host is the ordinary case, and refusing
55
- * to answer it until it can name that host would make the common setup the
56
- * awkward one.
48
+ * What stays Lambder's is the shape of a transport's questions: Set-Cookie
49
+ * header lists in (storeSetCookies), `name=value` pairs out (cookiePairs),
50
+ * and a target given as host and path, since a transport that never speaks
51
+ * HTTP has no URL. An omitted field matches anything, because a jar pointed
52
+ * at a single host is the ordinary case and should not have to name it.
57
53
  *
58
- * SameSite is stored but never consulted. It answers "did another site
59
- * initiate this", and a transport call has no initiating site: every call here
60
- * is same-site by construction.
54
+ * SameSite is stored but never consulted: it answers "did another site
55
+ * initiate this", and every transport call is same-site by construction.
61
56
  */
62
57
  export declare class LambderCookieJar {
63
58
  private readonly jar;
@@ -76,25 +71,24 @@ export declare class LambderCookieJar {
76
71
  * came from. Its `host` is the sending host, which every Domain is checked
77
72
  * against, and its `path` is the default Path of a cookie that names none.
78
73
  *
79
- * A Domain the sender is not under does not narrow a cookie, it voids it
80
- * (RFC 6265 section 5.3 step 6), and so does a Domain that is a public
81
- * suffix. Both are how evil.example.com would otherwise plant a cookie
82
- * that bank.example.com is handed on the next call.
74
+ * A Domain the sender is not under voids the cookie rather than narrowing
75
+ * it (RFC 6265 section 5.3 step 6), and so does a public-suffix Domain.
76
+ * Otherwise evil.example.com could plant a cookie that bank.example.com
77
+ * is handed on the next call.
83
78
  */
84
79
  storeSetCookies(headers: readonly string[], request?: LambderCookieTarget): void;
85
80
  /** Every live cookie. */
86
81
  list(): LambderStoredCookie[];
87
82
  /**
88
83
  * The Cookie header pairs the next request carries, as `name=value`, in
89
- * the order RFC 6265 section 5.4 puts them in: the longest Path first,
90
- * and among equal paths the one set first. Servers that read only the
91
- * first value of a repeated name depend on that order, and so does any
92
- * test reasoning about which of two same-named cookies wins.
84
+ * RFC 6265 section 5.4 order: longest Path first, and among equal paths
85
+ * the one set first. Servers that read only the first value of a repeated
86
+ * name depend on that order, as does any test about which of two
87
+ * same-named cookies wins.
93
88
  *
94
- * Only the cookies whose scope covers the target travel. A field the
95
- * target leaves out is one the caller could not know, and matches
96
- * anything: a caller that cannot name its own host still gets the cookies
97
- * of the one host its jar talks to.
89
+ * Only cookies whose scope covers the target travel. An omitted target
90
+ * field matches anything, so a caller that cannot name its host still
91
+ * gets the cookies of the one host its jar talks to.
98
92
  */
99
93
  cookiePairs(target?: LambderCookieTarget): string[];
100
94
  /**
@@ -107,10 +101,9 @@ export declare class LambderCookieJar {
107
101
  } & LambderCookieTarget): string | undefined;
108
102
  /**
109
103
  * The live cookies whose scope reaches this target, in RFC 6265 send
110
- * order. Delegated to tough-cookie whenever the target names a host,
111
- * which is the case worth getting exactly right; an unnamed host falls
112
- * back to every cookie the jar holds, filtered by the rules that do not
113
- * need one and ordered by the same rule.
104
+ * order. Delegated to tough-cookie whenever a host is known, the case
105
+ * worth getting exactly right; with no host, every cookie the jar holds
106
+ * is filtered by the rules that need none and ordered the same way.
114
107
  */
115
108
  private matchingCookies;
116
109
  /** Number of live cookies. */