capdag 1.205.540 → 1.211.563

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.
@@ -2087,7 +2087,7 @@ function buildRunGraphData(data) {
2087
2087
  // labels the anchor→entry edge on the first body.
2088
2088
  //
2089
2089
  // Without such a preceding sequence cap, the source itself is
2090
- // already a list (e.g. `media:pdf;list` source_media_urn) and the
2090
+ // already a list (e.g. `media:ext=pdf;list` source_media_urn) and the
2091
2091
  // ForEach iterates it directly.
2092
2092
  let seqProducerStepIdx = -1;
2093
2093
  let seqProducerStep = null;
package/capdag.js CHANGED
@@ -6069,6 +6069,14 @@ function probeCartridgeCapGroups(entryPath) {
6069
6069
  child.on('error', (e) => finish(new Error(`cartridge ${entryPath} spawn error: ${e.message}`)));
6070
6070
  child.on('close', () => finish(new Error(`cartridge ${entryPath} HELLO failed: connection closed before receiving HELLO`)));
6071
6071
 
6072
+ // A cartridge that exits or closes its stdin before reading our HELLO
6073
+ // surfaces the broken pipe as an ASYNCHRONOUS 'error' event on the pipe
6074
+ // (EPIPE), not as a synchronous throw from write(). Without a listener,
6075
+ // Node treats that as an unhandled 'error' and aborts the whole host
6076
+ // process. Catch it here and route it through finish() so a single
6077
+ // misbehaving cartridge fails its own probe instead of killing discovery.
6078
+ child.stdin.on('error', (e) => finish(new Error(`cartridge ${entryPath} HELLO failed: ${e.message}`)));
6079
+
6072
6080
  timer = setTimeout(() => finish(new Error(`cartridge ${entryPath} HELLO failed: timeout`)), 5000);
6073
6081
 
6074
6082
  // Send our HELLO first (host side).
@@ -6120,6 +6128,9 @@ function probeCartridgeCapGroups(entryPath) {
6120
6128
  buf = Buffer.concat([buf, chunk]);
6121
6129
  tryParse();
6122
6130
  });
6131
+ // Guard the read side too: an error on the child's stdout (e.g. the pipe
6132
+ // breaking mid-read) must not become an unhandled 'error' event.
6133
+ child.stdout.on('error', (e) => finish(new Error(`cartridge ${entryPath} HELLO failed: ${e.message}`)));
6123
6134
  });
6124
6135
  }
6125
6136
 
package/capdag.test.js CHANGED
@@ -692,7 +692,7 @@ function test046_matchingSemanticsFallbackPattern() {
692
692
 
693
693
  // TEST047: Matching semantics - thumbnail fallback with void input
694
694
  function test047_matchingSemanticsThumbnailVoidInput() {
695
- const cap = CapUrn.fromString('cap:in="media:void";generate-thumbnail;out="media:image;png;thumbnail"');
695
+ const cap = CapUrn.fromString('cap:in="media:void";generate-thumbnail;out="media:ext=png;image;thumbnail"');
696
696
  const request = CapUrn.fromString('cap:ext=pdf;in="media:void";generate-thumbnail;out="media:image"');
697
697
  assert(cap.accepts(request), 'Void input cap should accept request; cap output conforms to less-specific request output');
698
698
  }
@@ -727,25 +727,25 @@ function test050_matchingSemanticsDirectionMismatch() {
727
727
  function test890_directionSemanticMatching() {
728
728
  // Generic wildcard cap accepts specific pdf request
729
729
  const genericCap = CapUrn.fromString(
730
- 'cap:in="media:";generate-thumbnail;out="media:image;png;thumbnail"'
730
+ 'cap:in="media:";generate-thumbnail;out="media:ext=png;image;thumbnail"'
731
731
  );
732
732
  const pdfRequest = CapUrn.fromString(
733
- 'cap:in="media:ext=pdf";generate-thumbnail;out="media:image;png;thumbnail"'
733
+ 'cap:in="media:ext=pdf";generate-thumbnail;out="media:ext=png;image;thumbnail"'
734
734
  );
735
735
  assert(genericCap.accepts(pdfRequest), 'Generic wildcard cap must accept pdf request');
736
736
 
737
737
  // Also accepts epub
738
738
  const epubRequest = CapUrn.fromString(
739
- 'cap:in="media:epub";generate-thumbnail;out="media:image;png;thumbnail"'
739
+ 'cap:in="media:ext=epub";generate-thumbnail;out="media:ext=png;image;thumbnail"'
740
740
  );
741
741
  assert(genericCap.accepts(epubRequest), 'Generic wildcard cap must accept epub request');
742
742
 
743
743
  // Reverse: specific pdf cap does NOT accept generic bytes request
744
744
  const pdfCap = CapUrn.fromString(
745
- 'cap:in="media:ext=pdf";generate-thumbnail;out="media:image;png;thumbnail"'
745
+ 'cap:in="media:ext=pdf";generate-thumbnail;out="media:ext=png;image;thumbnail"'
746
746
  );
747
747
  const genericRequest = CapUrn.fromString(
748
- 'cap:in="media:";generate-thumbnail;out="media:image;png;thumbnail"'
748
+ 'cap:in="media:";generate-thumbnail;out="media:ext=png;image;thumbnail"'
749
749
  );
750
750
  assert(!pdfCap.accepts(genericRequest), 'Specific pdf cap must NOT accept generic wildcard request');
751
751
 
@@ -754,7 +754,7 @@ function test890_directionSemanticMatching() {
754
754
 
755
755
  // Output direction: cap producing more specific output satisfies less specific request
756
756
  const specificOutCap = CapUrn.fromString(
757
- 'cap:in="media:";generate-thumbnail;out="media:image;png;thumbnail"'
757
+ 'cap:in="media:";generate-thumbnail;out="media:ext=png;image;thumbnail"'
758
758
  );
759
759
  const genericOutRequest = CapUrn.fromString(
760
760
  'cap:in="media:";generate-thumbnail;out="media:image"'
@@ -767,7 +767,7 @@ function test890_directionSemanticMatching() {
767
767
  'cap:in="media:";generate-thumbnail;out="media:image"'
768
768
  );
769
769
  const specificOutRequest = CapUrn.fromString(
770
- 'cap:in="media:";generate-thumbnail;out="media:image;png;thumbnail"'
770
+ 'cap:in="media:";generate-thumbnail;out="media:ext=png;image;thumbnail"'
771
771
  );
772
772
  assert(!genericOutCap.accepts(specificOutRequest),
773
773
  'Generic output cap must NOT satisfy specific output request');
@@ -776,34 +776,34 @@ function test890_directionSemanticMatching() {
776
776
  // TEST891: Semantic direction specificity — more constraints in
777
777
  // either axis means a higher score under the truth-table-driven sum.
778
778
  // media: (top, no tags) scores 0; each marker tag scores 2; each
779
- // exact tag scores 3.
779
+ // exact-value tag (e.g. ext=png) scores 4.
780
780
  function test891_directionSemanticSpecificity() {
781
781
  const genericCap = CapUrn.fromString(
782
- 'cap:in="media:";generate-thumbnail;out="media:image;png;thumbnail"'
782
+ 'cap:in="media:";generate-thumbnail;out="media:ext=png;image;thumbnail"'
783
783
  );
784
784
  const specificCap = CapUrn.fromString(
785
- 'cap:in="media:ext=pdf";generate-thumbnail;out="media:image;png;thumbnail"'
785
+ 'cap:in="media:ext=pdf";generate-thumbnail;out="media:ext=png;image;thumbnail"'
786
786
  );
787
787
 
788
788
  // generic:
789
- // out=media:image;png;thumbnail -> 2+2+2 = 6
789
+ // out=media:ext=png;image;thumbnail -> 4 (ext=png exact-value) + 2 + 2 = 8
790
790
  // in=media: -> 0
791
791
  // y: generate-thumbnail marker -> 2
792
- // spec_C = 10000*6 + 100*0 + 2 = 60002
793
- assertEqual(genericCap.specificity(), 10000*6 + 100*0 + 2,
794
- 'out=image;png;thumbnail(6) + in=media:(0) + generate-thumbnail marker(2) = 60002');
792
+ // spec_C = 10000*8 + 100*0 + 2 = 80002
793
+ assertEqual(genericCap.specificity(), 10000*8 + 100*0 + 2,
794
+ 'out=ext=png(4)+image(2)+thumbnail(2)=8 + in=media:(0) + generate-thumbnail marker(2) = 80002');
795
795
  // specific:
796
- // out=media:image;png;thumbnail -> 6
796
+ // out=media:ext=png;image;thumbnail -> 8
797
797
  // in=media:ext=pdf -> 4 (ext=pdf is an exact-value tag, not a bare marker)
798
798
  // y: generate-thumbnail marker -> 2
799
- // spec_C = 10000*6 + 100*4 + 2 = 60402
800
- assertEqual(specificCap.specificity(), 10000*6 + 100*4 + 2,
801
- 'out=image;png;thumbnail(6) + in=ext=pdf(4) + generate-thumbnail marker(2) = 60402');
799
+ // spec_C = 10000*8 + 100*4 + 2 = 80402
800
+ assertEqual(specificCap.specificity(), 10000*8 + 100*4 + 2,
801
+ 'out=ext=png(4)+image(2)+thumbnail(2)=8 + in=ext=pdf(4) + generate-thumbnail marker(2) = 80402');
802
802
  assert(specificCap.specificity() > genericCap.specificity(), 'pdf should be more specific');
803
803
 
804
804
  // CapMatcher should prefer more specific
805
805
  const pdfRequest = CapUrn.fromString(
806
- 'cap:in="media:ext=pdf";generate-thumbnail;out="media:image;png;thumbnail"'
806
+ 'cap:in="media:ext=pdf";generate-thumbnail;out="media:ext=png;image;thumbnail"'
807
807
  );
808
808
  const best = CapMatcher.findBestMatch([genericCap, specificCap], pdfRequest);
809
809
  assert(best !== null, 'Should find a match');
@@ -1008,7 +1008,7 @@ function test075_accepts() {
1008
1008
  function test076_specificity() {
1009
1009
  const s1 = MediaUrn.fromString('media:');
1010
1010
  const s2 = MediaUrn.fromString('media:ext=pdf');
1011
- const s3 = MediaUrn.fromString('media:image;png;thumbnail');
1011
+ const s3 = MediaUrn.fromString('media:ext=png;image;thumbnail');
1012
1012
  assert(s2.specificity() > s1.specificity(), 'pdf should be more specific than wildcard');
1013
1013
  assert(s3.specificity() > s2.specificity(), 'image;png;thumbnail should be more specific than pdf');
1014
1014
  }
@@ -1208,13 +1208,13 @@ function test109_extensionsWithMetadataAndValidation() {
1208
1208
  function test110_multipleExtensions() {
1209
1209
  const mediaDefs = [
1210
1210
  {
1211
- urn: 'media:image;jpeg',
1211
+ urn: 'media:ext=jpeg;image',
1212
1212
  media_type: 'image/jpeg',
1213
1213
  title: 'JPEG Image',
1214
1214
  extensions: ['jpg', 'jpeg']
1215
1215
  }
1216
1216
  ];
1217
- const resolved = resolveMediaUrn('media:image;jpeg', mediaDefs);
1217
+ const resolved = resolveMediaUrn('media:ext=jpeg;image', mediaDefs);
1218
1218
  assertEqual(resolved.extensions.length, 2, 'Should have two extensions');
1219
1219
  assertEqual(resolved.extensions[0], 'jpg', 'First extension should be jpg');
1220
1220
  assertEqual(resolved.extensions[1], 'jpeg', 'Second extension should be jpeg');
@@ -1414,7 +1414,7 @@ function test0053_CapFabGetOutgoingConformsToMatching() {
1414
1414
  assertEqual(outgoingFromBroad.length, 1, 'Exact URN must match');
1415
1415
 
1416
1416
  // A totally unrelated URN must not match.
1417
- const outgoingFromUnrelated = graph.getOutgoing('media:image;png');
1417
+ const outgoingFromUnrelated = graph.getOutgoing('media:ext=png;image');
1418
1418
  assertEqual(outgoingFromUnrelated.length, 0, 'Unrelated URN must not match');
1419
1419
  }
1420
1420
 
@@ -1663,7 +1663,7 @@ function test312_allUrnBuildersProduceValidUrns() {
1663
1663
  function test0059_JS_buildExtensionIndex() {
1664
1664
  const mediaDefs = [
1665
1665
  { urn: 'media:ext=pdf', media_type: 'application/pdf', extensions: ['pdf'] },
1666
- { urn: 'media:image;jpeg', media_type: 'image/jpeg', extensions: ['jpg', 'jpeg'] },
1666
+ { urn: 'media:ext=jpeg;image', media_type: 'image/jpeg', extensions: ['jpg', 'jpeg'] },
1667
1667
  { urn: 'media:fmt=json', media_type: 'application/json', extensions: ['json'] }
1668
1668
  ];
1669
1669
  const index = buildExtensionIndex(mediaDefs);
@@ -1709,7 +1709,7 @@ function test0069_JS_mediaUrnsForExtension() {
1709
1709
  function test0070_JS_getExtensionMappings() {
1710
1710
  const mediaDefs = [
1711
1711
  { urn: 'media:ext=pdf', media_type: 'application/pdf', extensions: ['pdf'] },
1712
- { urn: 'media:image;jpeg', media_type: 'image/jpeg', extensions: ['jpg', 'jpeg'] }
1712
+ { urn: 'media:ext=jpeg;image', media_type: 'image/jpeg', extensions: ['jpg', 'jpeg'] }
1713
1713
  ];
1714
1714
  const mappings = getExtensionMappings(mediaDefs);
1715
1715
  assert(Array.isArray(mappings), 'Should return an array');
@@ -2751,8 +2751,8 @@ async function test1878_bundledProviderWithoutBakedHashIsRejected() {
2751
2751
  // TEST1312: is_image returns true only when image marker tag is present
2752
2752
  function test1312_isImage() {
2753
2753
  assert(MediaUrn.fromString(MEDIA_PNG).isImage(), 'MEDIA_PNG should be image');
2754
- assert(MediaUrn.fromString('media:image;png;thumbnail').isImage(), 'media:image;png;thumbnail should be image');
2755
- assert(MediaUrn.fromString('media:image;jpg').isImage(), 'media:image;jpg should be image');
2754
+ assert(MediaUrn.fromString('media:ext=png;image;thumbnail').isImage(), 'media:ext=png;image;thumbnail should be image');
2755
+ assert(MediaUrn.fromString('media:ext=jpg;image').isImage(), 'media:ext=jpg;image should be image');
2756
2756
  // Non-image types
2757
2757
  assert(!MediaUrn.fromString(MEDIA_PDF).isImage(), 'MEDIA_PDF should not be image');
2758
2758
  assert(!MediaUrn.fromString(MEDIA_STRING).isImage(), 'MEDIA_STRING should not be image');
@@ -2764,7 +2764,7 @@ function test1312_isImage() {
2764
2764
  function test1313_isAudio() {
2765
2765
  assert(MediaUrn.fromString(MEDIA_AUDIO).isAudio(), 'MEDIA_AUDIO should be audio');
2766
2766
  assert(MediaUrn.fromString(MEDIA_AUDIO_SPEECH).isAudio(), 'MEDIA_AUDIO_SPEECH should be audio');
2767
- assert(MediaUrn.fromString('media:audio;mp3').isAudio(), 'media:audio;mp3 should be audio');
2767
+ assert(MediaUrn.fromString('media:audio;ext=mp3').isAudio(), 'media:audio;ext=mp3 should be audio');
2768
2768
  // Non-audio types
2769
2769
  assert(!MediaUrn.fromString(MEDIA_VIDEO).isAudio(), 'MEDIA_VIDEO should not be audio');
2770
2770
  assert(!MediaUrn.fromString(MEDIA_PNG).isAudio(), 'MEDIA_PNG should not be audio');
@@ -2774,7 +2774,7 @@ function test1313_isAudio() {
2774
2774
  // TEST1314: is_video returns true only when video marker tag is present
2775
2775
  function test1314_isVideo() {
2776
2776
  assert(MediaUrn.fromString(MEDIA_VIDEO).isVideo(), 'MEDIA_VIDEO should be video');
2777
- assert(MediaUrn.fromString('media:video;mp4').isVideo(), 'media:video;mp4 should be video');
2777
+ assert(MediaUrn.fromString('media:ext=mp4;video').isVideo(), 'media:ext=mp4;video should be video');
2778
2778
  // Non-video types
2779
2779
  assert(!MediaUrn.fromString(MEDIA_AUDIO).isVideo(), 'MEDIA_AUDIO should not be video');
2780
2780
  assert(!MediaUrn.fromString(MEDIA_PNG).isVideo(), 'MEDIA_PNG should not be video');
@@ -3197,7 +3197,7 @@ function test653_invalidEffectNoneDeclarationRejected() {
3197
3197
  // TEST654: effect=none preserves runtime media identity.
3198
3198
  function test654_effectNonePreservesRuntimeMedia() {
3199
3199
  const decimate = CapUrn.fromString('cap:decimate-sequence;effect=none');
3200
- const png = MediaUrn.fromString('media:image;png');
3200
+ const png = MediaUrn.fromString('media:ext=png;image');
3201
3201
  const pdf = MediaUrn.fromString('media:ext=pdf');
3202
3202
  assertEqual(decimate.inferRuntimeOutputMedia(png).toString(), png.toString(), 'effect=none should preserve png');
3203
3203
  assertEqual(decimate.inferRuntimeOutputMedia(pdf).toString(), pdf.toString(), 'effect=none should preserve pdf');
@@ -3206,7 +3206,7 @@ function test654_effectNonePreservesRuntimeMedia() {
3206
3206
  // TEST655: default effect=declared does not preserve runtime refinements.
3207
3207
  function test655_effectDeclaredUsesDeclaredOutput() {
3208
3208
  const resize = CapUrn.fromString('cap:in=media:image;out=media:image;resize');
3209
- const png = MediaUrn.fromString('media:image;png;width=4000');
3209
+ const png = MediaUrn.fromString('media:ext=png;image;width=4000');
3210
3210
  assertEqual(
3211
3211
  resize.inferRuntimeOutputMedia(png).toString(),
3212
3212
  'media:image',
@@ -3303,7 +3303,7 @@ function test0096_Machine_fanOut() {
3303
3303
  const g = Machine.fromString(
3304
3304
  '[meta cap:in="media:ext=pdf";extract-metadata;out="media:enc=utf-8;file-metadata;record"]' +
3305
3305
  '[outline cap:in="media:ext=pdf";extract-outline;out="media:document-outline;enc=utf-8;record"]' +
3306
- '[thumb cap:in="media:ext=pdf";generate-thumbnail;out="media:image;png;thumbnail"]' +
3306
+ '[thumb cap:in="media:ext=pdf";generate-thumbnail;out="media:ext=png;image;thumbnail"]' +
3307
3307
  '[doc -> meta -> metadata]' +
3308
3308
  '[doc -> outline -> outline_data]' +
3309
3309
  '[doc -> thumb -> thumbnail]'
@@ -3319,9 +3319,9 @@ function test0096_Machine_fanOut() {
3319
3319
  // TEST0097: Machine fan in secondary assigned by prior wiring
3320
3320
  function test0097_Machine_fanInSecondaryAssignedByPriorWiring() {
3321
3321
  const g = Machine.fromString(
3322
- '[thumb cap:in="media:ext=pdf";generate-thumbnail;out="media:image;png;thumbnail"]' +
3322
+ '[thumb cap:in="media:ext=pdf";generate-thumbnail;out="media:ext=png;image;thumbnail"]' +
3323
3323
  '[model_dl cap:in="media:enc=utf-8;model-spec";download;out="media:enc=utf-8;model-spec"]' +
3324
- '[describe cap:in="media:image;png";describe-image;out="media:enc=utf-8;image-description"]' +
3324
+ '[describe cap:in="media:ext=png;image";describe-image;out="media:enc=utf-8;image-description"]' +
3325
3325
  '[doc -> thumb -> thumbnail]' +
3326
3326
  '[spec_input -> model_dl -> model_spec]' +
3327
3327
  '[(thumbnail, model_spec) -> describe -> description]'
@@ -3333,12 +3333,12 @@ function test0097_Machine_fanInSecondaryAssignedByPriorWiring() {
3333
3333
  // TEST0098: Machine fan in secondary unassigned gets wildcard
3334
3334
  function test0098_Machine_fanInSecondaryUnassignedGetsWildcard() {
3335
3335
  const g = Machine.fromString(
3336
- '[describe cap:in="media:image;png";describe-image;out="media:enc=utf-8;image-description"]\n' +
3336
+ '[describe cap:in="media:ext=png;image";describe-image;out="media:enc=utf-8;image-description"]\n' +
3337
3337
  '[(thumbnail, model_spec) -> describe -> description]'
3338
3338
  );
3339
3339
  assertEqual(g.edges().length, 1);
3340
3340
  assertEqual(g.edges()[0].sources.length, 2);
3341
- assertEqual(g.edges()[0].sources[0].toString(), 'media:image;png');
3341
+ assertEqual(g.edges()[0].sources[0].toString(), 'media:ext=png;image');
3342
3342
  assertEqual(g.edges()[0].sources[1].toString(), 'media:');
3343
3343
  }
3344
3344
 
@@ -3376,7 +3376,7 @@ function test0114_Machine_conflictingMediaTypesFail() {
3376
3376
  assertThrowsWithCode(
3377
3377
  () => Machine.fromString(
3378
3378
  '[cap1 cap:in="media:enc=utf-8;ext=txt";a;out="media:ext=pdf"]' +
3379
- '[cap2 cap:in="media:audio;wav";b;out="media:enc=utf-8;ext=txt"]' +
3379
+ '[cap2 cap:in="media:audio;ext=wav";b;out="media:enc=utf-8;ext=txt"]' +
3380
3380
  '[src -> cap1 -> mid]' +
3381
3381
  '[mid -> cap2 -> dst]'
3382
3382
  ),
@@ -3463,9 +3463,9 @@ function test0123_Machine_lineBasedLoop() {
3463
3463
  // TEST0124: Machine line based fan in
3464
3464
  function test0124_Machine_lineBasedFanIn() {
3465
3465
  const g = Machine.fromString(
3466
- 'thumb cap:in="media:ext=pdf";generate-thumbnail;out="media:image;png;thumbnail"\n' +
3466
+ 'thumb cap:in="media:ext=pdf";generate-thumbnail;out="media:ext=png;image;thumbnail"\n' +
3467
3467
  'model_dl cap:in="media:enc=utf-8;model-spec";download;out="media:enc=utf-8;model-spec"\n' +
3468
- 'describe cap:in="media:image;png";describe-image;out="media:enc=utf-8;image-description"\n' +
3468
+ 'describe cap:in="media:ext=png;image";describe-image;out="media:enc=utf-8;image-description"\n' +
3469
3469
  'doc -> thumb -> thumbnail\n' +
3470
3470
  'spec_input -> model_dl -> model_spec\n' +
3471
3471
  '(thumbnail, model_spec) -> describe -> description'
@@ -3861,7 +3861,7 @@ function test0152_Machine_roundtripFanOut() {
3861
3861
  const original = new Machine([
3862
3862
  mkEdge('media:ext=pdf', 'cap:in="media:ext=pdf";extract-metadata;out="media:enc=utf-8;file-metadata;record"', 'media:enc=utf-8;file-metadata;record'),
3863
3863
  mkEdge('media:ext=pdf', 'cap:in="media:ext=pdf";extract-outline;out="media:document-outline;enc=utf-8;record"', 'media:document-outline;enc=utf-8;record'),
3864
- mkEdge('media:ext=pdf', 'cap:in="media:ext=pdf";generate-thumbnail;out="media:image;png;thumbnail"', 'media:image;png;thumbnail'),
3864
+ mkEdge('media:ext=pdf', 'cap:in="media:ext=pdf";generate-thumbnail;out="media:ext=png;image;thumbnail"', 'media:ext=png;image;thumbnail'),
3865
3865
  ]);
3866
3866
  const notation = original.toMachineNotation();
3867
3867
  const reparsed = Machine.fromString(notation);
@@ -4137,7 +4137,7 @@ function test0177_Machine_parseMachineWithAST_multilinePositions() {
4137
4137
  // TEST0178: Machine parse machine with a s t fan in source locations
4138
4138
  function test0178_Machine_parseMachineWithAST_fanInSourceLocations() {
4139
4139
  const input = [
4140
- '[describe cap:in="media:image;png";describe-image;out="media:enc=utf-8;image-description"]',
4140
+ '[describe cap:in="media:ext=png;image";describe-image;out="media:enc=utf-8;image-description"]',
4141
4141
  '[(thumbnail, model_spec) -> describe -> description]'
4142
4142
  ].join('\n');
4143
4143
  const result = parseMachineWithAST(input);
@@ -4259,7 +4259,7 @@ function test0186_Machine_toMermaid_emptyGraph() {
4259
4259
  // TEST0187: Machine to mermaid fan in
4260
4260
  function test0187_Machine_toMermaid_fanIn() {
4261
4261
  const machine = Machine.fromString(
4262
- '[describe cap:in="media:image;png";describe-image;out="media:enc=utf-8;image-description"]' +
4262
+ '[describe cap:in="media:ext=png;image";describe-image;out="media:enc=utf-8;image-description"]' +
4263
4263
  '[(thumbnail, model_spec) -> describe -> description]'
4264
4264
  );
4265
4265
  const mermaid = machine.toMermaid();
@@ -4272,7 +4272,7 @@ function test0187_Machine_toMermaid_fanIn() {
4272
4272
  function test0188_Machine_toMermaid_fanOut() {
4273
4273
  const input = [
4274
4274
  '[meta cap:in="media:ext=pdf";extract-metadata;out="media:enc=utf-8;file-metadata;record"]',
4275
- '[thumb cap:in="media:ext=pdf";generate-thumbnail;out="media:image;png;thumbnail"]',
4275
+ '[thumb cap:in="media:ext=pdf";generate-thumbnail;out="media:ext=png;image;thumbnail"]',
4276
4276
  '[doc -> meta -> metadata]',
4277
4277
  '[doc -> thumb -> thumbnail]'
4278
4278
  ].join('');
@@ -4646,10 +4646,10 @@ function test0207_Renderer_classifyStrandCapSteps_capFlags() {
4646
4646
  // prevForEach=true; cap3 should have nextCollect=true; cap2 should
4647
4647
  // have neither.
4648
4648
  const steps = [
4649
- makeForEachStep('media:pdf;list'),
4650
- makeCapStep('cap:in="media:ext=pdf";a;out="media:image;png"', 'a', 'media:ext=pdf', 'media:image;png', false, false),
4651
- makeCapStep('cap:in="media:image;png";b;out="media:jpg"', 'b', 'media:image;png', 'media:jpg', false, false),
4652
- makeCapStep('cap:in="media:jpg";c;out="media:ext=txt"', 'c', 'media:jpg', 'media:ext=txt', false, false),
4649
+ makeForEachStep('media:ext=pdf;list'),
4650
+ makeCapStep('cap:in="media:ext=pdf";a;out="media:ext=png;image"', 'a', 'media:ext=pdf', 'media:ext=png;image', false, false),
4651
+ makeCapStep('cap:in="media:ext=png;image";b;out="media:ext=jpg"', 'b', 'media:ext=png;image', 'media:ext=jpg', false, false),
4652
+ makeCapStep('cap:in="media:ext=jpg";c;out="media:ext=txt"', 'c', 'media:ext=jpg', 'media:ext=txt', false, false),
4653
4653
  makeCollectStep('media:ext=txt'),
4654
4654
  ];
4655
4655
  const { capStepIndices, capFlags } = rendererClassifyStrandCapSteps(steps);
@@ -4757,15 +4757,15 @@ function test0211_Renderer_buildStrandGraphData_foreachCollectSpan() {
4757
4757
  // labels on cap edges — they're distinct processing units in the
4758
4758
  // plan. This mirrors capdag's plan_builder.rs exactly.
4759
4759
  const payload = withMediaDisplayNames({
4760
- source_media_urn: 'media:pdf;list',
4760
+ source_media_urn: 'media:ext=pdf;list',
4761
4761
  target_media_urn: 'media:txt;list',
4762
4762
  steps: [
4763
- makeForEachStep('media:pdf;list'),
4763
+ makeForEachStep('media:ext=pdf;list'),
4764
4764
  makeCapStep('cap:in="media:ext=pdf";extract;out="media:ext=txt"', 'extract', 'media:ext=pdf', 'media:ext=txt', false, false),
4765
4765
  makeCollectStep('media:ext=txt'),
4766
4766
  ],
4767
4767
  }, {
4768
- 'media:pdf;list': 'PDF List',
4768
+ 'media:ext=pdf;list': 'PDF List',
4769
4769
  'media:ext=txt': 'Plain Text',
4770
4770
  'media:txt;list': 'Text List',
4771
4771
  });
@@ -4905,15 +4905,15 @@ function test0215_Renderer_collapseStrand_singleCapBodyKeepsCapOwnLabel() {
4905
4905
  // with the entry edge labeled "extract" and an unlabeled
4906
4906
  // connector bridge to the output.
4907
4907
  const payload = withMediaDisplayNames({
4908
- source_media_urn: 'media:pdf;list',
4908
+ source_media_urn: 'media:ext=pdf;list',
4909
4909
  target_media_urn: 'media:txt;list',
4910
4910
  steps: [
4911
- makeForEachStep('media:pdf;list'),
4911
+ makeForEachStep('media:ext=pdf;list'),
4912
4912
  makeCapStep('cap:in="media:ext=pdf";extract;out="media:ext=txt"', 'extract', 'media:ext=pdf', 'media:ext=txt', false, false),
4913
4913
  makeCollectStep('media:ext=txt'),
4914
4914
  ],
4915
4915
  }, {
4916
- 'media:pdf;list': 'PDF List',
4916
+ 'media:ext=pdf;list': 'PDF List',
4917
4917
  'media:ext=txt': 'Plain Text',
4918
4918
  'media:txt;list': 'Text List',
4919
4919
  });
@@ -5209,12 +5209,12 @@ function test0223_Renderer_buildRunGraphData_pagesSuccessesAndFailures() {
5209
5209
  // Show-more nodes: one for 3 hidden successes, one for 2 hidden
5210
5210
  // failures.
5211
5211
  const strand = {
5212
- source_media_urn: 'media:pdf;list',
5212
+ source_media_urn: 'media:ext=pdf;list',
5213
5213
  target_media_urn: 'media:ext=txt',
5214
5214
  steps: [
5215
- makeForEachStep('media:pdf;list'),
5216
- makeCapStep('cap:in="media:ext=pdf";a;out="media:image;png"', 'a', 'media:ext=pdf', 'media:image;png', false, false),
5217
- makeCapStep('cap:in="media:image;png";b;out="media:ext=txt"', 'b', 'media:image;png', 'media:ext=txt', false, false),
5215
+ makeForEachStep('media:ext=pdf;list'),
5216
+ makeCapStep('cap:in="media:ext=pdf";a;out="media:ext=png;image"', 'a', 'media:ext=pdf', 'media:ext=png;image', false, false),
5217
+ makeCapStep('cap:in="media:ext=png;image";b;out="media:ext=txt"', 'b', 'media:ext=png;image', 'media:ext=txt', false, false),
5218
5218
  makeCollectStep('media:ext=txt'),
5219
5219
  ],
5220
5220
  };
@@ -5230,16 +5230,16 @@ function test0223_Renderer_buildRunGraphData_pagesSuccessesAndFailures() {
5230
5230
  saved_paths: [],
5231
5231
  total_bytes: 0,
5232
5232
  duration_ms: 0,
5233
- failed_cap: 'cap:in="media:image;png";b;out="media:ext=txt"',
5233
+ failed_cap: 'cap:in="media:ext=png;image";b;out="media:ext=txt"',
5234
5234
  error: 'oom',
5235
5235
  });
5236
5236
  }
5237
5237
  const payload = {
5238
5238
  resolved_strand: strand,
5239
5239
  media_display_names: {
5240
- 'media:pdf;list': 'PDF List',
5240
+ 'media:ext=pdf;list': 'PDF List',
5241
5241
  'media:ext=pdf': 'PDF',
5242
- 'media:image;png': 'PNG',
5242
+ 'media:ext=png;image': 'PNG',
5243
5243
  'media:ext=txt': 'Text',
5244
5244
  },
5245
5245
  body_outcomes: outcomes,
@@ -5276,10 +5276,10 @@ function test0224_Renderer_buildRunGraphData_failureWithoutFailedCapRendersFullT
5276
5276
  // Strand [ForEach, Cap, Collect] → body has 1 cap. Each body
5277
5277
  // replica emits 1 entry node + 1 body cap node = 2 nodes.
5278
5278
  const strand = {
5279
- source_media_urn: 'media:pdf;list',
5279
+ source_media_urn: 'media:ext=pdf;list',
5280
5280
  target_media_urn: 'media:ext=txt',
5281
5281
  steps: [
5282
- makeForEachStep('media:pdf;list'),
5282
+ makeForEachStep('media:ext=pdf;list'),
5283
5283
  makeCapStep('cap:in="media:ext=pdf";a;out="media:ext=txt"', 'a', 'media:ext=pdf', 'media:ext=txt', false, false),
5284
5284
  makeCollectStep('media:ext=txt'),
5285
5285
  ],
@@ -5287,7 +5287,7 @@ function test0224_Renderer_buildRunGraphData_failureWithoutFailedCapRendersFullT
5287
5287
  const payload = {
5288
5288
  resolved_strand: strand,
5289
5289
  media_display_names: {
5290
- 'media:pdf;list': 'PDF List',
5290
+ 'media:ext=pdf;list': 'PDF List',
5291
5291
  'media:ext=pdf': 'PDF',
5292
5292
  'media:ext=txt': 'Text',
5293
5293
  },
@@ -5560,10 +5560,10 @@ function test0229_Renderer_buildRunGraphData_closedForeachSuccessMergesAtCollect
5560
5560
  // Actually simpler: [ForEach, Cap_a, Collect] with source=list
5561
5561
  // and target=list.
5562
5562
  const strand = {
5563
- source_media_urn: 'media:pdf;list',
5563
+ source_media_urn: 'media:ext=pdf;list',
5564
5564
  target_media_urn: 'media:txt;list',
5565
5565
  steps: [
5566
- makeForEachStep('media:pdf;list'),
5566
+ makeForEachStep('media:ext=pdf;list'),
5567
5567
  makeCapStep('cap:in="media:ext=pdf";extract;out="media:ext=txt"', 'extract', 'media:ext=pdf', 'media:ext=txt', false, false),
5568
5568
  makeCollectStep('media:ext=txt'),
5569
5569
  ],
@@ -5571,7 +5571,7 @@ function test0229_Renderer_buildRunGraphData_closedForeachSuccessMergesAtCollect
5571
5571
  const payload = {
5572
5572
  resolved_strand: strand,
5573
5573
  media_display_names: {
5574
- 'media:pdf;list': 'PDF List',
5574
+ 'media:ext=pdf;list': 'PDF List',
5575
5575
  'media:ext=pdf': 'PDF',
5576
5576
  'media:ext=txt': 'Text',
5577
5577
  'media:txt;list': 'Text List',
@@ -5835,18 +5835,18 @@ function test0238_Renderer_buildResolvedMachineGraphData_fanInProducesEdgePerAss
5835
5835
  strands: [
5836
5836
  {
5837
5837
  nodes: [
5838
- { id: 'n0', urn: 'media:image;png', title: 'PNG Image' },
5838
+ { id: 'n0', urn: 'media:ext=png;image', title: 'PNG Image' },
5839
5839
  { id: 'n1', urn: 'media:enc=utf-8;model-spec', title: 'Model Spec' },
5840
5840
  { id: 'n2', urn: 'media:enc=utf-8;image-description', title: 'Image Description' },
5841
5841
  ],
5842
5842
  edges: [
5843
5843
  {
5844
5844
  alias: 'edge_0',
5845
- cap_urn: 'cap:in=media:image;png;describe-image;out=media:enc=utf-8;image-description',
5845
+ cap_urn: 'cap:in="media:ext=png;image";describe-image;out="media:enc=utf-8;image-description"',
5846
5846
  title: 'Describe Image',
5847
5847
  is_loop: false,
5848
5848
  assignment: [
5849
- { cap_arg_media_urn: 'media:image;png', source_node: 'n0' },
5849
+ { cap_arg_media_urn: 'media:ext=png;image', source_node: 'n0' },
5850
5850
  { cap_arg_media_urn: 'media:enc=utf-8;model-spec', source_node: 'n1' },
5851
5851
  ],
5852
5852
  target_node: 'n2',
@@ -5945,7 +5945,7 @@ function test0240_Renderer_buildResolvedMachineGraphData_duplicateNodeIdAcrossSt
5945
5945
  output_anchor_nodes: ['n0'],
5946
5946
  },
5947
5947
  {
5948
- nodes: [{ id: 'n0', urn: 'media:html', title: 'HTML' }],
5948
+ nodes: [{ id: 'n0', urn: 'media:ext=html', title: 'HTML' }],
5949
5949
  edges: [],
5950
5950
  input_anchor_nodes: ['n0'],
5951
5951
  output_anchor_nodes: ['n0'],
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": "1.205.540"
43
+ "version": "1.211.563"
44
44
  }