@mdbase-dev/connect-protocol 0.1.0-beta.113 → 0.1.0-beta.115

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/dist/index.d.ts CHANGED
@@ -177,10 +177,13 @@ export interface ApplicationRequirements {
177
177
  access?: "contract" | "full_collection";
178
178
  /** Restrict authorization to durable provider-backed collections. */
179
179
  collection_kind?: "hosted";
180
+ /** Explicit identity consent. Omission discloses no account metadata. */
181
+ people?: import("./people.js").ApplicationPeopleRequirement;
180
182
  /** First-class non-Markdown file access requested independently of records. */
181
183
  files?: ApplicationFileRequirement;
182
184
  }
183
- export type LegacyApplicationRequirements = Omit<ApplicationRequirements, "capabilities" | "files" | "access"> & {
185
+ export { PEOPLE_PERMISSIONS, approvedPeoplePermissions, type AccountIdentity, type AccountProfile, type CurrentAccountResponse, type CollectionMemberProfile, type ApplicationPeopleRequirement, type PeoplePermission } from "./people.js";
186
+ export type LegacyApplicationRequirements = Omit<ApplicationRequirements, "capabilities" | "files" | "access" | "people"> & {
184
187
  access: "full_collection";
185
188
  capabilities?: LegacyApplicationCapabilityRequirements;
186
189
  files?: LegacyApplicationFileRequirement;
@@ -198,11 +201,7 @@ export interface ApplicationProvisions {
198
201
  configuration?: ConfigurationProvision[];
199
202
  }
200
203
  export interface GrantScope {
201
- /**
202
- * Legacy semantic scope payload retained for wire/storage compatibility.
203
- * Canonical application grants use an empty array; contracts remain available
204
- * through collection resources and operation-level semantic selectors.
205
- */
204
+ /** Legacy scope only; canonical grants use an empty array and discover contracts through resources and semantic selectors. */
206
205
  contracts: CollectionContractDescriptor[];
207
206
  /** New authority must be `full_collection`; `contract` is legacy-only. */
208
207
  access: "contract" | "full_collection";
@@ -784,6 +783,7 @@ export interface QueryRecord<Frontmatter extends JsonObject = JsonObject> {
784
783
  file: Partial<CollectionFileMetadata> & {
785
784
  path?: string;
786
785
  };
786
+ values?: JsonObject;
787
787
  /** Present when the authority returned a normalized contract projection. */
788
788
  contract?: DataContractViewIdentity;
789
789
  }
package/dist/index.js CHANGED
@@ -93,3 +93,4 @@ export const CONNECT_SCHEMA_IDS = {
93
93
  files: "https://mdbase.dev/connect/schemas/files.v1.json",
94
94
  sync: "https://mdbase.dev/connect/schemas/sync.v1.json"
95
95
  };
96
+ export { PEOPLE_PERMISSIONS, approvedPeoplePermissions } from "./people.js";
package/dist/manifest.js CHANGED
@@ -282,6 +282,17 @@ function validateProvisionRequirements(value) {
282
282
  }
283
283
  for (const [resourceIndex, resource] of declaredResources.entries()) {
284
284
  const source = String(resource.source);
285
+ if (resource.upgrade_from !== undefined) {
286
+ const baseline = asObject(resource.upgrade_from);
287
+ const path = `/provisions/type_packs/${packIndex}/manifest/resources/${resourceIndex}/upgrade_from`;
288
+ if (resource.kind !== "type" || resource.mode !== "seed") {
289
+ issues.push(issue(path, "seedUpgrade", "is only allowed for seed types"));
290
+ }
291
+ if (typeof baseline.document === "string"
292
+ && baseline.digest !== `sha256:${createHash("sha256").update(baseline.document).digest("hex")}`) {
293
+ issues.push(issue(`${path}/digest`, "digest", "does not match the baseline document"));
294
+ }
295
+ }
285
296
  const document = embedded.get(source);
286
297
  if (typeof document !== "string") {
287
298
  issues.push(issue(`/provisions/type_packs/${packIndex}/resources`, "resourceSet", `is missing manifest source ${source}`));
@@ -409,6 +420,13 @@ function validateCapabilityRequirements(value) {
409
420
  if (fileOverlap) {
410
421
  issues.push(issue("/requirements/files/optional", "disjoint", `must not repeat required file action ${fileOverlap}`));
411
422
  }
423
+ const people = asObject(requirements.people);
424
+ const requiredPeople = Array.isArray(people.required) ? people.required.map(String) : [];
425
+ const optionalPeople = Array.isArray(people.optional) ? people.optional.map(String) : [];
426
+ const peopleOverlap = optionalPeople.find((permission) => requiredPeople.includes(permission));
427
+ if (peopleOverlap) {
428
+ issues.push(issue("/requirements/people/optional", "disjoint", `must not repeat required people permission ${peopleOverlap}`));
429
+ }
412
430
  return issues;
413
431
  }
414
432
  // Frozen cross-field rules from c2596a6e; notably, absent capabilities skip
@@ -0,0 +1,33 @@
1
+ export declare const PEOPLE_PERMISSIONS: readonly ["identity", "members"];
2
+ export type PeoplePermission = (typeof PEOPLE_PERMISSIONS)[number];
3
+ /**
4
+ * Control-plane consent declared in the exact application manifest. Required
5
+ * permissions gate approval; the approving user chooses optional ones, and the
6
+ * grant records the result.
7
+ */
8
+ export interface ApplicationPeopleRequirement {
9
+ version: 1;
10
+ required?: PeoplePermission[];
11
+ optional?: PeoplePermission[];
12
+ }
13
+ /**
14
+ * Validates an approval choice against a declaration. An omitted choice
15
+ * approves every declared permission, matching file-action approval.
16
+ */
17
+ export declare function approvedPeoplePermissions(requirement: ApplicationPeopleRequirement | undefined, selected?: readonly string[]): PeoplePermission[];
18
+ /** Portable account identity, not a credential or collection permission. */
19
+ export interface AccountIdentity {
20
+ issuer: string;
21
+ subject: string;
22
+ }
23
+ export interface AccountProfile extends AccountIdentity {
24
+ name: string;
25
+ }
26
+ /** Wire response for the current account. */
27
+ export interface CurrentAccountResponse extends AccountProfile {
28
+ /** Where this account manages its person record; a route, never an identity. */
29
+ person_settings_url?: string;
30
+ }
31
+ export interface CollectionMemberProfile extends AccountProfile {
32
+ role: "owner" | "editor" | "viewer";
33
+ }
package/dist/people.js ADDED
@@ -0,0 +1,26 @@
1
+ export const PEOPLE_PERMISSIONS = ["identity", "members"];
2
+ /**
3
+ * Validates an approval choice against a declaration. An omitted choice
4
+ * approves every declared permission, matching file-action approval.
5
+ */
6
+ export function approvedPeoplePermissions(requirement, selected) {
7
+ if (!requirement) {
8
+ if (selected?.length)
9
+ throw new Error("People permissions require an application people declaration.");
10
+ return [];
11
+ }
12
+ const required = requirement.required ?? [];
13
+ const optional = requirement.optional ?? [];
14
+ const declared = new Set([...required, ...optional]);
15
+ if (requirement.version !== 1
16
+ || declared.size === 0
17
+ || declared.size !== required.length + optional.length
18
+ || [...declared].some((permission) => !PEOPLE_PERMISSIONS.includes(permission))) {
19
+ throw new Error("People requirements need unique, disjoint, known required and optional permissions.");
20
+ }
21
+ const chosen = new Set(selected ?? declared);
22
+ if (required.some((permission) => !chosen.has(permission)) || [...chosen].some((permission) => !declared.has(permission))) {
23
+ throw new Error("Required people permissions must be approved and optional people permissions must be declared.");
24
+ }
25
+ return PEOPLE_PERMISSIONS.filter((permission) => chosen.has(permission));
26
+ }
@@ -36,6 +36,11 @@ export declare const MDBASE_TIMER_FIRED_CONTRACT: {
36
36
  readonly digest: "sha256:41105be7a7abf33b31ced47e1e1965242236e40ccaea286b959b0a8c591f5642";
37
37
  };
38
38
  export interface TypePackManifestResource {
39
+ /** Explicit three-way upgrade; only valid for seed type resources. */
40
+ upgrade_from?: {
41
+ digest: string;
42
+ document: string;
43
+ };
39
44
  kind: "contract" | "type" | "schema";
40
45
  mode: "managed" | "seed";
41
46
  source: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mdbase-dev/connect-protocol",
3
- "version": "0.1.0-beta.113",
3
+ "version": "0.1.0-beta.115",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -122,6 +122,17 @@
122
122
  "const": "hosted",
123
123
  "description": "Require a durable provider-backed collection with direct sync support."
124
124
  },
125
+ "people": {
126
+ "type": "object",
127
+ "additionalProperties": false,
128
+ "required": ["version"],
129
+ "anyOf": [{ "required": ["required"] }, { "required": ["optional"] }],
130
+ "properties": {
131
+ "version": { "const": 1 },
132
+ "required": { "$ref": "#/$defs/peoplePermissions" },
133
+ "optional": { "$ref": "#/$defs/peoplePermissions" }
134
+ }
135
+ },
125
136
  "files": { "$ref": "#/$defs/fileRequirement" },
126
137
  "configuration": {
127
138
  "type": "array",
@@ -208,6 +219,10 @@
208
219
  "offline.replica"
209
220
  ]
210
221
  },
222
+ "peoplePermissions": {
223
+ "type": "array", "minItems": 1, "uniqueItems": true,
224
+ "items": { "enum": ["identity", "members"] }
225
+ },
211
226
  "fileRequirement": {
212
227
  "type": "object",
213
228
  "additionalProperties": false,
@@ -303,6 +318,15 @@
303
318
  "properties": {
304
319
  "kind": { "enum": ["contract", "type", "schema"] },
305
320
  "mode": { "enum": ["managed", "seed"] },
321
+ "upgrade_from": {
322
+ "type": "object",
323
+ "additionalProperties": false,
324
+ "required": ["digest", "document"],
325
+ "properties": {
326
+ "digest": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" },
327
+ "document": { "type": "string", "maxLength": 262144 }
328
+ }
329
+ },
306
330
  "source": { "type": "string", "minLength": 1, "maxLength": 240 },
307
331
  "target": { "type": "string", "minLength": 1, "maxLength": 240 },
308
332
  "digest": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" }