@mdbase-dev/connect 0.1.0-beta.30 → 0.1.0-beta.32

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 (64) hide show
  1. package/README.md +75 -19
  2. package/dist/advanced.d.ts +7 -0
  3. package/dist/advanced.js +4 -0
  4. package/dist/application-session.d.ts +10 -5
  5. package/dist/application-session.js +32 -16
  6. package/dist/async.js +11 -5
  7. package/dist/authority-proof.d.ts +3 -0
  8. package/dist/authority-proof.js +24 -0
  9. package/dist/browser/integrity.json +1 -1
  10. package/dist/browser/mdbase-connect.min.js +3 -3
  11. package/dist/browser/mdbase-connect.min.js.sha384 +1 -1
  12. package/dist/browser.d.ts +3 -0
  13. package/dist/browser.js +5 -0
  14. package/dist/capabilities.js +2 -1
  15. package/dist/collection-client.d.ts +28 -28
  16. package/dist/collection-client.js +42 -42
  17. package/dist/connect-options.d.ts +12 -4
  18. package/dist/connect.d.ts +12 -4
  19. package/dist/connect.js +105 -104
  20. package/dist/connection-file-transport.d.ts +2 -2
  21. package/dist/connection-file-transport.js +4 -4
  22. package/dist/connection-notifications.d.ts +10 -4
  23. package/dist/connection-notifications.js +41 -23
  24. package/dist/connection-transport.d.ts +19 -15
  25. package/dist/connection-transport.js +202 -202
  26. package/dist/connection-types.d.ts +5 -4
  27. package/dist/connection.d.ts +43 -43
  28. package/dist/connection.js +221 -77
  29. package/dist/crypto-entry.d.ts +4 -0
  30. package/dist/crypto-entry.js +4 -0
  31. package/dist/crypto.js +4 -4
  32. package/dist/external-store.d.ts +10 -0
  33. package/dist/external-store.js +10 -0
  34. package/dist/file-transfer-internals.d.ts +22 -0
  35. package/dist/file-transfer-internals.js +186 -0
  36. package/dist/files.d.ts +16 -12
  37. package/dist/files.js +52 -206
  38. package/dist/hosted-file-request.js +3 -3
  39. package/dist/index.d.ts +2 -5
  40. package/dist/index.js +1 -5
  41. package/dist/internal-types.d.ts +8 -1
  42. package/dist/local-file-transport.d.ts +2 -2
  43. package/dist/local-file-transport.js +13 -13
  44. package/dist/mdbase-connect.d.ts +6 -4
  45. package/dist/mdbase-connect.js +9 -8
  46. package/dist/notifications.d.ts +3 -2
  47. package/dist/operation-helpers.d.ts +3 -3
  48. package/dist/operation-helpers.js +12 -29
  49. package/dist/operation-types.d.ts +33 -8
  50. package/dist/outcomes.d.ts +15 -15
  51. package/dist/outcomes.js +13 -1
  52. package/dist/pending-mutation-store.d.ts +13 -0
  53. package/dist/pending-mutation-store.js +64 -0
  54. package/dist/request-budget.d.ts +27 -0
  55. package/dist/request-budget.js +95 -0
  56. package/dist/runtime-utils.d.ts +2 -0
  57. package/dist/runtime-utils.js +19 -0
  58. package/dist/session.d.ts +4 -3
  59. package/dist/session.js +4 -4
  60. package/dist/stored-token.d.ts +10 -0
  61. package/dist/stored-token.js +60 -0
  62. package/dist/token-response.d.ts +11 -0
  63. package/dist/token-response.js +77 -0
  64. package/package.json +12 -2
@@ -1,4 +1,5 @@
1
1
  import type { CollectionOperation, FileCapability, GrantScope, JsonObject, SyncChangesPage, SyncMutation, SyncMutationReceipt, SyncSession, SyncSnapshotPage } from "@mdbase-dev/connect-protocol";
2
+ import type { ConnectRequestOptions } from "./operation-types.js";
2
3
  export type MdbaseConnectionRoute = "remote" | "direct" | "relay";
3
4
  export type DirectAccessStatus = "disabled" | "permission_required" | "checking" | "available" | "unavailable" | "denied";
4
5
  export interface MdbaseConnectionInfo {
@@ -25,10 +26,10 @@ export interface MdbaseAuthorizationCapabilities {
25
26
  missingOperations: CollectionOperation[];
26
27
  }
27
28
  export interface MdbaseSyncTransport<Frontmatter extends JsonObject = JsonObject> {
28
- openSession(): Promise<SyncSession>;
29
- snapshot(snapshotId: string, page?: string): Promise<SyncSnapshotPage<Frontmatter>>;
30
- changes(after: number, limit?: number): Promise<SyncChangesPage<Frontmatter>>;
31
- mutate(mutation: SyncMutation): Promise<SyncMutationReceipt<Frontmatter>>;
29
+ openSession(options?: ConnectRequestOptions): Promise<SyncSession>;
30
+ snapshot(snapshotId: string, page?: string, options?: ConnectRequestOptions): Promise<SyncSnapshotPage<Frontmatter>>;
31
+ changes(after: number, limit?: number, options?: ConnectRequestOptions): Promise<SyncChangesPage<Frontmatter>>;
32
+ mutate(mutation: SyncMutation, options?: ConnectRequestOptions): Promise<SyncMutationReceipt<Frontmatter>>;
32
33
  }
33
34
  export interface MdbaseSyncConnection<Frontmatter extends JsonObject = JsonObject> {
34
35
  collectionId: string;
@@ -1,20 +1,21 @@
1
- import type { CollectionChange, CollectionChangesPage, CollectionDescription, CollectionOperation, CollectionTypeDocument, CreateViewSourceInput, DeleteViewSourceInput, DeleteViewSourceResult, ExecuteViewInput, FileCapability, GrantScope, JsonObject, ReadViewSourceInput, RecordDocument, SavedViewExecution, SavedViewList, SavedViewSourceDocument, ApplyTypePackInput, AssessTypePackInput, TypePackApplyResult, TypePackAssessment, UpdateViewSourceInput } from "@mdbase-dev/connect-protocol";
1
+ import type { CollectionChangesPage, CollectionDescription, CollectionOperation, CollectionTypeDocument, CreateViewSourceInput, DeleteViewSourceInput, DeleteViewSourceResult, ExecuteViewInput, FileCapability, GrantScope, JsonObject, ReadViewSourceInput, RecordDocument, SavedViewExecution, SavedViewList, SavedViewSourceDocument, ApplyTypePackInput, AssessTypePackInput, TypePackApplyResult, TypePackAssessment, 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
5
  import { MdbaseFileClient } from "./files.js";
6
6
  import { type Application } from "./internal-types.js";
7
7
  import type { MdbaseNativeNotificationRegistration, MdbaseNativeNotificationRegistrationOptions, MdbaseNotificationRegistration, MdbaseNotificationRegistrationOptions } from "./notifications.js";
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";
8
+ import type { ChangesInput, CreateInput, CreateTypeInput, DeleteInput, DeletePreflightResult, DeleteProgressOptions, DeleteResult, MdbaseDesiredTimer, MdbaseTimer, MdbaseTimerList, MdbaseTimerReconciliation, ConnectRequestOptions, PendingMutation, QueryInput, QueryPage, QueryPagesOptions, QueryResult, ReadInput, ReadTypeInput, RenameInput, RenamePreflightResult, RenameProgressOptions, RenameResult, UpdateInput, UpdateTypeInput, MdbaseWatchSubscription, WatchInput } from "./operation-types.js";
9
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";
10
10
  import type { MdbaseDeviceAuthorization } from "./authorization-types.js";
11
+ import { type ResolvedConnectTimeouts } from "./request-budget.js";
11
12
  export type MdbaseAuthorizationTarget = {
12
13
  kind: "choose";
13
14
  } | {
14
15
  kind: "collection";
15
16
  collectionId: string;
16
17
  };
17
- export interface MdbaseAuthorizeOptions {
18
+ export interface MdbaseAuthorizeOptions extends ConnectRequestOptions {
18
19
  operations?: CollectionOperation[];
19
20
  /** Choose any compatible collection, or require one exact collection. */
20
21
  target?: MdbaseAuthorizationTarget;
@@ -24,15 +25,12 @@ export interface MdbaseAuthorizeOptions {
24
25
  onDeviceCode?: (authorization: MdbaseDeviceAuthorization) => void;
25
26
  /** Replace the default popup for a downloaded application's approval page. */
26
27
  openVerification?: (authorization: MdbaseDeviceAuthorization) => void | Promise<void>;
27
- /** Stop polling and discard the unapproved, in-memory key. */
28
- signal?: AbortSignal;
29
28
  }
30
- export interface MdbaseConnectionAuthorizeOptions {
29
+ export interface MdbaseConnectionAuthorizeOptions extends ConnectRequestOptions {
31
30
  operations?: CollectionOperation[];
32
31
  returnTo?: string;
33
32
  onDeviceCode?: (authorization: MdbaseDeviceAuthorization) => void;
34
33
  openVerification?: (authorization: MdbaseDeviceAuthorization) => void | Promise<void>;
35
- signal?: AbortSignal;
36
34
  }
37
35
  export interface MdbaseConnectEnvironment {
38
36
  distribution: "web" | "portable";
@@ -54,7 +52,8 @@ export interface MdbaseConnectionInternals<Frontmatter extends JsonObject> exten
54
52
  readonly keyStore: GrantKeyStore;
55
53
  readonly directAccessMode: "auto" | "disabled";
56
54
  readonly loopbackUrl: string;
57
- register(): Promise<Application>;
55
+ readonly timeouts: ResolvedConnectTimeouts;
56
+ register(options?: ConnectRequestOptions): Promise<Application>;
58
57
  authorize(options?: MdbaseAuthorizeOptions): Promise<MdbaseAuthorizationOutcome<Frontmatter>>;
59
58
  notificationKey(collectionId: string, transport?: "web_push" | "fcm"): string;
60
59
  }
@@ -73,7 +72,7 @@ export declare class MdbaseConnection<Frontmatter extends JsonObject = JsonObjec
73
72
  get fileCapability(): FileCapability | null;
74
73
  get directAccess(): DirectAccessStatus;
75
74
  get route(): MdbaseConnectionRoute;
76
- register(): Promise<ConnectOutcome<Application, RegistrationProblemCode>>;
75
+ register(options?: ConnectRequestOptions): Promise<ConnectOutcome<Application, RegistrationProblemCode>>;
77
76
  info(): MdbaseConnectionInfo | null;
78
77
  authorizationCapabilities(requiredOperations?: CollectionOperation[]): MdbaseAuthorizationCapabilities;
79
78
  hasOperations(requiredOperations: CollectionOperation[]): boolean;
@@ -84,9 +83,9 @@ export declare class MdbaseConnection<Frontmatter extends JsonObject = JsonObjec
84
83
  }, AuthorizationProblemCode>>;
85
84
  onConnectionChange(listener: (connection: MdbaseConnectionInfo | null) => void): () => void;
86
85
  notifyStorageChanged(): void;
87
- checkDirectAccess(): Promise<ConnectOutcome<DirectAccessStatus, DirectAccessProblemCode>>;
86
+ checkDirectAccess(options?: ConnectRequestOptions): Promise<ConnectOutcome<DirectAccessStatus, DirectAccessProblemCode>>;
88
87
  /** Call from a user gesture to request browser permission for direct local access. */
89
- requestDirectAccess(): Promise<ConnectOutcome<DirectAccessStatus, DirectAccessProblemCode>>;
88
+ requestDirectAccess(options?: ConnectRequestOptions): Promise<ConnectOutcome<DirectAccessStatus, DirectAccessProblemCode>>;
90
89
  disableDirectAccess(): void;
91
90
  /**
92
91
  * Return one offline-replication transport regardless of whether the
@@ -102,48 +101,49 @@ export declare class MdbaseConnection<Frontmatter extends JsonObject = JsonObjec
102
101
  */
103
102
  registerNotifications(options: MdbaseNotificationRegistrationOptions): Promise<ConnectOutcome<MdbaseNotificationRegistration, NotificationProblemCode>>;
104
103
  registerNativeNotifications(options: MdbaseNativeNotificationRegistrationOptions): Promise<ConnectOutcome<MdbaseNativeNotificationRegistration, NotificationProblemCode>>;
105
- unregisterNativeNotifications(): Promise<ConnectOutcome<void, NotificationProblemCode>>;
106
- unregisterNotifications(serviceWorker?: ServiceWorkerRegistration): Promise<ConnectOutcome<void, NotificationProblemCode>>;
104
+ unregisterNativeNotifications(options?: ConnectRequestOptions): Promise<ConnectOutcome<void, NotificationProblemCode>>;
105
+ unregisterNotifications(serviceWorker?: ServiceWorkerRegistration, options?: ConnectRequestOptions): Promise<ConnectOutcome<void, NotificationProblemCode>>;
107
106
  forget(): void;
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>>;
107
+ describe(options?: ConnectRequestOptions): Promise<ConnectOutcome<CollectionDescription, CollectionDescriptionProblemCode>>;
108
+ changes(input?: ChangesInput, options?: ConnectRequestOptions): Promise<ConnectOutcome<CollectionChangesPage, CollectionChangesProblemCode>>;
109
+ read(input: ReadInput, options?: ConnectRequestOptions): Promise<ConnectOutcome<RecordDocument<Frontmatter>, CollectionReadProblemCode>>;
110
+ query(input?: QueryInput, options?: ConnectRequestOptions): Promise<ConnectOutcome<QueryResult<Frontmatter>, CollectionQueryProblemCode>>;
112
111
  queryPages(input?: QueryInput, options?: QueryPagesOptions<Frontmatter>): AsyncGenerator<ConnectOutcome<QueryPage<Frontmatter>, CollectionQueryProblemCode>>;
113
112
  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>>;
113
+ listViews(options?: ConnectRequestOptions): Promise<ConnectOutcome<SavedViewList, CollectionReadProblemCode>>;
114
+ executeView(input: ExecuteViewInput, options?: ConnectRequestOptions): Promise<ConnectOutcome<SavedViewExecution<Frontmatter>, CollectionReadProblemCode>>;
115
+ readViewSource(input: ReadViewSourceInput, options?: ConnectRequestOptions): Promise<ConnectOutcome<SavedViewSourceDocument, CollectionReadProblemCode>>;
116
+ createViewSource(input: CreateViewSourceInput, options?: ConnectRequestOptions): Promise<ConnectOutcome<SavedViewSourceDocument, CollectionMutationProblemCode>>;
117
+ updateViewSource(input: UpdateViewSourceInput, options?: ConnectRequestOptions): Promise<ConnectOutcome<SavedViewSourceDocument, CollectionMutationProblemCode>>;
118
+ deleteViewSource(input: DeleteViewSourceInput, options?: ConnectRequestOptions): Promise<ConnectOutcome<DeleteViewSourceResult, CollectionMutationProblemCode>>;
119
+ create(input: CreateInput<Frontmatter>, options?: ConnectRequestOptions): Promise<ConnectOutcome<RecordDocument<Frontmatter>, CollectionMutationProblemCode>>;
120
+ update(input: UpdateInput<Frontmatter>, options?: ConnectRequestOptions): Promise<ConnectOutcome<RecordDocument<Frontmatter>, CollectionMutationProblemCode>>;
121
+ delete(input: DeleteInput, options?: ConnectRequestOptions): Promise<ConnectOutcome<DeleteResult, CollectionMutationProblemCode>>;
122
+ preflightDelete(input: DeleteInput, options?: ConnectRequestOptions): Promise<ConnectOutcome<DeletePreflightResult, CollectionMutationProblemCode>>;
123
+ rename(input: RenameInput, options?: ConnectRequestOptions): Promise<ConnectOutcome<RenameResult, CollectionMutationProblemCode>>;
124
+ preflightRename(input: RenameInput, options?: ConnectRequestOptions): Promise<ConnectOutcome<RenamePreflightResult, CollectionMutationProblemCode>>;
126
125
  renameWithProgress(input: RenameInput, options?: RenameProgressOptions): Promise<ConnectOutcome<RenameResult, CollectionMutationProblemCode>>;
127
126
  deleteWithProgress(input: DeleteInput, options?: DeleteProgressOptions): Promise<ConnectOutcome<DeleteResult, CollectionMutationProblemCode>>;
128
- pendingMutation(): PendingMutationSummary | null;
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
- assessTypePack(input: AssessTypePackInput): Promise<ConnectOutcome<TypePackAssessment, CollectionTypeProblemCode>>;
135
- applyTypePack(input: ApplyTypePackInput): Promise<ConnectOutcome<TypePackApplyResult, CollectionTypeProblemCode>>;
136
- listTimers(namespace: string): Promise<ConnectOutcome<MdbaseTimerList, CollectionReadProblemCode>>;
127
+ pendingMutations<Result = unknown>(): readonly PendingMutation<Result>[];
128
+ pendingMutation<Result = unknown>(requestId: string): PendingMutation<Result> | null;
129
+ private recoverPendingMutation;
130
+ validate(input?: JsonObject, options?: ConnectRequestOptions): Promise<ConnectOutcome<JsonObject, CollectionReadProblemCode>>;
131
+ readType(input: ReadTypeInput, options?: ConnectRequestOptions): Promise<ConnectOutcome<CollectionTypeDocument, CollectionTypeProblemCode>>;
132
+ createType(input: CreateTypeInput, options?: ConnectRequestOptions): Promise<ConnectOutcome<CollectionTypeDocument, CollectionTypeProblemCode>>;
133
+ updateType(input: UpdateTypeInput, options?: ConnectRequestOptions): Promise<ConnectOutcome<CollectionTypeDocument, CollectionTypeProblemCode>>;
134
+ assessTypePack(input: AssessTypePackInput, options?: ConnectRequestOptions): Promise<ConnectOutcome<TypePackAssessment, CollectionTypeProblemCode>>;
135
+ applyTypePack(input: ApplyTypePackInput, options?: ConnectRequestOptions): Promise<ConnectOutcome<TypePackApplyResult, CollectionTypeProblemCode>>;
136
+ listTimers(namespace: string, options?: ConnectRequestOptions): Promise<ConnectOutcome<MdbaseTimerList, CollectionReadProblemCode>>;
137
137
  putTimer(input: {
138
138
  namespace: string;
139
139
  criterion_id: string;
140
140
  timer: MdbaseDesiredTimer;
141
- }): Promise<ConnectOutcome<MdbaseTimer, CollectionMutationProblemCode>>;
141
+ }, options?: ConnectRequestOptions): Promise<ConnectOutcome<MdbaseTimer, CollectionMutationProblemCode>>;
142
142
  cancelTimer(input: {
143
143
  namespace: string;
144
144
  id: string;
145
145
  generation?: number;
146
- }): Promise<ConnectOutcome<{
146
+ }, options?: ConnectRequestOptions): Promise<ConnectOutcome<{
147
147
  namespace: string;
148
148
  id: string;
149
149
  cancelled: boolean;
@@ -152,8 +152,8 @@ export declare class MdbaseConnection<Frontmatter extends JsonObject = JsonObjec
152
152
  namespace: string;
153
153
  criterion_id: string;
154
154
  timers: MdbaseDesiredTimer[];
155
- }): Promise<ConnectOutcome<MdbaseTimerReconciliation, CollectionMutationProblemCode>>;
156
- watch(options?: WatchOptions): AsyncGenerator<ConnectOutcome<CollectionChange, CollectionChangesProblemCode>>;
157
- operation<Result>(operation: CollectionOperation, input: unknown, options?: OperationRequestOptions): Promise<ConnectOutcome<Result>>;
155
+ }, options?: ConnectRequestOptions): Promise<ConnectOutcome<MdbaseTimerReconciliation, CollectionMutationProblemCode>>;
156
+ watch(input?: WatchInput, options?: ConnectRequestOptions): Promise<ConnectOutcome<MdbaseWatchSubscription, CollectionChangesProblemCode>>;
157
+ operation<Result>(operation: CollectionOperation, input: unknown, options?: ConnectRequestOptions): Promise<ConnectOutcome<Result>>;
158
158
  private emitConnection;
159
159
  }
@@ -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 { 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
  });
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);
@@ -265,7 +268,7 @@ export class MdbaseConnection {
265
268
  }
266
269
  async renameWithProgress(input, options = {}) {
267
270
  const started = Date.now();
268
- const resumed = this.pendingMutation()?.operation === "rename";
271
+ const resumed = false;
269
272
  let estimate;
270
273
  const emit = (state, cancellable, completedUnits = 0) => {
271
274
  options.onProgress?.({
@@ -317,7 +320,7 @@ export class MdbaseConnection {
317
320
  }
318
321
  async deleteWithProgress(input, options = {}) {
319
322
  const started = Date.now();
320
- const resumed = this.pendingMutation()?.operation === "delete";
323
+ const resumed = false;
321
324
  let estimate;
322
325
  const emit = (state, cancellable, completedUnits = 0) => {
323
326
  options.onProgress?.({
@@ -367,44 +370,71 @@ export class MdbaseConnection {
367
370
  throw error;
368
371
  }
369
372
  }
370
- pendingMutation() {
371
- return this.transport.pendingMutation();
373
+ pendingMutations() {
374
+ return this.transport.pendingMutations().map((summary) => ({
375
+ ...summary,
376
+ recover: (options) => captureConnectOutcome(() => this.recoverPendingMutation(summary.requestId, options), COLLECTION_MUTATION_PROBLEM_CODES)
377
+ }));
372
378
  }
373
- resumePendingMutation(input, options) {
374
- return captureConnectOutcome(() => this.transport.resumePendingMutation(input, options), COLLECTION_MUTATION_PROBLEM_CODES);
379
+ pendingMutation(requestId) {
380
+ const summary = this.transport.pendingMutation(requestId);
381
+ if (!summary)
382
+ return null;
383
+ return {
384
+ ...summary,
385
+ recover: (options) => captureConnectOutcome(() => this.recoverPendingMutation(requestId, options), COLLECTION_MUTATION_PROBLEM_CODES)
386
+ };
375
387
  }
376
- validate(input = {}) {
377
- return this.collectionClient.validate(input);
388
+ async recoverPendingMutation(requestId, options) {
389
+ const result = await this.transport.recoverPendingMutation(requestId, options);
390
+ if (result && typeof result === "object" && "valid" in result) {
391
+ const envelope = result;
392
+ if (!envelope.valid)
393
+ throw new MdbaseConnectError(operationProblem(envelope));
394
+ return envelope.result;
395
+ }
396
+ return result;
397
+ }
398
+ validate(input = {}, options) {
399
+ return this.collectionClient.validate(input, options);
378
400
  }
379
- readType(input) {
380
- return this.collectionClient.readType(input);
401
+ readType(input, options) {
402
+ return this.collectionClient.readType(input, options);
381
403
  }
382
- createType(input) {
383
- return this.collectionClient.createType(input);
404
+ createType(input, options) {
405
+ return this.collectionClient.createType(input, options);
384
406
  }
385
- updateType(input) {
386
- return this.collectionClient.updateType(input);
407
+ updateType(input, options) {
408
+ return this.collectionClient.updateType(input, options);
387
409
  }
388
- assessTypePack(input) {
389
- return this.collectionClient.assessTypePack(input);
410
+ assessTypePack(input, options) {
411
+ return this.collectionClient.assessTypePack(input, options);
390
412
  }
391
- applyTypePack(input) {
392
- return this.collectionClient.applyTypePack(input);
413
+ applyTypePack(input, options) {
414
+ return this.collectionClient.applyTypePack(input, options);
393
415
  }
394
- listTimers(namespace) {
395
- return this.collectionClient.listTimers(namespace);
416
+ listTimers(namespace, options) {
417
+ return this.collectionClient.listTimers(namespace, options);
396
418
  }
397
- putTimer(input) {
398
- return this.collectionClient.putTimer(input);
419
+ putTimer(input, options) {
420
+ return this.collectionClient.putTimer(input, options);
399
421
  }
400
- cancelTimer(input) {
401
- return this.collectionClient.cancelTimer(input);
422
+ cancelTimer(input, options) {
423
+ return this.collectionClient.cancelTimer(input, options);
402
424
  }
403
- reconcileTimers(input) {
404
- return this.collectionClient.reconcileTimers(input);
425
+ reconcileTimers(input, options) {
426
+ return this.collectionClient.reconcileTimers(input, options);
405
427
  }
406
- watch(options = {}) {
407
- return this.collectionClient.watch(options);
428
+ watch(input = {}, options = {}) {
429
+ return captureConnectOutcome(() => withRequestBudget(options, this.internals.timeouts.watchStartMs, async (budget) => {
430
+ const initial = await this.collectionClient.changes(input.cursor === undefined ? {} : { after: input.cursor, limit: 200 }, { signal: budget.signal, timeoutMs: null });
431
+ if (!initial.ok)
432
+ throw new MdbaseConnectError(initial.problem);
433
+ if (initial.value.reset) {
434
+ throw new MdbaseConnectError(connectProblem("change_cursor_reset", "The collection change cursor expired. Refresh collection state before subscribing again."));
435
+ }
436
+ return new CollectionWatchSubscription(this.collectionClient, initial.value.cursor, input, input.cursor === undefined ? [] : initial.value.events, this.internals.timeouts.watchStartMs);
437
+ }), COLLECTION_CHANGES_PROBLEM_CODES);
408
438
  }
409
439
  operation(operation, input, options) {
410
440
  return this.collectionClient.operation(operation, input, options);
@@ -415,3 +445,117 @@ export class MdbaseConnection {
415
445
  listener(connection);
416
446
  }
417
447
  }
448
+ class CollectionWatchSubscription {
449
+ client;
450
+ input;
451
+ watchStartTimeoutMs;
452
+ changes = new Set();
453
+ statuses = new Set();
454
+ problems = new Set();
455
+ controller = new AbortController();
456
+ removeLifetimeAbort;
457
+ currentStatus;
458
+ currentProblem = null;
459
+ pendingChanges;
460
+ constructor(client, cursor, input, pendingChanges, watchStartTimeoutMs) {
461
+ this.client = client;
462
+ this.input = input;
463
+ this.watchStartTimeoutMs = watchStartTimeoutMs;
464
+ this.pendingChanges = [...pendingChanges];
465
+ this.currentStatus = { state: "connected", cursor, recovered: false };
466
+ const lifetimeSignal = input.lifetimeSignal;
467
+ if (lifetimeSignal?.aborted)
468
+ this.close();
469
+ else if (lifetimeSignal) {
470
+ const close = () => this.close();
471
+ lifetimeSignal.addEventListener("abort", close, { once: true });
472
+ this.removeLifetimeAbort = () => lifetimeSignal.removeEventListener("abort", close);
473
+ }
474
+ if (!this.controller.signal.aborted)
475
+ void this.run(cursor);
476
+ }
477
+ get status() {
478
+ return this.currentStatus;
479
+ }
480
+ get problem() {
481
+ return this.currentProblem;
482
+ }
483
+ subscribe(listener, onStatus, onProblem) {
484
+ this.changes.add(listener);
485
+ if (onStatus) {
486
+ this.statuses.add(onStatus);
487
+ onStatus(this.currentStatus);
488
+ }
489
+ if (onProblem) {
490
+ this.problems.add(onProblem);
491
+ if (this.currentProblem)
492
+ onProblem(this.currentProblem);
493
+ }
494
+ for (const change of this.pendingChanges)
495
+ listener(change);
496
+ this.pendingChanges = [];
497
+ return () => {
498
+ this.changes.delete(listener);
499
+ if (onStatus)
500
+ this.statuses.delete(onStatus);
501
+ if (onProblem)
502
+ this.problems.delete(onProblem);
503
+ };
504
+ }
505
+ close() {
506
+ if (this.controller.signal.aborted)
507
+ return;
508
+ this.controller.abort();
509
+ this.removeLifetimeAbort?.();
510
+ this.removeLifetimeAbort = undefined;
511
+ const cursor = "cursor" in this.currentStatus ? this.currentStatus.cursor : undefined;
512
+ this.publishStatus({ state: "closed", ...(cursor === undefined ? {} : { cursor }) });
513
+ }
514
+ async run(cursor) {
515
+ let firstStatus = true;
516
+ const iterator = this.client.watch({
517
+ cursor,
518
+ pollIntervalMs: this.input.pollIntervalMs,
519
+ retry: this.input.retry,
520
+ signal: this.controller.signal,
521
+ timeoutMs: this.watchStartTimeoutMs,
522
+ onStatus: (status) => {
523
+ if (firstStatus && status.state === "connecting") {
524
+ firstStatus = false;
525
+ return;
526
+ }
527
+ firstStatus = false;
528
+ this.publishStatus(status);
529
+ }
530
+ });
531
+ try {
532
+ for await (const outcome of iterator) {
533
+ if (this.controller.signal.aborted)
534
+ return;
535
+ if (!outcome.ok) {
536
+ this.currentProblem = outcome.problem;
537
+ for (const listener of this.problems)
538
+ listener(outcome.problem);
539
+ return;
540
+ }
541
+ for (const listener of this.changes)
542
+ listener(outcome.value);
543
+ }
544
+ }
545
+ finally {
546
+ if (!this.controller.signal.aborted)
547
+ this.close();
548
+ }
549
+ }
550
+ publishStatus(status) {
551
+ this.currentStatus = status;
552
+ for (const listener of this.statuses)
553
+ listener(status);
554
+ }
555
+ }
556
+ function syncRequestOptions(options, defaultTimeoutMs) {
557
+ return {
558
+ ...options,
559
+ timeoutMs: options?.timeoutMs === undefined ? defaultTimeoutMs : options.timeoutMs
560
+ };
561
+ }
@@ -0,0 +1,4 @@
1
+ /** Opt-in cryptographic storage and transport primitives. */
2
+ export * from "./crypto.js";
3
+ export * from "./file-crypto.js";
4
+ export * from "./application-identity.js";
@@ -0,0 +1,4 @@
1
+ /** Opt-in cryptographic storage and transport primitives. */
2
+ export * from "./crypto.js";
3
+ export * from "./file-crypto.js";
4
+ export * from "./application-identity.js";