@mdbase-dev/connect 0.1.0-beta.31 → 0.1.0-beta.33
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/README.md +112 -34
- package/dist/advanced.d.ts +9 -0
- package/dist/advanced.js +6 -0
- package/dist/application-session.d.ts +39 -14
- package/dist/application-session.js +201 -70
- package/dist/async.js +11 -5
- package/dist/authority-proof.d.ts +3 -0
- package/dist/authority-proof.js +24 -0
- package/dist/browser/integrity.json +1 -1
- package/dist/browser/mdbase-connect.min.js +3 -3
- package/dist/browser/mdbase-connect.min.js.sha384 +1 -1
- package/dist/browser.d.ts +3 -0
- package/dist/browser.js +5 -0
- package/dist/capabilities.js +2 -1
- package/dist/collection-client.d.ts +36 -33
- package/dist/collection-client.js +549 -104
- package/dist/connect-authorization-helpers.d.ts +3 -0
- package/dist/connect-authorization-helpers.js +13 -0
- package/dist/connect-options.d.ts +12 -4
- package/dist/connect.d.ts +12 -4
- package/dist/connect.js +102 -103
- package/dist/connection-file-transport.d.ts +2 -2
- package/dist/connection-file-transport.js +4 -4
- package/dist/connection-notifications.d.ts +10 -4
- package/dist/connection-notifications.js +41 -23
- package/dist/connection-transport.d.ts +19 -15
- package/dist/connection-transport.js +202 -202
- package/dist/connection-types.d.ts +5 -4
- package/dist/connection.d.ts +47 -46
- package/dist/connection.js +320 -162
- package/dist/crypto-entry.d.ts +7 -0
- package/dist/crypto-entry.js +4 -0
- package/dist/crypto.js +4 -4
- package/dist/external-store.d.ts +10 -0
- package/dist/external-store.js +10 -0
- package/dist/file-transfer-internals.d.ts +23 -0
- package/dist/file-transfer-internals.js +224 -0
- package/dist/files.d.ts +36 -14
- package/dist/files.js +92 -222
- package/dist/hosted-file-request.js +3 -3
- package/dist/index.d.ts +24 -19
- package/dist/index.js +10 -18
- package/dist/internal-types.d.ts +9 -1
- package/dist/local-file-transport.d.ts +2 -2
- package/dist/local-file-transport.js +13 -13
- package/dist/mdbase-connect.d.ts +6 -4
- package/dist/mdbase-connect.js +9 -8
- package/dist/notifications.d.ts +3 -2
- package/dist/operation-helpers.d.ts +3 -3
- package/dist/operation-helpers.js +17 -34
- package/dist/operation-types.d.ts +435 -45
- package/dist/outcomes.d.ts +15 -15
- package/dist/outcomes.js +13 -1
- package/dist/pending-mutation-store.d.ts +13 -0
- package/dist/pending-mutation-store.js +64 -0
- package/dist/request-budget.d.ts +27 -0
- package/dist/request-budget.js +95 -0
- package/dist/runtime-utils.d.ts +2 -0
- package/dist/runtime-utils.js +19 -0
- package/dist/session.d.ts +5 -4
- package/dist/session.js +6 -5
- package/dist/stored-token.d.ts +10 -0
- package/dist/stored-token.js +60 -0
- package/dist/token-response.d.ts +11 -0
- package/dist/token-response.js +77 -0
- package/dist/watch-policy.d.ts +8 -0
- package/dist/watch-policy.js +12 -0
- package/package.json +12 -2
package/dist/connection.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { MdbaseCollectionClient } from "./collection-client.js";
|
|
2
2
|
import { ConnectionNotifications } from "./connection-notifications.js";
|
|
3
3
|
import { ConnectionTransport } from "./connection-transport.js";
|
|
4
|
-
import { MdbaseConnectError } from "./errors.js";
|
|
4
|
+
import { MdbaseConnectError, connectProblem, operationProblem } from "./errors.js";
|
|
5
5
|
import { MdbaseFileClient } from "./files.js";
|
|
6
6
|
import { DEFAULT_OPERATIONS } from "./internal-types.js";
|
|
7
7
|
import { assertDeletePreview, assertRenamePreview, deleteEstimate, isCancellation, renameEstimate, throwIfCancelled, uniqueOperations } from "./operation-helpers.js";
|
|
8
|
-
import { AUTHORIZATION_PROBLEM_CODES, COLLECTION_MUTATION_PROBLEM_CODES, DIRECT_ACCESS_PROBLEM_CODES, NOTIFICATION_PROBLEM_CODES, REGISTRATION_PROBLEM_CODES, captureConnectOutcome, connectFailure, connectSuccess } from "./outcomes.js";
|
|
8
|
+
import { AUTHORIZATION_PROBLEM_CODES, COLLECTION_CHANGES_PROBLEM_CODES, COLLECTION_MUTATION_PROBLEM_CODES, DIRECT_ACCESS_PROBLEM_CODES, NOTIFICATION_PROBLEM_CODES, REGISTRATION_PROBLEM_CODES, captureConnectOutcome, connectFailure, connectSuccess } from "./outcomes.js";
|
|
9
|
+
import { withCooperativeRequestBudget, withRequestBudget } from "./request-budget.js";
|
|
9
10
|
export class MdbaseConnection {
|
|
10
11
|
internals;
|
|
11
12
|
collectionId;
|
|
@@ -25,6 +26,7 @@ export class MdbaseConnection {
|
|
|
25
26
|
loopbackUrl: internals.loopbackUrl,
|
|
26
27
|
collectionId,
|
|
27
28
|
internals,
|
|
29
|
+
timeouts: internals.timeouts,
|
|
28
30
|
onChange: () => this.emitConnection()
|
|
29
31
|
});
|
|
30
32
|
this.files = new MdbaseFileClient(() => this.fileCapability, (method, path, input, signal) => this.transport.files.control(method, path, input, signal), {
|
|
@@ -32,16 +34,17 @@ export class MdbaseConnection {
|
|
|
32
34
|
downloadChunk: (session, chunkIndex, signal) => this.transport.files.downloadChunk(session, chunkIndex, signal)
|
|
33
35
|
}, {
|
|
34
36
|
downloadPart: (session, partIndex, expectedLength, signal) => this.transport.files.downloadHostedPart(session, partIndex, expectedLength, signal)
|
|
35
|
-
});
|
|
37
|
+
}, internals.timeouts);
|
|
36
38
|
this.collectionClient = new MdbaseCollectionClient({
|
|
37
39
|
operation: (operation, input, requestOptions) => this.transport.performOperation(operation, input, requestOptions)
|
|
38
|
-
});
|
|
40
|
+
}, internals.timeouts.requestMs);
|
|
39
41
|
this.notifications = new ConnectionNotifications({
|
|
40
42
|
serverUrl: internals.serverUrl,
|
|
41
43
|
storage: internals.storage,
|
|
42
|
-
authorizedToken: () => this.transport.authorizedToken(),
|
|
43
|
-
register: () => this.internals.register(),
|
|
44
|
-
notificationKey: (transport) => internals.notificationKey(collectionId, transport)
|
|
44
|
+
authorizedToken: (signal) => this.transport.authorizedToken({ signal, timeoutMs: null }),
|
|
45
|
+
register: (signal) => this.internals.register({ signal, timeoutMs: null }),
|
|
46
|
+
notificationKey: (transport) => internals.notificationKey(collectionId, transport),
|
|
47
|
+
requestTimeoutMs: internals.timeouts.requestMs
|
|
45
48
|
});
|
|
46
49
|
}
|
|
47
50
|
get displayName() {
|
|
@@ -63,8 +66,8 @@ export class MdbaseConnection {
|
|
|
63
66
|
get route() {
|
|
64
67
|
return this.transport.route;
|
|
65
68
|
}
|
|
66
|
-
register() {
|
|
67
|
-
return captureConnectOutcome(() => this.internals.register(), REGISTRATION_PROBLEM_CODES);
|
|
69
|
+
register(options) {
|
|
70
|
+
return captureConnectOutcome(() => this.internals.register(options), REGISTRATION_PROBLEM_CODES);
|
|
68
71
|
}
|
|
69
72
|
info() {
|
|
70
73
|
const token = this.transport.currentToken();
|
|
@@ -123,12 +126,12 @@ export class MdbaseConnection {
|
|
|
123
126
|
notifyStorageChanged() {
|
|
124
127
|
this.transport.notifyStorageChanged();
|
|
125
128
|
}
|
|
126
|
-
checkDirectAccess() {
|
|
127
|
-
return captureConnectOutcome(() => this.transport.checkDirectAccess(), DIRECT_ACCESS_PROBLEM_CODES);
|
|
129
|
+
checkDirectAccess(options) {
|
|
130
|
+
return captureConnectOutcome(() => this.transport.checkDirectAccess(options), DIRECT_ACCESS_PROBLEM_CODES);
|
|
128
131
|
}
|
|
129
132
|
/** Call from a user gesture to request browser permission for direct local access. */
|
|
130
|
-
requestDirectAccess() {
|
|
131
|
-
return captureConnectOutcome(() => this.transport.requestDirectAccess(), DIRECT_ACCESS_PROBLEM_CODES);
|
|
133
|
+
requestDirectAccess(options) {
|
|
134
|
+
return captureConnectOutcome(() => this.transport.requestDirectAccess(options), DIRECT_ACCESS_PROBLEM_CODES);
|
|
132
135
|
}
|
|
133
136
|
disableDirectAccess() {
|
|
134
137
|
this.transport.disableDirectAccess();
|
|
@@ -151,21 +154,21 @@ export class MdbaseConnection {
|
|
|
151
154
|
collectionId,
|
|
152
155
|
replicaId,
|
|
153
156
|
transport: {
|
|
154
|
-
openSession: () => this.transport.performOperation("sync", { action: "open_session" }),
|
|
155
|
-
snapshot: (snapshotId, page) => this.transport.performOperation("sync", {
|
|
157
|
+
openSession: (options) => this.transport.performOperation("sync", { action: "open_session" }, syncRequestOptions(options, this.internals.timeouts.syncMs)),
|
|
158
|
+
snapshot: (snapshotId, page, options) => this.transport.performOperation("sync", {
|
|
156
159
|
action: "snapshot",
|
|
157
160
|
snapshot_id: snapshotId,
|
|
158
161
|
...(page ? { page } : {})
|
|
159
|
-
}),
|
|
160
|
-
changes: (after, limit = 200) => this.transport.performOperation("sync", {
|
|
162
|
+
}, syncRequestOptions(options, this.internals.timeouts.syncMs)),
|
|
163
|
+
changes: (after, limit = 200, options) => this.transport.performOperation("sync", {
|
|
161
164
|
action: "changes",
|
|
162
165
|
after,
|
|
163
166
|
limit
|
|
164
|
-
}),
|
|
165
|
-
mutate: (mutation) => this.transport.performOperation("sync", {
|
|
167
|
+
}, syncRequestOptions(options, this.internals.timeouts.syncMs)),
|
|
168
|
+
mutate: (mutation, options) => this.transport.performOperation("sync", {
|
|
166
169
|
action: "mutate",
|
|
167
170
|
mutation
|
|
168
|
-
})
|
|
171
|
+
}, syncRequestOptions(options, this.internals.timeouts.syncMs))
|
|
169
172
|
}
|
|
170
173
|
};
|
|
171
174
|
}
|
|
@@ -174,15 +177,15 @@ export class MdbaseConnection {
|
|
|
174
177
|
collectionId,
|
|
175
178
|
replicaId,
|
|
176
179
|
transport: {
|
|
177
|
-
openSession: () => this.transport.performAuthoritySyncRequest(collectionId, replicaId, "POST", "sessions"),
|
|
178
|
-
snapshot: (snapshotId, page) => {
|
|
180
|
+
openSession: (options) => this.transport.performAuthoritySyncRequest(collectionId, replicaId, "POST", "sessions", undefined, options),
|
|
181
|
+
snapshot: (snapshotId, page, options) => {
|
|
179
182
|
const query = new URLSearchParams({ snapshot_id: snapshotId });
|
|
180
183
|
if (page)
|
|
181
184
|
query.set("page", page);
|
|
182
|
-
return this.transport.performAuthoritySyncRequest(collectionId, replicaId, "GET", `snapshot?${query}
|
|
185
|
+
return this.transport.performAuthoritySyncRequest(collectionId, replicaId, "GET", `snapshot?${query}`, undefined, options);
|
|
183
186
|
},
|
|
184
|
-
changes: (after, limit = 200) => this.transport.performAuthoritySyncRequest(collectionId, replicaId, "GET", `changes?${new URLSearchParams({ after: String(after), limit: String(limit) })}
|
|
185
|
-
mutate: (mutation) => this.transport.performAuthoritySyncRequest(collectionId, replicaId, "POST", "mutations", mutation)
|
|
187
|
+
changes: (after, limit = 200, options) => this.transport.performAuthoritySyncRequest(collectionId, replicaId, "GET", `changes?${new URLSearchParams({ after: String(after), limit: String(limit) })}`, undefined, options),
|
|
188
|
+
mutate: (mutation, options) => this.transport.performAuthoritySyncRequest(collectionId, replicaId, "POST", "mutations", mutation, options)
|
|
186
189
|
}
|
|
187
190
|
};
|
|
188
191
|
}
|
|
@@ -198,25 +201,25 @@ export class MdbaseConnection {
|
|
|
198
201
|
registerNativeNotifications(options) {
|
|
199
202
|
return captureConnectOutcome(() => this.notifications.registerNativeNotifications(options), NOTIFICATION_PROBLEM_CODES);
|
|
200
203
|
}
|
|
201
|
-
unregisterNativeNotifications() {
|
|
202
|
-
return captureConnectOutcome(() => this.notifications.unregisterNativeNotifications(), NOTIFICATION_PROBLEM_CODES);
|
|
204
|
+
unregisterNativeNotifications(options) {
|
|
205
|
+
return captureConnectOutcome(() => this.notifications.unregisterNativeNotifications(options), NOTIFICATION_PROBLEM_CODES);
|
|
203
206
|
}
|
|
204
|
-
unregisterNotifications(serviceWorker) {
|
|
205
|
-
return captureConnectOutcome(() => this.notifications.unregisterNotifications(serviceWorker), NOTIFICATION_PROBLEM_CODES);
|
|
207
|
+
unregisterNotifications(serviceWorker, options) {
|
|
208
|
+
return captureConnectOutcome(() => this.notifications.unregisterNotifications(serviceWorker, options), NOTIFICATION_PROBLEM_CODES);
|
|
206
209
|
}
|
|
207
210
|
forget() {
|
|
208
211
|
const token = this.transport.currentToken();
|
|
209
|
-
this.internals.removeToken(this.collectionId, token?.keyHandle, "not_authorized");
|
|
212
|
+
this.internals.removeToken(this.collectionId, token?.keyHandle, "not_authorized", true);
|
|
210
213
|
this.transport.notifyStorageChanged();
|
|
211
214
|
}
|
|
212
|
-
describe() {
|
|
213
|
-
return this.collectionClient.describe();
|
|
215
|
+
describe(options) {
|
|
216
|
+
return this.collectionClient.describe(options);
|
|
214
217
|
}
|
|
215
218
|
changes(input = {}, options) {
|
|
216
219
|
return this.collectionClient.changes(input, options);
|
|
217
220
|
}
|
|
218
|
-
read(input) {
|
|
219
|
-
return this.collectionClient.read(input);
|
|
221
|
+
read(input, options) {
|
|
222
|
+
return this.collectionClient.read(input, options);
|
|
220
223
|
}
|
|
221
224
|
query(input = {}, options) {
|
|
222
225
|
return this.collectionClient.query(input, options);
|
|
@@ -227,29 +230,29 @@ export class MdbaseConnection {
|
|
|
227
230
|
queryAll(input = {}, options = {}) {
|
|
228
231
|
return this.collectionClient.queryAll(input, options);
|
|
229
232
|
}
|
|
230
|
-
listViews() {
|
|
231
|
-
return this.collectionClient.listViews();
|
|
233
|
+
listViews(options) {
|
|
234
|
+
return this.collectionClient.listViews(options);
|
|
232
235
|
}
|
|
233
|
-
executeView(input) {
|
|
234
|
-
return this.collectionClient.executeView(input);
|
|
236
|
+
executeView(input, options) {
|
|
237
|
+
return this.collectionClient.executeView(input, options);
|
|
235
238
|
}
|
|
236
|
-
readViewSource(input) {
|
|
237
|
-
return this.collectionClient.readViewSource(input);
|
|
239
|
+
readViewSource(input, options) {
|
|
240
|
+
return this.collectionClient.readViewSource(input, options);
|
|
238
241
|
}
|
|
239
|
-
createViewSource(input) {
|
|
240
|
-
return this.collectionClient.createViewSource(input);
|
|
242
|
+
createViewSource(input, options) {
|
|
243
|
+
return this.collectionClient.createViewSource(input, options);
|
|
241
244
|
}
|
|
242
|
-
updateViewSource(input) {
|
|
243
|
-
return this.collectionClient.updateViewSource(input);
|
|
245
|
+
updateViewSource(input, options) {
|
|
246
|
+
return this.collectionClient.updateViewSource(input, options);
|
|
244
247
|
}
|
|
245
|
-
deleteViewSource(input) {
|
|
246
|
-
return this.collectionClient.deleteViewSource(input);
|
|
248
|
+
deleteViewSource(input, options) {
|
|
249
|
+
return this.collectionClient.deleteViewSource(input, options);
|
|
247
250
|
}
|
|
248
|
-
create(input) {
|
|
249
|
-
return this.collectionClient.create(input);
|
|
251
|
+
create(input, options) {
|
|
252
|
+
return this.collectionClient.create(input, options);
|
|
250
253
|
}
|
|
251
|
-
update(input) {
|
|
252
|
-
return this.collectionClient.update(input);
|
|
254
|
+
update(input, options) {
|
|
255
|
+
return this.collectionClient.update(input, options);
|
|
253
256
|
}
|
|
254
257
|
delete(input, options) {
|
|
255
258
|
return this.collectionClient.delete(input, options);
|
|
@@ -264,150 +267,188 @@ export class MdbaseConnection {
|
|
|
264
267
|
return this.collectionClient.preflightRename(input, options);
|
|
265
268
|
}
|
|
266
269
|
async renameWithProgress(input, options = {}) {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
throwIfCancelled(options.signal);
|
|
283
|
-
emit("preflighting", true);
|
|
284
|
-
const previewOutcome = options.preflight
|
|
285
|
-
? connectSuccess(options.preflight)
|
|
286
|
-
: await this.preflightRename(input, { signal: options.signal });
|
|
287
|
-
if (!previewOutcome.ok)
|
|
288
|
-
return previewOutcome;
|
|
289
|
-
const preview = previewOutcome.value;
|
|
270
|
+
return withCooperativeRequestBudget(options, this.internals.timeouts.requestMs, async (budget) => {
|
|
271
|
+
const started = Date.now();
|
|
272
|
+
const resumed = false;
|
|
273
|
+
let estimate;
|
|
274
|
+
const emit = (state, cancellable, completedUnits = 0) => {
|
|
275
|
+
options.onProgress?.({
|
|
276
|
+
operation: "rename",
|
|
277
|
+
state,
|
|
278
|
+
elapsedMs: Date.now() - started,
|
|
279
|
+
cancellable,
|
|
280
|
+
resumed,
|
|
281
|
+
completedUnits,
|
|
282
|
+
...(estimate ? { estimate } : {})
|
|
283
|
+
});
|
|
284
|
+
};
|
|
290
285
|
try {
|
|
291
|
-
|
|
286
|
+
throwIfCancelled(budget.signal);
|
|
287
|
+
emit("preflighting", true);
|
|
288
|
+
const previewOutcome = options.preflight
|
|
289
|
+
? connectSuccess(options.preflight)
|
|
290
|
+
: await this.preflightRename(input, { signal: budget.signal, timeoutMs: null });
|
|
291
|
+
if (!previewOutcome.ok)
|
|
292
|
+
return previewOutcome;
|
|
293
|
+
const preview = previewOutcome.value;
|
|
294
|
+
try {
|
|
295
|
+
assertRenamePreview(input, preview);
|
|
296
|
+
}
|
|
297
|
+
catch (error) {
|
|
298
|
+
if (error instanceof MdbaseConnectError) {
|
|
299
|
+
return connectFailure(error.problem);
|
|
300
|
+
}
|
|
301
|
+
throw error;
|
|
302
|
+
}
|
|
303
|
+
estimate = renameEstimate(input, preview);
|
|
304
|
+
emit("ready", true);
|
|
305
|
+
throwIfCancelled(budget.signal);
|
|
306
|
+
const cancellable = this.transport.hasResumableMutationTransport();
|
|
307
|
+
emit("applying", cancellable);
|
|
308
|
+
const result = await this.rename(input, cancellable
|
|
309
|
+
? { signal: budget.signal, timeoutMs: null }
|
|
310
|
+
: { timeoutMs: remainingTimeout(budget.remainingMs()) });
|
|
311
|
+
if (result.ok)
|
|
312
|
+
emit("completed", false, estimate.totalUnits);
|
|
313
|
+
return result;
|
|
292
314
|
}
|
|
293
315
|
catch (error) {
|
|
316
|
+
if (isCancellation(error, budget.signal))
|
|
317
|
+
emit("cancelled", false);
|
|
294
318
|
if (error instanceof MdbaseConnectError) {
|
|
295
319
|
return connectFailure(error.problem);
|
|
296
320
|
}
|
|
297
321
|
throw error;
|
|
298
322
|
}
|
|
299
|
-
|
|
300
|
-
emit("ready", true);
|
|
301
|
-
throwIfCancelled(options.signal);
|
|
302
|
-
const cancellable = this.transport.hasResumableMutationTransport();
|
|
303
|
-
emit("applying", cancellable);
|
|
304
|
-
const result = await this.rename(input, cancellable ? { signal: options.signal } : undefined);
|
|
305
|
-
if (result.ok)
|
|
306
|
-
emit("completed", false, estimate.totalUnits);
|
|
307
|
-
return result;
|
|
308
|
-
}
|
|
309
|
-
catch (error) {
|
|
310
|
-
if (isCancellation(error, options.signal))
|
|
311
|
-
emit("cancelled", false);
|
|
312
|
-
if (error instanceof MdbaseConnectError) {
|
|
313
|
-
return connectFailure(error.problem);
|
|
314
|
-
}
|
|
315
|
-
throw error;
|
|
316
|
-
}
|
|
323
|
+
});
|
|
317
324
|
}
|
|
318
325
|
async deleteWithProgress(input, options = {}) {
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
throwIfCancelled(options.signal);
|
|
335
|
-
emit("preflighting", true);
|
|
336
|
-
const previewOutcome = options.preflight
|
|
337
|
-
? connectSuccess(options.preflight)
|
|
338
|
-
: await this.preflightDelete(input, { signal: options.signal });
|
|
339
|
-
if (!previewOutcome.ok)
|
|
340
|
-
return previewOutcome;
|
|
341
|
-
const preview = previewOutcome.value;
|
|
326
|
+
return withCooperativeRequestBudget(options, this.internals.timeouts.requestMs, async (budget) => {
|
|
327
|
+
const started = Date.now();
|
|
328
|
+
const resumed = false;
|
|
329
|
+
let estimate;
|
|
330
|
+
const emit = (state, cancellable, completedUnits = 0) => {
|
|
331
|
+
options.onProgress?.({
|
|
332
|
+
operation: "delete",
|
|
333
|
+
state,
|
|
334
|
+
elapsedMs: Date.now() - started,
|
|
335
|
+
cancellable,
|
|
336
|
+
resumed,
|
|
337
|
+
completedUnits,
|
|
338
|
+
...(estimate ? { estimate } : {})
|
|
339
|
+
});
|
|
340
|
+
};
|
|
342
341
|
try {
|
|
343
|
-
|
|
342
|
+
throwIfCancelled(budget.signal);
|
|
343
|
+
emit("preflighting", true);
|
|
344
|
+
const previewOutcome = options.preflight
|
|
345
|
+
? connectSuccess(options.preflight)
|
|
346
|
+
: await this.preflightDelete(input, { signal: budget.signal, timeoutMs: null });
|
|
347
|
+
if (!previewOutcome.ok)
|
|
348
|
+
return previewOutcome;
|
|
349
|
+
const preview = previewOutcome.value;
|
|
350
|
+
try {
|
|
351
|
+
assertDeletePreview(input, preview);
|
|
352
|
+
}
|
|
353
|
+
catch (error) {
|
|
354
|
+
if (error instanceof MdbaseConnectError) {
|
|
355
|
+
return connectFailure(error.problem);
|
|
356
|
+
}
|
|
357
|
+
throw error;
|
|
358
|
+
}
|
|
359
|
+
estimate = deleteEstimate(preview);
|
|
360
|
+
emit("ready", true);
|
|
361
|
+
throwIfCancelled(budget.signal);
|
|
362
|
+
const cancellable = this.transport.hasResumableMutationTransport();
|
|
363
|
+
emit("applying", cancellable);
|
|
364
|
+
const result = await this.delete(input, cancellable
|
|
365
|
+
? { signal: budget.signal, timeoutMs: null }
|
|
366
|
+
: { timeoutMs: remainingTimeout(budget.remainingMs()) });
|
|
367
|
+
if (result.ok)
|
|
368
|
+
emit("completed", false, estimate.totalUnits);
|
|
369
|
+
return result;
|
|
344
370
|
}
|
|
345
371
|
catch (error) {
|
|
372
|
+
if (isCancellation(error, budget.signal))
|
|
373
|
+
emit("cancelled", false);
|
|
346
374
|
if (error instanceof MdbaseConnectError) {
|
|
347
375
|
return connectFailure(error.problem);
|
|
348
376
|
}
|
|
349
377
|
throw error;
|
|
350
378
|
}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
throw error;
|
|
368
|
-
}
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
pendingMutations() {
|
|
382
|
+
return this.transport.pendingMutations().map((summary) => ({
|
|
383
|
+
...summary,
|
|
384
|
+
recover: (options) => captureConnectOutcome(() => this.recoverPendingMutation(summary.requestId, options), COLLECTION_MUTATION_PROBLEM_CODES)
|
|
385
|
+
}));
|
|
386
|
+
}
|
|
387
|
+
pendingMutation(requestId) {
|
|
388
|
+
const summary = this.transport.pendingMutation(requestId);
|
|
389
|
+
if (!summary)
|
|
390
|
+
return null;
|
|
391
|
+
return {
|
|
392
|
+
...summary,
|
|
393
|
+
recover: (options) => captureConnectOutcome(() => this.recoverPendingMutation(requestId, options), COLLECTION_MUTATION_PROBLEM_CODES)
|
|
394
|
+
};
|
|
369
395
|
}
|
|
370
|
-
|
|
371
|
-
|
|
396
|
+
async recoverPendingMutation(requestId, options) {
|
|
397
|
+
const result = await this.transport.recoverPendingMutation(requestId, options);
|
|
398
|
+
if (result && typeof result === "object" && "valid" in result) {
|
|
399
|
+
const envelope = result;
|
|
400
|
+
if (!envelope.valid)
|
|
401
|
+
throw new MdbaseConnectError(operationProblem(envelope));
|
|
402
|
+
return envelope.result;
|
|
403
|
+
}
|
|
404
|
+
return result;
|
|
372
405
|
}
|
|
373
|
-
|
|
374
|
-
return
|
|
406
|
+
validate(input = {}, options) {
|
|
407
|
+
return this.collectionClient.validate(input, options);
|
|
375
408
|
}
|
|
376
|
-
|
|
377
|
-
return this.collectionClient.
|
|
409
|
+
readType(input, options) {
|
|
410
|
+
return this.collectionClient.readType(input, options);
|
|
378
411
|
}
|
|
379
|
-
|
|
380
|
-
return this.collectionClient.
|
|
412
|
+
createType(input, options) {
|
|
413
|
+
return this.collectionClient.createType(input, options);
|
|
381
414
|
}
|
|
382
|
-
|
|
383
|
-
return this.collectionClient.
|
|
415
|
+
updateType(input, options) {
|
|
416
|
+
return this.collectionClient.updateType(input, options);
|
|
384
417
|
}
|
|
385
|
-
|
|
386
|
-
return this.collectionClient.
|
|
418
|
+
assessTypePack(input, options) {
|
|
419
|
+
return this.collectionClient.assessTypePack(input, options);
|
|
387
420
|
}
|
|
388
|
-
|
|
389
|
-
return this.collectionClient.
|
|
421
|
+
applyTypePack(input, options) {
|
|
422
|
+
return this.collectionClient.applyTypePack(input, options);
|
|
390
423
|
}
|
|
391
|
-
|
|
392
|
-
return this.collectionClient.
|
|
424
|
+
assessCollectionSetup(input, options) {
|
|
425
|
+
return this.collectionClient.assessCollectionSetup(input, options);
|
|
393
426
|
}
|
|
394
|
-
|
|
395
|
-
return this.collectionClient.
|
|
427
|
+
applyCollectionSetup(input, options) {
|
|
428
|
+
return this.collectionClient.applyCollectionSetup(input, options);
|
|
396
429
|
}
|
|
397
|
-
|
|
398
|
-
return this.collectionClient.
|
|
430
|
+
listTimers(namespace, options) {
|
|
431
|
+
return this.collectionClient.listTimers(namespace, options);
|
|
399
432
|
}
|
|
400
|
-
|
|
401
|
-
return this.collectionClient.
|
|
433
|
+
putTimer(input, options) {
|
|
434
|
+
return this.collectionClient.putTimer(input, options);
|
|
402
435
|
}
|
|
403
|
-
|
|
404
|
-
return this.collectionClient.
|
|
436
|
+
cancelTimer(input, options) {
|
|
437
|
+
return this.collectionClient.cancelTimer(input, options);
|
|
405
438
|
}
|
|
406
|
-
|
|
407
|
-
return this.collectionClient.
|
|
439
|
+
reconcileTimers(input, options) {
|
|
440
|
+
return this.collectionClient.reconcileTimers(input, options);
|
|
408
441
|
}
|
|
409
|
-
|
|
410
|
-
return this.
|
|
442
|
+
watch(input = {}, options = {}) {
|
|
443
|
+
return captureConnectOutcome(() => withRequestBudget(options, this.internals.timeouts.watchStartMs, async (budget) => {
|
|
444
|
+
const initial = await this.collectionClient.changes(input.cursor === undefined ? {} : { after: input.cursor, limit: 200 }, { signal: budget.signal, timeoutMs: null });
|
|
445
|
+
if (!initial.ok)
|
|
446
|
+
throw new MdbaseConnectError(initial.problem);
|
|
447
|
+
if (initial.value.reset) {
|
|
448
|
+
throw new MdbaseConnectError(connectProblem("change_cursor_reset", "The collection change cursor expired. Refresh collection state before subscribing again."));
|
|
449
|
+
}
|
|
450
|
+
return new CollectionWatchSubscription(this.collectionClient, initial.value.cursor, input, input.cursor === undefined ? [] : initial.value.events, this.internals.timeouts.watchStartMs);
|
|
451
|
+
}), COLLECTION_CHANGES_PROBLEM_CODES);
|
|
411
452
|
}
|
|
412
453
|
emitConnection() {
|
|
413
454
|
const connection = this.info();
|
|
@@ -415,3 +456,120 @@ export class MdbaseConnection {
|
|
|
415
456
|
listener(connection);
|
|
416
457
|
}
|
|
417
458
|
}
|
|
459
|
+
class CollectionWatchSubscription {
|
|
460
|
+
client;
|
|
461
|
+
input;
|
|
462
|
+
watchStartTimeoutMs;
|
|
463
|
+
changes = new Set();
|
|
464
|
+
statuses = new Set();
|
|
465
|
+
problems = new Set();
|
|
466
|
+
controller = new AbortController();
|
|
467
|
+
removeLifetimeAbort;
|
|
468
|
+
currentStatus;
|
|
469
|
+
currentProblem = null;
|
|
470
|
+
pendingChanges;
|
|
471
|
+
constructor(client, cursor, input, pendingChanges, watchStartTimeoutMs) {
|
|
472
|
+
this.client = client;
|
|
473
|
+
this.input = input;
|
|
474
|
+
this.watchStartTimeoutMs = watchStartTimeoutMs;
|
|
475
|
+
this.pendingChanges = [...pendingChanges];
|
|
476
|
+
this.currentStatus = { state: "connected", cursor, recovered: false };
|
|
477
|
+
const lifetimeSignal = input.lifetimeSignal;
|
|
478
|
+
if (lifetimeSignal?.aborted)
|
|
479
|
+
this.close();
|
|
480
|
+
else if (lifetimeSignal) {
|
|
481
|
+
const close = () => this.close();
|
|
482
|
+
lifetimeSignal.addEventListener("abort", close, { once: true });
|
|
483
|
+
this.removeLifetimeAbort = () => lifetimeSignal.removeEventListener("abort", close);
|
|
484
|
+
}
|
|
485
|
+
if (!this.controller.signal.aborted)
|
|
486
|
+
void this.run(cursor);
|
|
487
|
+
}
|
|
488
|
+
get status() {
|
|
489
|
+
return this.currentStatus;
|
|
490
|
+
}
|
|
491
|
+
get problem() {
|
|
492
|
+
return this.currentProblem;
|
|
493
|
+
}
|
|
494
|
+
subscribe(listener, onStatus, onProblem) {
|
|
495
|
+
this.changes.add(listener);
|
|
496
|
+
if (onStatus) {
|
|
497
|
+
this.statuses.add(onStatus);
|
|
498
|
+
onStatus(this.currentStatus);
|
|
499
|
+
}
|
|
500
|
+
if (onProblem) {
|
|
501
|
+
this.problems.add(onProblem);
|
|
502
|
+
if (this.currentProblem)
|
|
503
|
+
onProblem(this.currentProblem);
|
|
504
|
+
}
|
|
505
|
+
for (const change of this.pendingChanges)
|
|
506
|
+
listener(change);
|
|
507
|
+
this.pendingChanges = [];
|
|
508
|
+
return () => {
|
|
509
|
+
this.changes.delete(listener);
|
|
510
|
+
if (onStatus)
|
|
511
|
+
this.statuses.delete(onStatus);
|
|
512
|
+
if (onProblem)
|
|
513
|
+
this.problems.delete(onProblem);
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
close() {
|
|
517
|
+
if (this.controller.signal.aborted)
|
|
518
|
+
return;
|
|
519
|
+
this.controller.abort();
|
|
520
|
+
this.removeLifetimeAbort?.();
|
|
521
|
+
this.removeLifetimeAbort = undefined;
|
|
522
|
+
const cursor = "cursor" in this.currentStatus ? this.currentStatus.cursor : undefined;
|
|
523
|
+
this.publishStatus({ state: "closed", ...(cursor === undefined ? {} : { cursor }) });
|
|
524
|
+
}
|
|
525
|
+
async run(cursor) {
|
|
526
|
+
let firstStatus = true;
|
|
527
|
+
const iterator = this.client.watch({
|
|
528
|
+
cursor,
|
|
529
|
+
pollIntervalMs: this.input.pollIntervalMs,
|
|
530
|
+
retry: this.input.retry,
|
|
531
|
+
signal: this.controller.signal,
|
|
532
|
+
timeoutMs: this.watchStartTimeoutMs,
|
|
533
|
+
onStatus: (status) => {
|
|
534
|
+
if (firstStatus && status.state === "connecting") {
|
|
535
|
+
firstStatus = false;
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
firstStatus = false;
|
|
539
|
+
this.publishStatus(status);
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
try {
|
|
543
|
+
for await (const outcome of iterator) {
|
|
544
|
+
if (this.controller.signal.aborted)
|
|
545
|
+
return;
|
|
546
|
+
if (!outcome.ok) {
|
|
547
|
+
this.currentProblem = outcome.problem;
|
|
548
|
+
for (const listener of this.problems)
|
|
549
|
+
listener(outcome.problem);
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
for (const listener of this.changes)
|
|
553
|
+
listener(outcome.value);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
finally {
|
|
557
|
+
if (!this.controller.signal.aborted)
|
|
558
|
+
this.close();
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
publishStatus(status) {
|
|
562
|
+
this.currentStatus = status;
|
|
563
|
+
for (const listener of this.statuses)
|
|
564
|
+
listener(status);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
function syncRequestOptions(options, defaultTimeoutMs) {
|
|
568
|
+
return {
|
|
569
|
+
...options,
|
|
570
|
+
timeoutMs: options?.timeoutMs === undefined ? defaultTimeoutMs : options.timeoutMs
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
function remainingTimeout(remainingMs) {
|
|
574
|
+
return remainingMs === null ? null : Math.max(1, Math.ceil(remainingMs));
|
|
575
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Opt-in cryptographic storage and transport primitives. */
|
|
2
|
+
export { IndexedDbGrantKeyStore, MemoryGrantKeyStore, RelayCryptoError, signAuthorityRequest, authorityProofMessage, encryptRelayRequest, decryptRelayResponse, deriveP256SharedSecret, validateGrantEncryption } from "./crypto.js";
|
|
3
|
+
export type { GrantKeyRecord, GrantKeyStore, RelayBinding, AuthorityProofInput } from "./crypto.js";
|
|
4
|
+
export { FileTransferCryptoError, GrantFileTransferCipher, deriveFileTransferKey } from "./file-crypto.js";
|
|
5
|
+
export type { FileTransferBinding } from "./file-crypto.js";
|
|
6
|
+
export { IndexedDbApplicationIdentityStore, MemoryApplicationIdentityStore, ApplicationIdentityStoreError, applicationInstallationId, signApplicationAuthorization, applicationIdentity } from "./application-identity.js";
|
|
7
|
+
export type { ApplicationIdentity, ApplicationIdentityStore } from "./application-identity.js";
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Opt-in cryptographic storage and transport primitives. */
|
|
2
|
+
export { IndexedDbGrantKeyStore, MemoryGrantKeyStore, RelayCryptoError, signAuthorityRequest, authorityProofMessage, encryptRelayRequest, decryptRelayResponse, deriveP256SharedSecret, validateGrantEncryption } from "./crypto.js";
|
|
3
|
+
export { FileTransferCryptoError, GrantFileTransferCipher, deriveFileTransferKey } from "./file-crypto.js";
|
|
4
|
+
export { IndexedDbApplicationIdentityStore, MemoryApplicationIdentityStore, ApplicationIdentityStoreError, applicationInstallationId, signApplicationAuthorization, applicationIdentity } from "./application-identity.js";
|