@mdbase-dev/connect 0.1.0-beta.94 → 0.1.0-beta.95

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 CHANGED
@@ -151,6 +151,46 @@ versioned capabilities in their manifest; they never maintain a parallel array
151
151
  of protocol operations. Use `getSnapshot()` and `subscribe()` directly or
152
152
  through your framework's external-store integration.
153
153
 
154
+ ### Legacy v1 and capability-group v2 declarations
155
+
156
+ The same SDK accepts both declaration versions without translating legacy exact
157
+ operations into broader groups. `requirements.capabilities.contract_version`
158
+ selects the semantics; an absent capability declaration means legacy v1.
159
+ `manifest_version` remains the document-format version, not this selector.
160
+
161
+ - **V1:** `authorize({ operations })`, `connection.requestOperations(operations)`,
162
+ and `session.ensureOperations(operations)` retain independent exact operations.
163
+ Incremental requests retain the existing exact grant. Legacy `files.actions`
164
+ and capability aliases (for example `records.update` and `files.read`) remain
165
+ supported. Bare registry/connection `authorize()` retains the predecessor's
166
+ `describe, changes, read, query` default; application sessions derive their
167
+ exact operation selection from the legacy capability declaration.
168
+ - **V2:** use `capabilities`, `requestCapabilities()`, and `ensureCapabilities()`.
169
+ Required groups are always included; omitted optional selection requests all
170
+ declared optional groups. Incremental requests retain already granted declared
171
+ groups. Files use `required`/`optional` actions.
172
+
173
+ Mixing `operations` and `capabilities`, or using an operations authorization API
174
+ with a v2 declaration, returns `invalid_application_manifest`, including for
175
+ JavaScript callers and already-sufficient grants. A rejected v2 request is never
176
+ retried as v1. Saved credentials and pending mutations are not translated or
177
+ replayed when selecting a semantic version; signed authorization retains its
178
+ registered declaration digest and exact recovery-transport requirements.
179
+
180
+ The SDK's `MdbaseAppManifest` and capability input types are explicit v1/v2
181
+ unions. `LegacyMdbaseAppManifest`, `LegacyApplicationRequirements`, and
182
+ `MdbaseApplicationRequirements` are available for explicit annotations. The
183
+ protocol package's canonical `ApplicationRequirements` remains v2-only.
184
+ Application sessions still require an explicit full-collection, versioned
185
+ capability declaration; no-capability legacy declarations can use the registry
186
+ or advanced session APIs.
187
+
188
+ This bridge is not a v2 readiness or rollout gate: structured v2 file,
189
+ notification, offline-runtime, and authority readiness evidence remains
190
+ incomplete. Legacy readiness aliases are evaluated only for v1. Positive server
191
+ and selected-authority v2 support gating remains separate work; this SDK change
192
+ does not advertise new support or authorize automatic consumer conversion.
193
+
154
194
  Before and during startup the session distinguishes `not_started`, `starting`,
155
195
  and `start_failed`; the last state carries the original typed problem and a
156
196
  later `start()` retries. `destroyed` is terminal. Once started, the session
@@ -304,22 +344,17 @@ documents both threat models. New or changed declaration criteria never silently
304
344
  broaden an existing grant: handle `notification_reauthorization_required` by
305
345
  running authorization again before retrying registration.
306
346
 
307
- Before opening a feature, inspect its exact authorization gap instead of
308
- waiting for an operation to fail:
347
+ Before opening a feature, request its declared capability group instead of
348
+ selecting individual transport operations:
309
349
 
310
350
  ```ts
311
- const required = ["read", "query", "update"] as const;
312
- const capabilities = connection.authorizationCapabilities([...required]);
313
- if (!capabilities.sufficient) {
314
- console.log("Needs", capabilities.missingOperations);
315
- await connection.requestOperations([...required]);
316
- }
351
+ await connection.requestCapabilities(["records.edit"]);
317
352
  ```
318
353
 
319
- `requestOperations()` is a no-op when the current grant is sufficient. When a
320
- replacement grant is needed, it requests the least-privilege union of the
321
- already granted operations and the missing requirements. An
322
- An `insufficient_access` problem carries the same `granted_operations`,
354
+ `requestCapabilities()` is a no-op when the current grant already includes the
355
+ complete group. When authorization is needed, required groups remain fixed and
356
+ the requested optional groups are expanded atomically. An
357
+ `insufficient_access` problem carries the internal `granted_operations`,
323
358
  `missing_operations`, and `required_operations` metadata with a `reauthorize`
324
359
  recovery action.
325
360
 
@@ -346,7 +381,7 @@ When the local connector definitively rejects an encrypted grant, the SDK does
346
381
  not bypass that decision through the relay. It classifies the error as requiring
347
382
  authorization, removes only the matching stale credential, and emits a `null`
348
383
  connection through `onConnectionChange()`. A subsequent
349
- `requestOperations()` call therefore starts authorization instead of trusting
384
+ `requestCapabilities()` call therefore starts authorization instead of trusting
350
385
  the stale cached capability list.
351
386
 
352
387
  Applications can register and maintain type definitions when their
@@ -0,0 +1,22 @@
1
+ import { type ApplicationCapabilityId, type ApplicationCapabilityRequirements, type LegacyApplicationCapabilityRequirements, type LegacyApplicationCapabilityId, type ApplicationRequirements, type LegacyApplicationRequirements, type MdbaseAppManifest, type LegacyMdbaseAppManifest, type CollectionOperation } from "@mdbase-dev/connect-protocol";
2
+ export declare const DEFAULT_OPERATIONS: CollectionOperation[];
3
+ /** SDK input compatibility boundary; protocol ApplicationRequirements remains v2-only. */
4
+ export type MdbaseApplicationManifest = MdbaseAppManifest | LegacyMdbaseAppManifest;
5
+ export type MdbaseApplicationRequirements = ApplicationRequirements | LegacyApplicationRequirements;
6
+ export type MdbaseApplicationCapabilityRequirements = ApplicationCapabilityRequirements | LegacyApplicationCapabilityRequirements;
7
+ export type MdbaseApplicationCapabilityId = ApplicationCapabilityId | LegacyApplicationCapabilityId;
8
+ interface MdbaseAuthorizationSelection {
9
+ operations?: CollectionOperation[];
10
+ capabilities?: MdbaseApplicationCapabilityId[];
11
+ }
12
+ export declare function semanticContractVersion(requirements: MdbaseApplicationRequirements): 1 | 2;
13
+ export declare function invalidAuthorizationSelection(message: string): never;
14
+ export declare function validateAuthorizationSelection(requirements: MdbaseApplicationRequirements, options: MdbaseAuthorizationSelection): void;
15
+ export declare function declaredOperations(requirements: MdbaseApplicationRequirements): CollectionOperation[];
16
+ export declare function authorizationOperations(application: {
17
+ requirements: MdbaseApplicationRequirements;
18
+ provisions?: MdbaseApplicationManifest["provisions"];
19
+ }, options: MdbaseAuthorizationSelection): CollectionOperation[];
20
+ export declare function retainedCapabilities(requirements: MdbaseApplicationRequirements, operations: CollectionOperation[]): MdbaseApplicationCapabilityId[];
21
+ export declare function authorizationFiles(requirements: MdbaseApplicationRequirements): import("@mdbase-dev/connect-protocol").ApplicationFileRequest | undefined;
22
+ export {};
@@ -0,0 +1,75 @@
1
+ import { APPLICATION_SETUP_OPERATIONS, applicationFileRequest, capabilityOperationsForContractVersion } from "@mdbase-dev/connect-protocol";
2
+ import { connectError } from "./errors.js";
3
+ export const DEFAULT_OPERATIONS = ["describe", "changes", "read", "query"];
4
+ export function semanticContractVersion(requirements) {
5
+ const version = requirements.capabilities === undefined ? 1 : requirements.capabilities?.contract_version;
6
+ if (version !== 1 && version !== 2)
7
+ invalidAuthorizationSelection("Unsupported declared capability contract version.");
8
+ return version;
9
+ }
10
+ export function invalidAuthorizationSelection(message) {
11
+ throw connectError("invalid_application_manifest", message, {
12
+ details: { issues: [{ path: "/requirements/capabilities", keyword: "authorizationSelection", message, params: {} }] }
13
+ });
14
+ }
15
+ export function validateAuthorizationSelection(requirements, options) {
16
+ if (options.operations !== undefined && options.capabilities !== undefined) {
17
+ invalidAuthorizationSelection("Specify operations or capabilities, not both.");
18
+ }
19
+ if (options.operations !== undefined && semanticContractVersion(requirements) !== 1) {
20
+ invalidAuthorizationSelection("Exact operations authorization is only supported for legacy v1 declarations.");
21
+ }
22
+ if (options.capabilities !== undefined) {
23
+ const declared = new Set([
24
+ ...(requirements.capabilities?.required ?? []),
25
+ ...(requirements.capabilities?.optional ?? [])
26
+ ]);
27
+ if (options.capabilities.some((id) => !declared.has(id)
28
+ || capabilityOperationsForContractVersion(semanticContractVersion(requirements), id) === undefined)) {
29
+ invalidAuthorizationSelection("Authorization can only include capabilities declared by the application.");
30
+ }
31
+ }
32
+ }
33
+ export function declaredOperations(requirements) {
34
+ const capabilities = requirements.capabilities;
35
+ if (!capabilities)
36
+ return [...DEFAULT_OPERATIONS];
37
+ return [...new Set([...capabilities.required, ...(capabilities.optional ?? [])].flatMap((id) => capabilityOperationsForContractVersion(capabilities.contract_version, id) ?? []))];
38
+ }
39
+ export function authorizationOperations(application, options) {
40
+ const { requirements } = application;
41
+ validateAuthorizationSelection(requirements, options);
42
+ // Legacy callers selected independent exact operations, including subsets of declarations.
43
+ if (semanticContractVersion(requirements) === 1) {
44
+ if (options.operations !== undefined)
45
+ return [...new Set(options.operations)];
46
+ if (options.capabilities === undefined)
47
+ return [...DEFAULT_OPERATIONS];
48
+ }
49
+ const declared = requirements.capabilities;
50
+ const selected = [...(declared?.required ?? []), ...(options.capabilities ?? declared?.optional ?? [])];
51
+ const operations = selected.flatMap((id) => capabilityOperationsForContractVersion(semanticContractVersion(requirements), id) ?? []);
52
+ const hasSetup = (application.provisions?.type_packs.length ?? 0) > 0
53
+ || (application.provisions?.configuration?.length ?? 0) > 0;
54
+ return [...new Set([
55
+ ...operations,
56
+ ...(semanticContractVersion(requirements) === 2 && hasSetup ? APPLICATION_SETUP_OPERATIONS : [])
57
+ ])];
58
+ }
59
+ export function retainedCapabilities(requirements, operations) {
60
+ return [...(requirements.capabilities?.required ?? []), ...(requirements.capabilities?.optional ?? [])].filter((id) => (capabilityOperationsForContractVersion(semanticContractVersion(requirements), id) ?? []).every((operation) => operations.includes(operation)));
61
+ }
62
+ export function authorizationFiles(requirements) {
63
+ const files = requirements.files;
64
+ if (!files)
65
+ return undefined;
66
+ if (semanticContractVersion(requirements) === 1) {
67
+ if (!("actions" in files) || "required" in files || "optional" in files) {
68
+ invalidAuthorizationSelection("Legacy file declarations must use exact files.actions.");
69
+ }
70
+ return { actions: [...files.actions], scope: structuredClone(files.scope) };
71
+ }
72
+ if ("actions" in files)
73
+ invalidAuthorizationSelection("V2 file declarations must use required/optional file actions.");
74
+ return applicationFileRequest(files);
75
+ }
@@ -1,5 +1,6 @@
1
- import { type ApplicationCapabilityId, type ConnectProblem, type JsonObject, type MdbaseAppManifest } from "@mdbase-dev/connect-protocol";
2
- import { type MdbaseEffectiveCapabilities } from "./capabilities.js";
1
+ import { type ConnectProblem, type JsonObject, type CollectionOperation } from "@mdbase-dev/connect-protocol";
2
+ import { type MdbaseApplicationManifest as MdbaseAppManifest, type MdbaseApplicationCapabilityId as ApplicationCapabilityId } from "./application-contract.js";
3
+ import { type ApplicationSessionContext } from "./structured-readiness.js";
3
4
  import type { MdbaseAuthorizationOutcome, MdbaseAuthorizeOptions, MdbaseConnection } from "./connection.js";
4
5
  import type { MdbaseConnectionInfo } from "./connection-types.js";
5
6
  import { type ConnectOutcome, type CollectionTypeProblemCode, type RegistrationProblemCode, type SessionProblemCode } from "./outcomes.js";
@@ -53,12 +54,6 @@ export interface MdbaseCollectionSetupUpdate {
53
54
  canApply: boolean;
54
55
  reason: string;
55
56
  }
56
- interface ApplicationSessionContext {
57
- collectionId: string;
58
- info: MdbaseConnectionInfo;
59
- capabilities: MdbaseEffectiveCapabilities;
60
- connections: MdbaseConnectionInfo[];
61
- }
62
57
  export type MdbaseApplicationSessionSnapshot = {
63
58
  status: "not_started";
64
59
  connections: MdbaseConnectionInfo[];
@@ -117,6 +112,7 @@ export declare class MdbaseApplicationSession<Frontmatter extends JsonObject = J
117
112
  private setupAssessment;
118
113
  private setupTypePackAdoptions;
119
114
  private verificationGeneration;
115
+ private readinessController?;
120
116
  private lifecycleGeneration;
121
117
  private readonly operationControllers;
122
118
  private startOperation;
@@ -142,17 +138,22 @@ export declare class MdbaseApplicationSession<Frontmatter extends JsonObject = J
142
138
  forget(collectionId: string): ConnectOutcome<void, SessionProblemCode>;
143
139
  authorize(target: "choose" | "selected" | {
144
140
  collectionId: string;
145
- }, options?: Omit<MdbaseAuthorizeOptions, "operations" | "returnTo" | "target">): Promise<ConnectOutcome<MdbaseAuthorizationOutcome<Frontmatter>, SessionProblemCode>>;
141
+ }, options?: Omit<MdbaseAuthorizeOptions, "returnTo" | "target">): Promise<ConnectOutcome<MdbaseAuthorizationOutcome<Frontmatter>, SessionProblemCode>>;
146
142
  handleAuthorizationCallback(callbackUrl: string | URL, options?: ConnectRequestOptions): Promise<ConnectOutcome<MdbaseConnection<Frontmatter>, SessionProblemCode>>;
147
143
  completeAuthorization(callbackUrl?: string | URL, options?: ConnectRequestOptions): Promise<ConnectOutcome<MdbaseConnection<Frontmatter>, SessionProblemCode>>;
148
144
  ensureCapabilities(capabilities: ApplicationCapabilityId[], options?: ConnectRequestOptions): Promise<ConnectOutcome<MdbaseAuthorizationOutcome<Frontmatter> | {
149
145
  kind: "unchanged";
150
146
  connection: MdbaseConnection<Frontmatter>;
151
147
  }, SessionProblemCode>>;
148
+ ensureOperations(operations: CollectionOperation[], options?: ConnectRequestOptions): Promise<ConnectOutcome<{
149
+ kind: "unchanged";
150
+ connection: MdbaseConnection<Frontmatter>;
151
+ } | MdbaseAuthorizationOutcome<Frontmatter>, SessionProblemCode>>;
152
152
  applyCollectionSetup(options?: ConnectRequestOptions): Promise<ConnectOutcome<MdbaseApplicationSessionSnapshot, CollectionTypeProblemCode | SessionProblemCode>>;
153
153
  private applyCollectionSetupStarted;
154
154
  private refresh;
155
155
  private verifySetup;
156
+ private publishReady;
156
157
  private collectionSetupInput;
157
158
  private context;
158
159
  private publish;
@@ -164,4 +165,3 @@ export declare class MdbaseApplicationSession<Frontmatter extends JsonObject = J
164
165
  private requireManifest;
165
166
  private requireApplication;
166
167
  }
167
- export {};
@@ -1,10 +1,11 @@
1
- import { capabilityOperations, operationsForApplicationCapabilities } from "@mdbase-dev/connect-protocol";
2
- /* The versioned protocol package is the sole capability-to-operation compiler. */
3
- import { effectiveCapabilities } from "./capabilities.js";
1
+ import { APPLICATION_SETUP_OPERATIONS } from "@mdbase-dev/connect-protocol";
2
+ import { declaredOperations, validateAuthorizationSelection } from "./application-contract.js";
3
+ import { applicationReadinessContext, collectionSetupInput, publishedStructuredReadiness, verifyStructuredReadiness, verificationKey, verificationValue } from "./structured-readiness.js";
4
4
  import { MdbaseConnectError, connectProblem } from "./errors.js";
5
5
  import { connectFailure, connectSuccess } from "./outcomes.js";
6
6
  import { createRequestBudget, resolveConnectTimeouts, withRequestBudget } from "./request-budget.js";
7
7
  import { defaultCallbackUrl } from "./runtime-utils.js";
8
+ import { declarationIdFromFamilyIdentity } from "./connect-authorization-helpers.js";
8
9
  import { MdbaseSession } from "./session.js";
9
10
  export class MdbaseMemoryVerificationStore {
10
11
  values = new Map();
@@ -37,6 +38,7 @@ export class MdbaseApplicationSession {
37
38
  setupAssessment = null;
38
39
  setupTypePackAdoptions = null;
39
40
  verificationGeneration = 0;
41
+ readinessController;
40
42
  lifecycleGeneration = 0;
41
43
  operationControllers = new Set();
42
44
  startOperation = null;
@@ -75,6 +77,7 @@ export class MdbaseApplicationSession {
75
77
  }
76
78
  abandonStart(operation) {
77
79
  operation.controller.abort();
80
+ this.readinessController?.abort();
78
81
  this.startOperation = null;
79
82
  this.lifecycleGeneration += 1;
80
83
  this.verificationGeneration += 1;
@@ -165,7 +168,7 @@ export class MdbaseApplicationSession {
165
168
  const base = new MdbaseSession(this.connect, {
166
169
  selection: this.options.selection,
167
170
  autoSelect: this.options.autoSelect,
168
- operations: operationsForSession(capabilities)
171
+ operations: operationsForSession(capabilities, manifest.value)
169
172
  });
170
173
  this.base = base;
171
174
  try {
@@ -209,6 +212,7 @@ export class MdbaseApplicationSession {
209
212
  return;
210
213
  this.lifecycleGeneration += 1;
211
214
  this.verificationGeneration += 1;
215
+ this.readinessController?.abort();
212
216
  this.startOperation?.controller.abort();
213
217
  this.startOperation = null;
214
218
  for (const controller of this.operationControllers)
@@ -305,6 +309,7 @@ export class MdbaseApplicationSession {
305
309
  }
306
310
  ensureCapabilities(capabilities, options) {
307
311
  return this.withLifecycleBase(options, (base, requestOptions, generation) => {
312
+ validateAuthorizationSelection(this.manifest.requirements, { ...options, capabilities });
308
313
  const manifestRequirements = this.manifest.requirements?.capabilities;
309
314
  const declared = new Set([
310
315
  ...(manifestRequirements?.required ?? []),
@@ -325,7 +330,7 @@ export class MdbaseApplicationSession {
325
330
  }
326
331
  })));
327
332
  }
328
- return base.ensureOperations(operationsForIds(capabilities), requestOptions).then(async (outcome) => {
333
+ return base.ensureCapabilities(capabilities, requestOptions).then(async (outcome) => {
329
334
  if (outcome.ok && outcome.value.kind === "connected" && this.lifecycleCurrent(generation)) {
330
335
  await this.refresh(true, requestOptions);
331
336
  }
@@ -333,6 +338,15 @@ export class MdbaseApplicationSession {
333
338
  });
334
339
  });
335
340
  }
341
+ ensureOperations(operations, options) {
342
+ return this.withLifecycleBase(options, async (base, requestOptions, generation) => {
343
+ validateAuthorizationSelection(this.manifest.requirements, { ...options, operations });
344
+ const outcome = await base.ensureOperations(operations, requestOptions);
345
+ if (outcome.ok && outcome.value.kind === "connected" && this.lifecycleCurrent(generation))
346
+ await this.refresh(true, requestOptions);
347
+ return outcome;
348
+ });
349
+ }
336
350
  applyCollectionSetup(options) {
337
351
  return this.withLifecycleBase(options, (_base, requestOptions, generation) => this.applyCollectionSetupStarted(requestOptions, generation));
338
352
  }
@@ -347,6 +361,7 @@ export class MdbaseApplicationSession {
347
361
  if (!connection) {
348
362
  return connectFailure(connectProblem("collection_not_ready", "The collection is not ready."));
349
363
  }
364
+ const verificationGeneration = this.verificationGeneration;
350
365
  const assessment = this.setupAssessment;
351
366
  const input = {
352
367
  ...this.collectionSetupInput(this.setupTypePackAdoptions ?? undefined),
@@ -364,6 +379,9 @@ export class MdbaseApplicationSession {
364
379
  }
365
380
  return connectFailure(connectProblem("session_destroyed", lifecycleProblemMessage("session_destroyed")));
366
381
  }
382
+ if (verificationGeneration !== this.verificationGeneration || this.connection() !== connection) {
383
+ return connectFailure(connectProblem("collection_not_ready", "The selected collection changed during setup."));
384
+ }
367
385
  if (!applied.ok) {
368
386
  if (applied.problem.code === "application_declaration_mismatch") {
369
387
  this.setupAssessment = null;
@@ -381,6 +399,7 @@ export class MdbaseApplicationSession {
381
399
  if (!this.base || !this.manifest)
382
400
  return;
383
401
  const current = this.base.getSnapshot();
402
+ this.readinessController?.abort();
384
403
  this.verificationGeneration += 1;
385
404
  const generation = this.verificationGeneration;
386
405
  this.setupAssessment = null;
@@ -401,18 +420,22 @@ export class MdbaseApplicationSession {
401
420
  return;
402
421
  }
403
422
  if (!context.capabilities.requiredAvailable
423
+ || context.readiness?.files.state === "requires_authorization"
404
424
  || !accessRequirementSatisfied(this.manifest, context.info)) {
405
425
  this.publish({ status: "authorization_required", ...context });
406
426
  return;
407
427
  }
408
- const managesSetup = this.manifest.requirements?.capabilities?.required
409
- .includes("collection.setup.apply") ?? false;
428
+ const managesSetup = (this.manifest.provisions?.type_packs.length ?? 0) > 0
429
+ || (this.manifest.provisions?.configuration?.length ?? 0) > 0;
410
430
  if (!managesSetup) {
411
- this.publish({ status: "ready", verification: "verified", ...context });
431
+ const verification = this.publishReady(context, generation, options);
432
+ if (awaitVerification)
433
+ await verification;
412
434
  return;
413
435
  }
414
436
  const key = verificationKey(this.manifest, current.collectionId);
415
- if (this.verificationStore.read(key) === verificationValue(this.manifest)) {
437
+ if (context.readiness?.contracts.state !== "checking"
438
+ && this.verificationStore.read(key) === verificationValue(this.manifest)) {
416
439
  this.publish({ status: "ready", verification: "cached", ...context });
417
440
  }
418
441
  else {
@@ -469,36 +492,51 @@ export class MdbaseApplicationSession {
469
492
  return;
470
493
  }
471
494
  this.verificationStore.write(verificationKey(manifest, context.collectionId), verificationValue(manifest));
472
- this.publish({ status: "ready", verification: "verified", ...context });
495
+ if (context.readiness)
496
+ context.readiness.setup = { state: "current", evidence: [{ source: "authority", fact: "Collection setup assessment is current." }] };
497
+ await this.publishReady(context, generation, options);
498
+ }
499
+ async publishReady(context, generation, options) {
500
+ const contracts = this.requireManifest().requirements?.contracts ?? [];
501
+ if (context.readiness && contracts.length) {
502
+ const connection = this.connection();
503
+ if (!connection || connection.collectionId !== context.collectionId)
504
+ return;
505
+ this.publish({ status: "checking_setup", ...context });
506
+ const controller = new AbortController();
507
+ this.readinessController?.abort();
508
+ this.readinessController = controller;
509
+ try {
510
+ if (!await verifyStructuredReadiness({
511
+ ...context, readiness: context.readiness, contracts, connection, controller, options,
512
+ requestMs: this.timeouts.requestMs,
513
+ isCurrent: () => generation === this.verificationGeneration && this.connection() === connection,
514
+ publish: result => this.publish({ ...result, ...context })
515
+ }))
516
+ return;
517
+ }
518
+ finally {
519
+ if (this.readinessController === controller)
520
+ this.readinessController = undefined;
521
+ }
522
+ }
523
+ if (generation === this.verificationGeneration)
524
+ this.publish({ status: "ready", verification: "verified", ...context });
473
525
  }
474
526
  collectionSetupInput(typePackAdoptions) {
475
527
  const manifest = this.requireManifest();
476
528
  const application = this.requireApplication();
477
- return {
478
- applicationId: declarationIdFromFamilyIdentity(application.family_identity),
479
- declarationDigest: `sha256:${application.manifest_digest}`,
480
- requirements: {
481
- configuration: manifest.requirements?.configuration ?? []
482
- },
483
- provisions: {
484
- configuration: manifest.provisions?.configuration ?? [],
485
- typePacks: manifest.provisions?.type_packs ?? []
486
- },
487
- ...(typePackAdoptions ? { typePackAdoptions } : {})
488
- };
529
+ return collectionSetupInput(manifest, declarationIdFromFamilyIdentity(application.family_identity), `sha256:${application.manifest_digest}`, typePackAdoptions);
489
530
  }
490
531
  context(connection) {
491
- const info = connection.info();
492
- return {
493
- collectionId: connection.collectionId,
494
- info,
495
- capabilities: effectiveCapabilities(this.requireManifest().requirements.capabilities, this.requireManifest(), info),
496
- connections: this.connect.connections()
497
- };
532
+ return applicationReadinessContext(this.requireManifest(), connection.collectionId, connection.info(), this.connect.connections());
498
533
  }
499
534
  publish(snapshot) {
500
535
  if (this.snapshot.status === "destroyed" && snapshot.status !== "destroyed")
501
536
  return;
537
+ if ("readiness" in snapshot && snapshot.readiness) {
538
+ snapshot = { ...snapshot, readiness: publishedStructuredReadiness(snapshot.readiness, snapshot.status, snapshot.status === "ready" ? snapshot.verification : undefined) };
539
+ }
502
540
  if (JSON.stringify(this.snapshot) === JSON.stringify(snapshot))
503
541
  return;
504
542
  this.snapshot = snapshot;
@@ -594,12 +632,13 @@ export class MdbaseApplicationSession {
594
632
  return this.application;
595
633
  }
596
634
  }
597
- function operationsForSession(requirements) {
598
- return operationsForApplicationCapabilities(requirements);
599
- }
600
- function operationsForIds(capabilities) {
601
- const definitions = capabilities.flatMap((id) => capabilityOperations(id));
602
- return [...new Set(definitions)];
635
+ function operationsForSession(requirements, manifest) {
636
+ const hasSetup = (manifest.provisions?.type_packs.length ?? 0) > 0
637
+ || (manifest.provisions?.configuration?.length ?? 0) > 0;
638
+ return [
639
+ ...declaredOperations(manifest.requirements),
640
+ ...(requirements.contract_version === 2 && hasSetup ? APPLICATION_SETUP_OPERATIONS : [])
641
+ ];
603
642
  }
604
643
  function definitionUpdate(provision, assessment) {
605
644
  const statusReason = assessment.status === "conflict"
@@ -659,28 +698,11 @@ function reviewableTypePackAdoptions(assessment) {
659
698
  }
660
699
  return adoptions;
661
700
  }
662
- function verificationKey(manifest, collectionId) {
663
- return `mdbase-application-session:v1:${manifest.id}:${collectionId}`;
664
- }
665
- function verificationValue(manifest) {
666
- return JSON.stringify({
667
- capabilities: manifest.requirements?.capabilities,
668
- requirements: manifest.requirements?.configuration ?? [],
669
- provisions: manifest.provisions ?? {}
670
- });
671
- }
672
701
  function accessRequirementSatisfied(manifest, connection) {
673
702
  return manifest.requirements?.access === "full_collection"
674
703
  && connection.scope.access === "full_collection"
675
704
  && connection.scope.contracts.length === 0;
676
705
  }
677
- function declarationIdFromFamilyIdentity(familyIdentity) {
678
- const prefix = "bundle:";
679
- if (!familyIdentity.startsWith(prefix) || familyIdentity.length === prefix.length) {
680
- throw new Error("The registered application has no valid declaration identity.");
681
- }
682
- return familyIdentity.slice(prefix.length);
683
- }
684
706
  function lifecycleProblemMessage(code) {
685
707
  if (code === "session_destroyed")
686
708
  return "This application session has been destroyed.";
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "file": "mdbase-connect.min.js",
3
- "integrity": "sha384-kDjazWo3W17cBn4WByvZ0jGGc1aqQxNTWglefSAMrEoEzGRgo8YNzRYSI3sQb8tx"
3
+ "integrity": "sha384-pt5qe5l2IuGgDjsId1oPNVT/zCR+0TLeEJNt6xKdZQSxAACXxaCqoxbZd37EriJh"
4
4
  }