@proteinjs/db-file 1.7.3 → 1.7.5
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 +22 -0
- package/dist/generated/index.js +1 -1
- package/dist/generated/index.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/src/FileReachabilityResolver.d.ts +27 -0
- package/dist/src/FileReachabilityResolver.d.ts.map +1 -0
- package/dist/src/FileReachabilityResolver.js +9 -0
- package/dist/src/FileReachabilityResolver.js.map +1 -0
- package/dist/src/FileStorage.d.ts +28 -3
- package/dist/src/FileStorage.d.ts.map +1 -1
- package/dist/src/FileStorage.js +72 -11
- package/dist/src/FileStorage.js.map +1 -1
- package/dist/src/routes/getFile.d.ts +4 -1
- package/dist/src/routes/getFile.d.ts.map +1 -1
- package/dist/src/routes/getFile.js +4 -1
- package/dist/src/routes/getFile.js.map +1 -1
- package/dist/test/FileByteAccess.test.d.ts +2 -0
- package/dist/test/FileByteAccess.test.d.ts.map +1 -0
- package/dist/test/FileByteAccess.test.js +252 -0
- package/dist/test/FileByteAccess.test.js.map +1 -0
- package/dist/test/FileSharedReachability.test.d.ts +2 -0
- package/dist/test/FileSharedReachability.test.d.ts.map +1 -0
- package/dist/test/FileSharedReachability.test.js +358 -0
- package/dist/test/FileSharedReachability.test.js.map +1 -0
- package/dist/test/FileTestEnvironment.d.ts.map +1 -1
- package/dist/test/FileTestEnvironment.js +4 -0
- package/dist/test/FileTestEnvironment.js.map +1 -1
- package/generated/index.ts +1 -1
- package/index.ts +1 -0
- package/package.json +2 -2
- package/src/FileReachabilityResolver.ts +29 -0
- package/src/FileStorage.ts +53 -4
- package/src/routes/getFile.ts +5 -2
- package/test/FileByteAccess.test.ts +143 -0
- package/test/FileSharedReachability.test.ts +229 -0
- package/test/FileTestEnvironment.ts +4 -0
|
@@ -111,6 +111,10 @@ var FileTestEnvironment = /** @class */ (function () {
|
|
|
111
111
|
objectCache['@proteinjs/db/Table'] = __spreadArray(__spreadArray([], Object.values(user_1.tables), true), Object.values(tables_1.tables), true);
|
|
112
112
|
// The byte-deletion seam under test: prod loads it from the generated source graph.
|
|
113
113
|
objectCache['@proteinjs/db/TableWatcher'] = [new FileStorageTableWatcher_1.FileStorageTableWatcher()];
|
|
114
|
+
// The shared-content reachability seam getFile consults on a scoped-read miss (prod loads
|
|
115
|
+
// implementations from the generated source graph; a graph-less harness lookup THROWS).
|
|
116
|
+
// Default: no registered resolvers. Suites exercising the seam overwrite this entry.
|
|
117
|
+
objectCache['@proteinjs/db-file/FileReachabilityResolver'] = [];
|
|
114
118
|
server_api_1.Session.setData({
|
|
115
119
|
sessionId: 'test-session',
|
|
116
120
|
user: 'guest',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileTestEnvironment.js","sourceRoot":"","sources":["../../test/FileTestEnvironment.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,oCAAyD;AACzD,kEAA6D;AAC7D,0DAAiG;AACjG,oDAA2F;AAC3F,oDAAyD;AACzD,wCAAuE;AACvE,+CAA4D;AAC5D,kDAAiD;AAEjD,0EAAyE;AAEzE;oFACoF;AACpF;IAAA;QACE,gBAAW,GAAG,MAAe,CAAC;IAUhC,CAAC;IAPC,wCAAO,GAAP,UAAQ,IAAiB;QACvB,sBAAsB,CAAC,IAAI,GAAG,IAAI,CAAC;IACrC,CAAC;IAED,wCAAO,GAAP;QACE,OAAO,sBAAsB,CAAC,IAAI,CAAC;IACrC,CAAC;IACH,6BAAC;AAAD,CAAC,AAXD,IAWC;AAKD;;;;;GAKG;AACH;IAAA;QACW,kBAAa,GAAG,IAAI,iCAAa,CAAC;YACzC,SAAS,EAAE,gBAAgB;YAC3B,YAAY,EAAE,gBAAgB;YAC9B,YAAY,EAAE,MAAM;SACrB,CAAC,CAAC;QACK,kBAAa,GAAG,IAAA,uBAAgB,EAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACrD,aAAQ,GAAG,IAAI,eAAQ,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"FileTestEnvironment.js","sourceRoot":"","sources":["../../test/FileTestEnvironment.ts"],"names":[],"mappings":"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"}
|
package/generated/index.ts
CHANGED
|
@@ -9,7 +9,7 @@ import '@proteinjs/user';
|
|
|
9
9
|
/** Generate Source Graph */
|
|
10
10
|
|
|
11
11
|
const sourceGraph =
|
|
12
|
-
'{"options":{"directed":true,"multigraph":false,"compound":false},"nodes":[{"v":"@proteinjs/db-file/DefaultFileStorageDriverFactory","value":{"packageName":"@proteinjs/db-file","name":"DefaultFileStorageDriverFactory","filePath":"src/FileStorage.ts","qualifiedName":"@proteinjs/db-file/DefaultFileStorageDriverFactory","properties":[],"methods":[{"name":"getDriver","returnType":{"packageName":"@proteinjs/db-file","name":"FileStorageDriver","filePath":null,"qualifiedName":"@proteinjs/db-file/FileStorageDriver","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":true,"isStatic":false,"visibility":"public","parameters":[]}],"typeParameters":[],"directParents":[{"packageName":"@proteinjs/reflection","name":"Loadable","filePath":null,"qualifiedName":"@proteinjs/reflection/Loadable","properties":[],"methods":[],"typeParameters":[],"directParents":[]}],"sourceType":3}},{"v":"@proteinjs/reflection/Loadable"},{"v":"@proteinjs/db-file/FileStorage","value":{"packageName":"@proteinjs/db-file","name":"FileStorage","filePath":"src/FileStorage.ts","qualifiedName":"@proteinjs/db-file/FileStorage","isAbstract":false,"isStatic":false,"visibility":"public","properties":[{"name":"driver","type":{"packageName":"@proteinjs/db-file","name":"FileStorageDriver","filePath":null,"qualifiedName":"@proteinjs/db-file/FileStorageDriver","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":true,"visibility":"private"},{"name":"serviceMetadata","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"}],"methods":[{"name":"getDriver","returnType":{"packageName":"@proteinjs/db-file","name":"FileStorageDriver","filePath":null,"qualifiedName":"@proteinjs/db-file/FileStorageDriver","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":true,"visibility":"public","parameters":[]},{"name":"createFile","returnType":{"packageName":"","name":"Promise<File>","filePath":null,"qualifiedName":"/Promise<File>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"fileMetaData","type":{"packageName":"","name":"Omit<File, keyof ScopedRecord>","filePath":null,"qualifiedName":"/Omit<File, keyof ScopedRecord>","typeParameters":null,"directParents":null}},{"name":"fileData","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]},{"name":"getFile","returnType":{"packageName":"","name":"Promise<File>","filePath":null,"qualifiedName":"/Promise<File>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"fileId","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]},{"name":"getFileData","returnType":{"packageName":"","name":"Promise<string>","filePath":null,"qualifiedName":"/Promise<string>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"fileId","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]},{"name":"getSignedUrl","returnType":{"packageName":"","name":"Promise<string | undefined>","filePath":null,"qualifiedName":"/Promise<string | undefined>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"fileId","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}},{"name":"options","type":{"packageName":"@proteinjs/db-file","name":"{ ttlMs?: number }","filePath":null,"qualifiedName":"@proteinjs/db-file/{ ttlMs?: number }","typeParameters":null,"directParents":null}}]},{"name":"updateFileData","returnType":{"packageName":"","name":"Promise<void>","filePath":null,"qualifiedName":"/Promise<void>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"fileId","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}},{"name":"data","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]},{"name":"updateFile","returnType":{"packageName":"","name":"Promise<void>","filePath":null,"qualifiedName":"/Promise<void>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"file","type":{"packageName":"","name":"Omit<File, keyof ScopedRecord>","filePath":null,"qualifiedName":"/Omit<File, keyof ScopedRecord>","typeParameters":null,"directParents":null}}]},{"name":"deleteFile","returnType":{"packageName":"","name":"Promise<void>","filePath":null,"qualifiedName":"/Promise<void>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"fileId","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]}],"typeParameters":[],"directParentInterfaces":[{"packageName":"@proteinjs/db-file","name":"FileStorageService","filePath":null,"qualifiedName":"@proteinjs/db-file/FileStorageService","properties":[],"methods":[],"typeParameters":[],"directParents":[]}],"directParentClasses":[],"sourceType":2}},{"v":"@proteinjs/db-file/FileStorageService","value":{"packageName":"@proteinjs/db-file","name":"FileStorageService","filePath":"src/services/FileStorageService.ts","qualifiedName":"@proteinjs/db-file/FileStorageService","properties":[],"methods":[{"name":"createFile","returnType":{"packageName":"","name":"Promise<File>","filePath":null,"qualifiedName":"/Promise<File>","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":true,"isStatic":false,"visibility":"public","parameters":[{"name":"fileMetaData","type":{"packageName":"","name":"Omit<File, keyof ScopedRecord>","filePath":null,"qualifiedName":"/Omit<File, keyof ScopedRecord>","typeParameters":null,"directParents":null}},{"name":"fileData","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]},{"name":"getFile","returnType":{"packageName":"","name":"Promise<File>","filePath":null,"qualifiedName":"/Promise<File>","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":true,"isStatic":false,"visibility":"public","parameters":[{"name":"fileId","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]},{"name":"getFileData","returnType":{"packageName":"","name":"Promise<string>","filePath":null,"qualifiedName":"/Promise<string>","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":true,"isStatic":false,"visibility":"public","parameters":[{"name":"fileId","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]},{"name":"updateFileData","returnType":{"packageName":"","name":"Promise<void>","filePath":null,"qualifiedName":"/Promise<void>","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":true,"isStatic":false,"visibility":"public","parameters":[{"name":"fileId","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}},{"name":"data","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]},{"name":"updateFile","returnType":{"packageName":"","name":"Promise<void>","filePath":null,"qualifiedName":"/Promise<void>","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":true,"isStatic":false,"visibility":"public","parameters":[{"name":"file","type":{"packageName":"","name":"Omit<File, keyof ScopedRecord>","filePath":null,"qualifiedName":"/Omit<File, keyof ScopedRecord>","typeParameters":null,"directParents":null}}]},{"name":"deleteFile","returnType":{"packageName":"","name":"Promise<void>","filePath":null,"qualifiedName":"/Promise<void>","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":true,"isStatic":false,"visibility":"public","parameters":[{"name":"fileId","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]}],"typeParameters":[],"directParents":[{"packageName":"@proteinjs/service","name":"Service","filePath":null,"qualifiedName":"@proteinjs/service/Service","properties":[],"methods":[],"typeParameters":[],"directParents":[]}],"sourceType":3}},{"v":"@proteinjs/db-file/FileStorageTableWatcher","value":{"packageName":"@proteinjs/db-file","name":"FileStorageTableWatcher","filePath":"src/FileStorageTableWatcher.ts","qualifiedName":"@proteinjs/db-file/FileStorageTableWatcher","isAbstract":false,"isStatic":false,"visibility":"public","properties":[{"name":"logger","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private"}],"methods":[{"name":"name","returnType":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[]},{"name":"table","returnType":{"packageName":"","name":"Table<File>","filePath":null,"qualifiedName":"/Table<File>","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[]},{"name":"beforeDelete","returnType":{"packageName":"","name":"Promise<void>","filePath":null,"qualifiedName":"/Promise<void>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"recordsToDelete","type":{"packageName":"","name":"T[]","filePath":null,"qualifiedName":"/T[]","typeParameters":null,"directParents":null}},{"name":"initialQb","type":{"packageName":"","name":"QueryBuilder<T>","filePath":null,"qualifiedName":"/QueryBuilder<T>","typeParameters":null,"directParents":null}},{"name":"deleteQb","type":{"packageName":"","name":"QueryBuilder<T>","filePath":null,"qualifiedName":"/QueryBuilder<T>","typeParameters":null,"directParents":null}}]}],"typeParameters":[],"directParentInterfaces":[{"packageName":"@proteinjs/db","name":"TableWatcher","filePath":null,"qualifiedName":"@proteinjs/db/TableWatcher","properties":[],"methods":[],"typeParameters":["@proteinjs/db-file/File"],"directParents":[]}],"directParentClasses":[],"sourceType":2}},{"v":"@proteinjs/db/TableWatcher"},{"v":"@proteinjs/db-file/getFile","value":{"packageName":"@proteinjs/db-file","name":"getFile","filePath":"src/routes/getFile.ts","qualifiedName":"@proteinjs/db-file/getFile","type":{"packageName":"@proteinjs/server-api","name":"Route","filePath":null,"qualifiedName":"@proteinjs/server-api/Route","typeParameters":[],"directParents":[{"packageName":"@proteinjs/server-api","name":"Route","filePath":null,"qualifiedName":"@proteinjs/server-api/Route","typeParameters":[],"directParents":null}]},"isExported":true,"isConst":true,"sourceType":0}},{"v":"@proteinjs/server-api/Route"},{"v":"@proteinjs/service/Service"},{"v":"@proteinjs/db-file/FileData","value":{"packageName":"@proteinjs/db-file","name":"FileData","filePath":"src/tables/FileDataTable.ts","qualifiedName":"@proteinjs/db-file/FileData","properties":[{"name":"file","type":{"packageName":"","name":"Reference<File>","filePath":null,"qualifiedName":"/Reference<File>","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"order","type":{"packageName":"","name":"number","filePath":null,"qualifiedName":"/number","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"data","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"}],"methods":[],"typeParameters":[],"directParents":[{"packageName":"@proteinjs/user","name":"ScopedRecord","filePath":null,"qualifiedName":"@proteinjs/user/ScopedRecord","properties":[],"methods":[],"typeParameters":[],"directParents":[]}],"sourceType":3}},{"v":"@proteinjs/user/ScopedRecord"},{"v":"@proteinjs/db-file/FileDataTable","value":{"packageName":"@proteinjs/db-file","name":"FileDataTable","filePath":"src/tables/FileDataTable.ts","qualifiedName":"@proteinjs/db-file/FileDataTable","isAbstract":false,"isStatic":false,"visibility":"public","properties":[{"name":"name","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"auth","type":{"packageName":"","name":"Table<FileData>[\'auth\']","filePath":null,"qualifiedName":"/Table<FileData>[\'auth\']","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"columns","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"}],"methods":[],"typeParameters":[],"directParentInterfaces":[],"directParentClasses":[{"packageName":"@proteinjs/db","name":"Table","filePath":null,"qualifiedName":"@proteinjs/db/Table","isAbstract":false,"isStatic":false,"visibility":"public","properties":[],"methods":[],"typeParameters":["@proteinjs/db-file/FileData"],"directParentInterfaces":[],"directParentClasses":[]}],"sourceType":2}},{"v":"@proteinjs/db/Table"},{"v":"@proteinjs/db-file/File","value":{"packageName":"@proteinjs/db-file","name":"File","filePath":"src/tables/FileTable.ts","qualifiedName":"@proteinjs/db-file/File","properties":[{"name":"name","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"type","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"size","type":{"packageName":"","name":"number","filePath":null,"qualifiedName":"/number","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"preview","type":{"packageName":"","name":"Reference<File>","filePath":null,"qualifiedName":"/Reference<File>","typeParameters":null,"directParents":null},"isOptional":true,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"width","type":{"packageName":"","name":"number","filePath":null,"qualifiedName":"/number","typeParameters":null,"directParents":null},"isOptional":true,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"height","type":{"packageName":"","name":"number","filePath":null,"qualifiedName":"/number","typeParameters":null,"directParents":null},"isOptional":true,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"durationMs","type":{"packageName":"","name":"number","filePath":null,"qualifiedName":"/number","typeParameters":null,"directParents":null},"isOptional":true,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"sourceUrl","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null},"isOptional":true,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"sourcePageUrl","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null},"isOptional":true,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"retrievedAt","type":{"packageName":"","name":"Date","filePath":null,"qualifiedName":"/Date","typeParameters":null,"directParents":null},"isOptional":true,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"contentHash","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null},"isOptional":true,"isAbstract":false,"isStatic":false,"visibility":"public"}],"methods":[],"typeParameters":[],"directParents":[{"packageName":"@proteinjs/user","name":"ScopedRecord","filePath":null,"qualifiedName":"@proteinjs/user/ScopedRecord","properties":[],"methods":[],"typeParameters":[],"directParents":[]}],"sourceType":3}},{"v":"@proteinjs/db-file/FileTable","value":{"packageName":"@proteinjs/db-file","name":"FileTable","filePath":"src/tables/FileTable.ts","qualifiedName":"@proteinjs/db-file/FileTable","isAbstract":false,"isStatic":false,"visibility":"public","properties":[{"name":"name","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"auth","type":{"packageName":"","name":"Table<File>[\'auth\']","filePath":null,"qualifiedName":"/Table<File>[\'auth\']","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"columns","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"indexes","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"}],"methods":[],"typeParameters":[],"directParentInterfaces":[],"directParentClasses":[{"packageName":"@proteinjs/db","name":"Table","filePath":null,"qualifiedName":"@proteinjs/db/Table","isAbstract":false,"isStatic":false,"visibility":"public","properties":[],"methods":[],"typeParameters":["@proteinjs/db-file/File"],"directParentInterfaces":[],"directParentClasses":[]}],"sourceType":2}}],"edges":[{"v":"@proteinjs/db-file/DefaultFileStorageDriverFactory","w":"@proteinjs/reflection/Loadable","value":"extends interface"},{"v":"@proteinjs/db-file/FileStorage","w":"@proteinjs/db-file/FileStorageService","value":"implements interface"},{"v":"@proteinjs/db-file/FileStorageTableWatcher","w":"@proteinjs/db/TableWatcher","value":"implements interface"},{"v":"@proteinjs/db-file/getFile","w":"@proteinjs/server-api/Route","value":"has type"},{"v":"@proteinjs/db-file/FileStorageService","w":"@proteinjs/service/Service","value":"extends interface"},{"v":"@proteinjs/db-file/FileData","w":"@proteinjs/user/ScopedRecord","value":"extends interface"},{"v":"@proteinjs/db-file/FileDataTable","w":"@proteinjs/db/Table","value":"extends class"},{"v":"@proteinjs/db-file/File","w":"@proteinjs/user/ScopedRecord","value":"extends interface"},{"v":"@proteinjs/db-file/FileTable","w":"@proteinjs/db/Table","value":"extends class"}]}';
|
|
12
|
+
'{"options":{"directed":true,"multigraph":false,"compound":false},"nodes":[{"v":"@proteinjs/db-file/FileReachabilityResolver","value":{"packageName":"@proteinjs/db-file","name":"FileReachabilityResolver","filePath":"src/FileReachabilityResolver.ts","qualifiedName":"@proteinjs/db-file/FileReachabilityResolver","properties":[],"methods":[{"name":"canReadViaReference","returnType":{"packageName":"","name":"Promise<boolean>","filePath":null,"qualifiedName":"/Promise<boolean>","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":true,"isStatic":false,"visibility":"public","parameters":[{"name":"fileId","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]}],"typeParameters":[],"directParents":[{"packageName":"@proteinjs/reflection","name":"Loadable","filePath":null,"qualifiedName":"@proteinjs/reflection/Loadable","properties":[],"methods":[],"typeParameters":[],"directParents":[]}],"sourceType":3}},{"v":"@proteinjs/reflection/Loadable"},{"v":"@proteinjs/db-file/DefaultFileStorageDriverFactory","value":{"packageName":"@proteinjs/db-file","name":"DefaultFileStorageDriverFactory","filePath":"src/FileStorage.ts","qualifiedName":"@proteinjs/db-file/DefaultFileStorageDriverFactory","properties":[],"methods":[{"name":"getDriver","returnType":{"packageName":"@proteinjs/db-file","name":"FileStorageDriver","filePath":null,"qualifiedName":"@proteinjs/db-file/FileStorageDriver","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":true,"isStatic":false,"visibility":"public","parameters":[]}],"typeParameters":[],"directParents":[{"packageName":"@proteinjs/reflection","name":"Loadable","filePath":null,"qualifiedName":"@proteinjs/reflection/Loadable","properties":[],"methods":[],"typeParameters":[],"directParents":[]}],"sourceType":3}},{"v":"@proteinjs/db-file/FileStorage","value":{"packageName":"@proteinjs/db-file","name":"FileStorage","filePath":"src/FileStorage.ts","qualifiedName":"@proteinjs/db-file/FileStorage","isAbstract":false,"isStatic":false,"visibility":"public","properties":[{"name":"driver","type":{"packageName":"@proteinjs/db-file","name":"FileStorageDriver","filePath":null,"qualifiedName":"@proteinjs/db-file/FileStorageDriver","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":true,"visibility":"private"},{"name":"serviceMetadata","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"}],"methods":[{"name":"getDriver","returnType":{"packageName":"@proteinjs/db-file","name":"FileStorageDriver","filePath":null,"qualifiedName":"@proteinjs/db-file/FileStorageDriver","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":true,"visibility":"public","parameters":[]},{"name":"createFile","returnType":{"packageName":"","name":"Promise<File>","filePath":null,"qualifiedName":"/Promise<File>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"fileMetaData","type":{"packageName":"","name":"Omit<File, keyof ScopedRecord>","filePath":null,"qualifiedName":"/Omit<File, keyof ScopedRecord>","typeParameters":null,"directParents":null}},{"name":"fileData","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]},{"name":"getFile","returnType":{"packageName":"","name":"Promise<File>","filePath":null,"qualifiedName":"/Promise<File>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"fileId","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]},{"name":"getFileData","returnType":{"packageName":"","name":"Promise<string>","filePath":null,"qualifiedName":"/Promise<string>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"fileId","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]},{"name":"getSignedUrl","returnType":{"packageName":"","name":"Promise<string | undefined>","filePath":null,"qualifiedName":"/Promise<string | undefined>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"fileId","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}},{"name":"options","type":{"packageName":"@proteinjs/db-file","name":"{ ttlMs?: number }","filePath":null,"qualifiedName":"@proteinjs/db-file/{ ttlMs?: number }","typeParameters":null,"directParents":null}}]},{"name":"updateFileData","returnType":{"packageName":"","name":"Promise<void>","filePath":null,"qualifiedName":"/Promise<void>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"fileId","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}},{"name":"data","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]},{"name":"updateFile","returnType":{"packageName":"","name":"Promise<void>","filePath":null,"qualifiedName":"/Promise<void>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"file","type":{"packageName":"","name":"Omit<File, keyof ScopedRecord>","filePath":null,"qualifiedName":"/Omit<File, keyof ScopedRecord>","typeParameters":null,"directParents":null}}]},{"name":"deleteFile","returnType":{"packageName":"","name":"Promise<void>","filePath":null,"qualifiedName":"/Promise<void>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"fileId","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]}],"typeParameters":[],"directParentInterfaces":[{"packageName":"@proteinjs/db-file","name":"FileStorageService","filePath":null,"qualifiedName":"@proteinjs/db-file/FileStorageService","properties":[],"methods":[],"typeParameters":[],"directParents":[]}],"directParentClasses":[],"sourceType":2}},{"v":"@proteinjs/db-file/FileStorageService","value":{"packageName":"@proteinjs/db-file","name":"FileStorageService","filePath":"src/services/FileStorageService.ts","qualifiedName":"@proteinjs/db-file/FileStorageService","properties":[],"methods":[{"name":"createFile","returnType":{"packageName":"","name":"Promise<File>","filePath":null,"qualifiedName":"/Promise<File>","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":true,"isStatic":false,"visibility":"public","parameters":[{"name":"fileMetaData","type":{"packageName":"","name":"Omit<File, keyof ScopedRecord>","filePath":null,"qualifiedName":"/Omit<File, keyof ScopedRecord>","typeParameters":null,"directParents":null}},{"name":"fileData","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]},{"name":"getFile","returnType":{"packageName":"","name":"Promise<File>","filePath":null,"qualifiedName":"/Promise<File>","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":true,"isStatic":false,"visibility":"public","parameters":[{"name":"fileId","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]},{"name":"getFileData","returnType":{"packageName":"","name":"Promise<string>","filePath":null,"qualifiedName":"/Promise<string>","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":true,"isStatic":false,"visibility":"public","parameters":[{"name":"fileId","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]},{"name":"updateFileData","returnType":{"packageName":"","name":"Promise<void>","filePath":null,"qualifiedName":"/Promise<void>","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":true,"isStatic":false,"visibility":"public","parameters":[{"name":"fileId","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}},{"name":"data","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]},{"name":"updateFile","returnType":{"packageName":"","name":"Promise<void>","filePath":null,"qualifiedName":"/Promise<void>","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":true,"isStatic":false,"visibility":"public","parameters":[{"name":"file","type":{"packageName":"","name":"Omit<File, keyof ScopedRecord>","filePath":null,"qualifiedName":"/Omit<File, keyof ScopedRecord>","typeParameters":null,"directParents":null}}]},{"name":"deleteFile","returnType":{"packageName":"","name":"Promise<void>","filePath":null,"qualifiedName":"/Promise<void>","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":true,"isStatic":false,"visibility":"public","parameters":[{"name":"fileId","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]}],"typeParameters":[],"directParents":[{"packageName":"@proteinjs/service","name":"Service","filePath":null,"qualifiedName":"@proteinjs/service/Service","properties":[],"methods":[],"typeParameters":[],"directParents":[]}],"sourceType":3}},{"v":"@proteinjs/db-file/FileStorageTableWatcher","value":{"packageName":"@proteinjs/db-file","name":"FileStorageTableWatcher","filePath":"src/FileStorageTableWatcher.ts","qualifiedName":"@proteinjs/db-file/FileStorageTableWatcher","isAbstract":false,"isStatic":false,"visibility":"public","properties":[{"name":"logger","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private"}],"methods":[{"name":"name","returnType":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[]},{"name":"table","returnType":{"packageName":"","name":"Table<File>","filePath":null,"qualifiedName":"/Table<File>","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[]},{"name":"beforeDelete","returnType":{"packageName":"","name":"Promise<void>","filePath":null,"qualifiedName":"/Promise<void>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"recordsToDelete","type":{"packageName":"","name":"T[]","filePath":null,"qualifiedName":"/T[]","typeParameters":null,"directParents":null}},{"name":"initialQb","type":{"packageName":"","name":"QueryBuilder<T>","filePath":null,"qualifiedName":"/QueryBuilder<T>","typeParameters":null,"directParents":null}},{"name":"deleteQb","type":{"packageName":"","name":"QueryBuilder<T>","filePath":null,"qualifiedName":"/QueryBuilder<T>","typeParameters":null,"directParents":null}}]}],"typeParameters":[],"directParentInterfaces":[{"packageName":"@proteinjs/db","name":"TableWatcher","filePath":null,"qualifiedName":"@proteinjs/db/TableWatcher","properties":[],"methods":[],"typeParameters":["@proteinjs/db-file/File"],"directParents":[]}],"directParentClasses":[],"sourceType":2}},{"v":"@proteinjs/db/TableWatcher"},{"v":"@proteinjs/db-file/getFile","value":{"packageName":"@proteinjs/db-file","name":"getFile","filePath":"src/routes/getFile.ts","qualifiedName":"@proteinjs/db-file/getFile","type":{"packageName":"@proteinjs/server-api","name":"Route","filePath":null,"qualifiedName":"@proteinjs/server-api/Route","typeParameters":[],"directParents":[{"packageName":"@proteinjs/server-api","name":"Route","filePath":null,"qualifiedName":"@proteinjs/server-api/Route","typeParameters":[],"directParents":null}]},"isExported":true,"isConst":true,"sourceType":0}},{"v":"@proteinjs/server-api/Route"},{"v":"@proteinjs/db-file/FileData","value":{"packageName":"@proteinjs/db-file","name":"FileData","filePath":"src/tables/FileDataTable.ts","qualifiedName":"@proteinjs/db-file/FileData","properties":[{"name":"file","type":{"packageName":"","name":"Reference<File>","filePath":null,"qualifiedName":"/Reference<File>","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"order","type":{"packageName":"","name":"number","filePath":null,"qualifiedName":"/number","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"data","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"}],"methods":[],"typeParameters":[],"directParents":[{"packageName":"@proteinjs/user","name":"ScopedRecord","filePath":null,"qualifiedName":"@proteinjs/user/ScopedRecord","properties":[],"methods":[],"typeParameters":[],"directParents":[]}],"sourceType":3}},{"v":"@proteinjs/user/ScopedRecord"},{"v":"@proteinjs/db-file/FileDataTable","value":{"packageName":"@proteinjs/db-file","name":"FileDataTable","filePath":"src/tables/FileDataTable.ts","qualifiedName":"@proteinjs/db-file/FileDataTable","isAbstract":false,"isStatic":false,"visibility":"public","properties":[{"name":"name","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"auth","type":{"packageName":"","name":"Table<FileData>[\'auth\']","filePath":null,"qualifiedName":"/Table<FileData>[\'auth\']","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"columns","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"}],"methods":[],"typeParameters":[],"directParentInterfaces":[],"directParentClasses":[{"packageName":"@proteinjs/db","name":"Table","filePath":null,"qualifiedName":"@proteinjs/db/Table","isAbstract":false,"isStatic":false,"visibility":"public","properties":[],"methods":[],"typeParameters":["@proteinjs/db-file/FileData"],"directParentInterfaces":[],"directParentClasses":[]}],"sourceType":2}},{"v":"@proteinjs/db/Table"},{"v":"@proteinjs/db-file/File","value":{"packageName":"@proteinjs/db-file","name":"File","filePath":"src/tables/FileTable.ts","qualifiedName":"@proteinjs/db-file/File","properties":[{"name":"name","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"type","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"size","type":{"packageName":"","name":"number","filePath":null,"qualifiedName":"/number","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"preview","type":{"packageName":"","name":"Reference<File>","filePath":null,"qualifiedName":"/Reference<File>","typeParameters":null,"directParents":null},"isOptional":true,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"width","type":{"packageName":"","name":"number","filePath":null,"qualifiedName":"/number","typeParameters":null,"directParents":null},"isOptional":true,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"height","type":{"packageName":"","name":"number","filePath":null,"qualifiedName":"/number","typeParameters":null,"directParents":null},"isOptional":true,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"durationMs","type":{"packageName":"","name":"number","filePath":null,"qualifiedName":"/number","typeParameters":null,"directParents":null},"isOptional":true,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"sourceUrl","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null},"isOptional":true,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"sourcePageUrl","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null},"isOptional":true,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"retrievedAt","type":{"packageName":"","name":"Date","filePath":null,"qualifiedName":"/Date","typeParameters":null,"directParents":null},"isOptional":true,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"contentHash","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null},"isOptional":true,"isAbstract":false,"isStatic":false,"visibility":"public"}],"methods":[],"typeParameters":[],"directParents":[{"packageName":"@proteinjs/user","name":"ScopedRecord","filePath":null,"qualifiedName":"@proteinjs/user/ScopedRecord","properties":[],"methods":[],"typeParameters":[],"directParents":[]}],"sourceType":3}},{"v":"@proteinjs/db-file/FileTable","value":{"packageName":"@proteinjs/db-file","name":"FileTable","filePath":"src/tables/FileTable.ts","qualifiedName":"@proteinjs/db-file/FileTable","isAbstract":false,"isStatic":false,"visibility":"public","properties":[{"name":"name","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"auth","type":{"packageName":"","name":"Table<File>[\'auth\']","filePath":null,"qualifiedName":"/Table<File>[\'auth\']","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"columns","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"indexes","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"}],"methods":[],"typeParameters":[],"directParentInterfaces":[],"directParentClasses":[{"packageName":"@proteinjs/db","name":"Table","filePath":null,"qualifiedName":"@proteinjs/db/Table","isAbstract":false,"isStatic":false,"visibility":"public","properties":[],"methods":[],"typeParameters":["@proteinjs/db-file/File"],"directParentInterfaces":[],"directParentClasses":[]}],"sourceType":2}},{"v":"@proteinjs/service/Service"}],"edges":[{"v":"@proteinjs/db-file/FileReachabilityResolver","w":"@proteinjs/reflection/Loadable","value":"extends interface"},{"v":"@proteinjs/db-file/DefaultFileStorageDriverFactory","w":"@proteinjs/reflection/Loadable","value":"extends interface"},{"v":"@proteinjs/db-file/FileStorage","w":"@proteinjs/db-file/FileStorageService","value":"implements interface"},{"v":"@proteinjs/db-file/FileStorageTableWatcher","w":"@proteinjs/db/TableWatcher","value":"implements interface"},{"v":"@proteinjs/db-file/getFile","w":"@proteinjs/server-api/Route","value":"has type"},{"v":"@proteinjs/db-file/FileData","w":"@proteinjs/user/ScopedRecord","value":"extends interface"},{"v":"@proteinjs/db-file/FileDataTable","w":"@proteinjs/db/Table","value":"extends class"},{"v":"@proteinjs/db-file/File","w":"@proteinjs/user/ScopedRecord","value":"extends interface"},{"v":"@proteinjs/db-file/FileTable","w":"@proteinjs/db/Table","value":"extends class"},{"v":"@proteinjs/db-file/FileStorageService","w":"@proteinjs/service/Service","value":"extends interface"}]}';
|
|
13
13
|
|
|
14
14
|
/** Generate Source Links */
|
|
15
15
|
|
package/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ export * from './src/services/FileStorageService';
|
|
|
10
10
|
|
|
11
11
|
export * from './src/routes/getFileRoute';
|
|
12
12
|
|
|
13
|
+
export * from './src/FileReachabilityResolver';
|
|
13
14
|
export * from './src/FileStorageDriver';
|
|
14
15
|
export * from './src/DbFileStorageDriver';
|
|
15
16
|
export * from './src/FileStorageTableWatcher';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@proteinjs/db-file",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.5",
|
|
4
4
|
"description": "File storage in the db",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -46,5 +46,5 @@
|
|
|
46
46
|
},
|
|
47
47
|
"main": "./dist/generated/index.js",
|
|
48
48
|
"types": "./dist/generated/index.d.ts",
|
|
49
|
-
"gitHead": "
|
|
49
|
+
"gitHead": "31b2584ebfcf690d1472d2ce9f1f75dc79a97481"
|
|
50
50
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Loadable, SourceRepository } from '@proteinjs/reflection';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The shared-content file leg (SHARING_EXPANSION §1): the caller may read a file when they may
|
|
5
|
+
* read a row that references it. File rows are ScopedRecord — owner-private — but files get
|
|
6
|
+
* EMBEDDED in shareable content (a shared thought's media nodes), where the content's own access
|
|
7
|
+
* model says a share recipient may read them; a purely scoped row read as the access check
|
|
8
|
+
* refuses every recipient (the shared-thought media defect, SHARING_EXPANSION §0).
|
|
9
|
+
*
|
|
10
|
+
* Content packages register resolvers that answer reachability through their own GRANT-FILTERED
|
|
11
|
+
* reads — the same derivation the content itself is read through — so file access derives from
|
|
12
|
+
* content access, revocation included, and file scope is never widened. Dependency direction is
|
|
13
|
+
* the SharedThoughtResolver pattern: the interface lives here in db-file; implementations live
|
|
14
|
+
* in the content packages above (thought, later chat) and are discovered via SourceRepository.
|
|
15
|
+
*
|
|
16
|
+
* Consulted by `FileStorage.getFile` only after the caller's scoped read MISSES (the owner never
|
|
17
|
+
* pays for it), and it widens READS only: writes and deletes stay owner-scoped.
|
|
18
|
+
*/
|
|
19
|
+
export interface FileReachabilityResolver extends Loadable {
|
|
20
|
+
/**
|
|
21
|
+
* True when the CURRENT session user can read a row that references this file. Implementations
|
|
22
|
+
* MUST resolve through their grant-filtered reads (e.g. the shared db) so a revoked grant
|
|
23
|
+
* closes file access the same moment it closes content access.
|
|
24
|
+
*/
|
|
25
|
+
canReadViaReference(fileId: string): Promise<boolean>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const getFileReachabilityResolvers = () =>
|
|
29
|
+
SourceRepository.get().objects<FileReachabilityResolver>('@proteinjs/db-file/FileReachabilityResolver');
|
package/src/FileStorage.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { ScopedRecord, getScopedDb } from '@proteinjs/user';
|
|
1
|
+
import { ScopedRecord, getScopedDb, getScopedDbAsSystem } from '@proteinjs/user';
|
|
2
2
|
import { File } from './tables/FileTable';
|
|
3
3
|
import { tables } from './tables/tables';
|
|
4
4
|
import { FileStorageService, getFileStorageService } from './services/FileStorageService';
|
|
5
5
|
import { FileStorageDriver } from './FileStorageDriver';
|
|
6
|
+
import { getFileReachabilityResolvers } from './FileReachabilityResolver';
|
|
6
7
|
import { Loadable, SourceRepository } from '@proteinjs/reflection';
|
|
7
8
|
import { Logger } from '@proteinjs/logger';
|
|
8
9
|
import { DbFileStorageDriver } from './DbFileStorageDriver';
|
|
@@ -73,29 +74,64 @@ export class FileStorage implements FileStorageService {
|
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
/**
|
|
76
|
-
* Retrieves the metadata of a given file
|
|
77
|
+
* Retrieves the metadata of a given file — THE file-read access decision every serving path
|
|
78
|
+
* derives from (`/file/:id`, signed-URL minting, the browser service).
|
|
79
|
+
*
|
|
80
|
+
* Two legs: the caller's own file resolves by the SCOPED read; when that misses, the
|
|
81
|
+
* shared-content leg asks the registered {@link FileReachabilityResolver}s whether the caller
|
|
82
|
+
* can read a row that REFERENCES the file (a shared thought's media node) — content access
|
|
83
|
+
* confers file access, through the content's own grant-filtered read, never by widening file
|
|
84
|
+
* scope. No resolver vouching means the miss stands. Reads only: writes/deletes stay scoped.
|
|
77
85
|
* @param fileId - The `id` of the file.
|
|
78
|
-
* @returns The file metadata.
|
|
86
|
+
* @returns The file metadata, or `undefined` when the caller can neither own nor reach it.
|
|
79
87
|
*/
|
|
80
88
|
async getFile(fileId: string): Promise<File> {
|
|
81
89
|
const db = getScopedDb();
|
|
82
90
|
const file = await db.get(tables.File, { id: fileId });
|
|
91
|
+
if (file) {
|
|
92
|
+
return file;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
for (const resolver of getFileReachabilityResolvers()) {
|
|
96
|
+
if (await resolver.canReadViaReference(fileId)) {
|
|
97
|
+
// Reachability established through the caller's grant-filtered content read — the row
|
|
98
|
+
// itself lives in the owner's scope, so it is served via system read.
|
|
99
|
+
return await getScopedDbAsSystem().get(tables.File, { id: fileId });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
83
103
|
return file;
|
|
84
104
|
}
|
|
85
105
|
|
|
86
106
|
/**
|
|
87
107
|
* Retrieves the data chunks associated with a given file.
|
|
108
|
+
*
|
|
109
|
+
* Gated on the {@link getFile} row read — the same two-legged access decision every serving
|
|
110
|
+
* path derives from (the caller's SCOPED read, else shared-content reachability), so bytes are
|
|
111
|
+
* exactly as reachable as the row that names them. The gate lives here because this is the
|
|
112
|
+
* browser-facing service boundary (`FileStorageService` is `allUsers`) and the driver byte ops
|
|
113
|
+
* below are deliberately unscoped. Server-side doors that make their OWN documented access
|
|
114
|
+
* decision (the avatar route, issue/ticket attachment doors) read bytes through
|
|
115
|
+
* `FileStorage.getDriver()` instead.
|
|
88
116
|
* @param fileId - The `id` of the file.
|
|
89
117
|
* @returns The file data as a single string.
|
|
118
|
+
* @throws When the file row does not exist or is not readable by the caller — the same named
|
|
119
|
+
* miss either way, so existence is not leaked to unauthorized callers.
|
|
90
120
|
*/
|
|
91
121
|
async getFileData(fileId: string): Promise<string> {
|
|
122
|
+
const file = await this.getFile(fileId);
|
|
123
|
+
if (!file) {
|
|
124
|
+
throw new Error(`File not found: ${fileId}`);
|
|
125
|
+
}
|
|
126
|
+
|
|
92
127
|
return await FileStorage.getDriver().getFileData(fileId);
|
|
93
128
|
}
|
|
94
129
|
|
|
95
130
|
/**
|
|
96
131
|
* Mint a short-lived, read-only URL for the file's bytes (server-only; not part of the
|
|
97
132
|
* browser-facing `FileStorageService`). A signed URL is a bearer capability, so minting
|
|
98
|
-
* verifies the caller can read the file row (
|
|
133
|
+
* verifies the caller can read the file row (the two-legged {@link getFile} read — own scope
|
|
134
|
+
* or shared-content reachability) before signing.
|
|
99
135
|
* @param fileId - The `id` of the file.
|
|
100
136
|
* @param options.ttlMs - How long the URL stays valid; the driver applies its default when omitted.
|
|
101
137
|
* @returns The signed URL, or `undefined` when the driver has no external URL space
|
|
@@ -118,10 +154,23 @@ export class FileStorage implements FileStorageService {
|
|
|
118
154
|
|
|
119
155
|
/**
|
|
120
156
|
* Updates the data chunks associated with a given file.
|
|
157
|
+
*
|
|
158
|
+
* Gated on the caller's SCOPED row read — owner scope is the write grant on ScopedRecords
|
|
159
|
+
* (the same authorization a scoped row update enforces). Deliberately STRICTER than
|
|
160
|
+
* {@link getFileData}: the {@link FileReachabilityResolver} leg widens READS only, so a
|
|
161
|
+
* share recipient who can read the bytes still cannot write them.
|
|
121
162
|
* @param fileId - The `id` of the file.
|
|
122
163
|
* @param data - The new data string to replace the existing data.
|
|
164
|
+
* @throws When the file row does not exist or is not writable by the caller — the same named
|
|
165
|
+
* miss either way, so existence is not leaked to unauthorized callers.
|
|
123
166
|
*/
|
|
124
167
|
async updateFileData(fileId: string, data: string): Promise<void> {
|
|
168
|
+
const db = getScopedDb();
|
|
169
|
+
const file = await db.get(tables.File, { id: fileId });
|
|
170
|
+
if (!file) {
|
|
171
|
+
throw new Error(`File not found: ${fileId}`);
|
|
172
|
+
}
|
|
173
|
+
|
|
125
174
|
await FileStorage.getDriver().updateFileData(fileId, data);
|
|
126
175
|
}
|
|
127
176
|
|
package/src/routes/getFile.ts
CHANGED
|
@@ -4,7 +4,10 @@ import { UserAuth } from '@proteinjs/user';
|
|
|
4
4
|
import { resolveByteRange } from './byteRange';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* Serves a file's bytes. Auth first
|
|
7
|
+
* Serves a file's bytes. Auth first: logged-in, then the `FileStorage.getFile` row read as the
|
|
8
|
+
* access decision — the caller's SCOPED read, else the shared-content reachability leg
|
|
9
|
+
* (`FileReachabilityResolver`: the caller may read a file when they may read a row that
|
|
10
|
+
* references it, e.g. a shared thought's media node). Then one of two serving paths:
|
|
8
11
|
*
|
|
9
12
|
* - **302 redirect** to a short-lived signed URL when the driver's store has an external URL
|
|
10
13
|
* space (GCS). The app URL stays the one stable reference every `<img>`/`<video>`/chip uses;
|
|
@@ -28,7 +31,7 @@ export const getFile: Route = {
|
|
|
28
31
|
const fileId = request.params.id;
|
|
29
32
|
const fileStorage = getFileStorage();
|
|
30
33
|
try {
|
|
31
|
-
// The file row decides existence (a scoped read, so it is also the access check).
|
|
34
|
+
// The file row decides existence (a scoped-or-reachable read, so it is also the access check).
|
|
32
35
|
const file = await fileStorage.getFile(fileId);
|
|
33
36
|
if (!file) {
|
|
34
37
|
response.status(404).send('File not found');
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { SourceRepository } from '@proteinjs/reflection';
|
|
2
|
+
import { User } from '@proteinjs/user';
|
|
3
|
+
import { File } from '../src/tables/FileTable';
|
|
4
|
+
import { FileStorage } from '../src/FileStorage';
|
|
5
|
+
import { DbFileStorageDriver } from '../src/DbFileStorageDriver';
|
|
6
|
+
import { FileTestEnvironment } from './FileTestEnvironment';
|
|
7
|
+
|
|
8
|
+
/** Every byte value once — a payload that corrupts under any text/base64 mix-up. */
|
|
9
|
+
const rawBytes = Buffer.from(Array.from({ length: 256 }, (_, i) => i));
|
|
10
|
+
const rawBase64 = rawBytes.toString('base64');
|
|
11
|
+
/** A same-length replacement payload, byte-distinct at every position — the poisoning attempt. */
|
|
12
|
+
const poisonBytes = Buffer.from(Array.from({ length: 256 }, (_, i) => 255 - i));
|
|
13
|
+
const poisonBase64 = poisonBytes.toString('base64');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The byte-access gates on the browser-facing service (FileStorageService is `allUsers`, so
|
|
17
|
+
* these methods are every logged-in user's to call with any file id they can guess or leak):
|
|
18
|
+
*
|
|
19
|
+
* - **Reads** (`getFileData`): gated on the {@link FileStorage.getFile} row read — the same
|
|
20
|
+
* two-legged access decision every serving path derives from (the caller's SCOPED read, else
|
|
21
|
+
* the FileReachabilityResolver shared-content leg). Bytes are exactly as reachable as the row
|
|
22
|
+
* that names them. Ungated, a foreign file id read another user's bytes outright (the driver
|
|
23
|
+
* byte ops are deliberately unscoped — the access wall lives at this service boundary).
|
|
24
|
+
* - **Writes** (`updateFileData`): gated on the caller's SCOPED row read — owner scope is the
|
|
25
|
+
* write grant on ScopedRecords. Stricter than reads: the resolver leg widens READS only, so a
|
|
26
|
+
* share recipient who can read bytes still cannot write them. Ungated, a foreign file id let
|
|
27
|
+
* any caller poison another user's bytes (with the DbFileStorageDriver: caller-scoped chunk
|
|
28
|
+
* inserts landing beside the owner's — this suite runs that real driver so the intact-bytes
|
|
29
|
+
* assertions prove no chunk ever landed).
|
|
30
|
+
*
|
|
31
|
+
* The resolver is stubbed at the seam (content packages sit above db-file); denial is the named
|
|
32
|
+
* `File not found` — existence is not leaked to unauthorized callers.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
const testEnv = new FileTestEnvironment();
|
|
36
|
+
type SourceRepositoryInternals = { objectCache: Record<string, unknown[]> };
|
|
37
|
+
const objectCache = () => (SourceRepository.get() as unknown as SourceRepositoryInternals).objectCache;
|
|
38
|
+
|
|
39
|
+
let owner: User;
|
|
40
|
+
let recipient: User;
|
|
41
|
+
let stranger: User;
|
|
42
|
+
/** File ids the stub resolver reports as reachable for the current user. */
|
|
43
|
+
const reachableFileIds = new Set<string>();
|
|
44
|
+
/** How many times the seam was consulted — the owner's scoped leg must never pay for it. */
|
|
45
|
+
let resolverConsultations = 0;
|
|
46
|
+
|
|
47
|
+
beforeAll(async () => {
|
|
48
|
+
await testEnv.beforeAll();
|
|
49
|
+
// The seam getFile consults on a scoped-read miss, stubbed (the real thought-media resolver
|
|
50
|
+
// lives above this package).
|
|
51
|
+
objectCache()['@proteinjs/db-file/FileReachabilityResolver'] = [
|
|
52
|
+
{
|
|
53
|
+
canReadViaReference: async (fileId: string) => {
|
|
54
|
+
resolverConsultations++;
|
|
55
|
+
return reachableFileIds.has(fileId);
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
// The real chunked driver: write-gate bites are proven at the chunk-row level, not a mock.
|
|
60
|
+
testEnv.setDriver(new DbFileStorageDriver());
|
|
61
|
+
owner = await testEnv.createUser({ name: 'File owner', email: 'byte-owner@test.local' });
|
|
62
|
+
recipient = await testEnv.createUser({ name: 'Share recipient', email: 'byte-recipient@test.local' });
|
|
63
|
+
stranger = await testEnv.createUser({ name: 'Stranger', email: 'byte-stranger@test.local' });
|
|
64
|
+
testEnv.actAs(owner);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
afterAll(async () => {
|
|
68
|
+
delete objectCache()['@proteinjs/db-file/FileReachabilityResolver'];
|
|
69
|
+
await testEnv.afterAll();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
beforeEach(() => {
|
|
73
|
+
reachableFileIds.clear();
|
|
74
|
+
resolverConsultations = 0;
|
|
75
|
+
testEnv.actAs(owner);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const createOwnerFile = async (name: string): Promise<File> =>
|
|
79
|
+
await new FileStorage().createFile(
|
|
80
|
+
{ name, type: 'application/octet-stream', size: rawBytes.length } as File,
|
|
81
|
+
rawBase64
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
describe('byte reads gate on the file-row read (the getFile decision)', () => {
|
|
85
|
+
it('the owner reads their bytes through the scoped leg alone — resolvers are never consulted', async () => {
|
|
86
|
+
const file = await createOwnerFile('own-bytes.bin');
|
|
87
|
+
|
|
88
|
+
const fileDataBase64 = await new FileStorage().getFileData(file.id);
|
|
89
|
+
|
|
90
|
+
expect(fileDataBase64).toEqual(rawBase64);
|
|
91
|
+
expect(resolverConsultations).toEqual(0);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("THE VULN (read): a stranger holding only the file id is DENIED another user's bytes", async () => {
|
|
95
|
+
const file = await createOwnerFile('private-bytes.bin');
|
|
96
|
+
|
|
97
|
+
testEnv.actAs(stranger);
|
|
98
|
+
await expect(new FileStorage().getFileData(file.id)).rejects.toThrow(`File not found: ${file.id}`);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('a shared-content recipient (resolver-vouched) reads the bytes byte-identical', async () => {
|
|
102
|
+
const file = await createOwnerFile('shared-bytes.bin');
|
|
103
|
+
reachableFileIds.add(file.id);
|
|
104
|
+
|
|
105
|
+
testEnv.actAs(recipient);
|
|
106
|
+
const fileDataBase64 = await new FileStorage().getFileData(file.id);
|
|
107
|
+
|
|
108
|
+
expect(fileDataBase64).toEqual(rawBase64);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
describe('byte writes gate on the caller WRITING the row (owner scope; read-reachability never grants it)', () => {
|
|
113
|
+
it("THE VULN (write): a stranger holding only the file id cannot poison another user's bytes", async () => {
|
|
114
|
+
const file = await createOwnerFile('unpoisonable.bin');
|
|
115
|
+
|
|
116
|
+
testEnv.actAs(stranger);
|
|
117
|
+
await expect(new FileStorage().updateFileData(file.id, poisonBase64)).rejects.toThrow(`File not found: ${file.id}`);
|
|
118
|
+
|
|
119
|
+
// The outcome that matters: no chunk landed — the owner's bytes read back untouched.
|
|
120
|
+
testEnv.actAs(owner);
|
|
121
|
+
expect(await new FileStorage().getFileData(file.id)).toEqual(rawBase64);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("read-reachability does NOT grant write: a vouched recipient's byte write is denied, bytes intact", async () => {
|
|
125
|
+
const file = await createOwnerFile('read-only-share.bin');
|
|
126
|
+
reachableFileIds.add(file.id);
|
|
127
|
+
|
|
128
|
+
testEnv.actAs(recipient);
|
|
129
|
+
expect(await new FileStorage().getFileData(file.id)).toEqual(rawBase64);
|
|
130
|
+
await expect(new FileStorage().updateFileData(file.id, poisonBase64)).rejects.toThrow(`File not found: ${file.id}`);
|
|
131
|
+
|
|
132
|
+
testEnv.actAs(owner);
|
|
133
|
+
expect(await new FileStorage().getFileData(file.id)).toEqual(rawBase64);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('the owner replaces their bytes wholesale', async () => {
|
|
137
|
+
const file = await createOwnerFile('rewritable.bin');
|
|
138
|
+
|
|
139
|
+
await new FileStorage().updateFileData(file.id, poisonBase64);
|
|
140
|
+
|
|
141
|
+
expect(await new FileStorage().getFileData(file.id)).toEqual(poisonBase64);
|
|
142
|
+
});
|
|
143
|
+
});
|