@mdbase-dev/connect-dev 0.1.0-beta.27 → 0.1.0-beta.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +61 -1
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -30,6 +30,9 @@ export function validateAppManifest(value, options = {}) {
30
30
  const originResult = validateManifestOrigins(value, options.allowLocal === true);
31
31
  if (!originResult.valid)
32
32
  return originResult;
33
+ const capabilityResult = validateCapabilityRequirements(value);
34
+ if (!capabilityResult.valid)
35
+ return capabilityResult;
33
36
  return validateProvisionRequirements(value);
34
37
  }
35
38
  export function validateDataContract(value) {
@@ -542,6 +545,63 @@ function validateProvisionRequirements(value) {
542
545
  }
543
546
  return { valid: true, issues: [] };
544
547
  }
548
+ function validateCapabilityRequirements(value) {
549
+ const manifest = asObject(value);
550
+ const requirements = asObject(manifest.requirements);
551
+ const capabilities = asObject(requirements.capabilities);
552
+ if (Object.keys(capabilities).length === 0)
553
+ return { valid: true, issues: [] };
554
+ const required = Array.isArray(capabilities.required)
555
+ ? capabilities.required.map(String)
556
+ : [];
557
+ const optional = Array.isArray(capabilities.optional)
558
+ ? capabilities.optional.map(String)
559
+ : [];
560
+ if (new Set(required).size !== required.length) {
561
+ return semanticIssue("/requirements/capabilities/required", "must not contain duplicates");
562
+ }
563
+ if (new Set(optional).size !== optional.length) {
564
+ return semanticIssue("/requirements/capabilities/optional", "must not contain duplicates");
565
+ }
566
+ const overlap = optional.find((capability) => required.includes(capability));
567
+ if (overlap) {
568
+ return semanticIssue("/requirements/capabilities/optional", `must not repeat required capability ${overlap}`);
569
+ }
570
+ const declared = new Set([...required, ...optional]);
571
+ const provisions = asObject(manifest.provisions);
572
+ if (Array.isArray(requirements.contracts)
573
+ && requirements.contracts.length > 0
574
+ && requirements.access !== "full_collection"
575
+ && !required.includes("definitions.contracts.current")) {
576
+ return semanticIssue("/requirements/capabilities/required", "must require definitions.contracts.current for contract-scoped requirements");
577
+ }
578
+ if (declared.has("definitions.type-pack.apply")) {
579
+ if (requirements.access !== "full_collection") {
580
+ return semanticIssue("/requirements/access", "must be full_collection for definitions.type-pack.apply");
581
+ }
582
+ if (!required.includes("definitions.type-pack.apply")) {
583
+ return semanticIssue("/requirements/capabilities/required", "must require definitions.type-pack.apply when it is declared");
584
+ }
585
+ if (!Array.isArray(provisions.type_packs) || provisions.type_packs.length === 0) {
586
+ return semanticIssue("/provisions/type_packs", "must contain a pack for definitions.type-pack.apply");
587
+ }
588
+ }
589
+ if (declared.has("notifications.background-delivery")
590
+ && !Array.isArray(asObject(manifest.notifications).criteria)) {
591
+ return semanticIssue("/notifications/criteria", "must be declared for notifications.background-delivery");
592
+ }
593
+ const fileRequirement = asObject(requirements.files);
594
+ const fileActions = Array.isArray(fileRequirement.actions)
595
+ ? new Set(fileRequirement.actions.map(String))
596
+ : new Set();
597
+ for (const action of ["list", "read", "add", "replace", "move", "delete"]) {
598
+ const capability = `files.${action}`;
599
+ if (declared.has(capability) !== fileActions.has(action)) {
600
+ return semanticIssue("/requirements/capabilities", `${capability} and requirements.files.actions.${action} must be declared together`);
601
+ }
602
+ }
603
+ return { valid: true, issues: [] };
604
+ }
545
605
  function localManifestSchemaCandidate(value) {
546
606
  const candidate = clone(value);
547
607
  const object = asObject(candidate);
@@ -601,7 +661,7 @@ function sandboxDescription(value = {}) {
601
661
  collection_id: value.collection_id ?? "01900000-0000-7000-8000-000000000001",
602
662
  display_name: value.display_name ?? "Developer sandbox",
603
663
  spec_version: value.spec_version ?? "0.3.0",
604
- operations: value.operations ?? ["describe", "changes", "read", "query", "list_views", "execute_view", "read_view_source", "validate", "create", "update", "delete", "rename", "create_view_source", "update_view_source", "delete_view_source", "read_type", "create_type", "update_type", "apply_type_pack"],
664
+ operations: value.operations ?? ["describe", "changes", "read", "query", "list_views", "execute_view", "read_view_source", "validate", "create", "update", "delete", "rename", "create_view_source", "update_view_source", "delete_view_source", "read_type", "create_type", "update_type", "assess_type_pack", "apply_type_pack"],
605
665
  change_cursor: value.change_cursor ?? 0,
606
666
  types: clone(value.types ?? []),
607
667
  contracts: clone(value.contracts ?? [])
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mdbase-dev/connect-dev",
3
- "version": "0.1.0-beta.27",
3
+ "version": "0.1.0-beta.28",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -26,8 +26,8 @@
26
26
  "dependencies": {
27
27
  "ajv": "^8.20.0",
28
28
  "yaml": "^2.9.0",
29
- "@mdbase-dev/connect": "0.1.0-beta.27",
30
- "@mdbase-dev/connect-protocol": "0.1.0-beta.27"
29
+ "@mdbase-dev/connect": "0.1.0-beta.28",
30
+ "@mdbase-dev/connect-protocol": "0.1.0-beta.28"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/node": "^24.0.0",