@proteinjs/db-file 1.8.4 → 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.
- package/CHANGELOG.md +30 -0
- package/dist/generated/index.d.ts.map +1 -1
- package/dist/generated/index.js +3 -1
- package/dist/generated/index.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/src/DbFileStorageDriver.d.ts +7 -0
- package/dist/src/DbFileStorageDriver.d.ts.map +1 -1
- package/dist/src/DbFileStorageDriver.js +69 -41
- package/dist/src/DbFileStorageDriver.js.map +1 -1
- package/dist/src/FileCopyForOthers.d.ts +52 -0
- package/dist/src/FileCopyForOthers.d.ts.map +1 -0
- package/dist/src/FileCopyForOthers.js +62 -0
- package/dist/src/FileCopyForOthers.js.map +1 -0
- package/dist/src/FileStorage.d.ts +145 -11
- package/dist/src/FileStorage.d.ts.map +1 -1
- package/dist/src/FileStorage.js +537 -41
- package/dist/src/FileStorage.js.map +1 -1
- package/dist/src/FileStorageDriver.d.ts +11 -0
- package/dist/src/FileStorageDriver.d.ts.map +1 -1
- package/dist/src/FileStorageError.d.ts +62 -0
- package/dist/src/FileStorageError.d.ts.map +1 -0
- package/dist/src/FileStorageError.js +118 -0
- package/dist/src/FileStorageError.js.map +1 -0
- package/dist/src/FileVariantMaker.d.ts +61 -0
- package/dist/src/FileVariantMaker.d.ts.map +1 -0
- package/dist/src/FileVariantMaker.js +62 -0
- package/dist/src/FileVariantMaker.js.map +1 -0
- package/dist/src/routes/FileResponder.d.ts +41 -0
- package/dist/src/routes/FileResponder.d.ts.map +1 -0
- package/dist/src/routes/FileResponder.js +135 -0
- package/dist/src/routes/FileResponder.js.map +1 -0
- package/dist/src/routes/getFile.d.ts +5 -13
- package/dist/src/routes/getFile.d.ts.map +1 -1
- package/dist/src/routes/getFile.js +20 -56
- package/dist/src/routes/getFile.js.map +1 -1
- package/dist/src/routes/getFileVariant.d.ts +20 -0
- package/dist/src/routes/getFileVariant.d.ts.map +1 -0
- package/dist/src/routes/getFileVariant.js +112 -0
- package/dist/src/routes/getFileVariant.js.map +1 -0
- package/dist/src/routes/getFileVariantRoute.d.ts +7 -0
- package/dist/src/routes/getFileVariantRoute.d.ts.map +1 -0
- package/dist/src/routes/getFileVariantRoute.js +9 -0
- package/dist/src/routes/getFileVariantRoute.js.map +1 -0
- package/dist/src/tables/FileTable.d.ts +55 -2
- package/dist/src/tables/FileTable.d.ts.map +1 -1
- package/dist/src/tables/FileTable.js +8 -0
- package/dist/src/tables/FileTable.js.map +1 -1
- package/dist/test/DbFileStorageDriverMissingBytes.test.d.ts +2 -0
- package/dist/test/DbFileStorageDriverMissingBytes.test.d.ts.map +1 -0
- package/dist/test/DbFileStorageDriverMissingBytes.test.js +184 -0
- package/dist/test/DbFileStorageDriverMissingBytes.test.js.map +1 -0
- package/dist/test/FileByteAccess.test.js +6 -3
- package/dist/test/FileByteAccess.test.js.map +1 -1
- package/dist/test/FileCopyForOthers.test.d.ts +2 -0
- package/dist/test/FileCopyForOthers.test.d.ts.map +1 -0
- package/dist/test/FileCopyForOthers.test.js +1143 -0
- package/dist/test/FileCopyForOthers.test.js.map +1 -0
- package/dist/test/FileMediaMetadata.test.js +60 -5
- package/dist/test/FileMediaMetadata.test.js.map +1 -1
- package/dist/test/FileStorageError.test.d.ts +2 -0
- package/dist/test/FileStorageError.test.d.ts.map +1 -0
- package/dist/test/FileStorageError.test.js +68 -0
- package/dist/test/FileStorageError.test.js.map +1 -0
- package/dist/test/FileTestEnvironment.d.ts.map +1 -1
- package/dist/test/FileTestEnvironment.js +3 -0
- package/dist/test/FileTestEnvironment.js.map +1 -1
- package/dist/test/FileVariants.test.d.ts +2 -0
- package/dist/test/FileVariants.test.d.ts.map +1 -0
- package/dist/test/FileVariants.test.js +1008 -0
- package/dist/test/FileVariants.test.js.map +1 -0
- package/dist/test/GetFileRoute.test.js +29 -0
- package/dist/test/GetFileRoute.test.js.map +1 -1
- package/generated/index.ts +3 -1
- package/index.ts +4 -0
- package/package.json +5 -5
- package/src/DbFileStorageDriver.ts +32 -21
- package/src/FileCopyForOthers.ts +71 -0
- package/src/FileStorage.ts +351 -18
- package/src/FileStorageDriver.ts +12 -0
- package/src/FileStorageError.ts +123 -0
- package/src/FileVariantMaker.ts +84 -0
- package/src/routes/FileResponder.ts +85 -0
- package/src/routes/getFile.ts +9 -44
- package/src/routes/getFileVariant.ts +53 -0
- package/src/routes/getFileVariantRoute.ts +7 -0
- package/src/tables/FileTable.ts +57 -1
- package/test/DbFileStorageDriverMissingBytes.test.ts +85 -0
- package/test/FileByteAccess.test.ts +7 -1
- package/test/FileCopyForOthers.test.ts +702 -0
- package/test/FileMediaMetadata.test.ts +44 -0
- package/test/FileStorageError.test.ts +79 -0
- package/test/FileTestEnvironment.ts +3 -0
- package/test/FileVariants.test.ts +604 -0
- package/test/GetFileRoute.test.ts +19 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __extends = (this && this.__extends) || (function () {
|
|
3
|
+
var extendStatics = function (d, b) {
|
|
4
|
+
extendStatics = Object.setPrototypeOf ||
|
|
5
|
+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
6
|
+
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
7
|
+
return extendStatics(d, b);
|
|
8
|
+
};
|
|
9
|
+
return function (d, b) {
|
|
10
|
+
if (typeof b !== "function" && b !== null)
|
|
11
|
+
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
12
|
+
extendStatics(d, b);
|
|
13
|
+
function __() { this.constructor = d; }
|
|
14
|
+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
15
|
+
};
|
|
16
|
+
})();
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.FileCopyRefused = exports.getFileCopyForOthers = void 0;
|
|
19
|
+
var reflection_1 = require("@proteinjs/reflection");
|
|
20
|
+
var getFileCopyForOthers = function () {
|
|
21
|
+
return reflection_1.SourceRepository.get().object('@proteinjs/db-file/FileCopyForOthers');
|
|
22
|
+
};
|
|
23
|
+
exports.getFileCopyForOthers = getFileCopyForOthers;
|
|
24
|
+
/**
|
|
25
|
+
* What a non-owner's read becomes when the registered maker could not make a copy: the file is
|
|
26
|
+
* served to no one but its owner. The seam's own cause, thrown by `FileStorage` in place of
|
|
27
|
+
* whatever the maker threw (the maker's own one-line reason kept as `reason`) and answered by
|
|
28
|
+
* `FileStorage` itself, once for every door — the browser service, `GET /file/:id`, a server-side
|
|
29
|
+
* reader — as a 404 `ServiceRefusal` saying only `File not found`, so a door never meets it: a
|
|
30
|
+
* deliberate refusal every door tells from a failure the same way. Its message and `reason` stay
|
|
31
|
+
* on the server (`FileStorage` logs them at WARN); a caller reads the words a missing file gets.
|
|
32
|
+
* Read by shape ({@link FileCopyRefused.is}), so it holds across duplicate copies of this package.
|
|
33
|
+
*/
|
|
34
|
+
var FileCopyRefused = /** @class */ (function (_super) {
|
|
35
|
+
__extends(FileCopyRefused, _super);
|
|
36
|
+
function FileCopyRefused(fileId, cause) {
|
|
37
|
+
var _this = this;
|
|
38
|
+
var reason = FileCopyRefused.plain(cause);
|
|
39
|
+
_this = _super.call(this, "File ".concat(fileId, " is not available to anyone but its owner").concat(reason ? ": ".concat(reason) : '')) || this;
|
|
40
|
+
_this.name = FileCopyRefused.NAME;
|
|
41
|
+
Object.setPrototypeOf(_this, FileCopyRefused.prototype);
|
|
42
|
+
_this.fileId = fileId;
|
|
43
|
+
_this.reason = reason;
|
|
44
|
+
return _this;
|
|
45
|
+
}
|
|
46
|
+
FileCopyRefused.is = function (error) {
|
|
47
|
+
return (error === null || error === void 0 ? void 0 : error.name) === FileCopyRefused.NAME;
|
|
48
|
+
};
|
|
49
|
+
FileCopyRefused.plain = function (cause) {
|
|
50
|
+
var message = cause instanceof Error ? cause.message : typeof cause === 'string' ? cause : '';
|
|
51
|
+
var line = message.replace(/\s+/g, ' ').trim();
|
|
52
|
+
return line.length > FileCopyRefused.MAX_REASON_CHARS
|
|
53
|
+
? "".concat(line.slice(0, FileCopyRefused.MAX_REASON_CHARS), "\u2026")
|
|
54
|
+
: line;
|
|
55
|
+
};
|
|
56
|
+
FileCopyRefused.NAME = 'FileCopyRefused';
|
|
57
|
+
/** Free text that reaches a message is one line and bounded. */
|
|
58
|
+
FileCopyRefused.MAX_REASON_CHARS = 300;
|
|
59
|
+
return FileCopyRefused;
|
|
60
|
+
}(Error));
|
|
61
|
+
exports.FileCopyRefused = FileCopyRefused;
|
|
62
|
+
//# sourceMappingURL=FileCopyForOthers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileCopyForOthers.js","sourceRoot":"","sources":["../../src/FileCopyForOthers.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,oDAAmE;AA8B5D,IAAM,oBAAoB,GAAG;IAClC,OAAA,6BAAgB,CAAC,GAAG,EAAE,CAAC,MAAM,CAAoB,sCAAsC,CAAC;AAAxF,CAAwF,CAAC;AAD9E,QAAA,oBAAoB,wBAC0D;AAE3F;;;;;;;;;GASG;AACH;IAAqC,mCAAK;IAOxC,yBAAY,MAAc,EAAE,KAAc;QAA1C,iBAOC;QANC,IAAM,MAAM,GAAG,eAAe,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBAC5C,kBAAM,eAAQ,MAAM,sDAA4C,MAAM,CAAC,CAAC,CAAC,YAAK,MAAM,CAAE,CAAC,CAAC,CAAC,EAAE,CAAE,CAAC;QAC9F,KAAI,CAAC,IAAI,GAAG,eAAe,CAAC,IAAI,CAAC;QACjC,MAAM,CAAC,cAAc,CAAC,KAAI,EAAE,eAAe,CAAC,SAAS,CAAC,CAAC;QACvD,KAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,KAAI,CAAC,MAAM,GAAG,MAAM,CAAC;;IACvB,CAAC;IAEM,kBAAE,GAAT,UAAU,KAAc;QACtB,OAAO,CAAC,KAA+C,aAA/C,KAAK,uBAAL,KAAK,CAA4C,IAAI,MAAK,eAAe,CAAC,IAAI,CAAC;IACzF,CAAC;IAEc,qBAAK,GAApB,UAAqB,KAAc;QACjC,IAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QAChG,IAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QACjD,OAAO,IAAI,CAAC,MAAM,GAAG,eAAe,CAAC,gBAAgB;YACnD,CAAC,CAAC,UAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,eAAe,CAAC,gBAAgB,CAAC,WAAG;YACvD,CAAC,CAAC,IAAI,CAAC;IACX,CAAC;IAzBuB,oBAAI,GAAG,iBAAiB,CAAC;IACjD,gEAAgE;IACxC,gCAAgB,GAAG,GAAG,CAAC;IAwBjD,sBAAC;CAAA,AA3BD,CAAqC,KAAK,GA2BzC;AA3BY,0CAAe"}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
1
3
|
import { ScopedRecord } from '@proteinjs/user';
|
|
2
4
|
import { File } from './tables/FileTable';
|
|
3
5
|
import { FileStorageService } from './services/FileStorageService';
|
|
4
6
|
import { FileStorageDriver } from './FileStorageDriver';
|
|
7
|
+
import { FileVariantKind } from './FileVariantMaker';
|
|
5
8
|
import { Loadable } from '@proteinjs/reflection';
|
|
6
9
|
/**
|
|
7
10
|
* A convenience factory function so code using this is portable (can be used in server or browser).
|
|
@@ -21,11 +24,14 @@ export interface DefaultFileStorageDriverFactory extends Loadable {
|
|
|
21
24
|
*/
|
|
22
25
|
export declare class FileStorage implements FileStorageService {
|
|
23
26
|
private static driver;
|
|
27
|
+
/** The read path's derivations running in this process, by file and kind — see {@link deriveOnce}. */
|
|
28
|
+
private static derivations;
|
|
24
29
|
serviceMetadata: {
|
|
25
30
|
auth: {
|
|
26
31
|
allUsers: boolean;
|
|
27
32
|
};
|
|
28
33
|
};
|
|
34
|
+
private logger;
|
|
29
35
|
/**
|
|
30
36
|
* The `FileStorageDriver` for this process — provided by the `DefaultFileStorageDriverFactory`
|
|
31
37
|
* implementation, defaulting to the `DbFileStorageDriver`. One resolution for every byte
|
|
@@ -44,15 +50,46 @@ export declare class FileStorage implements FileStorageService {
|
|
|
44
50
|
* Retrieves the metadata of a given file — THE file-read access decision every serving path
|
|
45
51
|
* derives from (`/file/:id`, signed-URL minting, the browser service).
|
|
46
52
|
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* can read
|
|
50
|
-
*
|
|
51
|
-
*
|
|
53
|
+
* Three legs, cheapest first: the caller's own file resolves by the SCOPED read. When that
|
|
54
|
+
* misses, the derived leg: a VARIANT the seam made (`File.variantOf`) is readable by whoever
|
|
55
|
+
* can read its original — this same decision, asked of the original — so a variant derived
|
|
56
|
+
* after the content was placed (no content row names it) is reachable by exactly the readers
|
|
57
|
+
* of the file it was made from; one system point read decides whether the row is a variant at
|
|
58
|
+
* all. Last, the shared-content leg asks the registered {@link FileReachabilityResolver}s
|
|
59
|
+
* whether the caller can read a row that REFERENCES the file (a shared thought's media node) —
|
|
60
|
+
* content access confers file access, through the content's own grant-filtered read, never by
|
|
61
|
+
* widening file scope. No leg vouching means the miss stands. Reads only: writes/deletes stay
|
|
62
|
+
* scoped.
|
|
52
63
|
* @param fileId - The `id` of the file.
|
|
53
64
|
* @returns The file metadata, or `undefined` when the caller can neither own nor reach it.
|
|
54
65
|
*/
|
|
55
66
|
getFile(fileId: string): Promise<File>;
|
|
67
|
+
/**
|
|
68
|
+
* The variant of this kind of a file — the derived File the row names, or one made now, ONCE,
|
|
69
|
+
* by the registered {@link FileVariantMaker} (the read path's door, behind
|
|
70
|
+
* `GET /file/:id/variant/:kind`: a file made before the seam existed, or by a producer that did
|
|
71
|
+
* not derive it, gets its variant on the first request from a surface that draws it; every
|
|
72
|
+
* later request is served the same row without the maker). Server-only, like `getSignedUrl` —
|
|
73
|
+
* the browser asks by URL, never by service call. Access is the original's ({@link getFile});
|
|
74
|
+
* the variant is stored as the owner's own File whoever's read made it, and is served like any
|
|
75
|
+
* File afterwards — with the same non-owner copy rule.
|
|
76
|
+
* @returns The variant's File row, or `undefined` when the caller cannot read the original, no
|
|
77
|
+
* maker is registered, the maker does not apply to this file for this kind, or the
|
|
78
|
+
* maker could not make it of these bytes (the consumer then draws the original).
|
|
79
|
+
*/
|
|
80
|
+
getVariant(fileId: string, kind: FileVariantKind): Promise<File | undefined>;
|
|
81
|
+
/**
|
|
82
|
+
* Make and store every variant the registered maker applies to, with the original's bytes in
|
|
83
|
+
* hand (the ingest's door — one decode of bytes just stored, never a re-read). Each is named on
|
|
84
|
+
* the row; a kind the maker does not apply to is left unset (the read path may derive it later
|
|
85
|
+
* if the maker's rule changes). With no maker registered nothing is made.
|
|
86
|
+
* @returns The caller's file row with each variant made here named on it (the row as the caller
|
|
87
|
+
* holds it, not a re-read — an unset column stays unset), and the variant Files made.
|
|
88
|
+
*/
|
|
89
|
+
deriveVariants(file: File, bytes: Buffer): Promise<{
|
|
90
|
+
file: File;
|
|
91
|
+
variants: Partial<Record<FileVariantKind, File>>;
|
|
92
|
+
}>;
|
|
56
93
|
/**
|
|
57
94
|
* Retrieves the data chunks associated with a given file.
|
|
58
95
|
*
|
|
@@ -61,24 +98,49 @@ export declare class FileStorage implements FileStorageService {
|
|
|
61
98
|
* exactly as reachable as the row that names them. The gate lives here because this is the
|
|
62
99
|
* browser-facing service boundary (`FileStorageService` is `allUsers`) and the driver byte ops
|
|
63
100
|
* below are deliberately unscoped. Server-side doors that make their OWN documented access
|
|
64
|
-
* decision (
|
|
65
|
-
*
|
|
101
|
+
* decision (an attachment door keyed by a record's linkage, say) serve through
|
|
102
|
+
* {@link getAuthorizedFileData} — the same owner-or-copy bytes, without this gate.
|
|
103
|
+
*
|
|
104
|
+
* WHICH bytes: the owner's read serves the original; anyone else's — a read that reached the
|
|
105
|
+
* file through the shared-content leg — serves the {@link FileCopyForOthers} copy
|
|
106
|
+
* ({@link servedFileId}): the same substitution `getSignedUrl` makes, so the proxy route, the
|
|
107
|
+
* signed-URL route and every server-side reader of this door agree.
|
|
66
108
|
* @param fileId - The `id` of the file.
|
|
67
109
|
* @returns The file data as a single string.
|
|
68
|
-
* @throws
|
|
69
|
-
*
|
|
110
|
+
* @throws ServiceRefusal (404, `File not found`) when the file row does not exist or is not
|
|
111
|
+
* readable by the caller, and when the caller is not the owner and no copy can be made
|
|
112
|
+
* ({@link servedFileId}) — ONE answer for all three ({@link notFound}), so no door tells
|
|
113
|
+
* a missing file from one this caller may not have.
|
|
70
114
|
*/
|
|
71
115
|
getFileData(fileId: string): Promise<string>;
|
|
116
|
+
/**
|
|
117
|
+
* The bytes THIS caller is served for a file a server-side door has ALREADY decided the caller
|
|
118
|
+
* may read, by its own documented rule (an attachment door keyed by a record's linkage, say —
|
|
119
|
+
* a read neither the caller's scope nor a {@link FileReachabilityResolver} would open). The
|
|
120
|
+
* door vouches for access; this decides WHICH bytes, exactly as {@link getFileData} does: the
|
|
121
|
+
* original for the file's owner, the {@link FileCopyForOthers} copy for anyone else — the same
|
|
122
|
+
* copy, made once and named on the row, that every other door serves them. Server-only (not
|
|
123
|
+
* part of the browser-facing `FileStorageService`), so the rule that opens the file stays the
|
|
124
|
+
* door's.
|
|
125
|
+
* @param file - The file's row, as the door read it (its `scope` names the owner).
|
|
126
|
+
* @returns The file data as a single string.
|
|
127
|
+
* @throws ServiceRefusal (404) when the caller is not the owner and no copy can be made — the same
|
|
128
|
+
* refusal every door gives ({@link servedFileId}).
|
|
129
|
+
*/
|
|
130
|
+
getAuthorizedFileData(file: File): Promise<string>;
|
|
72
131
|
/**
|
|
73
132
|
* Mint a short-lived, read-only URL for the file's bytes (server-only; not part of the
|
|
74
133
|
* browser-facing `FileStorageService`). A signed URL is a bearer capability, so minting
|
|
75
134
|
* verifies the caller can read the file row (the two-legged {@link getFile} read — own scope
|
|
76
|
-
* or shared-content reachability) before signing
|
|
135
|
+
* or shared-content reachability) before signing — and signs the bytes THAT caller is served:
|
|
136
|
+
* the original for the owner, the {@link FileCopyForOthers} copy for anyone else.
|
|
77
137
|
* @param fileId - The `id` of the file.
|
|
78
138
|
* @param options.ttlMs - How long the URL stays valid; the driver applies its default when omitted.
|
|
79
139
|
* @returns The signed URL, or `undefined` when the driver has no external URL space
|
|
80
140
|
* (`DbFileStorageDriver`) — the caller then serves bytes through the proxy route.
|
|
81
|
-
* @throws
|
|
141
|
+
* @throws ServiceRefusal (404, `File not found`) when the file row does not exist or is not
|
|
142
|
+
* readable by the caller, and when the caller is not the owner and no copy can be made —
|
|
143
|
+
* the one answer {@link getFileData} gives ({@link notFound}).
|
|
82
144
|
*/
|
|
83
145
|
getSignedUrl(fileId: string, options?: {
|
|
84
146
|
ttlMs?: number;
|
|
@@ -90,6 +152,10 @@ export declare class FileStorage implements FileStorageService {
|
|
|
90
152
|
* (the same authorization a scoped row update enforces). Deliberately STRICTER than
|
|
91
153
|
* {@link getFileData}: the {@link FileReachabilityResolver} leg widens READS only, so a
|
|
92
154
|
* share recipient who can read the bytes still cannot write them.
|
|
155
|
+
*
|
|
156
|
+
* New bytes make every derived File stale — the copy others were served, the preview, the
|
|
157
|
+
* stage variant: each is dropped here — the row forgets it, then its row (and, through the
|
|
158
|
+
* delete watcher, its bytes) goes — and the next read that wants one makes a fresh one.
|
|
93
159
|
* @param fileId - The `id` of the file.
|
|
94
160
|
* @param data - The new data string to replace the existing data.
|
|
95
161
|
* @throws When the file row does not exist or is not writable by the caller — the same named
|
|
@@ -109,5 +175,73 @@ export declare class FileStorage implements FileStorageService {
|
|
|
109
175
|
* @param fileId - The `id` of the file to delete.
|
|
110
176
|
*/
|
|
111
177
|
deleteFile(fileId: string): Promise<void>;
|
|
178
|
+
/**
|
|
179
|
+
* The id whose bytes this caller is served: the file's own for its owner, or for a file with
|
|
180
|
+
* no maker (or one the maker does not apply to); otherwise the {@link FileCopyForOthers} copy —
|
|
181
|
+
* the one already named on the row, or made now, once, and named for every later read.
|
|
182
|
+
*
|
|
183
|
+
* ONE REFUSAL, EVERY DOOR: when no copy can be made (the seam's {@link FileCopyRefused}), the file
|
|
184
|
+
* is served to no one but its owner, so to this caller it is not there — a
|
|
185
|
+
* `ServiceRefusal(404, 'File not found')`, thrown here so the browser service, `GET /file/:id` in
|
|
186
|
+
* both its shapes and the server-side door answer the same by construction: the service router
|
|
187
|
+
* answers 404 `{ error: 'File not found' }`, the executor logs one WARN (a refusal, never a
|
|
188
|
+
* failure), the route answers `404 File not found` — the same answer, at every door, as a row the
|
|
189
|
+
* caller cannot read ({@link notFound}). The refusal carries no reason, file id or owner: the
|
|
190
|
+
* maker's reason is the server's, logged here in one WARN, so nothing a caller reads tells a
|
|
191
|
+
* refused copy from a file that is not there.
|
|
192
|
+
*/
|
|
193
|
+
private servedFileId;
|
|
194
|
+
/**
|
|
195
|
+
* Whether the caller is the file's owner — the row's `scope` is the owner (the scoped read
|
|
196
|
+
* {@link getFile} makes first is exactly `scope = the caller`), so a row {@link getFile} handed
|
|
197
|
+
* back whose scope is someone else's was reached through the shared-content leg. A row with no
|
|
198
|
+
* owner recorded serves as itself: there is no one to withhold it from.
|
|
199
|
+
*/
|
|
200
|
+
private ownedByCaller;
|
|
201
|
+
/**
|
|
202
|
+
* Makes the copy (the maker's bytes, from the original's), stores it as a File in the owner's
|
|
203
|
+
* scope and names it on the original's row. Two non-owners reading at once may both get here:
|
|
204
|
+
* the row's word wins — decided in ONE transaction (the read of the row and the write of its
|
|
205
|
+
* word together, so racing writers are serialized and the loser's transaction, retried, finds
|
|
206
|
+
* the winner named) — and a copy that lost is deleted after the commit, the first is served.
|
|
207
|
+
*/
|
|
208
|
+
private makeCopyForOthers;
|
|
209
|
+
/**
|
|
210
|
+
* The read path's derivation, ONCE per file and kind at a time in this process: the readers
|
|
211
|
+
* that ask for the same absent variant while one derives (a shared document opened by many on
|
|
212
|
+
* one tick — every face asks the same URL) wait for that derivation's answer instead of each
|
|
213
|
+
* reading the original, encoding it and storing an object the row's word would then discard.
|
|
214
|
+
* Across processes the row's word in one transaction ({@link makeVariant}) still decides: this
|
|
215
|
+
* bounds the cost, that bounds the outcome. A maker that cannot make the variant of these
|
|
216
|
+
* bytes (a picture its decoder cannot read) answers `undefined` — nothing is stored, the reader
|
|
217
|
+
* draws the file itself, the next reader asks again — said in the log, never as a failed read;
|
|
218
|
+
* any other failure (the store, the database) is the reader's as it would be for any File.
|
|
219
|
+
*/
|
|
220
|
+
private deriveOnce;
|
|
221
|
+
/**
|
|
222
|
+
* Makes the variant (the maker's answer from the original's bytes), stores it as a File in the
|
|
223
|
+
* OWNER's scope naming its original, and names it on the original's row. Two readers may derive
|
|
224
|
+
* the same kind at once: the row's word wins — decided in ONE transaction, exactly as the copy
|
|
225
|
+
* for others is named — and the variant that lost is deleted after the commit. The maker's own
|
|
226
|
+
* failure (it could not make the variant of these bytes) is thrown as {@link FileVariantNotMade}
|
|
227
|
+
* with the maker's reason, so the doors can tell it from a failure of the store or the database;
|
|
228
|
+
* nothing is stored on it.
|
|
229
|
+
* @returns The id of the variant the row names.
|
|
230
|
+
*/
|
|
231
|
+
private makeVariant;
|
|
232
|
+
/**
|
|
233
|
+
* The row forgets each derived File first (nothing serves a copy or a variant the row no longer
|
|
234
|
+
* names), then their rows go — the bytes with them, through the delete watcher: the copy for
|
|
235
|
+
* others and every variant kind, one rule.
|
|
236
|
+
*/
|
|
237
|
+
private dropDerivedFiles;
|
|
238
|
+
/**
|
|
239
|
+
* THE answer a byte door gives a caller who does not get the file's bytes — a row that is not
|
|
240
|
+
* there, one this caller cannot read, or a copy that cannot be made for them: a 404 refusal in
|
|
241
|
+
* the same words every time, never the id, so the answer reveals nothing about existence (the
|
|
242
|
+
* service router sends `{ error: 'File not found' }`, the executor logs it at WARN, the routes
|
|
243
|
+
* send `404 File not found`).
|
|
244
|
+
*/
|
|
245
|
+
private static notFound;
|
|
112
246
|
}
|
|
113
247
|
//# sourceMappingURL=FileStorage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileStorage.d.ts","sourceRoot":"","sources":["../../src/FileStorage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"FileStorage.d.ts","sourceRoot":"","sources":["../../src/FileStorage.ts"],"names":[],"mappings":";;AACA,OAAO,EAAE,YAAY,EAA8C,MAAM,iBAAiB,CAAC;AAC3F,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAE1C,OAAO,EAAE,kBAAkB,EAAyB,MAAM,+BAA+B,CAAC;AAC1F,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAGxD,OAAO,EAGL,eAAe,EAGhB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,QAAQ,EAAoB,MAAM,uBAAuB,CAAC;AAKnE;;;KAGK;AACL,eAAO,MAAM,cAAc,mBACiE,CAAC;AAE7F;;GAEG;AACH,MAAM,WAAW,+BAAgC,SAAQ,QAAQ;IAC/D,SAAS,IAAI,iBAAiB,CAAC;CAChC;AAED;;;;GAIG;AACH,qBAAa,WAAY,YAAW,kBAAkB;IACpD,OAAO,CAAC,MAAM,CAAC,MAAM,CAAoB;IACzC,sGAAsG;IACtG,OAAO,CAAC,MAAM,CAAC,WAAW,CAAkD;IAErE,eAAe;;;;MAIpB;IACF,OAAO,CAAC,MAAM,CAA+C;IAE7D;;;;;OAKG;IACH,MAAM,CAAC,SAAS,IAAI,iBAAiB;IAkBrC;;;;;OAKG;IACG,UAAU,CAAC,YAAY,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,YAAY,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO/F;;;;;;;;;;;;;;;;OAgBG;IACG,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA4B5C;;;;;;;;;;;;OAYG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,GAAG,SAAS,CAAC;IAiBlF;;;;;;;OAOG;IACG,cAAc,CAClB,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC;QAAE,IAAI,EAAE,IAAI,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC,CAAA;KAAE,CAAC;IAgB5E;;;;;;;;;;;;;;;;;;;;;OAqBG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IASlD;;;;;;;;;;;;;OAaG;IACG,qBAAqB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC;IAIxD;;;;;;;;;;;;;OAaG;IACG,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAc7F;;;;;;;;;;;;;;;OAeG;IACG,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWjE;;;OAGG;IACG,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,YAAY,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAUrE;;;;;;OAMG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK/C;;;;;;;;;;;;;;OAcG;YACW,YAAY;IAyB1B;;;;;OAKG;IACH,OAAO,CAAC,aAAa;IAIrB;;;;;;OAMG;YACW,iBAAiB;IAwC/B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,UAAU;IAyBlB;;;;;;;;;OASG;YACW,WAAW;IAuCzB;;;;OAIG;YACW,gBAAgB;IAiB9B;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ;CAGxB"}
|