@proteinjs/db-file 1.8.3 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/dist/generated/index.d.ts.map +1 -1
  3. package/dist/generated/index.js +3 -1
  4. package/dist/generated/index.js.map +1 -1
  5. package/dist/index.d.ts +4 -0
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +4 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/src/DbFileStorageDriver.d.ts +7 -0
  10. package/dist/src/DbFileStorageDriver.d.ts.map +1 -1
  11. package/dist/src/DbFileStorageDriver.js +69 -41
  12. package/dist/src/DbFileStorageDriver.js.map +1 -1
  13. package/dist/src/FileCopyForOthers.d.ts +52 -0
  14. package/dist/src/FileCopyForOthers.d.ts.map +1 -0
  15. package/dist/src/FileCopyForOthers.js +62 -0
  16. package/dist/src/FileCopyForOthers.js.map +1 -0
  17. package/dist/src/FileStorage.d.ts +145 -11
  18. package/dist/src/FileStorage.d.ts.map +1 -1
  19. package/dist/src/FileStorage.js +537 -41
  20. package/dist/src/FileStorage.js.map +1 -1
  21. package/dist/src/FileStorageDriver.d.ts +11 -0
  22. package/dist/src/FileStorageDriver.d.ts.map +1 -1
  23. package/dist/src/FileStorageError.d.ts +62 -0
  24. package/dist/src/FileStorageError.d.ts.map +1 -0
  25. package/dist/src/FileStorageError.js +118 -0
  26. package/dist/src/FileStorageError.js.map +1 -0
  27. package/dist/src/FileVariantMaker.d.ts +61 -0
  28. package/dist/src/FileVariantMaker.d.ts.map +1 -0
  29. package/dist/src/FileVariantMaker.js +62 -0
  30. package/dist/src/FileVariantMaker.js.map +1 -0
  31. package/dist/src/routes/FileResponder.d.ts +41 -0
  32. package/dist/src/routes/FileResponder.d.ts.map +1 -0
  33. package/dist/src/routes/FileResponder.js +135 -0
  34. package/dist/src/routes/FileResponder.js.map +1 -0
  35. package/dist/src/routes/getFile.d.ts +5 -13
  36. package/dist/src/routes/getFile.d.ts.map +1 -1
  37. package/dist/src/routes/getFile.js +20 -56
  38. package/dist/src/routes/getFile.js.map +1 -1
  39. package/dist/src/routes/getFileVariant.d.ts +20 -0
  40. package/dist/src/routes/getFileVariant.d.ts.map +1 -0
  41. package/dist/src/routes/getFileVariant.js +112 -0
  42. package/dist/src/routes/getFileVariant.js.map +1 -0
  43. package/dist/src/routes/getFileVariantRoute.d.ts +7 -0
  44. package/dist/src/routes/getFileVariantRoute.d.ts.map +1 -0
  45. package/dist/src/routes/getFileVariantRoute.js +9 -0
  46. package/dist/src/routes/getFileVariantRoute.js.map +1 -0
  47. package/dist/src/tables/FileTable.d.ts +55 -2
  48. package/dist/src/tables/FileTable.d.ts.map +1 -1
  49. package/dist/src/tables/FileTable.js +8 -0
  50. package/dist/src/tables/FileTable.js.map +1 -1
  51. package/dist/test/DbFileStorageDriverMissingBytes.test.d.ts +2 -0
  52. package/dist/test/DbFileStorageDriverMissingBytes.test.d.ts.map +1 -0
  53. package/dist/test/DbFileStorageDriverMissingBytes.test.js +184 -0
  54. package/dist/test/DbFileStorageDriverMissingBytes.test.js.map +1 -0
  55. package/dist/test/FileByteAccess.test.js +6 -3
  56. package/dist/test/FileByteAccess.test.js.map +1 -1
  57. package/dist/test/FileCopyForOthers.test.d.ts +2 -0
  58. package/dist/test/FileCopyForOthers.test.d.ts.map +1 -0
  59. package/dist/test/FileCopyForOthers.test.js +1143 -0
  60. package/dist/test/FileCopyForOthers.test.js.map +1 -0
  61. package/dist/test/FileMediaMetadata.test.js +60 -5
  62. package/dist/test/FileMediaMetadata.test.js.map +1 -1
  63. package/dist/test/FileStorageError.test.d.ts +2 -0
  64. package/dist/test/FileStorageError.test.d.ts.map +1 -0
  65. package/dist/test/FileStorageError.test.js +68 -0
  66. package/dist/test/FileStorageError.test.js.map +1 -0
  67. package/dist/test/FileTestEnvironment.d.ts.map +1 -1
  68. package/dist/test/FileTestEnvironment.js +3 -0
  69. package/dist/test/FileTestEnvironment.js.map +1 -1
  70. package/dist/test/FileVariants.test.d.ts +2 -0
  71. package/dist/test/FileVariants.test.d.ts.map +1 -0
  72. package/dist/test/FileVariants.test.js +1008 -0
  73. package/dist/test/FileVariants.test.js.map +1 -0
  74. package/dist/test/GetFileRoute.test.js +29 -0
  75. package/dist/test/GetFileRoute.test.js.map +1 -1
  76. package/generated/index.ts +3 -1
  77. package/index.ts +4 -0
  78. package/package.json +5 -5
  79. package/src/DbFileStorageDriver.ts +32 -21
  80. package/src/FileCopyForOthers.ts +71 -0
  81. package/src/FileStorage.ts +351 -18
  82. package/src/FileStorageDriver.ts +12 -0
  83. package/src/FileStorageError.ts +123 -0
  84. package/src/FileVariantMaker.ts +84 -0
  85. package/src/routes/FileResponder.ts +85 -0
  86. package/src/routes/getFile.ts +9 -44
  87. package/src/routes/getFileVariant.ts +53 -0
  88. package/src/routes/getFileVariantRoute.ts +7 -0
  89. package/src/tables/FileTable.ts +57 -1
  90. package/test/DbFileStorageDriverMissingBytes.test.ts +85 -0
  91. package/test/FileByteAccess.test.ts +7 -1
  92. package/test/FileCopyForOthers.test.ts +702 -0
  93. package/test/FileMediaMetadata.test.ts +44 -0
  94. package/test/FileStorageError.test.ts +79 -0
  95. package/test/FileTestEnvironment.ts +3 -0
  96. package/test/FileVariants.test.ts +604 -0
  97. package/test/GetFileRoute.test.ts +19 -0
@@ -60,6 +60,49 @@ describe('File media metadata columns', () => {
60
60
  expect(row.durationMs).toEqual(24_500);
61
61
  });
62
62
 
63
+ it('round-trips the producer attribution — how the bytes came to exist (`origin`)', async () => {
64
+ const file = await new FileStorage().createFile(
65
+ { name: 'frame.png', type: 'image/png', size: 4, width: 1440, height: 900, origin: 'mockup' } as File,
66
+ Buffer.from('png!').toString('base64')
67
+ );
68
+
69
+ const row = await getDbAsSystem().get(tables.File, { id: file.id });
70
+ expect(row.origin).toEqual('mockup');
71
+ // Absent for everything that does not state its producer (a NULL column reads back empty).
72
+ const plain = await new FileStorage().createFile(
73
+ { name: 'note.txt', type: 'text/plain', size: 2 } as File,
74
+ Buffer.from('hi').toString('base64')
75
+ );
76
+ expect((await getDbAsSystem().get(tables.File, { id: plain.id })).origin).toBeFalsy();
77
+ });
78
+
79
+ it('round-trips the model beside the producer — which model made the bytes (`originModel`), only where one did', async () => {
80
+ const made = await new FileStorage().createFile(
81
+ {
82
+ name: 'mark.png',
83
+ type: 'image/png',
84
+ size: 4,
85
+ width: 1024,
86
+ height: 1024,
87
+ origin: 'generation',
88
+ originModel: 'example-image-model-1',
89
+ } as File,
90
+ Buffer.from('mark').toString('base64')
91
+ );
92
+ const madeRow = await getDbAsSystem().get(tables.File, { id: made.id });
93
+ expect(madeRow.origin).toEqual('generation');
94
+ expect(madeRow.originModel).toEqual('example-image-model-1');
95
+
96
+ // A producer that is not a model states its kind and no model: the two columns are independent.
97
+ const uploaded = await new FileStorage().createFile(
98
+ { name: 'photo.jpg', type: 'image/jpeg', size: 5, origin: 'upload' } as File,
99
+ Buffer.from('photo').toString('base64')
100
+ );
101
+ const uploadedRow = await getDbAsSystem().get(tables.File, { id: uploaded.id });
102
+ expect(uploadedRow.origin).toEqual('upload');
103
+ expect(uploadedRow.originModel).toBeFalsy();
104
+ });
105
+
63
106
  it('round-trips the rights record on a web-saved copy — licence name, deed URL and the credit sentence', async () => {
64
107
  const file = await new FileStorage().createFile(
65
108
  {
@@ -97,5 +140,6 @@ describe('File media metadata columns', () => {
97
140
  expect(row.durationMs ?? undefined).toBeUndefined();
98
141
  expect(row.license ?? undefined).toBeUndefined();
99
142
  expect(row.attribution ?? undefined).toBeUndefined();
143
+ expect(row.originModel ?? undefined).toBeUndefined();
100
144
  });
101
145
  });
@@ -0,0 +1,79 @@
1
+ import util from 'util';
2
+ import { FileStorageError } from '../src/FileStorageError';
3
+
4
+ /** An obviously fake credential — what a store client's error carries in its request headers. */
5
+ const MARKER = 'FAKE-TOKEN-MARKER';
6
+
7
+ /**
8
+ * What a driver's error carries is the driver's to own: a code, a plain message, the HTTP status.
9
+ * Whatever a caller does with one — print it, serialise it, inspect it — reads those three facts.
10
+ */
11
+ describe('FileStorageError', () => {
12
+ it('carries the code, a plain message and the status — and nothing else', () => {
13
+ const error = new FileStorageError('not-found', 'getFileData failed for file file-1', {
14
+ status: 404,
15
+ detail: 'No such object: a-bucket/file-1',
16
+ });
17
+
18
+ expect(error).toBeInstanceOf(Error);
19
+ expect(error.code).toEqual('not-found');
20
+ expect(error.status).toEqual(404);
21
+ expect(error.message).toEqual(
22
+ 'getFileData failed for file file-1: not-found (HTTP 404) — No such object: a-bucket/file-1'
23
+ );
24
+ expect(Object.keys(error).sort()).toEqual(['code', 'name', 'status']);
25
+ expect(Object.getOwnPropertyNames(error).sort()).toEqual(['code', 'message', 'name', 'stack', 'status']);
26
+ });
27
+
28
+ it('never reads an object into its message — only text is kept as the store’s detail', () => {
29
+ const vendorError = { message: 'boom', config: { headers: { Authorization: `Bearer ${MARKER}` } } };
30
+
31
+ const error = new FileStorageError('unknown', 'createFile failed for file file-2', { detail: vendorError });
32
+
33
+ expect(error.message).toEqual('createFile failed for file file-2: unknown');
34
+ expect(util.inspect(error, { depth: null, showHidden: true })).not.toContain(MARKER);
35
+ });
36
+
37
+ it('keeps a store’s text plain: one line, bounded, no URL query string, no bearer credential', () => {
38
+ const detail =
39
+ `Request to https://storage.example/b/a-bucket/o/file-3?X-Goog-Signature=${MARKER}&X-Goog-Expires=900 failed\n` +
40
+ `with headers Authorization: Bearer ${MARKER} ` +
41
+ 'x'.repeat(1000);
42
+
43
+ const error = new FileStorageError('unavailable', 'getSignedUrl failed for file file-3', { status: 503, detail });
44
+
45
+ expect(error.message).not.toContain(MARKER);
46
+ expect(error.message).not.toContain('\n');
47
+ expect(error.message).toContain('https://storage.example/b/a-bucket/o/file-3 failed');
48
+ expect(error.message).toContain('Bearer [masked]');
49
+ expect(error.message.length).toBeLessThan(400);
50
+ });
51
+
52
+ it('is recognised by its shape, so a copy from a duplicate package still reads as not-found', () => {
53
+ const fromAnotherCopy = Object.assign(new Error('getFileData failed'), {
54
+ name: 'FileStorageError',
55
+ code: 'not-found',
56
+ });
57
+
58
+ expect(FileStorageError.is(fromAnotherCopy)).toBe(true);
59
+ expect(FileStorageError.isNotFound(fromAnotherCopy)).toBe(true);
60
+ expect(FileStorageError.isNotFound(new FileStorageError('forbidden', 'getFileData failed'))).toBe(false);
61
+ expect(FileStorageError.is(Object.assign(new Error('a vendor error'), { code: 404 }))).toBe(false);
62
+ expect(FileStorageError.is(Object.assign(new Error('x'), { name: 'FileStorageError', code: 'made-up' }))).toBe(
63
+ false
64
+ );
65
+ expect(FileStorageError.is(undefined)).toBe(false);
66
+ });
67
+
68
+ it('reads one HTTP status one way', () => {
69
+ expect(FileStorageError.codeForStatus(404)).toEqual('not-found');
70
+ expect(FileStorageError.codeForStatus(412)).toEqual('precondition-failed');
71
+ expect(FileStorageError.codeForStatus(401)).toEqual('forbidden');
72
+ expect(FileStorageError.codeForStatus(403)).toEqual('forbidden');
73
+ expect(FileStorageError.codeForStatus(408)).toEqual('unavailable');
74
+ expect(FileStorageError.codeForStatus(429)).toEqual('unavailable');
75
+ expect(FileStorageError.codeForStatus(503)).toEqual('unavailable');
76
+ expect(FileStorageError.codeForStatus(400)).toEqual('unknown');
77
+ expect(FileStorageError.codeForStatus(undefined)).toEqual('unknown');
78
+ });
79
+ });
@@ -62,6 +62,9 @@ export class FileTestEnvironment {
62
62
  // implementations from the generated source graph; a graph-less harness lookup THROWS).
63
63
  // Default: no registered resolvers. Suites exercising the seam overwrite this entry.
64
64
  objectCache['@proteinjs/db-file/FileReachabilityResolver'] = [];
65
+ // The copy-for-others seam a non-owner read consults (same graph-less rule). Default: no
66
+ // maker — everyone is served the original. Suites exercising the seam overwrite this entry.
67
+ objectCache['@proteinjs/db-file/FileCopyForOthers'] = [];
65
68
  Session.setData({
66
69
  sessionId: 'test-session',
67
70
  user: 'guest',