@volontariapp/domain-storage 0.5.0 → 0.6.0-snap-87ccfd8

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 (46) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +1 -0
  5. package/dist/index.js.map +1 -1
  6. package/dist/models/file.model.d.ts +30 -0
  7. package/dist/models/file.model.d.ts.map +1 -0
  8. package/dist/models/file.model.js +146 -0
  9. package/dist/models/file.model.js.map +1 -0
  10. package/dist/models/index.d.ts +4 -0
  11. package/dist/models/index.d.ts.map +1 -0
  12. package/dist/models/index.js +4 -0
  13. package/dist/models/index.js.map +1 -0
  14. package/dist/models/released-entity.model.d.ts +7 -0
  15. package/dist/models/released-entity.model.d.ts.map +1 -0
  16. package/dist/models/released-entity.model.js +33 -0
  17. package/dist/models/released-entity.model.js.map +1 -0
  18. package/dist/models/transformers/bigint-number.transformer.d.ts +3 -0
  19. package/dist/models/transformers/bigint-number.transformer.d.ts.map +1 -0
  20. package/dist/models/transformers/bigint-number.transformer.js +5 -0
  21. package/dist/models/transformers/bigint-number.transformer.js.map +1 -0
  22. package/dist/test/bigint-number.transformer.unit.spec.d.ts +2 -0
  23. package/dist/test/bigint-number.transformer.unit.spec.d.ts.map +1 -0
  24. package/dist/test/bigint-number.transformer.unit.spec.js +18 -0
  25. package/dist/test/bigint-number.transformer.unit.spec.js.map +1 -0
  26. package/dist/test/data-source.d.ts +6 -0
  27. package/dist/test/data-source.d.ts.map +1 -0
  28. package/dist/test/data-source.js +36 -0
  29. package/dist/test/data-source.js.map +1 -0
  30. package/dist/test/factories/file.factory.d.ts +4 -0
  31. package/dist/test/factories/file.factory.d.ts.map +1 -0
  32. package/dist/test/factories/file.factory.js +20 -0
  33. package/dist/test/factories/file.factory.js.map +1 -0
  34. package/dist/test/integration/file.model.int.spec.d.ts +2 -0
  35. package/dist/test/integration/file.model.int.spec.d.ts.map +1 -0
  36. package/dist/test/integration/file.model.int.spec.js +159 -0
  37. package/dist/test/integration/file.model.int.spec.js.map +1 -0
  38. package/dist/test/migrations/domain/1791300000000-InitialStorageSchema.d.ts +7 -0
  39. package/dist/test/migrations/domain/1791300000000-InitialStorageSchema.d.ts.map +1 -0
  40. package/dist/test/migrations/domain/1791300000000-InitialStorageSchema.js +55 -0
  41. package/dist/test/migrations/domain/1791300000000-InitialStorageSchema.js.map +1 -0
  42. package/dist/test/setup.d.ts +2 -0
  43. package/dist/test/setup.d.ts.map +1 -0
  44. package/dist/test/setup.js +2 -0
  45. package/dist/test/setup.js.map +1 -0
  46. package/package.json +12 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - feat(domain-storage): add FileModel and ReleasedEntityModel (TypeORM)
8
+
9
+ Add the `FileModel` (table `files`) and `ReleasedEntityModel` (table `released_entities`) mapped on the `ms_storage` schema, with explicit snake_case column names, the partial indexes used by the purge jobs, and a `bigint` to number transformer for the file sizes.
10
+ Add the integration test setup (`test:integration`, `migration:run`, a test migration and a data source) so that `ms-storage`, `worker-storage` and `post-processor-storage` share a single persistence model.
11
+ `typeorm` is now required by consumers that import the models.
12
+
3
13
  ## 0.5.0
4
14
 
5
15
  ### Minor Changes
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ export * from './contracts/index.js';
2
2
  export * from './enums/index.js';
3
3
  export * from './exceptions/index.js';
4
4
  export * from './helpers/index.js';
5
+ export * from './models/index.js';
5
6
  export * from './policies/index.js';
6
7
  export * from './value-objects/index.js';
7
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC"}
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@ export * from './contracts/index.js';
2
2
  export * from './enums/index.js';
3
3
  export * from './exceptions/index.js';
4
4
  export * from './helpers/index.js';
5
+ export * from './models/index.js';
5
6
  export * from './policies/index.js';
6
7
  export * from './value-objects/index.js';
7
8
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC"}
@@ -0,0 +1,30 @@
1
+ import { EntityType } from '../enums/entity-type.enum.js';
2
+ import { FileStatus } from '../enums/file-status.enum.js';
3
+ import { RejectionReason } from '../enums/rejection-reason.enum.js';
4
+ import { ScanStatus } from '../enums/scan-status.enum.js';
5
+ import { ValidationMode } from '../enums/validation-mode.enum.js';
6
+ export declare class FileModel {
7
+ id: string;
8
+ ownerId: string;
9
+ entityType: EntityType;
10
+ entityId: string | null;
11
+ status: FileStatus;
12
+ scanStatus: ScanStatus;
13
+ rejectionReason: RejectionReason | null;
14
+ declaredMimeType: string;
15
+ declaredSize: number;
16
+ actualSize: number | null;
17
+ quarantineKey: string;
18
+ publicKey: string | null;
19
+ scanAttempts: number;
20
+ rescanScheduledAt: Date | null;
21
+ validationMode: ValidationMode;
22
+ uploadExpiresAt: Date;
23
+ confirmedAt: Date | null;
24
+ scannedAt: Date | null;
25
+ reservedAt: Date | null;
26
+ attachedAt: Date | null;
27
+ createdAt: Date;
28
+ updatedAt: Date;
29
+ }
30
+ //# sourceMappingURL=file.model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file.model.d.ts","sourceRoot":"","sources":["../../src/models/file.model.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAGlE,qBAUa,SAAS;IAGpB,EAAE,EAAG,MAAM,CAAC;IAGZ,OAAO,EAAG,MAAM,CAAC;IAGjB,UAAU,EAAG,UAAU,CAAC;IAGxB,QAAQ,EAAG,MAAM,GAAG,IAAI,CAAC;IAGzB,MAAM,EAAG,UAAU,CAAC;IAGpB,UAAU,EAAG,UAAU,CAAC;IAGxB,eAAe,EAAG,eAAe,GAAG,IAAI,CAAC;IAGzC,gBAAgB,EAAG,MAAM,CAAC;IAG1B,YAAY,EAAG,MAAM,CAAC;IAQtB,UAAU,EAAG,MAAM,GAAG,IAAI,CAAC;IAI3B,aAAa,EAAG,MAAM,CAAC;IAIvB,SAAS,EAAG,MAAM,GAAG,IAAI,CAAC;IAI1B,YAAY,EAAG,MAAM,CAAC;IAItB,iBAAiB,EAAG,IAAI,GAAG,IAAI,CAAC;IAIhC,cAAc,EAAG,cAAc,CAAC;IAGhC,eAAe,EAAG,IAAI,CAAC;IAGvB,WAAW,EAAG,IAAI,GAAG,IAAI,CAAC;IAG1B,SAAS,EAAG,IAAI,GAAG,IAAI,CAAC;IAGxB,UAAU,EAAG,IAAI,GAAG,IAAI,CAAC;IAGzB,UAAU,EAAG,IAAI,GAAG,IAAI,CAAC;IAGzB,SAAS,EAAG,IAAI,CAAC;IAGjB,SAAS,EAAG,IAAI,CAAC;CAClB"}
@@ -0,0 +1,146 @@
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
+ import { Column, CreateDateColumn, Entity, Index, PrimaryColumn, UpdateDateColumn } from 'typeorm';
11
+ import { EntityType } from '../enums/entity-type.enum.js';
12
+ import { FileStatus } from '../enums/file-status.enum.js';
13
+ import { ScanStatus } from '../enums/scan-status.enum.js';
14
+ import { ValidationMode } from '../enums/validation-mode.enum.js';
15
+ import { bigintNumberTransformer } from './transformers/bigint-number.transformer.js';
16
+ let FileModel = class FileModel {
17
+ id;
18
+ ownerId;
19
+ entityType;
20
+ entityId;
21
+ status;
22
+ scanStatus;
23
+ rejectionReason;
24
+ declaredMimeType;
25
+ declaredSize;
26
+ actualSize;
27
+ quarantineKey;
28
+ publicKey;
29
+ scanAttempts;
30
+ rescanScheduledAt;
31
+ validationMode;
32
+ uploadExpiresAt;
33
+ confirmedAt;
34
+ scannedAt;
35
+ reservedAt;
36
+ attachedAt;
37
+ createdAt;
38
+ updatedAt;
39
+ };
40
+ __decorate([
41
+ PrimaryColumn({ type: 'uuid' }),
42
+ __metadata("design:type", String)
43
+ ], FileModel.prototype, "id", void 0);
44
+ __decorate([
45
+ Column({ name: 'owner_id', type: 'uuid' }),
46
+ __metadata("design:type", String)
47
+ ], FileModel.prototype, "ownerId", void 0);
48
+ __decorate([
49
+ Column({ name: 'entity_type', type: 'varchar' }),
50
+ __metadata("design:type", String)
51
+ ], FileModel.prototype, "entityType", void 0);
52
+ __decorate([
53
+ Column({ name: 'entity_id', type: 'uuid', nullable: true }),
54
+ __metadata("design:type", Object)
55
+ ], FileModel.prototype, "entityId", void 0);
56
+ __decorate([
57
+ Column({ type: 'varchar', default: FileStatus.PENDING }),
58
+ __metadata("design:type", String)
59
+ ], FileModel.prototype, "status", void 0);
60
+ __decorate([
61
+ Column({ name: 'scan_status', type: 'varchar', default: ScanStatus.AWAITING_UPLOAD }),
62
+ __metadata("design:type", String)
63
+ ], FileModel.prototype, "scanStatus", void 0);
64
+ __decorate([
65
+ Column({ name: 'rejection_reason', type: 'varchar', nullable: true }),
66
+ __metadata("design:type", Object)
67
+ ], FileModel.prototype, "rejectionReason", void 0);
68
+ __decorate([
69
+ Column({ name: 'declared_mime_type', type: 'varchar' }),
70
+ __metadata("design:type", String)
71
+ ], FileModel.prototype, "declaredMimeType", void 0);
72
+ __decorate([
73
+ Column({ name: 'declared_size', type: 'bigint', transformer: bigintNumberTransformer }),
74
+ __metadata("design:type", Number)
75
+ ], FileModel.prototype, "declaredSize", void 0);
76
+ __decorate([
77
+ Column({
78
+ name: 'actual_size',
79
+ type: 'bigint',
80
+ nullable: true,
81
+ transformer: bigintNumberTransformer,
82
+ }),
83
+ __metadata("design:type", Object)
84
+ ], FileModel.prototype, "actualSize", void 0);
85
+ __decorate([
86
+ Column({ name: 'quarantine_key', type: 'varchar' }),
87
+ __metadata("design:type", String)
88
+ ], FileModel.prototype, "quarantineKey", void 0);
89
+ __decorate([
90
+ Column({ name: 'public_key', type: 'varchar', nullable: true }),
91
+ __metadata("design:type", Object)
92
+ ], FileModel.prototype, "publicKey", void 0);
93
+ __decorate([
94
+ Column({ name: 'scan_attempts', type: 'int', default: 0 }),
95
+ __metadata("design:type", Number)
96
+ ], FileModel.prototype, "scanAttempts", void 0);
97
+ __decorate([
98
+ Column({ name: 'rescan_scheduled_at', type: 'timestamptz', nullable: true }),
99
+ __metadata("design:type", Object)
100
+ ], FileModel.prototype, "rescanScheduledAt", void 0);
101
+ __decorate([
102
+ Column({ name: 'validation_mode', type: 'varchar' }),
103
+ __metadata("design:type", String)
104
+ ], FileModel.prototype, "validationMode", void 0);
105
+ __decorate([
106
+ Column({ name: 'upload_expires_at', type: 'timestamptz' }),
107
+ __metadata("design:type", Date)
108
+ ], FileModel.prototype, "uploadExpiresAt", void 0);
109
+ __decorate([
110
+ Column({ name: 'confirmed_at', type: 'timestamptz', nullable: true }),
111
+ __metadata("design:type", Object)
112
+ ], FileModel.prototype, "confirmedAt", void 0);
113
+ __decorate([
114
+ Column({ name: 'scanned_at', type: 'timestamptz', nullable: true }),
115
+ __metadata("design:type", Object)
116
+ ], FileModel.prototype, "scannedAt", void 0);
117
+ __decorate([
118
+ Column({ name: 'reserved_at', type: 'timestamptz', nullable: true }),
119
+ __metadata("design:type", Object)
120
+ ], FileModel.prototype, "reservedAt", void 0);
121
+ __decorate([
122
+ Column({ name: 'attached_at', type: 'timestamptz', nullable: true }),
123
+ __metadata("design:type", Object)
124
+ ], FileModel.prototype, "attachedAt", void 0);
125
+ __decorate([
126
+ CreateDateColumn({ name: 'created_at', type: 'timestamptz' }),
127
+ __metadata("design:type", Date)
128
+ ], FileModel.prototype, "createdAt", void 0);
129
+ __decorate([
130
+ UpdateDateColumn({ name: 'updated_at', type: 'timestamptz' }),
131
+ __metadata("design:type", Date)
132
+ ], FileModel.prototype, "updatedAt", void 0);
133
+ FileModel = __decorate([
134
+ Entity('files'),
135
+ Index('idx_files_entity', ['entityType', 'entityId']),
136
+ Index('idx_files_owner', ['ownerId']),
137
+ Index('idx_files_awaiting', ['uploadExpiresAt'], {
138
+ where: `"scan_status" = 'AWAITING_UPLOAD'`,
139
+ }),
140
+ Index('idx_files_scanning', ['confirmedAt'], { where: `"scan_status" = 'SCANNING'` }),
141
+ Index('idx_files_unused', ['confirmedAt'], { where: `"status" = 'PENDING'` }),
142
+ Index('idx_files_reserved', ['reservedAt'], { where: `"status" = 'RESERVED'` }),
143
+ Index('idx_files_orphaned', ['updatedAt'], { where: `"status" = 'ORPHANED'` })
144
+ ], FileModel);
145
+ export { FileModel };
146
+ //# sourceMappingURL=file.model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file.model.js","sourceRoot":"","sources":["../../src/models/file.model.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export * from './file.model.js';
2
+ export * from './released-entity.model.js';
3
+ export * from './transformers/bigint-number.transformer.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/models/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6CAA6C,CAAC"}
@@ -0,0 +1,4 @@
1
+ export * from './file.model.js';
2
+ export * from './released-entity.model.js';
3
+ export * from './transformers/bigint-number.transformer.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/models/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6CAA6C,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { EntityType } from '../enums/entity-type.enum.js';
2
+ export declare class ReleasedEntityModel {
3
+ entityType: EntityType;
4
+ entityId: string;
5
+ releasedAt: Date;
6
+ }
7
+ //# sourceMappingURL=released-entity.model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"released-entity.model.d.ts","sourceRoot":"","sources":["../../src/models/released-entity.model.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAM1D,qBACa,mBAAmB;IAE9B,UAAU,EAAG,UAAU,CAAC;IAGxB,QAAQ,EAAG,MAAM,CAAC;IAGlB,UAAU,EAAG,IAAI,CAAC;CACnB"}
@@ -0,0 +1,33 @@
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
+ import { CreateDateColumn, Entity, PrimaryColumn } from 'typeorm';
11
+ import { EntityType } from '../enums/entity-type.enum.js';
12
+ let ReleasedEntityModel = class ReleasedEntityModel {
13
+ entityType;
14
+ entityId;
15
+ releasedAt;
16
+ };
17
+ __decorate([
18
+ PrimaryColumn({ name: 'entity_type', type: 'varchar' }),
19
+ __metadata("design:type", String)
20
+ ], ReleasedEntityModel.prototype, "entityType", void 0);
21
+ __decorate([
22
+ PrimaryColumn({ name: 'entity_id', type: 'uuid' }),
23
+ __metadata("design:type", String)
24
+ ], ReleasedEntityModel.prototype, "entityId", void 0);
25
+ __decorate([
26
+ CreateDateColumn({ name: 'released_at', type: 'timestamptz' }),
27
+ __metadata("design:type", Date)
28
+ ], ReleasedEntityModel.prototype, "releasedAt", void 0);
29
+ ReleasedEntityModel = __decorate([
30
+ Entity('released_entities')
31
+ ], ReleasedEntityModel);
32
+ export { ReleasedEntityModel };
33
+ //# sourceMappingURL=released-entity.model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"released-entity.model.js","sourceRoot":"","sources":["../../src/models/released-entity.model.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAOnD,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;IAE9B,UAAU,CAAc;IAGxB,QAAQ,CAAU;IAGlB,UAAU,CAAQ;CACnB,CAAA;AAPC;IADC,aAAa,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;;uDAChC;AAGxB;IADC,aAAa,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;qDACjC;AAGlB;IADC,gBAAgB,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;8BAClD,IAAI;uDAAC;AARP,mBAAmB;IAD/B,MAAM,CAAC,mBAAmB,CAAC;GACf,mBAAmB,CAS/B"}
@@ -0,0 +1,3 @@
1
+ import type { ValueTransformer } from 'typeorm';
2
+ export declare const bigintNumberTransformer: ValueTransformer;
3
+ //# sourceMappingURL=bigint-number.transformer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bigint-number.transformer.d.ts","sourceRoot":"","sources":["../../../src/models/transformers/bigint-number.transformer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAOhD,eAAO,MAAM,uBAAuB,EAAE,gBAIrC,CAAC"}
@@ -0,0 +1,5 @@
1
+ export const bigintNumberTransformer = {
2
+ to: (value) => value,
3
+ from: (value) => value === null || value === undefined ? null : Number(value),
4
+ };
5
+ //# sourceMappingURL=bigint-number.transformer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bigint-number.transformer.js","sourceRoot":"","sources":["../../../src/models/transformers/bigint-number.transformer.ts"],"names":[],"mappings":"AAOA,MAAM,CAAC,MAAM,uBAAuB,GAAqB;IACvD,EAAE,EAAE,CAAC,KAAgC,EAA6B,EAAE,CAAC,KAAK;IAC1E,IAAI,EAAE,CAAC,KAAgC,EAAiB,EAAE,CACxD,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;CAC/D,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=bigint-number.transformer.unit.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bigint-number.transformer.unit.spec.d.ts","sourceRoot":"","sources":["../../src/test/bigint-number.transformer.unit.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,18 @@
1
+ import { describe, expect, it } from '@jest/globals';
2
+ import { bigintNumberTransformer } from '../index.js';
3
+ describe('bigintNumberTransformer', () => {
4
+ it('converts the string returned by pg into a number', () => {
5
+ expect(bigintNumberTransformer.from('10485760')).toBe(10485760);
6
+ });
7
+ it('keeps null as null when reading', () => {
8
+ expect(bigintNumberTransformer.from(null)).toBeNull();
9
+ });
10
+ it('treats an undefined database value as null', () => {
11
+ expect(bigintNumberTransformer.from(undefined)).toBeNull();
12
+ });
13
+ it('writes numbers and null unchanged', () => {
14
+ expect(bigintNumberTransformer.to(1024)).toBe(1024);
15
+ expect(bigintNumberTransformer.to(null)).toBeNull();
16
+ });
17
+ });
18
+ //# sourceMappingURL=bigint-number.transformer.unit.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bigint-number.transformer.unit.spec.js","sourceRoot":"","sources":["../../src/test/bigint-number.transformer.unit.spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAEtD,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACvC,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,MAAM,CAAC,uBAAuB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,CAAC,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,CAAC,uBAAuB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,CAAC,uBAAuB,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpD,MAAM,CAAC,uBAAuB,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACtD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { DataSource } from 'typeorm';
2
+ export declare const testDataSource: DataSource;
3
+ export declare const initializeTestDb: () => Promise<void>;
4
+ export declare const closeTestDb: () => Promise<void>;
5
+ export declare const truncateAll: () => Promise<void>;
6
+ //# sourceMappingURL=data-source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-source.d.ts","sourceRoot":"","sources":["../../src/test/data-source.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAQrC,eAAO,MAAM,cAAc,YAWzB,CAAC;AAMH,eAAO,MAAM,gBAAgB,QAAa,OAAO,CAAC,IAAI,CAYrD,CAAC;AAEF,eAAO,MAAM,WAAW,QAAa,OAAO,CAAC,IAAI,CAIhD,CAAC;AAEF,eAAO,MAAM,WAAW,QAAa,OAAO,CAAC,IAAI,CAEhD,CAAC"}
@@ -0,0 +1,36 @@
1
+ import { DataSource } from 'typeorm';
2
+ import { FileModel, ReleasedEntityModel } from '../models/index.js';
3
+ import { InitialStorageSchema1791300000000 } from './migrations/domain/1791300000000-InitialStorageSchema.js';
4
+ const LOCAL_HOSTS = ['localhost', '127.0.0.1'];
5
+ const host = process.env.TEST_DB_HOST ?? 'localhost';
6
+ export const testDataSource = new DataSource({
7
+ type: 'postgres',
8
+ host,
9
+ port: Number(process.env.TEST_DB_PORT ?? 5437),
10
+ username: process.env.TEST_DB_USER ?? 'user',
11
+ password: process.env.TEST_DB_PASSWORD ?? 'password',
12
+ database: process.env.TEST_DB_NAME ?? 'ms_storage',
13
+ entities: [FileModel, ReleasedEntityModel],
14
+ migrations: [InitialStorageSchema1791300000000],
15
+ synchronize: false,
16
+ logging: false,
17
+ });
18
+ export const initializeTestDb = async () => {
19
+ if (!LOCAL_HOSTS.includes(host)) {
20
+ throw new Error(`Refusing to drop the database on '${host}': integration tests only run against a local database.`);
21
+ }
22
+ if (!testDataSource.isInitialized) {
23
+ await testDataSource.initialize();
24
+ await testDataSource.dropDatabase();
25
+ await testDataSource.runMigrations();
26
+ }
27
+ };
28
+ export const closeTestDb = async () => {
29
+ if (testDataSource.isInitialized) {
30
+ await testDataSource.destroy();
31
+ }
32
+ };
33
+ export const truncateAll = async () => {
34
+ await testDataSource.query('TRUNCATE TABLE files, released_entities RESTART IDENTITY CASCADE');
35
+ };
36
+ //# sourceMappingURL=data-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-source.js","sourceRoot":"","sources":["../../src/test/data-source.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,EAAE,iCAAiC,EAAE,MAAM,2DAA2D,CAAC;AAE9G,MAAM,WAAW,GAAsB,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;AAElE,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,WAAW,CAAC;AAErD,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,UAAU,CAAC;IAC3C,IAAI,EAAE,UAAU;IAChB,IAAI;IACJ,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,IAAI,CAAC;IAC9C,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,MAAM;IAC5C,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,UAAU;IACpD,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,YAAY;IAClD,QAAQ,EAAE,CAAC,SAAS,EAAE,mBAAmB,CAAC;IAC1C,UAAU,EAAE,CAAC,iCAAiC,CAAC;IAC/C,WAAW,EAAE,KAAK;IAClB,OAAO,EAAE,KAAK;CACf,CAAC,CAAC;AAMH,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,IAAmB,EAAE;IACxD,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CACb,qCAAqC,IAAI,yDAAyD,CACnG,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,cAAc,CAAC,aAAa,EAAE,CAAC;QAClC,MAAM,cAAc,CAAC,UAAU,EAAE,CAAC;QAClC,MAAM,cAAc,CAAC,YAAY,EAAE,CAAC;QACpC,MAAM,cAAc,CAAC,aAAa,EAAE,CAAC;IACvC,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,IAAmB,EAAE;IACnD,IAAI,cAAc,CAAC,aAAa,EAAE,CAAC;QACjC,MAAM,cAAc,CAAC,OAAO,EAAE,CAAC;IACjC,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,IAAmB,EAAE;IACnD,MAAM,cAAc,CAAC,KAAK,CAAC,kEAAkE,CAAC,CAAC;AACjG,CAAC,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { FileModel } from '../../models/file.model.js';
2
+ export type FileData = Pick<FileModel, keyof FileModel>;
3
+ export declare const buildFileData: (overrides?: Partial<FileData>) => Partial<FileData>;
4
+ //# sourceMappingURL=file.factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file.factory.d.ts","sourceRoot":"","sources":["../../../src/test/factories/file.factory.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAM5D,MAAM,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,EAAE,MAAM,SAAS,CAAC,CAAC;AAGxD,eAAO,MAAM,aAAa,eAAe,OAAO,CAAC,QAAQ,CAAC,KAAQ,OAAO,CAAC,QAAQ,CAcjF,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { EntityType } from '../../enums/entity-type.enum.js';
2
+ import { ValidationMode } from '../../enums/validation-mode.enum.js';
3
+ import { buildQuarantineObjectKey } from '../../helpers/object-key.helpers.js';
4
+ import { FileId } from '../../value-objects/file-id.vo.js';
5
+ const UPLOAD_TTL_MS = 15 * 60 * 1000;
6
+ export const buildFileData = (overrides = {}) => {
7
+ const id = overrides.id ?? FileId.generate().getValue();
8
+ return {
9
+ id,
10
+ ownerId: FileId.generate().getValue(),
11
+ entityType: EntityType.POST,
12
+ declaredMimeType: 'image/png',
13
+ declaredSize: 1024,
14
+ quarantineKey: buildQuarantineObjectKey(id),
15
+ validationMode: ValidationMode.SYNC,
16
+ uploadExpiresAt: new Date(Date.now() + UPLOAD_TTL_MS),
17
+ ...overrides,
18
+ };
19
+ };
20
+ //# sourceMappingURL=file.factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file.factory.js","sourceRoot":"","sources":["../../../src/test/factories/file.factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AACrE,OAAO,EAAE,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAE/E,OAAO,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;AAE3D,MAAM,aAAa,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAMrC,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,YAA+B,EAAE,EAAqB,EAAE;IACpF,MAAM,EAAE,GAAG,SAAS,CAAC,EAAE,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC;IAExD,OAAO;QACL,EAAE;QACF,OAAO,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;QACrC,UAAU,EAAE,UAAU,CAAC,IAAI;QAC3B,gBAAgB,EAAE,WAAW;QAC7B,YAAY,EAAE,IAAI;QAClB,aAAa,EAAE,wBAAwB,CAAC,EAAE,CAAC;QAC3C,cAAc,EAAE,cAAc,CAAC,IAAI;QACnC,eAAe,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,aAAa,CAAC;QACrD,GAAG,SAAS;KACb,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=file.model.int.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file.model.int.spec.d.ts","sourceRoot":"","sources":["../../../src/test/integration/file.model.int.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,159 @@
1
+ import { afterAll, beforeAll, beforeEach, describe, expect, it } from '@jest/globals';
2
+ import { EntityType, FileId, FileModel, FileStatus, RejectionReason, ReleasedEntityModel, ScanStatus, ValidationMode, } from '../../index.js';
3
+ import { closeTestDb, initializeTestDb, testDataSource, truncateAll } from '../data-source.js';
4
+ import { buildFileData } from '../factories/file.factory.js';
5
+ const dbColumns = async (table) => {
6
+ const rows = await testDataSource.query(`SELECT column_name FROM information_schema.columns WHERE table_schema = 'public' AND table_name = $1`, [table]);
7
+ return rows.map((row) => row.column_name).sort();
8
+ };
9
+ const modelColumns = (model) => testDataSource
10
+ .getMetadata(model)
11
+ .columns.map((column) => column.databaseName)
12
+ .sort();
13
+ describe('files and released_entities persistence (integration)', () => {
14
+ beforeAll(async () => {
15
+ await initializeTestDb();
16
+ });
17
+ afterAll(async () => {
18
+ await closeTestDb();
19
+ });
20
+ beforeEach(async () => {
21
+ await truncateAll();
22
+ });
23
+ describe('schema', () => {
24
+ it('maps every column of the files table, and only those', async () => {
25
+ expect(modelColumns(FileModel)).toEqual(await dbColumns('files'));
26
+ });
27
+ it('maps every column of the released_entities table, and only those', async () => {
28
+ expect(modelColumns(ReleasedEntityModel)).toEqual(await dbColumns('released_entities'));
29
+ });
30
+ it('declares the indexes of the spec, the purge ones being partial', async () => {
31
+ const indexes = await testDataSource.query(`SELECT indexname, indexdef FROM pg_indexes WHERE schemaname = 'public' AND tablename = 'files'`);
32
+ const byName = new Map(indexes.map((index) => [index.indexname, index.indexdef]));
33
+ for (const name of ['idx_files_entity', 'idx_files_owner']) {
34
+ expect(byName.get(name)).toBeDefined();
35
+ }
36
+ for (const name of [
37
+ 'idx_files_awaiting',
38
+ 'idx_files_scanning',
39
+ 'idx_files_unused',
40
+ 'idx_files_reserved',
41
+ 'idx_files_orphaned',
42
+ ]) {
43
+ expect(byName.get(name)).toContain('WHERE');
44
+ }
45
+ });
46
+ it('declares the same indexes on the model as in the migration', () => {
47
+ const modelIndexes = testDataSource
48
+ .getMetadata(FileModel)
49
+ .indices.map((index) => index.name)
50
+ .sort();
51
+ expect(modelIndexes).toEqual([
52
+ 'idx_files_awaiting',
53
+ 'idx_files_entity',
54
+ 'idx_files_orphaned',
55
+ 'idx_files_owner',
56
+ 'idx_files_reserved',
57
+ 'idx_files_scanning',
58
+ 'idx_files_unused',
59
+ ]);
60
+ });
61
+ });
62
+ describe('FileModel', () => {
63
+ const repository = () => testDataSource.getRepository(FileModel);
64
+ it('applies the database defaults on a minimal insert', async () => {
65
+ const data = buildFileData();
66
+ await repository().save(repository().create(data));
67
+ const saved = await repository().findOneByOrFail({ id: data.id });
68
+ expect(saved.status).toBe(FileStatus.PENDING);
69
+ expect(saved.scanStatus).toBe(ScanStatus.AWAITING_UPLOAD);
70
+ expect(saved.scanAttempts).toBe(0);
71
+ expect(saved.entityId).toBeNull();
72
+ expect(saved.rejectionReason).toBeNull();
73
+ expect(saved.actualSize).toBeNull();
74
+ expect(saved.publicKey).toBeNull();
75
+ expect(saved.rescanScheduledAt).toBeNull();
76
+ expect(saved.confirmedAt).toBeNull();
77
+ expect(saved.scannedAt).toBeNull();
78
+ expect(saved.reservedAt).toBeNull();
79
+ expect(saved.attachedAt).toBeNull();
80
+ expect(saved.createdAt).toBeInstanceOf(Date);
81
+ expect(saved.updatedAt).toBeInstanceOf(Date);
82
+ });
83
+ it('reads bigint sizes back as numbers', async () => {
84
+ const data = buildFileData({ declaredSize: 10 * 1024 * 1024, actualSize: 9_999_999 });
85
+ await repository().save(repository().create(data));
86
+ const saved = await repository().findOneByOrFail({ id: data.id });
87
+ expect(saved.declaredSize).toBe(10 * 1024 * 1024);
88
+ expect(saved.actualSize).toBe(9_999_999);
89
+ });
90
+ it('round-trips a fully populated row', async () => {
91
+ const entityId = FileId.generate().getValue();
92
+ const now = new Date('2026-10-06T10:00:00.000Z');
93
+ const data = buildFileData({
94
+ entityType: EntityType.EVENT_COVER,
95
+ entityId,
96
+ status: FileStatus.ORPHANED,
97
+ scanStatus: ScanStatus.REJECTED,
98
+ rejectionReason: RejectionReason.SCAN_TIMEOUT,
99
+ publicKey: 'event-cover/public.webp',
100
+ scanAttempts: 2,
101
+ rescanScheduledAt: now,
102
+ validationMode: ValidationMode.ASYNC,
103
+ confirmedAt: now,
104
+ scannedAt: now,
105
+ reservedAt: now,
106
+ attachedAt: now,
107
+ });
108
+ await repository().save(repository().create(data));
109
+ const saved = await repository().findOneByOrFail({ id: data.id });
110
+ expect(saved).toMatchObject({
111
+ entityType: EntityType.EVENT_COVER,
112
+ entityId,
113
+ status: FileStatus.ORPHANED,
114
+ scanStatus: ScanStatus.REJECTED,
115
+ rejectionReason: RejectionReason.SCAN_TIMEOUT,
116
+ publicKey: 'event-cover/public.webp',
117
+ scanAttempts: 2,
118
+ validationMode: ValidationMode.ASYNC,
119
+ });
120
+ expect(saved.rescanScheduledAt?.toISOString()).toBe(now.toISOString());
121
+ expect(saved.attachedAt?.toISOString()).toBe(now.toISOString());
122
+ });
123
+ it('refreshes updated_at on update', async () => {
124
+ const data = buildFileData();
125
+ const created = await repository().save(repository().create(data));
126
+ const initialUpdatedAt = created.updatedAt.getTime();
127
+ await new Promise((resolve) => setTimeout(resolve, 20));
128
+ await repository().update({ id: data.id }, { status: FileStatus.RESERVED });
129
+ const updated = await repository().findOneByOrFail({ id: data.id });
130
+ expect(updated.status).toBe(FileStatus.RESERVED);
131
+ expect(updated.updatedAt.getTime()).toBeGreaterThan(initialUpdatedAt);
132
+ });
133
+ it('rejects a second row with the same id', async () => {
134
+ const data = buildFileData();
135
+ await repository().insert(repository().create(data));
136
+ await expect(repository().insert(repository().create(data))).rejects.toThrow();
137
+ });
138
+ it('rejects a row without its NOT NULL columns', async () => {
139
+ const incomplete = buildFileData({ quarantineKey: undefined });
140
+ await expect(repository().insert(repository().create(incomplete))).rejects.toThrow();
141
+ });
142
+ });
143
+ describe('ReleasedEntityModel', () => {
144
+ const repository = () => testDataSource.getRepository(ReleasedEntityModel);
145
+ it('defaults released_at on insert', async () => {
146
+ const entityId = FileId.generate().getValue();
147
+ await repository().insert({ entityType: EntityType.POST, entityId });
148
+ const saved = await repository().findOneByOrFail({ entityType: EntityType.POST, entityId });
149
+ expect(saved.releasedAt).toBeInstanceOf(Date);
150
+ });
151
+ it('uses (entity_type, entity_id) as primary key', async () => {
152
+ const entityId = FileId.generate().getValue();
153
+ await repository().insert({ entityType: EntityType.POST, entityId });
154
+ await expect(repository().insert({ entityType: EntityType.POST, entityId })).rejects.toThrow();
155
+ await expect(repository().insert({ entityType: EntityType.EVENT_COVER, entityId })).resolves.toBeDefined();
156
+ });
157
+ });
158
+ });
159
+ //# sourceMappingURL=file.model.int.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file.model.int.spec.js","sourceRoot":"","sources":["../../../src/test/integration/file.model.int.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { MigrationInterface, QueryRunner } from 'typeorm';
2
+ export declare class InitialStorageSchema1791300000000 implements MigrationInterface {
3
+ name: string;
4
+ up(queryRunner: QueryRunner): Promise<void>;
5
+ down(queryRunner: QueryRunner): Promise<void>;
6
+ }
7
+ //# sourceMappingURL=1791300000000-InitialStorageSchema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"1791300000000-InitialStorageSchema.d.ts","sourceRoot":"","sources":["../../../../src/test/migrations/domain/1791300000000-InitialStorageSchema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAM/D,qBAAa,iCAAkC,YAAW,kBAAkB;IAC1E,IAAI,SAAuC;IAE9B,EAAE,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IA0D3C,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAW3D"}
@@ -0,0 +1,55 @@
1
+ export class InitialStorageSchema1791300000000 {
2
+ name = 'InitialStorageSchema1791300000000';
3
+ async up(queryRunner) {
4
+ await queryRunner.query(`CREATE TABLE "files" (
5
+ "id" uuid NOT NULL,
6
+ "owner_id" uuid NOT NULL,
7
+ "entity_type" character varying NOT NULL,
8
+ "entity_id" uuid,
9
+ "status" character varying NOT NULL DEFAULT 'PENDING',
10
+ "scan_status" character varying NOT NULL DEFAULT 'AWAITING_UPLOAD',
11
+ "rejection_reason" character varying,
12
+ "declared_mime_type" character varying NOT NULL,
13
+ "declared_size" bigint NOT NULL,
14
+ "actual_size" bigint,
15
+ "quarantine_key" character varying NOT NULL,
16
+ "public_key" character varying,
17
+ "scan_attempts" integer NOT NULL DEFAULT 0,
18
+ "rescan_scheduled_at" TIMESTAMP WITH TIME ZONE,
19
+ "validation_mode" character varying NOT NULL,
20
+ "upload_expires_at" TIMESTAMP WITH TIME ZONE NOT NULL,
21
+ "confirmed_at" TIMESTAMP WITH TIME ZONE,
22
+ "scanned_at" TIMESTAMP WITH TIME ZONE,
23
+ "reserved_at" TIMESTAMP WITH TIME ZONE,
24
+ "attached_at" TIMESTAMP WITH TIME ZONE,
25
+ "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
26
+ "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
27
+ CONSTRAINT "PK_files_id" PRIMARY KEY ("id")
28
+ )`);
29
+ await queryRunner.query(`CREATE INDEX "idx_files_entity" ON "files" ("entity_type", "entity_id")`);
30
+ await queryRunner.query(`CREATE INDEX "idx_files_owner" ON "files" ("owner_id")`);
31
+ await queryRunner.query(`CREATE INDEX "idx_files_awaiting" ON "files" ("upload_expires_at") WHERE "scan_status" = 'AWAITING_UPLOAD'`);
32
+ await queryRunner.query(`CREATE INDEX "idx_files_scanning" ON "files" ("confirmed_at") WHERE "scan_status" = 'SCANNING'`);
33
+ await queryRunner.query(`CREATE INDEX "idx_files_unused" ON "files" ("confirmed_at") WHERE "status" = 'PENDING'`);
34
+ await queryRunner.query(`CREATE INDEX "idx_files_reserved" ON "files" ("reserved_at") WHERE "status" = 'RESERVED'`);
35
+ await queryRunner.query(`CREATE INDEX "idx_files_orphaned" ON "files" ("updated_at") WHERE "status" = 'ORPHANED'`);
36
+ await queryRunner.query(`CREATE TABLE "released_entities" (
37
+ "entity_type" character varying NOT NULL,
38
+ "entity_id" uuid NOT NULL,
39
+ "released_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
40
+ CONSTRAINT "PK_released_entities" PRIMARY KEY ("entity_type", "entity_id")
41
+ )`);
42
+ }
43
+ async down(queryRunner) {
44
+ await queryRunner.query(`DROP TABLE "released_entities"`);
45
+ await queryRunner.query(`DROP INDEX "public"."idx_files_orphaned"`);
46
+ await queryRunner.query(`DROP INDEX "public"."idx_files_reserved"`);
47
+ await queryRunner.query(`DROP INDEX "public"."idx_files_unused"`);
48
+ await queryRunner.query(`DROP INDEX "public"."idx_files_scanning"`);
49
+ await queryRunner.query(`DROP INDEX "public"."idx_files_awaiting"`);
50
+ await queryRunner.query(`DROP INDEX "public"."idx_files_owner"`);
51
+ await queryRunner.query(`DROP INDEX "public"."idx_files_entity"`);
52
+ await queryRunner.query(`DROP TABLE "files"`);
53
+ }
54
+ }
55
+ //# sourceMappingURL=1791300000000-InitialStorageSchema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"1791300000000-InitialStorageSchema.js","sourceRoot":"","sources":["../../../../src/test/migrations/domain/1791300000000-InitialStorageSchema.ts"],"names":[],"mappings":"AAMA,MAAM,OAAO,iCAAiC;IAC5C,IAAI,GAAG,mCAAmC,CAAC;IAEpC,KAAK,CAAC,EAAE,CAAC,WAAwB;QACtC,MAAM,WAAW,CAAC,KAAK,CACrB;;;;;;;;;;;;;;;;;;;;;;;;QAwBE,CACH,CAAC;QACF,MAAM,WAAW,CAAC,KAAK,CACrB,yEAAyE,CAC1E,CAAC;QACF,MAAM,WAAW,CAAC,KAAK,CAAC,wDAAwD,CAAC,CAAC;QAClF,MAAM,WAAW,CAAC,KAAK,CACrB,4GAA4G,CAC7G,CAAC;QACF,MAAM,WAAW,CAAC,KAAK,CACrB,gGAAgG,CACjG,CAAC;QACF,MAAM,WAAW,CAAC,KAAK,CACrB,wFAAwF,CACzF,CAAC;QACF,MAAM,WAAW,CAAC,KAAK,CACrB,0FAA0F,CAC3F,CAAC;QACF,MAAM,WAAW,CAAC,KAAK,CACrB,yFAAyF,CAC1F,CAAC;QAEF,MAAM,WAAW,CAAC,KAAK,CACrB;;;;;QAKE,CACH,CAAC;IACJ,CAAC;IAEM,KAAK,CAAC,IAAI,CAAC,WAAwB;QACxC,MAAM,WAAW,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;QAC1D,MAAM,WAAW,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC;QACpE,MAAM,WAAW,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC;QACpE,MAAM,WAAW,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAC;QAClE,MAAM,WAAW,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC;QACpE,MAAM,WAAW,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC;QACpE,MAAM,WAAW,CAAC,KAAK,CAAC,uCAAuC,CAAC,CAAC;QACjE,MAAM,WAAW,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAC;QAClE,MAAM,WAAW,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;IAChD,CAAC;CACF"}
@@ -0,0 +1,2 @@
1
+ import 'reflect-metadata';
2
+ //# sourceMappingURL=setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/test/setup.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC"}
@@ -0,0 +1,2 @@
1
+ import 'reflect-metadata';
2
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/test/setup.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volontariapp/domain-storage",
3
- "version": "0.5.0",
3
+ "version": "0.6.0-snap-87ccfd8",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "provenance": true
@@ -30,7 +30,13 @@
30
30
  "prepublishOnly": "yarn build",
31
31
  "build": "tsc -p tsconfig.json",
32
32
  "lint": "eslint src/",
33
- "test": "node --experimental-vm-modules ../../node_modules/.bin/jest"
33
+ "test": "node --experimental-vm-modules ../../node_modules/.bin/jest",
34
+ "test:integration": "node --experimental-vm-modules ../../node_modules/.bin/jest --config jest.integration.config.js --runInBand",
35
+ "test:all": "yarn test && yarn test:integration",
36
+ "test:coverage": "node --experimental-vm-modules ../../node_modules/.bin/jest --coverage --coverageReporters=\"text\" --coverageReporters=\"json-summary\" --coverageReporters=\"lcov\" --coverageReporters=\"json\"",
37
+ "test:coverage:integration": "node --experimental-vm-modules ../../node_modules/.bin/jest --config jest.integration.config.js --coverage --coverageReporters=\"text\" --coverageReporters=\"json-summary\" --coverageReporters=\"lcov\" --coverageReporters=\"json\" --runInBand",
38
+ "typeorm": "typeorm-ts-node-esm",
39
+ "migration:run": "TYPEORM_MIGRATION_RUN=true yarn typeorm migration:run -d src/test/data-source.ts"
34
40
  },
35
41
  "dependencies": {
36
42
  "@volontariapp/errors": "0.6.2"
@@ -40,7 +46,11 @@
40
46
  "@types/jest": "^30.0.0",
41
47
  "@types/node": "^22.10.7",
42
48
  "jest": "^30.3.0",
49
+ "pg": "^8.20.0",
50
+ "reflect-metadata": "^0.2.2",
43
51
  "ts-jest": "^29.4.6",
52
+ "ts-node": "^10.9.2",
53
+ "typeorm": "^0.3.28",
44
54
  "typescript": "5.7.3"
45
55
  }
46
56
  }