@volontariapp/domain-storage 0.4.0-snap-01438ee → 0.5.0-snap-d9332f0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Add pure helpers `buildQuarantineObjectKey`, `buildPublicObjectKey` and `buildPublicFileUrl` to compute object keys and public URLs without any network call.
8
+
3
9
  ## 0.4.0
4
10
 
5
11
  ### Minor Changes
@@ -0,0 +1,2 @@
1
+ export * from './object-key.helpers.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/helpers/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from './object-key.helpers.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/helpers/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { EntityType } from '../enums/entity-type.enum.js';
2
+ export declare const QUARANTINE_KEY_PREFIX = "quarantine";
3
+ export declare function buildQuarantineObjectKey(fileId: string): string;
4
+ export declare function buildPublicObjectKey(entityType: EntityType, fileId: string): string;
5
+ export declare function buildPublicFileUrl(entityType: EntityType, fileId: string, baseUrl: string): string;
6
+ //# sourceMappingURL=object-key.helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"object-key.helpers.d.ts","sourceRoot":"","sources":["../../src/helpers/object-key.helpers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAI/D,eAAO,MAAM,qBAAqB,eAAe,CAAC;AAiBlD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE/D;AAMD,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAInF;AAMD,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACd,MAAM,CAQR"}
@@ -0,0 +1,33 @@
1
+ import { BadRequestError } from '@volontariapp/errors';
2
+ import { VALIDATION_POLICY_BY_ENTITY } from '../policies/validation-policy.constants.js';
3
+ import { FileId } from '../value-objects/file-id.vo.js';
4
+ export const QUARANTINE_KEY_PREFIX = 'quarantine';
5
+ function resolveFileId(fileId) {
6
+ return FileId.create(fileId).getValue();
7
+ }
8
+ function toEntityFolder(entityType) {
9
+ if (!Object.hasOwn(VALIDATION_POLICY_BY_ENTITY, entityType)) {
10
+ throw new BadRequestError(`Unknown entity type: '${entityType}'`, 'INVALID_ENTITY_TYPE', {
11
+ entityType,
12
+ });
13
+ }
14
+ return entityType.toLowerCase().replaceAll('_', '-');
15
+ }
16
+ export function buildQuarantineObjectKey(fileId) {
17
+ return `${QUARANTINE_KEY_PREFIX}/${resolveFileId(fileId)}`;
18
+ }
19
+ export function buildPublicObjectKey(entityType, fileId) {
20
+ const folder = toEntityFolder(entityType);
21
+ const { format } = VALIDATION_POLICY_BY_ENTITY[entityType].output;
22
+ return `${folder}/${resolveFileId(fileId)}.${format}`;
23
+ }
24
+ export function buildPublicFileUrl(entityType, fileId, baseUrl) {
25
+ const normalizedBaseUrl = typeof baseUrl === 'string' ? baseUrl.trim().replace(/\/+$/, '') : '';
26
+ if (normalizedBaseUrl === '') {
27
+ throw new BadRequestError('Public base URL must not be empty', 'INVALID_PUBLIC_BASE_URL', {
28
+ baseUrl,
29
+ });
30
+ }
31
+ return `${normalizedBaseUrl}/${buildPublicObjectKey(entityType, fileId)}`;
32
+ }
33
+ //# sourceMappingURL=object-key.helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"object-key.helpers.js","sourceRoot":"","sources":["../../src/helpers/object-key.helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,OAAO,EAAE,2BAA2B,EAAE,MAAM,4CAA4C,CAAC;AACzF,OAAO,EAAE,MAAM,EAAE,MAAM,gCAAgC,CAAC;AAExD,MAAM,CAAC,MAAM,qBAAqB,GAAG,YAAY,CAAC;AAElD,SAAS,aAAa,CAAC,MAAc;IACnC,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC;AAC1C,CAAC;AAGD,SAAS,cAAc,CAAC,UAAsB;IAC5C,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,2BAA2B,EAAE,UAAU,CAAC,EAAE,CAAC;QAC5D,MAAM,IAAI,eAAe,CAAC,yBAAyB,UAAU,GAAG,EAAE,qBAAqB,EAAE;YACvF,UAAU;SACX,CAAC,CAAC;IACL,CAAC;IACD,OAAO,UAAU,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AACvD,CAAC;AAGD,MAAM,UAAU,wBAAwB,CAAC,MAAc;IACrD,OAAO,GAAG,qBAAqB,IAAI,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;AAC7D,CAAC;AAMD,MAAM,UAAU,oBAAoB,CAAC,UAAsB,EAAE,MAAc;IACzE,MAAM,MAAM,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;IAC1C,MAAM,EAAE,MAAM,EAAE,GAAG,2BAA2B,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC;IAClE,OAAO,GAAG,MAAM,IAAI,aAAa,CAAC,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;AACxD,CAAC;AAMD,MAAM,UAAU,kBAAkB,CAChC,UAAsB,EACtB,MAAc,EACd,OAAe;IAEf,MAAM,iBAAiB,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAChG,IAAI,iBAAiB,KAAK,EAAE,EAAE,CAAC;QAC7B,MAAM,IAAI,eAAe,CAAC,mCAAmC,EAAE,yBAAyB,EAAE;YACxF,OAAO;SACR,CAAC,CAAC;IACL,CAAC;IACD,OAAO,GAAG,iBAAiB,IAAI,oBAAoB,CAAC,UAAU,EAAE,MAAM,CAAC,EAAE,CAAC;AAC5E,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from './contracts/index.js';
2
2
  export * from './enums/index.js';
3
3
  export * from './exceptions/index.js';
4
+ export * from './helpers/index.js';
4
5
  export * from './policies/index.js';
5
6
  export * from './value-objects/index.js';
6
7
  //# 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,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,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC"}
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from './contracts/index.js';
2
2
  export * from './enums/index.js';
3
3
  export * from './exceptions/index.js';
4
+ export * from './helpers/index.js';
4
5
  export * from './policies/index.js';
5
6
  export * from './value-objects/index.js';
6
7
  //# 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,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,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=object-key.helpers.unit.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"object-key.helpers.unit.spec.d.ts","sourceRoot":"","sources":["../../src/test/object-key.helpers.unit.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,66 @@
1
+ import { describe, expect, it } from '@jest/globals';
2
+ import { EntityType, VALIDATION_POLICY_BY_ENTITY, buildPublicFileUrl, buildPublicObjectKey, buildQuarantineObjectKey, } from '../index.js';
3
+ const FILE_ID = '3f2b8c1e-5d4a-4e7b-9a6c-1d2e3f4a5b6c';
4
+ const BASE_URL = 'https://cdn.volontariapp.com';
5
+ const EXPECTED_KEYS = [
6
+ [EntityType.POST, `post/${FILE_ID}.webp`],
7
+ [EntityType.EVENT_COVER, `event-cover/${FILE_ID}.webp`],
8
+ [EntityType.USER_AVATAR, `user-avatar/${FILE_ID}.webp`],
9
+ [EntityType.BADGE_ICON, `badge-icon/${FILE_ID}.png`],
10
+ ];
11
+ describe('Object key helpers', () => {
12
+ describe('buildQuarantineObjectKey', () => {
13
+ it('should prefix the file id with quarantine/', () => {
14
+ expect(buildQuarantineObjectKey(FILE_ID)).toBe(`quarantine/${FILE_ID}`);
15
+ });
16
+ it('should be deterministic', () => {
17
+ expect(buildQuarantineObjectKey(FILE_ID)).toBe(buildQuarantineObjectKey(FILE_ID));
18
+ });
19
+ it.each(['', 'not-a-uuid', '../etc/passwd', `${FILE_ID}/x`])('should reject the invalid file id %p', (invalid) => {
20
+ expect(() => buildQuarantineObjectKey(invalid)).toThrow();
21
+ });
22
+ });
23
+ describe('buildPublicObjectKey', () => {
24
+ it.each(EXPECTED_KEYS)('should build the key of %s', (entityType, expected) => {
25
+ expect(buildPublicObjectKey(entityType, FILE_ID)).toBe(expected);
26
+ });
27
+ it('should use the output format of the validation policy as extension', () => {
28
+ for (const entityType of Object.values(EntityType)) {
29
+ const { format } = VALIDATION_POLICY_BY_ENTITY[entityType].output;
30
+ expect(buildPublicObjectKey(entityType, FILE_ID).endsWith(`.${format}`)).toBe(true);
31
+ }
32
+ });
33
+ it('should be deterministic', () => {
34
+ expect(buildPublicObjectKey(EntityType.POST, FILE_ID)).toBe(buildPublicObjectKey(EntityType.POST, FILE_ID));
35
+ });
36
+ it('should reject an invalid file id', () => {
37
+ expect(() => buildPublicObjectKey(EntityType.POST, 'nope')).toThrow();
38
+ });
39
+ it('should reject an unknown entity type', () => {
40
+ expect(() => buildPublicObjectKey('UNKNOWN', FILE_ID)).toThrow();
41
+ });
42
+ });
43
+ describe('buildPublicFileUrl', () => {
44
+ it.each(EXPECTED_KEYS)('should build the URL of %s', (entityType, key) => {
45
+ expect(buildPublicFileUrl(entityType, FILE_ID, BASE_URL)).toBe(`${BASE_URL}/${key}`);
46
+ });
47
+ it('should ignore trailing slashes of the base URL', () => {
48
+ const expected = `${BASE_URL}/post/${FILE_ID}.webp`;
49
+ expect(buildPublicFileUrl(EntityType.POST, FILE_ID, `${BASE_URL}/`)).toBe(expected);
50
+ expect(buildPublicFileUrl(EntityType.POST, FILE_ID, `${BASE_URL}///`)).toBe(expected);
51
+ });
52
+ it('should keep a path prefix in the base URL', () => {
53
+ expect(buildPublicFileUrl(EntityType.BADGE_ICON, FILE_ID, 'http://localhost:9000/pub/')).toBe(`http://localhost:9000/pub/badge-icon/${FILE_ID}.png`);
54
+ });
55
+ it('should be deterministic', () => {
56
+ expect(buildPublicFileUrl(EntityType.POST, FILE_ID, BASE_URL)).toBe(buildPublicFileUrl(EntityType.POST, FILE_ID, BASE_URL));
57
+ });
58
+ it.each(['', ' ', '/', '///'])('should reject the empty base URL %p', (baseUrl) => {
59
+ expect(() => buildPublicFileUrl(EntityType.POST, FILE_ID, baseUrl)).toThrow();
60
+ });
61
+ it('should reject an invalid file id', () => {
62
+ expect(() => buildPublicFileUrl(EntityType.POST, 'nope', BASE_URL)).toThrow();
63
+ });
64
+ });
65
+ });
66
+ //# sourceMappingURL=object-key.helpers.unit.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"object-key.helpers.unit.spec.js","sourceRoot":"","sources":["../../src/test/object-key.helpers.unit.spec.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volontariapp/domain-storage",
3
- "version": "0.4.0-snap-01438ee",
3
+ "version": "0.5.0-snap-d9332f0",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "provenance": true