@nestm/storage 0.1.0-alpha.0 → 0.1.0-alpha.10
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 +264 -0
- package/README.md +724 -25
- package/SECURITY.md +101 -0
- package/dist/ai-sdk/ai-sdk-workspace-tools.d.ts +91 -0
- package/dist/ai-sdk/ai-sdk-workspace-tools.d.ts.map +1 -0
- package/dist/ai-sdk/ai-sdk-workspace-tools.js +491 -0
- package/dist/ai-sdk/ai-sdk-workspace-tools.js.map +1 -0
- package/dist/ai-sdk/index.d.ts +2 -0
- package/dist/ai-sdk/index.d.ts.map +1 -0
- package/dist/ai-sdk/index.js +2 -0
- package/dist/ai-sdk/index.js.map +1 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +4 -0
- package/dist/core/index.js.map +1 -0
- package/dist/files-sdk/files-sdk.driver.d.ts +85 -3
- package/dist/files-sdk/files-sdk.driver.d.ts.map +1 -1
- package/dist/files-sdk/files-sdk.driver.js +1437 -37
- package/dist/files-sdk/files-sdk.driver.js.map +1 -1
- package/dist/files-sdk/fs/index.d.ts +42 -0
- package/dist/files-sdk/fs/index.d.ts.map +1 -0
- package/dist/files-sdk/fs/index.js +1117 -0
- package/dist/files-sdk/fs/index.js.map +1 -0
- package/dist/files-sdk/index.d.ts +1 -1
- package/dist/files-sdk/index.d.ts.map +1 -1
- package/dist/files-sdk/index.js.map +1 -1
- package/dist/files-sdk/provider/index.d.ts +58 -0
- package/dist/files-sdk/provider/index.d.ts.map +1 -0
- package/dist/files-sdk/provider/index.js +147 -0
- package/dist/files-sdk/provider/index.js.map +1 -0
- package/dist/files-sdk/s3/construction-metadata.d.ts +9 -0
- package/dist/files-sdk/s3/construction-metadata.d.ts.map +1 -0
- package/dist/files-sdk/s3/construction-metadata.js +24 -0
- package/dist/files-sdk/s3/construction-metadata.js.map +1 -0
- package/dist/files-sdk/s3/index.d.ts +54 -0
- package/dist/files-sdk/s3/index.d.ts.map +1 -0
- package/dist/files-sdk/s3/index.js +1505 -0
- package/dist/files-sdk/s3/index.js.map +1 -0
- package/dist/gateway/index.d.ts +1 -1
- package/dist/gateway/index.d.ts.map +1 -1
- package/dist/gateway/index.js.map +1 -1
- package/dist/gateway/storage-gateway-fastify-parser.d.ts.map +1 -1
- package/dist/gateway/storage-gateway-fastify-parser.js.map +1 -1
- package/dist/gateway/storage-gateway.controller.d.ts +12 -11
- package/dist/gateway/storage-gateway.controller.d.ts.map +1 -1
- package/dist/gateway/storage-gateway.controller.js +240 -70
- package/dist/gateway/storage-gateway.controller.js.map +1 -1
- package/dist/gateway/storage-gateway.guard.d.ts.map +1 -1
- package/dist/gateway/storage-gateway.guard.js.map +1 -1
- package/dist/gateway/storage-gateway.module.d.ts.map +1 -1
- package/dist/gateway/storage-gateway.module.js +60 -1
- package/dist/gateway/storage-gateway.module.js.map +1 -1
- package/dist/gateway/storage-gateway.tokens.d.ts +1 -0
- package/dist/gateway/storage-gateway.tokens.d.ts.map +1 -1
- package/dist/gateway/storage-gateway.tokens.js +1 -0
- package/dist/gateway/storage-gateway.tokens.js.map +1 -1
- package/dist/gateway/storage-gateway.types.d.ts +53 -10
- package/dist/gateway/storage-gateway.types.d.ts.map +1 -1
- package/dist/gateway/storage-gateway.types.js.map +1 -1
- package/dist/index.d.ts +1 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -3
- package/dist/index.js.map +1 -1
- package/dist/inject-storage.decorator.js.map +1 -1
- package/dist/storage-etag.d.ts +13 -0
- package/dist/storage-etag.d.ts.map +1 -0
- package/dist/storage-etag.js +31 -0
- package/dist/storage-etag.js.map +1 -0
- package/dist/storage-upload-control.d.ts.map +1 -1
- package/dist/storage.client.d.ts +15 -3
- package/dist/storage.client.d.ts.map +1 -1
- package/dist/storage.client.js +204 -2
- package/dist/storage.client.js.map +1 -1
- package/dist/storage.driver.d.ts +28 -1
- package/dist/storage.driver.d.ts.map +1 -1
- package/dist/storage.driver.js.map +1 -1
- package/dist/storage.error.d.ts +17 -10
- package/dist/storage.error.d.ts.map +1 -1
- package/dist/storage.error.js +47 -1
- package/dist/storage.error.js.map +1 -1
- package/dist/storage.module.d.ts.map +1 -1
- package/dist/storage.module.js.map +1 -1
- package/dist/storage.service.d.ts.map +1 -1
- package/dist/storage.service.js +28 -3
- package/dist/storage.service.js.map +1 -1
- package/dist/storage.tokens.js.map +1 -1
- package/dist/storage.types.d.ts +150 -1
- package/dist/storage.types.d.ts.map +1 -1
- package/dist/storage.types.js.map +1 -1
- package/dist/testing/index.d.ts +1 -0
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +1 -0
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/provider-conformance.d.ts +65 -0
- package/dist/testing/provider-conformance.d.ts.map +1 -0
- package/dist/testing/provider-conformance.js +869 -0
- package/dist/testing/provider-conformance.js.map +1 -0
- package/dist/workspace/index.d.ts +5 -0
- package/dist/workspace/index.d.ts.map +1 -0
- package/dist/workspace/index.js +5 -0
- package/dist/workspace/index.js.map +1 -0
- package/dist/workspace/storage-workspace.cursor.d.ts +71 -0
- package/dist/workspace/storage-workspace.cursor.d.ts.map +1 -0
- package/dist/workspace/storage-workspace.cursor.js +408 -0
- package/dist/workspace/storage-workspace.cursor.js.map +1 -0
- package/dist/workspace/storage-workspace.d.ts +6 -0
- package/dist/workspace/storage-workspace.d.ts.map +1 -0
- package/dist/workspace/storage-workspace.error.d.ts +32 -0
- package/dist/workspace/storage-workspace.error.d.ts.map +1 -0
- package/dist/workspace/storage-workspace.error.js +75 -0
- package/dist/workspace/storage-workspace.error.js.map +1 -0
- package/dist/workspace/storage-workspace.js +849 -0
- package/dist/workspace/storage-workspace.js.map +1 -0
- package/dist/workspace/storage-workspace.path.d.ts +9 -0
- package/dist/workspace/storage-workspace.path.d.ts.map +1 -0
- package/dist/workspace/storage-workspace.path.js +68 -0
- package/dist/workspace/storage-workspace.path.js.map +1 -0
- package/dist/workspace/storage-workspace.types.d.ts +123 -0
- package/dist/workspace/storage-workspace.types.d.ts.map +1 -0
- package/dist/workspace/storage-workspace.types.js +22 -0
- package/dist/workspace/storage-workspace.types.js.map +1 -0
- package/package.json +103 -35
package/dist/index.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
export
|
|
2
|
-
export { StorageError, StorageErrorCode, isStorageError, normalizeStorageError, } from './storage.error.js';
|
|
1
|
+
export * from './core/index.js';
|
|
3
2
|
export { InjectStorage } from './inject-storage.decorator.js';
|
|
4
3
|
export { StorageModule } from './storage.module.js';
|
|
5
4
|
export { StorageService } from './storage.service.js';
|
|
6
5
|
export { DEFAULT_STORAGE_NAME, STORAGE, getStorageToken, } from './storage.tokens.js';
|
|
7
|
-
export { StorageUploadControl, } from './storage-upload-control.js';
|
|
8
6
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAapD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EACL,oBAAoB,EACpB,OAAO,EACP,eAAe,GAChB,MAAM,qBAAqB,CAAC","sourcesContent":["export * from './core/index.js';\nexport { InjectStorage } from './inject-storage.decorator.js';\nexport { StorageModule } from './storage.module.js';\nexport type {\n StorageAsyncStoreDefinition,\n StorageClassStoreDefinition,\n StorageDriverFactory,\n StorageExistingStoreDefinition,\n StorageFactoryStoreDefinition,\n StorageFeatureAsyncOptions,\n StorageFeatureOptions,\n StorageModuleAsyncOptions,\n StorageModuleOptions,\n StorageStoreDefinition,\n} from './storage-module.options.js';\nexport { StorageService } from './storage.service.js';\nexport {\n DEFAULT_STORAGE_NAME,\n STORAGE,\n getStorageToken,\n} from './storage.tokens.js';\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inject-storage.decorator.js","sourceRoot":"","sources":["../src/inject-storage.decorator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAExC,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE5E,MAAM,UAAU,aAAa,CAC3B,
|
|
1
|
+
{"version":3,"file":"inject-storage.decorator.js","sourceRoot":"","sources":["../src/inject-storage.decorator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAExC,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE5E,MAAM,UAAU,aAAa,CAC3B,IAAI,GAAW,oBAAoB;IAEnC,OAAO,MAAM,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;AACvC,CAAC","sourcesContent":["import { Inject } from '@nestjs/common';\n\nimport { DEFAULT_STORAGE_NAME, getStorageToken } from './storage.tokens.js';\n\nexport function InjectStorage(\n name: string = DEFAULT_STORAGE_NAME,\n): ParameterDecorator & PropertyDecorator {\n return Inject(getStorageToken(name));\n}\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Maximum UTF-8 byte length of a normalized storage ETag. */
|
|
2
|
+
export declare const MAX_STORAGE_ETAG_BYTES = 1024;
|
|
3
|
+
/** Returns whether a value is the package's canonical bare ETag form. */
|
|
4
|
+
export declare function isCanonicalStorageEtag(value: unknown): value is string;
|
|
5
|
+
/**
|
|
6
|
+
* Normalizes one provider-returned strong entity tag to the canonical bare
|
|
7
|
+
* form. Providers may return either the HTTP-quoted representation or an
|
|
8
|
+
* already-normalized value; lists, weak tags, and malformed values fail.
|
|
9
|
+
*/
|
|
10
|
+
export declare function normalizeProviderStorageEtag(value: unknown): string | undefined;
|
|
11
|
+
/** Serializes one canonical bare ETag as exactly one HTTP entity tag. */
|
|
12
|
+
export declare function storageEtagHeader(value: unknown): string | undefined;
|
|
13
|
+
//# sourceMappingURL=storage-etag.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-etag.d.ts","sourceRoot":"","sources":["../src/storage-etag.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,eAAO,MAAM,sBAAsB,OAAO,CAAC;AAQ3C,yEAAyE;AACzE,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAMtE;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,OAAO,GACb,MAAM,GAAG,SAAS,CASpB;AAED,yEAAyE;AACzE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAEpE"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Maximum UTF-8 byte length of a normalized storage ETag. */
|
|
2
|
+
export const MAX_STORAGE_ETAG_BYTES = 1024;
|
|
3
|
+
// Deliberately narrower than RFC 9110's etagc grammar. Commas and backslashes
|
|
4
|
+
// are excluded so a normalized value cannot be confused with an entity-tag
|
|
5
|
+
// list or be interpreted differently by legacy HTTP implementations.
|
|
6
|
+
const canonicalStorageEtag = /^(?!\*$)(?![Ww]\/)[\x21\x23-\x2B\x2D-\x5B\x5D-\x7E]+$/u;
|
|
7
|
+
/** Returns whether a value is the package's canonical bare ETag form. */
|
|
8
|
+
export function isCanonicalStorageEtag(value) {
|
|
9
|
+
return (typeof value === 'string' &&
|
|
10
|
+
value.length <= MAX_STORAGE_ETAG_BYTES &&
|
|
11
|
+
canonicalStorageEtag.test(value));
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Normalizes one provider-returned strong entity tag to the canonical bare
|
|
15
|
+
* form. Providers may return either the HTTP-quoted representation or an
|
|
16
|
+
* already-normalized value; lists, weak tags, and malformed values fail.
|
|
17
|
+
*/
|
|
18
|
+
export function normalizeProviderStorageEtag(value) {
|
|
19
|
+
if (typeof value !== 'string') {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
const opaque = value.startsWith('"') && value.endsWith('"') && value.length >= 3
|
|
23
|
+
? value.slice(1, -1)
|
|
24
|
+
: value;
|
|
25
|
+
return isCanonicalStorageEtag(opaque) ? opaque : undefined;
|
|
26
|
+
}
|
|
27
|
+
/** Serializes one canonical bare ETag as exactly one HTTP entity tag. */
|
|
28
|
+
export function storageEtagHeader(value) {
|
|
29
|
+
return isCanonicalStorageEtag(value) ? `"${value}"` : undefined;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=storage-etag.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-etag.js","sourceRoot":"","sources":["../src/storage-etag.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAC;AAE3C,8EAA8E;AAC9E,2EAA2E;AAC3E,qEAAqE;AACrE,MAAM,oBAAoB,GACxB,wDAAwD,CAAC;AAE3D,yEAAyE;AACzE,MAAM,UAAU,sBAAsB,CAAC,KAAc;IACnD,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,CAAC,MAAM,IAAI,sBAAsB;QACtC,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC,CACjC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,4BAA4B,CAC1C,KAAc;IAEd,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,MAAM,GACV,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC;QAC/D,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACpB,CAAC,CAAC,KAAK,CAAC;IACZ,OAAO,sBAAsB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AAC7D,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,iBAAiB,CAAC,KAAc;IAC9C,OAAO,sBAAsB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AAClE,CAAC","sourcesContent":["/** Maximum UTF-8 byte length of a normalized storage ETag. */\nexport const MAX_STORAGE_ETAG_BYTES = 1024;\n\n// Deliberately narrower than RFC 9110's etagc grammar. Commas and backslashes\n// are excluded so a normalized value cannot be confused with an entity-tag\n// list or be interpreted differently by legacy HTTP implementations.\nconst canonicalStorageEtag =\n /^(?!\\*$)(?![Ww]\\/)[\\x21\\x23-\\x2B\\x2D-\\x5B\\x5D-\\x7E]+$/u;\n\n/** Returns whether a value is the package's canonical bare ETag form. */\nexport function isCanonicalStorageEtag(value: unknown): value is string {\n return (\n typeof value === 'string' &&\n value.length <= MAX_STORAGE_ETAG_BYTES &&\n canonicalStorageEtag.test(value)\n );\n}\n\n/**\n * Normalizes one provider-returned strong entity tag to the canonical bare\n * form. Providers may return either the HTTP-quoted representation or an\n * already-normalized value; lists, weak tags, and malformed values fail.\n */\nexport function normalizeProviderStorageEtag(\n value: unknown,\n): string | undefined {\n if (typeof value !== 'string') {\n return undefined;\n }\n const opaque =\n value.startsWith('\"') && value.endsWith('\"') && value.length >= 3\n ? value.slice(1, -1)\n : value;\n return isCanonicalStorageEtag(opaque) ? opaque : undefined;\n}\n\n/** Serializes one canonical bare ETag as exactly one HTTP entity tag. */\nexport function storageEtagHeader(value: unknown): string | undefined {\n return isCanonicalStorageEtag(value) ? `\"${value}\"` : undefined;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage-upload-control.d.ts","sourceRoot":"","sources":["../src/storage-upload-control.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,mBAAmB,GAC7B,MAAM,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC;AAEtE;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,0BAA0B,CAAC;IAC5C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAkHD,qBAAa,oBAAoB
|
|
1
|
+
{"version":3,"file":"storage-upload-control.d.ts","sourceRoot":"","sources":["../src/storage-upload-control.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,mBAAmB,GAC7B,MAAM,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC;AAEtE;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,0BAA0B,CAAC;IAC5C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAkHD,qBAAa,oBAAoB;IAC/B,cAEC;IAED,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,oBAAoB,CAiBhD;IAED,IAAI,MAAM,IAAI,mBAAmB,CAEhC;IAED,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,IAAI,KAAK,IAAI,MAAM,GAAG,SAAS,CAE9B;IAED,KAAK,IAAI,IAAI,CAEZ;IAED,MAAM,IAAI,IAAI,CAEb;IAED,KAAK,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAErC;IAED,MAAM,IAAI,qBAAqB,GAAG,SAAS,CAW1C;CACF"}
|
package/dist/storage.client.d.ts
CHANGED
|
@@ -1,24 +1,28 @@
|
|
|
1
|
-
import type { OnApplicationShutdown } from '@nestjs/common';
|
|
2
1
|
import type { StorageDriver } from './storage.driver.js';
|
|
3
|
-
import type { StorageBody, StorageBufferedDownloadOptions, StorageBulkOptions, StorageCapabilities, StorageDeleteManyResult, StorageDownloadManyResult, StorageDownloadOptions, StorageExistsManyResult, StorageHeadManyResult, StorageListOptions, StorageListResult, StorageObject, StorageObjectMetadata, StorageOperationOptions, StoragePlugin, StorageSearchOptions, StorageSignedDownloadOptions, StorageSignedUpload, StorageSignedUploadOptions, StorageUploadManyItem, StorageUploadManyOptions, StorageUploadManyResult, StorageUploadOptions, StorageUploadResult } from './storage.types.js';
|
|
2
|
+
import type { StorageBody, StorageBufferedDownloadOptions, StorageBulkOptions, StorageCapabilities, StorageConditionalDeleteOptions, StorageConditionalReadOptions, StorageConditionalUploadOptions, StorageDeleteManyResult, StorageDownloadManyResult, StorageDownloadOptions, StorageExistsManyResult, StorageHeadManyResult, StorageListOptions, StorageListResult, StorageObject, StorageObjectMetadata, StorageOperationOptions, StoragePlugin, StoragePromotionOptions, StorageSearchOptions, StorageSignedDownloadOptions, StorageSignedUpload, StorageSignedUploadOptions, StorageUploadManyItem, StorageUploadManyOptions, StorageUploadManyResult, StorageUploadOptions, StorageUploadResult } from './storage.types.js';
|
|
4
3
|
export declare const DEFAULT_BUFFER_LIMIT: number;
|
|
5
4
|
type BulkOperationOptions = StorageBulkOptions & StorageOperationOptions;
|
|
6
5
|
type DownloadManyOptions = StorageBulkOptions & StorageDownloadOptions;
|
|
7
6
|
export interface StorageFileHandle {
|
|
8
7
|
readonly key: string;
|
|
9
8
|
upload(body: StorageBody, options?: StorageUploadOptions): Promise<StorageUploadResult>;
|
|
9
|
+
uploadConditional(body: StorageBody, options: StorageConditionalUploadOptions): Promise<StorageUploadResult>;
|
|
10
10
|
download(options?: StorageDownloadOptions): Promise<StorageObject>;
|
|
11
|
+
downloadConditional(options: StorageConditionalReadOptions): Promise<StorageObject>;
|
|
11
12
|
bytes(options?: StorageBufferedDownloadOptions): Promise<Uint8Array>;
|
|
12
13
|
text(options?: StorageBufferedDownloadOptions): Promise<string>;
|
|
13
14
|
head(options?: StorageOperationOptions): Promise<StorageObjectMetadata>;
|
|
14
15
|
exists(options?: StorageOperationOptions): Promise<boolean>;
|
|
15
16
|
delete(options?: StorageOperationOptions): Promise<void>;
|
|
17
|
+
deleteConditional(options: StorageConditionalDeleteOptions): Promise<void>;
|
|
16
18
|
signDownload(options?: StorageSignedDownloadOptions): Promise<string>;
|
|
17
19
|
signUpload(options: StorageSignedUploadOptions): Promise<StorageSignedUpload>;
|
|
18
20
|
copyTo(destinationKey: string, options?: StorageOperationOptions): Promise<void>;
|
|
19
21
|
moveTo(destinationKey: string, options?: StorageOperationOptions): Promise<void>;
|
|
22
|
+
/** Conditionally copies this staged object and deliberately retains it. */
|
|
23
|
+
promoteTo(destinationKey: string, options: StoragePromotionOptions): Promise<void>;
|
|
20
24
|
}
|
|
21
|
-
export declare class StorageClient
|
|
25
|
+
export declare class StorageClient {
|
|
22
26
|
#private;
|
|
23
27
|
readonly name: string;
|
|
24
28
|
constructor(name: string, driver: StorageDriver, plugins?: readonly StoragePlugin[]);
|
|
@@ -26,15 +30,23 @@ export declare class StorageClient implements OnApplicationShutdown {
|
|
|
26
30
|
get capabilities(): Readonly<StorageCapabilities>;
|
|
27
31
|
file(key: string): StorageFileHandle;
|
|
28
32
|
upload(key: string, body: StorageBody, options?: StorageUploadOptions): Promise<StorageUploadResult>;
|
|
33
|
+
uploadConditional(key: string, body: StorageBody, options: StorageConditionalUploadOptions): Promise<StorageUploadResult>;
|
|
29
34
|
downloadStream(key: string, options?: StorageDownloadOptions): Promise<StorageObject>;
|
|
35
|
+
downloadConditional(key: string, options: StorageConditionalReadOptions): Promise<StorageObject>;
|
|
30
36
|
downloadBytes(key: string, options?: StorageBufferedDownloadOptions): Promise<Uint8Array>;
|
|
31
37
|
downloadText(key: string, options?: StorageBufferedDownloadOptions): Promise<string>;
|
|
32
38
|
downloadJson<Result = unknown>(key: string, options?: StorageBufferedDownloadOptions): Promise<Result>;
|
|
33
39
|
head(key: string, options?: StorageOperationOptions): Promise<StorageObjectMetadata>;
|
|
34
40
|
exists(key: string, options?: StorageOperationOptions): Promise<boolean>;
|
|
35
41
|
delete(key: string, options?: StorageOperationOptions): Promise<void>;
|
|
42
|
+
deleteConditional(key: string, options: StorageConditionalDeleteOptions): Promise<void>;
|
|
36
43
|
copy(sourceKey: string, destinationKey: string, options?: StorageOperationOptions): Promise<void>;
|
|
37
44
|
move(sourceKey: string, destinationKey: string, options?: StorageOperationOptions): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Conditionally copies a staged object to a final key. This operation does
|
|
47
|
+
* not delete the source; delete it after the application commit succeeds.
|
|
48
|
+
*/
|
|
49
|
+
promote(sourceKey: string, destinationKey: string, options: StoragePromotionOptions): Promise<void>;
|
|
38
50
|
list(options?: StorageListOptions): Promise<StorageListResult>;
|
|
39
51
|
listAll(options?: StorageListOptions): AsyncGenerator<StorageObjectMetadata, void>;
|
|
40
52
|
search(pattern: string | RegExp, options?: StorageSearchOptions): AsyncIterable<StorageObjectMetadata>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.client.d.ts","sourceRoot":"","sources":["../src/storage.client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"storage.client.d.ts","sourceRoot":"","sources":["../src/storage.client.ts"],"names":[],"mappings":"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"}
|
package/dist/storage.client.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { settleMany } from './internal/settle-many.js';
|
|
2
|
+
import { isCanonicalStorageEtag } from './storage-etag.js';
|
|
2
3
|
import { StorageError, StorageErrorCode, normalizeStorageError, } from './storage.error.js';
|
|
3
4
|
export const DEFAULT_BUFFER_LIMIT = 10 * 1024 * 1024;
|
|
4
5
|
function assertKey(key, label = 'key') {
|
|
@@ -146,27 +147,116 @@ export class StorageClient {
|
|
|
146
147
|
return {
|
|
147
148
|
copyTo: (destinationKey, options) => this.copy(key, destinationKey, options),
|
|
148
149
|
delete: (options) => this.delete(key, options),
|
|
150
|
+
deleteConditional: (options) => this.deleteConditional(key, options),
|
|
149
151
|
download: (options) => this.downloadStream(key, options),
|
|
152
|
+
downloadConditional: (options) => this.downloadConditional(key, options),
|
|
150
153
|
exists: (options) => this.exists(key, options),
|
|
151
154
|
head: (options) => this.head(key, options),
|
|
152
155
|
key,
|
|
153
156
|
moveTo: (destinationKey, options) => this.move(key, destinationKey, options),
|
|
157
|
+
promoteTo: (destinationKey, options) => this.promote(key, destinationKey, options),
|
|
154
158
|
signDownload: (options) => this.signDownload(key, options),
|
|
155
159
|
signUpload: (options) => this.signUpload(key, options),
|
|
156
160
|
bytes: (options) => this.downloadBytes(key, options),
|
|
157
161
|
text: (options) => this.downloadText(key, options),
|
|
158
162
|
upload: (body, options) => this.upload(key, body, options),
|
|
163
|
+
uploadConditional: (body, options) => this.uploadConditional(key, body, options),
|
|
159
164
|
};
|
|
160
165
|
}
|
|
161
166
|
upload(key, body, options) {
|
|
162
167
|
assertKey(key);
|
|
163
168
|
return this.#execute({ key, operation: 'upload', store: this.name }, () => this.#driver.upload(key, body, options));
|
|
164
169
|
}
|
|
170
|
+
uploadConditional(key, body, options) {
|
|
171
|
+
assertKey(key);
|
|
172
|
+
const condition = options.condition;
|
|
173
|
+
if (condition === undefined ||
|
|
174
|
+
(condition.type !== 'create' && condition.type !== 'replace')) {
|
|
175
|
+
invalidArgument('condition.type must be "create" or "replace".');
|
|
176
|
+
}
|
|
177
|
+
if (condition.type === 'replace' &&
|
|
178
|
+
!isCanonicalStorageEtag(condition.etag)) {
|
|
179
|
+
invalidArgument('condition.etag must be a canonical storage ETag.');
|
|
180
|
+
}
|
|
181
|
+
const capability = condition.type === 'create'
|
|
182
|
+
? this.#driver.capabilities.conditionalCreate
|
|
183
|
+
: this.#driver.capabilities.conditionalReplace;
|
|
184
|
+
const uploadConditional = this.#driver.uploadConditional;
|
|
185
|
+
const multipartRequested = options.multipart !== undefined && options.multipart !== false;
|
|
186
|
+
const multipartCapability = this.#driver.capabilities.conditionalMultipartCompletion;
|
|
187
|
+
const multipartSupported = !multipartRequested ||
|
|
188
|
+
(condition.type === 'create'
|
|
189
|
+
? multipartCapability?.create === true
|
|
190
|
+
: multipartCapability?.replace === true);
|
|
191
|
+
if (capability === undefined ||
|
|
192
|
+
!multipartSupported ||
|
|
193
|
+
uploadConditional === undefined) {
|
|
194
|
+
throw new StorageError(`Store "${this.name}" does not support the requested conditional upload.`, {
|
|
195
|
+
code: StorageErrorCode.NOT_SUPPORTED,
|
|
196
|
+
key,
|
|
197
|
+
operation: 'upload',
|
|
198
|
+
permanent: true,
|
|
199
|
+
store: this.name,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
return this.#execute({ key, operation: 'upload', store: this.name }, () => uploadConditional.call(this.#driver, key, body, options), (result) => {
|
|
203
|
+
if (capability.resultEtag && !isCanonicalStorageEtag(result.etag)) {
|
|
204
|
+
throw new StorageError(`Store "${this.name}" committed a conditional upload without the advertised result ETag.`, {
|
|
205
|
+
code: StorageErrorCode.PROVIDER,
|
|
206
|
+
key,
|
|
207
|
+
operation: 'upload',
|
|
208
|
+
permanent: true,
|
|
209
|
+
store: this.name,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
return {
|
|
213
|
+
...(isCanonicalStorageEtag(result.etag) && {
|
|
214
|
+
appliedEtag: result.etag,
|
|
215
|
+
}),
|
|
216
|
+
};
|
|
217
|
+
});
|
|
218
|
+
}
|
|
165
219
|
downloadStream(key, options) {
|
|
166
220
|
assertKey(key);
|
|
167
221
|
assertDownloadOptions(options);
|
|
168
222
|
return this.#execute({ key, operation: 'download', store: this.name }, () => this.#driver.download(key, options));
|
|
169
223
|
}
|
|
224
|
+
downloadConditional(key, options) {
|
|
225
|
+
assertKey(key);
|
|
226
|
+
assertDownloadOptions(options);
|
|
227
|
+
if (options === undefined ||
|
|
228
|
+
options.condition === undefined ||
|
|
229
|
+
typeof options.condition !== 'object' ||
|
|
230
|
+
options.condition === null) {
|
|
231
|
+
invalidArgument('conditional read requires a condition object.');
|
|
232
|
+
}
|
|
233
|
+
const { etag, version } = options.condition;
|
|
234
|
+
if (etag !== undefined && !isCanonicalStorageEtag(etag)) {
|
|
235
|
+
invalidArgument('condition.etag must be a canonical storage ETag.');
|
|
236
|
+
}
|
|
237
|
+
if (version !== undefined &&
|
|
238
|
+
(typeof version !== 'string' || version.length === 0)) {
|
|
239
|
+
invalidArgument('condition.version must be a non-empty string.');
|
|
240
|
+
}
|
|
241
|
+
if (etag === undefined && version === undefined) {
|
|
242
|
+
invalidArgument('conditional read requires an etag, version, or both.');
|
|
243
|
+
}
|
|
244
|
+
const capability = this.#driver.capabilities.conditionalRead;
|
|
245
|
+
const downloadConditional = this.#driver.downloadConditional;
|
|
246
|
+
if (downloadConditional === undefined ||
|
|
247
|
+
capability === undefined ||
|
|
248
|
+
(etag !== undefined && !capability.etag) ||
|
|
249
|
+
(version !== undefined && !capability.version)) {
|
|
250
|
+
throw new StorageError(`Store "${this.name}" does not support the requested conditional read.`, {
|
|
251
|
+
code: StorageErrorCode.NOT_SUPPORTED,
|
|
252
|
+
key,
|
|
253
|
+
operation: 'download',
|
|
254
|
+
permanent: true,
|
|
255
|
+
store: this.name,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
return this.#execute({ key, operation: 'download', store: this.name }, () => downloadConditional.call(this.#driver, key, options));
|
|
259
|
+
}
|
|
170
260
|
async downloadBytes(key, options) {
|
|
171
261
|
const { maxBytes = DEFAULT_BUFFER_LIMIT, ...download } = options ?? {};
|
|
172
262
|
const object = await this.downloadStream(key, download);
|
|
@@ -203,6 +293,25 @@ export class StorageClient {
|
|
|
203
293
|
assertKey(key);
|
|
204
294
|
return this.#execute({ key, operation: 'delete', store: this.name }, () => this.#driver.delete(key, options));
|
|
205
295
|
}
|
|
296
|
+
deleteConditional(key, options) {
|
|
297
|
+
assertKey(key);
|
|
298
|
+
if (options.condition === undefined ||
|
|
299
|
+
!isCanonicalStorageEtag(options.condition.etag)) {
|
|
300
|
+
invalidArgument('condition.etag must be a canonical storage ETag.');
|
|
301
|
+
}
|
|
302
|
+
const capability = this.#driver.capabilities.conditionalDelete;
|
|
303
|
+
const deleteConditional = this.#driver.deleteConditional;
|
|
304
|
+
if (capability?.etag !== true || deleteConditional === undefined) {
|
|
305
|
+
throw new StorageError(`Store "${this.name}" does not support conditional delete.`, {
|
|
306
|
+
code: StorageErrorCode.NOT_SUPPORTED,
|
|
307
|
+
key,
|
|
308
|
+
operation: 'delete',
|
|
309
|
+
permanent: true,
|
|
310
|
+
store: this.name,
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
return this.#execute({ key, operation: 'delete', store: this.name }, () => deleteConditional.call(this.#driver, key, options), () => ({}));
|
|
314
|
+
}
|
|
206
315
|
copy(sourceKey, destinationKey, options) {
|
|
207
316
|
assertKey(sourceKey, 'source key');
|
|
208
317
|
assertKey(destinationKey, 'destination key');
|
|
@@ -223,6 +332,73 @@ export class StorageClient {
|
|
|
223
332
|
to: destinationKey,
|
|
224
333
|
}, () => this.#driver.move(sourceKey, destinationKey, options));
|
|
225
334
|
}
|
|
335
|
+
/**
|
|
336
|
+
* Conditionally copies a staged object to a final key. This operation does
|
|
337
|
+
* not delete the source; delete it after the application commit succeeds.
|
|
338
|
+
*/
|
|
339
|
+
promote(sourceKey, destinationKey, options) {
|
|
340
|
+
assertKey(sourceKey, 'source key');
|
|
341
|
+
assertKey(destinationKey, 'destination key');
|
|
342
|
+
const sourceEtag = options.sourceEtag;
|
|
343
|
+
const sourceVersion = options.sourceVersion;
|
|
344
|
+
if (sourceEtag !== undefined && !isCanonicalStorageEtag(sourceEtag)) {
|
|
345
|
+
invalidArgument('sourceEtag must be a canonical storage ETag.');
|
|
346
|
+
}
|
|
347
|
+
if (sourceVersion !== undefined &&
|
|
348
|
+
(typeof sourceVersion !== 'string' || sourceVersion.length === 0)) {
|
|
349
|
+
invalidArgument('sourceVersion must be a non-empty string.');
|
|
350
|
+
}
|
|
351
|
+
const destination = options.destination;
|
|
352
|
+
if (destination !== undefined &&
|
|
353
|
+
(typeof destination !== 'object' ||
|
|
354
|
+
destination === null ||
|
|
355
|
+
(destination.type !== 'create' && destination.type !== 'replace'))) {
|
|
356
|
+
invalidArgument('destination.type must be "create" or "replace".');
|
|
357
|
+
}
|
|
358
|
+
if (sourceEtag === undefined &&
|
|
359
|
+
sourceVersion === undefined &&
|
|
360
|
+
destination === undefined) {
|
|
361
|
+
invalidArgument('promote requires a source or destination precondition.');
|
|
362
|
+
}
|
|
363
|
+
if (destination?.type === 'replace' &&
|
|
364
|
+
!isCanonicalStorageEtag(destination.etag)) {
|
|
365
|
+
invalidArgument('destination.etag must be a canonical storage ETag.');
|
|
366
|
+
}
|
|
367
|
+
const sourceCapability = this.#driver.capabilities.conditionalCopySource;
|
|
368
|
+
const destinationCapability = this.#driver.capabilities.conditionalCopyDestination;
|
|
369
|
+
const promote = this.#driver.promote;
|
|
370
|
+
const hasSourcePredicate = sourceEtag !== undefined || sourceVersion !== undefined;
|
|
371
|
+
if (promote === undefined ||
|
|
372
|
+
(sourceEtag !== undefined && sourceCapability?.etag !== true) ||
|
|
373
|
+
(sourceVersion !== undefined && sourceCapability?.version !== true) ||
|
|
374
|
+
(hasSourcePredicate &&
|
|
375
|
+
destination === undefined &&
|
|
376
|
+
sourceCapability?.requiresDestinationPredicate === true) ||
|
|
377
|
+
(destination?.type === 'create' &&
|
|
378
|
+
destinationCapability?.create !== true) ||
|
|
379
|
+
(destination?.type === 'replace' &&
|
|
380
|
+
destinationCapability?.replace !== true) ||
|
|
381
|
+
(destination !== undefined &&
|
|
382
|
+
!hasSourcePredicate &&
|
|
383
|
+
destinationCapability?.requiresSourcePredicate === true) ||
|
|
384
|
+
(destination !== undefined &&
|
|
385
|
+
hasSourcePredicate &&
|
|
386
|
+
destinationCapability?.atomicWithSource !== true)) {
|
|
387
|
+
throw new StorageError(`Store "${this.name}" does not support the requested conditional promotion.`, {
|
|
388
|
+
code: StorageErrorCode.NOT_SUPPORTED,
|
|
389
|
+
key: sourceKey,
|
|
390
|
+
operation: 'promote',
|
|
391
|
+
permanent: true,
|
|
392
|
+
store: this.name,
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
return this.#execute({
|
|
396
|
+
from: sourceKey,
|
|
397
|
+
operation: 'promote',
|
|
398
|
+
store: this.name,
|
|
399
|
+
to: destinationKey,
|
|
400
|
+
}, () => promote.call(this.#driver, sourceKey, destinationKey, options), () => ({}));
|
|
401
|
+
}
|
|
226
402
|
list(options) {
|
|
227
403
|
assertListOptions(options);
|
|
228
404
|
return this.#execute({ operation: 'list', store: this.name }, () => this.#driver.list(options));
|
|
@@ -363,23 +539,49 @@ export class StorageClient {
|
|
|
363
539
|
this.#closed = true;
|
|
364
540
|
await this.#driver.close?.();
|
|
365
541
|
}
|
|
366
|
-
async #execute(context, operation) {
|
|
542
|
+
async #execute(context, operation, appliedMutation) {
|
|
543
|
+
let appliedSettlement;
|
|
367
544
|
try {
|
|
368
545
|
for (const plugin of this.#plugins) {
|
|
369
546
|
await plugin.beforeOperation?.(context);
|
|
370
547
|
}
|
|
371
548
|
const result = await operation();
|
|
549
|
+
if (appliedMutation !== undefined) {
|
|
550
|
+
// Settle before describing the result so even an unexpected local
|
|
551
|
+
// result-validation failure cannot turn a committed mutation into a
|
|
552
|
+
// safe-to-retry error.
|
|
553
|
+
appliedSettlement = {};
|
|
554
|
+
appliedSettlement = appliedMutation(result);
|
|
555
|
+
}
|
|
372
556
|
for (const plugin of this.#plugins) {
|
|
373
557
|
await plugin.afterOperation?.(context, result);
|
|
374
558
|
}
|
|
375
559
|
return result;
|
|
376
560
|
}
|
|
377
561
|
catch (error) {
|
|
378
|
-
|
|
562
|
+
let normalized = normalizeStorageError(error, {
|
|
379
563
|
...(context.key !== undefined && { key: context.key }),
|
|
380
564
|
operation: context.operation,
|
|
381
565
|
store: this.name,
|
|
382
566
|
});
|
|
567
|
+
if (appliedSettlement !== undefined) {
|
|
568
|
+
normalized = new StorageError(normalized.message, {
|
|
569
|
+
aborted: normalized.aborted,
|
|
570
|
+
applied: true,
|
|
571
|
+
...(isCanonicalStorageEtag(appliedSettlement.appliedEtag) && {
|
|
572
|
+
appliedEtag: appliedSettlement.appliedEtag,
|
|
573
|
+
}),
|
|
574
|
+
cause: normalized.cause,
|
|
575
|
+
code: normalized.code,
|
|
576
|
+
...(normalized.key !== undefined && { key: normalized.key }),
|
|
577
|
+
...(normalized.operation !== undefined && {
|
|
578
|
+
operation: normalized.operation,
|
|
579
|
+
}),
|
|
580
|
+
permanent: normalized.permanent,
|
|
581
|
+
...(normalized.store !== undefined && { store: normalized.store }),
|
|
582
|
+
timedOut: normalized.timedOut,
|
|
583
|
+
});
|
|
584
|
+
}
|
|
383
585
|
for (const plugin of this.#plugins) {
|
|
384
586
|
try {
|
|
385
587
|
await plugin.onError?.(context, normalized);
|