@nestm/storage 0.1.0-alpha.5 → 0.1.0-alpha.6

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 (55) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +0 -114
  3. package/package.json +1 -17
  4. package/dist/artifacts/artifact-storage.d.ts +0 -161
  5. package/dist/artifacts/artifact-storage.d.ts.map +0 -1
  6. package/dist/artifacts/artifact-storage.js +0 -452
  7. package/dist/artifacts/artifact-storage.js.map +0 -1
  8. package/dist/artifacts/crypto/codec.d.ts +0 -44
  9. package/dist/artifacts/crypto/codec.d.ts.map +0 -1
  10. package/dist/artifacts/crypto/codec.js +0 -273
  11. package/dist/artifacts/crypto/codec.js.map +0 -1
  12. package/dist/artifacts/crypto/context.d.ts +0 -48
  13. package/dist/artifacts/crypto/context.d.ts.map +0 -1
  14. package/dist/artifacts/crypto/context.js +0 -37
  15. package/dist/artifacts/crypto/context.js.map +0 -1
  16. package/dist/artifacts/crypto/dek-cache.d.ts +0 -38
  17. package/dist/artifacts/crypto/dek-cache.d.ts.map +0 -1
  18. package/dist/artifacts/crypto/dek-cache.js +0 -137
  19. package/dist/artifacts/crypto/dek-cache.js.map +0 -1
  20. package/dist/artifacts/crypto/index.d.ts +0 -25
  21. package/dist/artifacts/crypto/index.d.ts.map +0 -1
  22. package/dist/artifacts/crypto/index.js +0 -29
  23. package/dist/artifacts/crypto/index.js.map +0 -1
  24. package/dist/artifacts/crypto/key-provider.d.ts +0 -44
  25. package/dist/artifacts/crypto/key-provider.d.ts.map +0 -1
  26. package/dist/artifacts/crypto/key-provider.js +0 -36
  27. package/dist/artifacts/crypto/key-provider.js.map +0 -1
  28. package/dist/artifacts/crypto/kms-provider.d.ts +0 -16
  29. package/dist/artifacts/crypto/kms-provider.d.ts.map +0 -1
  30. package/dist/artifacts/crypto/kms-provider.js +0 -90
  31. package/dist/artifacts/crypto/kms-provider.js.map +0 -1
  32. package/dist/artifacts/crypto/local-provider.d.ts +0 -17
  33. package/dist/artifacts/crypto/local-provider.d.ts.map +0 -1
  34. package/dist/artifacts/crypto/local-provider.js +0 -58
  35. package/dist/artifacts/crypto/local-provider.js.map +0 -1
  36. package/dist/artifacts/env-config.d.ts +0 -15
  37. package/dist/artifacts/env-config.d.ts.map +0 -1
  38. package/dist/artifacts/env-config.js +0 -95
  39. package/dist/artifacts/env-config.js.map +0 -1
  40. package/dist/artifacts/index.d.ts +0 -6
  41. package/dist/artifacts/index.d.ts.map +0 -1
  42. package/dist/artifacts/index.js +0 -6
  43. package/dist/artifacts/index.js.map +0 -1
  44. package/dist/artifacts/nest/index.d.ts +0 -29
  45. package/dist/artifacts/nest/index.d.ts.map +0 -1
  46. package/dist/artifacts/nest/index.js +0 -129
  47. package/dist/artifacts/nest/index.js.map +0 -1
  48. package/dist/artifacts/object-store.d.ts +0 -27
  49. package/dist/artifacts/object-store.d.ts.map +0 -1
  50. package/dist/artifacts/object-store.js +0 -198
  51. package/dist/artifacts/object-store.js.map +0 -1
  52. package/dist/artifacts/storage-driver.d.ts +0 -48
  53. package/dist/artifacts/storage-driver.d.ts.map +0 -1
  54. package/dist/artifacts/storage-driver.js +0 -89
  55. package/dist/artifacts/storage-driver.js.map +0 -1
@@ -1,129 +0,0 @@
1
- var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
- return c > 3 && r && Object.defineProperty(target, key, r), r;
6
- };
7
- var __metadata = (this && this.__metadata) || function (k, v) {
8
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
- };
10
- var __param = (this && this.__param) || function (paramIndex, decorator) {
11
- return function (target, key) { decorator(target, key, paramIndex); }
12
- };
13
- import { Inject, Module, } from '@nestjs/common';
14
- import { StorageModule } from '../../storage.module.js';
15
- import { getStorageToken } from '../../storage.tokens.js';
16
- import { createArtifactStorageWithClient, } from '../artifact-storage.js';
17
- import { createObjectStoreWithClient, } from '../object-store.js';
18
- import { ARTIFACT_STORAGE_CLIENT_NAME, OBJECT_STORAGE_CLIENT_NAME, createArtifactStorageDriver, createObjectStorageDriver, } from '../storage-driver.js';
19
- export const ARTIFACT_STORAGE = Symbol.for('@nestm/storage/artifacts/artifact-storage');
20
- export const OBJECT_STORE = Symbol.for('@nestm/storage/artifacts/object-store');
21
- export const ARTIFACT_STORAGE_MODULE_OPTIONS = Symbol.for('@nestm/storage/artifacts/module-options');
22
- export function InjectArtifactStorage() {
23
- return Inject(ARTIFACT_STORAGE);
24
- }
25
- export function InjectObjectStore() {
26
- return Inject(OBJECT_STORE);
27
- }
28
- let ArtifactStorageOptionsHostModule = class ArtifactStorageOptionsHostModule {
29
- };
30
- ArtifactStorageOptionsHostModule = __decorate([
31
- Module({})
32
- ], ArtifactStorageOptionsHostModule);
33
- function syncOptionsHost(options) {
34
- return {
35
- module: ArtifactStorageOptionsHostModule,
36
- providers: [
37
- { provide: ARTIFACT_STORAGE_MODULE_OPTIONS, useValue: options },
38
- ],
39
- exports: [ARTIFACT_STORAGE_MODULE_OPTIONS],
40
- };
41
- }
42
- function asyncOptionsHost(options) {
43
- return {
44
- module: ArtifactStorageOptionsHostModule,
45
- imports: options.imports ?? [],
46
- providers: [
47
- {
48
- provide: ARTIFACT_STORAGE_MODULE_OPTIONS,
49
- inject: options.inject ?? [],
50
- useFactory: options.useFactory,
51
- },
52
- ],
53
- exports: [ARTIFACT_STORAGE_MODULE_OPTIONS],
54
- };
55
- }
56
- function domainProviders() {
57
- return [
58
- {
59
- provide: ARTIFACT_STORAGE,
60
- inject: [
61
- ARTIFACT_STORAGE_MODULE_OPTIONS,
62
- getStorageToken(ARTIFACT_STORAGE_CLIENT_NAME),
63
- ],
64
- useFactory: (options, client) => createArtifactStorageWithClient(options.config, options.crypto, client),
65
- },
66
- {
67
- provide: OBJECT_STORE,
68
- inject: [
69
- ARTIFACT_STORAGE_MODULE_OPTIONS,
70
- getStorageToken(OBJECT_STORAGE_CLIENT_NAME),
71
- ],
72
- useFactory: (options, client) => createObjectStoreWithClient(options.config, options.crypto, client),
73
- },
74
- ArtifactStorageCryptoShutdown,
75
- ];
76
- }
77
- let ArtifactStorageCryptoShutdown = class ArtifactStorageCryptoShutdown {
78
- options;
79
- constructor(options) {
80
- this.options = options;
81
- }
82
- onApplicationShutdown() {
83
- this.options.crypto.keyProvider.clear();
84
- }
85
- };
86
- ArtifactStorageCryptoShutdown = __decorate([
87
- __param(0, Inject(ARTIFACT_STORAGE_MODULE_OPTIONS)),
88
- __metadata("design:paramtypes", [Object])
89
- ], ArtifactStorageCryptoShutdown);
90
- function composeModule(optionsHost, isGlobal) {
91
- const rawStorageModule = StorageModule.forRootAsync({
92
- imports: [optionsHost],
93
- default: ARTIFACT_STORAGE_CLIENT_NAME,
94
- isGlobal,
95
- stores: [
96
- {
97
- name: ARTIFACT_STORAGE_CLIENT_NAME,
98
- inject: [ARTIFACT_STORAGE_MODULE_OPTIONS],
99
- useFactory: (options) => createArtifactStorageDriver(options.config),
100
- },
101
- {
102
- name: OBJECT_STORAGE_CLIENT_NAME,
103
- inject: [ARTIFACT_STORAGE_MODULE_OPTIONS],
104
- useFactory: (options) => createObjectStorageDriver(options.config),
105
- },
106
- ],
107
- });
108
- return {
109
- module: ArtifactStorageModule,
110
- global: isGlobal,
111
- imports: [optionsHost, rawStorageModule],
112
- providers: domainProviders(),
113
- exports: [ARTIFACT_STORAGE, OBJECT_STORE, StorageModule],
114
- };
115
- }
116
- let ArtifactStorageModule = class ArtifactStorageModule {
117
- static forRoot(options) {
118
- const { config, crypto } = options;
119
- return composeModule(syncOptionsHost({ config, crypto }), options.isGlobal === true);
120
- }
121
- static forRootAsync(options) {
122
- return composeModule(asyncOptionsHost(options), options.isGlobal === true);
123
- }
124
- };
125
- ArtifactStorageModule = __decorate([
126
- Module({})
127
- ], ArtifactStorageModule);
128
- export { ArtifactStorageModule };
129
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/artifacts/nest/index.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Inject,\n Module,\n type DynamicModule,\n type FactoryProvider,\n type ModuleMetadata,\n type OnApplicationShutdown,\n type Provider,\n} from '@nestjs/common';\nimport type { StorageClient } from '../../storage.client.js';\nimport { StorageModule } from '../../storage.module.js';\nimport { getStorageToken } from '../../storage.tokens.js';\n\nimport {\n createArtifactStorageWithClient,\n type ArtifactStorage,\n} from '../artifact-storage.js';\nimport type { StorageCrypto } from '../crypto/index.js';\nimport {\n createObjectStoreWithClient,\n type ObjectStore,\n} from '../object-store.js';\nimport {\n ARTIFACT_STORAGE_CLIENT_NAME,\n OBJECT_STORAGE_CLIENT_NAME,\n createArtifactStorageDriver,\n createObjectStorageDriver,\n type ArtifactStorageConfig,\n} from '../storage-driver.js';\n\nexport const ARTIFACT_STORAGE = Symbol.for(\n '@nestm/storage/artifacts/artifact-storage',\n);\nexport const OBJECT_STORE = Symbol.for('@nestm/storage/artifacts/object-store');\nexport const ARTIFACT_STORAGE_MODULE_OPTIONS = Symbol.for(\n '@nestm/storage/artifacts/module-options',\n);\n\nexport interface ArtifactStorageModuleOptions {\n config: ArtifactStorageConfig;\n crypto: StorageCrypto;\n /** Makes the product adapters and @nestm/storage clients globally injectable. */\n isGlobal?: boolean;\n}\n\ninterface ResolvedArtifactStorageModuleOptions {\n config: ArtifactStorageConfig;\n crypto: StorageCrypto;\n}\n\nexport interface ArtifactStorageModuleAsyncOptions extends Pick<\n ModuleMetadata,\n 'imports'\n> {\n inject?: FactoryProvider['inject'];\n isGlobal?: boolean;\n useFactory: (\n ...dependencies: never[]\n ) =>\n | ResolvedArtifactStorageModuleOptions\n | Promise<ResolvedArtifactStorageModuleOptions>;\n}\n\nexport function InjectArtifactStorage(): ParameterDecorator &\n PropertyDecorator {\n return Inject(ARTIFACT_STORAGE);\n}\n\nexport function InjectObjectStore(): ParameterDecorator & PropertyDecorator {\n return Inject(OBJECT_STORE);\n}\n\n@Module({})\nclass ArtifactStorageOptionsHostModule {}\n\nfunction syncOptionsHost(\n options: ResolvedArtifactStorageModuleOptions,\n): DynamicModule {\n return {\n module: ArtifactStorageOptionsHostModule,\n providers: [\n { provide: ARTIFACT_STORAGE_MODULE_OPTIONS, useValue: options },\n ],\n exports: [ARTIFACT_STORAGE_MODULE_OPTIONS],\n };\n}\n\nfunction asyncOptionsHost(\n options: ArtifactStorageModuleAsyncOptions,\n): DynamicModule {\n return {\n module: ArtifactStorageOptionsHostModule,\n imports: options.imports ?? [],\n providers: [\n {\n provide: ARTIFACT_STORAGE_MODULE_OPTIONS,\n inject: options.inject ?? [],\n useFactory: options.useFactory,\n },\n ],\n exports: [ARTIFACT_STORAGE_MODULE_OPTIONS],\n };\n}\n\nfunction domainProviders(): Provider[] {\n return [\n {\n provide: ARTIFACT_STORAGE,\n inject: [\n ARTIFACT_STORAGE_MODULE_OPTIONS,\n getStorageToken(ARTIFACT_STORAGE_CLIENT_NAME),\n ],\n useFactory: (\n options: ResolvedArtifactStorageModuleOptions,\n client: StorageClient,\n ): ArtifactStorage =>\n createArtifactStorageWithClient(options.config, options.crypto, client),\n },\n {\n provide: OBJECT_STORE,\n inject: [\n ARTIFACT_STORAGE_MODULE_OPTIONS,\n getStorageToken(OBJECT_STORAGE_CLIENT_NAME),\n ],\n useFactory: (\n options: ResolvedArtifactStorageModuleOptions,\n client: StorageClient,\n ): ObjectStore =>\n createObjectStoreWithClient(options.config, options.crypto, client),\n },\n ArtifactStorageCryptoShutdown,\n ];\n}\n\nclass ArtifactStorageCryptoShutdown implements OnApplicationShutdown {\n constructor(\n @Inject(ARTIFACT_STORAGE_MODULE_OPTIONS)\n private readonly options: ResolvedArtifactStorageModuleOptions,\n ) {}\n\n onApplicationShutdown(): void {\n this.options.crypto.keyProvider.clear();\n }\n}\n\nfunction composeModule(\n optionsHost: DynamicModule,\n isGlobal: boolean,\n): DynamicModule {\n const rawStorageModule = StorageModule.forRootAsync({\n imports: [optionsHost],\n default: ARTIFACT_STORAGE_CLIENT_NAME,\n isGlobal,\n stores: [\n {\n name: ARTIFACT_STORAGE_CLIENT_NAME,\n inject: [ARTIFACT_STORAGE_MODULE_OPTIONS],\n useFactory: (options: ResolvedArtifactStorageModuleOptions) =>\n createArtifactStorageDriver(options.config),\n },\n {\n name: OBJECT_STORAGE_CLIENT_NAME,\n inject: [ARTIFACT_STORAGE_MODULE_OPTIONS],\n useFactory: (options: ResolvedArtifactStorageModuleOptions) =>\n createObjectStorageDriver(options.config),\n },\n ],\n });\n\n return {\n module: ArtifactStorageModule,\n global: isGlobal,\n imports: [optionsHost, rawStorageModule],\n providers: domainProviders(),\n exports: [ARTIFACT_STORAGE, OBJECT_STORE, StorageModule],\n };\n}\n\n@Module({})\nexport class ArtifactStorageModule {\n static forRoot(options: ArtifactStorageModuleOptions): DynamicModule {\n const { config, crypto } = options;\n return composeModule(\n syncOptionsHost({ config, crypto }),\n options.isGlobal === true,\n );\n }\n\n static forRootAsync(\n options: ArtifactStorageModuleAsyncOptions,\n ): DynamicModule {\n return composeModule(asyncOptionsHost(options), options.isGlobal === true);\n }\n}\n"]}
@@ -1,27 +0,0 @@
1
- import { type StorageClient } from '../core/index.js';
2
- import { type ReadRef, type ScopeRef } from './artifact-storage.js';
3
- import { type StorageCrypto } from './crypto/index.js';
4
- import { type ArtifactStorageConfig } from './storage-driver.js';
5
- /**
6
- * Generic key→bytes object store (not artifact-shaped). Backs org-logo uploads and staged
7
- * artifact uploads. Objects are CAE1 envelopes like artifact files; the content type lives in
8
- * the authenticated envelope header on every provider. Filesystem `.ct` sidecars remain read-only
9
- * compatibility data and are never emitted.
10
- */
11
- export interface ObjectStore {
12
- putObject(key: string, body: Buffer, contentType: string, ref: ScopeRef): Promise<void>;
13
- /** `null` if absent; throws `EnvelopeError` on decrypt failure (never masked as a miss). */
14
- getObject(key: string, ref: ReadRef): Promise<{
15
- body: Buffer;
16
- contentType?: string;
17
- } | null>;
18
- deleteObject(key: string): Promise<void>;
19
- /** Delete every object under `prefix` last modified more than `olderThanMs` ago. Returns the count deleted. */
20
- sweepObjects(prefix: string, olderThanMs: number): Promise<number>;
21
- /** Keys beginning with `prefix` (store namespace), sorted lexicographically. */
22
- listObjects(prefix: string): Promise<string[]>;
23
- }
24
- export declare function createObjectStore(cfg: ArtifactStorageConfig, crypto: StorageCrypto): Promise<ObjectStore>;
25
- /** Framework-neutral domain adapter used by both direct consumers and the Nest composition entry. */
26
- export declare function createObjectStoreWithClient(cfg: ArtifactStorageConfig, crypto: StorageCrypto, client: StorageClient): ObjectStore;
27
- //# sourceMappingURL=object-store.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"object-store.d.ts","sourceRoot":"","sources":["../../src/artifacts/object-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,aAAa,EAEnB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAEL,KAAK,OAAO,EACZ,KAAK,QAAQ,EACd,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAIL,KAAK,aAAa,EACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAIL,KAAK,qBAAqB,EAC3B,MAAM,qBAAqB,CAAC;AAE7B;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,SAAS,CACP,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,QAAQ,GACZ,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,4FAA4F;IAC5F,SAAS,CACP,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,OAAO,GACX,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IAC1D,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,+GAA+G;IAC/G,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACnE,gFAAgF;IAChF,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CAChD;AAED,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,qBAAqB,EAC1B,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,WAAW,CAAC,CAGtB;AAED,qGAAqG;AACrG,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,qBAAqB,EAC1B,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,aAAa,GACpB,WAAW,CAOb"}
@@ -1,198 +0,0 @@
1
- import { StorageErrorCode, isStorageError, } from '../core/index.js';
2
- import { warnLegacyRead, } from './artifact-storage.js';
3
- import { open, seal, } from './crypto/index.js';
4
- import { createObjectStorageClient, DEFAULT_OBJECT_NAMESPACES, resolveObjectNamespaces, } from './storage-driver.js';
5
- export async function createObjectStore(cfg, crypto) {
6
- const client = await createObjectStorageClient(cfg);
7
- return createObjectStoreWithClient(cfg, crypto, client);
8
- }
9
- /** Framework-neutral domain adapter used by both direct consumers and the Nest composition entry. */
10
- export function createObjectStoreWithClient(cfg, crypto, client) {
11
- return new ClientObjectStore(client, crypto, cfg.provider === 'fs', cfg.objectNamespaces ?? DEFAULT_OBJECT_NAMESPACES);
12
- }
13
- /**
14
- * Envelope context for a store object. `path` is the store-relative KEY on every provider —
15
- * the fs `_objects/` nesting is a driver-level prefix and must never reach the AAD, or two
16
- * providers would bind ciphertext to different addresses for the same logical object.
17
- */
18
- function objectCtx(key, ref) {
19
- return {
20
- scope: ref.scope,
21
- artifactId: null,
22
- version: ref.version ?? null,
23
- path: key,
24
- };
25
- }
26
- function aliasesLegacyFilesystemSidecar(segment) {
27
- let end = segment.length;
28
- while (end > 0 &&
29
- (segment.charCodeAt(end - 1) === 0x2e ||
30
- segment.charCodeAt(end - 1) === 0x20)) {
31
- end--;
32
- }
33
- return segment.slice(0, end).toLowerCase().endsWith('.ct');
34
- }
35
- function assertObjectKey(key, namespaces, filesystemLayout, allowEmpty = false) {
36
- const firstSegment = key.split(/[/\\]/, 1)[0] ?? '';
37
- const segments = key.split(/[/\\]/);
38
- if (allowEmpty && segments.at(-1) === '')
39
- segments.pop();
40
- if ((!allowEmpty && key === '') ||
41
- key.startsWith('/') ||
42
- key.includes('\0') ||
43
- (filesystemLayout && segments.some(aliasesLegacyFilesystemSidecar)) ||
44
- segments.some((segment) => segment === '' || segment === '.' || segment === '..') ||
45
- (key !== '' && !namespaces.has(firstSegment.toLowerCase()))) {
46
- throw new Error(`invalid object key: ${key}`);
47
- }
48
- }
49
- async function collectRawObject(object) {
50
- const chunks = [];
51
- const reader = object.body.getReader();
52
- try {
53
- while (true) {
54
- const { done, value } = await reader.read();
55
- if (done)
56
- break;
57
- chunks.push(Buffer.from(value));
58
- }
59
- }
60
- finally {
61
- reader.releaseLock();
62
- }
63
- return Buffer.concat(chunks);
64
- }
65
- function isObjectKeyAllowed(key, namespaces, filesystemLayout) {
66
- try {
67
- assertObjectKey(key, namespaces, filesystemLayout);
68
- return true;
69
- }
70
- catch {
71
- return false;
72
- }
73
- }
74
- class ClientObjectStore {
75
- client;
76
- crypto;
77
- filesystemLayout;
78
- namespaces;
79
- constructor(client, crypto, filesystemLayout, namespaces) {
80
- this.client = client;
81
- this.crypto = crypto;
82
- this.filesystemLayout = filesystemLayout;
83
- this.namespaces = resolveObjectNamespaces(namespaces);
84
- }
85
- async putObject(key, body, contentType, ref) {
86
- assertObjectKey(key, this.namespaces, this.filesystemLayout);
87
- const sealed = await seal(body, objectCtx(key, ref), this.crypto.keyProvider, contentType);
88
- await this.client.upload(key, sealed, {
89
- // Real content type rides the authenticated envelope; raw storage remains opaque.
90
- contentType: 'application/octet-stream',
91
- });
92
- if (this.filesystemLayout)
93
- await this.client.delete(`${key}.ct`);
94
- }
95
- async getObject(key, ref) {
96
- assertObjectKey(key, this.namespaces, this.filesystemLayout);
97
- const stored = await this.downloadRaw(this.client, key);
98
- if (stored === null)
99
- return null;
100
- const out = await open(stored.raw, objectCtx(key, ref), this.crypto.keyProvider, this.crypto.allowLegacyPlaintext === undefined
101
- ? {}
102
- : { allowLegacyPlaintext: this.crypto.allowLegacyPlaintext });
103
- let contentType = out.contentType;
104
- if (out.legacy) {
105
- warnLegacyRead(this.filesystemLayout ? 'fs-object' : 'object', key);
106
- if (this.filesystemLayout) {
107
- contentType = await this.readLegacyFilesystemContentType(key);
108
- }
109
- else {
110
- contentType = stored.contentType;
111
- }
112
- }
113
- return {
114
- body: out.plain,
115
- ...(contentType === undefined ? {} : { contentType }),
116
- };
117
- }
118
- async deleteObject(key) {
119
- assertObjectKey(key, this.namespaces, this.filesystemLayout);
120
- await this.client.delete(key);
121
- if (this.filesystemLayout)
122
- await this.client.delete(`${key}.ct`);
123
- }
124
- async sweepObjects(prefix, olderThanMs) {
125
- assertObjectKey(prefix, this.namespaces, this.filesystemLayout, true);
126
- if (!Number.isFinite(olderThanMs) || olderThanMs < 0) {
127
- throw new RangeError('olderThanMs must be a finite non-negative number');
128
- }
129
- const cutoff = Date.now() - olderThanMs;
130
- let deleted = 0;
131
- for await (const object of this.client.listAll({ prefix })) {
132
- if (!isObjectKeyAllowed(object.key, this.namespaces, this.filesystemLayout)) {
133
- continue;
134
- }
135
- if (this.filesystemLayout && object.key.toLowerCase().endsWith('.ct'))
136
- continue;
137
- if (!isExpired(object.lastModified, cutoff))
138
- continue;
139
- await this.client.delete(object.key);
140
- if (this.filesystemLayout)
141
- await this.client.delete(`${object.key}.ct`);
142
- deleted++;
143
- }
144
- return deleted;
145
- }
146
- async listObjects(prefix) {
147
- assertObjectKey(prefix, this.namespaces, this.filesystemLayout, true);
148
- const keys = new Set();
149
- for await (const object of this.client.listAll({ prefix })) {
150
- if (!isObjectKeyAllowed(object.key, this.namespaces, this.filesystemLayout)) {
151
- continue;
152
- }
153
- if (this.filesystemLayout && object.key.toLowerCase().endsWith('.ct'))
154
- continue;
155
- keys.add(object.key);
156
- }
157
- return [...keys].sort();
158
- }
159
- async downloadRaw(client, key) {
160
- try {
161
- const object = await client.downloadStream(key);
162
- const raw = await collectRawObject(object);
163
- return {
164
- raw,
165
- ...(object.contentType === undefined
166
- ? {}
167
- : { contentType: object.contentType }),
168
- };
169
- }
170
- catch (error) {
171
- if (isStorageError(error) && error.code === StorageErrorCode.NOT_FOUND) {
172
- return null;
173
- }
174
- throw error;
175
- }
176
- }
177
- async readLegacyFilesystemContentType(key) {
178
- try {
179
- return await this.client.downloadText(`${key}.ct`, {
180
- maxBytes: 64 * 1024,
181
- });
182
- }
183
- catch (error) {
184
- if (isStorageError(error) && error.code === StorageErrorCode.NOT_FOUND) {
185
- return undefined;
186
- }
187
- throw error;
188
- }
189
- }
190
- }
191
- function isExpired(lastModified, cutoff) {
192
- const modifiedAt = lastModified?.getTime();
193
- // A provider that cannot supply a trustworthy timestamp cannot prove an object is expired.
194
- return (modifiedAt !== undefined &&
195
- Number.isFinite(modifiedAt) &&
196
- modifiedAt < cutoff);
197
- }
198
- //# sourceMappingURL=object-store.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"object-store.js","sourceRoot":"","sources":["../../src/artifacts/object-store.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n StorageErrorCode,\n isStorageError,\n type StorageClient,\n type StorageObject,\n} from '../core/index.js';\n\nimport {\n warnLegacyRead,\n type ReadRef,\n type ScopeRef,\n} from './artifact-storage.js';\nimport {\n open,\n seal,\n type EnvelopeContext,\n type StorageCrypto,\n} from './crypto/index.js';\nimport {\n createObjectStorageClient,\n DEFAULT_OBJECT_NAMESPACES,\n resolveObjectNamespaces,\n type ArtifactStorageConfig,\n} from './storage-driver.js';\n\n/**\n * Generic key→bytes object store (not artifact-shaped). Backs org-logo uploads and staged\n * artifact uploads. Objects are CAE1 envelopes like artifact files; the content type lives in\n * the authenticated envelope header on every provider. Filesystem `.ct` sidecars remain read-only\n * compatibility data and are never emitted.\n */\nexport interface ObjectStore {\n putObject(\n key: string,\n body: Buffer,\n contentType: string,\n ref: ScopeRef,\n ): Promise<void>;\n /** `null` if absent; throws `EnvelopeError` on decrypt failure (never masked as a miss). */\n getObject(\n key: string,\n ref: ReadRef,\n ): Promise<{ body: Buffer; contentType?: string } | null>;\n deleteObject(key: string): Promise<void>;\n /** Delete every object under `prefix` last modified more than `olderThanMs` ago. Returns the count deleted. */\n sweepObjects(prefix: string, olderThanMs: number): Promise<number>;\n /** Keys beginning with `prefix` (store namespace), sorted lexicographically. */\n listObjects(prefix: string): Promise<string[]>;\n}\n\nexport async function createObjectStore(\n cfg: ArtifactStorageConfig,\n crypto: StorageCrypto,\n): Promise<ObjectStore> {\n const client = await createObjectStorageClient(cfg);\n return createObjectStoreWithClient(cfg, crypto, client);\n}\n\n/** Framework-neutral domain adapter used by both direct consumers and the Nest composition entry. */\nexport function createObjectStoreWithClient(\n cfg: ArtifactStorageConfig,\n crypto: StorageCrypto,\n client: StorageClient,\n): ObjectStore {\n return new ClientObjectStore(\n client,\n crypto,\n cfg.provider === 'fs',\n cfg.objectNamespaces ?? DEFAULT_OBJECT_NAMESPACES,\n );\n}\n\n/**\n * Envelope context for a store object. `path` is the store-relative KEY on every provider —\n * the fs `_objects/` nesting is a driver-level prefix and must never reach the AAD, or two\n * providers would bind ciphertext to different addresses for the same logical object.\n */\nfunction objectCtx(key: string, ref: ScopeRef | ReadRef): EnvelopeContext {\n return {\n scope: ref.scope as string,\n artifactId: null,\n version: ref.version ?? null,\n path: key,\n };\n}\n\nfunction aliasesLegacyFilesystemSidecar(segment: string): boolean {\n let end = segment.length;\n while (\n end > 0 &&\n (segment.charCodeAt(end - 1) === 0x2e ||\n segment.charCodeAt(end - 1) === 0x20)\n ) {\n end--;\n }\n return segment.slice(0, end).toLowerCase().endsWith('.ct');\n}\n\nfunction assertObjectKey(\n key: string,\n namespaces: ReadonlySet<string>,\n filesystemLayout: boolean,\n allowEmpty = false,\n): void {\n const firstSegment = key.split(/[/\\\\]/, 1)[0] ?? '';\n const segments = key.split(/[/\\\\]/);\n if (allowEmpty && segments.at(-1) === '') segments.pop();\n if (\n (!allowEmpty && key === '') ||\n key.startsWith('/') ||\n key.includes('\\0') ||\n (filesystemLayout && segments.some(aliasesLegacyFilesystemSidecar)) ||\n segments.some(\n (segment) => segment === '' || segment === '.' || segment === '..',\n ) ||\n (key !== '' && !namespaces.has(firstSegment.toLowerCase()))\n ) {\n throw new Error(`invalid object key: ${key}`);\n }\n}\n\nasync function collectRawObject(object: StorageObject): Promise<Buffer> {\n const chunks: Buffer[] = [];\n const reader = object.body.getReader();\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n chunks.push(Buffer.from(value));\n }\n } finally {\n reader.releaseLock();\n }\n return Buffer.concat(chunks);\n}\n\ninterface RawObject {\n raw: Buffer;\n contentType?: string;\n}\n\nfunction isObjectKeyAllowed(\n key: string,\n namespaces: ReadonlySet<string>,\n filesystemLayout: boolean,\n): boolean {\n try {\n assertObjectKey(key, namespaces, filesystemLayout);\n return true;\n } catch {\n return false;\n }\n}\n\nclass ClientObjectStore implements ObjectStore {\n private readonly namespaces: ReadonlySet<string>;\n\n constructor(\n private readonly client: StorageClient,\n private readonly crypto: StorageCrypto,\n private readonly filesystemLayout: boolean,\n namespaces: readonly string[],\n ) {\n this.namespaces = resolveObjectNamespaces(namespaces);\n }\n\n async putObject(\n key: string,\n body: Buffer,\n contentType: string,\n ref: ScopeRef,\n ): Promise<void> {\n assertObjectKey(key, this.namespaces, this.filesystemLayout);\n const sealed = await seal(\n body,\n objectCtx(key, ref),\n this.crypto.keyProvider,\n contentType,\n );\n await this.client.upload(key, sealed, {\n // Real content type rides the authenticated envelope; raw storage remains opaque.\n contentType: 'application/octet-stream',\n });\n if (this.filesystemLayout) await this.client.delete(`${key}.ct`);\n }\n\n async getObject(\n key: string,\n ref: ReadRef,\n ): Promise<{ body: Buffer; contentType?: string } | null> {\n assertObjectKey(key, this.namespaces, this.filesystemLayout);\n const stored = await this.downloadRaw(this.client, key);\n if (stored === null) return null;\n const out = await open(\n stored.raw,\n objectCtx(key, ref),\n this.crypto.keyProvider,\n this.crypto.allowLegacyPlaintext === undefined\n ? {}\n : { allowLegacyPlaintext: this.crypto.allowLegacyPlaintext },\n );\n let contentType = out.contentType;\n if (out.legacy) {\n warnLegacyRead(this.filesystemLayout ? 'fs-object' : 'object', key);\n if (this.filesystemLayout) {\n contentType = await this.readLegacyFilesystemContentType(key);\n } else {\n contentType = stored.contentType;\n }\n }\n return {\n body: out.plain,\n ...(contentType === undefined ? {} : { contentType }),\n };\n }\n\n async deleteObject(key: string): Promise<void> {\n assertObjectKey(key, this.namespaces, this.filesystemLayout);\n await this.client.delete(key);\n if (this.filesystemLayout) await this.client.delete(`${key}.ct`);\n }\n\n async sweepObjects(prefix: string, olderThanMs: number): Promise<number> {\n assertObjectKey(prefix, this.namespaces, this.filesystemLayout, true);\n if (!Number.isFinite(olderThanMs) || olderThanMs < 0) {\n throw new RangeError('olderThanMs must be a finite non-negative number');\n }\n const cutoff = Date.now() - olderThanMs;\n let deleted = 0;\n for await (const object of this.client.listAll({ prefix })) {\n if (\n !isObjectKeyAllowed(object.key, this.namespaces, this.filesystemLayout)\n ) {\n continue;\n }\n if (this.filesystemLayout && object.key.toLowerCase().endsWith('.ct'))\n continue;\n if (!isExpired(object.lastModified, cutoff)) continue;\n await this.client.delete(object.key);\n if (this.filesystemLayout) await this.client.delete(`${object.key}.ct`);\n deleted++;\n }\n return deleted;\n }\n\n async listObjects(prefix: string): Promise<string[]> {\n assertObjectKey(prefix, this.namespaces, this.filesystemLayout, true);\n const keys = new Set<string>();\n for await (const object of this.client.listAll({ prefix })) {\n if (\n !isObjectKeyAllowed(object.key, this.namespaces, this.filesystemLayout)\n ) {\n continue;\n }\n if (this.filesystemLayout && object.key.toLowerCase().endsWith('.ct'))\n continue;\n keys.add(object.key);\n }\n return [...keys].sort();\n }\n\n private async downloadRaw(\n client: StorageClient,\n key: string,\n ): Promise<RawObject | null> {\n try {\n const object = await client.downloadStream(key);\n const raw = await collectRawObject(object);\n return {\n raw,\n ...(object.contentType === undefined\n ? {}\n : { contentType: object.contentType }),\n };\n } catch (error) {\n if (isStorageError(error) && error.code === StorageErrorCode.NOT_FOUND) {\n return null;\n }\n throw error;\n }\n }\n\n private async readLegacyFilesystemContentType(\n key: string,\n ): Promise<string | undefined> {\n try {\n return await this.client.downloadText(`${key}.ct`, {\n maxBytes: 64 * 1024,\n });\n } catch (error) {\n if (isStorageError(error) && error.code === StorageErrorCode.NOT_FOUND) {\n return undefined;\n }\n throw error;\n }\n }\n}\n\nfunction isExpired(lastModified: Date | undefined, cutoff: number): boolean {\n const modifiedAt = lastModified?.getTime();\n // A provider that cannot supply a trustworthy timestamp cannot prove an object is expired.\n return (\n modifiedAt !== undefined &&\n Number.isFinite(modifiedAt) &&\n modifiedAt < cutoff\n );\n}\n"]}
@@ -1,48 +0,0 @@
1
- import { StorageClient, type StorageDriver } from '../core/index.js';
2
- import { type StorageProviderConfig, type StorageProviderName } from '../files-sdk/provider/index.js';
3
- export declare const ARTIFACT_STORAGE_CLIENT_NAME = "artifacts";
4
- export declare const OBJECT_STORAGE_CLIENT_NAME = "objects";
5
- export declare const DEFAULT_OBJECT_NAMESPACES: readonly ['_staging', 'org-logos'];
6
- /**
7
- * Where the generic object store lives relative to the artifact store on a filesystem. Artifact
8
- * keys are `<artifactId>/<path>`, so an unprefixed object key like `org-logos/x.png` would read
9
- * back as an artifact directory named `org-logos` — listing, sweeping, and removal would all
10
- * cross the two. Filesystems therefore retain the historical `_objects` directory. Object-store
11
- * providers retain their historical unprefixed keys; the configured top-level namespace
12
- * allowlist keeps those keys disjoint from artifact ids without a rolling-deploy layout change.
13
- */
14
- export declare const OBJECT_STORE_PREFIX = "_objects";
15
- /**
16
- * Which store backs artifacts, and how to reach it. `provider` is any slug `@nestm/storage`
17
- * can build — `fs`, `s3`, `gcs`, `azure`, `r2`, `minio`, `supabase`, and ~40 more — so a
18
- * deployment picks its store from the environment instead of the platform shipping a driver
19
- * per backend. Only the selected provider's SDK is ever imported.
20
- */
21
- export interface ArtifactStorageConfig {
22
- provider: StorageProviderName;
23
- /** Key prefix so artifacts can share a bucket (or a directory tree) with other apps. */
24
- prefix?: string;
25
- /**
26
- * Flat provider settings: `bucket`/`region`/`endpoint` for an object store, `root` for the
27
- * filesystem, `accountName`/`container` for Azure. Each provider reads what it needs.
28
- * Credentials may be omitted wherever the provider's SDK resolves its own chain — on AWS that
29
- * is the ECS task role, so S3 needs no long-lived keys in env.
30
- */
31
- config?: StorageProviderConfig;
32
- /**
33
- * Allowed top-level ObjectStore namespaces. Artifact ids matching one of these values are
34
- * rejected, keeping the two public facades disjoint on providers where they share a key root.
35
- */
36
- objectNamespaces?: readonly string[];
37
- }
38
- /** Default filesystem root, shared by the api and the sandbox so zero-config dev lines up. */
39
- export declare function defaultFsRoot(): string;
40
- /** Resolve and validate the case-insensitive top-level ObjectStore namespace allowlist. */
41
- export declare function resolveObjectNamespaces(namespaces?: readonly string[]): ReadonlySet<string>;
42
- /** Fail fast on an unknown slug with the full list, rather than at the first upload. */
43
- export declare function assertStorageProvider(provider: string): StorageProviderName;
44
- export declare function createArtifactStorageDriver(cfg: ArtifactStorageConfig): Promise<StorageDriver>;
45
- export declare function createObjectStorageDriver(cfg: ArtifactStorageConfig): Promise<StorageDriver>;
46
- export declare function createArtifactStorageClient(cfg: ArtifactStorageConfig): Promise<StorageClient>;
47
- export declare function createObjectStorageClient(cfg: ArtifactStorageConfig): Promise<StorageClient>;
48
- //# sourceMappingURL=storage-driver.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"storage-driver.d.ts","sourceRoot":"","sources":["../../src/artifacts/storage-driver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAIL,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACzB,MAAM,gCAAgC,CAAC;AAIxC,eAAO,MAAM,4BAA4B,cAAc,CAAC;AACxD,eAAO,MAAM,0BAA0B,YAAY,CAAC;AACpD,eAAO,MAAM,yBAAyB,YAAI,UAAU,EAAE,WAAW,CAAU,CAAC;AAE5E;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,aAAa,CAAC;AAE9C;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,wFAAwF;IACxF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,qBAAqB,CAAC;IAC/B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAED,8FAA8F;AAC9F,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAMD,2FAA2F;AAC3F,wBAAgB,uBAAuB,CACrC,UAAU,GAAE,SAAS,MAAM,EAA8B,GACxD,WAAW,CAAC,MAAM,CAAC,CAyBrB;AAUD,wFAAwF;AACxF,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,mBAAmB,CAU3E;AAmBD,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,qBAAqB,GACzB,OAAO,CAAC,aAAa,CAAC,CAExB;AAED,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,qBAAqB,GACzB,OAAO,CAAC,aAAa,CAAC,CAKxB;AAED,wBAAsB,2BAA2B,CAC/C,GAAG,EAAE,qBAAqB,GACzB,OAAO,CAAC,aAAa,CAAC,CAKxB;AAED,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,qBAAqB,GACzB,OAAO,CAAC,aAAa,CAAC,CAKxB"}
@@ -1,89 +0,0 @@
1
- import { StorageClient } from '../core/index.js';
2
- import { createProviderStorageDriver, isStorageProvider, listStorageProviders, } from '../files-sdk/provider/index.js';
3
- import { tmpdir } from 'node:os';
4
- import { join } from 'node:path';
5
- export const ARTIFACT_STORAGE_CLIENT_NAME = 'artifacts';
6
- export const OBJECT_STORAGE_CLIENT_NAME = 'objects';
7
- export const DEFAULT_OBJECT_NAMESPACES = ['_staging', 'org-logos'];
8
- /**
9
- * Where the generic object store lives relative to the artifact store on a filesystem. Artifact
10
- * keys are `<artifactId>/<path>`, so an unprefixed object key like `org-logos/x.png` would read
11
- * back as an artifact directory named `org-logos` — listing, sweeping, and removal would all
12
- * cross the two. Filesystems therefore retain the historical `_objects` directory. Object-store
13
- * providers retain their historical unprefixed keys; the configured top-level namespace
14
- * allowlist keeps those keys disjoint from artifact ids without a rolling-deploy layout change.
15
- */
16
- export const OBJECT_STORE_PREFIX = '_objects';
17
- /** Default filesystem root, shared by the api and the sandbox so zero-config dev lines up. */
18
- export function defaultFsRoot() {
19
- return join(tmpdir(), 'concepta-artifacts-artifacts');
20
- }
21
- function trimSlashes(value) {
22
- return value.replace(/^\/+|\/+$/g, '');
23
- }
24
- /** Resolve and validate the case-insensitive top-level ObjectStore namespace allowlist. */
25
- export function resolveObjectNamespaces(namespaces = DEFAULT_OBJECT_NAMESPACES) {
26
- const resolved = new Set();
27
- for (const namespace of namespaces) {
28
- const normalized = namespace.toLowerCase();
29
- if (namespace === '' ||
30
- namespace === '.' ||
31
- namespace === '..' ||
32
- namespace.includes('/') ||
33
- namespace.includes('\\') ||
34
- namespace.includes('\0') ||
35
- Buffer.byteLength(namespace, 'utf8') > 255 ||
36
- normalized === OBJECT_STORE_PREFIX ||
37
- resolved.has(normalized)) {
38
- throw new Error(`invalid or duplicate object namespace: ${JSON.stringify(namespace)}`);
39
- }
40
- resolved.add(normalized);
41
- }
42
- if (resolved.size === 0) {
43
- throw new Error('at least one object namespace is required');
44
- }
45
- return resolved;
46
- }
47
- function joinPrefix(...parts) {
48
- const joined = parts
49
- .flatMap((part) => (part === undefined ? [] : [trimSlashes(part)]))
50
- .filter((part) => part.length > 0)
51
- .join('/');
52
- return joined.length === 0 ? undefined : joined;
53
- }
54
- /** Fail fast on an unknown slug with the full list, rather than at the first upload. */
55
- export function assertStorageProvider(provider) {
56
- if (!isStorageProvider(provider)) {
57
- const known = listStorageProviders()
58
- .map((candidate) => candidate.slug)
59
- .join(', ');
60
- throw new Error(`Unknown storage provider ${JSON.stringify(provider)}. Known providers: ${known}`);
61
- }
62
- return provider;
63
- }
64
- function providerConfig(cfg) {
65
- if (cfg.provider !== 'fs')
66
- return cfg.config ?? {};
67
- // The filesystem adapter needs a root; every other provider names its store some other way.
68
- return { root: defaultFsRoot(), ...cfg.config };
69
- }
70
- function driver(cfg, prefix) {
71
- return createProviderStorageDriver({
72
- provider: cfg.provider,
73
- config: providerConfig(cfg),
74
- ...(prefix === undefined ? {} : { prefix }),
75
- });
76
- }
77
- export function createArtifactStorageDriver(cfg) {
78
- return driver(cfg, joinPrefix(cfg.prefix));
79
- }
80
- export function createObjectStorageDriver(cfg) {
81
- return driver(cfg, joinPrefix(cfg.prefix, cfg.provider === 'fs' ? OBJECT_STORE_PREFIX : ''));
82
- }
83
- export async function createArtifactStorageClient(cfg) {
84
- return new StorageClient(ARTIFACT_STORAGE_CLIENT_NAME, await createArtifactStorageDriver(cfg));
85
- }
86
- export async function createObjectStorageClient(cfg) {
87
- return new StorageClient(OBJECT_STORAGE_CLIENT_NAME, await createObjectStorageDriver(cfg));
88
- }
89
- //# sourceMappingURL=storage-driver.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"storage-driver.js","sourceRoot":"","sources":["../../src/artifacts/storage-driver.ts"],"names":[],"mappings":"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","sourcesContent":["import { StorageClient, type StorageDriver } from '../core/index.js';\nimport {\n createProviderStorageDriver,\n isStorageProvider,\n listStorageProviders,\n type StorageProviderConfig,\n type StorageProviderName,\n} from '../files-sdk/provider/index.js';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\n\nexport const ARTIFACT_STORAGE_CLIENT_NAME = 'artifacts';\nexport const OBJECT_STORAGE_CLIENT_NAME = 'objects';\nexport const DEFAULT_OBJECT_NAMESPACES = ['_staging', 'org-logos'] as const;\n\n/**\n * Where the generic object store lives relative to the artifact store on a filesystem. Artifact\n * keys are `<artifactId>/<path>`, so an unprefixed object key like `org-logos/x.png` would read\n * back as an artifact directory named `org-logos` — listing, sweeping, and removal would all\n * cross the two. Filesystems therefore retain the historical `_objects` directory. Object-store\n * providers retain their historical unprefixed keys; the configured top-level namespace\n * allowlist keeps those keys disjoint from artifact ids without a rolling-deploy layout change.\n */\nexport const OBJECT_STORE_PREFIX = '_objects';\n\n/**\n * Which store backs artifacts, and how to reach it. `provider` is any slug `@nestm/storage`\n * can build — `fs`, `s3`, `gcs`, `azure`, `r2`, `minio`, `supabase`, and ~40 more — so a\n * deployment picks its store from the environment instead of the platform shipping a driver\n * per backend. Only the selected provider's SDK is ever imported.\n */\nexport interface ArtifactStorageConfig {\n provider: StorageProviderName;\n /** Key prefix so artifacts can share a bucket (or a directory tree) with other apps. */\n prefix?: string;\n /**\n * Flat provider settings: `bucket`/`region`/`endpoint` for an object store, `root` for the\n * filesystem, `accountName`/`container` for Azure. Each provider reads what it needs.\n * Credentials may be omitted wherever the provider's SDK resolves its own chain — on AWS that\n * is the ECS task role, so S3 needs no long-lived keys in env.\n */\n config?: StorageProviderConfig;\n /**\n * Allowed top-level ObjectStore namespaces. Artifact ids matching one of these values are\n * rejected, keeping the two public facades disjoint on providers where they share a key root.\n */\n objectNamespaces?: readonly string[];\n}\n\n/** Default filesystem root, shared by the api and the sandbox so zero-config dev lines up. */\nexport function defaultFsRoot(): string {\n return join(tmpdir(), 'concepta-artifacts-artifacts');\n}\n\nfunction trimSlashes(value: string): string {\n return value.replace(/^\\/+|\\/+$/g, '');\n}\n\n/** Resolve and validate the case-insensitive top-level ObjectStore namespace allowlist. */\nexport function resolveObjectNamespaces(\n namespaces: readonly string[] = DEFAULT_OBJECT_NAMESPACES,\n): ReadonlySet<string> {\n const resolved = new Set<string>();\n for (const namespace of namespaces) {\n const normalized = namespace.toLowerCase();\n if (\n namespace === '' ||\n namespace === '.' ||\n namespace === '..' ||\n namespace.includes('/') ||\n namespace.includes('\\\\') ||\n namespace.includes('\\0') ||\n Buffer.byteLength(namespace, 'utf8') > 255 ||\n normalized === OBJECT_STORE_PREFIX ||\n resolved.has(normalized)\n ) {\n throw new Error(\n `invalid or duplicate object namespace: ${JSON.stringify(namespace)}`,\n );\n }\n resolved.add(normalized);\n }\n if (resolved.size === 0) {\n throw new Error('at least one object namespace is required');\n }\n return resolved;\n}\n\nfunction joinPrefix(...parts: Array<string | undefined>): string | undefined {\n const joined = parts\n .flatMap((part) => (part === undefined ? [] : [trimSlashes(part)]))\n .filter((part) => part.length > 0)\n .join('/');\n return joined.length === 0 ? undefined : joined;\n}\n\n/** Fail fast on an unknown slug with the full list, rather than at the first upload. */\nexport function assertStorageProvider(provider: string): StorageProviderName {\n if (!isStorageProvider(provider)) {\n const known = listStorageProviders()\n .map((candidate) => candidate.slug)\n .join(', ');\n throw new Error(\n `Unknown storage provider ${JSON.stringify(provider)}. Known providers: ${known}`,\n );\n }\n return provider;\n}\n\nfunction providerConfig(cfg: ArtifactStorageConfig): StorageProviderConfig {\n if (cfg.provider !== 'fs') return cfg.config ?? {};\n // The filesystem adapter needs a root; every other provider names its store some other way.\n return { root: defaultFsRoot(), ...cfg.config };\n}\n\nfunction driver(\n cfg: ArtifactStorageConfig,\n prefix: string | undefined,\n): Promise<StorageDriver> {\n return createProviderStorageDriver({\n provider: cfg.provider,\n config: providerConfig(cfg),\n ...(prefix === undefined ? {} : { prefix }),\n });\n}\n\nexport function createArtifactStorageDriver(\n cfg: ArtifactStorageConfig,\n): Promise<StorageDriver> {\n return driver(cfg, joinPrefix(cfg.prefix));\n}\n\nexport function createObjectStorageDriver(\n cfg: ArtifactStorageConfig,\n): Promise<StorageDriver> {\n return driver(\n cfg,\n joinPrefix(cfg.prefix, cfg.provider === 'fs' ? OBJECT_STORE_PREFIX : ''),\n );\n}\n\nexport async function createArtifactStorageClient(\n cfg: ArtifactStorageConfig,\n): Promise<StorageClient> {\n return new StorageClient(\n ARTIFACT_STORAGE_CLIENT_NAME,\n await createArtifactStorageDriver(cfg),\n );\n}\n\nexport async function createObjectStorageClient(\n cfg: ArtifactStorageConfig,\n): Promise<StorageClient> {\n return new StorageClient(\n OBJECT_STORAGE_CLIENT_NAME,\n await createObjectStorageDriver(cfg),\n );\n}\n"]}