@peers-app/peers-sdk 0.25.8 → 0.25.10
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/__tests__/contract-proxy.test.js +72 -0
- package/dist/contracts/contract-proxy.d.ts +34 -1
- package/dist/contracts/contract-proxy.js +56 -21
- package/dist/contracts/contract-resolvers.js +1 -0
- package/dist/contracts/index.d.ts +1 -0
- package/dist/contracts/index.js +11 -1
- package/dist/contracts/system/local-folders-contract.d.ts +163 -0
- package/dist/contracts/system/local-folders-contract.js +235 -0
- package/dist/contracts/system/network-contracts.d.ts +81 -1
- package/dist/contracts/system/network-contracts.js +55 -2
- package/dist/data/change-tracking.d.ts +5 -0
- package/dist/data/change-tracking.js +18 -0
- package/dist/data/index.d.ts +1 -0
- package/dist/data/index.js +1 -0
- package/dist/data/local-folders.d.ts +176 -0
- package/dist/data/local-folders.js +116 -0
- package/dist/data/orm/sql-db.d.ts +3 -1
- package/dist/data/orm/table.d.ts +19 -0
- package/dist/data/orm/table.js +18 -0
- package/dist/data/persistent-vars.conflict.test.d.ts +1 -0
- package/dist/data/persistent-vars.conflict.test.js +126 -0
- package/dist/data/persistent-vars.d.ts +13 -0
- package/dist/data/persistent-vars.js +53 -0
- package/dist/data/sync-recovery-queue.d.ts +77 -0
- package/dist/data/sync-recovery-queue.js +128 -1
- package/dist/data/sync-recovery-queue.test.js +78 -1
- package/dist/rpc-types.d.ts +0 -3
- package/dist/rpc-types.js +0 -3
- package/dist/system-ids.d.ts +22 -0
- package/dist/system-ids.js +23 -1
- package/package.json +1 -1
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.localFoldersProviderPackageId = exports.LOCAL_FOLDERS_JOB_TTL_MS = exports.LOCAL_FOLDERS_RESERVED_DIR = exports.LOCAL_FOLDERS_CHUNK_MAX = exports.LOCAL_FOLDERS_CHUNK_AVG = exports.LOCAL_FOLDERS_CHUNK_MIN = exports.LOCAL_FOLDERS_READ_RANGE_MAX = exports.LOCAL_FOLDERS_CONTRACT_VERSION = void 0;
|
|
4
|
+
exports.getLocalFoldersContractDefinition = getLocalFoldersContractDefinition;
|
|
5
|
+
const system_ids_1 = require("../../system-ids");
|
|
6
|
+
const field_type_1 = require("../../types/field-type");
|
|
7
|
+
const system_contracts_1 = require("../system-contracts");
|
|
8
|
+
/** Version of the built-in System Local Folders contract. */
|
|
9
|
+
exports.LOCAL_FOLDERS_CONTRACT_VERSION = 1;
|
|
10
|
+
/** Maximum byte length accepted by `readRange` in one call. */
|
|
11
|
+
exports.LOCAL_FOLDERS_READ_RANGE_MAX = 256 * 1024;
|
|
12
|
+
/** FastCDC minimum block size. Files at or below this size are one block. */
|
|
13
|
+
exports.LOCAL_FOLDERS_CHUNK_MIN = 256 * 1024;
|
|
14
|
+
/** FastCDC target average block size. */
|
|
15
|
+
exports.LOCAL_FOLDERS_CHUNK_AVG = 1024 * 1024;
|
|
16
|
+
/** FastCDC maximum block size. */
|
|
17
|
+
exports.LOCAL_FOLDERS_CHUNK_MAX = 4 * 1024 * 1024;
|
|
18
|
+
/** Directory name reserved inside every grant for trash and in-flight files. */
|
|
19
|
+
exports.LOCAL_FOLDERS_RESERVED_DIR = ".peers";
|
|
20
|
+
/** How long finished index jobs stay readable via `getJob`. */
|
|
21
|
+
exports.LOCAL_FOLDERS_JOB_TTL_MS = 60 * 60 * 1000;
|
|
22
|
+
/** Package ID used by live System Local Folders tool instances. */
|
|
23
|
+
exports.localFoldersProviderPackageId = system_ids_1.peersSystemPackageId;
|
|
24
|
+
const field = (name, type, description, opts = {}) => ({ name, type, description, ...opts });
|
|
25
|
+
const grantIdField = field("grantId", field_type_1.FieldType.id, "Folder grant id.");
|
|
26
|
+
let definition;
|
|
27
|
+
/**
|
|
28
|
+
* Return the local-only System Local Folders contract shape.
|
|
29
|
+
*
|
|
30
|
+
* The host checks every path against an operator grant. Remote devices cannot call
|
|
31
|
+
* this contract; bytes between devices move through block exchange, not these tools.
|
|
32
|
+
*/
|
|
33
|
+
function getLocalFoldersContractDefinition() {
|
|
34
|
+
definition ??= {
|
|
35
|
+
contractId: system_ids_1.localFoldersContractId,
|
|
36
|
+
version: exports.LOCAL_FOLDERS_CONTRACT_VERSION,
|
|
37
|
+
devTag: "dev",
|
|
38
|
+
name: "System Local Folders",
|
|
39
|
+
description: "Scoped access to operator-granted folders on this device: listing, bounded reads, indexing, move, and trash.",
|
|
40
|
+
tables: [],
|
|
41
|
+
tools: [
|
|
42
|
+
{
|
|
43
|
+
name: "requestGrant",
|
|
44
|
+
usageDescription: "Grant a local folder. Omit absolutePath on desktop to open the folder picker. Isolated packages cannot create grants.",
|
|
45
|
+
inputFields: [
|
|
46
|
+
field("groupId", field_type_1.FieldType.id, "Group this folder is associated with."),
|
|
47
|
+
field("mode", field_type_1.FieldType.string, 'Either "read" or "readwrite".'),
|
|
48
|
+
field("packageId", field_type_1.FieldType.id, "Package allowed to use the grant.", {
|
|
49
|
+
optional: true,
|
|
50
|
+
}),
|
|
51
|
+
field("absolutePath", field_type_1.FieldType.string, "Absolute directory path. Operator callers only. Omit to open the picker.", { optional: true }),
|
|
52
|
+
],
|
|
53
|
+
outputFields: [
|
|
54
|
+
grantIdField,
|
|
55
|
+
field("groupId", field_type_1.FieldType.id, "Group id stored on the grant."),
|
|
56
|
+
field("absolutePath", field_type_1.FieldType.string, "Absolute path of the granted root."),
|
|
57
|
+
field("mode", field_type_1.FieldType.string, "Grant mode."),
|
|
58
|
+
field("packageId", field_type_1.FieldType.id, "Package the grant is scoped to.", { optional: true }),
|
|
59
|
+
field("createdAt", field_type_1.FieldType.number, "Creation time in unix epoch milliseconds."),
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: "listGrants",
|
|
64
|
+
usageDescription: "List active grants. Isolated callers see only grants scoped to their package.",
|
|
65
|
+
inputFields: [],
|
|
66
|
+
outputFields: [
|
|
67
|
+
field("grants", field_type_1.FieldType.object, "Active grants on this device.", { isArray: true }),
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: "revokeGrant",
|
|
72
|
+
usageDescription: "Revoke a grant. Access stops immediately. Isolated callers can revoke only their own grants.",
|
|
73
|
+
inputFields: [grantIdField],
|
|
74
|
+
outputFields: [field("revoked", field_type_1.FieldType.boolean, "Whether the grant is now revoked.")],
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: "list",
|
|
78
|
+
usageDescription: "List entries under a relative path inside a grant. Skips .peers.",
|
|
79
|
+
inputFields: [
|
|
80
|
+
grantIdField,
|
|
81
|
+
field("relPath", field_type_1.FieldType.string, "Relative directory. Empty lists the root.", {
|
|
82
|
+
optional: true,
|
|
83
|
+
}),
|
|
84
|
+
field("recursive", field_type_1.FieldType.boolean, "List the subtree.", { optional: true }),
|
|
85
|
+
],
|
|
86
|
+
outputFields: [field("entries", field_type_1.FieldType.object, "Directory entries.", { isArray: true })],
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: "stat",
|
|
90
|
+
usageDescription: "Stat one relative path inside a grant.",
|
|
91
|
+
inputFields: [
|
|
92
|
+
grantIdField,
|
|
93
|
+
field("relPath", field_type_1.FieldType.string, "Relative path inside the grant."),
|
|
94
|
+
],
|
|
95
|
+
outputFields: [
|
|
96
|
+
field("relPath", field_type_1.FieldType.string, "Relative path."),
|
|
97
|
+
field("name", field_type_1.FieldType.string, "Final path segment."),
|
|
98
|
+
field("kind", field_type_1.FieldType.string, "file, dir, or symlink."),
|
|
99
|
+
field("size", field_type_1.FieldType.number, "Size in bytes."),
|
|
100
|
+
field("mtimeMs", field_type_1.FieldType.number, "mtime in unix epoch milliseconds."),
|
|
101
|
+
field("inode", field_type_1.FieldType.string, "File identity string."),
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: "readRange",
|
|
106
|
+
usageDescription: `Read up to ${exports.LOCAL_FOLDERS_READ_RANGE_MAX} bytes at an offset. Returns base64.`,
|
|
107
|
+
inputFields: [
|
|
108
|
+
grantIdField,
|
|
109
|
+
field("relPath", field_type_1.FieldType.string, "Relative file path."),
|
|
110
|
+
field("offset", field_type_1.FieldType.number, "Byte offset."),
|
|
111
|
+
field("length", field_type_1.FieldType.number, "Requested length, capped by the host."),
|
|
112
|
+
],
|
|
113
|
+
outputFields: [
|
|
114
|
+
field("dataBase64", field_type_1.FieldType.string, "File bytes encoded as base64."),
|
|
115
|
+
field("bytesRead", field_type_1.FieldType.number, "Number of bytes read."),
|
|
116
|
+
],
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: "index",
|
|
120
|
+
usageDescription: "Start a host job that content-defines and hashes files into the local block index. Requires a readwrite grant.",
|
|
121
|
+
inputFields: [
|
|
122
|
+
grantIdField,
|
|
123
|
+
field("relPath", field_type_1.FieldType.string, "Relative file or directory. Empty indexes the grant.", {
|
|
124
|
+
optional: true,
|
|
125
|
+
}),
|
|
126
|
+
],
|
|
127
|
+
outputFields: [field("jobId", field_type_1.FieldType.id, "Index job id.")],
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: "getFileBlocks",
|
|
131
|
+
usageDescription: "Return the indexed content-defined blocks for one file. Read mode is enough. indexed is false when the file has not been indexed.",
|
|
132
|
+
inputFields: [
|
|
133
|
+
grantIdField,
|
|
134
|
+
field("relPath", field_type_1.FieldType.string, "Relative file path inside the grant."),
|
|
135
|
+
],
|
|
136
|
+
outputFields: [
|
|
137
|
+
field("indexed", field_type_1.FieldType.boolean, "Whether an index row exists for this path."),
|
|
138
|
+
field("size", field_type_1.FieldType.number, "File size in bytes at index time.", { optional: true }),
|
|
139
|
+
field("mtimeMs", field_type_1.FieldType.number, "mtime in unix epoch milliseconds.", {
|
|
140
|
+
optional: true,
|
|
141
|
+
}),
|
|
142
|
+
field("blocks", field_type_1.FieldType.object, "Ordered blocks with hash and size.", {
|
|
143
|
+
isArray: true,
|
|
144
|
+
}),
|
|
145
|
+
],
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
name: "getJob",
|
|
149
|
+
usageDescription: "Return the status of an index job, including after it finishes.",
|
|
150
|
+
inputFields: [field("jobId", field_type_1.FieldType.id, "Job id returned by index.")],
|
|
151
|
+
outputFields: [
|
|
152
|
+
field("jobId", field_type_1.FieldType.id, "Job id."),
|
|
153
|
+
field("kind", field_type_1.FieldType.string, "Job kind. v1 is index."),
|
|
154
|
+
field("status", field_type_1.FieldType.string, "running, done, error, or cancelled."),
|
|
155
|
+
field("done", field_type_1.FieldType.number, "Units completed."),
|
|
156
|
+
field("total", field_type_1.FieldType.number, "Units discovered."),
|
|
157
|
+
field("error", field_type_1.FieldType.string, "Failure message.", { optional: true }),
|
|
158
|
+
field("result", field_type_1.FieldType.object, "Index counts when status is done.", {
|
|
159
|
+
optional: true,
|
|
160
|
+
}),
|
|
161
|
+
],
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
name: "cancelJob",
|
|
165
|
+
usageDescription: "Cancel a running index job.",
|
|
166
|
+
inputFields: [field("jobId", field_type_1.FieldType.id, "Job id returned by index.")],
|
|
167
|
+
outputFields: [field("cancelled", field_type_1.FieldType.boolean, "Whether the job was cancelled.")],
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
name: "move",
|
|
171
|
+
usageDescription: "Move a path inside a readwrite grant. Atomic where the OS allows.",
|
|
172
|
+
inputFields: [
|
|
173
|
+
grantIdField,
|
|
174
|
+
field("from", field_type_1.FieldType.string, "Relative source path."),
|
|
175
|
+
field("to", field_type_1.FieldType.string, "Relative destination path."),
|
|
176
|
+
],
|
|
177
|
+
outputFields: [field("moved", field_type_1.FieldType.boolean, "Whether the move completed.")],
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
name: "trash",
|
|
181
|
+
usageDescription: "Move a path to .peers/trash/<date>/ inside a readwrite grant.",
|
|
182
|
+
inputFields: [grantIdField, field("relPath", field_type_1.FieldType.string, "Relative path to trash.")],
|
|
183
|
+
outputFields: [
|
|
184
|
+
field("trashed", field_type_1.FieldType.boolean, "Whether the path was moved to trash."),
|
|
185
|
+
field("trashPath", field_type_1.FieldType.string, "Relative path inside the grant trash."),
|
|
186
|
+
],
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
name: "purgeTrash",
|
|
190
|
+
usageDescription: "Delete trash entries older than the given number of days.",
|
|
191
|
+
inputFields: [
|
|
192
|
+
grantIdField,
|
|
193
|
+
field("olderThanDays", field_type_1.FieldType.number, "Delete trash dated strictly before this many days ago."),
|
|
194
|
+
],
|
|
195
|
+
outputFields: [field("removed", field_type_1.FieldType.number, "Number of trash entries removed.")],
|
|
196
|
+
},
|
|
197
|
+
],
|
|
198
|
+
observables: [],
|
|
199
|
+
events: [
|
|
200
|
+
{
|
|
201
|
+
name: "changed",
|
|
202
|
+
description: "Debounced filesystem changes inside a grant. The reserved .peers directory is omitted. v1 hosts emit created, modified, and deleted. renamed is reserved for a later host.",
|
|
203
|
+
payloadFields: [
|
|
204
|
+
grantIdField,
|
|
205
|
+
field("changes", field_type_1.FieldType.object, "Changes with relPath and kind. v1 hosts emit created, modified, or deleted. renamed is reserved.", { isArray: true }),
|
|
206
|
+
],
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: "jobProgress",
|
|
210
|
+
description: "Progress for a host index job.",
|
|
211
|
+
payloadFields: [
|
|
212
|
+
field("jobId", field_type_1.FieldType.id, "Job id."),
|
|
213
|
+
field("done", field_type_1.FieldType.number, "Units completed."),
|
|
214
|
+
field("total", field_type_1.FieldType.number, "Units discovered."),
|
|
215
|
+
],
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
name: "jobFinished",
|
|
219
|
+
description: "An index job finished, failed, or was cancelled.",
|
|
220
|
+
payloadFields: [
|
|
221
|
+
field("jobId", field_type_1.FieldType.id, "Job id."),
|
|
222
|
+
field("status", field_type_1.FieldType.string, "done, error, or cancelled."),
|
|
223
|
+
field("error", field_type_1.FieldType.string, "Failure message.", { optional: true }),
|
|
224
|
+
],
|
|
225
|
+
},
|
|
226
|
+
],
|
|
227
|
+
};
|
|
228
|
+
return definition;
|
|
229
|
+
}
|
|
230
|
+
(0, system_contracts_1.registerSystemContract)({
|
|
231
|
+
providerPackageId: system_ids_1.peersSystemPackageId,
|
|
232
|
+
contractId: system_ids_1.localFoldersContractId,
|
|
233
|
+
version: exports.LOCAL_FOLDERS_CONTRACT_VERSION,
|
|
234
|
+
getDefinition: getLocalFoldersContractDefinition,
|
|
235
|
+
});
|
|
@@ -17,6 +17,7 @@ export declare const networkDiagnosticsToolIds: {
|
|
|
17
17
|
readonly getGroupDetails: "00ms1yb50yuru8h1fg4t9na3e";
|
|
18
18
|
readonly getDisconnectedDevices: "00ms1yb50y3jpoby5zomltahr";
|
|
19
19
|
readonly queryRemoteLogs: "00ms1yb50y2fbfnth22yz52eu";
|
|
20
|
+
readonly getSyncRecoveryEntries: "00mujw2tgbpoohxvwd23u9moz";
|
|
20
21
|
};
|
|
21
22
|
/** Stable IDs for tools in System Network Control v1. */
|
|
22
23
|
export declare const networkControlToolIds: {
|
|
@@ -26,6 +27,9 @@ export declare const networkControlToolIds: {
|
|
|
26
27
|
readonly deleteDevice: "00ms1yb50y5mc448nuavlex86";
|
|
27
28
|
readonly syncGroupNow: "00ms1yb50ylnqlra26jx232py";
|
|
28
29
|
readonly resetChangeTracking: "00ms1yb50yfuackpl1w37hmxc";
|
|
30
|
+
readonly retrySyncRecovery: "00mujw2tgcelvpg556n836ah7";
|
|
31
|
+
readonly resolveSyncRecovery: "00mujw2tgciywn8krykc6gmwx";
|
|
32
|
+
readonly resetDeviceSyncInfo: "00mujw2tgc5lrezqrw4oneaot";
|
|
29
33
|
};
|
|
30
34
|
/** Overall status shown by the shell's compact network indicator. */
|
|
31
35
|
export type NetworkStatusState = "offline" | "syncing" | "synced" | "error";
|
|
@@ -51,6 +55,11 @@ export interface INetworkStatusSummary {
|
|
|
51
55
|
disconnectedContextCount: number;
|
|
52
56
|
/** Sync contexts that currently have at least one live SyncGroup connection. */
|
|
53
57
|
connectedContextCount: number;
|
|
58
|
+
/**
|
|
59
|
+
* Recovery-queue entries that are parked or have been retrying long enough
|
|
60
|
+
* that an operator should look at them. Zero means the indicator stays green.
|
|
61
|
+
*/
|
|
62
|
+
stuckRecoveryCount: number;
|
|
54
63
|
lastSuccessfulSyncAt?: number;
|
|
55
64
|
errors: INetworkSyncError[];
|
|
56
65
|
}
|
|
@@ -176,6 +185,56 @@ export interface INetworkControlResult {
|
|
|
176
185
|
success: boolean;
|
|
177
186
|
message: string;
|
|
178
187
|
}
|
|
188
|
+
/** How an operator resolves one stuck recovery-queue entry. */
|
|
189
|
+
export type SyncRecoveryResolution = "keepLocal" | "deleteEverywhere" | "ignoreLocal";
|
|
190
|
+
/** One recovery-queue entry prepared for the Sync Recovery screen. */
|
|
191
|
+
export interface INetworkSyncRecoveryEntry {
|
|
192
|
+
dataContextId: string;
|
|
193
|
+
dataContextName: string;
|
|
194
|
+
syncRecoveryId: string;
|
|
195
|
+
tableName: string;
|
|
196
|
+
recordId: string;
|
|
197
|
+
firstFailedAt: number;
|
|
198
|
+
lastFailedAt: number;
|
|
199
|
+
attemptCount: number;
|
|
200
|
+
latestError: string;
|
|
201
|
+
status: "retrying" | "parked";
|
|
202
|
+
parkedReason?: "schema" | "unique-conflict" | "unresolvable";
|
|
203
|
+
stuck: boolean;
|
|
204
|
+
/** True when this device has a row that "Keep this device's version" can snapshot. */
|
|
205
|
+
localRowExists: boolean;
|
|
206
|
+
/** Other record id holding the same unique key, when the failure is a unique conflict. */
|
|
207
|
+
conflictingRecordId?: string;
|
|
208
|
+
/**
|
|
209
|
+
* Record id Keep will tombstone.
|
|
210
|
+
* The other stored row when both exist, or this queued id when only the other row is stored.
|
|
211
|
+
*/
|
|
212
|
+
tombstoneRecordId?: string;
|
|
213
|
+
/**
|
|
214
|
+
* Short label taken from the row Keep will snapshot, or from queued history when nothing is stored.
|
|
215
|
+
*/
|
|
216
|
+
recordSummary?: string;
|
|
217
|
+
/**
|
|
218
|
+
* Pretty-printed row Keep will snapshot, with secret values hidden.
|
|
219
|
+
* Dates and other special values use the change-history encoding (`__DATE`, and so on).
|
|
220
|
+
*/
|
|
221
|
+
keptRecordPreview?: string;
|
|
222
|
+
/**
|
|
223
|
+
* Pretty-printed queued history when it differs from {@link keptRecordPreview}.
|
|
224
|
+
* Uses the same secret hiding and change-history encoding.
|
|
225
|
+
*/
|
|
226
|
+
queuedRecordPreview?: string;
|
|
227
|
+
/**
|
|
228
|
+
* Pretty-printed stored record, with secret values hidden.
|
|
229
|
+
* Prefers the row Keep will snapshot, then queued history.
|
|
230
|
+
*/
|
|
231
|
+
recordPreview?: string;
|
|
232
|
+
/**
|
|
233
|
+
* Author time of the change-tracking row that failed to materialize.
|
|
234
|
+
* Hybrid-logical-clock milliseconds. Omitted when no change row is stored.
|
|
235
|
+
*/
|
|
236
|
+
changeCreatedAt?: number;
|
|
237
|
+
}
|
|
179
238
|
/** Typed consumer proxy for System Network Diagnostics v1. */
|
|
180
239
|
export interface INetworkDiagnosticsConsumer extends IContractConsumerBase {
|
|
181
240
|
tools: {
|
|
@@ -207,6 +266,11 @@ export interface INetworkDiagnosticsConsumer extends IContractConsumerBase {
|
|
|
207
266
|
deviceId: string;
|
|
208
267
|
query?: INetworkRemoteLogsQuery;
|
|
209
268
|
}): Promise<INetworkRemoteLogsResult>;
|
|
269
|
+
getSyncRecoveryEntries(input?: {
|
|
270
|
+
dataContextId?: string;
|
|
271
|
+
}): Promise<{
|
|
272
|
+
entries: INetworkSyncRecoveryEntry[];
|
|
273
|
+
}>;
|
|
210
274
|
};
|
|
211
275
|
observables: {
|
|
212
276
|
status: Observable<INetworkStatusSummary>;
|
|
@@ -228,8 +292,24 @@ export interface INetworkControlConsumer extends IContractConsumerBase {
|
|
|
228
292
|
syncGroupNow(input: {
|
|
229
293
|
groupId: string;
|
|
230
294
|
}): Promise<INetworkControlResult>;
|
|
231
|
-
resetChangeTracking(input
|
|
295
|
+
resetChangeTracking(input?: {
|
|
296
|
+
dataContextId?: string;
|
|
297
|
+
}): Promise<INetworkControlResult>;
|
|
298
|
+
resetDeviceSyncInfo(input?: {
|
|
299
|
+
dataContextId?: string;
|
|
300
|
+
}): Promise<INetworkControlResult>;
|
|
301
|
+
retrySyncRecovery(input: {
|
|
302
|
+
dataContextId: string;
|
|
303
|
+
tableName?: string;
|
|
304
|
+
recordId?: string;
|
|
305
|
+
syncRecoveryId?: string;
|
|
306
|
+
}): Promise<INetworkControlResult>;
|
|
307
|
+
resolveSyncRecovery(input: {
|
|
232
308
|
dataContextId: string;
|
|
309
|
+
tableName: string;
|
|
310
|
+
recordId: string;
|
|
311
|
+
resolution: SyncRecoveryResolution;
|
|
312
|
+
syncRecoveryId?: string;
|
|
233
313
|
}): Promise<INetworkControlResult>;
|
|
234
314
|
};
|
|
235
315
|
}
|
|
@@ -19,6 +19,7 @@ exports.networkDiagnosticsToolIds = {
|
|
|
19
19
|
getGroupDetails: "00ms1yb50yuru8h1fg4t9na3e",
|
|
20
20
|
getDisconnectedDevices: "00ms1yb50y3jpoby5zomltahr",
|
|
21
21
|
queryRemoteLogs: "00ms1yb50y2fbfnth22yz52eu",
|
|
22
|
+
getSyncRecoveryEntries: "00mujw2tgbpoohxvwd23u9moz",
|
|
22
23
|
};
|
|
23
24
|
/** Stable IDs for tools in System Network Control v1. */
|
|
24
25
|
exports.networkControlToolIds = {
|
|
@@ -28,6 +29,9 @@ exports.networkControlToolIds = {
|
|
|
28
29
|
deleteDevice: "00ms1yb50y5mc448nuavlex86",
|
|
29
30
|
syncGroupNow: "00ms1yb50ylnqlra26jx232py",
|
|
30
31
|
resetChangeTracking: "00ms1yb50yfuackpl1w37hmxc",
|
|
32
|
+
retrySyncRecovery: "00mujw2tgcelvpg556n836ah7",
|
|
33
|
+
resolveSyncRecovery: "00mujw2tgciywn8krykc6gmwx",
|
|
34
|
+
resetDeviceSyncInfo: "00mujw2tgc5lrezqrw4oneaot",
|
|
31
35
|
};
|
|
32
36
|
const field = (name, type, description, opts = {}) => ({ name, type, description, ...opts });
|
|
33
37
|
let diagnosticsDefinition;
|
|
@@ -106,6 +110,18 @@ function getNetworkDiagnosticsContractDefinition() {
|
|
|
106
110
|
field("queriedAt", field_type_1.FieldType.number, "Local completion timestamp."),
|
|
107
111
|
],
|
|
108
112
|
},
|
|
113
|
+
{
|
|
114
|
+
name: "getSyncRecoveryEntries",
|
|
115
|
+
usageDescription: "List records whose synchronized changes could not be materialized.",
|
|
116
|
+
inputFields: [
|
|
117
|
+
field("dataContextId", field_type_1.FieldType.id, "Optional personal or group context ID.", {
|
|
118
|
+
optional: true,
|
|
119
|
+
}),
|
|
120
|
+
],
|
|
121
|
+
outputFields: [
|
|
122
|
+
field("entries", field_type_1.FieldType.object, "Recovery queue entries.", { isArray: true }),
|
|
123
|
+
],
|
|
124
|
+
},
|
|
109
125
|
],
|
|
110
126
|
observables: [
|
|
111
127
|
{
|
|
@@ -166,9 +182,46 @@ function getNetworkControlContractDefinition() {
|
|
|
166
182
|
},
|
|
167
183
|
{
|
|
168
184
|
name: "resetChangeTracking",
|
|
169
|
-
usageDescription: "Reset one data context
|
|
185
|
+
usageDescription: "Reset change tracking for one data context, or every local context when omitted.",
|
|
186
|
+
inputFields: [
|
|
187
|
+
field("dataContextId", field_type_1.FieldType.id, "Personal or group context ID to reset.", {
|
|
188
|
+
optional: true,
|
|
189
|
+
}),
|
|
190
|
+
],
|
|
191
|
+
outputFields: resultFields,
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
name: "resetDeviceSyncInfo",
|
|
195
|
+
usageDescription: "Clear device sync checkpoints for one data context, or every local context when omitted.",
|
|
196
|
+
inputFields: [
|
|
197
|
+
field("dataContextId", field_type_1.FieldType.id, "Personal or group context ID to reset.", {
|
|
198
|
+
optional: true,
|
|
199
|
+
}),
|
|
200
|
+
],
|
|
201
|
+
outputFields: resultFields,
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
name: "retrySyncRecovery",
|
|
205
|
+
usageDescription: "Retry one recovery entry, or every entry in a data context.",
|
|
206
|
+
inputFields: [
|
|
207
|
+
field("dataContextId", field_type_1.FieldType.id, "Personal or group context ID."),
|
|
208
|
+
field("tableName", field_type_1.FieldType.string, "Table of one entry to retry.", { optional: true }),
|
|
209
|
+
field("recordId", field_type_1.FieldType.string, "Record id of one entry to retry.", {
|
|
210
|
+
optional: true,
|
|
211
|
+
}),
|
|
212
|
+
field("syncRecoveryId", field_type_1.FieldType.id, "Queue row to retry. When set, a replaced row is not retried.", { optional: true }),
|
|
213
|
+
],
|
|
214
|
+
outputFields: resultFields,
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
name: "resolveSyncRecovery",
|
|
218
|
+
usageDescription: "Resolve one recovery entry by snapshotting this device, tombstoning the record, or ignoring it locally.",
|
|
170
219
|
inputFields: [
|
|
171
|
-
field("dataContextId", field_type_1.FieldType.id, "Personal or group context ID
|
|
220
|
+
field("dataContextId", field_type_1.FieldType.id, "Personal or group context ID."),
|
|
221
|
+
field("tableName", field_type_1.FieldType.string, "Table of the queued record."),
|
|
222
|
+
field("recordId", field_type_1.FieldType.string, "Record id of the queued record."),
|
|
223
|
+
field("resolution", field_type_1.FieldType.string, "keepLocal, deleteEverywhere, or ignoreLocal."),
|
|
224
|
+
field("syncRecoveryId", field_type_1.FieldType.id, "Queue row to resolve. When set, a replaced row is left unchanged.", { optional: true }),
|
|
172
225
|
],
|
|
173
226
|
outputFields: resultFields,
|
|
174
227
|
},
|
|
@@ -133,6 +133,11 @@ export declare class ChangeTrackingTable extends SQLDataSource<IChangeRecord> {
|
|
|
133
133
|
* than `supersededAt`. Used after a root snapshot, delete, or restore.
|
|
134
134
|
*/
|
|
135
135
|
markAllPriorChangesSuperseded(tableName: string, recordId: string, supersededAt: number): Promise<void>;
|
|
136
|
+
/**
|
|
137
|
+
* Synchronous form of {@link markAllPriorChangesSuperseded} for an open
|
|
138
|
+
* writer transaction. The table must already be initialized.
|
|
139
|
+
*/
|
|
140
|
+
markAllPriorChangesSupersededSync(tableName: string, recordId: string, supersededAt: number): void;
|
|
136
141
|
/**
|
|
137
142
|
* Batch update supersededAt timestamps for multiple changes.
|
|
138
143
|
* More efficient than updating one at a time.
|
|
@@ -236,6 +236,24 @@ class ChangeTrackingTable extends orm_1.SQLDataSource {
|
|
|
236
236
|
supersededAt IS NULL
|
|
237
237
|
`, { tableName, recordId, supersededAt });
|
|
238
238
|
}
|
|
239
|
+
/**
|
|
240
|
+
* Synchronous form of {@link markAllPriorChangesSuperseded} for an open
|
|
241
|
+
* writer transaction. The table must already be initialized.
|
|
242
|
+
*/
|
|
243
|
+
markAllPriorChangesSupersededSync(tableName, recordId, supersededAt) {
|
|
244
|
+
if (!this.db.execSync) {
|
|
245
|
+
throw new Error("Recovery writes require a synchronous database");
|
|
246
|
+
}
|
|
247
|
+
this.db.execSync(`
|
|
248
|
+
UPDATE "${this.tableName}"
|
|
249
|
+
SET supersededAt = $supersededAt
|
|
250
|
+
WHERE
|
|
251
|
+
tableName = $tableName AND
|
|
252
|
+
recordId = $recordId AND
|
|
253
|
+
createdAt < $supersededAt AND
|
|
254
|
+
supersededAt IS NULL
|
|
255
|
+
`, { tableName, recordId, supersededAt });
|
|
256
|
+
}
|
|
239
257
|
/**
|
|
240
258
|
* Batch update supersededAt timestamps for multiple changes.
|
|
241
259
|
* More efficient than updating one at a time.
|
package/dist/data/index.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export * from "./group-secret";
|
|
|
14
14
|
export * from "./group-share";
|
|
15
15
|
export * from "./groups";
|
|
16
16
|
export * from "./invites";
|
|
17
|
+
export * from "./local-folders";
|
|
17
18
|
export * from "./messages";
|
|
18
19
|
export * from "./package-table-identity";
|
|
19
20
|
export * from "./package-version-resolver";
|
package/dist/data/index.js
CHANGED
|
@@ -30,6 +30,7 @@ __exportStar(require("./group-secret"), exports);
|
|
|
30
30
|
__exportStar(require("./group-share"), exports);
|
|
31
31
|
__exportStar(require("./groups"), exports);
|
|
32
32
|
__exportStar(require("./invites"), exports);
|
|
33
|
+
__exportStar(require("./local-folders"), exports);
|
|
33
34
|
__exportStar(require("./messages"), exports);
|
|
34
35
|
__exportStar(require("./package-table-identity"), exports);
|
|
35
36
|
__exportStar(require("./package-version-resolver"), exports);
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { DataContext } from "../context/data-context";
|
|
3
|
+
import { type ITableMetaData } from "./orm/types";
|
|
4
|
+
/** Content-defined block descriptor stored on an indexed file. */
|
|
5
|
+
export declare const localFolderBlockSchema: z.ZodObject<{
|
|
6
|
+
hash: z.ZodString;
|
|
7
|
+
size: z.ZodNumber;
|
|
8
|
+
}, "strip", z.ZodTypeAny, {
|
|
9
|
+
size: number;
|
|
10
|
+
hash: string;
|
|
11
|
+
}, {
|
|
12
|
+
size: number;
|
|
13
|
+
hash: string;
|
|
14
|
+
}>;
|
|
15
|
+
/** One content-defined block: hash and length. */
|
|
16
|
+
export type ILocalFolderBlock = z.infer<typeof localFolderBlockSchema>;
|
|
17
|
+
/** Access mode recorded on a folder grant. */
|
|
18
|
+
export declare const localFolderGrantModeSchema: z.ZodEnum<["read", "readwrite"]>;
|
|
19
|
+
/** `read` lists and reads; `readwrite` may also move, trash, and index. */
|
|
20
|
+
export type LocalFolderGrantMode = z.infer<typeof localFolderGrantModeSchema>;
|
|
21
|
+
/**
|
|
22
|
+
* Operator grant of one absolute folder to an optional package.
|
|
23
|
+
* Stored only on this device, in the personal database.
|
|
24
|
+
*/
|
|
25
|
+
export declare const localFolderGrantSchema: z.ZodObject<{
|
|
26
|
+
grantId: z.ZodEffects<z.ZodString, string, string>;
|
|
27
|
+
packageId: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
28
|
+
groupId: z.ZodEffects<z.ZodString, string, string>;
|
|
29
|
+
absolutePath: z.ZodString;
|
|
30
|
+
mode: z.ZodEnum<["read", "readwrite"]>;
|
|
31
|
+
createdAt: z.ZodNumber;
|
|
32
|
+
revokedAt: z.ZodOptional<z.ZodNumber>;
|
|
33
|
+
}, "strip", z.ZodTypeAny, {
|
|
34
|
+
groupId: string;
|
|
35
|
+
createdAt: number;
|
|
36
|
+
mode: "readwrite" | "read";
|
|
37
|
+
grantId: string;
|
|
38
|
+
absolutePath: string;
|
|
39
|
+
packageId?: string | undefined;
|
|
40
|
+
revokedAt?: number | undefined;
|
|
41
|
+
}, {
|
|
42
|
+
groupId: string;
|
|
43
|
+
createdAt: number;
|
|
44
|
+
mode: "readwrite" | "read";
|
|
45
|
+
grantId: string;
|
|
46
|
+
absolutePath: string;
|
|
47
|
+
packageId?: string | undefined;
|
|
48
|
+
revokedAt?: number | undefined;
|
|
49
|
+
}>;
|
|
50
|
+
/** A device-local folder grant. */
|
|
51
|
+
export type ILocalFolderGrant = z.infer<typeof localFolderGrantSchema>;
|
|
52
|
+
/** Indexed file under a grant. One row per `(grantId, relPath)`. */
|
|
53
|
+
export declare const localFolderFileSchema: z.ZodObject<{
|
|
54
|
+
fileIndexId: z.ZodEffects<z.ZodString, string, string>;
|
|
55
|
+
grantId: z.ZodEffects<z.ZodString, string, string>;
|
|
56
|
+
relPath: z.ZodString;
|
|
57
|
+
size: z.ZodNumber;
|
|
58
|
+
mtimeMs: z.ZodNumber;
|
|
59
|
+
inode: z.ZodString;
|
|
60
|
+
blocks: z.ZodArray<z.ZodObject<{
|
|
61
|
+
hash: z.ZodString;
|
|
62
|
+
size: z.ZodNumber;
|
|
63
|
+
}, "strip", z.ZodTypeAny, {
|
|
64
|
+
size: number;
|
|
65
|
+
hash: string;
|
|
66
|
+
}, {
|
|
67
|
+
size: number;
|
|
68
|
+
hash: string;
|
|
69
|
+
}>, "many">;
|
|
70
|
+
}, "strip", z.ZodTypeAny, {
|
|
71
|
+
size: number;
|
|
72
|
+
grantId: string;
|
|
73
|
+
relPath: string;
|
|
74
|
+
mtimeMs: number;
|
|
75
|
+
inode: string;
|
|
76
|
+
blocks: {
|
|
77
|
+
size: number;
|
|
78
|
+
hash: string;
|
|
79
|
+
}[];
|
|
80
|
+
fileIndexId: string;
|
|
81
|
+
}, {
|
|
82
|
+
size: number;
|
|
83
|
+
grantId: string;
|
|
84
|
+
relPath: string;
|
|
85
|
+
mtimeMs: number;
|
|
86
|
+
inode: string;
|
|
87
|
+
blocks: {
|
|
88
|
+
size: number;
|
|
89
|
+
hash: string;
|
|
90
|
+
}[];
|
|
91
|
+
fileIndexId: string;
|
|
92
|
+
}>;
|
|
93
|
+
/** A local index row for one file. */
|
|
94
|
+
export type ILocalFolderFile = z.infer<typeof localFolderFileSchema>;
|
|
95
|
+
/**
|
|
96
|
+
* One servable block location. The same hash may appear in several files;
|
|
97
|
+
* `blockHash` is indexed, not unique.
|
|
98
|
+
*/
|
|
99
|
+
export declare const localFolderBlockRowSchema: z.ZodObject<{
|
|
100
|
+
blockRowId: z.ZodEffects<z.ZodString, string, string>;
|
|
101
|
+
blockHash: z.ZodString;
|
|
102
|
+
grantId: z.ZodEffects<z.ZodString, string, string>;
|
|
103
|
+
relPath: z.ZodString;
|
|
104
|
+
offset: z.ZodNumber;
|
|
105
|
+
length: z.ZodNumber;
|
|
106
|
+
mtimeMs: z.ZodNumber;
|
|
107
|
+
}, "strip", z.ZodTypeAny, {
|
|
108
|
+
length: number;
|
|
109
|
+
offset: number;
|
|
110
|
+
grantId: string;
|
|
111
|
+
relPath: string;
|
|
112
|
+
mtimeMs: number;
|
|
113
|
+
blockRowId: string;
|
|
114
|
+
blockHash: string;
|
|
115
|
+
}, {
|
|
116
|
+
length: number;
|
|
117
|
+
offset: number;
|
|
118
|
+
grantId: string;
|
|
119
|
+
relPath: string;
|
|
120
|
+
mtimeMs: number;
|
|
121
|
+
blockRowId: string;
|
|
122
|
+
blockHash: string;
|
|
123
|
+
}>;
|
|
124
|
+
/** A local index row mapping a block hash to a byte range in a real file. */
|
|
125
|
+
export type ILocalFolderBlockRow = z.infer<typeof localFolderBlockRowSchema>;
|
|
126
|
+
/** Table metadata for {@link LocalFolderGrants}. */
|
|
127
|
+
export declare const localFolderGrantsMetaData: ITableMetaData;
|
|
128
|
+
/** Table metadata for {@link LocalFolderFiles}. */
|
|
129
|
+
export declare const localFolderFilesMetaData: ITableMetaData;
|
|
130
|
+
/** Table metadata for {@link LocalFolderBlocks}. */
|
|
131
|
+
export declare const localFolderBlocksMetaData: ITableMetaData;
|
|
132
|
+
/**
|
|
133
|
+
* Device-local folder grants. Pass the personal data context; these rows do not sync.
|
|
134
|
+
*
|
|
135
|
+
* @param dataContext Context whose database holds the grants. Defaults to the current context.
|
|
136
|
+
*/
|
|
137
|
+
export declare function LocalFolderGrants(dataContext?: DataContext): import("..").Table<{
|
|
138
|
+
groupId: string;
|
|
139
|
+
createdAt: number;
|
|
140
|
+
mode: "readwrite" | "read";
|
|
141
|
+
grantId: string;
|
|
142
|
+
absolutePath: string;
|
|
143
|
+
packageId?: string | undefined;
|
|
144
|
+
revokedAt?: number | undefined;
|
|
145
|
+
}>;
|
|
146
|
+
/**
|
|
147
|
+
* Device-local indexed files. Pass the personal data context; these rows do not sync.
|
|
148
|
+
*
|
|
149
|
+
* @param dataContext Context whose database holds the index. Defaults to the current context.
|
|
150
|
+
*/
|
|
151
|
+
export declare function LocalFolderFiles(dataContext?: DataContext): import("..").Table<{
|
|
152
|
+
size: number;
|
|
153
|
+
grantId: string;
|
|
154
|
+
relPath: string;
|
|
155
|
+
mtimeMs: number;
|
|
156
|
+
inode: string;
|
|
157
|
+
blocks: {
|
|
158
|
+
size: number;
|
|
159
|
+
hash: string;
|
|
160
|
+
}[];
|
|
161
|
+
fileIndexId: string;
|
|
162
|
+
}>;
|
|
163
|
+
/**
|
|
164
|
+
* Device-local block locations. Pass the personal data context; these rows do not sync.
|
|
165
|
+
*
|
|
166
|
+
* @param dataContext Context whose database holds the index. Defaults to the current context.
|
|
167
|
+
*/
|
|
168
|
+
export declare function LocalFolderBlocks(dataContext?: DataContext): import("..").Table<{
|
|
169
|
+
length: number;
|
|
170
|
+
offset: number;
|
|
171
|
+
grantId: string;
|
|
172
|
+
relPath: string;
|
|
173
|
+
mtimeMs: number;
|
|
174
|
+
blockRowId: string;
|
|
175
|
+
blockHash: string;
|
|
176
|
+
}>;
|