@mdbase-dev/connect 0.1.0-beta.21 → 0.1.0-beta.23
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 +108 -38
- package/dist/authorization-url.d.ts +2 -1
- package/dist/authorization-url.js +5 -7
- 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/collection-client.d.ts +34 -31
- package/dist/collection-client.js +107 -61
- package/dist/connect.d.ts +4 -3
- package/dist/connect.js +53 -38
- package/dist/connection-file-transport.d.ts +31 -0
- package/dist/connection-file-transport.js +42 -0
- package/dist/connection-notifications.js +21 -21
- package/dist/connection-transport.d.ts +2 -0
- package/dist/connection-transport.js +95 -37
- package/dist/connection-types.d.ts +2 -1
- package/dist/connection.d.ts +49 -45
- package/dist/connection.js +73 -33
- package/dist/crypto.d.ts +5 -6
- package/dist/crypto.js +13 -13
- package/dist/errors.d.ts +38 -24
- package/dist/errors.js +130 -71
- package/dist/file-crypto.d.ts +25 -0
- package/dist/file-crypto.js +132 -0
- package/dist/file-sha256.d.ts +12 -0
- package/dist/file-sha256.js +143 -0
- package/dist/file-stream-source.d.ts +17 -0
- package/dist/file-stream-source.js +157 -0
- package/dist/files.d.ts +91 -0
- package/dist/files.js +644 -0
- package/dist/hosted-file-request.d.ts +6 -0
- package/dist/hosted-file-request.js +63 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +3 -0
- package/dist/internal-types.d.ts +3 -1
- package/dist/local-file-transport.d.ts +30 -0
- package/dist/local-file-transport.js +367 -0
- package/dist/notifications.js +5 -5
- package/dist/operation-helpers.d.ts +2 -2
- package/dist/operation-helpers.js +22 -18
- package/dist/operation-types.d.ts +3 -4
- package/dist/outcomes.d.ts +51 -0
- package/dist/outcomes.js +168 -0
- package/dist/runtime-utils.d.ts +6 -1
- package/dist/runtime-utils.js +90 -11
- package/dist/selection.js +3 -3
- package/dist/session.d.ts +9 -8
- package/dist/session.js +31 -20
- package/package.json +2 -2
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { isConnectProblem } from "@mdbase-dev/connect-protocol";
|
|
1
2
|
import { decryptRelayResponse, encryptRelayRequest, RelayCryptoError, signAuthorityRequest } from "./crypto.js";
|
|
2
|
-
import { MdbaseConnectError } from "./errors.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { MdbaseConnectError, connectError, serverConnectError } from "./errors.js";
|
|
4
|
+
import { ConnectionFileTransport } from "./connection-file-transport.js";
|
|
5
|
+
import { directFallbackStatus, isMutation, localNetworkPermission, loopbackRequest, operationFingerprint, operationTransportError, sameAuthorization, throwIfCancelled, unknownMutationOutcome } from "./operation-helpers.js";
|
|
6
|
+
import { apiError, oauthErrorCode, parseGrantScope, parseStored, validStoredAuthority, validStoredEncryption, validFileCapability } from "./runtime-utils.js";
|
|
5
7
|
export class ConnectionTransport {
|
|
6
8
|
serverUrl;
|
|
7
9
|
storage;
|
|
@@ -11,6 +13,7 @@ export class ConnectionTransport {
|
|
|
11
13
|
collectionId;
|
|
12
14
|
internals;
|
|
13
15
|
onChange;
|
|
16
|
+
files;
|
|
14
17
|
refreshPromise = null;
|
|
15
18
|
directStatus;
|
|
16
19
|
currentRoute = "relay";
|
|
@@ -25,6 +28,21 @@ export class ConnectionTransport {
|
|
|
25
28
|
this.collectionId = options.collectionId;
|
|
26
29
|
this.internals = options.internals;
|
|
27
30
|
this.onChange = options.onChange;
|
|
31
|
+
this.files = new ConnectionFileTransport({
|
|
32
|
+
keyStore: this.keyStore,
|
|
33
|
+
serverUrl: this.serverUrl,
|
|
34
|
+
loopbackUrl: this.loopbackUrl,
|
|
35
|
+
authorizedToken: () => this.authorizedToken(),
|
|
36
|
+
refreshAuthorization: () => this.refreshAuthorization(),
|
|
37
|
+
shouldAttemptDirect: (token) => this.shouldAttemptDirect(token),
|
|
38
|
+
onDirectAvailable: () => {
|
|
39
|
+
this.markDirectAvailable();
|
|
40
|
+
this.setRoute("direct");
|
|
41
|
+
},
|
|
42
|
+
onDirectUnavailable: () => this.markDirectUnavailable(),
|
|
43
|
+
onRelayAvailable: () => this.setRoute("relay"),
|
|
44
|
+
authorityProofHeaders: (token, method, url, body, credential) => this.authorityProofHeaders(token, method, url, body, credential)
|
|
45
|
+
});
|
|
28
46
|
this.directStatus = this.directAccessMode === "disabled"
|
|
29
47
|
? "disabled"
|
|
30
48
|
: "unavailable";
|
|
@@ -83,7 +101,7 @@ export class ConnectionTransport {
|
|
|
83
101
|
async resumePendingMutation(input, options) {
|
|
84
102
|
const pending = this.pendingMutation();
|
|
85
103
|
if (!pending) {
|
|
86
|
-
throw
|
|
104
|
+
throw connectError("no_pending_mutation", "There is no interrupted mutation to resume.");
|
|
87
105
|
}
|
|
88
106
|
return this.performOperation(pending.operation, input, options);
|
|
89
107
|
}
|
|
@@ -91,15 +109,13 @@ export class ConnectionTransport {
|
|
|
91
109
|
throwIfCancelled(options.signal);
|
|
92
110
|
let token = this.currentToken();
|
|
93
111
|
if (!token)
|
|
94
|
-
throw
|
|
112
|
+
throw connectError("not_authorized", "Connect this application before accessing a collection.");
|
|
95
113
|
if (!token.operations.includes(operation)) {
|
|
96
|
-
throw
|
|
97
|
-
requiresAuthorization: true,
|
|
98
|
-
recovery: "reauthorize",
|
|
114
|
+
throw connectError("insufficient_access", `This connection does not allow ${operation}.`, {
|
|
99
115
|
details: {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
116
|
+
required_operations: [operation],
|
|
117
|
+
granted_operations: [...token.operations],
|
|
118
|
+
missing_operations: [operation]
|
|
103
119
|
}
|
|
104
120
|
});
|
|
105
121
|
}
|
|
@@ -107,14 +123,14 @@ export class ConnectionTransport {
|
|
|
107
123
|
if (!tryDirect) {
|
|
108
124
|
token = await this.authorizedToken();
|
|
109
125
|
if (!token)
|
|
110
|
-
throw
|
|
126
|
+
throw connectError("not_authorized", "Reconnect this application to continue.");
|
|
111
127
|
}
|
|
112
128
|
let attempt;
|
|
113
129
|
try {
|
|
114
130
|
attempt = await this.sendOperation(token, operation, input, tryDirect, options);
|
|
115
131
|
}
|
|
116
132
|
catch (error) {
|
|
117
|
-
throw operationTransportError(error, options.signal, isMutation(operation, input) && this.pendingMutation() !== null);
|
|
133
|
+
throw operationTransportError(error, options.signal, isMutation(operation, input) && this.pendingMutation() !== null, token.authority ? "hosted_provider_unavailable" : "relay_unavailable");
|
|
118
134
|
}
|
|
119
135
|
let response = attempt.response;
|
|
120
136
|
const staleBinding = response.status === 409
|
|
@@ -123,7 +139,7 @@ export class ConnectionTransport {
|
|
|
123
139
|
if (attempt.pendingMutation
|
|
124
140
|
&& (attempt.directDeliveryUncertain
|
|
125
141
|
|| (attempt.encryptedRequest && attempt.resumingMutation))) {
|
|
126
|
-
throw
|
|
142
|
+
throw connectError("operation_outcome_unknown", "The direct operation may have completed, but its encrypted grant changed before the response could be recovered. Refresh before making another change.", { operationOutcome: "unknown" });
|
|
127
143
|
}
|
|
128
144
|
if (attempt.pendingMutation)
|
|
129
145
|
this.clearPendingMutation();
|
|
@@ -133,17 +149,25 @@ export class ConnectionTransport {
|
|
|
133
149
|
attempt = await this.sendOperation(token, operation, input, tryDirect, options);
|
|
134
150
|
}
|
|
135
151
|
catch (error) {
|
|
136
|
-
throw operationTransportError(error, options.signal, isMutation(operation, input) && this.pendingMutation() !== null);
|
|
152
|
+
throw operationTransportError(error, options.signal, isMutation(operation, input) && this.pendingMutation() !== null, token.authority ? "hosted_provider_unavailable" : "relay_unavailable");
|
|
137
153
|
}
|
|
138
154
|
response = attempt.response;
|
|
139
155
|
}
|
|
140
|
-
|
|
156
|
+
let body;
|
|
157
|
+
try {
|
|
158
|
+
body = await response.json();
|
|
159
|
+
}
|
|
160
|
+
catch (cause) {
|
|
161
|
+
if (attempt.pendingMutation)
|
|
162
|
+
throw unknownMutationOutcome(cause);
|
|
163
|
+
throw connectError("invalid_operation_response", "The collection authority returned a response that is not valid JSON.", { cause });
|
|
164
|
+
}
|
|
141
165
|
if (!response.ok) {
|
|
142
166
|
const error = apiError(body, "operation_failed", "Collection operation failed.", response.status);
|
|
143
167
|
if (attempt.pendingMutation
|
|
144
168
|
&& (attempt.directDeliveryUncertain
|
|
145
169
|
|| (attempt.encryptedRequest && attempt.resumingMutation))) {
|
|
146
|
-
throw
|
|
170
|
+
throw unknownMutationOutcome(error);
|
|
147
171
|
}
|
|
148
172
|
if (attempt.pendingMutation && !attempt.directDeliveryUncertain) {
|
|
149
173
|
this.clearPendingMutation();
|
|
@@ -156,26 +180,54 @@ export class ConnectionTransport {
|
|
|
156
180
|
if (attempt.encryptedRequest) {
|
|
157
181
|
const encryptedResponse = body?.envelope;
|
|
158
182
|
if (!encryptedResponse || !token.encryption || !token.grantId || !token.keyHandle) {
|
|
159
|
-
|
|
183
|
+
if (attempt.pendingMutation)
|
|
184
|
+
throw unknownMutationOutcome(new Error("Encrypted operation response was missing its envelope."));
|
|
185
|
+
throw connectError("invalid_encrypted_response", "The relay did not return an encrypted connector response.");
|
|
160
186
|
}
|
|
161
187
|
try {
|
|
162
188
|
const decrypted = await decryptRelayResponse(this.keyStore, token.keyHandle, { grantId: token.grantId, applicationId: token.clientId, encryption: token.encryption }, attempt.encryptedRequest, encryptedResponse);
|
|
163
189
|
if (attempt.pendingMutation)
|
|
164
190
|
this.clearPendingMutation();
|
|
165
191
|
if (!decrypted.ok)
|
|
166
|
-
throw
|
|
192
|
+
throw serverConnectError(decrypted.problem.code === "unknown"
|
|
193
|
+
? decrypted.problem.server_code
|
|
194
|
+
: decrypted.problem.code, decrypted.problem.message, {
|
|
195
|
+
details: decrypted.problem.details,
|
|
196
|
+
operationOutcome: decrypted.problem.operation_outcome ?? "rejected",
|
|
197
|
+
traceId: decrypted.problem.trace_id
|
|
198
|
+
});
|
|
167
199
|
return decrypted.result;
|
|
168
200
|
}
|
|
169
201
|
catch (error) {
|
|
170
202
|
if (error instanceof MdbaseConnectError)
|
|
171
203
|
throw error;
|
|
172
|
-
if (error instanceof RelayCryptoError)
|
|
173
|
-
|
|
204
|
+
if (error instanceof RelayCryptoError) {
|
|
205
|
+
if (attempt.pendingMutation)
|
|
206
|
+
throw unknownMutationOutcome(error);
|
|
207
|
+
throw serverConnectError(error.code, error.message);
|
|
208
|
+
}
|
|
174
209
|
throw error;
|
|
175
210
|
}
|
|
176
211
|
}
|
|
177
212
|
if (body?.protocol_version !== 1 || body?.request_id !== attempt.requestId) {
|
|
178
|
-
|
|
213
|
+
if (attempt.pendingMutation)
|
|
214
|
+
throw unknownMutationOutcome(new Error("Operation response protocol or request ID did not match."));
|
|
215
|
+
throw connectError("invalid_operation_response", "The collection authority returned a response for a different protocol request.");
|
|
216
|
+
}
|
|
217
|
+
if (body.ok === false) {
|
|
218
|
+
if (!isConnectProblem(body.problem)) {
|
|
219
|
+
if (attempt.pendingMutation)
|
|
220
|
+
throw unknownMutationOutcome(new Error("Operation rejection did not contain a canonical problem."));
|
|
221
|
+
throw connectError("invalid_operation_response", "The collection authority returned an invalid operation problem.");
|
|
222
|
+
}
|
|
223
|
+
if (attempt.pendingMutation)
|
|
224
|
+
this.clearPendingMutation();
|
|
225
|
+
throw new MdbaseConnectError(body.problem);
|
|
226
|
+
}
|
|
227
|
+
if (body.ok !== true || !("result" in body)) {
|
|
228
|
+
if (attempt.pendingMutation)
|
|
229
|
+
throw unknownMutationOutcome(new Error("Successful operation response did not contain a result."));
|
|
230
|
+
throw connectError("invalid_operation_response", "The collection authority returned an incomplete operation response.");
|
|
179
231
|
}
|
|
180
232
|
if (attempt.pendingMutation)
|
|
181
233
|
this.clearPendingMutation();
|
|
@@ -186,7 +238,7 @@ export class ConnectionTransport {
|
|
|
186
238
|
if (!token?.authority
|
|
187
239
|
|| token.collectionId !== collectionId
|
|
188
240
|
|| token.authority.replicaId !== replicaId) {
|
|
189
|
-
throw
|
|
241
|
+
throw connectError("authority_authorization_changed", "Reconnect this collection authority before synchronizing.");
|
|
190
242
|
}
|
|
191
243
|
let response = await this.sendAuthoritySyncRequest(token, method, path, input);
|
|
192
244
|
if (response.status === 401 && token.refreshToken) {
|
|
@@ -194,7 +246,7 @@ export class ConnectionTransport {
|
|
|
194
246
|
if (!token.authority
|
|
195
247
|
|| token.collectionId !== collectionId
|
|
196
248
|
|| token.authority.replicaId !== replicaId) {
|
|
197
|
-
throw
|
|
249
|
+
throw connectError("authority_authorization_changed", "Reconnect this collection authority before synchronizing.");
|
|
198
250
|
}
|
|
199
251
|
response = await this.sendAuthoritySyncRequest(token, method, path, input);
|
|
200
252
|
}
|
|
@@ -205,7 +257,7 @@ export class ConnectionTransport {
|
|
|
205
257
|
}
|
|
206
258
|
async sendAuthoritySyncRequest(token, method, path, input) {
|
|
207
259
|
if (!token.authority) {
|
|
208
|
-
throw
|
|
260
|
+
throw connectError("not_remote_authority", "This authorization has no remote authority endpoint.");
|
|
209
261
|
}
|
|
210
262
|
const url = `${token.authority.syncUrl}/${path}`;
|
|
211
263
|
const body = input === undefined ? undefined : JSON.stringify(input);
|
|
@@ -237,7 +289,7 @@ export class ConnectionTransport {
|
|
|
237
289
|
|| pending.keyId !== token.encryption?.key_id
|
|
238
290
|
|| pending.operation !== operation
|
|
239
291
|
|| pending.inputFingerprint !== inputFingerprint) {
|
|
240
|
-
throw
|
|
292
|
+
throw connectError("pending_mutation_unresolved", "A previous write still has an unknown outcome. Retry that exact write before making another change.");
|
|
241
293
|
}
|
|
242
294
|
requestId = pending.requestId;
|
|
243
295
|
resumingMutation = true;
|
|
@@ -246,13 +298,13 @@ export class ConnectionTransport {
|
|
|
246
298
|
}
|
|
247
299
|
if (token.encryption && !token.authority) {
|
|
248
300
|
if (!token.grantId || !token.keyHandle) {
|
|
249
|
-
throw
|
|
301
|
+
throw connectError("missing_grant_key", "Reconnect this application to restore encrypted access.");
|
|
250
302
|
}
|
|
251
303
|
try {
|
|
252
304
|
if (pendingMutation) {
|
|
253
305
|
if (pending) {
|
|
254
306
|
if (!pending.envelope) {
|
|
255
|
-
throw
|
|
307
|
+
throw connectError("pending_mutation_unresolved", "The pending write belongs to a different transport. Reconnect before retrying it.");
|
|
256
308
|
}
|
|
257
309
|
encryptedRequest = pending.envelope;
|
|
258
310
|
}
|
|
@@ -276,7 +328,7 @@ export class ConnectionTransport {
|
|
|
276
328
|
}
|
|
277
329
|
catch (error) {
|
|
278
330
|
if (error instanceof RelayCryptoError)
|
|
279
|
-
throw
|
|
331
|
+
throw serverConnectError(error.code, error.message);
|
|
280
332
|
throw error;
|
|
281
333
|
}
|
|
282
334
|
body = encryptedRequest;
|
|
@@ -341,7 +393,7 @@ export class ConnectionTransport {
|
|
|
341
393
|
}
|
|
342
394
|
catch (error) {
|
|
343
395
|
if (pendingMutation && (directDeliveryUncertain || resumingMutation))
|
|
344
|
-
throw
|
|
396
|
+
throw unknownMutationOutcome(error);
|
|
345
397
|
throw error;
|
|
346
398
|
}
|
|
347
399
|
let response;
|
|
@@ -358,7 +410,7 @@ export class ConnectionTransport {
|
|
|
358
410
|
}
|
|
359
411
|
catch (error) {
|
|
360
412
|
if (pendingMutation && (directDeliveryUncertain || resumingMutation))
|
|
361
|
-
throw
|
|
413
|
+
throw unknownMutationOutcome(error);
|
|
362
414
|
throw error;
|
|
363
415
|
}
|
|
364
416
|
if (response.ok)
|
|
@@ -526,6 +578,9 @@ export class ConnectionTransport {
|
|
|
526
578
|
if (!parseGrantScope(token.scope)) {
|
|
527
579
|
return invalidate(token.keyHandle);
|
|
528
580
|
}
|
|
581
|
+
if (token.fileCapability && !validFileCapability(token.fileCapability)) {
|
|
582
|
+
return invalidate(token.keyHandle);
|
|
583
|
+
}
|
|
529
584
|
if (token.authority
|
|
530
585
|
&& !validStoredAuthority(token.authority, token.collectionId)) {
|
|
531
586
|
return invalidate(token.keyHandle);
|
|
@@ -564,7 +619,7 @@ export class ConnectionTransport {
|
|
|
564
619
|
return token;
|
|
565
620
|
if (!token.refreshToken || (token.refreshExpiresAt ?? 0) <= Date.now()) {
|
|
566
621
|
if (this.directCapable(token)) {
|
|
567
|
-
throw
|
|
622
|
+
throw connectError("relay_authorization_expired", "Direct access is still available on this computer, but using the relay requires reconnecting this application.");
|
|
568
623
|
}
|
|
569
624
|
this.internals.removeToken(this.collectionId, token.keyHandle);
|
|
570
625
|
return null;
|
|
@@ -582,10 +637,10 @@ export class ConnectionTransport {
|
|
|
582
637
|
async performRefresh() {
|
|
583
638
|
const current = this.currentToken();
|
|
584
639
|
if (!current?.refreshToken) {
|
|
585
|
-
throw
|
|
640
|
+
throw connectError("not_authorized", "Reconnect this application to continue.");
|
|
586
641
|
}
|
|
587
642
|
if ((current.refreshExpiresAt ?? 0) <= Date.now()) {
|
|
588
|
-
throw
|
|
643
|
+
throw connectError("relay_authorization_expired", "Direct access is still available on this computer, but using the relay requires reconnecting this application.");
|
|
589
644
|
}
|
|
590
645
|
const attemptedRefreshToken = current.refreshToken;
|
|
591
646
|
const refreshUrl = `${this.serverUrl}/oauth/token`;
|
|
@@ -614,6 +669,9 @@ export class ConnectionTransport {
|
|
|
614
669
|
if (!this.directCapable(current)) {
|
|
615
670
|
this.internals.removeToken(this.collectionId, current.keyHandle);
|
|
616
671
|
}
|
|
672
|
+
if ((oauthErrorCode(body) ?? body?.error?.code) === "invalid_grant") {
|
|
673
|
+
throw connectError("authorization_expired", body?.error_description ?? body?.error?.message ?? "Reconnect this application to continue.", { status: response.status });
|
|
674
|
+
}
|
|
617
675
|
throw apiError(body, "authorization_expired", "Reconnect this application to continue.", response.status);
|
|
618
676
|
}
|
|
619
677
|
return this.storeTokenResponse(body, current.clientId, current.keyHandle);
|
|
@@ -622,7 +680,7 @@ export class ConnectionTransport {
|
|
|
622
680
|
if (!token.authority?.proofPublicKey)
|
|
623
681
|
return {};
|
|
624
682
|
if (!token.keyHandle) {
|
|
625
|
-
throw
|
|
683
|
+
throw connectError("missing_grant_key", "Reconnect this application to restore remote authority request signing.");
|
|
626
684
|
}
|
|
627
685
|
try {
|
|
628
686
|
const target = new URL(url);
|
|
@@ -635,14 +693,14 @@ export class ConnectionTransport {
|
|
|
635
693
|
}
|
|
636
694
|
catch (error) {
|
|
637
695
|
if (error instanceof RelayCryptoError) {
|
|
638
|
-
throw
|
|
696
|
+
throw serverConnectError(error.code, error.message);
|
|
639
697
|
}
|
|
640
698
|
throw error;
|
|
641
699
|
}
|
|
642
700
|
}
|
|
643
701
|
storeTokenResponse(body, clientId, keyHandle) {
|
|
644
702
|
if (body.collection_id !== this.collectionId) {
|
|
645
|
-
throw
|
|
703
|
+
throw connectError("collection_mismatch", "The refreshed authorization belongs to a different collection.");
|
|
646
704
|
}
|
|
647
705
|
const token = this.internals.storeTokenResponse(body, clientId, keyHandle);
|
|
648
706
|
this.currentRoute = token.authority ? "remote" : "relay";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CollectionOperation, GrantScope, JsonObject, SyncChangesPage, SyncMutation, SyncMutationReceipt, SyncSession, SyncSnapshotPage } from "@mdbase-dev/connect-protocol";
|
|
1
|
+
import type { CollectionOperation, FileCapability, GrantScope, JsonObject, SyncChangesPage, SyncMutation, SyncMutationReceipt, SyncSession, SyncSnapshotPage } from "@mdbase-dev/connect-protocol";
|
|
2
2
|
export type MdbaseConnectionRoute = "remote" | "direct" | "relay";
|
|
3
3
|
export type DirectAccessStatus = "disabled" | "permission_required" | "checking" | "available" | "unavailable" | "denied";
|
|
4
4
|
export interface MdbaseConnectionInfo {
|
|
@@ -6,6 +6,7 @@ export interface MdbaseConnectionInfo {
|
|
|
6
6
|
displayName: string;
|
|
7
7
|
operations: CollectionOperation[];
|
|
8
8
|
scope: GrantScope;
|
|
9
|
+
fileCapability?: FileCapability;
|
|
9
10
|
route: MdbaseConnectionRoute;
|
|
10
11
|
directAccess: DirectAccessStatus;
|
|
11
12
|
}
|
package/dist/connection.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import type { CollectionChange, CollectionChangesPage, CollectionDescription, CollectionOperation, CollectionTypeDocument, CreateViewSourceInput, DeleteViewSourceInput, DeleteViewSourceResult, ExecuteViewInput, GrantScope, JsonObject,
|
|
1
|
+
import type { CollectionChange, CollectionChangesPage, CollectionDescription, CollectionOperation, CollectionTypeDocument, CreateViewSourceInput, DeleteViewSourceInput, DeleteViewSourceResult, ExecuteViewInput, FileCapability, GrantScope, JsonObject, ReadViewSourceInput, RecordDocument, SavedViewExecution, SavedViewList, SavedViewSourceDocument, TypePackInstallResult, TypePackProvision, UpdateViewSourceInput } from "@mdbase-dev/connect-protocol";
|
|
2
2
|
import { type ConnectionTransportInternals } from "./connection-transport.js";
|
|
3
3
|
import type { DirectAccessStatus, MdbaseAuthorizationCapabilities, MdbaseConnectionInfo, MdbaseConnectionRoute, MdbaseSyncConnection } from "./connection-types.js";
|
|
4
4
|
import type { GrantKeyStore } from "./crypto.js";
|
|
5
|
+
import { MdbaseFileClient } from "./files.js";
|
|
5
6
|
import { type Application } from "./internal-types.js";
|
|
6
7
|
import type { MdbaseNativeNotificationRegistration, MdbaseNativeNotificationRegistrationOptions, MdbaseNotificationRegistration, MdbaseNotificationRegistrationOptions } from "./notifications.js";
|
|
7
8
|
import type { ChangesInput, CreateInput, CreateTypeInput, DeleteInput, DeletePreflightResult, DeleteProgressOptions, DeleteResult, MdbaseDesiredTimer, MdbaseTimer, MdbaseTimerList, MdbaseTimerReconciliation, OperationRequestOptions, PendingMutationSummary, QueryInput, QueryPage, QueryPagesOptions, QueryResult, ReadInput, ReadTypeInput, RenameInput, RenamePreflightResult, RenameProgressOptions, RenameResult, UpdateInput, UpdateTypeInput, WatchOptions } from "./operation-types.js";
|
|
9
|
+
import { type AuthorizationProblemCode, type CollectionChangesProblemCode, type CollectionDescriptionProblemCode, type CollectionMutationProblemCode, type CollectionQueryProblemCode, type CollectionReadProblemCode, type CollectionTypeProblemCode, type ConnectOutcome, type DirectAccessProblemCode, type NotificationProblemCode, type RegistrationProblemCode } from "./outcomes.js";
|
|
8
10
|
import type { MdbaseDeviceAuthorization } from "./authorization-types.js";
|
|
9
11
|
export type MdbaseAuthorizationTarget = {
|
|
10
12
|
kind: "choose";
|
|
@@ -62,27 +64,29 @@ export declare class MdbaseConnection<Frontmatter extends JsonObject = JsonObjec
|
|
|
62
64
|
private readonly collectionClient;
|
|
63
65
|
private readonly notifications;
|
|
64
66
|
private readonly transport;
|
|
67
|
+
readonly files: MdbaseFileClient;
|
|
65
68
|
private readonly connectionListeners;
|
|
66
69
|
constructor(internals: MdbaseConnectionInternals<Frontmatter>, collectionId: string);
|
|
67
70
|
get displayName(): string;
|
|
68
71
|
get operations(): CollectionOperation[];
|
|
69
|
-
get scope(): GrantScope;
|
|
72
|
+
get scope(): GrantScope | null;
|
|
73
|
+
get fileCapability(): FileCapability | null;
|
|
70
74
|
get directAccess(): DirectAccessStatus;
|
|
71
75
|
get route(): MdbaseConnectionRoute;
|
|
72
|
-
register(): Promise<Application
|
|
76
|
+
register(): Promise<ConnectOutcome<Application, RegistrationProblemCode>>;
|
|
73
77
|
info(): MdbaseConnectionInfo | null;
|
|
74
78
|
authorizationCapabilities(requiredOperations?: CollectionOperation[]): MdbaseAuthorizationCapabilities;
|
|
75
79
|
hasOperations(requiredOperations: CollectionOperation[]): boolean;
|
|
76
|
-
authorize(options?: MdbaseConnectionAuthorizeOptions): Promise<MdbaseAuthorizationOutcome<Frontmatter>>;
|
|
77
|
-
requestOperations(requiredOperations: CollectionOperation[], options?: Pick<MdbaseConnectionAuthorizeOptions, "returnTo">): Promise<MdbaseAuthorizationOutcome<Frontmatter> | {
|
|
80
|
+
authorize(options?: MdbaseConnectionAuthorizeOptions): Promise<ConnectOutcome<MdbaseAuthorizationOutcome<Frontmatter>, AuthorizationProblemCode>>;
|
|
81
|
+
requestOperations(requiredOperations: CollectionOperation[], options?: Pick<MdbaseConnectionAuthorizeOptions, "returnTo">): Promise<ConnectOutcome<MdbaseAuthorizationOutcome<Frontmatter> | {
|
|
78
82
|
kind: "unchanged";
|
|
79
83
|
connection: MdbaseConnection<Frontmatter>;
|
|
80
|
-
}
|
|
84
|
+
}, AuthorizationProblemCode>>;
|
|
81
85
|
onConnectionChange(listener: (connection: MdbaseConnectionInfo | null) => void): () => void;
|
|
82
86
|
notifyStorageChanged(): void;
|
|
83
|
-
checkDirectAccess(): Promise<DirectAccessStatus
|
|
87
|
+
checkDirectAccess(): Promise<ConnectOutcome<DirectAccessStatus, DirectAccessProblemCode>>;
|
|
84
88
|
/** Call from a user gesture to request browser permission for direct local access. */
|
|
85
|
-
requestDirectAccess(): Promise<DirectAccessStatus
|
|
89
|
+
requestDirectAccess(): Promise<ConnectOutcome<DirectAccessStatus, DirectAccessProblemCode>>;
|
|
86
90
|
disableDirectAccess(): void;
|
|
87
91
|
/**
|
|
88
92
|
* Return one offline-replication transport regardless of whether the
|
|
@@ -96,59 +100,59 @@ export declare class MdbaseConnection<Frontmatter extends JsonObject = JsonObjec
|
|
|
96
100
|
* Criteria are evaluated by the collection authority. Push payloads contain
|
|
97
101
|
* only an opaque cursor and static presentation copy.
|
|
98
102
|
*/
|
|
99
|
-
registerNotifications(options: MdbaseNotificationRegistrationOptions): Promise<MdbaseNotificationRegistration
|
|
100
|
-
registerNativeNotifications(options: MdbaseNativeNotificationRegistrationOptions): Promise<MdbaseNativeNotificationRegistration
|
|
101
|
-
unregisterNativeNotifications(): Promise<void
|
|
102
|
-
unregisterNotifications(serviceWorker?: ServiceWorkerRegistration): Promise<void
|
|
103
|
+
registerNotifications(options: MdbaseNotificationRegistrationOptions): Promise<ConnectOutcome<MdbaseNotificationRegistration, NotificationProblemCode>>;
|
|
104
|
+
registerNativeNotifications(options: MdbaseNativeNotificationRegistrationOptions): Promise<ConnectOutcome<MdbaseNativeNotificationRegistration, NotificationProblemCode>>;
|
|
105
|
+
unregisterNativeNotifications(): Promise<ConnectOutcome<void, NotificationProblemCode>>;
|
|
106
|
+
unregisterNotifications(serviceWorker?: ServiceWorkerRegistration): Promise<ConnectOutcome<void, NotificationProblemCode>>;
|
|
103
107
|
forget(): void;
|
|
104
|
-
describe(): Promise<CollectionDescription
|
|
105
|
-
changes(input?: ChangesInput, options?: OperationRequestOptions): Promise<CollectionChangesPage
|
|
106
|
-
read(input: ReadInput): Promise<
|
|
107
|
-
query(input?: QueryInput, options?: OperationRequestOptions): Promise<
|
|
108
|
-
queryPages(input?: QueryInput, options?: QueryPagesOptions<Frontmatter>): AsyncGenerator<QueryPage<Frontmatter>>;
|
|
109
|
-
queryAll(input?: QueryInput, options?: QueryPagesOptions<Frontmatter>): Promise<QueryResult<Frontmatter>>;
|
|
110
|
-
listViews(): Promise<
|
|
111
|
-
executeView(input: ExecuteViewInput): Promise<
|
|
112
|
-
readViewSource(input: ReadViewSourceInput): Promise<
|
|
113
|
-
createViewSource(input: CreateViewSourceInput): Promise<
|
|
114
|
-
updateViewSource(input: UpdateViewSourceInput): Promise<
|
|
115
|
-
deleteViewSource(input: DeleteViewSourceInput): Promise<
|
|
116
|
-
create(input: CreateInput<Frontmatter>): Promise<
|
|
117
|
-
update(input: UpdateInput<Frontmatter>): Promise<
|
|
118
|
-
delete(input: DeleteInput, options?: OperationRequestOptions): Promise<
|
|
119
|
-
preflightDelete(input: DeleteInput, options?: OperationRequestOptions): Promise<
|
|
120
|
-
rename(input: RenameInput, options?: OperationRequestOptions): Promise<
|
|
121
|
-
preflightRename(input: RenameInput, options?: OperationRequestOptions): Promise<
|
|
122
|
-
renameWithProgress(input: RenameInput, options?: RenameProgressOptions): Promise<
|
|
123
|
-
deleteWithProgress(input: DeleteInput, options?: DeleteProgressOptions): Promise<
|
|
108
|
+
describe(): Promise<ConnectOutcome<CollectionDescription, CollectionDescriptionProblemCode>>;
|
|
109
|
+
changes(input?: ChangesInput, options?: OperationRequestOptions): Promise<ConnectOutcome<CollectionChangesPage, CollectionChangesProblemCode>>;
|
|
110
|
+
read(input: ReadInput): Promise<ConnectOutcome<RecordDocument<Frontmatter>, CollectionReadProblemCode>>;
|
|
111
|
+
query(input?: QueryInput, options?: OperationRequestOptions): Promise<ConnectOutcome<QueryResult<Frontmatter>, CollectionQueryProblemCode>>;
|
|
112
|
+
queryPages(input?: QueryInput, options?: QueryPagesOptions<Frontmatter>): AsyncGenerator<ConnectOutcome<QueryPage<Frontmatter>, CollectionQueryProblemCode>>;
|
|
113
|
+
queryAll(input?: QueryInput, options?: QueryPagesOptions<Frontmatter>): Promise<ConnectOutcome<QueryResult<Frontmatter>, CollectionQueryProblemCode>>;
|
|
114
|
+
listViews(): Promise<ConnectOutcome<SavedViewList, CollectionReadProblemCode>>;
|
|
115
|
+
executeView(input: ExecuteViewInput): Promise<ConnectOutcome<SavedViewExecution<Frontmatter>, CollectionReadProblemCode>>;
|
|
116
|
+
readViewSource(input: ReadViewSourceInput): Promise<ConnectOutcome<SavedViewSourceDocument, CollectionReadProblemCode>>;
|
|
117
|
+
createViewSource(input: CreateViewSourceInput): Promise<ConnectOutcome<SavedViewSourceDocument, CollectionMutationProblemCode>>;
|
|
118
|
+
updateViewSource(input: UpdateViewSourceInput): Promise<ConnectOutcome<SavedViewSourceDocument, CollectionMutationProblemCode>>;
|
|
119
|
+
deleteViewSource(input: DeleteViewSourceInput): Promise<ConnectOutcome<DeleteViewSourceResult, CollectionMutationProblemCode>>;
|
|
120
|
+
create(input: CreateInput<Frontmatter>): Promise<ConnectOutcome<RecordDocument<Frontmatter>, CollectionMutationProblemCode>>;
|
|
121
|
+
update(input: UpdateInput<Frontmatter>): Promise<ConnectOutcome<RecordDocument<Frontmatter>, CollectionMutationProblemCode>>;
|
|
122
|
+
delete(input: DeleteInput, options?: OperationRequestOptions): Promise<ConnectOutcome<DeleteResult, CollectionMutationProblemCode>>;
|
|
123
|
+
preflightDelete(input: DeleteInput, options?: OperationRequestOptions): Promise<ConnectOutcome<DeletePreflightResult, CollectionMutationProblemCode>>;
|
|
124
|
+
rename(input: RenameInput, options?: OperationRequestOptions): Promise<ConnectOutcome<RenameResult, CollectionMutationProblemCode>>;
|
|
125
|
+
preflightRename(input: RenameInput, options?: OperationRequestOptions): Promise<ConnectOutcome<RenamePreflightResult, CollectionMutationProblemCode>>;
|
|
126
|
+
renameWithProgress(input: RenameInput, options?: RenameProgressOptions): Promise<ConnectOutcome<RenameResult, CollectionMutationProblemCode>>;
|
|
127
|
+
deleteWithProgress(input: DeleteInput, options?: DeleteProgressOptions): Promise<ConnectOutcome<DeleteResult, CollectionMutationProblemCode>>;
|
|
124
128
|
pendingMutation(): PendingMutationSummary | null;
|
|
125
|
-
resumePendingMutation<Result>(input: unknown, options?: OperationRequestOptions): Promise<Result
|
|
126
|
-
validate(input?: JsonObject): Promise<
|
|
127
|
-
readType(input: ReadTypeInput): Promise<
|
|
128
|
-
createType(input: CreateTypeInput): Promise<
|
|
129
|
-
updateType(input: UpdateTypeInput): Promise<
|
|
130
|
-
installTypePack(input: TypePackProvision): Promise<
|
|
131
|
-
listTimers(namespace: string): Promise<MdbaseTimerList
|
|
129
|
+
resumePendingMutation<Result>(input: unknown, options?: OperationRequestOptions): Promise<ConnectOutcome<Result, CollectionMutationProblemCode>>;
|
|
130
|
+
validate(input?: JsonObject): Promise<ConnectOutcome<JsonObject, CollectionReadProblemCode>>;
|
|
131
|
+
readType(input: ReadTypeInput): Promise<ConnectOutcome<CollectionTypeDocument, CollectionTypeProblemCode>>;
|
|
132
|
+
createType(input: CreateTypeInput): Promise<ConnectOutcome<CollectionTypeDocument, CollectionTypeProblemCode>>;
|
|
133
|
+
updateType(input: UpdateTypeInput): Promise<ConnectOutcome<CollectionTypeDocument, CollectionTypeProblemCode>>;
|
|
134
|
+
installTypePack(input: TypePackProvision): Promise<ConnectOutcome<TypePackInstallResult, CollectionTypeProblemCode>>;
|
|
135
|
+
listTimers(namespace: string): Promise<ConnectOutcome<MdbaseTimerList, CollectionReadProblemCode>>;
|
|
132
136
|
putTimer(input: {
|
|
133
137
|
namespace: string;
|
|
134
138
|
criterion_id: string;
|
|
135
139
|
timer: MdbaseDesiredTimer;
|
|
136
|
-
}): Promise<MdbaseTimer
|
|
140
|
+
}): Promise<ConnectOutcome<MdbaseTimer, CollectionMutationProblemCode>>;
|
|
137
141
|
cancelTimer(input: {
|
|
138
142
|
namespace: string;
|
|
139
143
|
id: string;
|
|
140
144
|
generation?: number;
|
|
141
|
-
}): Promise<{
|
|
145
|
+
}): Promise<ConnectOutcome<{
|
|
142
146
|
namespace: string;
|
|
143
147
|
id: string;
|
|
144
148
|
cancelled: boolean;
|
|
145
|
-
}
|
|
149
|
+
}, CollectionMutationProblemCode>>;
|
|
146
150
|
reconcileTimers(input: {
|
|
147
151
|
namespace: string;
|
|
148
152
|
criterion_id: string;
|
|
149
153
|
timers: MdbaseDesiredTimer[];
|
|
150
|
-
}): Promise<MdbaseTimerReconciliation
|
|
151
|
-
watch(options?: WatchOptions): AsyncGenerator<CollectionChange
|
|
152
|
-
operation<Result>(operation: CollectionOperation, input: unknown, options?: OperationRequestOptions): Promise<Result
|
|
154
|
+
}): Promise<ConnectOutcome<MdbaseTimerReconciliation, CollectionMutationProblemCode>>;
|
|
155
|
+
watch(options?: WatchOptions): AsyncGenerator<ConnectOutcome<CollectionChange, CollectionChangesProblemCode>>;
|
|
156
|
+
operation<Result>(operation: CollectionOperation, input: unknown, options?: OperationRequestOptions): Promise<ConnectOutcome<Result>>;
|
|
153
157
|
private emitConnection;
|
|
154
158
|
}
|