@mdbase-dev/connect-protocol 0.1.0-beta.31 → 0.1.0-beta.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,11 +6,13 @@ export const CONNECT_PROBLEM_CATALOG = {
6
6
  "application_identity_unavailable": { category: "compatibility", recovery: "upgrade_application" },
7
7
  "approval_window_blocked": { category: "authorization", recovery: "reauthorize" },
8
8
  "authority_authorization_changed": { category: "authorization", recovery: "reauthorize" },
9
+ "authorization_binding_incompatible": { category: "compatibility", recovery: "upgrade_application" },
9
10
  "authorization_cancelled": { category: "cancellation", recovery: "none" },
10
11
  "authorization_expired": { category: "authorization", recovery: "reauthorize" },
11
12
  "authorization_replayed": { category: "integrity", recovery: "reauthorize" },
12
13
  "browser_required": { category: "compatibility", recovery: "none" },
13
14
  "callback_url_required": { category: "validation", recovery: "fix_request" },
15
+ "capability_contract_incompatible": { category: "compatibility", recovery: "upgrade_application" },
14
16
  "change_cursor_reset": { category: "conflict", recovery: "refresh" },
15
17
  "collection_access_denied": { category: "authorization", recovery: "reauthorize" },
16
18
  "collection_configuration_invalid": { category: "validation", recovery: "repair_collection" },
@@ -31,6 +33,7 @@ export const CONNECT_PROBLEM_CATALOG = {
31
33
  "device_authorization_failed": { category: "authorization", recovery: "reauthorize" },
32
34
  "direct_operation_rejected": { category: "authorization", recovery: "reauthorize" },
33
35
  "discovery_failed": { category: "availability", recovery: "retry" },
36
+ "durable_mutation_unsupported": { category: "compatibility", recovery: "upgrade_connector" },
34
37
  "encrypted_relay_rejected": { category: "authorization", recovery: "reauthorize" },
35
38
  "encryption_required": { category: "authorization", recovery: "reauthorize" },
36
39
  "expired_token": { category: "authorization", recovery: "reauthorize" },
@@ -61,6 +64,8 @@ export const CONNECT_PROBLEM_CATALOG = {
61
64
  "manifest_load_failed": { category: "availability", recovery: "retry" },
62
65
  "manifest_required": { category: "validation", recovery: "fix_request" },
63
66
  "missing_grant_key": { category: "authorization", recovery: "reauthorize" },
67
+ "mutation_recovery_expired": { category: "compatibility", recovery: "none" },
68
+ "mutation_request_conflict": { category: "conflict", recovery: "resolve_conflict" },
64
69
  "no_pending_mutation": { category: "conflict", recovery: "refresh" },
65
70
  "not_authorized": { category: "authorization", recovery: "reauthorize" },
66
71
  "not_remote_authority": { category: "compatibility", recovery: "choose_collection" },
@@ -81,6 +86,11 @@ export const CONNECT_PROBLEM_CATALOG = {
81
86
  "reconnect_required": { category: "authorization", recovery: "reauthorize" },
82
87
  "redirect_uri_required": { category: "validation", recovery: "fix_request" },
83
88
  "reference_updates_unsupported": { category: "compatibility", recovery: "fix_request" },
89
+ "registry_backup_invalid": { category: "integrity", recovery: "contact_support" },
90
+ "registry_busy": { category: "availability", recovery: "retry" },
91
+ "registry_corrupt": { category: "integrity", recovery: "repair_collection" },
92
+ "registry_migration_failed": { category: "internal", recovery: "contact_support" },
93
+ "registry_schema_incompatible": { category: "compatibility", recovery: "upgrade_connector" },
84
94
  "relay_authorization_expired": { category: "authorization", recovery: "reauthorize" },
85
95
  "relay_unavailable": { category: "availability", recovery: "retry" },
86
96
  "sandbox_unsupported": { category: "compatibility", recovery: "fix_request" },
@@ -91,6 +101,7 @@ export const CONNECT_PROBLEM_CATALOG = {
91
101
  "temporarily_unavailable": { category: "availability", recovery: "retry" },
92
102
  "timeout": { category: "availability", recovery: "retry" },
93
103
  "token_exchange_failed": { category: "availability", recovery: "retry" },
104
+ "transport_protocol_incompatible": { category: "compatibility", recovery: "upgrade_connector" },
94
105
  "type_pack_provision_failed": { category: "validation", recovery: "repair_collection" },
95
106
  "unknown_collection": { category: "selection", recovery: "choose_collection" },
96
107
  "unsafe_file_path": { category: "validation", recovery: "fix_request" },
@@ -134,6 +145,70 @@ const CONNECT_PROBLEM_DETAIL_SCHEMAS = {
134
145
  }
135
146
  }
136
147
  },
148
+ "authorization_binding_incompatible": {
149
+ "type": "object",
150
+ "required": [
151
+ "contract",
152
+ "required",
153
+ "supported",
154
+ "peer"
155
+ ],
156
+ "properties": {
157
+ "contract": {
158
+ "type": "string"
159
+ },
160
+ "required": {
161
+ "type": "array",
162
+ "items": {
163
+ "type": "integer"
164
+ }
165
+ },
166
+ "supported": {
167
+ "type": "array",
168
+ "items": {
169
+ "type": "integer"
170
+ }
171
+ },
172
+ "peer": {
173
+ "type": "string"
174
+ },
175
+ "operation": {
176
+ "type": "string"
177
+ }
178
+ }
179
+ },
180
+ "capability_contract_incompatible": {
181
+ "type": "object",
182
+ "required": [
183
+ "contract",
184
+ "required",
185
+ "supported",
186
+ "peer"
187
+ ],
188
+ "properties": {
189
+ "contract": {
190
+ "type": "string"
191
+ },
192
+ "required": {
193
+ "type": "array",
194
+ "items": {
195
+ "type": "integer"
196
+ }
197
+ },
198
+ "supported": {
199
+ "type": "array",
200
+ "items": {
201
+ "type": "integer"
202
+ }
203
+ },
204
+ "peer": {
205
+ "type": "string"
206
+ },
207
+ "operation": {
208
+ "type": "string"
209
+ }
210
+ }
211
+ },
137
212
  "collection_configuration_invalid": {
138
213
  "type": "object",
139
214
  "required": [
@@ -226,6 +301,38 @@ const CONNECT_PROBLEM_DETAIL_SCHEMAS = {
226
301
  }
227
302
  }
228
303
  },
304
+ "durable_mutation_unsupported": {
305
+ "type": "object",
306
+ "required": [
307
+ "contract",
308
+ "required",
309
+ "supported",
310
+ "peer"
311
+ ],
312
+ "properties": {
313
+ "contract": {
314
+ "type": "string"
315
+ },
316
+ "required": {
317
+ "type": "array",
318
+ "items": {
319
+ "type": "integer"
320
+ }
321
+ },
322
+ "supported": {
323
+ "type": "array",
324
+ "items": {
325
+ "type": "integer"
326
+ }
327
+ },
328
+ "peer": {
329
+ "type": "string"
330
+ },
331
+ "operation": {
332
+ "type": "string"
333
+ }
334
+ }
335
+ },
229
336
  "insufficient_access": {
230
337
  "type": "object",
231
338
  "required": [
@@ -288,6 +395,17 @@ const CONNECT_PROBLEM_DETAIL_SCHEMAS = {
288
395
  }
289
396
  }
290
397
  },
398
+ "mutation_request_conflict": {
399
+ "type": "object",
400
+ "required": [
401
+ "request_id"
402
+ ],
403
+ "properties": {
404
+ "request_id": {
405
+ "type": "string"
406
+ }
407
+ }
408
+ },
291
409
  "operation_invalid": {
292
410
  "type": "object",
293
411
  "required": [
@@ -303,6 +421,17 @@ const CONNECT_PROBLEM_DETAIL_SCHEMAS = {
303
421
  "partial_result": {}
304
422
  }
305
423
  },
424
+ "operation_outcome_unknown": {
425
+ "type": "object",
426
+ "required": [
427
+ "request_id"
428
+ ],
429
+ "properties": {
430
+ "request_id": {
431
+ "type": "string"
432
+ }
433
+ }
434
+ },
306
435
  "rate_limited": {
307
436
  "type": "object",
308
437
  "required": [],
@@ -311,6 +440,38 @@ const CONNECT_PROBLEM_DETAIL_SCHEMAS = {
311
440
  "type": "integer"
312
441
  }
313
442
  }
443
+ },
444
+ "transport_protocol_incompatible": {
445
+ "type": "object",
446
+ "required": [
447
+ "contract",
448
+ "required",
449
+ "supported",
450
+ "peer"
451
+ ],
452
+ "properties": {
453
+ "contract": {
454
+ "type": "string"
455
+ },
456
+ "required": {
457
+ "type": "array",
458
+ "items": {
459
+ "type": "integer"
460
+ }
461
+ },
462
+ "supported": {
463
+ "type": "array",
464
+ "items": {
465
+ "type": "integer"
466
+ }
467
+ },
468
+ "peer": {
469
+ "type": "string"
470
+ },
471
+ "operation": {
472
+ "type": "string"
473
+ }
474
+ }
314
475
  }
315
476
  };
316
477
  export function isConnectProblemCode(code) {
package/dist/files.d.ts CHANGED
@@ -152,6 +152,8 @@ export interface FileTransferStatus {
152
152
  state: FileTransferState;
153
153
  received: number[];
154
154
  received_bytes: number;
155
+ /** Object-store receipts for the currently durable multipart parts. */
156
+ uploaded_parts: UploadedFilePart[];
155
157
  }
156
158
  export interface PrepareFileUploadPartRequest {
157
159
  protocol_version: 1;
package/dist/index.d.ts CHANGED
@@ -3,24 +3,31 @@ import type { ApplicationFileRequirement, CollectionFileDescriptor, FileCapabili
3
3
  import type { CollectionOperation } from "./operations.js";
4
4
  import type { ApplicationCapabilityRequirements } from "./capabilities.js";
5
5
  import type { ContractRequirement, ContractSetupChoice, TypePackProvision } from "./type-packs.js";
6
+ import type { ConfigurationProvision, ConfigurationRequirement } from "./collection-setup.js";
6
7
  import type { ApplicationAuthorizationProof } from "./application-authorization.js";
7
8
  export * from "./connect-problems.generated.js";
8
9
  export * from "./files.js";
9
10
  export * from "./operations.js";
11
+ export * from "./mutation-fingerprint.js";
12
+ export * from "./compatibility.js";
10
13
  export * from "./capabilities.js";
11
14
  export * from "./application-authorization.js";
12
15
  export * from "./type-packs.js";
16
+ export * from "./collection-setup.js";
13
17
  export declare const CONTROL_PROTOCOL_VERSION: 1;
14
- export declare const ENCRYPTED_RELAY_PROTOCOL_VERSION: 1;
15
- export declare const APPLICATION_AUTHORIZATION_PROTOCOL_VERSION: 2;
18
+ export { AUTHORIZATION_BINDING_PROTOCOL_VERSION as APPLICATION_AUTHORIZATION_PROTOCOL_VERSION } from "./compatibility.js";
16
19
  export declare const LOOPBACK_PROTOCOL_VERSION: 1;
17
20
  export declare const DEFAULT_LOOPBACK_PORT: 28485;
18
21
  export declare const RELAY_ENCRYPTION_SUITE: "P256-HKDF-SHA256-AES256GCM";
22
+ export declare const GRANT_ENCRYPTION_PROTOCOL_VERSION: 1;
19
23
  export declare const SYNC_PROTOCOL_VERSION: 1;
20
24
  export declare const CONTRACT_SETUP_CAPABILITY: "contract-setup-v1";
21
25
  export declare const FILE_RELAY_CAPABILITY: "file-relay-v1";
22
- export declare const RELAY_REQUIRED_CAPABILITIES: readonly ["application-authorization-v2", "authorization-activation", "encrypted-relay", "policy-ack"];
23
- export declare const RELAY_CAPABILITIES: readonly ["application-authorization-v2", "authorization-activation", "encrypted-relay", "policy-ack", "contract-setup-v1", "file-relay-v1"];
26
+ export declare const RELAY_REQUIRED_CAPABILITIES: readonly ["application-authorization-v4", "authorization-activation", "encrypted-relay", "policy-ack"];
27
+ export declare const MINIMUM_CONNECTOR_VERSION: "0.1.0-beta.33";
28
+ export declare const HOSTED_PROVIDER_REQUIRED_CAPABILITIES: readonly ["durable-mutation-journal-v1", "durable-file-lifecycle-v1"];
29
+ export declare const HOSTED_PROVIDER_CAPABILITIES: readonly ["durable-mutation-journal-v1", "durable-file-lifecycle-v1", "mutation-replay-after-credential-retirement-v1"];
30
+ export declare const RELAY_CAPABILITIES: readonly ["application-authorization-v4", "authorization-activation", "encrypted-relay", "policy-ack", "contract-setup-v1", "file-relay-v1"];
24
31
  export declare const AUTHORITY_PROOF_VERSION: 1;
25
32
  export declare const AUTHORITY_PROOF_ALGORITHM: "P256-SHA256";
26
33
  export declare const AUTHORITY_PROOF_DOMAIN: "mdbase-authority-request-proof-v1";
@@ -42,7 +49,7 @@ export declare const CONNECT_SCHEMA_IDS: {
42
49
  readonly dataContract: "https://mdbase.dev/schemas/v0.3/data-contract.schema.json";
43
50
  readonly eventActionInterop: "https://mdbase.dev/schemas/interop/v0.1/profile.schema.json";
44
51
  readonly protocol: "https://mdbase.dev/connect/schemas/connect-protocol.v1.json";
45
- readonly encryptedRelay: "https://mdbase.dev/connect/schemas/encrypted-relay.v1.json";
52
+ readonly encryptedRelay: "https://mdbase.dev/connect/schemas/encrypted-relay.v2.json";
46
53
  readonly files: "https://mdbase.dev/connect/schemas/files.v1.json";
47
54
  readonly sync: "https://mdbase.dev/connect/schemas/sync.v1.json";
48
55
  };
@@ -148,6 +155,8 @@ export interface NotificationWebhook {
148
155
  }
149
156
  export interface ApplicationRequirements {
150
157
  contracts: ContractRequirement[];
158
+ /** Collection extension values required before the application is ready. */
159
+ configuration?: ConfigurationRequirement[];
151
160
  /** Versioned semantic intent compiled by Connect into exact operations. */
152
161
  capabilities?: ApplicationCapabilityRequirements;
153
162
  /** Access boundary requested after compatibility and provisioning checks. */
@@ -159,6 +168,8 @@ export interface ApplicationRequirements {
159
168
  }
160
169
  export interface ApplicationProvisions {
161
170
  type_packs: TypePackProvision[];
171
+ /** Narrow semantic changes that may satisfy configuration requirements. */
172
+ configuration?: ConfigurationProvision[];
162
173
  }
163
174
  export interface GrantScope {
164
175
  /**
@@ -171,7 +182,7 @@ export interface GrantScope {
171
182
  }
172
183
  export interface RelayOperationRequest {
173
184
  type: "operation_request";
174
- protocol_version: 1;
185
+ protocol_version: 2;
175
186
  request_id: string;
176
187
  grant_id: string;
177
188
  collection_id: string;
@@ -181,29 +192,29 @@ export interface RelayOperationRequest {
181
192
  }
182
193
  export type RelayOperationResponse = {
183
194
  type: "operation_response";
184
- protocol_version: 1;
195
+ protocol_version: 2;
185
196
  request_id: string;
186
197
  ok: true;
187
198
  result: unknown;
188
199
  } | {
189
200
  type: "operation_response";
190
- protocol_version: 1;
201
+ protocol_version: 2;
191
202
  request_id: string;
192
203
  ok: false;
193
204
  problem: ConnectProblem;
194
205
  };
195
206
  export interface MdbaseOperationRequest<Input = unknown> {
196
- protocol_version: 1;
207
+ protocol_version: 2;
197
208
  request_id: string;
198
209
  input: Input;
199
210
  }
200
211
  export type MdbaseOperationResponse<Result = unknown> = {
201
- protocol_version: 1;
212
+ protocol_version: 2;
202
213
  request_id: string;
203
214
  ok: true;
204
215
  result: Result;
205
216
  } | {
206
- protocol_version: 1;
217
+ protocol_version: 2;
207
218
  request_id: string;
208
219
  ok: false;
209
220
  problem: ConnectProblem;
@@ -232,7 +243,9 @@ export interface GrantPolicy {
232
243
  application_authorization: ApplicationAuthorizationProof;
233
244
  }
234
245
  /** Presentation-only grant shape used outside a local authorization boundary. */
235
- export type GrantSummary = Omit<GrantPolicy, "application_authorization">;
246
+ export type GrantSummary = Omit<GrantPolicy, "application_authorization"> & {
247
+ contracts: import("./compatibility.js").ConnectContractRequirements;
248
+ };
236
249
  export interface GrantEncryption {
237
250
  protocol_version: 1;
238
251
  suite: typeof RELAY_ENCRYPTION_SUITE;
@@ -244,7 +257,7 @@ export interface GrantEncryption {
244
257
  connector_agreement_public_key: string;
245
258
  }
246
259
  export interface EncryptedRelayEnvelope {
247
- protocol_version: 1;
260
+ protocol_version: 2;
248
261
  suite: typeof RELAY_ENCRYPTION_SUITE;
249
262
  request_id: string;
250
263
  grant_id: string;
@@ -490,18 +503,22 @@ export interface RelayHello {
490
503
  protocol_version: 1;
491
504
  connector_version: string;
492
505
  capabilities: string[];
506
+ contract_support: import("./compatibility.js").ConnectContractSupport;
493
507
  }
494
508
  export interface RelayWelcome {
495
509
  type: "relay_welcome";
496
510
  protocol_version: 1;
497
511
  session_id: string;
498
512
  capabilities: string[];
513
+ contract_support: import("./compatibility.js").ConnectContractSupport;
499
514
  }
500
515
  export interface RelayIncompatible {
501
516
  type: "relay_incompatible";
502
517
  protocol_version: 1;
503
- code: "connector_upgrade_required";
518
+ code: "connector_upgrade_required" | "transport_protocol_incompatible" | "authorization_binding_incompatible" | "capability_contract_incompatible" | "durable_mutation_unsupported";
504
519
  message: string;
520
+ details?: import("./connect-problems.generated.js").ConnectProblemDetailsByCode["transport_protocol_incompatible" | "authorization_binding_incompatible" | "capability_contract_incompatible" | "durable_mutation_unsupported"];
521
+ minimum_connector_version: string;
505
522
  update_url: string;
506
523
  }
507
524
  export interface RelayPolicyApplied {
@@ -539,6 +556,8 @@ export interface AuthorizationActivationResponse {
539
556
  contracts: CollectionContractDescriptor[];
540
557
  /** Exact setup plan applied by the authority. */
541
558
  contract_setups: ContractSetupChoice[];
559
+ setup_assessment?: unknown;
560
+ provision_receipt?: unknown;
542
561
  error?: {
543
562
  code: string;
544
563
  message: string;
package/dist/index.js CHANGED
@@ -1,24 +1,36 @@
1
1
  export * from "./connect-problems.generated.js";
2
2
  export * from "./files.js";
3
3
  export * from "./operations.js";
4
+ export * from "./mutation-fingerprint.js";
5
+ export * from "./compatibility.js";
4
6
  export * from "./capabilities.js";
5
7
  export * from "./application-authorization.js";
6
8
  export * from "./type-packs.js";
9
+ export * from "./collection-setup.js";
7
10
  export const CONTROL_PROTOCOL_VERSION = 1;
8
- export const ENCRYPTED_RELAY_PROTOCOL_VERSION = 1;
9
- export const APPLICATION_AUTHORIZATION_PROTOCOL_VERSION = 2;
11
+ export { AUTHORIZATION_BINDING_PROTOCOL_VERSION as APPLICATION_AUTHORIZATION_PROTOCOL_VERSION } from "./compatibility.js";
10
12
  export const LOOPBACK_PROTOCOL_VERSION = 1;
11
13
  export const DEFAULT_LOOPBACK_PORT = 28_485;
12
14
  export const RELAY_ENCRYPTION_SUITE = "P256-HKDF-SHA256-AES256GCM";
15
+ export const GRANT_ENCRYPTION_PROTOCOL_VERSION = 1;
13
16
  export const SYNC_PROTOCOL_VERSION = 1;
14
17
  export const CONTRACT_SETUP_CAPABILITY = "contract-setup-v1";
15
18
  export const FILE_RELAY_CAPABILITY = "file-relay-v1";
16
19
  export const RELAY_REQUIRED_CAPABILITIES = [
17
- "application-authorization-v2",
20
+ "application-authorization-v4",
18
21
  "authorization-activation",
19
22
  "encrypted-relay",
20
23
  "policy-ack"
21
24
  ];
25
+ export const MINIMUM_CONNECTOR_VERSION = "0.1.0-beta.33";
26
+ export const HOSTED_PROVIDER_REQUIRED_CAPABILITIES = [
27
+ "durable-mutation-journal-v1",
28
+ "durable-file-lifecycle-v1"
29
+ ];
30
+ export const HOSTED_PROVIDER_CAPABILITIES = [
31
+ ...HOSTED_PROVIDER_REQUIRED_CAPABILITIES,
32
+ "mutation-replay-after-credential-retirement-v1"
33
+ ];
22
34
  export const RELAY_CAPABILITIES = [
23
35
  ...RELAY_REQUIRED_CAPABILITIES,
24
36
  CONTRACT_SETUP_CAPABILITY,
@@ -62,7 +74,7 @@ export const CONNECT_SCHEMA_IDS = {
62
74
  dataContract: "https://mdbase.dev/schemas/v0.3/data-contract.schema.json",
63
75
  eventActionInterop: "https://mdbase.dev/schemas/interop/v0.1/profile.schema.json",
64
76
  protocol: "https://mdbase.dev/connect/schemas/connect-protocol.v1.json",
65
- encryptedRelay: "https://mdbase.dev/connect/schemas/encrypted-relay.v1.json",
77
+ encryptedRelay: "https://mdbase.dev/connect/schemas/encrypted-relay.v2.json",
66
78
  files: "https://mdbase.dev/connect/schemas/files.v1.json",
67
79
  sync: "https://mdbase.dev/connect/schemas/sync.v1.json"
68
80
  };
package/dist/manifest.js CHANGED
@@ -40,7 +40,8 @@ export function validateAppManifest(value, options = {}) {
40
40
  const issues = [
41
41
  ...validateManifestOrigins(value, options.allowLocal === true),
42
42
  ...validateCapabilityRequirements(value),
43
- ...validateProvisionRequirements(value)
43
+ ...validateProvisionRequirements(value),
44
+ ...validateConfigurationSetup(value)
44
45
  ];
45
46
  return issues.length === 0 ? { valid: true, issues: [] } : invalid(issues);
46
47
  }
@@ -52,8 +53,16 @@ export function parseAppManifest(value, options = {}) {
52
53
  const manifest = structuredClone(value);
53
54
  return {
54
55
  ...manifest,
55
- requirements: manifest.requirements ?? { contracts: [] },
56
- provisions: manifest.provisions ?? { type_packs: [] },
56
+ requirements: {
57
+ contracts: [],
58
+ configuration: [],
59
+ ...manifest.requirements
60
+ },
61
+ provisions: {
62
+ type_packs: [],
63
+ configuration: [],
64
+ ...manifest.provisions
65
+ },
57
66
  notifications: manifest.notifications ?? { criteria: [] }
58
67
  };
59
68
  }
@@ -253,6 +262,95 @@ function validateProvisionRequirements(value) {
253
262
  }
254
263
  return issues;
255
264
  }
265
+ function validateConfigurationSetup(value) {
266
+ const manifest = asObject(value);
267
+ const requirements = asObject(manifest.requirements);
268
+ const provisions = asObject(manifest.provisions);
269
+ const requiredValues = Array.isArray(requirements.configuration)
270
+ ? requirements.configuration
271
+ : [];
272
+ const provisionValues = Array.isArray(provisions.configuration)
273
+ ? provisions.configuration
274
+ : [];
275
+ const issues = [];
276
+ const required = new Map();
277
+ for (const [index, candidate] of requiredValues.entries()) {
278
+ const requirement = asObject(candidate);
279
+ const id = String(requirement.id);
280
+ const path = String(requirement.path);
281
+ const pathError = configurationPointerError(path);
282
+ if (pathError) {
283
+ issues.push(issue(`/requirements/configuration/${index}/path`, "configurationPointer", pathError));
284
+ }
285
+ if (required.has(id)) {
286
+ issues.push(issue(`/requirements/configuration/${index}/id`, "uniqueRequirement", `duplicates configuration requirement ${id}`));
287
+ }
288
+ else {
289
+ required.set(id, { path, value: requirement.value, index });
290
+ }
291
+ }
292
+ const linked = new Set();
293
+ const contributionKeys = new Set();
294
+ for (const [index, candidate] of provisionValues.entries()) {
295
+ const provision = asObject(candidate);
296
+ const requirementId = String(provision.requirement);
297
+ const path = String(provision.path);
298
+ const pathError = configurationPointerError(path);
299
+ if (pathError) {
300
+ issues.push(issue(`/provisions/configuration/${index}/path`, "configurationPointer", pathError));
301
+ }
302
+ const requirement = required.get(requirementId);
303
+ if (!requirement) {
304
+ issues.push(issue(`/provisions/configuration/${index}/requirement`, "configurationRequirement", `references unknown configuration requirement ${requirementId}`));
305
+ continue;
306
+ }
307
+ if (linked.has(requirementId)) {
308
+ issues.push(issue(`/provisions/configuration/${index}/requirement`, "uniqueProvision", `configuration requirement ${requirementId} may have exactly one provision`));
309
+ }
310
+ linked.add(requirementId);
311
+ if (path !== requirement.path) {
312
+ issues.push(issue(`/provisions/configuration/${index}/path`, "configurationRequirement", `must equal /requirements/configuration/${requirement.index}/path`));
313
+ }
314
+ if (JSON.stringify(provision.value) !== JSON.stringify(requirement.value)) {
315
+ issues.push(issue(`/provisions/configuration/${index}/value`, "configurationRequirement", `must equal /requirements/configuration/${requirement.index}/value`));
316
+ }
317
+ const contributionKey = `${path}\0${JSON.stringify(provision.value)}`;
318
+ if (contributionKeys.has(contributionKey)) {
319
+ issues.push(issue(`/provisions/configuration/${index}`, "uniqueContribution", "duplicates another configuration path and value contribution"));
320
+ }
321
+ contributionKeys.add(contributionKey);
322
+ }
323
+ for (const [id, requirement] of required) {
324
+ if (!linked.has(id)) {
325
+ issues.push(issue(`/requirements/configuration/${requirement.index}`, "configurationProvision", `requires one provision linked by id ${id}`));
326
+ }
327
+ }
328
+ return issues;
329
+ }
330
+ function configurationPointerError(path) {
331
+ if (!path.startsWith("/") || new TextEncoder().encode(path).byteLength > 1024) {
332
+ return "must be a bounded RFC 6901 JSON pointer";
333
+ }
334
+ const encoded = path.slice(1).split("/");
335
+ if (encoded.length < 2 || encoded.length > 16) {
336
+ return "must address a value below an x-* extension namespace";
337
+ }
338
+ const segments = [];
339
+ for (const value of encoded) {
340
+ if (/~(?:[^01]|$)/u.test(value))
341
+ return "contains an invalid RFC 6901 escape";
342
+ const segment = value.replaceAll("~1", "/").replaceAll("~0", "~");
343
+ if (segment.length === 0
344
+ || segment === "-"
345
+ || /^\d+$/u.test(segment)
346
+ || [...segment].some((character) => /\p{Cc}/u.test(character)))
347
+ return "contains a disallowed object-key segment";
348
+ segments.push(segment);
349
+ }
350
+ return /^x-[a-z0-9][a-z0-9-]*$/u.test(segments[0])
351
+ ? undefined
352
+ : "must be inside a top-level x-* extension namespace";
353
+ }
256
354
  function validateCapabilityRequirements(value) {
257
355
  const manifest = asObject(value);
258
356
  const requirements = asObject(manifest.requirements);
@@ -285,8 +383,23 @@ function validateCapabilityRequirements(value) {
285
383
  }
286
384
  if (Array.isArray(provisions.type_packs)
287
385
  && provisions.type_packs.length > 0
288
- && !required.includes("definitions.type-pack.apply")) {
289
- issues.push(issue("/requirements/capabilities/required", "typePackCapability", "must require definitions.type-pack.apply when bundled type packs are declared"));
386
+ && !required.includes("collection.setup.apply")) {
387
+ issues.push(issue("/requirements/capabilities/required", "collectionSetupCapability", "must require collection.setup.apply when bundled type packs are declared"));
388
+ }
389
+ const hasConfigurationProvisions = Array.isArray(provisions.configuration)
390
+ && provisions.configuration.length > 0;
391
+ const hasSetupProvisions = hasConfigurationProvisions
392
+ || (Array.isArray(provisions.type_packs) && provisions.type_packs.length > 0);
393
+ if (hasConfigurationProvisions && !required.includes("collection.setup.apply")) {
394
+ issues.push(issue("/requirements/capabilities/required", "collectionSetupCapability", "must require collection.setup.apply when configuration provisions are declared"));
395
+ }
396
+ if (declared.has("collection.setup.apply")) {
397
+ if (requirements.access !== "full_collection") {
398
+ issues.push(issue("/requirements/access", "collectionSetupAccess", "must be full_collection for collection.setup.apply"));
399
+ }
400
+ if (!hasSetupProvisions) {
401
+ issues.push(issue("/provisions", "collectionSetupProvision", "must declare a configuration provision or type pack for collection.setup.apply"));
402
+ }
290
403
  }
291
404
  if (declared.has("notifications.background-delivery")
292
405
  && !Array.isArray(asObject(manifest.notifications).criteria)) {
@@ -0,0 +1,6 @@
1
+ import { type EncryptedOperation } from "./operations.js";
2
+ export declare const MUTATION_FINGERPRINT_SCHEMA_VERSION: 1;
3
+ export declare const MUTATION_FINGERPRINT_DOMAIN: "mdbase-connect mutation fingerprint v1\0";
4
+ export declare function canonicalMutationInput(input: unknown): string;
5
+ export declare function mutationFingerprintTranscript(operation: EncryptedOperation, input: unknown): Uint8Array<ArrayBuffer>;
6
+ export declare function mutationFingerprint(operation: EncryptedOperation, input: unknown): Promise<string>;