@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.
- package/CHANGELOG.md +28 -0
- package/dist/ai/features/ai-feature.registry.d.ts +7 -0
- package/dist/ai/features/ai-feature.registry.d.ts.map +1 -1
- package/dist/ai/features/ai-feature.registry.js +3 -0
- package/dist/ai/features/ai-feature.registry.js.map +1 -1
- package/dist/ai/keys/ai-key-resolver.service.d.ts +16 -0
- package/dist/ai/keys/ai-key-resolver.service.d.ts.map +1 -1
- package/dist/ai/keys/ai-key-resolver.service.js +44 -0
- package/dist/ai/keys/ai-key-resolver.service.js.map +1 -1
- package/dist/ai/keys/usable-models.service.d.ts +13 -1
- package/dist/ai/keys/usable-models.service.d.ts.map +1 -1
- package/dist/ai/keys/usable-models.service.js +37 -12
- package/dist/ai/keys/usable-models.service.js.map +1 -1
- package/dist/ai/runtime/ai-limits.service.d.ts +2 -1
- package/dist/ai/runtime/ai-limits.service.d.ts.map +1 -1
- package/dist/ai/runtime/ai-limits.service.js +7 -2
- package/dist/ai/runtime/ai-limits.service.js.map +1 -1
- package/dist/ai/runtime/ai-runtime.types.d.ts +46 -1
- package/dist/ai/runtime/ai-runtime.types.d.ts.map +1 -1
- package/dist/ai/runtime/ai-runtime.types.js.map +1 -1
- package/dist/ai/runtime/ai.service.d.ts +142 -5
- package/dist/ai/runtime/ai.service.d.ts.map +1 -1
- package/dist/ai/runtime/ai.service.js +353 -46
- package/dist/ai/runtime/ai.service.js.map +1 -1
- package/dist/ai/runtime/index.d.ts +1 -1
- package/dist/ai/runtime/index.d.ts.map +1 -1
- package/dist/ai/runtime/target-resolver.d.ts +2 -2
- package/dist/ai/runtime/target-resolver.d.ts.map +1 -1
- package/dist/ai/runtime/target-resolver.js +2 -1
- package/dist/ai/runtime/target-resolver.js.map +1 -1
- package/dist/ai/testing/conformance/ai-key-policy.suite.d.ts.map +1 -1
- package/dist/ai/testing/conformance/ai-key-policy.suite.js +54 -0
- package/dist/ai/testing/conformance/ai-key-policy.suite.js.map +1 -1
- package/dist/ai/testing/conformance/ai-secret-egress.suite.d.ts.map +1 -1
- package/dist/ai/testing/conformance/ai-secret-egress.suite.js +59 -0
- package/dist/ai/testing/conformance/ai-secret-egress.suite.js.map +1 -1
- package/dist/identity/device-auth/device-auth.service.d.ts +9 -2
- package/dist/identity/device-auth/device-auth.service.d.ts.map +1 -1
- package/dist/identity/device-auth/device-auth.service.js +44 -4
- package/dist/identity/device-auth/device-auth.service.js.map +1 -1
- package/dist/identity/device-auth/dto/device-code-request.dto.d.ts +12 -0
- package/dist/identity/device-auth/dto/device-code-request.dto.d.ts.map +1 -1
- package/dist/identity/identity.options.d.ts +25 -0
- package/dist/identity/identity.options.d.ts.map +1 -1
- package/dist/identity/identity.options.js +11 -0
- package/dist/identity/identity.options.js.map +1 -1
- package/dist/identity/index.d.ts +1 -1
- package/dist/identity/index.d.ts.map +1 -1
- package/dist/nodes/current-node.decorator.d.ts +18 -0
- package/dist/nodes/current-node.decorator.d.ts.map +1 -0
- package/dist/nodes/current-node.decorator.js +28 -0
- package/dist/nodes/current-node.decorator.js.map +1 -0
- package/dist/nodes/index.d.ts +8 -0
- package/dist/nodes/index.d.ts.map +1 -1
- package/dist/nodes/index.js +21 -2
- package/dist/nodes/index.js.map +1 -1
- package/dist/nodes/node-ownership.d.ts +34 -0
- package/dist/nodes/node-ownership.d.ts.map +1 -0
- package/dist/nodes/node-ownership.js +92 -0
- package/dist/nodes/node-ownership.js.map +1 -0
- package/dist/nodes/node-route.decorator.d.ts +31 -0
- package/dist/nodes/node-route.decorator.d.ts.map +1 -0
- package/dist/nodes/node-route.decorator.js +54 -0
- package/dist/nodes/node-route.decorator.js.map +1 -0
- package/dist/nodes/node-route.guard.d.ts +22 -0
- package/dist/nodes/node-route.guard.d.ts.map +1 -0
- package/dist/nodes/node-route.guard.js +74 -0
- package/dist/nodes/node-route.guard.js.map +1 -0
- package/dist/nodes/node-route.module.d.ts +21 -0
- package/dist/nodes/node-route.module.d.ts.map +1 -0
- package/dist/nodes/node-route.module.js +99 -0
- package/dist/nodes/node-route.module.js.map +1 -0
- package/dist/nodes/node-route.options.d.ts +59 -0
- package/dist/nodes/node-route.options.d.ts.map +1 -0
- package/dist/nodes/node-route.options.js +29 -0
- package/dist/nodes/node-route.options.js.map +1 -0
- package/dist/nodes/node-route.paths.d.ts +47 -0
- package/dist/nodes/node-route.paths.d.ts.map +1 -0
- package/dist/nodes/node-route.paths.js +129 -0
- package/dist/nodes/node-route.paths.js.map +1 -0
- package/dist/nodes/nodes.service.d.ts.map +1 -1
- package/dist/nodes/nodes.service.js +3 -15
- package/dist/nodes/nodes.service.js.map +1 -1
- package/dist/nodes/testing/conformance.d.ts +86 -0
- package/dist/nodes/testing/conformance.d.ts.map +1 -0
- package/dist/nodes/testing/conformance.js +230 -0
- package/dist/nodes/testing/conformance.js.map +1 -0
- package/dist/nodes/testing/discover.d.ts +13 -0
- package/dist/nodes/testing/discover.d.ts.map +1 -0
- package/dist/nodes/testing/discover.js +42 -0
- package/dist/nodes/testing/discover.js.map +1 -0
- package/dist/nodes/testing/index.d.ts +3 -0
- package/dist/nodes/testing/index.d.ts.map +1 -0
- package/dist/nodes/testing/index.js +13 -0
- package/dist/nodes/testing/index.js.map +1 -0
- package/dist/notifications/channels/browser-notification.channel.d.ts +11 -0
- package/dist/notifications/channels/browser-notification.channel.d.ts.map +1 -1
- package/dist/notifications/channels/browser-notification.channel.js +71 -48
- package/dist/notifications/channels/browser-notification.channel.js.map +1 -1
- package/dist/notifications/data/notifications-db.d.ts +19 -0
- package/dist/notifications/data/notifications-db.d.ts.map +1 -1
- package/dist/notifications/dto/notification.dto.d.ts +4 -0
- package/dist/notifications/dto/notification.dto.d.ts.map +1 -1
- package/dist/notifications/index.d.ts +1 -1
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js.map +1 -1
- package/dist/notifications/notification-inbox-writer.d.ts +112 -0
- package/dist/notifications/notification-inbox-writer.d.ts.map +1 -0
- package/dist/notifications/notification-inbox-writer.js +217 -0
- package/dist/notifications/notification-inbox-writer.js.map +1 -0
- package/dist/notifications/notification-store.service.d.ts +10 -0
- package/dist/notifications/notification-store.service.d.ts.map +1 -1
- package/dist/notifications/notification-store.service.js +55 -10
- package/dist/notifications/notification-store.service.js.map +1 -1
- package/dist/notifications/notification-stream.service.d.ts +8 -0
- package/dist/notifications/notification-stream.service.d.ts.map +1 -1
- package/dist/notifications/notification-stream.service.js.map +1 -1
- package/dist/notifications/notification.types.d.ts +74 -0
- package/dist/notifications/notification.types.d.ts.map +1 -1
- package/dist/notifications/notification.types.js.map +1 -1
- package/dist/notifications/notifications.controller.d.ts +1 -0
- package/dist/notifications/notifications.controller.d.ts.map +1 -1
- package/dist/notifications/notifications.controller.js +23 -0
- package/dist/notifications/notifications.controller.js.map +1 -1
- package/dist/notifications/notifications.service.d.ts +56 -1
- package/dist/notifications/notifications.service.d.ts.map +1 -1
- package/dist/notifications/notifications.service.js +122 -0
- package/dist/notifications/notifications.service.js.map +1 -1
- package/dist/notifications/registry/event.registry.d.ts +15 -0
- package/dist/notifications/registry/event.registry.d.ts.map +1 -1
- package/dist/notifications/registry/event.registry.js +3 -0
- package/dist/notifications/registry/event.registry.js.map +1 -1
- package/dist/notifications/retention/notification-inbox-purge.handler.d.ts.map +1 -1
- package/dist/notifications/retention/notification-inbox-purge.handler.js +16 -4
- package/dist/notifications/retention/notification-inbox-purge.handler.js.map +1 -1
- package/dist/notifications/testing/index.d.ts +2 -0
- package/dist/notifications/testing/index.d.ts.map +1 -1
- package/dist/notifications/testing/index.js +5 -1
- package/dist/notifications/testing/index.js.map +1 -1
- package/dist/storage/config/storage-config.service.d.ts +24 -0
- package/dist/storage/config/storage-config.service.d.ts.map +1 -1
- package/dist/storage/config/storage-config.service.js +31 -5
- package/dist/storage/config/storage-config.service.js.map +1 -1
- package/dist/storage/handlers/storage-cleanup.handler.d.ts +3 -1
- package/dist/storage/handlers/storage-cleanup.handler.d.ts.map +1 -1
- package/dist/storage/handlers/storage-cleanup.handler.js +10 -3
- package/dist/storage/handlers/storage-cleanup.handler.js.map +1 -1
- package/dist/storage/index.d.ts +5 -1
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +12 -3
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/objects/dto/get-part-urls.dto.d.ts +26 -0
- package/dist/storage/objects/dto/get-part-urls.dto.d.ts.map +1 -0
- package/dist/storage/objects/dto/get-part-urls.dto.js +25 -0
- package/dist/storage/objects/dto/get-part-urls.dto.js.map +1 -0
- package/dist/storage/objects/dto/init-upload.dto.d.ts +2 -1
- package/dist/storage/objects/dto/init-upload.dto.d.ts.map +1 -1
- package/dist/storage/objects/objects.controller.d.ts +13 -1
- package/dist/storage/objects/objects.controller.d.ts.map +1 -1
- package/dist/storage/objects/objects.controller.js +88 -1
- package/dist/storage/objects/objects.controller.js.map +1 -1
- package/dist/storage/objects/objects.service.d.ts +46 -1
- package/dist/storage/objects/objects.service.d.ts.map +1 -1
- package/dist/storage/objects/objects.service.js +277 -23
- package/dist/storage/objects/objects.service.js.map +1 -1
- package/dist/storage/objects/part-body-parser.d.ts +30 -0
- package/dist/storage/objects/part-body-parser.d.ts.map +1 -0
- package/dist/storage/objects/part-body-parser.js +45 -0
- package/dist/storage/objects/part-body-parser.js.map +1 -0
- package/dist/storage/objects/part-body.d.ts +23 -0
- package/dist/storage/objects/part-body.d.ts.map +1 -0
- package/dist/storage/objects/part-body.js +48 -0
- package/dist/storage/objects/part-body.js.map +1 -0
- package/dist/storage/processing/object-processing.service.d.ts +4 -1
- package/dist/storage/processing/object-processing.service.d.ts.map +1 -1
- package/dist/storage/processing/object-processing.service.js +11 -3
- package/dist/storage/processing/object-processing.service.js.map +1 -1
- package/dist/storage/profile-image/avatar.service.d.ts +3 -1
- package/dist/storage/profile-image/avatar.service.d.ts.map +1 -1
- package/dist/storage/profile-image/avatar.service.js +8 -3
- package/dist/storage/profile-image/avatar.service.js.map +1 -1
- package/dist/storage/profile-image/profile-image.service.d.ts +4 -1
- package/dist/storage/profile-image/profile-image.service.d.ts.map +1 -1
- package/dist/storage/profile-image/profile-image.service.js +11 -5
- package/dist/storage/profile-image/profile-image.service.js.map +1 -1
- package/dist/storage/providers/index.d.ts +3 -1
- package/dist/storage/providers/index.d.ts.map +1 -1
- package/dist/storage/providers/index.js +8 -1
- package/dist/storage/providers/index.js.map +1 -1
- package/dist/storage/providers/resolving-storage.provider.d.ts +11 -0
- package/dist/storage/providers/resolving-storage.provider.d.ts.map +1 -1
- package/dist/storage/providers/resolving-storage.provider.js +13 -0
- package/dist/storage/providers/resolving-storage.provider.js.map +1 -1
- package/dist/storage/providers/s3/s3-storage.provider.d.ts +9 -1
- package/dist/storage/providers/s3/s3-storage.provider.d.ts.map +1 -1
- package/dist/storage/providers/s3/s3-storage.provider.js +37 -0
- package/dist/storage/providers/s3/s3-storage.provider.js.map +1 -1
- package/dist/storage/providers/storage-provider-router.d.ts +64 -0
- package/dist/storage/providers/storage-provider-router.d.ts.map +1 -0
- package/dist/storage/providers/storage-provider-router.js +161 -0
- package/dist/storage/providers/storage-provider-router.js.map +1 -0
- package/dist/storage/providers/storage-provider.interface.d.ts +34 -1
- package/dist/storage/providers/storage-provider.interface.d.ts.map +1 -1
- package/dist/storage/providers/storage-provider.interface.js.map +1 -1
- package/dist/storage/providers/storage-provider.types.d.ts +86 -0
- package/dist/storage/providers/storage-provider.types.d.ts.map +1 -1
- package/dist/storage/providers/storage-provider.types.js +78 -0
- package/dist/storage/providers/storage-provider.types.js.map +1 -1
- package/dist/storage/providers/storage-providers.module.d.ts.map +1 -1
- package/dist/storage/providers/storage-providers.module.js +3 -1
- package/dist/storage/providers/storage-providers.module.js.map +1 -1
- package/dist/storage/testing/conformance.d.ts +12 -0
- package/dist/storage/testing/conformance.d.ts.map +1 -1
- package/dist/storage/testing/conformance.js +112 -0
- package/dist/storage/testing/conformance.js.map +1 -1
- package/dist/storage/testing/driver-conformance.d.ts +13 -1
- package/dist/storage/testing/driver-conformance.d.ts.map +1 -1
- package/dist/storage/testing/driver-conformance.js +134 -0
- package/dist/storage/testing/driver-conformance.js.map +1 -1
- package/dist/storage/testing/index.d.ts +1 -1
- package/dist/storage/testing/index.d.ts.map +1 -1
- package/dist/storage/testing/index.js +2 -1
- package/dist/storage/testing/index.js.map +1 -1
- package/dist/telemetry/stack/dto/telemetry-stack.dto.d.ts +1 -1
- 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":"
|
|
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;
|
|
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":"
|
|
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":";AAAA,gFAAgF;AAChF,qDAAqD;AACrD,gFAAgF;AAChF,EAAE;AACF,yEAAyE;AACzE,oCAAoC;AACpC,EAAE;AACF,0EAA0E;AAC1E,sEAAsE;AACtE,6EAA6E;AAC7E,yEAAyE;AACzE,8EAA8E;AAC9E,2EAA2E;AAC3E,6EAA6E;AAC7E,oCAAoC;AACpC,gFAAgF;;;
|
|
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":"
|
|
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]));
|