@marinoscar/platform-api 0.1.0-next.21 → 0.1.0-next.23

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 (225) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/ai/features/ai-feature.registry.d.ts +7 -0
  3. package/dist/ai/features/ai-feature.registry.d.ts.map +1 -1
  4. package/dist/ai/features/ai-feature.registry.js +3 -0
  5. package/dist/ai/features/ai-feature.registry.js.map +1 -1
  6. package/dist/ai/keys/ai-key-resolver.service.d.ts +16 -0
  7. package/dist/ai/keys/ai-key-resolver.service.d.ts.map +1 -1
  8. package/dist/ai/keys/ai-key-resolver.service.js +44 -0
  9. package/dist/ai/keys/ai-key-resolver.service.js.map +1 -1
  10. package/dist/ai/keys/usable-models.service.d.ts +13 -1
  11. package/dist/ai/keys/usable-models.service.d.ts.map +1 -1
  12. package/dist/ai/keys/usable-models.service.js +37 -12
  13. package/dist/ai/keys/usable-models.service.js.map +1 -1
  14. package/dist/ai/runtime/ai-limits.service.d.ts +2 -1
  15. package/dist/ai/runtime/ai-limits.service.d.ts.map +1 -1
  16. package/dist/ai/runtime/ai-limits.service.js +7 -2
  17. package/dist/ai/runtime/ai-limits.service.js.map +1 -1
  18. package/dist/ai/runtime/ai-runtime.types.d.ts +46 -1
  19. package/dist/ai/runtime/ai-runtime.types.d.ts.map +1 -1
  20. package/dist/ai/runtime/ai-runtime.types.js.map +1 -1
  21. package/dist/ai/runtime/ai.service.d.ts +142 -5
  22. package/dist/ai/runtime/ai.service.d.ts.map +1 -1
  23. package/dist/ai/runtime/ai.service.js +353 -46
  24. package/dist/ai/runtime/ai.service.js.map +1 -1
  25. package/dist/ai/runtime/index.d.ts +1 -1
  26. package/dist/ai/runtime/index.d.ts.map +1 -1
  27. package/dist/ai/runtime/target-resolver.d.ts +2 -2
  28. package/dist/ai/runtime/target-resolver.d.ts.map +1 -1
  29. package/dist/ai/runtime/target-resolver.js +2 -1
  30. package/dist/ai/runtime/target-resolver.js.map +1 -1
  31. package/dist/ai/testing/conformance/ai-key-policy.suite.d.ts.map +1 -1
  32. package/dist/ai/testing/conformance/ai-key-policy.suite.js +54 -0
  33. package/dist/ai/testing/conformance/ai-key-policy.suite.js.map +1 -1
  34. package/dist/ai/testing/conformance/ai-secret-egress.suite.d.ts.map +1 -1
  35. package/dist/ai/testing/conformance/ai-secret-egress.suite.js +59 -0
  36. package/dist/ai/testing/conformance/ai-secret-egress.suite.js.map +1 -1
  37. package/dist/identity/device-auth/device-auth.service.d.ts +9 -2
  38. package/dist/identity/device-auth/device-auth.service.d.ts.map +1 -1
  39. package/dist/identity/device-auth/device-auth.service.js +44 -4
  40. package/dist/identity/device-auth/device-auth.service.js.map +1 -1
  41. package/dist/identity/device-auth/dto/device-code-request.dto.d.ts +12 -0
  42. package/dist/identity/device-auth/dto/device-code-request.dto.d.ts.map +1 -1
  43. package/dist/identity/identity.options.d.ts +25 -0
  44. package/dist/identity/identity.options.d.ts.map +1 -1
  45. package/dist/identity/identity.options.js +11 -0
  46. package/dist/identity/identity.options.js.map +1 -1
  47. package/dist/identity/index.d.ts +1 -1
  48. package/dist/identity/index.d.ts.map +1 -1
  49. package/dist/nodes/current-node.decorator.d.ts +18 -0
  50. package/dist/nodes/current-node.decorator.d.ts.map +1 -0
  51. package/dist/nodes/current-node.decorator.js +28 -0
  52. package/dist/nodes/current-node.decorator.js.map +1 -0
  53. package/dist/nodes/index.d.ts +8 -0
  54. package/dist/nodes/index.d.ts.map +1 -1
  55. package/dist/nodes/index.js +21 -2
  56. package/dist/nodes/index.js.map +1 -1
  57. package/dist/nodes/node-ownership.d.ts +34 -0
  58. package/dist/nodes/node-ownership.d.ts.map +1 -0
  59. package/dist/nodes/node-ownership.js +92 -0
  60. package/dist/nodes/node-ownership.js.map +1 -0
  61. package/dist/nodes/node-route.decorator.d.ts +31 -0
  62. package/dist/nodes/node-route.decorator.d.ts.map +1 -0
  63. package/dist/nodes/node-route.decorator.js +54 -0
  64. package/dist/nodes/node-route.decorator.js.map +1 -0
  65. package/dist/nodes/node-route.guard.d.ts +22 -0
  66. package/dist/nodes/node-route.guard.d.ts.map +1 -0
  67. package/dist/nodes/node-route.guard.js +74 -0
  68. package/dist/nodes/node-route.guard.js.map +1 -0
  69. package/dist/nodes/node-route.module.d.ts +21 -0
  70. package/dist/nodes/node-route.module.d.ts.map +1 -0
  71. package/dist/nodes/node-route.module.js +99 -0
  72. package/dist/nodes/node-route.module.js.map +1 -0
  73. package/dist/nodes/node-route.options.d.ts +59 -0
  74. package/dist/nodes/node-route.options.d.ts.map +1 -0
  75. package/dist/nodes/node-route.options.js +29 -0
  76. package/dist/nodes/node-route.options.js.map +1 -0
  77. package/dist/nodes/node-route.paths.d.ts +47 -0
  78. package/dist/nodes/node-route.paths.d.ts.map +1 -0
  79. package/dist/nodes/node-route.paths.js +129 -0
  80. package/dist/nodes/node-route.paths.js.map +1 -0
  81. package/dist/nodes/nodes.service.d.ts.map +1 -1
  82. package/dist/nodes/nodes.service.js +3 -15
  83. package/dist/nodes/nodes.service.js.map +1 -1
  84. package/dist/nodes/testing/conformance.d.ts +86 -0
  85. package/dist/nodes/testing/conformance.d.ts.map +1 -0
  86. package/dist/nodes/testing/conformance.js +230 -0
  87. package/dist/nodes/testing/conformance.js.map +1 -0
  88. package/dist/nodes/testing/discover.d.ts +13 -0
  89. package/dist/nodes/testing/discover.d.ts.map +1 -0
  90. package/dist/nodes/testing/discover.js +42 -0
  91. package/dist/nodes/testing/discover.js.map +1 -0
  92. package/dist/nodes/testing/index.d.ts +3 -0
  93. package/dist/nodes/testing/index.d.ts.map +1 -0
  94. package/dist/nodes/testing/index.js +13 -0
  95. package/dist/nodes/testing/index.js.map +1 -0
  96. package/dist/notifications/channels/browser-notification.channel.d.ts +11 -0
  97. package/dist/notifications/channels/browser-notification.channel.d.ts.map +1 -1
  98. package/dist/notifications/channels/browser-notification.channel.js +71 -48
  99. package/dist/notifications/channels/browser-notification.channel.js.map +1 -1
  100. package/dist/notifications/data/notifications-db.d.ts +19 -0
  101. package/dist/notifications/data/notifications-db.d.ts.map +1 -1
  102. package/dist/notifications/dto/notification.dto.d.ts +4 -0
  103. package/dist/notifications/dto/notification.dto.d.ts.map +1 -1
  104. package/dist/notifications/index.d.ts +1 -1
  105. package/dist/notifications/index.d.ts.map +1 -1
  106. package/dist/notifications/index.js.map +1 -1
  107. package/dist/notifications/notification-inbox-writer.d.ts +112 -0
  108. package/dist/notifications/notification-inbox-writer.d.ts.map +1 -0
  109. package/dist/notifications/notification-inbox-writer.js +217 -0
  110. package/dist/notifications/notification-inbox-writer.js.map +1 -0
  111. package/dist/notifications/notification-store.service.d.ts +10 -0
  112. package/dist/notifications/notification-store.service.d.ts.map +1 -1
  113. package/dist/notifications/notification-store.service.js +55 -10
  114. package/dist/notifications/notification-store.service.js.map +1 -1
  115. package/dist/notifications/notification-stream.service.d.ts +8 -0
  116. package/dist/notifications/notification-stream.service.d.ts.map +1 -1
  117. package/dist/notifications/notification-stream.service.js.map +1 -1
  118. package/dist/notifications/notification.types.d.ts +74 -0
  119. package/dist/notifications/notification.types.d.ts.map +1 -1
  120. package/dist/notifications/notification.types.js.map +1 -1
  121. package/dist/notifications/notifications.controller.d.ts +1 -0
  122. package/dist/notifications/notifications.controller.d.ts.map +1 -1
  123. package/dist/notifications/notifications.controller.js +23 -0
  124. package/dist/notifications/notifications.controller.js.map +1 -1
  125. package/dist/notifications/notifications.service.d.ts +56 -1
  126. package/dist/notifications/notifications.service.d.ts.map +1 -1
  127. package/dist/notifications/notifications.service.js +122 -0
  128. package/dist/notifications/notifications.service.js.map +1 -1
  129. package/dist/notifications/registry/event.registry.d.ts +15 -0
  130. package/dist/notifications/registry/event.registry.d.ts.map +1 -1
  131. package/dist/notifications/registry/event.registry.js +3 -0
  132. package/dist/notifications/registry/event.registry.js.map +1 -1
  133. package/dist/notifications/retention/notification-inbox-purge.handler.d.ts.map +1 -1
  134. package/dist/notifications/retention/notification-inbox-purge.handler.js +16 -4
  135. package/dist/notifications/retention/notification-inbox-purge.handler.js.map +1 -1
  136. package/dist/notifications/testing/index.d.ts +2 -0
  137. package/dist/notifications/testing/index.d.ts.map +1 -1
  138. package/dist/notifications/testing/index.js +5 -1
  139. package/dist/notifications/testing/index.js.map +1 -1
  140. package/dist/storage/config/storage-config.service.d.ts +24 -0
  141. package/dist/storage/config/storage-config.service.d.ts.map +1 -1
  142. package/dist/storage/config/storage-config.service.js +31 -5
  143. package/dist/storage/config/storage-config.service.js.map +1 -1
  144. package/dist/storage/handlers/storage-cleanup.handler.d.ts +3 -1
  145. package/dist/storage/handlers/storage-cleanup.handler.d.ts.map +1 -1
  146. package/dist/storage/handlers/storage-cleanup.handler.js +10 -3
  147. package/dist/storage/handlers/storage-cleanup.handler.js.map +1 -1
  148. package/dist/storage/index.d.ts +5 -1
  149. package/dist/storage/index.d.ts.map +1 -1
  150. package/dist/storage/index.js +12 -3
  151. package/dist/storage/index.js.map +1 -1
  152. package/dist/storage/objects/dto/get-part-urls.dto.d.ts +26 -0
  153. package/dist/storage/objects/dto/get-part-urls.dto.d.ts.map +1 -0
  154. package/dist/storage/objects/dto/get-part-urls.dto.js +25 -0
  155. package/dist/storage/objects/dto/get-part-urls.dto.js.map +1 -0
  156. package/dist/storage/objects/dto/init-upload.dto.d.ts +2 -1
  157. package/dist/storage/objects/dto/init-upload.dto.d.ts.map +1 -1
  158. package/dist/storage/objects/objects.controller.d.ts +13 -1
  159. package/dist/storage/objects/objects.controller.d.ts.map +1 -1
  160. package/dist/storage/objects/objects.controller.js +88 -1
  161. package/dist/storage/objects/objects.controller.js.map +1 -1
  162. package/dist/storage/objects/objects.service.d.ts +46 -1
  163. package/dist/storage/objects/objects.service.d.ts.map +1 -1
  164. package/dist/storage/objects/objects.service.js +277 -23
  165. package/dist/storage/objects/objects.service.js.map +1 -1
  166. package/dist/storage/objects/part-body-parser.d.ts +30 -0
  167. package/dist/storage/objects/part-body-parser.d.ts.map +1 -0
  168. package/dist/storage/objects/part-body-parser.js +45 -0
  169. package/dist/storage/objects/part-body-parser.js.map +1 -0
  170. package/dist/storage/objects/part-body.d.ts +23 -0
  171. package/dist/storage/objects/part-body.d.ts.map +1 -0
  172. package/dist/storage/objects/part-body.js +48 -0
  173. package/dist/storage/objects/part-body.js.map +1 -0
  174. package/dist/storage/processing/object-processing.service.d.ts +4 -1
  175. package/dist/storage/processing/object-processing.service.d.ts.map +1 -1
  176. package/dist/storage/processing/object-processing.service.js +11 -3
  177. package/dist/storage/processing/object-processing.service.js.map +1 -1
  178. package/dist/storage/profile-image/avatar.service.d.ts +3 -1
  179. package/dist/storage/profile-image/avatar.service.d.ts.map +1 -1
  180. package/dist/storage/profile-image/avatar.service.js +8 -3
  181. package/dist/storage/profile-image/avatar.service.js.map +1 -1
  182. package/dist/storage/profile-image/profile-image.service.d.ts +4 -1
  183. package/dist/storage/profile-image/profile-image.service.d.ts.map +1 -1
  184. package/dist/storage/profile-image/profile-image.service.js +11 -5
  185. package/dist/storage/profile-image/profile-image.service.js.map +1 -1
  186. package/dist/storage/providers/index.d.ts +3 -1
  187. package/dist/storage/providers/index.d.ts.map +1 -1
  188. package/dist/storage/providers/index.js +8 -1
  189. package/dist/storage/providers/index.js.map +1 -1
  190. package/dist/storage/providers/resolving-storage.provider.d.ts +11 -0
  191. package/dist/storage/providers/resolving-storage.provider.d.ts.map +1 -1
  192. package/dist/storage/providers/resolving-storage.provider.js +13 -0
  193. package/dist/storage/providers/resolving-storage.provider.js.map +1 -1
  194. package/dist/storage/providers/s3/s3-storage.provider.d.ts +9 -1
  195. package/dist/storage/providers/s3/s3-storage.provider.d.ts.map +1 -1
  196. package/dist/storage/providers/s3/s3-storage.provider.js +37 -0
  197. package/dist/storage/providers/s3/s3-storage.provider.js.map +1 -1
  198. package/dist/storage/providers/storage-provider-router.d.ts +64 -0
  199. package/dist/storage/providers/storage-provider-router.d.ts.map +1 -0
  200. package/dist/storage/providers/storage-provider-router.js +161 -0
  201. package/dist/storage/providers/storage-provider-router.js.map +1 -0
  202. package/dist/storage/providers/storage-provider.interface.d.ts +34 -1
  203. package/dist/storage/providers/storage-provider.interface.d.ts.map +1 -1
  204. package/dist/storage/providers/storage-provider.interface.js.map +1 -1
  205. package/dist/storage/providers/storage-provider.types.d.ts +86 -0
  206. package/dist/storage/providers/storage-provider.types.d.ts.map +1 -1
  207. package/dist/storage/providers/storage-provider.types.js +78 -0
  208. package/dist/storage/providers/storage-provider.types.js.map +1 -1
  209. package/dist/storage/providers/storage-providers.module.d.ts.map +1 -1
  210. package/dist/storage/providers/storage-providers.module.js +3 -1
  211. package/dist/storage/providers/storage-providers.module.js.map +1 -1
  212. package/dist/storage/testing/conformance.d.ts +12 -0
  213. package/dist/storage/testing/conformance.d.ts.map +1 -1
  214. package/dist/storage/testing/conformance.js +112 -0
  215. package/dist/storage/testing/conformance.js.map +1 -1
  216. package/dist/storage/testing/driver-conformance.d.ts +13 -1
  217. package/dist/storage/testing/driver-conformance.d.ts.map +1 -1
  218. package/dist/storage/testing/driver-conformance.js +134 -0
  219. package/dist/storage/testing/driver-conformance.js.map +1 -1
  220. package/dist/storage/testing/index.d.ts +1 -1
  221. package/dist/storage/testing/index.d.ts.map +1 -1
  222. package/dist/storage/testing/index.js +2 -1
  223. package/dist/storage/testing/index.js.map +1 -1
  224. package/dist/telemetry/stack/dto/telemetry-stack.dto.d.ts +1 -1
  225. package/package.json +7 -3
@@ -87,4 +87,90 @@ export interface MultipartUploadInit {
87
87
  /** The object key. */
88
88
  key: string;
89
89
  }
90
+ /**
91
+ * One part of an in-flight multipart upload as the storage backend holds it.
92
+ *
93
+ * @stability experimental
94
+ */
95
+ export interface UploadedPart {
96
+ /** The part number (1-10000). */
97
+ partNumber: number;
98
+ /** The part's size in bytes, as the backend stores it. */
99
+ size: number;
100
+ /** The part's ETag, quoted or not exactly as the backend reports it; compare with {@link normalizeEtag}. */
101
+ eTag: string;
102
+ }
103
+ /**
104
+ * The result of storing one part through the API.
105
+ *
106
+ * @stability experimental
107
+ */
108
+ export interface StoredPart {
109
+ /** The part's ETag (an MD5 digest, quoted or not). */
110
+ eTag: string;
111
+ }
112
+ /**
113
+ * How a client gets a part to the backend (see `StorageProvider.partUploadMode`).
114
+ *
115
+ * - `presigned`: the provider signs a URL the client PUTs to directly.
116
+ * - `proxied`: the provider has no signed URLs; the bytes go through the API's
117
+ * part route into `StorageProvider.uploadPart`.
118
+ *
119
+ * @stability experimental
120
+ */
121
+ export type StoragePartUploadMode = 'presigned' | 'proxied';
122
+ /**
123
+ * A part body did not have the size the upload requires. Throw it from
124
+ * `StorageProvider.uploadPart` when the stream ends short or runs long; the
125
+ * objects API answers 400 `PART_SIZE_MISMATCH`.
126
+ *
127
+ * @stability experimental
128
+ */
129
+ export declare class PartSizeMismatchError extends Error {
130
+ readonly partNumber: number;
131
+ readonly expectedSize: number;
132
+ readonly receivedSize: number;
133
+ readonly exceeded: boolean;
134
+ /**
135
+ * @param partNumber - the part.
136
+ * @param expectedSize - the exact byte count the part must have.
137
+ * @param receivedSize - the bytes seen before the mismatch was detected.
138
+ * @param exceeded - true when the body was cut off for running past `expectedSize`.
139
+ */
140
+ constructor(partNumber: number, expectedSize: number, receivedSize: number, exceeded: boolean);
141
+ }
142
+ /**
143
+ * The backend no longer knows the multipart session (completed, aborted or
144
+ * garbage-collected). Throw it from `listUploadedParts` / `uploadPart` /
145
+ * `completeMultipartUpload`; the objects API answers 409 `UPLOAD_SESSION_INVALID`.
146
+ *
147
+ * @stability experimental
148
+ */
149
+ export declare class MultipartSessionNotFoundError extends Error {
150
+ readonly uploadId: string;
151
+ /** @param uploadId - the unknown session. */
152
+ constructor(uploadId: string);
153
+ }
154
+ /**
155
+ * `completeMultipartUpload` found parts missing or whose bytes do not match the
156
+ * ETag the client sent. Nothing is written; the objects API answers 409
157
+ * `UPLOAD_PARTS_MISSING` with the numbers so the client re-sends only those.
158
+ *
159
+ * @stability experimental
160
+ */
161
+ export declare class MultipartPartsMissingError extends Error {
162
+ readonly partNumbers: number[];
163
+ /** @param partNumbers - the missing or mismatching parts. */
164
+ constructor(partNumbers: number[]);
165
+ }
166
+ /**
167
+ * An ETag without its surrounding quotes, so a client's `"abc"` and a backend's
168
+ * `abc` compare equal.
169
+ *
170
+ * @param eTag - an ETag, quoted or not.
171
+ * @returns the bare value.
172
+ *
173
+ * @stability experimental
174
+ */
175
+ export declare function normalizeEtag(eTag: string): string;
90
176
  //# sourceMappingURL=storage-provider.types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"storage-provider.types.d.ts","sourceRoot":"","sources":["../../../src/storage/providers/storage-provider.types.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,sBAAsB;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,mCAAmC;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,sBAAsB;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,kBAAkB;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,qCAAqC;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,2BAA2B;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,0BAA0B,CAAC,EAAE,MAAM,CAAC;CACrC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,mBAAmB;IAClC,+EAA+E;IAC/E,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,sBAAsB;IACtB,GAAG,EAAE,MAAM,CAAC;CACb"}
1
+ {"version":3,"file":"storage-provider.types.d.ts","sourceRoot":"","sources":["../../../src/storage/providers/storage-provider.types.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,sBAAsB;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,mCAAmC;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,sBAAsB;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,kBAAkB;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,qCAAqC;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,2BAA2B;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,0BAA0B,CAAC,EAAE,MAAM,CAAC;CACrC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,mBAAmB;IAClC,+EAA+E;IAC/E,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,sBAAsB;IACtB,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,4GAA4G;IAC5G,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,qBAAqB,GAAG,WAAW,GAAG,SAAS,CAAC;AAE5D;;;;;;GAMG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;IAQ5C,QAAQ,CAAC,UAAU,EAAE,MAAM;IAC3B,QAAQ,CAAC,YAAY,EAAE,MAAM;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM;IAC7B,QAAQ,CAAC,QAAQ,EAAE,OAAO;IAV5B;;;;;OAKG;gBAEQ,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,OAAO;CAS7B;AAED;;;;;;GAMG;AACH,qBAAa,6BAA8B,SAAQ,KAAK;IAE1C,QAAQ,CAAC,QAAQ,EAAE,MAAM;IADrC,6CAA6C;gBACxB,QAAQ,EAAE,MAAM;CAItC;AAED;;;;;;GAMG;AACH,qBAAa,0BAA2B,SAAQ,KAAK;IAEvC,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE;IAD1C,6DAA6D;gBACxC,WAAW,EAAE,MAAM,EAAE;CAI3C;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAElD"}
@@ -1,3 +1,81 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MultipartPartsMissingError = exports.MultipartSessionNotFoundError = exports.PartSizeMismatchError = void 0;
4
+ exports.normalizeEtag = normalizeEtag;
5
+ /**
6
+ * A part body did not have the size the upload requires. Throw it from
7
+ * `StorageProvider.uploadPart` when the stream ends short or runs long; the
8
+ * objects API answers 400 `PART_SIZE_MISMATCH`.
9
+ *
10
+ * @stability experimental
11
+ */
12
+ class PartSizeMismatchError extends Error {
13
+ partNumber;
14
+ expectedSize;
15
+ receivedSize;
16
+ exceeded;
17
+ /**
18
+ * @param partNumber - the part.
19
+ * @param expectedSize - the exact byte count the part must have.
20
+ * @param receivedSize - the bytes seen before the mismatch was detected.
21
+ * @param exceeded - true when the body was cut off for running past `expectedSize`.
22
+ */
23
+ constructor(partNumber, expectedSize, receivedSize, exceeded) {
24
+ super(exceeded
25
+ ? `Part ${partNumber} is larger than the expected ${expectedSize} bytes`
26
+ : `Part ${partNumber} has ${receivedSize} bytes, expected ${expectedSize}`);
27
+ this.partNumber = partNumber;
28
+ this.expectedSize = expectedSize;
29
+ this.receivedSize = receivedSize;
30
+ this.exceeded = exceeded;
31
+ this.name = 'PartSizeMismatchError';
32
+ }
33
+ }
34
+ exports.PartSizeMismatchError = PartSizeMismatchError;
35
+ /**
36
+ * The backend no longer knows the multipart session (completed, aborted or
37
+ * garbage-collected). Throw it from `listUploadedParts` / `uploadPart` /
38
+ * `completeMultipartUpload`; the objects API answers 409 `UPLOAD_SESSION_INVALID`.
39
+ *
40
+ * @stability experimental
41
+ */
42
+ class MultipartSessionNotFoundError extends Error {
43
+ uploadId;
44
+ /** @param uploadId - the unknown session. */
45
+ constructor(uploadId) {
46
+ super(`Multipart upload ${uploadId} does not exist`);
47
+ this.uploadId = uploadId;
48
+ this.name = 'MultipartSessionNotFoundError';
49
+ }
50
+ }
51
+ exports.MultipartSessionNotFoundError = MultipartSessionNotFoundError;
52
+ /**
53
+ * `completeMultipartUpload` found parts missing or whose bytes do not match the
54
+ * ETag the client sent. Nothing is written; the objects API answers 409
55
+ * `UPLOAD_PARTS_MISSING` with the numbers so the client re-sends only those.
56
+ *
57
+ * @stability experimental
58
+ */
59
+ class MultipartPartsMissingError extends Error {
60
+ partNumbers;
61
+ /** @param partNumbers - the missing or mismatching parts. */
62
+ constructor(partNumbers) {
63
+ super(`Multipart upload is missing or has corrupt part(s): ${partNumbers.join(', ')}`);
64
+ this.partNumbers = partNumbers;
65
+ this.name = 'MultipartPartsMissingError';
66
+ }
67
+ }
68
+ exports.MultipartPartsMissingError = MultipartPartsMissingError;
69
+ /**
70
+ * An ETag without its surrounding quotes, so a client's `"abc"` and a backend's
71
+ * `abc` compare equal.
72
+ *
73
+ * @param eTag - an ETag, quoted or not.
74
+ * @returns the bare value.
75
+ *
76
+ * @stability experimental
77
+ */
78
+ function normalizeEtag(eTag) {
79
+ return eTag.replace(/^"+|"+$/g, '');
80
+ }
3
81
  //# sourceMappingURL=storage-provider.types.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"storage-provider.types.js","sourceRoot":"","sources":["../../../src/storage/providers/storage-provider.types.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"storage-provider.types.js","sourceRoot":"","sources":["../../../src/storage/providers/storage-provider.types.ts"],"names":[],"mappings":";;;AAyMA,sCAEC;AAtED;;;;;;GAMG;AACH,MAAa,qBAAsB,SAAQ,KAAK;IAQnC;IACA;IACA;IACA;IAVX;;;;;OAKG;IACH,YACW,UAAkB,EAClB,YAAoB,EACpB,YAAoB,EACpB,QAAiB;QAE1B,KAAK,CACH,QAAQ;YACN,CAAC,CAAC,QAAQ,UAAU,gCAAgC,YAAY,QAAQ;YACxE,CAAC,CAAC,QAAQ,UAAU,QAAQ,YAAY,oBAAoB,YAAY,EAAE,CAC7E,CAAC;QATO,eAAU,GAAV,UAAU,CAAQ;QAClB,iBAAY,GAAZ,YAAY,CAAQ;QACpB,iBAAY,GAAZ,YAAY,CAAQ;QACpB,aAAQ,GAAR,QAAQ,CAAS;QAO1B,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF;AApBD,sDAoBC;AAED;;;;;;GAMG;AACH,MAAa,6BAA8B,SAAQ,KAAK;IAEjC;IADrB,6CAA6C;IAC7C,YAAqB,QAAgB;QACnC,KAAK,CAAC,oBAAoB,QAAQ,iBAAiB,CAAC,CAAC;QADlC,aAAQ,GAAR,QAAQ,CAAQ;QAEnC,IAAI,CAAC,IAAI,GAAG,+BAA+B,CAAC;IAC9C,CAAC;CACF;AAND,sEAMC;AAED;;;;;;GAMG;AACH,MAAa,0BAA2B,SAAQ,KAAK;IAE9B;IADrB,6DAA6D;IAC7D,YAAqB,WAAqB;QACxC,KAAK,CAAC,uDAAuD,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QADpE,gBAAW,GAAX,WAAW,CAAU;QAExC,IAAI,CAAC,IAAI,GAAG,4BAA4B,CAAC;IAC3C,CAAC;CACF;AAND,gEAMC;AAED;;;;;;;;GAQG;AACH,SAAgB,aAAa,CAAC,IAAY;IACxC,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;AACtC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"storage-providers.module.d.ts","sourceRoot":"","sources":["../../../src/storage/providers/storage-providers.module.ts"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH;;;;;;;;;GASG;AACH,qBAca,sBAAsB;CAAG"}
1
+ {"version":3,"file":"storage-providers.module.d.ts","sourceRoot":"","sources":["../../../src/storage/providers/storage-providers.module.ts"],"names":[],"mappings":"AASA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH;;;;;;;;;GASG;AACH,qBAea,sBAAsB;CAAG"}
@@ -11,6 +11,7 @@ const common_1 = require("@nestjs/common");
11
11
  const index_1 = require("../../credentials/index");
12
12
  const storage_config_service_1 = require("../config/storage-config.service");
13
13
  const resolving_storage_provider_1 = require("./resolving-storage.provider");
14
+ const storage_provider_router_1 = require("./storage-provider-router");
14
15
  const storage_provider_binding_1 = require("./storage-provider.binding");
15
16
  const storage_provider_interface_1 = require("./storage-provider.interface");
16
17
  /**
@@ -82,13 +83,14 @@ exports.StorageProvidersModule = StorageProvidersModule = __decorate([
82
83
  providers: [
83
84
  storage_config_service_1.StorageConfigService,
84
85
  resolving_storage_provider_1.ResolvingStorageProvider,
86
+ storage_provider_router_1.StorageProviderRouter,
85
87
  {
86
88
  provide: storage_provider_interface_1.STORAGE_PROVIDER,
87
89
  useFactory: (binding, resolving) => binding ?? resolving,
88
90
  inject: [{ token: storage_provider_binding_1.STORAGE_PROVIDER_BINDING, optional: true }, resolving_storage_provider_1.ResolvingStorageProvider],
89
91
  },
90
92
  ],
91
- exports: [storage_provider_interface_1.STORAGE_PROVIDER, storage_config_service_1.StorageConfigService],
93
+ exports: [storage_provider_interface_1.STORAGE_PROVIDER, storage_config_service_1.StorageConfigService, storage_provider_router_1.StorageProviderRouter],
92
94
  })
93
95
  ], StorageProvidersModule);
94
96
  //# sourceMappingURL=storage-providers.module.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"storage-providers.module.js","sourceRoot":"","sources":["../../../src/storage/providers/storage-providers.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AAExC,mDAA4D;AAC5D,6EAAwE;AACxE,6EAAwE;AACxE,yEAAsE;AACtE,6EAAsF;AAEtF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH;;;;;;;;;GASG;AAeI,IAAM,sBAAsB,GAA5B,MAAM,sBAAsB;CAAG,CAAA;AAAzB,wDAAsB;iCAAtB,sBAAsB;IAdlC,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,yBAAiB,CAAC;QAC5B,SAAS,EAAE;YACT,6CAAoB;YACpB,qDAAwB;YACxB;gBACE,OAAO,EAAE,6CAAgB;gBACzB,UAAU,EAAE,CAAC,OAAoC,EAAE,SAAmC,EAAmB,EAAE,CACzG,OAAO,IAAI,SAAS;gBACtB,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,mDAAwB,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,qDAAwB,CAAC;aACxF;SACF;QACD,OAAO,EAAE,CAAC,6CAAgB,EAAE,6CAAoB,CAAC;KAClD,CAAC;GACW,sBAAsB,CAAG"}
1
+ {"version":3,"file":"storage-providers.module.js","sourceRoot":"","sources":["../../../src/storage/providers/storage-providers.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AAExC,mDAA4D;AAC5D,6EAAwE;AACxE,6EAAwE;AACxE,uEAAkE;AAClE,yEAAsE;AACtE,6EAAsF;AAEtF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH;;;;;;;;;GASG;AAgBI,IAAM,sBAAsB,GAA5B,MAAM,sBAAsB;CAAG,CAAA;AAAzB,wDAAsB;iCAAtB,sBAAsB;IAflC,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,yBAAiB,CAAC;QAC5B,SAAS,EAAE;YACT,6CAAoB;YACpB,qDAAwB;YACxB,+CAAqB;YACrB;gBACE,OAAO,EAAE,6CAAgB;gBACzB,UAAU,EAAE,CAAC,OAAoC,EAAE,SAAmC,EAAmB,EAAE,CACzG,OAAO,IAAI,SAAS;gBACtB,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,mDAAwB,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,qDAAwB,CAAC;aACxF;SACF;QACD,OAAO,EAAE,CAAC,6CAAgB,EAAE,6CAAoB,EAAE,+CAAqB,CAAC;KACzE,CAAC;GACW,sBAAsB,CAAG"}
@@ -1,5 +1,6 @@
1
1
  import type { ConformanceFinding, ConformanceSuite } from '../../testing/index';
2
2
  import { type StorageKeyPrefixDef } from '../storage-key-prefix.registry';
3
+ import '../drivers/builtin-storage-drivers';
3
4
  declare module '../../testing/index' {
4
5
  interface PlatformConformanceSuiteOptions {
5
6
  /** The storage slice's suite: its options, or `false` to opt out. Registered by importing `@marinoscar/platform-api/storage/testing`. */
@@ -44,6 +45,17 @@ export declare function checkOrgScopedKeys(defs: readonly StorageKeyPrefixDef[])
44
45
  * @stability experimental
45
46
  */
46
47
  export declare function checkStorageSettingsSchemas(): ConformanceFinding[];
48
+ /**
49
+ * Check 4: per-row routing. Two built-in drivers are configured (offline: a
50
+ * presigned URL is computed locally, nothing is fetched); the active one is
51
+ * flipped from A to B and back, and an object recorded on A must keep signing
52
+ * against A's bucket and host each time.
53
+ *
54
+ * @returns one finding per violated rule.
55
+ *
56
+ * @stability experimental
57
+ */
58
+ export declare function checkStorageRouting(): Promise<ConformanceFinding[]>;
47
59
  /**
48
60
  * The `storage` conformance suite. Registered when
49
61
  * `@marinoscar/platform-api/storage/testing` is imported.
@@ -1 +1 @@
1
- {"version":3,"file":"conformance.d.ts","sourceRoot":"","sources":["../../../src/storage/testing/conformance.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAmB,kBAAkB,EAAqB,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACpH,OAAO,EAKL,KAAK,mBAAmB,EACzB,MAAM,gCAAgC,CAAC;AAGxC,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,+BAA+B;QACvC,yIAAyI;QACzI,OAAO,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAAC;KAC7C;CACF;AAED;;;;GAIG;AACH,MAAM,WAAW,yBAAyB;IACxC,iGAAiG;IACjG,QAAQ,CAAC,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAChD;AASD;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,SAAS,mBAAmB,EAAE,EACpC,WAAW,EAAE,SAAS,MAAM,EAAE,GAC7B,kBAAkB,EAAE,CAiBtB;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,mBAAmB,EAAE,GAAG,kBAAkB,EAAE,CAe7F;AAED;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,IAAI,kBAAkB,EAAE,CAWlE;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,uBAAuB,EAAE,gBAAgB,CAAC,yBAAyB,CA2C/E,CAAC"}
1
+ {"version":3,"file":"conformance.d.ts","sourceRoot":"","sources":["../../../src/storage/testing/conformance.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAmB,kBAAkB,EAAqB,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACpH,OAAO,EAKL,KAAK,mBAAmB,EACzB,MAAM,gCAAgC,CAAC;AAOxC,OAAO,oCAAoC,CAAC;AAE5C,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,+BAA+B;QACvC,yIAAyI;QACzI,OAAO,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAAC;KAC7C;CACF;AAED;;;;GAIG;AACH,MAAM,WAAW,yBAAyB;IACxC,iGAAiG;IACjG,QAAQ,CAAC,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAChD;AAUD;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,SAAS,mBAAmB,EAAE,EACpC,WAAW,EAAE,SAAS,MAAM,EAAE,GAC7B,kBAAkB,EAAE,CAiBtB;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,mBAAmB,EAAE,GAAG,kBAAkB,EAAE,CAe7F;AAED;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,IAAI,kBAAkB,EAAE,CAWlE;AAED;;;;;;;;;GASG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAyFzE;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,uBAAuB,EAAE,gBAAgB,CAAC,yBAAyB,CAiD/E,CAAC"}
@@ -14,19 +14,29 @@
14
14
  // key without an organization; `orgKeyPrefixes` returns exactly them;
15
15
  // 3. no-secret: the `storage` settings namespace and the admin view cannot
16
16
  // carry the secret access key.
17
+ // 4. routing: an object recorded on driver A stays addressable on driver A
18
+ // (its recorded bucket, its own signing) after the active driver becomes B,
19
+ // and a legacy row, the active location and an app-bound provider route to
20
+ // the provider in force.
17
21
  // =============================================================================
18
22
  Object.defineProperty(exports, "__esModule", { value: true });
19
23
  exports.storageConformanceSuite = void 0;
20
24
  exports.checkStorageKeyPrefixes = checkStorageKeyPrefixes;
21
25
  exports.checkOrgScopedKeys = checkOrgScopedKeys;
22
26
  exports.checkStorageSettingsSchemas = checkStorageSettingsSchemas;
27
+ exports.checkStorageRouting = checkStorageRouting;
28
+ const common_1 = require("@nestjs/common");
23
29
  const storage_1 = require("@marinoscar/platform-contract/storage");
24
30
  const index_1 = require("../../testing/index");
25
31
  const storage_key_prefix_registry_1 = require("../storage-key-prefix.registry");
26
32
  const storage_key_prefixes_1 = require("../storage-key-prefixes");
33
+ const storage_provider_router_1 = require("../providers/storage-provider-router");
34
+ const storage_driver_1 = require("../drivers/storage-driver");
35
+ require("../drivers/builtin-storage-drivers");
27
36
  const FILE_PREFIXES = 'storage-key-prefixes';
28
37
  const FILE_KEYS = 'storage-object-keys';
29
38
  const FILE_SCHEMAS = 'storage-schemas';
39
+ const FILE_ROUTING = 'storage-routing';
30
40
  /** A fixed UUID for the key checks; never a real organization. */
31
41
  const PROBE_ORG = '00000000-0000-4000-8000-000000000001';
32
42
  /**
@@ -102,6 +112,102 @@ function checkStorageSettingsSchemas() {
102
112
  }
103
113
  return findings;
104
114
  }
115
+ /**
116
+ * Check 4: per-row routing. Two built-in drivers are configured (offline: a
117
+ * presigned URL is computed locally, nothing is fetched); the active one is
118
+ * flipped from A to B and back, and an object recorded on A must keep signing
119
+ * against A's bucket and host each time.
120
+ *
121
+ * @returns one finding per violated rule.
122
+ *
123
+ * @stability experimental
124
+ */
125
+ async function checkStorageRouting() {
126
+ const findings = [];
127
+ const fail = (message) => findings.push({ file: FILE_ROUTING, message });
128
+ const resolutions = {
129
+ s3: {
130
+ configured: true,
131
+ config: {
132
+ provider: 's3',
133
+ bucket: 'routing-s3-bucket',
134
+ region: 'us-east-1',
135
+ settings: { bucket: 'routing-s3-bucket', region: 'us-east-1', endpoint: '', accessKeyId: 'AKIAROUTINGS3', forcePathStyle: null },
136
+ secrets: { secretAccessKey: 'routing-secret-s3' },
137
+ },
138
+ },
139
+ r2: {
140
+ configured: true,
141
+ config: {
142
+ provider: 'r2',
143
+ bucket: 'routing-r2-bucket',
144
+ region: 'auto',
145
+ endpoint: 'https://routingacct.r2.cloudflarestorage.com',
146
+ settings: { bucket: 'routing-r2-bucket', accountId: 'routingacct', region: '', endpoint: '', accessKeyId: 'AKIAROUTINGR2', forcePathStyle: null },
147
+ secrets: { secretAccessKey: 'routing-secret-r2' },
148
+ },
149
+ },
150
+ };
151
+ const providers = [];
152
+ const buildActive = async (id) => {
153
+ const resolution = resolutions[id];
154
+ if (!resolution.configured)
155
+ throw new Error('unreachable');
156
+ const built = await (0, storage_driver_1.requireStorageDriver)(id).build({
157
+ settings: resolution.config.settings,
158
+ secret: async (name) => resolution.config.secrets[name] ?? null,
159
+ logger: new common_1.Logger('storage-routing'),
160
+ partSize: 5 * 1024 * 1024,
161
+ });
162
+ providers.push(built);
163
+ return built;
164
+ };
165
+ try {
166
+ for (const activeId of ['s3', 'r2']) {
167
+ const config = {
168
+ activeProvider: async () => activeId,
169
+ resolveDriver: async (id) => resolutions[id],
170
+ };
171
+ const current = await buildActive(activeId);
172
+ const router = new storage_provider_router_1.StorageProviderRouter({ resolveCurrent: async () => current }, config, { get: (_key, fallback) => fallback });
173
+ // An object recorded on r2 stays on r2; one recorded on s3 stays on s3.
174
+ for (const [recorded, host] of [['r2', 'r2.cloudflarestorage.com'], ['s3', 'amazonaws.com']]) {
175
+ const resolution = resolutions[recorded];
176
+ if (!resolution.configured)
177
+ continue;
178
+ const provider = await router.forLocation({ provider: recorded, bucket: resolution.config.bucket });
179
+ const url = await provider.getSignedDownloadUrl('routing/probe.txt', { expiresIn: 60 });
180
+ if (provider.kind !== recorded)
181
+ fail(`active=${activeId}: a row recorded on "${recorded}" was routed to "${provider.kind}"`);
182
+ if (!url.includes(resolution.config.bucket))
183
+ fail(`active=${activeId}: a row recorded on "${recorded}" signed outside its bucket`);
184
+ if (!url.includes(host))
185
+ fail(`active=${activeId}: a row recorded on "${recorded}" signed against the wrong host`);
186
+ }
187
+ // A row whose recorded bucket differs from the driver's current one signs against the recorded bucket.
188
+ const moved = await router.forLocation({ provider: 'r2', bucket: 'routing-r2-previous' });
189
+ if (!(await moved.getSignedDownloadUrl('routing/probe.txt')).includes('routing-r2-previous')) {
190
+ fail(`active=${activeId}: a row recorded on a previous bucket was not routed to that bucket`);
191
+ }
192
+ // A legacy row (no recorded location) and an unrecorded provider use the one in force.
193
+ if ((await router.forLocation(null)) !== current)
194
+ fail(`active=${activeId}: a legacy row was not routed to the active provider`);
195
+ if ((await router.forObject({ storageProvider: null, bucket: null })) !== current) {
196
+ fail(`active=${activeId}: a row with no recorded provider was not routed to the active provider`);
197
+ }
198
+ }
199
+ // An app-bound provider routes itself: the router must hand it back for every row.
200
+ const bound = { kind: 'app-bound' };
201
+ const boundRouter = new storage_provider_router_1.StorageProviderRouter(bound);
202
+ if ((await boundRouter.forLocation({ provider: 'r2', bucket: 'x' })) !== bound)
203
+ fail('an app-bound provider was bypassed by the router');
204
+ }
205
+ finally {
206
+ for (const provider of providers)
207
+ provider.destroy?.();
208
+ }
209
+ return findings;
210
+ }
105
211
  /**
106
212
  * The `storage` conformance suite. Registered when
107
213
  * `@marinoscar/platform-api/storage/testing` is imported.
@@ -155,6 +261,12 @@ exports.storageConformanceSuite = {
155
261
  expect(report.findings.filter((finding) => finding.file === FILE_SCHEMAS)).toEqual([]);
156
262
  },
157
263
  },
264
+ {
265
+ name: 'storage-routing: an object stays on the provider and bucket it was written to after the active driver changes',
266
+ run: async (_report, expect) => {
267
+ expect(await checkStorageRouting()).toEqual([]);
268
+ },
269
+ },
158
270
  ];
159
271
  },
160
272
  };
@@ -1 +1 @@
1
- {"version":3,"file":"conformance.js","sourceRoot":"","sources":["../../../src/storage/testing/conformance.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"conformance.js","sourceRoot":"","sources":["../../../src/storage/testing/conformance.ts"],"names":[],"mappings":"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"}
@@ -17,7 +17,7 @@ export interface StorageDriverConformanceHarness {
17
17
  *
18
18
  * @stability experimental
19
19
  */
20
- export type StorageDriverConformanceScenario = 'definition' | 'provider' | 'roundTrip' | 'streamedUpload' | 'signedUrl' | 'listKeys' | 'testConnection' | 'provision' | 'pure';
20
+ export type StorageDriverConformanceScenario = 'definition' | 'provider' | 'roundTrip' | 'streamedUpload' | 'signedUrl' | 'listKeys' | 'testConnection' | 'provision' | 'multipartList' | 'proxiedPart' | 'pure';
21
21
  /**
22
22
  * Options of {@link describeStorageDriverConformance}.
23
23
  *
@@ -44,6 +44,18 @@ export interface StorageDriverConformanceOptions extends StorageDriverConformanc
44
44
  skip?: readonly StorageDriverConformanceScenario[];
45
45
  /** Size of the streamed upload in bytes. Default 6 MiB. */
46
46
  streamedBytes?: number;
47
+ /**
48
+ * PUTs one part to a presigned part URL (`getSignedUploadUrl`), so the kit can
49
+ * exercise `listUploadedParts` on a driver that takes presigned parts. Without
50
+ * it the `multipartList` scenario only runs for `proxied` drivers.
51
+ */
52
+ putPart?: (url: string, body: Buffer) => Promise<void>;
53
+ /**
54
+ * Size in bytes of the part the `proxiedPart` scenario streams. Default 64 MiB:
55
+ * the scenario fails when the driver still holds more than half of it (after a
56
+ * forced collection) while the part is consumed, i.e. when it buffers the part.
57
+ */
58
+ proxiedPartBytes?: number;
47
59
  }
48
60
  /**
49
61
  * Registers the storage driver conformance suite.
@@ -1 +1 @@
1
- {"version":3,"file":"driver-conformance.d.ts","sourceRoot":"","sources":["../../../src/storage/testing/driver-conformance.ts"],"names":[],"mappings":"AAuDA,OAAO,EAGL,KAAK,uBAAuB,EAC7B,MAAM,2BAA2B,CAAC;AAEnC;;;;GAIG;AACH,MAAM,WAAW,+BAA+B;IAC9C,+BAA+B;IAC/B,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,IAAI,KAAK,OAAO,CAAC;IACpD,qFAAqF;IACrF,EAAE,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC;IACvD,6BAA6B;IAC7B,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,GAAG,CAAC;CAClC;AAED;;;;GAIG;AACH,MAAM,MAAM,gCAAgC,GACxC,YAAY,GACZ,UAAU,GACV,WAAW,GACX,gBAAgB,GAChB,WAAW,GACX,UAAU,GACV,gBAAgB,GAChB,WAAW,GACX,MAAM,CAAC;AAEX;;;;GAIG;AACH,MAAM,WAAW,+BAAgC,SAAQ,+BAA+B;IACtF,uIAAuI;IACvI,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,yFAAyF;IACzF,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IAC1D,mEAAmE;IACnE,IAAI,CAAC,EAAE,SAAS,gCAAgC,EAAE,CAAC;IACnD,2DAA2D;IAC3D,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAkED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,uBAAuB,CAAC,GAAG,CAAC,GAAG,MAAM,EAC7C,OAAO,EAAE,+BAA+B,GACvC,IAAI,CA0ON"}
1
+ {"version":3,"file":"driver-conformance.d.ts","sourceRoot":"","sources":["../../../src/storage/testing/driver-conformance.ts"],"names":[],"mappings":"AAgEA,OAAO,EAGL,KAAK,uBAAuB,EAC7B,MAAM,2BAA2B,CAAC;AAEnC;;;;GAIG;AACH,MAAM,WAAW,+BAA+B;IAC9C,+BAA+B;IAC/B,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,IAAI,KAAK,OAAO,CAAC;IACpD,qFAAqF;IACrF,EAAE,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC;IACvD,6BAA6B;IAC7B,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,GAAG,CAAC;CAClC;AAED;;;;GAIG;AACH,MAAM,MAAM,gCAAgC,GACxC,YAAY,GACZ,UAAU,GACV,WAAW,GACX,gBAAgB,GAChB,WAAW,GACX,UAAU,GACV,gBAAgB,GAChB,WAAW,GACX,eAAe,GACf,aAAa,GACb,MAAM,CAAC;AAEX;;;;GAIG;AACH,MAAM,WAAW,+BAAgC,SAAQ,+BAA+B;IACtF,uIAAuI;IACvI,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,yFAAyF;IACzF,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IAC1D,mEAAmE;IACnE,IAAI,CAAC,EAAE,SAAS,gCAAgC,EAAE,CAAC;IACnD,2DAA2D;IAC3D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAqFD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,uBAAuB,CAAC,GAAG,CAAC,GAAG,MAAM,EAC7C,OAAO,EAAE,+BAA+B,GACvC,IAAI,CA2VN"}
@@ -44,6 +44,13 @@
44
44
  // - `testConnection` NEVER THROWS and NEVER returns secret material: with
45
45
  // valid settings, with the driver's defaults and no secrets, and with a
46
46
  // sentinel secret the driver must not echo into its message, details or checks;
47
+ // - multipart extensions, when the provider declares them: `listUploadedParts`
48
+ // reports the parts of an in-flight upload (and, for a driver that takes
49
+ // presigned parts, the kit PUTs them through `putPart`); a `proxied` driver
50
+ // accepts a STREAMED `uploadPart` larger than the process's buffer budget
51
+ // (it samples external memory while the lazily generated part is consumed),
52
+ // discards a part whose stream ends short, and completes an upload built
53
+ // from API parts;
47
54
  // - `provision`, when defined, can be repeated;
48
55
  // - `location` and `missing`, when defined, are pure and well formed.
49
56
  // =============================================================================
@@ -52,6 +59,8 @@ exports.describeStorageDriverConformance = describeStorageDriverConformance;
52
59
  const common_1 = require("@nestjs/common");
53
60
  const node_crypto_1 = require("node:crypto");
54
61
  const node_stream_1 = require("node:stream");
62
+ const node_vm_1 = require("node:vm");
63
+ const node_v8_1 = require("node:v8");
55
64
  const storage_1 = require("@marinoscar/platform-contract/storage");
56
65
  const storage_driver_1 = require("../drivers/storage-driver");
57
66
  /** A value no driver may ever echo back from `testConnection`. */
@@ -72,7 +81,26 @@ const PROVIDER_METHODS = [
72
81
  'getBucket',
73
82
  ];
74
83
  const DEFAULT_STREAMED_BYTES = 6 * 1024 * 1024;
84
+ const DEFAULT_PROXIED_PART_BYTES = 64 * 1024 * 1024;
85
+ const PROXIED_SAMPLE_BYTES = 4 * 1024 * 1024;
75
86
  const STREAM_CHUNK_BYTES = 64 * 1024;
87
+ /**
88
+ * A function that runs a full garbage collection, or `null` when the runtime
89
+ * will not give one. Enabled at run time (`--expose-gc` through V8's flags), so
90
+ * the kit needs no launch flag: external memory (Buffers) is only reclaimed
91
+ * lazily, and without a forced collection a driver that streams correctly still
92
+ * looks like one that holds the part.
93
+ */
94
+ function gcFunction() {
95
+ try {
96
+ (0, node_v8_1.setFlagsFromString)('--expose-gc');
97
+ const gc = (0, node_vm_1.runInNewContext)('gc');
98
+ return typeof gc === 'function' ? gc : null;
99
+ }
100
+ catch {
101
+ return null;
102
+ }
103
+ }
76
104
  async function collect(stream) {
77
105
  const chunks = [];
78
106
  for await (const chunk of stream)
@@ -312,6 +340,112 @@ function describeStorageDriverConformance(driver, options) {
312
340
  }
313
341
  });
314
342
  }
343
+ scenario('multipartList', 'listUploadedParts reports the parts the backend holds for an in-flight upload', async () => {
344
+ await withProvider(async (provider) => {
345
+ if (provider.listUploadedParts === undefined)
346
+ return;
347
+ const key = `${keyPrefix()}multipart.bin`;
348
+ const first = Buffer.alloc(1024, 1);
349
+ const second = Buffer.alloc(2048, 2);
350
+ const proxied = provider.partUploadMode === 'proxied';
351
+ if (!proxied && options.putPart === undefined)
352
+ return;
353
+ const { uploadId } = await provider.initMultipartUpload(key, { mimeType: 'application/octet-stream' });
354
+ try {
355
+ expect(await provider.listUploadedParts(key, uploadId)).toEqual([]);
356
+ for (const [index, body] of [first, second].entries()) {
357
+ const partNumber = index + 1;
358
+ if (proxied) {
359
+ const stored = await provider.uploadPart(key, uploadId, partNumber, node_stream_1.Readable.from([body]), body.length);
360
+ expect(typeof stored.eTag).toBe('string');
361
+ }
362
+ else {
363
+ await options.putPart(await provider.getSignedUploadUrl(key, uploadId, partNumber, 60), body);
364
+ }
365
+ }
366
+ const held = await provider.listUploadedParts(key, uploadId);
367
+ expect(held.map((part) => part.partNumber).sort((a, b) => a - b)).toEqual([1, 2]);
368
+ expect(held.find((part) => part.partNumber === 1)?.size).toBe(first.length);
369
+ expect(held.find((part) => part.partNumber === 2)?.size).toBe(second.length);
370
+ for (const part of held)
371
+ expect(part.eTag.length).toBeGreaterThan(0);
372
+ }
373
+ finally {
374
+ await provider.abortMultipartUpload(key, uploadId);
375
+ }
376
+ });
377
+ });
378
+ scenario('proxiedPart', 'a proxied driver streams a part larger than the buffer budget, and discards a short one', async () => {
379
+ await withProvider(async (provider) => {
380
+ if (provider.partUploadMode !== 'proxied')
381
+ return;
382
+ expect(typeof provider.uploadPart).toBe('function');
383
+ const key = `${keyPrefix()}proxied.bin`;
384
+ const partBytes = options.proxiedPartBytes ?? DEFAULT_PROXIED_PART_BYTES;
385
+ const { uploadId } = await provider.initMultipartUpload(key, { mimeType: 'application/octet-stream' });
386
+ try {
387
+ // 1. A part bigger than the budget, produced lazily; sample external
388
+ // memory as each chunk is pulled. A driver that buffers the part
389
+ // holds all of it at once.
390
+ // External memory is sampled after a FORCED collection every few
391
+ // MiB, so garbage the driver has already let go of is not counted:
392
+ // only bytes it still holds are. (Skipped where the runtime offers
393
+ // no way to collect.)
394
+ const gc = gcFunction();
395
+ gc?.();
396
+ const baseline = process.memoryUsage().arrayBuffers;
397
+ let peak = 0;
398
+ let produced = 0;
399
+ let sinceSample = 0;
400
+ const source = node_stream_1.Readable.from((function* () {
401
+ while (produced < partBytes) {
402
+ const size = Math.min(STREAM_CHUNK_BYTES, partBytes - produced);
403
+ produced += size;
404
+ sinceSample += size;
405
+ if (gc !== null && sinceSample >= PROXIED_SAMPLE_BYTES) {
406
+ sinceSample = 0;
407
+ gc();
408
+ peak = Math.max(peak, process.memoryUsage().arrayBuffers - baseline);
409
+ }
410
+ yield Buffer.allocUnsafeSlow(size).fill(7);
411
+ }
412
+ })(), { objectMode: false, highWaterMark: STREAM_CHUNK_BYTES });
413
+ const stored = await provider.uploadPart(key, uploadId, 1, source, partBytes);
414
+ expect(typeof stored.eTag).toBe('string');
415
+ expect(produced).toBe(partBytes);
416
+ if (gc !== null)
417
+ expect(peak).toBeLessThan(partBytes / 2);
418
+ // 2. A body that fails part-way (the platform errors the stream when it
419
+ // ends short or runs long) must not leave a part behind.
420
+ let refused = false;
421
+ try {
422
+ let sent = false;
423
+ const failing = new node_stream_1.Readable({
424
+ read() {
425
+ if (sent) {
426
+ this.destroy(new Error('stream ended early'));
427
+ return;
428
+ }
429
+ sent = true;
430
+ this.push(Buffer.alloc(16, 1));
431
+ },
432
+ });
433
+ await provider.uploadPart(key, uploadId, 2, failing, partBytes);
434
+ }
435
+ catch {
436
+ refused = true;
437
+ }
438
+ expect(refused).toBe(true);
439
+ if (provider.listUploadedParts !== undefined) {
440
+ const held = await provider.listUploadedParts(key, uploadId);
441
+ expect(held.map((part) => part.partNumber)).toEqual([1]);
442
+ }
443
+ }
444
+ finally {
445
+ await provider.abortMultipartUpload(key, uploadId);
446
+ }
447
+ });
448
+ });
315
449
  scenario('pure', 'location and missing are pure and well formed', () => {
316
450
  const settings = def.settingsSchema.parse({ ...def.defaults, ...options.settings });
317
451
  const present = Object.fromEntries((def.secrets ?? []).map((secret) => [secret.name, true]));