@nestm/storage 0.1.0-alpha.0

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 (94) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/CONTRIBUTING.md +83 -0
  3. package/LICENSE +21 -0
  4. package/README.md +386 -0
  5. package/SECURITY.md +25 -0
  6. package/dist/files-sdk/files-sdk.driver.d.ts +36 -0
  7. package/dist/files-sdk/files-sdk.driver.d.ts.map +1 -0
  8. package/dist/files-sdk/files-sdk.driver.js +352 -0
  9. package/dist/files-sdk/files-sdk.driver.js.map +1 -0
  10. package/dist/files-sdk/index.d.ts +3 -0
  11. package/dist/files-sdk/index.d.ts.map +1 -0
  12. package/dist/files-sdk/index.js +2 -0
  13. package/dist/files-sdk/index.js.map +1 -0
  14. package/dist/gateway/index.d.ts +3 -0
  15. package/dist/gateway/index.d.ts.map +1 -0
  16. package/dist/gateway/index.js +3 -0
  17. package/dist/gateway/index.js.map +1 -0
  18. package/dist/gateway/storage-gateway-fastify-parser.d.ts +9 -0
  19. package/dist/gateway/storage-gateway-fastify-parser.d.ts.map +1 -0
  20. package/dist/gateway/storage-gateway-fastify-parser.js +50 -0
  21. package/dist/gateway/storage-gateway-fastify-parser.js.map +1 -0
  22. package/dist/gateway/storage-gateway.controller.d.ts +31 -0
  23. package/dist/gateway/storage-gateway.controller.d.ts.map +1 -0
  24. package/dist/gateway/storage-gateway.controller.js +601 -0
  25. package/dist/gateway/storage-gateway.controller.js.map +1 -0
  26. package/dist/gateway/storage-gateway.guard.d.ts +7 -0
  27. package/dist/gateway/storage-gateway.guard.d.ts.map +1 -0
  28. package/dist/gateway/storage-gateway.guard.js +40 -0
  29. package/dist/gateway/storage-gateway.guard.js.map +1 -0
  30. package/dist/gateway/storage-gateway.module.d.ts +6 -0
  31. package/dist/gateway/storage-gateway.module.d.ts.map +1 -0
  32. package/dist/gateway/storage-gateway.module.js +98 -0
  33. package/dist/gateway/storage-gateway.module.js.map +1 -0
  34. package/dist/gateway/storage-gateway.tokens.d.ts +3 -0
  35. package/dist/gateway/storage-gateway.tokens.d.ts.map +1 -0
  36. package/dist/gateway/storage-gateway.tokens.js +3 -0
  37. package/dist/gateway/storage-gateway.tokens.js.map +1 -0
  38. package/dist/gateway/storage-gateway.types.d.ts +65 -0
  39. package/dist/gateway/storage-gateway.types.d.ts.map +1 -0
  40. package/dist/gateway/storage-gateway.types.js +13 -0
  41. package/dist/gateway/storage-gateway.types.js.map +1 -0
  42. package/dist/index.d.ts +11 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +8 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/inject-storage.decorator.d.ts +2 -0
  47. package/dist/inject-storage.decorator.d.ts.map +1 -0
  48. package/dist/inject-storage.decorator.js +6 -0
  49. package/dist/inject-storage.decorator.js.map +1 -0
  50. package/dist/internal/settle-many.d.ts +7 -0
  51. package/dist/internal/settle-many.d.ts.map +1 -0
  52. package/dist/internal/settle-many.js +62 -0
  53. package/dist/internal/settle-many.js.map +1 -0
  54. package/dist/storage-module.options.d.ts +62 -0
  55. package/dist/storage-module.options.d.ts.map +1 -0
  56. package/dist/storage-module.options.js +2 -0
  57. package/dist/storage-module.options.js.map +1 -0
  58. package/dist/storage-upload-control.d.ts +22 -0
  59. package/dist/storage-upload-control.d.ts.map +1 -0
  60. package/dist/storage-upload-control.js +139 -0
  61. package/dist/storage-upload-control.js.map +1 -0
  62. package/dist/storage.client.d.ts +51 -0
  63. package/dist/storage.client.d.ts.map +1 -0
  64. package/dist/storage.client.js +395 -0
  65. package/dist/storage.client.js.map +1 -0
  66. package/dist/storage.driver.d.ts +18 -0
  67. package/dist/storage.driver.d.ts.map +1 -0
  68. package/dist/storage.driver.js +2 -0
  69. package/dist/storage.driver.js.map +1 -0
  70. package/dist/storage.error.d.ts +39 -0
  71. package/dist/storage.error.d.ts.map +1 -0
  72. package/dist/storage.error.js +49 -0
  73. package/dist/storage.error.js.map +1 -0
  74. package/dist/storage.module.d.ts +11 -0
  75. package/dist/storage.module.d.ts.map +1 -0
  76. package/dist/storage.module.js +170 -0
  77. package/dist/storage.module.js.map +1 -0
  78. package/dist/storage.service.d.ts +12 -0
  79. package/dist/storage.service.d.ts.map +1 -0
  80. package/dist/storage.service.js +246 -0
  81. package/dist/storage.service.js.map +1 -0
  82. package/dist/storage.tokens.d.ts +6 -0
  83. package/dist/storage.tokens.d.ts.map +1 -0
  84. package/dist/storage.tokens.js +13 -0
  85. package/dist/storage.tokens.js.map +1 -0
  86. package/dist/storage.types.d.ts +244 -0
  87. package/dist/storage.types.d.ts.map +1 -0
  88. package/dist/storage.types.js +2 -0
  89. package/dist/storage.types.js.map +1 -0
  90. package/dist/testing/index.d.ts +9 -0
  91. package/dist/testing/index.d.ts.map +1 -0
  92. package/dist/testing/index.js +10 -0
  93. package/dist/testing/index.js.map +1 -0
  94. package/package.json +112 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-module.options.d.ts","sourceRoot":"","sources":["../src/storage-module.options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EACd,cAAc,EACd,IAAI,EACL,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;CACpC;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,SAAS,sBAAsB,EAAE,CAAC;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;IACnC,uFAAuF;IACvF,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,SAAS,sBAAsB,EAAE,CAAC;IAC1C,OAAO,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;IACnC,OAAO,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,oBAAoB;IACnC,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;CAC3E;AAED,UAAU,uBAAuB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC,CAAC;IACpC,OAAO,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;CACpC;AAED,MAAM,WAAW,6BAA8B,SAAQ,uBAAuB;IAC5E,MAAM,CAAC,EAAE,eAAe,CAAC,QAAQ,CAAC,CAAC;IACnC,UAAU,EAAE,CACV,GAAG,YAAY,EAAE,KAAK,EAAE,KACrB,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IAC5C,QAAQ,CAAC,EAAE,KAAK,CAAC;IACjB,WAAW,CAAC,EAAE,KAAK,CAAC;CACrB;AAED,MAAM,WAAW,2BAA4B,SAAQ,uBAAuB;IAC1E,QAAQ,EAAE,IAAI,CAAC,oBAAoB,CAAC,CAAC;IACrC,MAAM,CAAC,EAAE,KAAK,CAAC;IACf,UAAU,CAAC,EAAE,KAAK,CAAC;IACnB,WAAW,CAAC,EAAE,KAAK,CAAC;CACrB;AAED,MAAM,WAAW,8BAA+B,SAAQ,uBAAuB;IAC7E,WAAW,EAAE,cAAc,CAAC,oBAAoB,CAAC,CAAC;IAClD,MAAM,CAAC,EAAE,KAAK,CAAC;IACf,QAAQ,CAAC,EAAE,KAAK,CAAC;IACjB,UAAU,CAAC,EAAE,KAAK,CAAC;CACpB;AAED,MAAM,MAAM,2BAA2B,GACnC,6BAA6B,GAC7B,2BAA2B,GAC3B,8BAA8B,CAAC;AAEnC,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,SAAS,2BAA2B,EAAE,CAAC;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;IACnC,OAAO,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC,CAAC;IACpC,uFAAuF;IACvF,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,SAAS,2BAA2B,EAAE,CAAC;IAC/C,OAAO,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;IACnC,OAAO,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC,CAAC;CACrC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=storage-module.options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-module.options.js","sourceRoot":"","sources":["../src/storage-module.options.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n FactoryProvider,\n InjectionToken,\n ModuleMetadata,\n Type,\n} from '@nestjs/common';\n\nimport type { StorageDriver } from './storage.driver.js';\nimport type { StoragePlugin } from './storage.types.js';\n\nexport interface StorageStoreDefinition {\n name?: string;\n driver: StorageDriver;\n plugins?: readonly StoragePlugin[];\n}\n\nexport interface StorageModuleOptions {\n stores: readonly StorageStoreDefinition[];\n default?: string;\n plugins?: readonly StoragePlugin[];\n /** Makes the configured clients and manager globally injectable. Defaults to false. */\n isGlobal?: boolean;\n}\n\nexport interface StorageFeatureOptions {\n stores: readonly StorageStoreDefinition[];\n plugins?: readonly StoragePlugin[];\n imports?: ModuleMetadata['imports'];\n}\n\nexport interface StorageDriverFactory {\n createStorageDriver(name: string): StorageDriver | Promise<StorageDriver>;\n}\n\ninterface StorageAsyncStoreCommon {\n name?: string;\n imports?: ModuleMetadata['imports'];\n plugins?: readonly StoragePlugin[];\n}\n\nexport interface StorageFactoryStoreDefinition extends StorageAsyncStoreCommon {\n inject?: FactoryProvider['inject'];\n useFactory: (\n ...dependencies: never[]\n ) => StorageDriver | Promise<StorageDriver>;\n useClass?: never;\n useExisting?: never;\n}\n\nexport interface StorageClassStoreDefinition extends StorageAsyncStoreCommon {\n useClass: Type<StorageDriverFactory>;\n inject?: never;\n useFactory?: never;\n useExisting?: never;\n}\n\nexport interface StorageExistingStoreDefinition extends StorageAsyncStoreCommon {\n useExisting: InjectionToken<StorageDriverFactory>;\n inject?: never;\n useClass?: never;\n useFactory?: never;\n}\n\nexport type StorageAsyncStoreDefinition =\n | StorageFactoryStoreDefinition\n | StorageClassStoreDefinition\n | StorageExistingStoreDefinition;\n\nexport interface StorageModuleAsyncOptions {\n stores: readonly StorageAsyncStoreDefinition[];\n default?: string;\n plugins?: readonly StoragePlugin[];\n imports?: ModuleMetadata['imports'];\n /** Makes the configured clients and manager globally injectable. Defaults to false. */\n isGlobal?: boolean;\n}\n\nexport interface StorageFeatureAsyncOptions {\n stores: readonly StorageAsyncStoreDefinition[];\n plugins?: readonly StoragePlugin[];\n imports?: ModuleMetadata['imports'];\n}\n"]}
@@ -0,0 +1,22 @@
1
+ export type StorageUploadStatus = 'idle' | 'uploading' | 'paused' | 'completed' | 'aborted' | 'error';
2
+ /**
3
+ * Opaque, JSON-serializable resume token. Its payload is deliberately unknown:
4
+ * provider session fields are an implementation detail of the pinned engine.
5
+ */
6
+ export interface StorageResumableToken {
7
+ readonly version: 1;
8
+ readonly format: '@nestm/storage/resumable';
9
+ readonly session: unknown;
10
+ }
11
+ export declare class StorageUploadControl {
12
+ constructor();
13
+ static from(token: unknown): StorageUploadControl;
14
+ get status(): StorageUploadStatus;
15
+ get loaded(): number;
16
+ get total(): number | undefined;
17
+ pause(): void;
18
+ resume(): void;
19
+ abort(reason?: unknown): Promise<void>;
20
+ toJSON(): StorageResumableToken | undefined;
21
+ }
22
+ //# sourceMappingURL=storage-upload-control.d.ts.map
@@ -0,0 +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;;IAK/B,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,oBAAoB;IAmBjD,IAAI,MAAM,IAAI,mBAAmB,CAEhC;IAED,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,IAAI,KAAK,IAAI,MAAM,GAAG,SAAS,CAE9B;IAED,KAAK,IAAI,IAAI;IAIb,MAAM,IAAI,IAAI;IAId,KAAK,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAItC,MAAM,IAAI,qBAAqB,GAAG,SAAS;CAY5C"}
@@ -0,0 +1,139 @@
1
+ import { UploadControl } from 'files-sdk';
2
+ import { StorageError, StorageErrorCode } from './storage.error.js';
3
+ const controls = new WeakMap();
4
+ function nonEmptyString(value) {
5
+ return typeof value === 'string' && value.length > 0;
6
+ }
7
+ function positiveNumber(value) {
8
+ return typeof value === 'number' && Number.isFinite(value) && value > 0;
9
+ }
10
+ function nonNegativeNumber(value) {
11
+ return typeof value === 'number' && Number.isFinite(value) && value >= 0;
12
+ }
13
+ function recordOf(value) {
14
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
15
+ ? value
16
+ : undefined;
17
+ }
18
+ function validParts(value) {
19
+ return (Array.isArray(value) &&
20
+ value.every((part) => {
21
+ const record = recordOf(part);
22
+ return (record !== undefined &&
23
+ positiveNumber(record.partNumber) &&
24
+ nonNegativeNumber(record.size) &&
25
+ (record.etag === undefined || nonEmptyString(record.etag)));
26
+ }));
27
+ }
28
+ function validResumableSession(value) {
29
+ const session = recordOf(value);
30
+ if (session === undefined || !nonEmptyString(session.provider)) {
31
+ return false;
32
+ }
33
+ const strings = (...fields) => fields.every((field) => nonEmptyString(session[field]));
34
+ switch (session.provider) {
35
+ case 's3':
36
+ return (strings('bucket', 'key', 'uploadId') && positiveNumber(session.partSize));
37
+ case 'gcs':
38
+ return strings('bucket', 'key', 'uri');
39
+ case 'google-drive':
40
+ return strings('key', 'uri');
41
+ case 'azure':
42
+ return (strings('container', 'blob', 'contentType') &&
43
+ positiveNumber(session.blockSize));
44
+ case 'onedrive':
45
+ return strings('itemPath', 'uploadUrl');
46
+ case 'dropbox':
47
+ return (strings('path', 'sessionId', 'contentType') &&
48
+ nonNegativeNumber(session.offset));
49
+ case 'vercel-blob':
50
+ return (strings('key', 'storageKey', 'uploadId', 'contentType') &&
51
+ positiveNumber(session.partSize) &&
52
+ validParts(session.parts));
53
+ case 'fs':
54
+ return strings('key', 'tempPath', 'contentType');
55
+ case 'memory':
56
+ return strings('key', 'uploadId', 'contentType');
57
+ case 'ftp':
58
+ case 'sftp':
59
+ return strings('key');
60
+ case 'bun-s3':
61
+ return strings('key', 'uploadId', 'contentType');
62
+ case 'supabase':
63
+ return strings('key', 'uri', 'contentType');
64
+ case 'appwrite':
65
+ return (strings('key', 'fileId', 'contentType') &&
66
+ nonNegativeNumber(session.offset));
67
+ case 'cloudinary':
68
+ return (strings('key', 'uploadId', 'contentType') &&
69
+ nonNegativeNumber(session.offset));
70
+ case 'box':
71
+ return strings('key', 'uploadId', 'contentType');
72
+ default:
73
+ return false;
74
+ }
75
+ }
76
+ function controlOf(control) {
77
+ const delegate = controls.get(control);
78
+ if (delegate === undefined) {
79
+ throw new StorageError('Invalid storage upload control.', {
80
+ code: StorageErrorCode.INVALID_ARGUMENT,
81
+ permanent: true,
82
+ });
83
+ }
84
+ return delegate;
85
+ }
86
+ export class StorageUploadControl {
87
+ constructor() {
88
+ controls.set(this, new UploadControl());
89
+ }
90
+ static from(token) {
91
+ const value = recordOf(token);
92
+ if (value === undefined ||
93
+ value.version !== 1 ||
94
+ value.format !== '@nestm/storage/resumable' ||
95
+ !validResumableSession(value.session)) {
96
+ throw new StorageError('Invalid storage resumable-upload token.', {
97
+ code: StorageErrorCode.INVALID_ARGUMENT,
98
+ permanent: true,
99
+ });
100
+ }
101
+ const wrapper = new StorageUploadControl();
102
+ controls.set(wrapper, UploadControl.from(value.session));
103
+ return wrapper;
104
+ }
105
+ get status() {
106
+ return controlOf(this).status;
107
+ }
108
+ get loaded() {
109
+ return controlOf(this).loaded;
110
+ }
111
+ get total() {
112
+ return controlOf(this).total;
113
+ }
114
+ pause() {
115
+ controlOf(this).pause();
116
+ }
117
+ resume() {
118
+ controlOf(this).resume();
119
+ }
120
+ abort(reason) {
121
+ return controlOf(this).abort(reason);
122
+ }
123
+ toJSON() {
124
+ const session = controlOf(this).toJSON();
125
+ if (session === undefined) {
126
+ return undefined;
127
+ }
128
+ return {
129
+ format: '@nestm/storage/resumable',
130
+ session,
131
+ version: 1,
132
+ };
133
+ }
134
+ }
135
+ /** @internal */
136
+ export function getFilesSdkUploadControl(control) {
137
+ return controlOf(control);
138
+ }
139
+ //# sourceMappingURL=storage-upload-control.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-upload-control.js","sourceRoot":"","sources":["../src/storage-upload-control.ts"],"names":[],"mappings":"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","sourcesContent":["import { UploadControl, type ResumableUploadSession } from 'files-sdk';\n\nimport { StorageError, StorageErrorCode } from './storage.error.js';\n\nexport type StorageUploadStatus =\n 'idle' | 'uploading' | 'paused' | 'completed' | 'aborted' | 'error';\n\n/**\n * Opaque, JSON-serializable resume token. Its payload is deliberately unknown:\n * provider session fields are an implementation detail of the pinned engine.\n */\nexport interface StorageResumableToken {\n readonly version: 1;\n readonly format: '@nestm/storage/resumable';\n readonly session: unknown;\n}\n\nconst controls = new WeakMap<StorageUploadControl, UploadControl>();\n\nfunction nonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.length > 0;\n}\n\nfunction positiveNumber(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value) && value > 0;\n}\n\nfunction nonNegativeNumber(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value) && value >= 0;\n}\n\nfunction recordOf(value: unknown): Record<string, unknown> | undefined {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\nfunction validParts(value: unknown): boolean {\n return (\n Array.isArray(value) &&\n value.every((part) => {\n const record = recordOf(part);\n return (\n record !== undefined &&\n positiveNumber(record.partNumber) &&\n nonNegativeNumber(record.size) &&\n (record.etag === undefined || nonEmptyString(record.etag))\n );\n })\n );\n}\n\nfunction validResumableSession(\n value: unknown,\n): value is ResumableUploadSession {\n const session = recordOf(value);\n if (session === undefined || !nonEmptyString(session.provider)) {\n return false;\n }\n\n const strings = (...fields: string[]): boolean =>\n fields.every((field) => nonEmptyString(session[field]));\n\n switch (session.provider) {\n case 's3':\n return (\n strings('bucket', 'key', 'uploadId') && positiveNumber(session.partSize)\n );\n case 'gcs':\n return strings('bucket', 'key', 'uri');\n case 'google-drive':\n return strings('key', 'uri');\n case 'azure':\n return (\n strings('container', 'blob', 'contentType') &&\n positiveNumber(session.blockSize)\n );\n case 'onedrive':\n return strings('itemPath', 'uploadUrl');\n case 'dropbox':\n return (\n strings('path', 'sessionId', 'contentType') &&\n nonNegativeNumber(session.offset)\n );\n case 'vercel-blob':\n return (\n strings('key', 'storageKey', 'uploadId', 'contentType') &&\n positiveNumber(session.partSize) &&\n validParts(session.parts)\n );\n case 'fs':\n return strings('key', 'tempPath', 'contentType');\n case 'memory':\n return strings('key', 'uploadId', 'contentType');\n case 'ftp':\n case 'sftp':\n return strings('key');\n case 'bun-s3':\n return strings('key', 'uploadId', 'contentType');\n case 'supabase':\n return strings('key', 'uri', 'contentType');\n case 'appwrite':\n return (\n strings('key', 'fileId', 'contentType') &&\n nonNegativeNumber(session.offset)\n );\n case 'cloudinary':\n return (\n strings('key', 'uploadId', 'contentType') &&\n nonNegativeNumber(session.offset)\n );\n case 'box':\n return strings('key', 'uploadId', 'contentType');\n default:\n return false;\n }\n}\n\nfunction controlOf(control: StorageUploadControl): UploadControl {\n const delegate = controls.get(control);\n if (delegate === undefined) {\n throw new StorageError('Invalid storage upload control.', {\n code: StorageErrorCode.INVALID_ARGUMENT,\n permanent: true,\n });\n }\n return delegate;\n}\n\nexport class StorageUploadControl {\n constructor() {\n controls.set(this, new UploadControl());\n }\n\n static from(token: unknown): StorageUploadControl {\n const value = recordOf(token);\n if (\n value === undefined ||\n value.version !== 1 ||\n value.format !== '@nestm/storage/resumable' ||\n !validResumableSession(value.session)\n ) {\n throw new StorageError('Invalid storage resumable-upload token.', {\n code: StorageErrorCode.INVALID_ARGUMENT,\n permanent: true,\n });\n }\n\n const wrapper = new StorageUploadControl();\n controls.set(wrapper, UploadControl.from(value.session));\n return wrapper;\n }\n\n get status(): StorageUploadStatus {\n return controlOf(this).status;\n }\n\n get loaded(): number {\n return controlOf(this).loaded;\n }\n\n get total(): number | undefined {\n return controlOf(this).total;\n }\n\n pause(): void {\n controlOf(this).pause();\n }\n\n resume(): void {\n controlOf(this).resume();\n }\n\n abort(reason?: unknown): Promise<void> {\n return controlOf(this).abort(reason);\n }\n\n toJSON(): StorageResumableToken | undefined {\n const session = controlOf(this).toJSON();\n if (session === undefined) {\n return undefined;\n }\n\n return {\n format: '@nestm/storage/resumable',\n session,\n version: 1,\n };\n }\n}\n\n/** @internal */\nexport function getFilesSdkUploadControl(\n control: StorageUploadControl,\n): UploadControl {\n return controlOf(control);\n}\n"]}
@@ -0,0 +1,51 @@
1
+ import type { OnApplicationShutdown } from '@nestjs/common';
2
+ 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';
4
+ export declare const DEFAULT_BUFFER_LIMIT: number;
5
+ type BulkOperationOptions = StorageBulkOptions & StorageOperationOptions;
6
+ type DownloadManyOptions = StorageBulkOptions & StorageDownloadOptions;
7
+ export interface StorageFileHandle {
8
+ readonly key: string;
9
+ upload(body: StorageBody, options?: StorageUploadOptions): Promise<StorageUploadResult>;
10
+ download(options?: StorageDownloadOptions): Promise<StorageObject>;
11
+ bytes(options?: StorageBufferedDownloadOptions): Promise<Uint8Array>;
12
+ text(options?: StorageBufferedDownloadOptions): Promise<string>;
13
+ head(options?: StorageOperationOptions): Promise<StorageObjectMetadata>;
14
+ exists(options?: StorageOperationOptions): Promise<boolean>;
15
+ delete(options?: StorageOperationOptions): Promise<void>;
16
+ signDownload(options?: StorageSignedDownloadOptions): Promise<string>;
17
+ signUpload(options: StorageSignedUploadOptions): Promise<StorageSignedUpload>;
18
+ copyTo(destinationKey: string, options?: StorageOperationOptions): Promise<void>;
19
+ moveTo(destinationKey: string, options?: StorageOperationOptions): Promise<void>;
20
+ }
21
+ export declare class StorageClient implements OnApplicationShutdown {
22
+ #private;
23
+ readonly name: string;
24
+ constructor(name: string, driver: StorageDriver, plugins?: readonly StoragePlugin[]);
25
+ get driverName(): string;
26
+ get capabilities(): Readonly<StorageCapabilities>;
27
+ file(key: string): StorageFileHandle;
28
+ upload(key: string, body: StorageBody, options?: StorageUploadOptions): Promise<StorageUploadResult>;
29
+ downloadStream(key: string, options?: StorageDownloadOptions): Promise<StorageObject>;
30
+ downloadBytes(key: string, options?: StorageBufferedDownloadOptions): Promise<Uint8Array>;
31
+ downloadText(key: string, options?: StorageBufferedDownloadOptions): Promise<string>;
32
+ downloadJson<Result = unknown>(key: string, options?: StorageBufferedDownloadOptions): Promise<Result>;
33
+ head(key: string, options?: StorageOperationOptions): Promise<StorageObjectMetadata>;
34
+ exists(key: string, options?: StorageOperationOptions): Promise<boolean>;
35
+ delete(key: string, options?: StorageOperationOptions): Promise<void>;
36
+ copy(sourceKey: string, destinationKey: string, options?: StorageOperationOptions): Promise<void>;
37
+ move(sourceKey: string, destinationKey: string, options?: StorageOperationOptions): Promise<void>;
38
+ list(options?: StorageListOptions): Promise<StorageListResult>;
39
+ listAll(options?: StorageListOptions): AsyncGenerator<StorageObjectMetadata, void>;
40
+ search(pattern: string | RegExp, options?: StorageSearchOptions): AsyncIterable<StorageObjectMetadata>;
41
+ signDownload(key: string, options?: StorageSignedDownloadOptions): Promise<string>;
42
+ signUpload(key: string, options: StorageSignedUploadOptions): Promise<StorageSignedUpload>;
43
+ uploadMany(items: readonly StorageUploadManyItem[], options?: StorageUploadManyOptions): Promise<StorageUploadManyResult>;
44
+ downloadMany(keys: readonly string[], options?: DownloadManyOptions): Promise<StorageDownloadManyResult>;
45
+ headMany(keys: readonly string[], options?: BulkOperationOptions): Promise<StorageHeadManyResult>;
46
+ existsMany(keys: readonly string[], options?: BulkOperationOptions): Promise<StorageExistsManyResult>;
47
+ deleteMany(keys: readonly string[], options?: BulkOperationOptions): Promise<StorageDeleteManyResult>;
48
+ onApplicationShutdown(): Promise<void>;
49
+ }
50
+ export {};
51
+ //# sourceMappingURL=storage.client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.client.d.ts","sourceRoot":"","sources":["../src/storage.client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,395 @@
1
+ import { settleMany } from './internal/settle-many.js';
2
+ import { StorageError, StorageErrorCode, normalizeStorageError, } from './storage.error.js';
3
+ export const DEFAULT_BUFFER_LIMIT = 10 * 1024 * 1024;
4
+ function assertKey(key, label = 'key') {
5
+ if (typeof key !== 'string' || key.length === 0) {
6
+ throw new StorageError(`${label} must be a non-empty string.`, {
7
+ code: StorageErrorCode.INVALID_ARGUMENT,
8
+ permanent: true,
9
+ });
10
+ }
11
+ }
12
+ function invalidArgument(message) {
13
+ throw new StorageError(message, {
14
+ code: StorageErrorCode.INVALID_ARGUMENT,
15
+ permanent: true,
16
+ });
17
+ }
18
+ function assertPositiveSafeInteger(value, label) {
19
+ if (value !== undefined && (!Number.isSafeInteger(value) || value <= 0)) {
20
+ invalidArgument(`${label} must be a positive safe integer.`);
21
+ }
22
+ }
23
+ function assertDownloadOptions(options) {
24
+ const range = options?.range;
25
+ if (range === undefined) {
26
+ return;
27
+ }
28
+ if (!Number.isSafeInteger(range.start) || range.start < 0) {
29
+ invalidArgument('range.start must be a non-negative safe integer.');
30
+ }
31
+ if (range.end !== undefined &&
32
+ (!Number.isSafeInteger(range.end) || range.end < range.start)) {
33
+ invalidArgument('range.end must be a safe integer greater than or equal to range.start.');
34
+ }
35
+ }
36
+ function assertListOptions(options) {
37
+ if (options?.delimiter !== undefined && options.delimiter.length === 0) {
38
+ invalidArgument('delimiter must be a non-empty string.');
39
+ }
40
+ assertPositiveSafeInteger(options?.limit, 'limit');
41
+ }
42
+ function assertSearchOptions(options) {
43
+ assertPositiveSafeInteger(options?.limit, 'limit');
44
+ assertPositiveSafeInteger(options?.maxResults, 'maxResults');
45
+ }
46
+ function assertSignedUploadOptions(options) {
47
+ assertPositiveSafeInteger(options.expiresIn, 'expiresIn');
48
+ assertPositiveSafeInteger(options.maxSize, 'maxSize');
49
+ if (options.minSize !== undefined &&
50
+ (!Number.isSafeInteger(options.minSize) || options.minSize < 0)) {
51
+ invalidArgument('minSize must be a non-negative safe integer.');
52
+ }
53
+ if (options.minSize !== undefined &&
54
+ options.maxSize !== undefined &&
55
+ options.minSize > options.maxSize) {
56
+ invalidArgument('minSize cannot be greater than maxSize.');
57
+ }
58
+ }
59
+ function operationOptions(options) {
60
+ if (options === undefined) {
61
+ return undefined;
62
+ }
63
+ return {
64
+ ...(options.retries !== undefined && { retries: options.retries }),
65
+ ...(options.signal !== undefined && { signal: options.signal }),
66
+ ...(options.timeout !== undefined && { timeout: options.timeout }),
67
+ };
68
+ }
69
+ function downloadOptions(options) {
70
+ if (options === undefined) {
71
+ return undefined;
72
+ }
73
+ return {
74
+ ...operationOptions(options),
75
+ ...(options.range !== undefined && { range: options.range }),
76
+ };
77
+ }
78
+ async function collectBody(object, maxBytes) {
79
+ if (maxBytes !== Infinity && (!Number.isFinite(maxBytes) || maxBytes < 0)) {
80
+ await object.body.cancel().catch(() => undefined);
81
+ throw new StorageError('maxBytes must be a non-negative finite number or Infinity.', {
82
+ code: StorageErrorCode.INVALID_ARGUMENT,
83
+ key: object.key,
84
+ permanent: true,
85
+ });
86
+ }
87
+ if (maxBytes !== Infinity && object.size > maxBytes) {
88
+ await object.body.cancel().catch(() => undefined);
89
+ throw new StorageError(`Object "${object.key}" is ${object.size} bytes, exceeding the ${maxBytes}-byte buffer limit.`, {
90
+ code: StorageErrorCode.LIMIT_EXCEEDED,
91
+ key: object.key,
92
+ permanent: true,
93
+ });
94
+ }
95
+ const chunks = [];
96
+ let total = 0;
97
+ const reader = object.body.getReader();
98
+ try {
99
+ while (true) {
100
+ const { done, value } = await reader.read();
101
+ if (done) {
102
+ break;
103
+ }
104
+ total += value.byteLength;
105
+ if (maxBytes !== Infinity && total > maxBytes) {
106
+ await reader.cancel().catch(() => undefined);
107
+ throw new StorageError(`Object "${object.key}" exceeded the ${maxBytes}-byte buffer limit while downloading.`, {
108
+ code: StorageErrorCode.LIMIT_EXCEEDED,
109
+ key: object.key,
110
+ permanent: true,
111
+ });
112
+ }
113
+ chunks.push(value);
114
+ }
115
+ }
116
+ finally {
117
+ reader.releaseLock();
118
+ }
119
+ const bytes = new Uint8Array(total);
120
+ let offset = 0;
121
+ for (const chunk of chunks) {
122
+ bytes.set(chunk, offset);
123
+ offset += chunk.byteLength;
124
+ }
125
+ return bytes;
126
+ }
127
+ export class StorageClient {
128
+ name;
129
+ #driver;
130
+ #plugins;
131
+ #closed = false;
132
+ constructor(name, driver, plugins = []) {
133
+ this.name = name;
134
+ assertKey(name, 'store name');
135
+ this.#driver = driver;
136
+ this.#plugins = [...plugins];
137
+ }
138
+ get driverName() {
139
+ return this.#driver.name;
140
+ }
141
+ get capabilities() {
142
+ return this.#driver.capabilities;
143
+ }
144
+ file(key) {
145
+ assertKey(key);
146
+ return {
147
+ copyTo: (destinationKey, options) => this.copy(key, destinationKey, options),
148
+ delete: (options) => this.delete(key, options),
149
+ download: (options) => this.downloadStream(key, options),
150
+ exists: (options) => this.exists(key, options),
151
+ head: (options) => this.head(key, options),
152
+ key,
153
+ moveTo: (destinationKey, options) => this.move(key, destinationKey, options),
154
+ signDownload: (options) => this.signDownload(key, options),
155
+ signUpload: (options) => this.signUpload(key, options),
156
+ bytes: (options) => this.downloadBytes(key, options),
157
+ text: (options) => this.downloadText(key, options),
158
+ upload: (body, options) => this.upload(key, body, options),
159
+ };
160
+ }
161
+ upload(key, body, options) {
162
+ assertKey(key);
163
+ return this.#execute({ key, operation: 'upload', store: this.name }, () => this.#driver.upload(key, body, options));
164
+ }
165
+ downloadStream(key, options) {
166
+ assertKey(key);
167
+ assertDownloadOptions(options);
168
+ return this.#execute({ key, operation: 'download', store: this.name }, () => this.#driver.download(key, options));
169
+ }
170
+ async downloadBytes(key, options) {
171
+ const { maxBytes = DEFAULT_BUFFER_LIMIT, ...download } = options ?? {};
172
+ const object = await this.downloadStream(key, download);
173
+ return collectBody(object, maxBytes);
174
+ }
175
+ async downloadText(key, options) {
176
+ return new TextDecoder().decode(await this.downloadBytes(key, options));
177
+ }
178
+ async downloadJson(key, options) {
179
+ const text = await this.downloadText(key, options);
180
+ try {
181
+ return JSON.parse(text);
182
+ }
183
+ catch (error) {
184
+ throw new StorageError(`Object "${key}" does not contain valid JSON.`, {
185
+ cause: error,
186
+ code: StorageErrorCode.INVALID_ARGUMENT,
187
+ key,
188
+ operation: 'download',
189
+ permanent: true,
190
+ store: this.name,
191
+ });
192
+ }
193
+ }
194
+ head(key, options) {
195
+ assertKey(key);
196
+ return this.#execute({ key, operation: 'head', store: this.name }, () => this.#driver.head(key, options));
197
+ }
198
+ exists(key, options) {
199
+ assertKey(key);
200
+ return this.#execute({ key, operation: 'exists', store: this.name }, () => this.#driver.exists(key, options));
201
+ }
202
+ delete(key, options) {
203
+ assertKey(key);
204
+ return this.#execute({ key, operation: 'delete', store: this.name }, () => this.#driver.delete(key, options));
205
+ }
206
+ copy(sourceKey, destinationKey, options) {
207
+ assertKey(sourceKey, 'source key');
208
+ assertKey(destinationKey, 'destination key');
209
+ return this.#execute({
210
+ from: sourceKey,
211
+ operation: 'copy',
212
+ store: this.name,
213
+ to: destinationKey,
214
+ }, () => this.#driver.copy(sourceKey, destinationKey, options));
215
+ }
216
+ move(sourceKey, destinationKey, options) {
217
+ assertKey(sourceKey, 'source key');
218
+ assertKey(destinationKey, 'destination key');
219
+ return this.#execute({
220
+ from: sourceKey,
221
+ operation: 'move',
222
+ store: this.name,
223
+ to: destinationKey,
224
+ }, () => this.#driver.move(sourceKey, destinationKey, options));
225
+ }
226
+ list(options) {
227
+ assertListOptions(options);
228
+ return this.#execute({ operation: 'list', store: this.name }, () => this.#driver.list(options));
229
+ }
230
+ async *listAll(options) {
231
+ const { delimiter: _delimiter, ...walkOptions } = options ?? {};
232
+ void _delimiter;
233
+ let cursor = walkOptions.cursor;
234
+ const seen = new Set();
235
+ do {
236
+ const page = await this.list({
237
+ ...walkOptions,
238
+ ...(cursor !== undefined && { cursor }),
239
+ });
240
+ yield* page.items;
241
+ cursor = page.cursor;
242
+ if (cursor !== undefined) {
243
+ if (seen.has(cursor)) {
244
+ throw new StorageError(`Store "${this.name}" returned a repeated pagination cursor.`, {
245
+ code: StorageErrorCode.PROVIDER,
246
+ operation: 'list',
247
+ permanent: true,
248
+ store: this.name,
249
+ });
250
+ }
251
+ seen.add(cursor);
252
+ }
253
+ } while (cursor !== undefined);
254
+ }
255
+ search(pattern, options) {
256
+ assertSearchOptions(options);
257
+ return this.#search(pattern, options);
258
+ }
259
+ async *#search(pattern, options) {
260
+ const context = {
261
+ operation: 'search',
262
+ store: this.name,
263
+ };
264
+ try {
265
+ for (const plugin of this.#plugins) {
266
+ await plugin.beforeOperation?.(context);
267
+ }
268
+ for await (const object of this.#driver.search(pattern, options)) {
269
+ yield object;
270
+ }
271
+ for (const plugin of this.#plugins) {
272
+ await plugin.afterOperation?.(context, undefined);
273
+ }
274
+ }
275
+ catch (error) {
276
+ const normalized = normalizeStorageError(error, {
277
+ operation: 'search',
278
+ store: this.name,
279
+ });
280
+ for (const plugin of this.#plugins) {
281
+ try {
282
+ await plugin.onError?.(context, normalized);
283
+ }
284
+ catch {
285
+ // Preserve the original storage error.
286
+ }
287
+ }
288
+ throw normalized;
289
+ }
290
+ }
291
+ signDownload(key, options) {
292
+ assertKey(key);
293
+ return this.#execute({ key, operation: 'signDownload', store: this.name }, () => this.#driver.signDownload(key, options));
294
+ }
295
+ signUpload(key, options) {
296
+ assertKey(key);
297
+ assertSignedUploadOptions(options);
298
+ return this.#execute({ key, operation: 'signUpload', store: this.name }, () => this.#driver.signUpload(key, options));
299
+ }
300
+ async uploadMany(items, options) {
301
+ const settled = await settleMany(items, (item) => item.key, (item) => this.upload(item.key, item.body, {
302
+ ...operationOptions(options),
303
+ ...(item.cacheControl !== undefined && {
304
+ cacheControl: item.cacheControl,
305
+ }),
306
+ ...(item.contentType !== undefined && {
307
+ contentType: item.contentType,
308
+ }),
309
+ ...(item.metadata !== undefined && { metadata: item.metadata }),
310
+ ...(item.multipart !== undefined && {
311
+ multipart: item.multipart,
312
+ }),
313
+ ...(options?.onProgress !== undefined && {
314
+ onProgress: (progress) => options.onProgress?.({ ...progress, key: item.key }),
315
+ }),
316
+ }), options);
317
+ return {
318
+ uploaded: settled.results,
319
+ ...(settled.errors.length > 0 && { errors: settled.errors }),
320
+ };
321
+ }
322
+ async downloadMany(keys, options) {
323
+ const settled = await settleMany(keys, (key) => key, (key) => this.downloadStream(key, downloadOptions(options)), options);
324
+ return {
325
+ downloaded: settled.results,
326
+ ...(settled.errors.length > 0 && { errors: settled.errors }),
327
+ };
328
+ }
329
+ async headMany(keys, options) {
330
+ const settled = await settleMany(keys, (key) => key, (key) => this.head(key, operationOptions(options)), options);
331
+ return {
332
+ objects: settled.results,
333
+ ...(settled.errors.length > 0 && { errors: settled.errors }),
334
+ };
335
+ }
336
+ async existsMany(keys, options) {
337
+ const settled = await settleMany(keys, (key) => key, async (key) => ({ exists: await this.exists(key, options), key }), options);
338
+ const existing = [];
339
+ const missing = [];
340
+ for (const result of settled.results) {
341
+ (result.exists ? existing : missing).push(result.key);
342
+ }
343
+ return {
344
+ existing,
345
+ missing,
346
+ ...(settled.errors.length > 0 && { errors: settled.errors }),
347
+ };
348
+ }
349
+ async deleteMany(keys, options) {
350
+ const settled = await settleMany(keys, (key) => key, async (key) => {
351
+ await this.delete(key, operationOptions(options));
352
+ return key;
353
+ }, options);
354
+ return {
355
+ deleted: settled.results,
356
+ ...(settled.errors.length > 0 && { errors: settled.errors }),
357
+ };
358
+ }
359
+ async onApplicationShutdown() {
360
+ if (this.#closed) {
361
+ return;
362
+ }
363
+ this.#closed = true;
364
+ await this.#driver.close?.();
365
+ }
366
+ async #execute(context, operation) {
367
+ try {
368
+ for (const plugin of this.#plugins) {
369
+ await plugin.beforeOperation?.(context);
370
+ }
371
+ const result = await operation();
372
+ for (const plugin of this.#plugins) {
373
+ await plugin.afterOperation?.(context, result);
374
+ }
375
+ return result;
376
+ }
377
+ catch (error) {
378
+ const normalized = normalizeStorageError(error, {
379
+ ...(context.key !== undefined && { key: context.key }),
380
+ operation: context.operation,
381
+ store: this.name,
382
+ });
383
+ for (const plugin of this.#plugins) {
384
+ try {
385
+ await plugin.onError?.(context, normalized);
386
+ }
387
+ catch {
388
+ // Preserve the original storage error.
389
+ }
390
+ }
391
+ throw normalized;
392
+ }
393
+ }
394
+ }
395
+ //# sourceMappingURL=storage.client.js.map