@mdbase-dev/connect 0.1.0-beta.21 → 0.1.0-beta.22

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.
Files changed (49) hide show
  1. package/README.md +108 -38
  2. package/dist/authorization-url.d.ts +2 -1
  3. package/dist/authorization-url.js +5 -7
  4. package/dist/browser/integrity.json +1 -1
  5. package/dist/browser/mdbase-connect.min.js +3 -3
  6. package/dist/browser/mdbase-connect.min.js.sha384 +1 -1
  7. package/dist/collection-client.d.ts +34 -31
  8. package/dist/collection-client.js +107 -61
  9. package/dist/connect.d.ts +4 -3
  10. package/dist/connect.js +53 -38
  11. package/dist/connection-file-transport.d.ts +31 -0
  12. package/dist/connection-file-transport.js +42 -0
  13. package/dist/connection-notifications.js +21 -21
  14. package/dist/connection-transport.d.ts +2 -0
  15. package/dist/connection-transport.js +95 -37
  16. package/dist/connection-types.d.ts +2 -1
  17. package/dist/connection.d.ts +49 -45
  18. package/dist/connection.js +73 -33
  19. package/dist/crypto.d.ts +5 -6
  20. package/dist/crypto.js +13 -13
  21. package/dist/errors.d.ts +38 -24
  22. package/dist/errors.js +130 -71
  23. package/dist/file-crypto.d.ts +25 -0
  24. package/dist/file-crypto.js +132 -0
  25. package/dist/file-sha256.d.ts +12 -0
  26. package/dist/file-sha256.js +143 -0
  27. package/dist/file-stream-source.d.ts +17 -0
  28. package/dist/file-stream-source.js +157 -0
  29. package/dist/files.d.ts +91 -0
  30. package/dist/files.js +644 -0
  31. package/dist/hosted-file-request.d.ts +6 -0
  32. package/dist/hosted-file-request.js +63 -0
  33. package/dist/index.d.ts +4 -1
  34. package/dist/index.js +3 -0
  35. package/dist/internal-types.d.ts +3 -1
  36. package/dist/local-file-transport.d.ts +30 -0
  37. package/dist/local-file-transport.js +367 -0
  38. package/dist/notifications.js +5 -5
  39. package/dist/operation-helpers.d.ts +2 -2
  40. package/dist/operation-helpers.js +22 -18
  41. package/dist/operation-types.d.ts +3 -4
  42. package/dist/outcomes.d.ts +51 -0
  43. package/dist/outcomes.js +168 -0
  44. package/dist/runtime-utils.d.ts +6 -1
  45. package/dist/runtime-utils.js +90 -11
  46. package/dist/selection.js +3 -3
  47. package/dist/session.d.ts +9 -8
  48. package/dist/session.js +31 -20
  49. 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 { directFallbackStatus, isMutation, localNetworkPermission, loopbackRequest, operationFingerprint, operationTransportError, sameAuthorization, throwIfCancelled, uncertainDirectMutation } from "./operation-helpers.js";
4
- import { apiError, parseGrantScope, parseStored, validStoredAuthority, validStoredEncryption } from "./runtime-utils.js";
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 new MdbaseConnectError("no_pending_mutation", "There is no interrupted mutation to resume.", { recovery: "none" });
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 new MdbaseConnectError("not_authorized", "Connect this application before accessing a collection.");
112
+ throw connectError("not_authorized", "Connect this application before accessing a collection.");
95
113
  if (!token.operations.includes(operation)) {
96
- throw new MdbaseConnectError("insufficient_access", `This connection does not allow ${operation}.`, {
97
- requiresAuthorization: true,
98
- recovery: "reauthorize",
114
+ throw connectError("insufficient_access", `This connection does not allow ${operation}.`, {
99
115
  details: {
100
- requiredOperations: [operation],
101
- grantedOperations: [...token.operations],
102
- missingOperations: [operation]
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 new MdbaseConnectError("not_authorized", "Reconnect this application to continue.");
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 new MdbaseConnectError("direct_outcome_unknown", "The direct operation may have completed, but its encrypted grant changed before the response could be recovered. Refresh before making another change.");
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
- const body = await response.json();
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 uncertainDirectMutation(error);
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
- throw new MdbaseConnectError("invalid_encrypted_response", "The relay did not return an encrypted connector response.");
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 new MdbaseConnectError(decrypted.error.code, decrypted.error.message);
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
- throw new MdbaseConnectError(error.code, error.message);
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
- throw new MdbaseConnectError("invalid_operation_response", "The collection authority returned a response for a different protocol request.");
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 new MdbaseConnectError("authority_authorization_changed", "Reconnect this collection authority before synchronizing.");
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 new MdbaseConnectError("authority_authorization_changed", "Reconnect this collection authority before synchronizing.");
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 new MdbaseConnectError("not_remote_authority", "This authorization has no remote authority endpoint.");
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 new MdbaseConnectError("pending_mutation_unresolved", "A previous write still has an unknown outcome. Retry that exact write before making another change.");
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 new MdbaseConnectError("missing_grant_key", "Reconnect this application to restore encrypted access.");
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 new MdbaseConnectError("pending_mutation_unresolved", "The pending write belongs to a different transport. Reconnect before retrying it.");
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 new MdbaseConnectError(error.code, error.message);
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 uncertainDirectMutation(error);
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 uncertainDirectMutation(error);
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 new MdbaseConnectError("relay_authorization_expired", "Direct access is still available on this computer, but using the relay requires reconnecting this application.");
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 new MdbaseConnectError("not_authorized", "Reconnect this application to continue.");
640
+ throw connectError("not_authorized", "Reconnect this application to continue.");
586
641
  }
587
642
  if ((current.refreshExpiresAt ?? 0) <= Date.now()) {
588
- throw new MdbaseConnectError("relay_authorization_expired", "Direct access is still available on this computer, but using the relay requires reconnecting this application.");
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 new MdbaseConnectError("missing_grant_key", "Reconnect this application to restore remote authority request signing.");
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 new MdbaseConnectError(error.code, error.message);
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 new MdbaseConnectError("collection_mismatch", "The refreshed authorization belongs to a different collection.");
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
  }
@@ -1,10 +1,12 @@
1
- import type { CollectionChange, CollectionChangesPage, CollectionDescription, CollectionOperation, CollectionTypeDocument, CreateViewSourceInput, DeleteViewSourceInput, DeleteViewSourceResult, ExecuteViewInput, GrantScope, JsonObject, MdbaseOperationEnvelope, ReadViewSourceInput, RecordDocument, SavedViewExecution, SavedViewList, SavedViewSourceDocument, TypePackInstallResult, TypePackProvision, UpdateViewSourceInput } from "@mdbase-dev/connect-protocol";
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<MdbaseOperationEnvelope<RecordDocument<Frontmatter>>>;
107
- query(input?: QueryInput, options?: OperationRequestOptions): Promise<MdbaseOperationEnvelope<QueryResult<Frontmatter>>>;
108
- queryPages(input?: QueryInput, options?: QueryPagesOptions<Frontmatter>): AsyncGenerator<QueryPage<Frontmatter>>;
109
- queryAll(input?: QueryInput, options?: QueryPagesOptions<Frontmatter>): Promise<QueryResult<Frontmatter>>;
110
- listViews(): Promise<MdbaseOperationEnvelope<SavedViewList>>;
111
- executeView(input: ExecuteViewInput): Promise<MdbaseOperationEnvelope<SavedViewExecution<Frontmatter>>>;
112
- readViewSource(input: ReadViewSourceInput): Promise<MdbaseOperationEnvelope<SavedViewSourceDocument>>;
113
- createViewSource(input: CreateViewSourceInput): Promise<MdbaseOperationEnvelope<SavedViewSourceDocument>>;
114
- updateViewSource(input: UpdateViewSourceInput): Promise<MdbaseOperationEnvelope<SavedViewSourceDocument>>;
115
- deleteViewSource(input: DeleteViewSourceInput): Promise<MdbaseOperationEnvelope<DeleteViewSourceResult>>;
116
- create(input: CreateInput<Frontmatter>): Promise<MdbaseOperationEnvelope<RecordDocument<Frontmatter>>>;
117
- update(input: UpdateInput<Frontmatter>): Promise<MdbaseOperationEnvelope<RecordDocument<Frontmatter>>>;
118
- delete(input: DeleteInput, options?: OperationRequestOptions): Promise<MdbaseOperationEnvelope<DeleteResult>>;
119
- preflightDelete(input: DeleteInput, options?: OperationRequestOptions): Promise<MdbaseOperationEnvelope<DeletePreflightResult>>;
120
- rename(input: RenameInput, options?: OperationRequestOptions): Promise<MdbaseOperationEnvelope<RenameResult>>;
121
- preflightRename(input: RenameInput, options?: OperationRequestOptions): Promise<MdbaseOperationEnvelope<RenamePreflightResult>>;
122
- renameWithProgress(input: RenameInput, options?: RenameProgressOptions): Promise<MdbaseOperationEnvelope<RenameResult>>;
123
- deleteWithProgress(input: DeleteInput, options?: DeleteProgressOptions): Promise<MdbaseOperationEnvelope<DeleteResult>>;
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<MdbaseOperationEnvelope>;
127
- readType(input: ReadTypeInput): Promise<MdbaseOperationEnvelope<CollectionTypeDocument>>;
128
- createType(input: CreateTypeInput): Promise<MdbaseOperationEnvelope<CollectionTypeDocument>>;
129
- updateType(input: UpdateTypeInput): Promise<MdbaseOperationEnvelope<CollectionTypeDocument>>;
130
- installTypePack(input: TypePackProvision): Promise<MdbaseOperationEnvelope<TypePackInstallResult>>;
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
  }