create-forgeon 0.3.19 → 0.3.21

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 (38) hide show
  1. package/package.json +1 -1
  2. package/src/cli/add-help.mjs +3 -2
  3. package/src/core/docs.test.mjs +1 -0
  4. package/src/core/scaffold.test.mjs +1 -0
  5. package/src/modules/accounts.mjs +9 -18
  6. package/src/modules/dependencies.mjs +153 -4
  7. package/src/modules/dependencies.test.mjs +58 -0
  8. package/src/modules/executor.test.mjs +544 -515
  9. package/src/modules/files-access.mjs +375 -375
  10. package/src/modules/files-image.mjs +512 -510
  11. package/src/modules/files-quotas.mjs +365 -365
  12. package/src/modules/files.mjs +5 -6
  13. package/src/modules/idempotency.test.mjs +3 -2
  14. package/src/modules/registry.mjs +20 -0
  15. package/src/modules/shared/files-runtime-wiring.mjs +13 -10
  16. package/src/run-add-module.mjs +39 -26
  17. package/src/run-add-module.test.mjs +228 -152
  18. package/src/run-scan-integrations.mjs +1 -0
  19. package/templates/base/package.json +1 -0
  20. package/templates/module-presets/accounts/packages/accounts-api/package.json +1 -0
  21. package/templates/module-presets/accounts/packages/accounts-api/src/auth-core.service.ts +15 -19
  22. package/templates/module-presets/accounts/{apps/api/src/accounts/prisma-accounts-persistence.store.ts → packages/accounts-api/src/auth.store.ts} +44 -166
  23. package/templates/module-presets/accounts/packages/accounts-api/src/forgeon-accounts.module.ts +7 -1
  24. package/templates/module-presets/accounts/packages/accounts-api/src/index.ts +3 -4
  25. package/templates/module-presets/accounts/packages/accounts-api/src/users.service.ts +10 -11
  26. package/templates/module-presets/accounts/packages/accounts-api/src/users.store.ts +113 -0
  27. package/templates/module-presets/accounts/packages/accounts-api/src/users.types.ts +48 -0
  28. package/templates/module-presets/files/packages/files/package.json +1 -0
  29. package/templates/module-presets/files/packages/files/src/files.ports.ts +0 -95
  30. package/templates/module-presets/files/packages/files/src/files.service.ts +43 -36
  31. package/templates/module-presets/files/{apps/api/src/files/prisma-files-persistence.store.ts → packages/files/src/files.store.ts} +77 -13
  32. package/templates/module-presets/files/packages/files/src/forgeon-files.module.ts +7 -116
  33. package/templates/module-presets/files/packages/files/src/index.ts +1 -0
  34. package/templates/module-presets/files-quotas/packages/files-quotas/package.json +20 -20
  35. package/templates/module-presets/files-quotas/packages/files-quotas/src/files-quotas.service.ts +118 -118
  36. package/templates/module-presets/files-quotas/packages/files-quotas/src/forgeon-files-quotas.module.ts +18 -18
  37. package/templates/module-presets/accounts/packages/accounts-api/src/accounts-persistence.port.ts +0 -67
  38. package/templates/module-presets/files/apps/api/src/files/forgeon-files-db-prisma.module.ts +0 -17
@@ -1,102 +1,7 @@
1
1
  import { Readable } from 'node:stream';
2
- import type { FileVariantKey } from './files.types';
3
2
 
4
- export const FILES_PERSISTENCE_PORT = 'FORGEON_FILES_PERSISTENCE_PORT';
5
3
  export const FILES_STORAGE_ADAPTER = 'FORGEON_FILES_STORAGE_ADAPTER';
6
4
 
7
- export type FilesBlobRecord = {
8
- id: string;
9
- hash: string;
10
- size: number;
11
- mimeType: string;
12
- storageDriver: string;
13
- storageKey: string;
14
- };
15
-
16
- export type FilesBlobRef = FilesBlobRecord & {
17
- created: boolean;
18
- };
19
-
20
- export type FilesRecordVariant = {
21
- variantKey: string;
22
- blobId: string;
23
- mimeType: string;
24
- size: number;
25
- status: string;
26
- blob?: {
27
- storageDriver: string;
28
- storageKey: string;
29
- };
30
- };
31
-
32
- export type FilesRecordAggregate = {
33
- id: string;
34
- publicId: string;
35
- storageKey: string;
36
- originalName: string;
37
- mimeType: string;
38
- size: number;
39
- storageDriver: string;
40
- ownerType: string;
41
- ownerId: string | null;
42
- visibility: string;
43
- createdById: string | null;
44
- createdAt: Date;
45
- updatedAt: Date;
46
- variants?: FilesRecordVariant[];
47
- };
48
-
49
- export type FilesRecordCreateInput = {
50
- publicId: string;
51
- storageKey: string;
52
- originalName: string;
53
- mimeType: string;
54
- size: number;
55
- storageDriver: string;
56
- ownerType: string;
57
- ownerId: string | null;
58
- visibility: string;
59
- createdById: string | null;
60
- };
61
-
62
- export type FilesBlobCreateInput = {
63
- hash: string;
64
- size: number;
65
- mimeType: string;
66
- storageDriver: string;
67
- storageKey: string;
68
- };
69
-
70
- export type FilesVariantCreateInput = {
71
- fileId: string;
72
- variantKey: FileVariantKey;
73
- blobId: string;
74
- mimeType: string;
75
- size: number;
76
- status: string;
77
- };
78
-
79
- export interface FilesPersistencePort {
80
- createFileRecord(data: FilesRecordCreateInput): Promise<{
81
- id: string;
82
- publicId: string;
83
- }>;
84
- deleteFileRecordById(id: string): Promise<void>;
85
- deleteFileRecordByPublicId(publicId: string): Promise<void>;
86
- findFileRecordWithVariantKeys(publicId: string): Promise<FilesRecordAggregate | null>;
87
- findFileRecordForDelete(publicId: string): Promise<FilesRecordAggregate | null>;
88
- findFileRecordForDownload(publicId: string): Promise<FilesRecordAggregate | null>;
89
- countOwnerUsage(ownerType: string, ownerId: string): Promise<{
90
- filesCount: number;
91
- totalBytes: number;
92
- }>;
93
- findBlobRef(hash: string, size: number, mimeType: string, storageDriver: string): Promise<FilesBlobRef | null>;
94
- createBlob(data: FilesBlobCreateInput): Promise<FilesBlobRecord>;
95
- createVariants(data: FilesVariantCreateInput[]): Promise<void>;
96
- findBlobById(id: string): Promise<FilesBlobRecord | null>;
97
- deleteBlobIfUnreferenced(id: string): Promise<boolean>;
98
- }
99
-
100
5
  export interface FilesStorageAdapter {
101
6
  readonly driver: string;
102
7
  put(buffer: Buffer, fileName: string): Promise<{
@@ -5,22 +5,15 @@ import {
5
5
  BadRequestException,
6
6
  Inject,
7
7
  Injectable,
8
- InternalServerErrorException,
9
8
  NotFoundException,
9
+ Optional,
10
10
  ServiceUnavailableException,
11
11
  } from '@nestjs/common';
12
12
  import { FilesConfigService } from './files-config.service';
13
- import {
14
- FILES_PERSISTENCE_PORT,
15
- FILES_STORAGE_ADAPTER,
16
- } from './files.ports';
17
- import type {
18
- FilesBlobRecord,
19
- FilesBlobRef,
20
- FilesPersistencePort,
21
- FilesRecordAggregate,
22
- FilesStorageAdapter,
23
- } from './files.ports';
13
+ import { FILES_STORAGE_ADAPTER } from './files.ports';
14
+ import type { FilesStorageAdapter } from './files.ports';
15
+ import { FilesStore } from './files.store';
16
+ import type { FilesBlobRef, FilesRecordAggregate } from './files.store';
24
17
  import type { FileRecordDto, FileVariantKey, StoredFileInput } from './files.types';
25
18
 
26
19
  type PrismaLikeError = {
@@ -44,10 +37,8 @@ type PersistedVariant = {
44
37
  @Injectable()
45
38
  export class FilesService {
46
39
  constructor(
47
- @Inject(FILES_PERSISTENCE_PORT)
48
- private readonly persistence: FilesPersistencePort,
49
- @Inject(FILES_STORAGE_ADAPTER)
50
- private readonly storageAdapter: FilesStorageAdapter,
40
+ private readonly filesStore: FilesStore,
41
+ @Optional() @Inject(FILES_STORAGE_ADAPTER) private readonly storageAdapter: FilesStorageAdapter | undefined,
51
42
  private readonly filesConfigService: FilesConfigService,
52
43
  ) {}
53
44
 
@@ -69,7 +60,7 @@ export class FilesService {
69
60
  createdBlobIds.push(originalBlob.id);
70
61
  }
71
62
 
72
- const record = await this.persistence.createFileRecord({
63
+ const record = await this.filesStore.createFileRecord({
73
64
  publicId: this.generatePublicId(),
74
65
  storageKey: originalBlob.storageKey,
75
66
  originalName: input.originalName,
@@ -110,7 +101,7 @@ export class FilesService {
110
101
  persistedVariantBlobIds.push(previewBlob.id);
111
102
  }
112
103
 
113
- await this.persistence.createVariants(
104
+ await this.filesStore.createVariants(
114
105
  persistedVariants.map((item, index) => ({
115
106
  fileId: record.id,
116
107
  variantKey: item.variantKey,
@@ -124,7 +115,7 @@ export class FilesService {
124
115
  return this.getByPublicId(record.publicId);
125
116
  } catch (error) {
126
117
  if (recordId) {
127
- await this.persistence.deleteFileRecordById(recordId).catch(() => undefined);
118
+ await this.filesStore.deleteFileRecordById(recordId).catch(() => undefined);
128
119
  }
129
120
  await this.cleanupCreatedBlobs(createdBlobIds);
130
121
  throw error;
@@ -144,13 +135,13 @@ export class FilesService {
144
135
  }
145
136
 
146
137
  async deleteByPublicId(publicId: string): Promise<{ deleted: boolean }> {
147
- const record = await this.persistence.findFileRecordForDelete(publicId);
138
+ const record = await this.filesStore.findFileRecordForDelete(publicId);
148
139
  if (!record) {
149
140
  throw new NotFoundException('File not found');
150
141
  }
151
142
 
152
143
  const blobIds = (record.variants ?? []).map((variant) => variant.blobId);
153
- await this.persistence.deleteFileRecordByPublicId(publicId);
144
+ await this.filesStore.deleteFileRecordByPublicId(publicId);
154
145
  await this.cleanupReferencedBlobs(blobIds);
155
146
 
156
147
  if ((record.variants ?? []).length === 0) {
@@ -161,7 +152,7 @@ export class FilesService {
161
152
  }
162
153
 
163
154
  async getByPublicId(publicId: string): Promise<FileRecordDto> {
164
- const record = await this.persistence.findFileRecordWithVariantKeys(publicId);
155
+ const record = await this.filesStore.findFileRecordWithVariantKeys(publicId);
165
156
  if (!record) {
166
157
  throw new NotFoundException('File not found');
167
158
  }
@@ -169,7 +160,7 @@ export class FilesService {
169
160
  }
170
161
 
171
162
  async getOwnerUsage(ownerType: string, ownerId: string): Promise<{ filesCount: number; totalBytes: number }> {
172
- return this.persistence.countOwnerUsage(ownerType, ownerId);
163
+ return this.filesStore.countOwnerUsage(ownerType, ownerId);
173
164
  }
174
165
 
175
166
  async openDownload(publicId: string, variant: FileVariantKey = 'original'): Promise<{
@@ -177,7 +168,7 @@ export class FilesService {
177
168
  mimeType: string;
178
169
  fileName: string;
179
170
  }> {
180
- const record = await this.persistence.findFileRecordForDownload(publicId);
171
+ const record = await this.filesStore.findFileRecordForDownload(publicId);
181
172
  if (!record) {
182
173
  throw new NotFoundException('File not found');
183
174
  }
@@ -215,6 +206,7 @@ export class FilesService {
215
206
  supportedVariants: FileVariantKey[];
216
207
  previewGenerationEnabled: boolean;
217
208
  }> {
209
+ this.requireStorageAdapter();
218
210
  return {
219
211
  status: 'ok',
220
212
  feature: 'files-variants',
@@ -250,14 +242,26 @@ export class FilesService {
250
242
  }
251
243
  }
252
244
 
245
+ private requireStorageAdapter(): FilesStorageAdapter {
246
+ if (this.storageAdapter) {
247
+ return this.storageAdapter;
248
+ }
249
+
250
+ throw new ServiceUnavailableException(
251
+ 'Files storage adapter is not configured. Install/add a files-storage-adapter provider.',
252
+ );
253
+ }
254
+
253
255
  private async store(buffer: Buffer, originalName: string): Promise<{ storageKey: string }> {
256
+ const storageAdapter = this.requireStorageAdapter();
254
257
  const extension = path.extname(originalName).toLowerCase();
255
258
  const fileName = `${Date.now()}-${crypto.randomUUID()}${extension}`;
256
- return this.storageAdapter.put(buffer, fileName);
259
+ return storageAdapter.put(buffer, fileName);
257
260
  }
258
261
 
259
262
  private async getOrCreateBlob(input: PreparedStoredFile, dedupe: boolean): Promise<FilesBlobRef> {
260
- const storageDriver = this.storageAdapter.driver;
263
+ const storageAdapter = this.requireStorageAdapter();
264
+ const storageDriver = storageAdapter.driver;
261
265
  const hash = this.computeContentHash(input.buffer);
262
266
 
263
267
  if (dedupe) {
@@ -269,7 +273,7 @@ export class FilesService {
269
273
 
270
274
  const stored = await this.store(input.buffer, input.fileName);
271
275
  try {
272
- const created = await this.persistence.createBlob({
276
+ const created = await this.filesStore.createBlob({
273
277
  hash,
274
278
  size: input.size,
275
279
  mimeType: input.mimeType,
@@ -321,21 +325,23 @@ export class FilesService {
321
325
  }
322
326
 
323
327
  private async openStoredContent(storageDriver: string, storageKey: string): Promise<Readable> {
324
- if (storageDriver !== this.storageAdapter.driver) {
328
+ const storageAdapter = this.requireStorageAdapter();
329
+ if (storageDriver !== storageAdapter.driver) {
325
330
  throw new ServiceUnavailableException(
326
- `File was stored with driver "${storageDriver}", but current adapter is "${this.storageAdapter.driver}".`,
331
+ `File was stored with driver "${storageDriver}", but current adapter is "${storageAdapter.driver}".`,
327
332
  );
328
333
  }
329
- return this.storageAdapter.open(storageKey);
334
+ return storageAdapter.open(storageKey);
330
335
  }
331
336
 
332
337
  private async deleteStoredContent(storageDriver: string, storageKey: string): Promise<void> {
333
- if (storageDriver !== this.storageAdapter.driver) {
338
+ const storageAdapter = this.requireStorageAdapter();
339
+ if (storageDriver !== storageAdapter.driver) {
334
340
  throw new ServiceUnavailableException(
335
- `File was stored with driver "${storageDriver}", but current adapter is "${this.storageAdapter.driver}".`,
341
+ `File was stored with driver "${storageDriver}", but current adapter is "${storageAdapter.driver}".`,
336
342
  );
337
343
  }
338
- await this.storageAdapter.delete(storageKey);
344
+ await storageAdapter.delete(storageKey);
339
345
  }
340
346
 
341
347
  private generatePublicId(): string {
@@ -353,12 +359,12 @@ export class FilesService {
353
359
  private async cleanupReferencedBlobs(blobIds: string[]): Promise<void> {
354
360
  const uniqueIds = [...new Set(blobIds.filter(Boolean))];
355
361
  for (const blobId of uniqueIds) {
356
- const blob = await this.persistence.findBlobById(blobId);
362
+ const blob = await this.filesStore.findBlobById(blobId);
357
363
  if (!blob) {
358
364
  continue;
359
365
  }
360
366
 
361
- const deleted = await this.persistence.deleteBlobIfUnreferenced(blob.id);
367
+ const deleted = await this.filesStore.deleteBlobIfUnreferenced(blob.id);
362
368
  if (!deleted) {
363
369
  continue;
364
370
  }
@@ -372,7 +378,7 @@ export class FilesService {
372
378
  mimeType: string,
373
379
  storageDriver: string,
374
380
  ): Promise<FilesBlobRef | null> {
375
- const existing = await this.persistence.findBlobRef(hash, size, mimeType, storageDriver);
381
+ const existing = await this.filesStore.findBlobRef(hash, size, mimeType, storageDriver);
376
382
  if (!existing) {
377
383
  return null;
378
384
  }
@@ -446,3 +452,4 @@ export class FilesService {
446
452
  return `${basePath}/${publicId}/download`;
447
453
  }
448
454
  }
455
+
@@ -1,17 +1,81 @@
1
- import {
2
- FILES_PERSISTENCE_PORT,
3
- type FilesBlobCreateInput,
4
- type FilesBlobRef,
5
- type FilesPersistencePort,
6
- type FilesRecordAggregate,
7
- type FilesRecordCreateInput,
8
- type FilesVariantCreateInput,
9
- } from '@forgeon/files';
10
- import { PrismaService } from '@forgeon/db-prisma';
11
1
  import { Injectable } from '@nestjs/common';
2
+ import { PrismaService } from '@forgeon/db-prisma';
3
+ import type { FileVariantKey } from './files.types';
4
+
5
+ export type FilesBlobRecord = {
6
+ id: string;
7
+ hash: string;
8
+ size: number;
9
+ mimeType: string;
10
+ storageDriver: string;
11
+ storageKey: string;
12
+ };
13
+
14
+ export type FilesBlobRef = FilesBlobRecord & {
15
+ created: boolean;
16
+ };
17
+
18
+ export type FilesRecordVariant = {
19
+ variantKey: string;
20
+ blobId: string;
21
+ mimeType: string;
22
+ size: number;
23
+ status: string;
24
+ blob?: {
25
+ storageDriver: string;
26
+ storageKey: string;
27
+ };
28
+ };
29
+
30
+ export type FilesRecordAggregate = {
31
+ id: string;
32
+ publicId: string;
33
+ storageKey: string;
34
+ originalName: string;
35
+ mimeType: string;
36
+ size: number;
37
+ storageDriver: string;
38
+ ownerType: string;
39
+ ownerId: string | null;
40
+ visibility: string;
41
+ createdById: string | null;
42
+ createdAt: Date;
43
+ updatedAt: Date;
44
+ variants?: FilesRecordVariant[];
45
+ };
46
+
47
+ export type FilesRecordCreateInput = {
48
+ publicId: string;
49
+ storageKey: string;
50
+ originalName: string;
51
+ mimeType: string;
52
+ size: number;
53
+ storageDriver: string;
54
+ ownerType: string;
55
+ ownerId: string | null;
56
+ visibility: string;
57
+ createdById: string | null;
58
+ };
59
+
60
+ export type FilesBlobCreateInput = {
61
+ hash: string;
62
+ size: number;
63
+ mimeType: string;
64
+ storageDriver: string;
65
+ storageKey: string;
66
+ };
67
+
68
+ export type FilesVariantCreateInput = {
69
+ fileId: string;
70
+ variantKey: FileVariantKey;
71
+ blobId: string;
72
+ mimeType: string;
73
+ size: number;
74
+ status: string;
75
+ };
12
76
 
13
77
  @Injectable()
14
- export class PrismaFilesPersistenceStore implements FilesPersistencePort {
78
+ export class FilesStore {
15
79
  constructor(private readonly prisma: PrismaService) {}
16
80
 
17
81
  async createFileRecord(data: FilesRecordCreateInput): Promise<{ id: string; publicId: string }> {
@@ -136,7 +200,7 @@ export class PrismaFilesPersistenceStore implements FilesPersistencePort {
136
200
  };
137
201
  }
138
202
 
139
- async createBlob(data: FilesBlobCreateInput) {
203
+ async createBlob(data: FilesBlobCreateInput): Promise<FilesBlobRecord> {
140
204
  return this.prisma.fileBlob.create({ data });
141
205
  }
142
206
 
@@ -144,7 +208,7 @@ export class PrismaFilesPersistenceStore implements FilesPersistencePort {
144
208
  await this.prisma.fileVariant.createMany({ data });
145
209
  }
146
210
 
147
- async findBlobById(id: string) {
211
+ async findBlobById(id: string): Promise<FilesBlobRecord | null> {
148
212
  return this.prisma.fileBlob.findUnique({
149
213
  where: { id },
150
214
  });
@@ -1,136 +1,27 @@
1
1
  import {
2
2
  DynamicModule,
3
- Injectable,
4
3
  Module,
5
4
  ModuleMetadata,
6
- Provider,
7
- ServiceUnavailableException,
8
5
  } from '@nestjs/common';
9
- import type {
10
- FilesBlobCreateInput,
11
- FilesPersistencePort,
12
- FilesRecordCreateInput,
13
- FilesStorageAdapter,
14
- FilesVariantCreateInput,
15
- } from './files.ports';
16
- import { FILES_PERSISTENCE_PORT, FILES_STORAGE_ADAPTER } from './files.ports';
6
+ import { DbPrismaModule } from '@forgeon/db-prisma';
17
7
  import { FilesController } from './files.controller';
18
8
  import { FilesConfigModule } from './files-config.module';
19
9
  import { FilesService } from './files.service';
10
+ import { FilesStore } from './files.store';
20
11
 
21
12
  export interface ForgeonFilesModuleOptions {
22
13
  imports?: ModuleMetadata['imports'];
23
- persistenceProvider?: Provider;
24
- storageAdapterProvider?: Provider;
25
14
  }
26
15
 
27
- @Injectable()
28
- class MissingFilesPersistencePort implements FilesPersistencePort {
29
- private unconfigured(): never {
30
- throw new ServiceUnavailableException(
31
- 'Files persistence provider is not configured. Install/add a db-adapter provider and wire FILES_PERSISTENCE_PORT.',
32
- );
33
- }
34
-
35
- async createFileRecord(_data: FilesRecordCreateInput): Promise<{ id: string; publicId: string }> {
36
- return this.unconfigured();
37
- }
38
-
39
- async deleteFileRecordById(_id: string): Promise<void> {
40
- return this.unconfigured();
41
- }
42
-
43
- async deleteFileRecordByPublicId(_publicId: string): Promise<void> {
44
- return this.unconfigured();
45
- }
46
-
47
- async findFileRecordWithVariantKeys(_publicId: string) {
48
- return this.unconfigured();
49
- }
50
-
51
- async findFileRecordForDelete(_publicId: string) {
52
- return this.unconfigured();
53
- }
54
-
55
- async findFileRecordForDownload(_publicId: string) {
56
- return this.unconfigured();
57
- }
58
-
59
- async countOwnerUsage(_ownerType: string, _ownerId: string) {
60
- return this.unconfigured();
61
- }
62
-
63
- async findBlobRef(_hash: string, _size: number, _mimeType: string, _storageDriver: string) {
64
- return this.unconfigured();
65
- }
66
-
67
- async createBlob(_data: FilesBlobCreateInput) {
68
- return this.unconfigured();
69
- }
70
-
71
- async createVariants(_data: FilesVariantCreateInput[]): Promise<void> {
72
- return this.unconfigured();
73
- }
74
-
75
- async findBlobById(_id: string) {
76
- return this.unconfigured();
77
- }
78
-
79
- async deleteBlobIfUnreferenced(_id: string) {
80
- return this.unconfigured();
81
- }
82
- }
83
-
84
- @Injectable()
85
- class MissingFilesStorageAdapter implements FilesStorageAdapter {
86
- readonly driver = 'unconfigured';
87
-
88
- private unconfigured(): never {
89
- throw new ServiceUnavailableException(
90
- 'Files storage adapter is not configured. Install/add a files-storage-adapter provider and wire FILES_STORAGE_ADAPTER.',
91
- );
92
- }
93
-
94
- async put(_buffer: Buffer, _fileName: string): Promise<{ storageKey: string }> {
95
- return this.unconfigured();
96
- }
97
-
98
- async open(_storageKey: string) {
99
- return this.unconfigured();
100
- }
101
-
102
- async delete(_storageKey: string): Promise<void> {
103
- return this.unconfigured();
104
- }
105
- }
106
-
107
- @Module({
108
- imports: [FilesConfigModule],
109
- controllers: [FilesController],
110
- providers: [FilesService],
111
- exports: [FilesConfigModule, FilesService],
112
- })
16
+ @Module({})
113
17
  export class ForgeonFilesModule {
114
18
  static register(options: ForgeonFilesModuleOptions = {}): DynamicModule {
115
- const persistenceProvider =
116
- options.persistenceProvider ??
117
- ({
118
- provide: FILES_PERSISTENCE_PORT,
119
- useClass: MissingFilesPersistencePort,
120
- } satisfies Provider);
121
-
122
- const storageAdapterProvider =
123
- options.storageAdapterProvider ??
124
- ({
125
- provide: FILES_STORAGE_ADAPTER,
126
- useClass: MissingFilesStorageAdapter,
127
- } satisfies Provider);
128
-
129
19
  return {
130
20
  module: ForgeonFilesModule,
131
- imports: [...(options.imports ?? [])],
132
- providers: [persistenceProvider, storageAdapterProvider],
133
- exports: [FILES_PERSISTENCE_PORT, FILES_STORAGE_ADAPTER],
21
+ imports: [FilesConfigModule, DbPrismaModule, ...(options.imports ?? [])],
22
+ controllers: [FilesController],
23
+ providers: [FilesStore, FilesService],
24
+ exports: [FilesConfigModule, FilesStore, FilesService],
134
25
  };
135
26
  }
136
27
  }
@@ -7,4 +7,5 @@ export * from './files-config.service';
7
7
  export * from './files-env.schema';
8
8
  export * from './files.ports';
9
9
  export * from './files.service';
10
+ export * from './files.store';
10
11
  export * from './files.types';
@@ -1,20 +1,20 @@
1
- {
2
- "name": "@forgeon/files-quotas",
3
- "version": "0.1.0",
4
- "private": true,
5
- "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
7
- "scripts": {
8
- "build": "tsc -p tsconfig.json"
9
- },
10
- "dependencies": {
11
- "@forgeon/files": "workspace:*",
12
- "@nestjs/common": "^11.0.1",
13
- "@nestjs/config": "^4.0.0",
14
- "zod": "^3.24.2"
15
- },
16
- "devDependencies": {
17
- "@types/node": "^22.10.7",
18
- "typescript": "^5.7.3"
19
- }
20
- }
1
+ {
2
+ "name": "@forgeon/files-quotas",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc -p tsconfig.json"
9
+ },
10
+ "dependencies": {
11
+ "@forgeon/files": "workspace:*",
12
+ "@nestjs/common": "^11.0.1",
13
+ "@nestjs/config": "^4.0.0",
14
+ "zod": "^3.24.2"
15
+ },
16
+ "devDependencies": {
17
+ "@types/node": "^22.10.7",
18
+ "typescript": "^5.7.3"
19
+ }
20
+ }