capdag 0.146.323 → 0.148.329

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.
@@ -158,10 +158,10 @@ function layoutForMode(mode) {
158
158
  algorithm: 'layered',
159
159
  'elk.direction': 'RIGHT',
160
160
  'elk.edgeRouting': 'POLYLINE',
161
- 'elk.layered.spacing.edgeEdgeBetweenLayers': 20,
162
- 'elk.layered.spacing.edgeNodeBetweenLayers': 30,
163
- 'elk.spacing.edgeEdge': 15,
164
- 'elk.spacing.edgeNode': 25,
161
+ 'elk.layered.spacing.edgeEdgeBetweenLayers': 30,
162
+ 'elk.layered.spacing.edgeNodeBetweenLayers': 40,
163
+ 'elk.spacing.edgeEdge': 25,
164
+ 'elk.spacing.edgeNode': 35,
165
165
  'elk.layered.crossingMinimization.strategy': 'LAYER_SWEEP',
166
166
  'elk.layered.nodePlacement.strategy': 'NETWORK_SIMPLEX',
167
167
  'elk.layered.considerModelOrder.strategy': 'NODES_AND_EDGES',
@@ -169,8 +169,8 @@ function layoutForMode(mode) {
169
169
  };
170
170
  if (mode === 'browse') {
171
171
  return Object.assign({}, base, {
172
- 'elk.layered.spacing.nodeNodeBetweenLayers': 150,
173
- 'elk.spacing.nodeNode': 50,
172
+ 'elk.layered.spacing.nodeNodeBetweenLayers': 180,
173
+ 'elk.spacing.nodeNode': 90,
174
174
  });
175
175
  }
176
176
  if (mode === 'strand') {
package/capdag.js CHANGED
@@ -3596,6 +3596,23 @@ class CartridgeCapSummary {
3596
3596
  }
3597
3597
  }
3598
3598
 
3599
+ /**
3600
+ * Cartridge cap group from registry
3601
+ */
3602
+ class CartridgeCapGroup {
3603
+ constructor(data) {
3604
+ if (!data.name) throw new Error('CapGroup missing name');
3605
+ this.name = data.name;
3606
+ this.caps = (data.caps || []).map(c => new CartridgeCapSummary(c.urn, c.title, c.description || ''));
3607
+ this.adapter_urns = data.adapter_urns || [];
3608
+ }
3609
+
3610
+ /** Flat caps in this group */
3611
+ allCaps() {
3612
+ return this.caps;
3613
+ }
3614
+ }
3615
+
3599
3616
  /**
3600
3617
  * Cartridge information from registry
3601
3618
  */
@@ -3610,7 +3627,8 @@ class CartridgeInfo {
3610
3627
  this.teamId = data.teamId || '';
3611
3628
  this.signedAt = data.signedAt || '';
3612
3629
  this.minAppVersion = data.minAppVersion || '';
3613
- this.caps = (data.caps || []).map(c => new CartridgeCapSummary(c.urn, c.title, c.description || ''));
3630
+ if (!Array.isArray(data.cap_groups)) throw new Error(`CartridgeInfo ${data.id || '?'}: missing cap_groups array`);
3631
+ this.cap_groups = data.cap_groups.map(g => new CartridgeCapGroup(g));
3614
3632
  this.categories = data.categories || [];
3615
3633
  this.tags = data.tags || [];
3616
3634
  // Versions with platform-specific builds
@@ -3618,6 +3636,21 @@ class CartridgeInfo {
3618
3636
  this.availableVersions = data.availableVersions || [];
3619
3637
  }
3620
3638
 
3639
+ /** All caps flattened across all cap_groups, deduplicated by URN */
3640
+ allCaps() {
3641
+ const seen = new Set();
3642
+ const result = [];
3643
+ for (const group of this.cap_groups) {
3644
+ for (const cap of group.caps) {
3645
+ if (!seen.has(cap.urn)) {
3646
+ seen.add(cap.urn);
3647
+ result.push(cap);
3648
+ }
3649
+ }
3650
+ }
3651
+ return result;
3652
+ }
3653
+
3621
3654
  /**
3622
3655
  * Check if cartridge is signed (has team_id and signed_at)
3623
3656
  */
@@ -3697,11 +3730,16 @@ class CartridgeRepoClient {
3697
3730
 
3698
3731
  const data = await response.json();
3699
3732
 
3700
- if (!data.cartridges || !Array.isArray(data.cartridges)) {
3701
- throw new Error(`Invalid cartridge registry response from ${repoUrl}: missing cartridges array`);
3733
+ if (!data.cartridges || typeof data.cartridges !== 'object') {
3734
+ throw new Error(`Invalid cartridge registry response from ${repoUrl}: missing cartridges object`);
3702
3735
  }
3703
3736
 
3704
- return data.cartridges.map(p => new CartridgeInfo(p));
3737
+ // Registry stores cartridges as an object keyed by id; normalize to array.
3738
+ return Object.entries(data.cartridges).map(([id, c]) => new CartridgeInfo({
3739
+ ...c,
3740
+ id,
3741
+ version: c.latestVersion
3742
+ }));
3705
3743
  }
3706
3744
 
3707
3745
  /**
@@ -3713,7 +3751,7 @@ class CartridgeRepoClient {
3713
3751
  for (const cartridge of cartridges) {
3714
3752
  cache.cartridges.set(cartridge.id, cartridge);
3715
3753
 
3716
- for (const cap of cartridge.caps) {
3754
+ for (const cap of cartridge.allCaps()) {
3717
3755
  if (!cache.capToCartridges.has(cap.urn)) {
3718
3756
  cache.capToCartridges.set(cap.urn, []);
3719
3757
  }
@@ -3773,7 +3811,7 @@ class CartridgeRepoClient {
3773
3811
  const cartridge = cache.cartridges.get(cartridgeId);
3774
3812
  if (!cartridge) continue;
3775
3813
 
3776
- const capInfo = cartridge.caps.find(c => c.urn === capUrn);
3814
+ const capInfo = cartridge.allCaps().find(c => c.urn === capUrn);
3777
3815
  if (!capInfo) continue;
3778
3816
 
3779
3817
  const pageUrl = cartridge.pageUrl || cache.repoUrl;
@@ -3889,6 +3927,9 @@ class CartridgeRepoServer {
3889
3927
  if (!build.package || !build.package.name) {
3890
3928
  throw new Error(`Cartridge ${id} v${version}: build[${i}] (${build.platform}) missing package.name`);
3891
3929
  }
3930
+ if (!build.package.url) {
3931
+ throw new Error(`Cartridge ${id} v${version}: build[${i}] (${build.platform}) missing package.url`);
3932
+ }
3892
3933
  }
3893
3934
  }
3894
3935
 
@@ -3942,7 +3983,10 @@ class CartridgeRepoServer {
3942
3983
  teamId: cartridge.teamId,
3943
3984
  signedAt: versionData.releaseDate,
3944
3985
  minAppVersion: versionData.minAppVersion || cartridge.minAppVersion,
3945
- caps: cartridge.caps || [],
3986
+ cap_groups: (() => {
3987
+ if (!Array.isArray(cartridge.cap_groups)) throw new Error(`Cartridge ${id}: missing cap_groups array`);
3988
+ return cartridge.cap_groups;
3989
+ })(),
3946
3990
  categories: cartridge.categories,
3947
3991
  tags: cartridge.tags,
3948
3992
  versions: cartridge.versions,
@@ -3977,12 +4021,13 @@ class CartridgeRepoServer {
3977
4021
  const cartridges = this.transformToCartridgeArray();
3978
4022
  const lowerQuery = query.toLowerCase();
3979
4023
 
3980
- return cartridges.filter(p =>
3981
- p.name.toLowerCase().includes(lowerQuery) ||
3982
- p.description.toLowerCase().includes(lowerQuery) ||
3983
- p.tags.some(t => t.toLowerCase().includes(lowerQuery)) ||
3984
- p.caps.some(c => c.urn.toLowerCase().includes(lowerQuery) || c.title.toLowerCase().includes(lowerQuery))
3985
- );
4024
+ return cartridges.filter(p => {
4025
+ const allCaps = (p.cap_groups || []).flatMap(g => g.caps || []);
4026
+ return p.name.toLowerCase().includes(lowerQuery) ||
4027
+ p.description.toLowerCase().includes(lowerQuery) ||
4028
+ p.tags.some(t => t.toLowerCase().includes(lowerQuery)) ||
4029
+ allCaps.some(c => c.urn.toLowerCase().includes(lowerQuery) || c.title.toLowerCase().includes(lowerQuery));
4030
+ });
3986
4031
  }
3987
4032
 
3988
4033
  /**
@@ -3998,7 +4043,9 @@ class CartridgeRepoServer {
3998
4043
  */
3999
4044
  getCartridgesByCap(capUrn) {
4000
4045
  const cartridges = this.transformToCartridgeArray();
4001
- return cartridges.filter(p => p.caps.some(c => c.urn === capUrn));
4046
+ return cartridges.filter(p =>
4047
+ (p.cap_groups || []).some(g => (g.caps || []).some(c => c.urn === capUrn))
4048
+ );
4002
4049
  }
4003
4050
  }
4004
4051
 
package/capdag.test.js CHANGED
@@ -1651,16 +1651,22 @@ const sampleRegistry = {
1651
1651
  minAppVersion: '1.0.0',
1652
1652
  categories: ['document'],
1653
1653
  tags: ['pdf', 'extractor'],
1654
- caps: [
1654
+ cap_groups: [
1655
1655
  {
1656
- urn: 'cap:in="media:pdf";op=disbind;out="media:disbound-page;textable;list"',
1657
- title: 'Disbind PDF',
1658
- description: 'Extract pages'
1659
- },
1660
- {
1661
- urn: 'cap:in="media:pdf";op=extract_metadata;out="media:file-metadata;textable;record"',
1662
- title: 'Extract Metadata',
1663
- description: 'Get PDF metadata'
1656
+ name: 'pdf-processing',
1657
+ adapter_urns: ['media:pdf'],
1658
+ caps: [
1659
+ {
1660
+ urn: 'cap:in="media:pdf";op=disbind;out="media:disbound-page;textable;list"',
1661
+ title: 'Disbind PDF',
1662
+ description: 'Extract pages'
1663
+ },
1664
+ {
1665
+ urn: 'cap:in="media:pdf";op=extract_metadata;out="media:file-metadata;textable;record"',
1666
+ title: 'Extract Metadata',
1667
+ description: 'Get PDF metadata'
1668
+ }
1669
+ ]
1664
1670
  }
1665
1671
  ],
1666
1672
  latestVersion: '0.81.5325',
@@ -1673,6 +1679,7 @@ const sampleRegistry = {
1673
1679
  platform: 'darwin-arm64',
1674
1680
  package: {
1675
1681
  name: 'pdfcartridge-0.81.5325.pkg',
1682
+ url: 'https://cartridges.machinefabric.com/pdfcartridge/0.81.5325/pdfcartridge-0.81.5325.pkg',
1676
1683
  sha256: '9b68724eb9220ecf01e8ed4f5f80c594fbac2239bc5bf675005ec882ecc5eba0',
1677
1684
  size: 5187485
1678
1685
  }
@@ -1689,11 +1696,17 @@ const sampleRegistry = {
1689
1696
  minAppVersion: '1.0.0',
1690
1697
  categories: ['text'],
1691
1698
  tags: ['txt', 'text'],
1692
- caps: [
1699
+ cap_groups: [
1693
1700
  {
1694
- urn: 'cap:in="media:txt;textable";op=disbind;out="media:disbound-page;textable;list"',
1695
- title: 'Disbind Text',
1696
- description: 'Extract text pages'
1701
+ name: 'text-processing',
1702
+ adapter_urns: ['media:txt;textable'],
1703
+ caps: [
1704
+ {
1705
+ urn: 'cap:in="media:txt;textable";op=disbind;out="media:disbound-page;textable;list"',
1706
+ title: 'Disbind Text',
1707
+ description: 'Extract text pages'
1708
+ }
1709
+ ]
1697
1710
  }
1698
1711
  ],
1699
1712
  latestVersion: '0.54.6408',
@@ -1706,6 +1719,7 @@ const sampleRegistry = {
1706
1719
  platform: 'darwin-arm64',
1707
1720
  package: {
1708
1721
  name: 'txtcartridge-0.54.6408.pkg',
1722
+ url: 'https://cartridges.machinefabric.com/txtcartridge/0.54.6408/txtcartridge-0.54.6408.pkg',
1709
1723
  sha256: 'abc123',
1710
1724
  size: 821000
1711
1725
  }
@@ -1725,13 +1739,17 @@ function test320_cartridgeInfoConstruction() {
1725
1739
  description: 'A test',
1726
1740
  teamId: 'TEAM123',
1727
1741
  signedAt: '2026-01-01',
1728
- caps: [{urn: 'cap:in="media:void";op=test;out="media:void"', title: 'Test', description: ''}],
1742
+ cap_groups: [{
1743
+ name: 'test-group',
1744
+ adapter_urns: ['media:void'],
1745
+ caps: [{urn: 'cap:in="media:void";op=test;out="media:void"', title: 'Test', description: ''}]
1746
+ }],
1729
1747
  versions: {
1730
1748
  '1.0.0': {
1731
1749
  releaseDate: '2026-01-01',
1732
1750
  changelog: ['Initial'],
1733
1751
  minAppVersion: '1.0.0',
1734
- builds: [{platform: 'darwin-arm64', package: {name: 'test-1.0.0.pkg', sha256: 'abc123', size: 100}}]
1752
+ builds: [{platform: 'darwin-arm64', package: {name: 'test-1.0.0.pkg', url: 'https://cartridges.machinefabric.com/testcartridge/1.0.0/test-1.0.0.pkg', sha256: 'abc123', size: 100}}]
1735
1753
  }
1736
1754
  },
1737
1755
  availableVersions: ['1.0.0']
@@ -1739,31 +1757,32 @@ function test320_cartridgeInfoConstruction() {
1739
1757
  const cartridge = new CartridgeInfo(data);
1740
1758
  assert(cartridge.id === 'testcartridge', 'ID should match');
1741
1759
  assert(cartridge.teamId === 'TEAM123', 'Team ID should match');
1742
- assert(cartridge.caps.length === 1, 'Should have 1 cap');
1743
- assert(cartridge.caps[0].urn === 'cap:in="media:void";op=test;out="media:void"', 'Cap URN should match');
1760
+ assert(cartridge.cap_groups.length === 1, 'Should have 1 cap_group');
1761
+ assert(cartridge.cap_groups[0].caps[0].urn === 'cap:in="media:void";op=test;out="media:void"', 'Cap URN should match');
1762
+ assert(cartridge.allCaps().length === 1, 'allCaps() should return 1 cap');
1744
1763
  }
1745
1764
 
1746
1765
  // TEST321: CartridgeInfo.is_signed() returns true when signature is present
1747
1766
  function test321_cartridgeInfoIsSigned() {
1748
- const signed = new CartridgeInfo({id: 'test', teamId: 'TEAM', signedAt: '2026-01-01', caps: []});
1767
+ const signed = new CartridgeInfo({id: 'test', teamId: 'TEAM', signedAt: '2026-01-01', cap_groups: []});
1749
1768
  assert(signed.isSigned() === true, 'Cartridge with teamId and signedAt should be signed');
1750
1769
 
1751
- const unsigned1 = new CartridgeInfo({id: 'test', teamId: '', signedAt: '2026-01-01', caps: []});
1770
+ const unsigned1 = new CartridgeInfo({id: 'test', teamId: '', signedAt: '2026-01-01', cap_groups: []});
1752
1771
  assert(unsigned1.isSigned() === false, 'Cartridge without teamId should not be signed');
1753
1772
 
1754
- const unsigned2 = new CartridgeInfo({id: 'test', teamId: 'TEAM', signedAt: '', caps: []});
1773
+ const unsigned2 = new CartridgeInfo({id: 'test', teamId: 'TEAM', signedAt: '', cap_groups: []});
1755
1774
  assert(unsigned2.isSigned() === false, 'Cartridge without signedAt should not be signed');
1756
1775
  }
1757
1776
 
1758
1777
  // TEST322: CartridgeInfo.build_for_platform() returns the build matching the current platform
1759
1778
  function test322_cartridgeInfoBuildForPlatform() {
1760
1779
  const withBuilds = new CartridgeInfo({
1761
- id: 'test', version: '1.0.0', caps: [],
1780
+ id: 'test', version: '1.0.0', cap_groups: [],
1762
1781
  versions: {
1763
1782
  '1.0.0': {
1764
1783
  builds: [
1765
- {platform: 'darwin-arm64', package: {name: 'test-darwin.pkg', sha256: 'abc', size: 100}},
1766
- {platform: 'linux-x86_64', package: {name: 'test-linux.pkg', sha256: 'def', size: 200}}
1784
+ {platform: 'darwin-arm64', package: {name: 'test-darwin.pkg', url: 'https://cartridges.machinefabric.com/test/1.0.0/test-darwin.pkg', sha256: 'abc', size: 100}},
1785
+ {platform: 'linux-x86_64', package: {name: 'test-linux.pkg', url: 'https://cartridges.machinefabric.com/test/1.0.0/test-linux.pkg', sha256: 'def', size: 200}}
1767
1786
  ]
1768
1787
  }
1769
1788
  },
@@ -1784,7 +1803,7 @@ function test322_cartridgeInfoBuildForPlatform() {
1784
1803
  assert(platforms.includes('darwin-arm64'), 'Should include darwin-arm64');
1785
1804
  assert(platforms.includes('linux-x86_64'), 'Should include linux-x86_64');
1786
1805
 
1787
- const noBuilds = new CartridgeInfo({id: 'test', version: '1.0.0', caps: [], versions: {}, availableVersions: []});
1806
+ const noBuilds = new CartridgeInfo({id: 'test', version: '1.0.0', cap_groups: [], versions: {}, availableVersions: []});
1788
1807
  assert(noBuilds.buildForPlatform('darwin-arm64') === null, 'Should return null when no versions');
1789
1808
  assert(noBuilds.availablePlatforms().length === 0, 'Should have no platforms');
1790
1809
  }
@@ -1837,8 +1856,9 @@ function test324_cartridgeRepoServerTransformToArray() {
1837
1856
  assert(pdf.versions['0.81.5325'].builds[0].package.sha256 === '9b68724eb9220ecf01e8ed4f5f80c594fbac2239bc5bf675005ec882ecc5eba0', 'Should have package SHA256');
1838
1857
  assert(Array.isArray(pdf.availableVersions), 'Should have availableVersions array');
1839
1858
  assert(pdf.availableVersions.includes('0.81.5325'), 'Should include latest version');
1840
- assert(Array.isArray(pdf.caps), 'Should have caps array');
1841
- assert(pdf.caps.length === 2, 'Should have 2 caps');
1859
+ assert(Array.isArray(pdf.cap_groups), 'Should have cap_groups array');
1860
+ assert(pdf.cap_groups.length === 1, 'Should have 1 cap_group');
1861
+ assert(pdf.cap_groups[0].caps.length === 2, 'Should have 2 caps in the group');
1842
1862
  }
1843
1863
 
1844
1864
  // TEST325: CartridgeRepoServer.get_cartridges() returns all parsed cartridges
package/package.json CHANGED
@@ -40,5 +40,5 @@
40
40
  "pretest": "npm run build:parser",
41
41
  "test": "node capdag.test.js"
42
42
  },
43
- "version": "0.146.323"
43
+ "version": "0.148.329"
44
44
  }