@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.
- 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,5 +1,4 @@
|
|
|
1
|
-
import type { CollectionOperation, JsonObject, QueryRecord, RecordDocument } from "@mdbase-dev/connect-protocol";
|
|
2
|
-
import type { MdbaseConnectError } from "./errors.js";
|
|
1
|
+
import type { CollectionOperation, ConnectProblem, JsonObject, QueryRecord, RecordDocument } from "@mdbase-dev/connect-protocol";
|
|
3
2
|
export interface MdbaseDesiredTimer {
|
|
4
3
|
/** Stable within the timer namespace. */
|
|
5
4
|
id: string;
|
|
@@ -241,11 +240,11 @@ export type WatchStatus = {
|
|
|
241
240
|
cursor?: number;
|
|
242
241
|
attempt: number;
|
|
243
242
|
retryInMs: number;
|
|
244
|
-
|
|
243
|
+
problem: ConnectProblem;
|
|
245
244
|
} | {
|
|
246
245
|
state: "reset_required";
|
|
247
246
|
cursor: number;
|
|
248
|
-
|
|
247
|
+
problem: ConnectProblem<"change_cursor_reset">;
|
|
249
248
|
};
|
|
250
249
|
/** Provider-neutral operation transport used by the typed collection client. */
|
|
251
250
|
export interface MdbaseCollectionTransport {
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { ConnectProblem, ConnectProblemCode, MdbaseDiagnostic } from "@mdbase-dev/connect-protocol";
|
|
2
|
+
export type ConnectSuccess<Value> = {
|
|
3
|
+
ok: true;
|
|
4
|
+
value: Value;
|
|
5
|
+
diagnostics: MdbaseDiagnostic[];
|
|
6
|
+
};
|
|
7
|
+
export type ConnectFailure<Code extends ConnectProblemCode = ConnectProblemCode> = {
|
|
8
|
+
ok: false;
|
|
9
|
+
problem: ConnectProblem<Code>;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Every expected failure from the public SDK is data. Exceptions are reserved
|
|
13
|
+
* for programming errors and broken SDK invariants.
|
|
14
|
+
*/
|
|
15
|
+
export type ConnectOutcome<Value, Code extends ConnectProblemCode = ConnectProblemCode> = ConnectSuccess<Value> | ConnectFailure<Code>;
|
|
16
|
+
export type CommonOperationProblemCode = "access_denied" | "access_paused" | "authorization_expired" | "collection_access_denied" | "connector_identity_changed" | "connector_offline" | "connector_upgrade_required" | "direct_operation_rejected" | "operation_outcome_unknown" | "encryption_required" | "encrypted_relay_rejected" | "hosted_provider_unavailable" | "insufficient_access" | "invalid_encrypted_response" | "invalid_operation_response" | "invalid_request" | "missing_grant_key" | "not_authorized" | "operation_cancelled" | "operation_failed" | "rate_limited" | "relay_authorization_expired" | "relay_unavailable" | "temporarily_unavailable" | "timeout" | "unsupported_operation";
|
|
17
|
+
export declare const COMMON_OPERATION_PROBLEM_CODES: readonly ["access_denied", "access_paused", "authorization_expired", "collection_access_denied", "connector_identity_changed", "connector_offline", "connector_upgrade_required", "direct_operation_rejected", "operation_outcome_unknown", "encryption_required", "encrypted_relay_rejected", "hosted_provider_unavailable", "insufficient_access", "invalid_encrypted_response", "invalid_operation_response", "invalid_request", "missing_grant_key", "not_authorized", "operation_cancelled", "operation_failed", "rate_limited", "relay_authorization_expired", "relay_unavailable", "temporarily_unavailable", "timeout", "unsupported_operation"];
|
|
18
|
+
export declare const ALL_CONNECT_PROBLEM_CODES: ConnectProblemCode[];
|
|
19
|
+
export type RegistrationProblemCode = "discovery_failed" | "invalid_application_manifest" | "invalid_request" | "manifest_load_failed" | "rate_limited" | "temporarily_unavailable" | "timeout";
|
|
20
|
+
export declare const REGISTRATION_PROBLEM_CODES: readonly ["discovery_failed", "invalid_application_manifest", "invalid_request", "manifest_load_failed", "rate_limited", "temporarily_unavailable", "timeout"];
|
|
21
|
+
export type AuthorizationProblemCode = RegistrationProblemCode | "access_denied" | "approval_window_blocked" | "authorization_cancelled" | "browser_required" | "collection_access_denied" | "collection_configuration_invalid" | "collection_contracts_missing" | "collection_incompatible" | "collection_invalid" | "collection_kind_unsupported" | "collection_mismatch" | "collection_not_found" | "collection_type_registry_invalid" | "collection_version_unsupported" | "connector_identity_changed" | "connector_offline" | "connector_upgrade_required" | "device_authorization_failed" | "encryption_required" | "expired_token" | "invalid_callback" | "invalid_device_authorization_response" | "invalid_token_response" | "scope_denied" | "token_exchange_failed";
|
|
22
|
+
export declare const AUTHORIZATION_PROBLEM_CODES: readonly ["discovery_failed", "invalid_application_manifest", "invalid_request", "manifest_load_failed", "rate_limited", "temporarily_unavailable", "timeout", "access_denied", "approval_window_blocked", "authorization_cancelled", "browser_required", "collection_access_denied", "collection_configuration_invalid", "collection_contracts_missing", "collection_incompatible", "collection_invalid", "collection_kind_unsupported", "collection_mismatch", "collection_not_found", "collection_type_registry_invalid", "collection_version_unsupported", "connector_identity_changed", "connector_offline", "connector_upgrade_required", "device_authorization_failed", "encryption_required", "expired_token", "invalid_callback", "invalid_device_authorization_response", "invalid_token_response", "scope_denied", "token_exchange_failed"];
|
|
23
|
+
export type NotificationProblemCode = RegistrationProblemCode | "authorization_expired" | "invalid_push_subscription" | "managed_fcm_not_declared" | "not_authorized" | "notification_criterion_not_declared" | "notification_reauthorization_required" | "notification_registration_failed" | "notification_unregistration_failed" | "notifications_not_declared" | "notifications_unavailable";
|
|
24
|
+
export declare const NOTIFICATION_PROBLEM_CODES: readonly ["discovery_failed", "invalid_application_manifest", "invalid_request", "manifest_load_failed", "rate_limited", "temporarily_unavailable", "timeout", "authorization_expired", "invalid_push_subscription", "managed_fcm_not_declared", "not_authorized", "notification_criterion_not_declared", "notification_reauthorization_required", "notification_registration_failed", "notification_unregistration_failed", "notifications_not_declared", "notifications_unavailable"];
|
|
25
|
+
export type DirectAccessProblemCode = "connector_offline" | "connector_upgrade_required" | "invalid_operation_response" | "not_authorized" | "operation_failed" | "temporarily_unavailable" | "timeout";
|
|
26
|
+
export declare const DIRECT_ACCESS_PROBLEM_CODES: readonly ["connector_offline", "connector_upgrade_required", "invalid_operation_response", "not_authorized", "operation_failed", "temporarily_unavailable", "timeout"];
|
|
27
|
+
export type SessionProblemCode = AuthorizationProblemCode | "collection_not_ready" | "collection_not_selected" | "unknown_collection";
|
|
28
|
+
export declare const SESSION_PROBLEM_CODES: readonly ["discovery_failed", "invalid_application_manifest", "invalid_request", "manifest_load_failed", "rate_limited", "temporarily_unavailable", "timeout", "access_denied", "approval_window_blocked", "authorization_cancelled", "browser_required", "collection_access_denied", "collection_configuration_invalid", "collection_contracts_missing", "collection_incompatible", "collection_invalid", "collection_kind_unsupported", "collection_mismatch", "collection_not_found", "collection_type_registry_invalid", "collection_version_unsupported", "connector_identity_changed", "connector_offline", "connector_upgrade_required", "device_authorization_failed", "encryption_required", "expired_token", "invalid_callback", "invalid_device_authorization_response", "invalid_token_response", "scope_denied", "token_exchange_failed", "collection_not_ready", "collection_not_selected", "unknown_collection"];
|
|
29
|
+
export type CollectionSetupProblemCode = "collection_configuration_invalid" | "collection_invalid" | "collection_type_registry_invalid" | "collection_version_unsupported";
|
|
30
|
+
export type CollectionDescriptionProblemCode = CommonOperationProblemCode | CollectionSetupProblemCode;
|
|
31
|
+
export type CollectionReadProblemCode = CollectionDescriptionProblemCode | "invalid_path" | "operation_invalid";
|
|
32
|
+
export type CollectionQueryProblemCode = CollectionReadProblemCode | "query_snapshot_changed" | "sandbox_unsupported";
|
|
33
|
+
export type CollectionMutationProblemCode = CollectionReadProblemCode | "concurrent_modification" | "invalid_preflight" | "pending_mutation_unresolved";
|
|
34
|
+
export type CollectionChangesProblemCode = CollectionDescriptionProblemCode | "change_cursor_reset";
|
|
35
|
+
export type CollectionTypeProblemCode = CollectionMutationProblemCode | "type_pack_provision_failed";
|
|
36
|
+
export declare const COLLECTION_SETUP_PROBLEM_CODES: readonly ["collection_configuration_invalid", "collection_invalid", "collection_type_registry_invalid", "collection_version_unsupported"];
|
|
37
|
+
export declare const COLLECTION_DESCRIPTION_PROBLEM_CODES: readonly ["access_denied", "access_paused", "authorization_expired", "collection_access_denied", "connector_identity_changed", "connector_offline", "connector_upgrade_required", "direct_operation_rejected", "operation_outcome_unknown", "encryption_required", "encrypted_relay_rejected", "hosted_provider_unavailable", "insufficient_access", "invalid_encrypted_response", "invalid_operation_response", "invalid_request", "missing_grant_key", "not_authorized", "operation_cancelled", "operation_failed", "rate_limited", "relay_authorization_expired", "relay_unavailable", "temporarily_unavailable", "timeout", "unsupported_operation", "collection_configuration_invalid", "collection_invalid", "collection_type_registry_invalid", "collection_version_unsupported"];
|
|
38
|
+
export declare const COLLECTION_READ_PROBLEM_CODES: readonly ["access_denied", "access_paused", "authorization_expired", "collection_access_denied", "connector_identity_changed", "connector_offline", "connector_upgrade_required", "direct_operation_rejected", "operation_outcome_unknown", "encryption_required", "encrypted_relay_rejected", "hosted_provider_unavailable", "insufficient_access", "invalid_encrypted_response", "invalid_operation_response", "invalid_request", "missing_grant_key", "not_authorized", "operation_cancelled", "operation_failed", "rate_limited", "relay_authorization_expired", "relay_unavailable", "temporarily_unavailable", "timeout", "unsupported_operation", "collection_configuration_invalid", "collection_invalid", "collection_type_registry_invalid", "collection_version_unsupported", "invalid_path", "operation_invalid"];
|
|
39
|
+
export declare const COLLECTION_QUERY_PROBLEM_CODES: readonly ["access_denied", "access_paused", "authorization_expired", "collection_access_denied", "connector_identity_changed", "connector_offline", "connector_upgrade_required", "direct_operation_rejected", "operation_outcome_unknown", "encryption_required", "encrypted_relay_rejected", "hosted_provider_unavailable", "insufficient_access", "invalid_encrypted_response", "invalid_operation_response", "invalid_request", "missing_grant_key", "not_authorized", "operation_cancelled", "operation_failed", "rate_limited", "relay_authorization_expired", "relay_unavailable", "temporarily_unavailable", "timeout", "unsupported_operation", "collection_configuration_invalid", "collection_invalid", "collection_type_registry_invalid", "collection_version_unsupported", "invalid_path", "operation_invalid", "query_snapshot_changed", "sandbox_unsupported"];
|
|
40
|
+
export declare const COLLECTION_MUTATION_PROBLEM_CODES: readonly ["access_denied", "access_paused", "authorization_expired", "collection_access_denied", "connector_identity_changed", "connector_offline", "connector_upgrade_required", "direct_operation_rejected", "operation_outcome_unknown", "encryption_required", "encrypted_relay_rejected", "hosted_provider_unavailable", "insufficient_access", "invalid_encrypted_response", "invalid_operation_response", "invalid_request", "missing_grant_key", "not_authorized", "operation_cancelled", "operation_failed", "rate_limited", "relay_authorization_expired", "relay_unavailable", "temporarily_unavailable", "timeout", "unsupported_operation", "collection_configuration_invalid", "collection_invalid", "collection_type_registry_invalid", "collection_version_unsupported", "invalid_path", "operation_invalid", "concurrent_modification", "invalid_preflight", "pending_mutation_unresolved"];
|
|
41
|
+
export declare const COLLECTION_CHANGES_PROBLEM_CODES: readonly ["access_denied", "access_paused", "authorization_expired", "collection_access_denied", "connector_identity_changed", "connector_offline", "connector_upgrade_required", "direct_operation_rejected", "operation_outcome_unknown", "encryption_required", "encrypted_relay_rejected", "hosted_provider_unavailable", "insufficient_access", "invalid_encrypted_response", "invalid_operation_response", "invalid_request", "missing_grant_key", "not_authorized", "operation_cancelled", "operation_failed", "rate_limited", "relay_authorization_expired", "relay_unavailable", "temporarily_unavailable", "timeout", "unsupported_operation", "collection_configuration_invalid", "collection_invalid", "collection_type_registry_invalid", "collection_version_unsupported", "change_cursor_reset"];
|
|
42
|
+
export declare const COLLECTION_TYPE_PROBLEM_CODES: readonly ["access_denied", "access_paused", "authorization_expired", "collection_access_denied", "connector_identity_changed", "connector_offline", "connector_upgrade_required", "direct_operation_rejected", "operation_outcome_unknown", "encryption_required", "encrypted_relay_rejected", "hosted_provider_unavailable", "insufficient_access", "invalid_encrypted_response", "invalid_operation_response", "invalid_request", "missing_grant_key", "not_authorized", "operation_cancelled", "operation_failed", "rate_limited", "relay_authorization_expired", "relay_unavailable", "temporarily_unavailable", "timeout", "unsupported_operation", "collection_configuration_invalid", "collection_invalid", "collection_type_registry_invalid", "collection_version_unsupported", "invalid_path", "operation_invalid", "concurrent_modification", "invalid_preflight", "pending_mutation_unresolved", "type_pack_provision_failed"];
|
|
43
|
+
export declare function connectSuccess<Value>(value: Value, diagnostics?: MdbaseDiagnostic[]): ConnectSuccess<Value>;
|
|
44
|
+
export declare function connectFailure<Code extends ConnectProblemCode>(problem: ConnectProblem<Code>): ConnectFailure<Code>;
|
|
45
|
+
export declare function captureConnectOutcome<Value, Code extends ConnectProblemCode>(operation: () => Promise<Value>, allowedCodes: readonly Code[]): Promise<ConnectOutcome<Value, Code>>;
|
|
46
|
+
export declare class ConnectOutcomeError extends Error {
|
|
47
|
+
readonly problem: ConnectProblem;
|
|
48
|
+
constructor(problem: ConnectProblem);
|
|
49
|
+
}
|
|
50
|
+
/** Explicitly opt back into exception flow for scripts and narrow adapters. */
|
|
51
|
+
export declare function unwrapConnectOutcome<Value>(outcome: ConnectOutcome<Value>): Value;
|
package/dist/outcomes.js
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { CONNECT_PROBLEM_CATALOG } from "@mdbase-dev/connect-protocol";
|
|
2
|
+
import { MdbaseConnectError, unknownConnectProblem } from "./errors.js";
|
|
3
|
+
export const COMMON_OPERATION_PROBLEM_CODES = [
|
|
4
|
+
"access_denied",
|
|
5
|
+
"access_paused",
|
|
6
|
+
"authorization_expired",
|
|
7
|
+
"collection_access_denied",
|
|
8
|
+
"connector_identity_changed",
|
|
9
|
+
"connector_offline",
|
|
10
|
+
"connector_upgrade_required",
|
|
11
|
+
"direct_operation_rejected",
|
|
12
|
+
"operation_outcome_unknown",
|
|
13
|
+
"encryption_required",
|
|
14
|
+
"encrypted_relay_rejected",
|
|
15
|
+
"hosted_provider_unavailable",
|
|
16
|
+
"insufficient_access",
|
|
17
|
+
"invalid_encrypted_response",
|
|
18
|
+
"invalid_operation_response",
|
|
19
|
+
"invalid_request",
|
|
20
|
+
"missing_grant_key",
|
|
21
|
+
"not_authorized",
|
|
22
|
+
"operation_cancelled",
|
|
23
|
+
"operation_failed",
|
|
24
|
+
"rate_limited",
|
|
25
|
+
"relay_authorization_expired",
|
|
26
|
+
"relay_unavailable",
|
|
27
|
+
"temporarily_unavailable",
|
|
28
|
+
"timeout",
|
|
29
|
+
"unsupported_operation"
|
|
30
|
+
];
|
|
31
|
+
export const ALL_CONNECT_PROBLEM_CODES = Object.keys(CONNECT_PROBLEM_CATALOG);
|
|
32
|
+
export const REGISTRATION_PROBLEM_CODES = [
|
|
33
|
+
"discovery_failed",
|
|
34
|
+
"invalid_application_manifest",
|
|
35
|
+
"invalid_request",
|
|
36
|
+
"manifest_load_failed",
|
|
37
|
+
"rate_limited",
|
|
38
|
+
"temporarily_unavailable",
|
|
39
|
+
"timeout"
|
|
40
|
+
];
|
|
41
|
+
export const AUTHORIZATION_PROBLEM_CODES = [
|
|
42
|
+
...REGISTRATION_PROBLEM_CODES,
|
|
43
|
+
"access_denied",
|
|
44
|
+
"approval_window_blocked",
|
|
45
|
+
"authorization_cancelled",
|
|
46
|
+
"browser_required",
|
|
47
|
+
"collection_access_denied",
|
|
48
|
+
"collection_configuration_invalid",
|
|
49
|
+
"collection_contracts_missing",
|
|
50
|
+
"collection_incompatible",
|
|
51
|
+
"collection_invalid",
|
|
52
|
+
"collection_kind_unsupported",
|
|
53
|
+
"collection_mismatch",
|
|
54
|
+
"collection_not_found",
|
|
55
|
+
"collection_type_registry_invalid",
|
|
56
|
+
"collection_version_unsupported",
|
|
57
|
+
"connector_identity_changed",
|
|
58
|
+
"connector_offline",
|
|
59
|
+
"connector_upgrade_required",
|
|
60
|
+
"device_authorization_failed",
|
|
61
|
+
"encryption_required",
|
|
62
|
+
"expired_token",
|
|
63
|
+
"invalid_callback",
|
|
64
|
+
"invalid_device_authorization_response",
|
|
65
|
+
"invalid_token_response",
|
|
66
|
+
"scope_denied",
|
|
67
|
+
"token_exchange_failed"
|
|
68
|
+
];
|
|
69
|
+
export const NOTIFICATION_PROBLEM_CODES = [
|
|
70
|
+
...REGISTRATION_PROBLEM_CODES,
|
|
71
|
+
"authorization_expired",
|
|
72
|
+
"invalid_push_subscription",
|
|
73
|
+
"managed_fcm_not_declared",
|
|
74
|
+
"not_authorized",
|
|
75
|
+
"notification_criterion_not_declared",
|
|
76
|
+
"notification_reauthorization_required",
|
|
77
|
+
"notification_registration_failed",
|
|
78
|
+
"notification_unregistration_failed",
|
|
79
|
+
"notifications_not_declared",
|
|
80
|
+
"notifications_unavailable"
|
|
81
|
+
];
|
|
82
|
+
export const DIRECT_ACCESS_PROBLEM_CODES = [
|
|
83
|
+
"connector_offline",
|
|
84
|
+
"connector_upgrade_required",
|
|
85
|
+
"invalid_operation_response",
|
|
86
|
+
"not_authorized",
|
|
87
|
+
"operation_failed",
|
|
88
|
+
"temporarily_unavailable",
|
|
89
|
+
"timeout"
|
|
90
|
+
];
|
|
91
|
+
export const SESSION_PROBLEM_CODES = [
|
|
92
|
+
...AUTHORIZATION_PROBLEM_CODES,
|
|
93
|
+
"collection_not_ready",
|
|
94
|
+
"collection_not_selected",
|
|
95
|
+
"unknown_collection"
|
|
96
|
+
];
|
|
97
|
+
export const COLLECTION_SETUP_PROBLEM_CODES = [
|
|
98
|
+
"collection_configuration_invalid",
|
|
99
|
+
"collection_invalid",
|
|
100
|
+
"collection_type_registry_invalid",
|
|
101
|
+
"collection_version_unsupported"
|
|
102
|
+
];
|
|
103
|
+
export const COLLECTION_DESCRIPTION_PROBLEM_CODES = [
|
|
104
|
+
...COMMON_OPERATION_PROBLEM_CODES,
|
|
105
|
+
...COLLECTION_SETUP_PROBLEM_CODES
|
|
106
|
+
];
|
|
107
|
+
export const COLLECTION_READ_PROBLEM_CODES = [
|
|
108
|
+
...COLLECTION_DESCRIPTION_PROBLEM_CODES,
|
|
109
|
+
"invalid_path",
|
|
110
|
+
"operation_invalid"
|
|
111
|
+
];
|
|
112
|
+
export const COLLECTION_QUERY_PROBLEM_CODES = [
|
|
113
|
+
...COLLECTION_READ_PROBLEM_CODES,
|
|
114
|
+
"query_snapshot_changed",
|
|
115
|
+
"sandbox_unsupported"
|
|
116
|
+
];
|
|
117
|
+
export const COLLECTION_MUTATION_PROBLEM_CODES = [
|
|
118
|
+
...COLLECTION_READ_PROBLEM_CODES,
|
|
119
|
+
"concurrent_modification",
|
|
120
|
+
"invalid_preflight",
|
|
121
|
+
"pending_mutation_unresolved"
|
|
122
|
+
];
|
|
123
|
+
export const COLLECTION_CHANGES_PROBLEM_CODES = [
|
|
124
|
+
...COLLECTION_DESCRIPTION_PROBLEM_CODES,
|
|
125
|
+
"change_cursor_reset"
|
|
126
|
+
];
|
|
127
|
+
export const COLLECTION_TYPE_PROBLEM_CODES = [
|
|
128
|
+
...COLLECTION_MUTATION_PROBLEM_CODES,
|
|
129
|
+
"type_pack_provision_failed"
|
|
130
|
+
];
|
|
131
|
+
export function connectSuccess(value, diagnostics = []) {
|
|
132
|
+
return { ok: true, value, diagnostics };
|
|
133
|
+
}
|
|
134
|
+
export function connectFailure(problem) {
|
|
135
|
+
return { ok: false, problem };
|
|
136
|
+
}
|
|
137
|
+
export async function captureConnectOutcome(operation, allowedCodes) {
|
|
138
|
+
try {
|
|
139
|
+
return connectSuccess(await operation());
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
if (!(error instanceof MdbaseConnectError))
|
|
143
|
+
throw error;
|
|
144
|
+
const problem = error.problem;
|
|
145
|
+
if (problem.code === "unknown" || allowedCodes.includes(problem.code)) {
|
|
146
|
+
return connectFailure(problem);
|
|
147
|
+
}
|
|
148
|
+
return connectFailure(unknownConnectProblem(problem.code, problem.message, {
|
|
149
|
+
details: problem.details,
|
|
150
|
+
operationOutcome: problem.operation_outcome,
|
|
151
|
+
traceId: problem.trace_id
|
|
152
|
+
}));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
export class ConnectOutcomeError extends Error {
|
|
156
|
+
problem;
|
|
157
|
+
constructor(problem) {
|
|
158
|
+
super(problem.message);
|
|
159
|
+
this.problem = problem;
|
|
160
|
+
this.name = "ConnectOutcomeError";
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/** Explicitly opt back into exception flow for scripts and narrow adapters. */
|
|
164
|
+
export function unwrapConnectOutcome(outcome) {
|
|
165
|
+
if (!outcome.ok)
|
|
166
|
+
throw new ConnectOutcomeError(outcome.problem);
|
|
167
|
+
return outcome.value;
|
|
168
|
+
}
|
package/dist/runtime-utils.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import type { GrantEncryption, GrantScope, MdbaseAppManifest } from "@mdbase-dev/connect-protocol";
|
|
1
|
+
import type { FileCapability, GrantEncryption, GrantScope, MdbaseAppManifest } from "@mdbase-dev/connect-protocol";
|
|
2
2
|
import { MdbaseConnectError } from "./errors.js";
|
|
3
3
|
import type { StoredToken } from "./internal-types.js";
|
|
4
4
|
import type { MdbaseDeviceAuthorization } from "./authorization-types.js";
|
|
5
|
+
type NetworkProblemCode = "notification_registration_failed" | "notification_unregistration_failed" | "notifications_unavailable" | "temporarily_unavailable";
|
|
6
|
+
/** Normalize fetch rejection at the exact I/O boundary. */
|
|
7
|
+
export declare function connectFetch(input: RequestInfo | URL, init: RequestInit | undefined, code: NetworkProblemCode, message: string): Promise<Response>;
|
|
5
8
|
export declare function canonicalLoopbackUrl(value: string): string;
|
|
6
9
|
export declare function createPkce(): Promise<{
|
|
7
10
|
verifier: string;
|
|
@@ -11,6 +14,7 @@ export declare function apiError(body: any, fallbackCode: string, fallbackMessag
|
|
|
11
14
|
export declare function oauthErrorCode(body: any): string | undefined;
|
|
12
15
|
export declare function validAuthorityTokenResponse(value: unknown, collectionId: unknown): boolean;
|
|
13
16
|
export declare function validStoredAuthority(authority: StoredToken["authority"], collectionId: string): boolean;
|
|
17
|
+
export declare function validFileCapability(value: unknown): value is FileCapability;
|
|
14
18
|
export declare function validStoredEncryption(encryption: StoredToken["encryption"], collectionId: string): encryption is GrantEncryption;
|
|
15
19
|
export declare function parseDeviceAuthorization(body: any): MdbaseDeviceAuthorization;
|
|
16
20
|
export declare function parseStored<T>(value: string | null): T | null;
|
|
@@ -31,3 +35,4 @@ export declare class MemoryStorage implements Storage {
|
|
|
31
35
|
}
|
|
32
36
|
export declare function isOpaquePortableManifest(manifest: MdbaseAppManifest | string): boolean;
|
|
33
37
|
export declare function manifestStorageFingerprint(manifest: MdbaseAppManifest): string;
|
|
38
|
+
export {};
|
package/dist/runtime-utils.js
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import { validateGrantEncryption } from "./crypto.js";
|
|
2
|
-
import {
|
|
2
|
+
import { connectError, serverConnectError } from "./errors.js";
|
|
3
3
|
import { canonicalJson } from "./operation-helpers.js";
|
|
4
4
|
import { bytesToBase64Url, randomBase64Url } from "./base64.js";
|
|
5
|
+
/** Normalize fetch rejection at the exact I/O boundary. */
|
|
6
|
+
export async function connectFetch(input, init, code, message) {
|
|
7
|
+
try {
|
|
8
|
+
return await fetch(input, init);
|
|
9
|
+
}
|
|
10
|
+
catch (cause) {
|
|
11
|
+
throw connectError(code, message, { cause });
|
|
12
|
+
}
|
|
13
|
+
}
|
|
5
14
|
export function canonicalLoopbackUrl(value) {
|
|
6
15
|
const url = new URL(value);
|
|
7
16
|
if (url.protocol !== "http:"
|
|
@@ -11,7 +20,7 @@ export function canonicalLoopbackUrl(value) {
|
|
|
11
20
|
|| url.pathname !== "/"
|
|
12
21
|
|| url.search
|
|
13
22
|
|| url.hash) {
|
|
14
|
-
throw
|
|
23
|
+
throw connectError("invalid_loopback_url", "loopbackUrl must be an HTTP origin on 127.0.0.1 or ::1.");
|
|
15
24
|
}
|
|
16
25
|
return url.origin;
|
|
17
26
|
}
|
|
@@ -21,7 +30,7 @@ export async function createPkce() {
|
|
|
21
30
|
return { verifier, challenge: bytesToBase64Url(new Uint8Array(digest)) };
|
|
22
31
|
}
|
|
23
32
|
export function apiError(body, fallbackCode, fallbackMessage, status) {
|
|
24
|
-
return
|
|
33
|
+
return serverConnectError(oauthErrorCode(body) ?? body?.error?.code ?? fallbackCode, body?.error_description ?? body?.error?.message ?? fallbackMessage, { status, details: body?.error?.details });
|
|
25
34
|
}
|
|
26
35
|
export function oauthErrorCode(body) {
|
|
27
36
|
return typeof body?.error === "string" ? body.error : undefined;
|
|
@@ -34,6 +43,7 @@ export function validAuthorityTokenResponse(value, collectionId) {
|
|
|
34
43
|
const authority = value;
|
|
35
44
|
if (typeof authority.operations_url !== "string"
|
|
36
45
|
|| typeof authority.sync_url !== "string"
|
|
46
|
+
|| typeof authority.files_url !== "string"
|
|
37
47
|
|| typeof authority.replica_id !== "string"
|
|
38
48
|
|| authority.replica_id.length === 0
|
|
39
49
|
|| typeof authority.access_token !== "string"
|
|
@@ -45,7 +55,8 @@ export function validAuthorityTokenResponse(value, collectionId) {
|
|
|
45
55
|
try {
|
|
46
56
|
const operations = new URL(authority.operations_url);
|
|
47
57
|
const sync = new URL(authority.sync_url);
|
|
48
|
-
|
|
58
|
+
const files = new URL(authority.files_url);
|
|
59
|
+
return [operations, sync, files].every((url) => (url.protocol === "https:"
|
|
49
60
|
|| (url.protocol === "http:"
|
|
50
61
|
&& ["localhost", "127.0.0.1", "[::1]", "::1"].includes(url.hostname)))
|
|
51
62
|
&& !url.username
|
|
@@ -54,9 +65,12 @@ export function validAuthorityTokenResponse(value, collectionId) {
|
|
|
54
65
|
&& !url.hash)
|
|
55
66
|
&& /^\/v1\/authorities\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\/operations$/i.test(operations.pathname)
|
|
56
67
|
&& /^\/v1\/authorities\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\/sync$/i.test(sync.pathname)
|
|
68
|
+
&& /^\/v1\/authorities\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\/files$/i.test(files.pathname)
|
|
57
69
|
&& operations.origin === sync.origin
|
|
70
|
+
&& operations.origin === files.origin
|
|
58
71
|
&& operations.pathname.split("/")[3] === collectionId
|
|
59
|
-
&& sync.pathname.split("/")[3] === collectionId
|
|
72
|
+
&& sync.pathname.split("/")[3] === collectionId
|
|
73
|
+
&& files.pathname.split("/")[3] === collectionId;
|
|
60
74
|
}
|
|
61
75
|
catch {
|
|
62
76
|
return false;
|
|
@@ -66,11 +80,76 @@ export function validStoredAuthority(authority, collectionId) {
|
|
|
66
80
|
return validAuthorityTokenResponse({
|
|
67
81
|
operations_url: authority?.operationsUrl,
|
|
68
82
|
sync_url: authority?.syncUrl,
|
|
83
|
+
files_url: authority?.filesUrl,
|
|
69
84
|
replica_id: authority?.replicaId,
|
|
70
85
|
access_token: authority?.accessToken,
|
|
71
86
|
proof_public_key: authority?.proofPublicKey
|
|
72
87
|
}, collectionId);
|
|
73
88
|
}
|
|
89
|
+
export function validFileCapability(value) {
|
|
90
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
91
|
+
return false;
|
|
92
|
+
const capability = value;
|
|
93
|
+
const actions = capability.actions;
|
|
94
|
+
if (capability.kind !== "files"
|
|
95
|
+
|| capability.protocol_version !== 1
|
|
96
|
+
|| !Array.isArray(actions)
|
|
97
|
+
|| actions.length === 0
|
|
98
|
+
|| actions.length > 6
|
|
99
|
+
|| new Set(actions).size !== actions.length
|
|
100
|
+
|| actions.some((action) => ![
|
|
101
|
+
"list",
|
|
102
|
+
"read",
|
|
103
|
+
"add",
|
|
104
|
+
"replace",
|
|
105
|
+
"move",
|
|
106
|
+
"delete"
|
|
107
|
+
].includes(action)))
|
|
108
|
+
return false;
|
|
109
|
+
const scope = capability.scope;
|
|
110
|
+
if (!scope || typeof scope !== "object" || Array.isArray(scope))
|
|
111
|
+
return false;
|
|
112
|
+
if (scope.kind === "collection") {
|
|
113
|
+
return Object.keys(scope).length === 1;
|
|
114
|
+
}
|
|
115
|
+
return scope.kind === "selected_folders"
|
|
116
|
+
&& Object.keys(scope).length === 2
|
|
117
|
+
&& Array.isArray(scope.folders)
|
|
118
|
+
&& scope.folders.length > 0
|
|
119
|
+
&& scope.folders.length <= 100
|
|
120
|
+
&& new Set(scope.folders).size === scope.folders.length
|
|
121
|
+
&& scope.folders.every((folder) => typeof folder === "string"
|
|
122
|
+
&& folder.length > 0
|
|
123
|
+
&& folder.length <= 1_024
|
|
124
|
+
&& validFileFolder(folder));
|
|
125
|
+
}
|
|
126
|
+
function validFileFolder(folder) {
|
|
127
|
+
const reserved = new Set([
|
|
128
|
+
".mdbase",
|
|
129
|
+
".git",
|
|
130
|
+
"node_modules",
|
|
131
|
+
"_contracts",
|
|
132
|
+
"_schemas",
|
|
133
|
+
"_types",
|
|
134
|
+
"_views"
|
|
135
|
+
]);
|
|
136
|
+
return !folder.startsWith("/")
|
|
137
|
+
&& !folder.endsWith("/")
|
|
138
|
+
&& !folder.includes("\\")
|
|
139
|
+
&& folder.split("/").every((component) => {
|
|
140
|
+
const stem = component.split(".")[0]?.toUpperCase() ?? "";
|
|
141
|
+
return component !== ""
|
|
142
|
+
&& component !== "."
|
|
143
|
+
&& component !== ".."
|
|
144
|
+
&& !component.startsWith(".")
|
|
145
|
+
&& !component.endsWith(" ")
|
|
146
|
+
&& !component.endsWith(".")
|
|
147
|
+
&& !/[<>:"|?*]/.test(component)
|
|
148
|
+
&& !reserved.has(component.toLowerCase())
|
|
149
|
+
&& !["CON", "PRN", "AUX", "NUL"].includes(stem)
|
|
150
|
+
&& !/^(COM|LPT)[1-9]$/.test(stem);
|
|
151
|
+
});
|
|
152
|
+
}
|
|
74
153
|
export function validStoredEncryption(encryption, collectionId) {
|
|
75
154
|
if (!encryption || typeof encryption !== "object" || Array.isArray(encryption)) {
|
|
76
155
|
return false;
|
|
@@ -92,7 +171,7 @@ export function parseDeviceAuthorization(body) {
|
|
|
92
171
|
|| typeof body?.verification_uri_complete !== "string"
|
|
93
172
|
|| !Number.isFinite(body?.expires_in)
|
|
94
173
|
|| !Number.isFinite(body?.interval)) {
|
|
95
|
-
throw
|
|
174
|
+
throw connectError("invalid_device_authorization_response", "Connect returned an invalid downloaded application authorization response.");
|
|
96
175
|
}
|
|
97
176
|
return {
|
|
98
177
|
userCode: body.user_code,
|
|
@@ -148,22 +227,22 @@ export function stripTrailingSlash(value) {
|
|
|
148
227
|
}
|
|
149
228
|
export function defaultManifestSource() {
|
|
150
229
|
if (typeof location === "undefined") {
|
|
151
|
-
throw
|
|
230
|
+
throw connectError("manifest_required", "manifest is required outside a browser environment.");
|
|
152
231
|
}
|
|
153
232
|
if (location.origin === "null") {
|
|
154
|
-
throw
|
|
233
|
+
throw connectError("manifest_required", "Downloaded applications must provide their v1 portable manifest inline.");
|
|
155
234
|
}
|
|
156
235
|
return new URL("/.well-known/mdbase-app.json", location.origin).href;
|
|
157
236
|
}
|
|
158
237
|
export function defaultRedirectUri() {
|
|
159
238
|
if (typeof location === "undefined") {
|
|
160
|
-
throw
|
|
239
|
+
throw connectError("redirect_uri_required", "redirectUri is required outside a browser environment.");
|
|
161
240
|
}
|
|
162
241
|
return location.href.split(/[?#]/)[0];
|
|
163
242
|
}
|
|
164
243
|
export function defaultCallbackUrl() {
|
|
165
244
|
if (typeof location === "undefined") {
|
|
166
|
-
throw
|
|
245
|
+
throw connectError("callback_url_required", "callbackUrl is required outside a browser environment.");
|
|
167
246
|
}
|
|
168
247
|
return location.href;
|
|
169
248
|
}
|
|
@@ -171,7 +250,7 @@ export function defaultStorage(memoryOnly) {
|
|
|
171
250
|
if (memoryOnly)
|
|
172
251
|
return new MemoryStorage();
|
|
173
252
|
if (typeof localStorage === "undefined") {
|
|
174
|
-
throw
|
|
253
|
+
throw connectError("storage_required", "storage is required outside a browser environment.");
|
|
175
254
|
}
|
|
176
255
|
try {
|
|
177
256
|
const probe = `mdbase-connect:probe:${randomBase64Url(8)}`;
|
package/dist/selection.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { cleanAuthorizationParameters, isAuthorizationCallbackUrl } from "./authorization-url.js";
|
|
2
|
-
import {
|
|
2
|
+
import { connectError } from "./errors.js";
|
|
3
3
|
/** URL-backed collection selection for browser and browser-shell applications. */
|
|
4
4
|
export class MdbaseBrowserSelection {
|
|
5
5
|
collectionParameter;
|
|
@@ -71,13 +71,13 @@ export class MdbaseBrowserSelection {
|
|
|
71
71
|
}
|
|
72
72
|
currentUrl() {
|
|
73
73
|
if (typeof location === "undefined") {
|
|
74
|
-
throw
|
|
74
|
+
throw connectError("browser_required", "Browser selection requires a browser environment.");
|
|
75
75
|
}
|
|
76
76
|
return new URL(location.href);
|
|
77
77
|
}
|
|
78
78
|
browserHistory() {
|
|
79
79
|
if (typeof history === "undefined") {
|
|
80
|
-
throw
|
|
80
|
+
throw connectError("browser_required", "Browser selection requires a browser environment.");
|
|
81
81
|
}
|
|
82
82
|
return history;
|
|
83
83
|
}
|
package/dist/session.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { CollectionOperation, JsonObject } from "@mdbase-dev/connect-protocol";
|
|
2
2
|
import { MdbaseConnection, type MdbaseAuthorizationOutcome, type MdbaseAuthorizationResult, type MdbaseAuthorizeOptions } from "./connection.js";
|
|
3
3
|
import type { MdbaseAuthorizationCapabilities, MdbaseConnectionInfo } from "./connection-types.js";
|
|
4
|
+
import { type AuthorizationProblemCode, type ConnectOutcome, type SessionProblemCode } from "./outcomes.js";
|
|
4
5
|
import type { MdbaseSelectionHistory, MdbaseSessionSelection } from "./selection.js";
|
|
5
6
|
export interface MdbaseSessionConnect<Frontmatter extends JsonObject> {
|
|
6
|
-
authorize(options?: MdbaseAuthorizeOptions): Promise<MdbaseAuthorizationOutcome<Frontmatter>>;
|
|
7
|
-
completeAuthorization(callbackUrl: string): Promise<MdbaseAuthorizationResult<Frontmatter>>;
|
|
7
|
+
authorize(options?: MdbaseAuthorizeOptions): Promise<ConnectOutcome<MdbaseAuthorizationOutcome<Frontmatter>, AuthorizationProblemCode>>;
|
|
8
|
+
completeAuthorization(callbackUrl: string): Promise<ConnectOutcome<MdbaseAuthorizationResult<Frontmatter>, AuthorizationProblemCode>>;
|
|
8
9
|
connections(): MdbaseConnectionInfo[];
|
|
9
10
|
connection(collectionId: string): MdbaseConnection<Frontmatter> | null;
|
|
10
11
|
unavailableReason(collectionId: string): MdbaseUnavailableReason | null;
|
|
@@ -51,25 +52,25 @@ export declare class MdbaseSession<Frontmatter extends JsonObject = JsonObject>
|
|
|
51
52
|
private transactionDepth;
|
|
52
53
|
private callbackPromise;
|
|
53
54
|
constructor(connect: MdbaseSessionConnect<Frontmatter>, options: MdbaseSessionOptions);
|
|
54
|
-
start(): Promise<MdbaseSessionSnapshot<Frontmatter>>;
|
|
55
|
+
start(): Promise<ConnectOutcome<MdbaseSessionSnapshot<Frontmatter>, SessionProblemCode>>;
|
|
55
56
|
destroy(): void;
|
|
56
57
|
getSnapshot(): MdbaseSessionSnapshot<Frontmatter>;
|
|
57
58
|
subscribe(listener: () => void): () => void;
|
|
58
59
|
select(collectionId: string, options?: {
|
|
59
60
|
history?: MdbaseSelectionHistory;
|
|
60
|
-
}): MdbaseConnection<Frontmatter>;
|
|
61
|
+
}): ConnectOutcome<MdbaseConnection<Frontmatter>, "unknown_collection">;
|
|
61
62
|
clearSelection(options?: {
|
|
62
63
|
history?: MdbaseSelectionHistory;
|
|
63
64
|
}): void;
|
|
64
65
|
forget(collectionId: string): void;
|
|
65
66
|
authorize(target: "choose" | "selected" | {
|
|
66
67
|
collectionId: string;
|
|
67
|
-
}, options?: Omit<MdbaseAuthorizeOptions, "operations" | "returnTo" | "target">): Promise<MdbaseAuthorizationOutcome<Frontmatter>>;
|
|
68
|
-
ensureOperations(requiredOperations: CollectionOperation[]): Promise<MdbaseAuthorizationOutcome<Frontmatter> | {
|
|
68
|
+
}, options?: Omit<MdbaseAuthorizeOptions, "operations" | "returnTo" | "target">): Promise<ConnectOutcome<MdbaseAuthorizationOutcome<Frontmatter>, SessionProblemCode>>;
|
|
69
|
+
ensureOperations(requiredOperations: CollectionOperation[]): Promise<ConnectOutcome<MdbaseAuthorizationOutcome<Frontmatter> | {
|
|
69
70
|
kind: "unchanged";
|
|
70
71
|
connection: MdbaseConnection<Frontmatter>;
|
|
71
|
-
}
|
|
72
|
-
handleAuthorizationCallback(callbackUrl: string): Promise<MdbaseConnection<Frontmatter>>;
|
|
72
|
+
}, SessionProblemCode>>;
|
|
73
|
+
handleAuthorizationCallback(callbackUrl: string): Promise<ConnectOutcome<MdbaseConnection<Frontmatter>, AuthorizationProblemCode>>;
|
|
73
74
|
private autoSelectOnlyConnection;
|
|
74
75
|
private refresh;
|
|
75
76
|
private refreshOnce;
|