@peers-app/peers-sdk 0.30.0 → 0.30.2
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/dist/contracts/contract-resolvers.js +1 -0
- package/dist/contracts/index.d.ts +1 -0
- package/dist/contracts/index.js +1 -0
- package/dist/contracts/system/file-storage-contract.d.ts +170 -0
- package/dist/contracts/system/file-storage-contract.js +123 -0
- package/dist/contracts/system/files-contract.d.ts +5 -1
- package/dist/contracts/system/files-contract.js +50 -9
- package/dist/contracts/system/files-shared.d.ts +68 -3
- package/dist/contracts/system/files-shared.js +65 -5
- package/dist/data/derived-row-id.d.ts +26 -0
- package/dist/data/derived-row-id.js +47 -0
- package/dist/data/files/chunk-store.d.ts +119 -0
- package/dist/data/files/chunk-store.js +112 -0
- package/dist/data/files/file-read-stream.d.ts +32 -3
- package/dist/data/files/file-read-stream.js +44 -29
- package/dist/data/files/file-rows.d.ts +77 -0
- package/dist/data/files/file-rows.js +182 -0
- package/dist/data/files/file-write-stream.d.ts +27 -0
- package/dist/data/files/file-write-stream.js +67 -42
- package/dist/data/files/file.types.d.ts +59 -23
- package/dist/data/files/file.types.js +54 -51
- package/dist/data/files/files.d.ts +28 -24
- package/dist/data/files/files.js +71 -85
- package/dist/data/files/index.d.ts +2 -0
- package/dist/data/files/index.js +2 -0
- package/dist/data/groups.d.ts +13 -0
- package/dist/data/groups.js +6 -0
- package/dist/data/index.d.ts +1 -0
- package/dist/data/index.js +1 -0
- package/dist/data/orm/write-checks.d.ts +15 -0
- package/dist/data/orm/write-checks.js +21 -0
- package/dist/define/system-contracts.d.ts +9 -2
- package/dist/define/system-contracts.js +6 -2
- package/dist/device-pairing/types.d.ts +2 -2
- package/dist/invites/invite-messages.d.ts +38 -0
- package/dist/package-installer/package-installer.d.ts +3 -5
- package/dist/package-installer/package-installer.js +5 -21
- package/dist/package-installer/package-propagation.js +6 -17
- package/dist/system-ids.d.ts +2 -0
- package/dist/system-ids.js +3 -1
- package/package.json +1 -1
|
@@ -5,8 +5,10 @@ import { FileReadStream } from "./file-read-stream";
|
|
|
5
5
|
import { FileWriteStream } from "./file-write-stream";
|
|
6
6
|
/**
|
|
7
7
|
* The chunked, content-hashed file store of one data context. Rows describe files; the bytes
|
|
8
|
-
* live in chunks
|
|
9
|
-
* device when it reads.
|
|
8
|
+
* live in chunks in the account's chunk store, shared by hash, and a device fetches a missing
|
|
9
|
+
* chunk from a connected device when it reads. A file's id is derived from its content, and its row never changes.
|
|
10
|
+
* Write through {@link FilesTable.saveFile}, never the table methods. The rules are checked here
|
|
11
|
+
* in every context, and by every device on the rows it receives in a group (`file-rows.ts`).
|
|
10
12
|
*/
|
|
11
13
|
export declare class FilesTable extends Table<IFile> {
|
|
12
14
|
/** @deprecated Direct inserts forbidden; use saveFile() or saveFileRecord() */
|
|
@@ -20,14 +22,19 @@ export declare class FilesTable extends Table<IFile> {
|
|
|
20
22
|
/**
|
|
21
23
|
* Start writing a file in chunks; `finalize()` saves its row.
|
|
22
24
|
*
|
|
23
|
-
* @param metadata - The new file's
|
|
25
|
+
* @param metadata - The new file's name, MIME type and `keep`.
|
|
24
26
|
*/
|
|
25
27
|
createWriteStream(metadata: IFileInput): Promise<FileWriteStream>;
|
|
26
28
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* @
|
|
29
|
+
* Store a file's row as it is, after the checks every device makes: a new row must have the
|
|
30
|
+
* id derived from its content (or be a legacy row), and a stored row never changes. Storing
|
|
31
|
+
* an existing ordinary file again writes nothing; saving a runtime bundle publishes a newer
|
|
32
|
+
* synced snapshot, including when restoring a deleted row. Writers use {@link saveFile} or a
|
|
33
|
+
* write stream; this is for copying a row from another context with its id.
|
|
34
|
+
*
|
|
35
|
+
* @param fileRecord - The row.
|
|
36
|
+
* @returns The row as stored.
|
|
37
|
+
* @throws When the row is refused, or a different row has its id.
|
|
31
38
|
*/
|
|
32
39
|
saveFileRecord(fileRecord: IFile): Promise<IFile>;
|
|
33
40
|
/**
|
|
@@ -40,11 +47,12 @@ export declare class FilesTable extends Table<IFile> {
|
|
|
40
47
|
*/
|
|
41
48
|
openReadStream(fileId: string, preloadChunksCount?: number, skipVerification?: boolean): Promise<FileReadStream | null>;
|
|
42
49
|
/**
|
|
43
|
-
* Save a whole file: chunk it, store the chunks and write its row.
|
|
50
|
+
* Save a whole file: chunk it, store the chunks and write its row. The same bytes, name, MIME
|
|
51
|
+
* type and `keep` give the same file, whose chunks are stored again.
|
|
44
52
|
*
|
|
45
|
-
* @param metaData - The file's
|
|
53
|
+
* @param metaData - The file's name, MIME type and `keep` (true unless given).
|
|
46
54
|
* @param data - The bytes, or a string saved as UTF-8.
|
|
47
|
-
* @returns The
|
|
55
|
+
* @returns The file's row.
|
|
48
56
|
*/
|
|
49
57
|
saveFile(metaData: IFileInput, data: Uint8Array | string): Promise<IFile>;
|
|
50
58
|
/**
|
|
@@ -58,25 +66,21 @@ export declare class FilesTable extends Table<IFile> {
|
|
|
58
66
|
skipVerification?: boolean;
|
|
59
67
|
}): Promise<Uint8Array | null>;
|
|
60
68
|
/**
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
* @param fileId - File to delete. A missing file is not an error.
|
|
64
|
-
*/
|
|
65
|
-
deleteFile(fileId: string): Promise<void>;
|
|
66
|
-
/**
|
|
67
|
-
* Save a large file's chunk hashes as an index file, itself chunked when large.
|
|
69
|
+
* Make sure this device has every chunk of a file, fetching the missing ones from connected
|
|
70
|
+
* devices {@link FILE_FETCH_WINDOW} at a time, without reading the file into memory.
|
|
68
71
|
*
|
|
69
|
-
* @param
|
|
70
|
-
* @returns
|
|
72
|
+
* @param fileId - File to fetch.
|
|
73
|
+
* @returns Whether every chunk is now on this device; false for a file with no row.
|
|
71
74
|
*/
|
|
72
|
-
|
|
75
|
+
fetchFile(fileId: string): Promise<boolean>;
|
|
73
76
|
/**
|
|
74
|
-
*
|
|
77
|
+
* Delete a runtime bundle row, which the runtime owns. Every other file is permanent: it's
|
|
78
|
+
* collected once nothing references it. Chunks stay stored, since other files may share them.
|
|
75
79
|
*
|
|
76
|
-
* @param
|
|
77
|
-
* @
|
|
80
|
+
* @param fileId - File to delete. A missing file is not an error.
|
|
81
|
+
* @throws When the file isn't a runtime bundle row.
|
|
78
82
|
*/
|
|
79
|
-
|
|
83
|
+
deleteFile(fileId: string): Promise<void>;
|
|
80
84
|
}
|
|
81
85
|
/**
|
|
82
86
|
* The Files table of a data context.
|
package/dist/data/files/files.js
CHANGED
|
@@ -4,16 +4,19 @@ exports.FilesTable = void 0;
|
|
|
4
4
|
exports.Files = Files;
|
|
5
5
|
const user_context_singleton_1 = require("../../context/user-context-singleton");
|
|
6
6
|
const rpc_types_1 = require("../../rpc-types");
|
|
7
|
-
const utils_1 = require("../../utils");
|
|
8
7
|
const orm_1 = require("../orm");
|
|
9
8
|
const table_definitions_system_1 = require("../orm/table-definitions.system");
|
|
9
|
+
const chunk_store_1 = require("./chunk-store");
|
|
10
10
|
const file_types_1 = require("./file.types");
|
|
11
11
|
const file_read_stream_1 = require("./file-read-stream");
|
|
12
|
+
const file_rows_1 = require("./file-rows");
|
|
12
13
|
const file_write_stream_1 = require("./file-write-stream");
|
|
13
14
|
/**
|
|
14
15
|
* The chunked, content-hashed file store of one data context. Rows describe files; the bytes
|
|
15
|
-
* live in chunks
|
|
16
|
-
* device when it reads.
|
|
16
|
+
* live in chunks in the account's chunk store, shared by hash, and a device fetches a missing
|
|
17
|
+
* chunk from a connected device when it reads. A file's id is derived from its content, and its row never changes.
|
|
18
|
+
* Write through {@link FilesTable.saveFile}, never the table methods. The rules are checked here
|
|
19
|
+
* in every context, and by every device on the rows it receives in a group (`file-rows.ts`).
|
|
17
20
|
*/
|
|
18
21
|
class FilesTable extends orm_1.Table {
|
|
19
22
|
/** @deprecated Direct inserts forbidden; use saveFile() or saveFileRecord() */
|
|
@@ -35,19 +38,39 @@ class FilesTable extends orm_1.Table {
|
|
|
35
38
|
/**
|
|
36
39
|
* Start writing a file in chunks; `finalize()` saves its row.
|
|
37
40
|
*
|
|
38
|
-
* @param metadata - The new file's
|
|
41
|
+
* @param metadata - The new file's name, MIME type and `keep`.
|
|
39
42
|
*/
|
|
40
43
|
async createWriteStream(metadata) {
|
|
41
44
|
return new file_write_stream_1.FileWriteStream(metadata, this);
|
|
42
45
|
}
|
|
43
46
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* @
|
|
47
|
+
* Store a file's row as it is, after the checks every device makes: a new row must have the
|
|
48
|
+
* id derived from its content (or be a legacy row), and a stored row never changes. Storing
|
|
49
|
+
* an existing ordinary file again writes nothing; saving a runtime bundle publishes a newer
|
|
50
|
+
* synced snapshot, including when restoring a deleted row. Writers use {@link saveFile} or a
|
|
51
|
+
* write stream; this is for copying a row from another context with its id.
|
|
52
|
+
*
|
|
53
|
+
* @param fileRecord - The row.
|
|
54
|
+
* @returns The row as stored.
|
|
55
|
+
* @throws When the row is refused, or a different row has its id.
|
|
48
56
|
*/
|
|
49
57
|
async saveFileRecord(fileRecord) {
|
|
50
|
-
|
|
58
|
+
const existing = await super.get(fileRecord.fileId);
|
|
59
|
+
(0, file_rows_1.checkFileWrite)(existing, fileRecord, fileRecord.fileId);
|
|
60
|
+
const runtimeBundle = (0, file_rows_1.isRuntimeBundleFile)(fileRecord);
|
|
61
|
+
if (existing && !runtimeBundle)
|
|
62
|
+
return existing;
|
|
63
|
+
// An ordinary file inserted since the read must stay untouched. Bundles can be rewritten
|
|
64
|
+
// identically so their pending reuse reaches collection's write watch and other devices.
|
|
65
|
+
const saved = await super.save(existing ?? fileRecord, {
|
|
66
|
+
insertOnly: !runtimeBundle || !existing,
|
|
67
|
+
// Even when still present locally, a reused bundle needs a new snapshot: another device
|
|
68
|
+
// may have deleted it, and collection's write watch must protect this pending reuse.
|
|
69
|
+
restoreIfDeleted: runtimeBundle,
|
|
70
|
+
saveAsSnapshot: runtimeBundle,
|
|
71
|
+
});
|
|
72
|
+
(0, file_rows_1.checkFileWrite)(saved, fileRecord, fileRecord.fileId);
|
|
73
|
+
return saved;
|
|
51
74
|
}
|
|
52
75
|
/**
|
|
53
76
|
* Open a file for chunked reads.
|
|
@@ -62,27 +85,19 @@ class FilesTable extends orm_1.Table {
|
|
|
62
85
|
if (!fileRecord) {
|
|
63
86
|
return null;
|
|
64
87
|
}
|
|
65
|
-
return new file_read_stream_1.FileReadStream(fileRecord,
|
|
88
|
+
return new file_read_stream_1.FileReadStream(fileRecord, preloadChunksCount, skipVerification);
|
|
66
89
|
}
|
|
67
90
|
/**
|
|
68
|
-
* Save a whole file: chunk it, store the chunks and write its row.
|
|
91
|
+
* Save a whole file: chunk it, store the chunks and write its row. The same bytes, name, MIME
|
|
92
|
+
* type and `keep` give the same file, whose chunks are stored again.
|
|
69
93
|
*
|
|
70
|
-
* @param metaData - The file's
|
|
94
|
+
* @param metaData - The file's name, MIME type and `keep` (true unless given).
|
|
71
95
|
* @param data - The bytes, or a string saved as UTF-8.
|
|
72
|
-
* @returns The
|
|
96
|
+
* @returns The file's row.
|
|
73
97
|
*/
|
|
74
98
|
async saveFile(metaData, data) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
...metaData,
|
|
78
|
-
fileSize: data.length, // Set the actual file size
|
|
79
|
-
});
|
|
80
|
-
// Write the entire data to the stream
|
|
81
|
-
if (typeof data === "string") {
|
|
82
|
-
data = new Uint8Array(Buffer.from(data, "utf8"));
|
|
83
|
-
}
|
|
84
|
-
await writeStream.write(data);
|
|
85
|
-
// Finalize and return the result
|
|
99
|
+
const writeStream = await this.createWriteStream(metaData);
|
|
100
|
+
await writeStream.write(typeof data === "string" ? new Uint8Array(Buffer.from(data, "utf8")) : data);
|
|
86
101
|
return await writeStream.finalize();
|
|
87
102
|
}
|
|
88
103
|
/**
|
|
@@ -93,8 +108,7 @@ class FilesTable extends orm_1.Table {
|
|
|
93
108
|
* @returns The bytes, or null when no such file row exists.
|
|
94
109
|
*/
|
|
95
110
|
async getFileContents(fileId, opts) {
|
|
96
|
-
|
|
97
|
-
const readStream = await this.openReadStream(fileId, undefined, opts?.skipVerification);
|
|
111
|
+
const readStream = await this.openReadStream(fileId, file_types_1.FILE_FETCH_WINDOW, opts?.skipVerification);
|
|
98
112
|
if (!readStream) {
|
|
99
113
|
return null;
|
|
100
114
|
}
|
|
@@ -102,65 +116,43 @@ class FilesTable extends orm_1.Table {
|
|
|
102
116
|
return await readStream.readAll();
|
|
103
117
|
}
|
|
104
118
|
/**
|
|
105
|
-
*
|
|
119
|
+
* Make sure this device has every chunk of a file, fetching the missing ones from connected
|
|
120
|
+
* devices {@link FILE_FETCH_WINDOW} at a time, without reading the file into memory.
|
|
106
121
|
*
|
|
107
|
-
* @param fileId - File to
|
|
122
|
+
* @param fileId - File to fetch.
|
|
123
|
+
* @returns Whether every chunk is now on this device; false for a file with no row.
|
|
108
124
|
*/
|
|
109
|
-
async
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
125
|
+
async fetchFile(fileId) {
|
|
126
|
+
const file = await this.get(fileId);
|
|
127
|
+
if (!file)
|
|
128
|
+
return false;
|
|
129
|
+
const store = await (0, chunk_store_1.getChunkStore)();
|
|
130
|
+
const chunkHashes = file.chunkHashes ?? [];
|
|
131
|
+
let complete = chunkHashes.length > 0 || file.fileSize === 0;
|
|
132
|
+
for (let start = 0; start < chunkHashes.length; start += file_types_1.FILE_FETCH_WINDOW) {
|
|
133
|
+
const window = chunkHashes.slice(start, start + file_types_1.FILE_FETCH_WINDOW);
|
|
134
|
+
const fetched = await Promise.all(window.map(async (hash) => (await store.has(hash)) ||
|
|
135
|
+
// The downloader checks a downloaded chunk's hash and stores it.
|
|
136
|
+
(await (0, chunk_store_1.downloadChunk)(hash).catch(() => null)) !== null));
|
|
137
|
+
if (fetched.includes(false))
|
|
138
|
+
complete = false;
|
|
114
139
|
}
|
|
115
|
-
|
|
116
|
-
if (fileRecord.indexFileId) {
|
|
117
|
-
await this.deleteFile(fileRecord.indexFileId);
|
|
118
|
-
}
|
|
119
|
-
// Note: We don't delete chunks since they may be shared with other files
|
|
120
|
-
// TODO Chunk cleanup needs to be implemented as a separate garbage collection process
|
|
121
|
-
// that removes chunks not referenced by any files
|
|
122
|
-
// Delete file record from database
|
|
123
|
-
await super.delete(fileId);
|
|
140
|
+
return complete;
|
|
124
141
|
}
|
|
125
142
|
/**
|
|
126
|
-
*
|
|
143
|
+
* Delete a runtime bundle row, which the runtime owns. Every other file is permanent: it's
|
|
144
|
+
* collected once nothing references it. Chunks stay stored, since other files may share them.
|
|
127
145
|
*
|
|
128
|
-
* @param
|
|
129
|
-
* @
|
|
130
|
-
*/
|
|
131
|
-
async createIndexFileRecursively(chunkHashes) {
|
|
132
|
-
// Create JSON content with chunk hashes
|
|
133
|
-
const indexContent = JSON.stringify(chunkHashes);
|
|
134
|
-
const indexBuffer = Buffer.from(indexContent, "utf8");
|
|
135
|
-
// Generate a new file ID for the index file
|
|
136
|
-
const indexFileId = (0, utils_1.newid)();
|
|
137
|
-
// Create metadata for the index file
|
|
138
|
-
const indexMetadata = {
|
|
139
|
-
fileId: indexFileId,
|
|
140
|
-
name: `index-${indexFileId}.json`,
|
|
141
|
-
fileSize: indexBuffer.length,
|
|
142
|
-
mimeType: "application/json",
|
|
143
|
-
isIndexFile: true,
|
|
144
|
-
};
|
|
145
|
-
// Recursively save the index file (this will chunk it if it's too large)
|
|
146
|
-
await this.saveFile(indexMetadata, indexBuffer);
|
|
147
|
-
return indexFileId;
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* Read a large file's chunk hashes back from its index file.
|
|
151
|
-
*
|
|
152
|
-
* @param indexFileId - The index file's id.
|
|
153
|
-
* @returns The chunk hashes, in order.
|
|
146
|
+
* @param fileId - File to delete. A missing file is not an error.
|
|
147
|
+
* @throws When the file isn't a runtime bundle row.
|
|
154
148
|
*/
|
|
155
|
-
async
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
throw new Error(`Index file not found: ${indexFileId}`);
|
|
149
|
+
async deleteFile(fileId) {
|
|
150
|
+
const fileRecord = await super.get(fileId);
|
|
151
|
+
if (!fileRecord) {
|
|
152
|
+
return;
|
|
160
153
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
return JSON.parse(indexContent);
|
|
154
|
+
(0, file_rows_1.checkFileWrite)(fileRecord, undefined, fileId);
|
|
155
|
+
await super.delete(fileId);
|
|
164
156
|
}
|
|
165
157
|
}
|
|
166
158
|
exports.FilesTable = FilesTable;
|
|
@@ -184,14 +176,8 @@ rpc_types_1.rpcServerCalls.getFileContentsBase64 = async (fileId) => {
|
|
|
184
176
|
return Buffer.from(data).toString("base64");
|
|
185
177
|
};
|
|
186
178
|
rpc_types_1.rpcServerCalls.saveFile = async (input) => {
|
|
187
|
-
const fileId = input.fileId || (0, utils_1.newid)();
|
|
188
179
|
const encoding = input.encoding || "utf8";
|
|
189
180
|
const dataBuffer = encoding === "base64" ? Buffer.from(input.data, "base64") : Buffer.from(input.data, "utf8");
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
name: input.name,
|
|
193
|
-
fileSize: dataBuffer.length,
|
|
194
|
-
mimeType: input.mimeType,
|
|
195
|
-
}, new Uint8Array(dataBuffer));
|
|
196
|
-
return file;
|
|
181
|
+
// Kept: Family Hub 0.3.x doesn't declare its file fields, so nothing would reference these.
|
|
182
|
+
return await Files().saveFile({ name: input.name, mimeType: input.mimeType, keep: true }, new Uint8Array(dataBuffer));
|
|
197
183
|
};
|
package/dist/data/files/index.js
CHANGED
|
@@ -15,9 +15,11 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
exports.FileWriteStream = exports.FileReadStream = void 0;
|
|
18
|
+
__exportStar(require("./chunk-store"), exports);
|
|
18
19
|
__exportStar(require("./file.types"), exports);
|
|
19
20
|
var file_read_stream_1 = require("./file-read-stream");
|
|
20
21
|
Object.defineProperty(exports, "FileReadStream", { enumerable: true, get: function () { return file_read_stream_1.FileReadStream; } });
|
|
22
|
+
__exportStar(require("./file-rows"), exports);
|
|
21
23
|
var file_write_stream_1 = require("./file-write-stream");
|
|
22
24
|
Object.defineProperty(exports, "FileWriteStream", { enumerable: true, get: function () { return file_write_stream_1.FileWriteStream; } });
|
|
23
25
|
__exportStar(require("./files"), exports);
|
package/dist/data/groups.d.ts
CHANGED
|
@@ -12,6 +12,13 @@ export declare const groupSchema: z.ZodObject<{
|
|
|
12
12
|
iconClassName: z.ZodOptional<z.ZodString>;
|
|
13
13
|
publicRole: z.ZodOptional<z.ZodNativeEnum<typeof GroupMemberRole>>;
|
|
14
14
|
disabled: z.ZodOptional<z.ZodBoolean>;
|
|
15
|
+
fileRetention: z.ZodOptional<z.ZodObject<{
|
|
16
|
+
days: z.ZodUnion<[z.ZodNumber, z.ZodLiteral<"never">]>;
|
|
17
|
+
}, "strip", z.ZodTypeAny, {
|
|
18
|
+
days: number | "never";
|
|
19
|
+
}, {
|
|
20
|
+
days: number | "never";
|
|
21
|
+
}>>;
|
|
15
22
|
founderUserId: z.ZodEffects<z.ZodString, string, string>;
|
|
16
23
|
signature: z.ZodString;
|
|
17
24
|
signedAt: z.ZodOptional<z.ZodNumber>;
|
|
@@ -29,6 +36,9 @@ export declare const groupSchema: z.ZodObject<{
|
|
|
29
36
|
iconClassName?: string | undefined;
|
|
30
37
|
signedAt?: number | undefined;
|
|
31
38
|
publicRole?: GroupMemberRole | undefined;
|
|
39
|
+
fileRetention?: {
|
|
40
|
+
days: number | "never";
|
|
41
|
+
} | undefined;
|
|
32
42
|
}, {
|
|
33
43
|
name: string;
|
|
34
44
|
description: string;
|
|
@@ -41,6 +51,9 @@ export declare const groupSchema: z.ZodObject<{
|
|
|
41
51
|
iconClassName?: string | undefined;
|
|
42
52
|
signedAt?: number | undefined;
|
|
43
53
|
publicRole?: GroupMemberRole | undefined;
|
|
54
|
+
fileRetention?: {
|
|
55
|
+
days: number | "never";
|
|
56
|
+
} | undefined;
|
|
44
57
|
}>;
|
|
45
58
|
/** A signed group record shared between personal and group contexts. */
|
|
46
59
|
export type IGroup = z.infer<typeof groupSchema>;
|
package/dist/data/groups.js
CHANGED
|
@@ -60,6 +60,12 @@ exports.groupSchema = zod_1.z.object({
|
|
|
60
60
|
.optional()
|
|
61
61
|
.describe("Whether the group is publicly readable"),
|
|
62
62
|
disabled: zod_1.z.boolean().optional().describe("Whether the group is currently disabled"),
|
|
63
|
+
// An object column stores JSON, so the number reads back as signed; a union would be a TEXT
|
|
64
|
+
// column that returns "30.0", and the row's signature would then fail.
|
|
65
|
+
fileRetention: zod_1.z
|
|
66
|
+
.object({ days: zod_1.z.union([zod_1.z.number().int().nonnegative(), zod_1.z.literal("never")]) })
|
|
67
|
+
.optional()
|
|
68
|
+
.describe("Days to retain unreferenced files before the grace period, or never; absent means 30 days."),
|
|
63
69
|
founderUserId: zod_types_1.zodPeerId.describe("The user ID of the group founder"),
|
|
64
70
|
signature: zod_1.z.string().describe("The signed hash of this data excluding the signature itself"),
|
|
65
71
|
signedAt: zod_1.z
|
package/dist/data/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export * from "./authority-write-checks";
|
|
|
4
4
|
export * from "./change-tracking";
|
|
5
5
|
export * from "./channels";
|
|
6
6
|
export * from "./create-group";
|
|
7
|
+
export * from "./derived-row-id";
|
|
7
8
|
export * from "./device-sync-info";
|
|
8
9
|
export * from "./devices";
|
|
9
10
|
export * from "./embeddings";
|
package/dist/data/index.js
CHANGED
|
@@ -20,6 +20,7 @@ __exportStar(require("./authority-write-checks"), exports);
|
|
|
20
20
|
__exportStar(require("./change-tracking"), exports);
|
|
21
21
|
__exportStar(require("./channels"), exports);
|
|
22
22
|
__exportStar(require("./create-group"), exports);
|
|
23
|
+
__exportStar(require("./derived-row-id"), exports);
|
|
23
24
|
__exportStar(require("./device-sync-info"), exports);
|
|
24
25
|
__exportStar(require("./devices"), exports);
|
|
25
26
|
__exportStar(require("./embeddings"), exports);
|
|
@@ -89,3 +89,18 @@ export declare function deletedSignedAt(tableName: string, context: ITableWriteC
|
|
|
89
89
|
* @param reason - Why, for logs and the sync recovery queue.
|
|
90
90
|
*/
|
|
91
91
|
export declare function writeNotAuthorized(tableName: string, reason: string): Error;
|
|
92
|
+
/**
|
|
93
|
+
* A {@link writeNotAuthorized} refusal that no data arriving later can lift, unlike one that
|
|
94
|
+
* waits on Users, Groups or GroupMembers rows still to sync. Sync keeps no recovery entry for
|
|
95
|
+
* such a refused delete of a record it has no row or other history for.
|
|
96
|
+
*
|
|
97
|
+
* @param tableName - Table the write was for.
|
|
98
|
+
* @param reason - Why, for logs.
|
|
99
|
+
*/
|
|
100
|
+
export declare function writeRefusedForGood(tableName: string, reason: string): Error;
|
|
101
|
+
/**
|
|
102
|
+
* Whether an error is a {@link writeRefusedForGood} refusal.
|
|
103
|
+
*
|
|
104
|
+
* @param error - The error a write check threw.
|
|
105
|
+
*/
|
|
106
|
+
export declare function isWriteRefusedForGood(error: unknown): boolean;
|
|
@@ -7,6 +7,8 @@ exports.checkLocalSave = checkLocalSave;
|
|
|
7
7
|
exports.signingStamp = signingStamp;
|
|
8
8
|
exports.deletedSignedAt = deletedSignedAt;
|
|
9
9
|
exports.writeNotAuthorized = writeNotAuthorized;
|
|
10
|
+
exports.writeRefusedForGood = writeRefusedForGood;
|
|
11
|
+
exports.isWriteRefusedForGood = isWriteRefusedForGood;
|
|
10
12
|
/**
|
|
11
13
|
* Prefix of every refusal from a {@link TableWriteCheck}. Sync recovery parks a change refused
|
|
12
14
|
* with it, and checks it again only from local history, when the group's authority changes.
|
|
@@ -120,3 +122,22 @@ async function deletedSignedAt(tableName, context, existing) {
|
|
|
120
122
|
function writeNotAuthorized(tableName, reason) {
|
|
121
123
|
return new Error(`${exports.WRITE_NOT_AUTHORIZED}: ${tableName}: ${reason}`);
|
|
122
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* A {@link writeNotAuthorized} refusal that no data arriving later can lift, unlike one that
|
|
127
|
+
* waits on Users, Groups or GroupMembers rows still to sync. Sync keeps no recovery entry for
|
|
128
|
+
* such a refused delete of a record it has no row or other history for.
|
|
129
|
+
*
|
|
130
|
+
* @param tableName - Table the write was for.
|
|
131
|
+
* @param reason - Why, for logs.
|
|
132
|
+
*/
|
|
133
|
+
function writeRefusedForGood(tableName, reason) {
|
|
134
|
+
return Object.assign(writeNotAuthorized(tableName, reason), { refusedForGood: true });
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Whether an error is a {@link writeRefusedForGood} refusal.
|
|
138
|
+
*
|
|
139
|
+
* @param error - The error a write check threw.
|
|
140
|
+
*/
|
|
141
|
+
function isWriteRefusedForGood(error) {
|
|
142
|
+
return error?.refusedForGood === true;
|
|
143
|
+
}
|
|
@@ -140,10 +140,17 @@ export declare const SystemContractCatalog: import("./contract-ref").IContractRe
|
|
|
140
140
|
}>;
|
|
141
141
|
/**
|
|
142
142
|
* System Files v1: save a file and read it back, in the package's own data context. Files sync
|
|
143
|
-
* to the context's other devices. Declare it with `pkg.consumes(SystemFiles)
|
|
143
|
+
* to the context's other devices. Declare it with `pkg.consumes(SystemFiles)`; save a file over
|
|
144
|
+
* 8 MiB with {@link saveWholeFile}.
|
|
144
145
|
*/
|
|
145
146
|
export declare const SystemFiles: import("./contract-ref").IContractRef<{
|
|
146
147
|
save(input: import("../contracts/system/files-shared").IFilesSaveInput): Promise<import("../contracts/system/files-shared").IFilesSaveResult>;
|
|
147
148
|
read(input: import("../contracts/system/files-shared").IFilesReadInput): Promise<import("../contracts/system/files-shared").IFilesReadResult>;
|
|
149
|
+
startSave(input: import("../contracts/system/files-shared").IFilesStartSaveInput): Promise<import("../contracts/system/files-shared").IFilesSaveSession>;
|
|
150
|
+
appendSave(input: import("../contracts/system/files-shared").IFilesAppendSaveInput): Promise<import("../contracts/system/files-shared").IFilesAppendSaveResult>;
|
|
151
|
+
finishSave(input: import("../contracts/system/files-shared").IFilesSaveSession): Promise<import("../contracts/system/files-shared").IFilesSaveResult>;
|
|
152
|
+
cancelSave(input: import("../contracts/system/files-shared").IFilesSaveSession): Promise<{
|
|
153
|
+
cancelled: boolean;
|
|
154
|
+
}>;
|
|
148
155
|
}>;
|
|
149
|
-
export { FILES_MAX_CALL_BYTES, type IFilesReadInput, type IFilesReadResult, type IFilesSaveInput, type IFilesSaveResult, readWholeFile, } from "../contracts/system/files-shared";
|
|
156
|
+
export { FILES_MAX_CALL_BYTES, FILES_MAX_FILE_BYTES, FILES_WHOLE_READ_MAX_BYTES, type FileBytesSource, type IFilesAppendSaveInput, type IFilesAppendSaveResult, type IFilesReadInput, type IFilesReadResult, type IFilesSaveInput, type IFilesSaveResult, type IFilesSaveSession, type IFilesStartSaveInput, readWholeFile, saveWholeFile, } from "../contracts/system/files-shared";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.readWholeFile = exports.FILES_MAX_CALL_BYTES = exports.SystemFiles = exports.SystemContractCatalog = exports.SystemScheduler = exports.SystemGroupMembers = exports.SystemIdentity = exports.SystemHttp = void 0;
|
|
3
|
+
exports.saveWholeFile = exports.readWholeFile = exports.FILES_WHOLE_READ_MAX_BYTES = exports.FILES_MAX_FILE_BYTES = exports.FILES_MAX_CALL_BYTES = exports.SystemFiles = exports.SystemContractCatalog = exports.SystemScheduler = exports.SystemGroupMembers = exports.SystemIdentity = exports.SystemHttp = void 0;
|
|
4
4
|
const system_ids_1 = require("../system-ids");
|
|
5
5
|
const contract_ref_1 = require("./contract-ref");
|
|
6
6
|
/**
|
|
@@ -25,9 +25,13 @@ exports.SystemScheduler = (0, contract_ref_1.contractRef)(system_ids_1.scheduler
|
|
|
25
25
|
exports.SystemContractCatalog = (0, contract_ref_1.contractRef)(system_ids_1.contractCatalogContractId, 1);
|
|
26
26
|
/**
|
|
27
27
|
* System Files v1: save a file and read it back, in the package's own data context. Files sync
|
|
28
|
-
* to the context's other devices. Declare it with `pkg.consumes(SystemFiles)
|
|
28
|
+
* to the context's other devices. Declare it with `pkg.consumes(SystemFiles)`; save a file over
|
|
29
|
+
* 8 MiB with {@link saveWholeFile}.
|
|
29
30
|
*/
|
|
30
31
|
exports.SystemFiles = (0, contract_ref_1.contractRef)(system_ids_1.filesContractId, 1);
|
|
31
32
|
var files_shared_1 = require("../contracts/system/files-shared");
|
|
32
33
|
Object.defineProperty(exports, "FILES_MAX_CALL_BYTES", { enumerable: true, get: function () { return files_shared_1.FILES_MAX_CALL_BYTES; } });
|
|
34
|
+
Object.defineProperty(exports, "FILES_MAX_FILE_BYTES", { enumerable: true, get: function () { return files_shared_1.FILES_MAX_FILE_BYTES; } });
|
|
35
|
+
Object.defineProperty(exports, "FILES_WHOLE_READ_MAX_BYTES", { enumerable: true, get: function () { return files_shared_1.FILES_WHOLE_READ_MAX_BYTES; } });
|
|
33
36
|
Object.defineProperty(exports, "readWholeFile", { enumerable: true, get: function () { return files_shared_1.readWholeFile; } });
|
|
37
|
+
Object.defineProperty(exports, "saveWholeFile", { enumerable: true, get: function () { return files_shared_1.saveWholeFile; } });
|
|
@@ -1630,8 +1630,8 @@ export declare const devicePairingStateSchema: z.ZodObject<{
|
|
|
1630
1630
|
code?: string | undefined;
|
|
1631
1631
|
error?: string | undefined;
|
|
1632
1632
|
role?: "source" | "destination" | undefined;
|
|
1633
|
-
expiresAt?: number | undefined;
|
|
1634
1633
|
sessionId?: string | undefined;
|
|
1634
|
+
expiresAt?: number | undefined;
|
|
1635
1635
|
remoteDevice?: {
|
|
1636
1636
|
userName?: string | undefined;
|
|
1637
1637
|
deviceName?: string | undefined;
|
|
@@ -1643,8 +1643,8 @@ export declare const devicePairingStateSchema: z.ZodObject<{
|
|
|
1643
1643
|
code?: string | undefined;
|
|
1644
1644
|
error?: string | undefined;
|
|
1645
1645
|
role?: "source" | "destination" | undefined;
|
|
1646
|
-
expiresAt?: number | undefined;
|
|
1647
1646
|
sessionId?: string | undefined;
|
|
1647
|
+
expiresAt?: number | undefined;
|
|
1648
1648
|
remoteDevice?: {
|
|
1649
1649
|
userName?: string | undefined;
|
|
1650
1650
|
deviceName?: string | undefined;
|
|
@@ -344,6 +344,13 @@ export declare const groupApprovalSchema: z.ZodObject<{
|
|
|
344
344
|
iconClassName: z.ZodOptional<z.ZodString>;
|
|
345
345
|
publicRole: z.ZodOptional<z.ZodNativeEnum<typeof import("../data").GroupMemberRole>>;
|
|
346
346
|
disabled: z.ZodOptional<z.ZodBoolean>;
|
|
347
|
+
fileRetention: z.ZodOptional<z.ZodObject<{
|
|
348
|
+
days: z.ZodUnion<[z.ZodNumber, z.ZodLiteral<"never">]>;
|
|
349
|
+
}, "strip", z.ZodTypeAny, {
|
|
350
|
+
days: number | "never";
|
|
351
|
+
}, {
|
|
352
|
+
days: number | "never";
|
|
353
|
+
}>>;
|
|
347
354
|
founderUserId: z.ZodEffects<z.ZodString, string, string>;
|
|
348
355
|
signature: z.ZodString;
|
|
349
356
|
signedAt: z.ZodOptional<z.ZodNumber>;
|
|
@@ -361,6 +368,9 @@ export declare const groupApprovalSchema: z.ZodObject<{
|
|
|
361
368
|
iconClassName?: string | undefined;
|
|
362
369
|
signedAt?: number | undefined;
|
|
363
370
|
publicRole?: import("../data").GroupMemberRole | undefined;
|
|
371
|
+
fileRetention?: {
|
|
372
|
+
days: number | "never";
|
|
373
|
+
} | undefined;
|
|
364
374
|
}, {
|
|
365
375
|
name: string;
|
|
366
376
|
description: string;
|
|
@@ -373,6 +383,9 @@ export declare const groupApprovalSchema: z.ZodObject<{
|
|
|
373
383
|
iconClassName?: string | undefined;
|
|
374
384
|
signedAt?: number | undefined;
|
|
375
385
|
publicRole?: import("../data").GroupMemberRole | undefined;
|
|
386
|
+
fileRetention?: {
|
|
387
|
+
days: number | "never";
|
|
388
|
+
} | undefined;
|
|
376
389
|
}>>;
|
|
377
390
|
membership: z.ZodLazy<z.ZodObject<{
|
|
378
391
|
groupMemberId: z.ZodEffects<z.ZodString, string, string>;
|
|
@@ -654,6 +667,9 @@ export declare const groupApprovalSchema: z.ZodObject<{
|
|
|
654
667
|
iconClassName?: string | undefined;
|
|
655
668
|
signedAt?: number | undefined;
|
|
656
669
|
publicRole?: import("../data").GroupMemberRole | undefined;
|
|
670
|
+
fileRetention?: {
|
|
671
|
+
days: number | "never";
|
|
672
|
+
} | undefined;
|
|
657
673
|
};
|
|
658
674
|
seed: {
|
|
659
675
|
users: {
|
|
@@ -734,6 +750,9 @@ export declare const groupApprovalSchema: z.ZodObject<{
|
|
|
734
750
|
iconClassName?: string | undefined;
|
|
735
751
|
signedAt?: number | undefined;
|
|
736
752
|
publicRole?: import("../data").GroupMemberRole | undefined;
|
|
753
|
+
fileRetention?: {
|
|
754
|
+
days: number | "never";
|
|
755
|
+
} | undefined;
|
|
737
756
|
};
|
|
738
757
|
seed: {
|
|
739
758
|
users: {
|
|
@@ -1296,6 +1315,13 @@ export declare const inviteMessageSchema: z.ZodDiscriminatedUnion<"kind", [z.Zod
|
|
|
1296
1315
|
iconClassName: z.ZodOptional<z.ZodString>;
|
|
1297
1316
|
publicRole: z.ZodOptional<z.ZodNativeEnum<typeof import("../data").GroupMemberRole>>;
|
|
1298
1317
|
disabled: z.ZodOptional<z.ZodBoolean>;
|
|
1318
|
+
fileRetention: z.ZodOptional<z.ZodObject<{
|
|
1319
|
+
days: z.ZodUnion<[z.ZodNumber, z.ZodLiteral<"never">]>;
|
|
1320
|
+
}, "strip", z.ZodTypeAny, {
|
|
1321
|
+
days: number | "never";
|
|
1322
|
+
}, {
|
|
1323
|
+
days: number | "never";
|
|
1324
|
+
}>>;
|
|
1299
1325
|
founderUserId: z.ZodEffects<z.ZodString, string, string>;
|
|
1300
1326
|
signature: z.ZodString;
|
|
1301
1327
|
signedAt: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1313,6 +1339,9 @@ export declare const inviteMessageSchema: z.ZodDiscriminatedUnion<"kind", [z.Zod
|
|
|
1313
1339
|
iconClassName?: string | undefined;
|
|
1314
1340
|
signedAt?: number | undefined;
|
|
1315
1341
|
publicRole?: import("../data").GroupMemberRole | undefined;
|
|
1342
|
+
fileRetention?: {
|
|
1343
|
+
days: number | "never";
|
|
1344
|
+
} | undefined;
|
|
1316
1345
|
}, {
|
|
1317
1346
|
name: string;
|
|
1318
1347
|
description: string;
|
|
@@ -1325,6 +1354,9 @@ export declare const inviteMessageSchema: z.ZodDiscriminatedUnion<"kind", [z.Zod
|
|
|
1325
1354
|
iconClassName?: string | undefined;
|
|
1326
1355
|
signedAt?: number | undefined;
|
|
1327
1356
|
publicRole?: import("../data").GroupMemberRole | undefined;
|
|
1357
|
+
fileRetention?: {
|
|
1358
|
+
days: number | "never";
|
|
1359
|
+
} | undefined;
|
|
1328
1360
|
}>>;
|
|
1329
1361
|
membership: z.ZodLazy<z.ZodObject<{
|
|
1330
1362
|
groupMemberId: z.ZodEffects<z.ZodString, string, string>;
|
|
@@ -1606,6 +1638,9 @@ export declare const inviteMessageSchema: z.ZodDiscriminatedUnion<"kind", [z.Zod
|
|
|
1606
1638
|
iconClassName?: string | undefined;
|
|
1607
1639
|
signedAt?: number | undefined;
|
|
1608
1640
|
publicRole?: import("../data").GroupMemberRole | undefined;
|
|
1641
|
+
fileRetention?: {
|
|
1642
|
+
days: number | "never";
|
|
1643
|
+
} | undefined;
|
|
1609
1644
|
};
|
|
1610
1645
|
seed: {
|
|
1611
1646
|
users: {
|
|
@@ -1686,6 +1721,9 @@ export declare const inviteMessageSchema: z.ZodDiscriminatedUnion<"kind", [z.Zod
|
|
|
1686
1721
|
iconClassName?: string | undefined;
|
|
1687
1722
|
signedAt?: number | undefined;
|
|
1688
1723
|
publicRole?: import("../data").GroupMemberRole | undefined;
|
|
1724
|
+
fileRetention?: {
|
|
1725
|
+
days: number | "never";
|
|
1726
|
+
} | undefined;
|
|
1689
1727
|
};
|
|
1690
1728
|
seed: {
|
|
1691
1729
|
users: {
|