capdag 0.153.347 → 0.157.363
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/build-browser.js +15 -15
- package/{cap-graph-renderer.js → cap-fab-renderer.js} +78 -78
- package/capdag.js +22 -22
- package/capdag.test.js +26 -26
- package/package.json +2 -2
package/build-browser.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// Build browser-compatible bundles of tagged-urn, capdag, and
|
|
3
|
-
// cap-
|
|
3
|
+
// cap-fab-renderer from the local sources + resolved tagged-urn
|
|
4
4
|
// dependency. Outputs three self-contained IIFE-wrapped JS files to
|
|
5
5
|
// `dist/` that each expose their exported classes as window globals.
|
|
6
6
|
//
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
// Load order at the consumer:
|
|
14
14
|
// 1. tagged-urn.js — defines window.TaggedUrn, etc.
|
|
15
15
|
// 2. capdag.js — reads window.TaggedUrn, defines CapUrn,
|
|
16
|
-
// MediaUrn, Cap,
|
|
17
|
-
// 3. cap-
|
|
18
|
-
// at call time, defines
|
|
16
|
+
// MediaUrn, Cap, CapFab, createCap, …
|
|
17
|
+
// 3. cap-fab-renderer.js — reads window.cytoscape + capdag globals
|
|
18
|
+
// at call time, defines CapFabRenderer.
|
|
19
19
|
//
|
|
20
20
|
// Running: `node build-browser.js [outDir]`. Default outDir is ./dist.
|
|
21
21
|
|
|
@@ -144,9 +144,9 @@ window.resolveMediaUrn = resolveMediaUrn;
|
|
|
144
144
|
window.buildExtensionIndex = buildExtensionIndex;
|
|
145
145
|
window.mediaUrnsForExtension = mediaUrnsForExtension;
|
|
146
146
|
window.getExtensionMappings = getExtensionMappings;
|
|
147
|
-
window.
|
|
148
|
-
window.
|
|
149
|
-
window.
|
|
147
|
+
window.CapFabEdge = CapFabEdge;
|
|
148
|
+
window.CapFabStats = CapFabStats;
|
|
149
|
+
window.CapFab = CapFab;
|
|
150
150
|
window.StdinSource = StdinSource;
|
|
151
151
|
window.StdinSourceKind = StdinSourceKind;
|
|
152
152
|
window.CapArgumentValue = CapArgumentValue;
|
|
@@ -164,8 +164,8 @@ window.parseMachine = parseMachine;
|
|
|
164
164
|
console.log(` wrote ${path.join(outDir, 'capdag.js')}`);
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
-
function
|
|
168
|
-
const srcPath = path.join(here, 'cap-
|
|
167
|
+
function buildCapFabRenderer() {
|
|
168
|
+
const srcPath = path.join(here, 'cap-fab-renderer.js');
|
|
169
169
|
const src = fs.readFileSync(srcPath, 'utf8');
|
|
170
170
|
// The file's CJS exports block is at the bottom, guarded by
|
|
171
171
|
// `typeof module !== 'undefined'`. Strip everything from that guard
|
|
@@ -174,8 +174,8 @@ function buildCapGraphRenderer() {
|
|
|
174
174
|
/if\s*\(\s*typeof\s+module\s*!==\s*'undefined'[\s\S]*$/,
|
|
175
175
|
''
|
|
176
176
|
);
|
|
177
|
-
const wrapped = `// cap-
|
|
178
|
-
// Generated from capdag-js/cap-
|
|
177
|
+
const wrapped = `// cap-fab-renderer — browser build
|
|
178
|
+
// Generated from capdag-js/cap-fab-renderer.js by capdag-js/build-browser.js.
|
|
179
179
|
// Do not edit directly. Requires cytoscape, cytoscape-elk, tagged-urn.js,
|
|
180
180
|
// and capdag.js to be loaded first.
|
|
181
181
|
|
|
@@ -184,15 +184,15 @@ function buildCapGraphRenderer() {
|
|
|
184
184
|
|
|
185
185
|
${stripped}
|
|
186
186
|
|
|
187
|
-
window.
|
|
187
|
+
window.CapFabRenderer = CapFabRenderer;
|
|
188
188
|
|
|
189
189
|
})();
|
|
190
190
|
`;
|
|
191
|
-
fs.writeFileSync(path.join(outDir, 'cap-
|
|
192
|
-
console.log(` wrote ${path.join(outDir, 'cap-
|
|
191
|
+
fs.writeFileSync(path.join(outDir, 'cap-fab-renderer.js'), wrapped);
|
|
192
|
+
console.log(` wrote ${path.join(outDir, 'cap-fab-renderer.js')}`);
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
buildTaggedUrn();
|
|
196
196
|
buildCapdag();
|
|
197
|
-
|
|
197
|
+
buildCapFabRenderer();
|
|
198
198
|
console.log(`browser bundles written to ${outDir}`);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//
|
|
1
|
+
// CapFabRenderer — unified graph rendering for capdag-js
|
|
2
2
|
//
|
|
3
3
|
// One class, four modes:
|
|
4
4
|
//
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
// * cytoscape-elk extension (registers itself on `cytoscape`)
|
|
29
29
|
// * elkjs (via cytoscape-elk)
|
|
30
30
|
// * TaggedUrn (from tagged-urn browser build)
|
|
31
|
-
// * CapUrn, MediaUrn, Cap, createCap,
|
|
31
|
+
// * CapUrn, MediaUrn, Cap, createCap, CapFab (from capdag.js)
|
|
32
32
|
//
|
|
33
33
|
// The renderer owns its own theme observer (<html data-theme>) so hosts do
|
|
34
34
|
// nothing to drive theme sync. It owns its own tooltip element and its own
|
|
@@ -54,13 +54,13 @@ function requireHostDependency(name) {
|
|
|
54
54
|
: null;
|
|
55
55
|
if (g === null) {
|
|
56
56
|
throw new Error(
|
|
57
|
-
`
|
|
57
|
+
`CapFabRenderer: no global object (window/global) — cannot resolve '${name}'`
|
|
58
58
|
);
|
|
59
59
|
}
|
|
60
60
|
const value = g[name];
|
|
61
61
|
if (value === undefined) {
|
|
62
62
|
throw new Error(
|
|
63
|
-
`
|
|
63
|
+
`CapFabRenderer: required host dependency '${name}' is not loaded. ` +
|
|
64
64
|
`Load cytoscape, cytoscape-elk, tagged-urn.js, and capdag.js before this script.`
|
|
65
65
|
);
|
|
66
66
|
}
|
|
@@ -86,7 +86,7 @@ function cardinalityLabel(input_is_sequence, output_is_sequence) {
|
|
|
86
86
|
// `CapOutput.is_sequence` names exactly.
|
|
87
87
|
function cardinalityFromCap(cap) {
|
|
88
88
|
if (!cap || typeof cap !== 'object') {
|
|
89
|
-
throw new Error('
|
|
89
|
+
throw new Error('CapFabRenderer: cardinalityFromCap requires a cap object');
|
|
90
90
|
}
|
|
91
91
|
const args = cap.args || [];
|
|
92
92
|
const mainArg = args.find(arg =>
|
|
@@ -112,7 +112,7 @@ function canonicalMediaUrn(mediaUrnString) {
|
|
|
112
112
|
// allowed to synthesize user-facing labels from URNs.
|
|
113
113
|
function mediaNodeLabel() {
|
|
114
114
|
throw new Error(
|
|
115
|
-
'
|
|
115
|
+
'CapFabRenderer: mediaNodeLabel() is no longer supported. ' +
|
|
116
116
|
'Pass explicit media titles/display names to the renderer.'
|
|
117
117
|
);
|
|
118
118
|
}
|
|
@@ -124,7 +124,7 @@ function requireExplicitDisplayName(canonicalUrn, displayEntries, context) {
|
|
|
124
124
|
if (candidate.isEquivalent(entry.media)) return entry.display;
|
|
125
125
|
}
|
|
126
126
|
throw new Error(
|
|
127
|
-
`
|
|
127
|
+
`CapFabRenderer: missing explicit display name for ${context} '${canonicalUrn}'`
|
|
128
128
|
);
|
|
129
129
|
}
|
|
130
130
|
|
|
@@ -142,7 +142,7 @@ function cssVarNumber(name, fallback) {
|
|
|
142
142
|
const parsed = parseFloat(raw);
|
|
143
143
|
if (!Number.isFinite(parsed)) {
|
|
144
144
|
throw new Error(
|
|
145
|
-
`
|
|
145
|
+
`CapFabRenderer: CSS variable '${name}' value '${raw}' is not a number`
|
|
146
146
|
);
|
|
147
147
|
}
|
|
148
148
|
return parsed;
|
|
@@ -209,7 +209,7 @@ function layoutForMode(mode) {
|
|
|
209
209
|
'elk.spacing.nodeNode': 40,
|
|
210
210
|
});
|
|
211
211
|
}
|
|
212
|
-
throw new Error(`
|
|
212
|
+
throw new Error(`CapFabRenderer: unknown mode '${mode}'`);
|
|
213
213
|
}
|
|
214
214
|
|
|
215
215
|
// =============================================================================
|
|
@@ -441,13 +441,13 @@ function createTooltipElement() {
|
|
|
441
441
|
|
|
442
442
|
function assertString(value, path) {
|
|
443
443
|
if (typeof value !== 'string' || value.length === 0) {
|
|
444
|
-
throw new Error(`
|
|
444
|
+
throw new Error(`CapFabRenderer: ${path} must be a non-empty string`);
|
|
445
445
|
}
|
|
446
446
|
}
|
|
447
447
|
|
|
448
448
|
function assertArray(value, path) {
|
|
449
449
|
if (!Array.isArray(value)) {
|
|
450
|
-
throw new Error(`
|
|
450
|
+
throw new Error(`CapFabRenderer: ${path} must be an array`);
|
|
451
451
|
}
|
|
452
452
|
}
|
|
453
453
|
|
|
@@ -455,7 +455,7 @@ function validateBrowseData(data) {
|
|
|
455
455
|
assertArray(data, 'browse mode data');
|
|
456
456
|
data.forEach((cap, idx) => {
|
|
457
457
|
if (!cap || typeof cap !== 'object') {
|
|
458
|
-
throw new Error(`
|
|
458
|
+
throw new Error(`CapFabRenderer browse mode: data[${idx}] is not an object`);
|
|
459
459
|
}
|
|
460
460
|
assertString(cap.urn, `browse mode data[${idx}].urn`);
|
|
461
461
|
assertString(cap.in_spec, `browse mode data[${idx}].in_spec (cap urn: ${cap.urn})`);
|
|
@@ -470,37 +470,37 @@ function validateBrowseData(data) {
|
|
|
470
470
|
// externally-tagged step_type.
|
|
471
471
|
function validateStrandStep(step, path) {
|
|
472
472
|
if (!step || typeof step !== 'object') {
|
|
473
|
-
throw new Error(`
|
|
473
|
+
throw new Error(`CapFabRenderer: ${path} is not an object`);
|
|
474
474
|
}
|
|
475
475
|
assertString(step.from_spec, `${path}.from_spec`);
|
|
476
476
|
assertString(step.to_spec, `${path}.to_spec`);
|
|
477
477
|
if (!step.step_type || typeof step.step_type !== 'object') {
|
|
478
|
-
throw new Error(`
|
|
478
|
+
throw new Error(`CapFabRenderer: ${path}.step_type must be an object`);
|
|
479
479
|
}
|
|
480
480
|
const keys = Object.keys(step.step_type);
|
|
481
481
|
if (keys.length !== 1) {
|
|
482
482
|
throw new Error(
|
|
483
|
-
`
|
|
483
|
+
`CapFabRenderer: ${path}.step_type must have exactly one variant key (got: ${keys.join(',')})`
|
|
484
484
|
);
|
|
485
485
|
}
|
|
486
486
|
const variant = keys[0];
|
|
487
487
|
if (variant !== 'Cap' && variant !== 'ForEach' && variant !== 'Collect') {
|
|
488
488
|
throw new Error(
|
|
489
|
-
`
|
|
489
|
+
`CapFabRenderer: ${path}.step_type variant must be Cap | ForEach | Collect (got: ${variant})`
|
|
490
490
|
);
|
|
491
491
|
}
|
|
492
492
|
const body = step.step_type[variant];
|
|
493
493
|
if (!body || typeof body !== 'object') {
|
|
494
|
-
throw new Error(`
|
|
494
|
+
throw new Error(`CapFabRenderer: ${path}.step_type.${variant} must be an object`);
|
|
495
495
|
}
|
|
496
496
|
if (variant === 'Cap') {
|
|
497
497
|
assertString(body.cap_urn, `${path}.step_type.Cap.cap_urn`);
|
|
498
498
|
assertString(body.title, `${path}.step_type.Cap.title`);
|
|
499
499
|
if (typeof body.input_is_sequence !== 'boolean') {
|
|
500
|
-
throw new Error(`
|
|
500
|
+
throw new Error(`CapFabRenderer: ${path}.step_type.Cap.input_is_sequence must be a boolean`);
|
|
501
501
|
}
|
|
502
502
|
if (typeof body.output_is_sequence !== 'boolean') {
|
|
503
|
-
throw new Error(`
|
|
503
|
+
throw new Error(`CapFabRenderer: ${path}.step_type.Cap.output_is_sequence must be a boolean`);
|
|
504
504
|
}
|
|
505
505
|
} else {
|
|
506
506
|
assertString(body.media_spec, `${path}.step_type.${variant}.media_spec`);
|
|
@@ -509,7 +509,7 @@ function validateStrandStep(step, path) {
|
|
|
509
509
|
|
|
510
510
|
function validateStrandPayload(data) {
|
|
511
511
|
if (!data || typeof data !== 'object') {
|
|
512
|
-
throw new Error('
|
|
512
|
+
throw new Error('CapFabRenderer strand mode: data must be an object');
|
|
513
513
|
}
|
|
514
514
|
assertString(data.source_spec, 'strand mode data.source_spec');
|
|
515
515
|
assertString(data.target_spec, 'strand mode data.target_spec');
|
|
@@ -519,28 +519,28 @@ function validateStrandPayload(data) {
|
|
|
519
519
|
});
|
|
520
520
|
if (data.media_display_names !== undefined
|
|
521
521
|
&& (data.media_display_names === null || typeof data.media_display_names !== 'object')) {
|
|
522
|
-
throw new Error('
|
|
522
|
+
throw new Error('CapFabRenderer strand mode: data.media_display_names must be an object when present');
|
|
523
523
|
}
|
|
524
524
|
if (data.source_display !== undefined && typeof data.source_display !== 'string') {
|
|
525
|
-
throw new Error('
|
|
525
|
+
throw new Error('CapFabRenderer strand mode: data.source_display must be a string when present');
|
|
526
526
|
}
|
|
527
527
|
}
|
|
528
528
|
|
|
529
529
|
function validateBodyOutcome(outcome, path) {
|
|
530
530
|
if (!outcome || typeof outcome !== 'object') {
|
|
531
|
-
throw new Error(`
|
|
531
|
+
throw new Error(`CapFabRenderer: ${path} is not an object`);
|
|
532
532
|
}
|
|
533
533
|
if (typeof outcome.body_index !== 'number' || !Number.isInteger(outcome.body_index) || outcome.body_index < 0) {
|
|
534
|
-
throw new Error(`
|
|
534
|
+
throw new Error(`CapFabRenderer: ${path}.body_index must be a non-negative integer`);
|
|
535
535
|
}
|
|
536
536
|
if (typeof outcome.success !== 'boolean') {
|
|
537
|
-
throw new Error(`
|
|
537
|
+
throw new Error(`CapFabRenderer: ${path}.success must be a boolean`);
|
|
538
538
|
}
|
|
539
539
|
assertArray(outcome.cap_urns, `${path}.cap_urns`);
|
|
540
540
|
outcome.cap_urns.forEach((u, i) => assertString(u, `${path}.cap_urns[${i}]`));
|
|
541
541
|
if (outcome.failed_cap !== undefined && outcome.failed_cap !== null
|
|
542
542
|
&& (typeof outcome.failed_cap !== 'string' || outcome.failed_cap.length === 0)) {
|
|
543
|
-
throw new Error(`
|
|
543
|
+
throw new Error(`CapFabRenderer: ${path}.failed_cap must be a non-empty string when present`);
|
|
544
544
|
}
|
|
545
545
|
if (!outcome.success && outcome.failed_cap === undefined) {
|
|
546
546
|
// Failure without a failed_cap is allowed (e.g. infrastructure
|
|
@@ -552,10 +552,10 @@ function validateBodyOutcome(outcome, path) {
|
|
|
552
552
|
|
|
553
553
|
function validateRunPayload(data) {
|
|
554
554
|
if (!data || typeof data !== 'object') {
|
|
555
|
-
throw new Error('
|
|
555
|
+
throw new Error('CapFabRenderer run mode: data must be an object');
|
|
556
556
|
}
|
|
557
557
|
if (!data.resolved_strand || typeof data.resolved_strand !== 'object') {
|
|
558
|
-
throw new Error('
|
|
558
|
+
throw new Error('CapFabRenderer run mode: data.resolved_strand must be an object');
|
|
559
559
|
}
|
|
560
560
|
validateStrandPayload(Object.assign({}, data.resolved_strand, {
|
|
561
561
|
media_display_names: data.media_display_names,
|
|
@@ -565,28 +565,28 @@ function validateRunPayload(data) {
|
|
|
565
565
|
validateBodyOutcome(o, `run mode data.body_outcomes[${idx}]`);
|
|
566
566
|
});
|
|
567
567
|
if (typeof data.visible_success_count !== 'number' || data.visible_success_count < 0) {
|
|
568
|
-
throw new Error('
|
|
568
|
+
throw new Error('CapFabRenderer run mode: data.visible_success_count must be a non-negative number');
|
|
569
569
|
}
|
|
570
570
|
if (typeof data.visible_failure_count !== 'number' || data.visible_failure_count < 0) {
|
|
571
|
-
throw new Error('
|
|
571
|
+
throw new Error('CapFabRenderer run mode: data.visible_failure_count must be a non-negative number');
|
|
572
572
|
}
|
|
573
573
|
if (typeof data.total_body_count !== 'number' || data.total_body_count < 0) {
|
|
574
|
-
throw new Error('
|
|
574
|
+
throw new Error('CapFabRenderer run mode: data.total_body_count must be a non-negative number');
|
|
575
575
|
}
|
|
576
576
|
}
|
|
577
577
|
|
|
578
578
|
function validateEditorGraphPayload(data) {
|
|
579
579
|
if (!data || typeof data !== 'object') {
|
|
580
|
-
throw new Error('
|
|
580
|
+
throw new Error('CapFabRenderer editor-graph mode: data must be an object');
|
|
581
581
|
}
|
|
582
582
|
assertArray(data.elements, 'editor-graph mode data.elements');
|
|
583
583
|
data.elements.forEach((el, idx) => {
|
|
584
584
|
if (!el || typeof el !== 'object') {
|
|
585
|
-
throw new Error(`
|
|
585
|
+
throw new Error(`CapFabRenderer editor-graph mode: data.elements[${idx}] is not an object`);
|
|
586
586
|
}
|
|
587
587
|
if (el.kind !== 'node' && el.kind !== 'cap' && el.kind !== 'edge') {
|
|
588
588
|
throw new Error(
|
|
589
|
-
`
|
|
589
|
+
`CapFabRenderer editor-graph mode: data.elements[${idx}].kind must be "node" | "cap" | "edge" (got: ${JSON.stringify(el.kind)})`
|
|
590
590
|
);
|
|
591
591
|
}
|
|
592
592
|
assertString(el.graph_id, `editor-graph mode data.elements[${idx}].graph_id`);
|
|
@@ -619,17 +619,17 @@ function validateEditorGraphPayload(data) {
|
|
|
619
619
|
// }
|
|
620
620
|
function validateResolvedMachinePayload(data) {
|
|
621
621
|
if (!data || typeof data !== 'object') {
|
|
622
|
-
throw new Error('
|
|
622
|
+
throw new Error('CapFabRenderer machine mode: data must be an object');
|
|
623
623
|
}
|
|
624
624
|
assertArray(data.strands, 'machine mode data.strands');
|
|
625
625
|
data.strands.forEach((strand, sIdx) => {
|
|
626
626
|
if (!strand || typeof strand !== 'object') {
|
|
627
|
-
throw new Error(`
|
|
627
|
+
throw new Error(`CapFabRenderer machine mode: data.strands[${sIdx}] is not an object`);
|
|
628
628
|
}
|
|
629
629
|
assertArray(strand.nodes, `machine mode data.strands[${sIdx}].nodes`);
|
|
630
630
|
strand.nodes.forEach((n, nIdx) => {
|
|
631
631
|
if (!n || typeof n !== 'object') {
|
|
632
|
-
throw new Error(`
|
|
632
|
+
throw new Error(`CapFabRenderer machine mode: data.strands[${sIdx}].nodes[${nIdx}] is not an object`);
|
|
633
633
|
}
|
|
634
634
|
assertString(n.id, `machine mode data.strands[${sIdx}].nodes[${nIdx}].id`);
|
|
635
635
|
assertString(n.urn, `machine mode data.strands[${sIdx}].nodes[${nIdx}].urn`);
|
|
@@ -638,18 +638,18 @@ function validateResolvedMachinePayload(data) {
|
|
|
638
638
|
assertArray(strand.edges, `machine mode data.strands[${sIdx}].edges`);
|
|
639
639
|
strand.edges.forEach((e, eIdx) => {
|
|
640
640
|
if (!e || typeof e !== 'object') {
|
|
641
|
-
throw new Error(`
|
|
641
|
+
throw new Error(`CapFabRenderer machine mode: data.strands[${sIdx}].edges[${eIdx}] is not an object`);
|
|
642
642
|
}
|
|
643
643
|
assertString(e.alias, `machine mode data.strands[${sIdx}].edges[${eIdx}].alias`);
|
|
644
644
|
assertString(e.cap_urn, `machine mode data.strands[${sIdx}].edges[${eIdx}].cap_urn`);
|
|
645
645
|
assertString(e.title, `machine mode data.strands[${sIdx}].edges[${eIdx}].title`);
|
|
646
646
|
if (typeof e.is_loop !== 'boolean') {
|
|
647
|
-
throw new Error(`
|
|
647
|
+
throw new Error(`CapFabRenderer machine mode: data.strands[${sIdx}].edges[${eIdx}].is_loop must be boolean`);
|
|
648
648
|
}
|
|
649
649
|
assertArray(e.assignment, `machine mode data.strands[${sIdx}].edges[${eIdx}].assignment`);
|
|
650
650
|
e.assignment.forEach((b, bIdx) => {
|
|
651
651
|
if (!b || typeof b !== 'object') {
|
|
652
|
-
throw new Error(`
|
|
652
|
+
throw new Error(`CapFabRenderer machine mode: data.strands[${sIdx}].edges[${eIdx}].assignment[${bIdx}] is not an object`);
|
|
653
653
|
}
|
|
654
654
|
assertString(b.cap_arg_media_urn, `machine mode data.strands[${sIdx}].edges[${eIdx}].assignment[${bIdx}].cap_arg_media_urn`);
|
|
655
655
|
assertString(b.source_node, `machine mode data.strands[${sIdx}].edges[${eIdx}].assignment[${bIdx}].source_node`);
|
|
@@ -700,11 +700,11 @@ function buildBrowseGraphData(capabilities) {
|
|
|
700
700
|
|
|
701
701
|
const CapUrn = requireHostDependency('CapUrn');
|
|
702
702
|
const createCap = requireHostDependency('createCap');
|
|
703
|
-
const
|
|
703
|
+
const CapFab = requireHostDependency('CapFab');
|
|
704
704
|
|
|
705
705
|
const nodesMap = new Map();
|
|
706
706
|
const edges = [];
|
|
707
|
-
const
|
|
707
|
+
const capFab = new CapFab();
|
|
708
708
|
const mediaTitles = new Map();
|
|
709
709
|
const capabilitiesByEdgeId = new Map();
|
|
710
710
|
|
|
@@ -729,8 +729,8 @@ function buildBrowseGraphData(capabilities) {
|
|
|
729
729
|
// malformed registry data.
|
|
730
730
|
const parsedUrn = CapUrn.fromString(capData.urn);
|
|
731
731
|
const cap = createCap(parsedUrn, title, capData.command || '');
|
|
732
|
-
const
|
|
733
|
-
|
|
732
|
+
const capFabEdgeIndex = capFab.edges.length;
|
|
733
|
+
capFab.addCap(cap, 'registry');
|
|
734
734
|
|
|
735
735
|
edges.push({
|
|
736
736
|
id: edgeId,
|
|
@@ -738,7 +738,7 @@ function buildBrowseGraphData(capabilities) {
|
|
|
738
738
|
target: outSpec,
|
|
739
739
|
title,
|
|
740
740
|
capability: capData,
|
|
741
|
-
|
|
741
|
+
capFabEdgeIndex,
|
|
742
742
|
});
|
|
743
743
|
capabilitiesByEdgeId.set(edgeId, capData);
|
|
744
744
|
}
|
|
@@ -751,7 +751,7 @@ function buildBrowseGraphData(capabilities) {
|
|
|
751
751
|
for (const node of nodes) {
|
|
752
752
|
if (!mediaTitles.has(node.id)) {
|
|
753
753
|
throw new Error(
|
|
754
|
-
`
|
|
754
|
+
`CapFabRenderer browse mode: missing explicit media title for '${node.id}'`
|
|
755
755
|
);
|
|
756
756
|
}
|
|
757
757
|
}
|
|
@@ -765,7 +765,7 @@ function buildBrowseGraphData(capabilities) {
|
|
|
765
765
|
reverseAdj.get(edge.target).add(edge.source);
|
|
766
766
|
}
|
|
767
767
|
|
|
768
|
-
return { nodes, edges, adjacency, reverseAdj,
|
|
768
|
+
return { nodes, edges, adjacency, reverseAdj, capFab, mediaTitles, capabilitiesByEdgeId };
|
|
769
769
|
}
|
|
770
770
|
|
|
771
771
|
function browseCytoscapeElements(built) {
|
|
@@ -791,7 +791,7 @@ function browseCytoscapeElements(built) {
|
|
|
791
791
|
title: edge.title,
|
|
792
792
|
cardinality,
|
|
793
793
|
fullUrn: edge.capability.urn,
|
|
794
|
-
|
|
794
|
+
capFabEdgeIndex: edge.capFabEdgeIndex,
|
|
795
795
|
color: edge.color,
|
|
796
796
|
},
|
|
797
797
|
};
|
|
@@ -992,7 +992,7 @@ function buildStrandGraphData(data) {
|
|
|
992
992
|
// Outer ForEach with no body caps is an illegal nesting; the
|
|
993
993
|
// plan builder throws. Mirror that.
|
|
994
994
|
throw new Error(
|
|
995
|
-
`
|
|
995
|
+
`CapFabRenderer strand: nested ForEach at step[${i}] but outer ForEach at step[${outer.index}] has no body caps`
|
|
996
996
|
);
|
|
997
997
|
}
|
|
998
998
|
prevNodeId = finalizeOuterForEach(outer, entry, exit);
|
|
@@ -1041,7 +1041,7 @@ function buildStrandGraphData(data) {
|
|
|
1041
1041
|
return;
|
|
1042
1042
|
}
|
|
1043
1043
|
|
|
1044
|
-
throw new Error(`
|
|
1044
|
+
throw new Error(`CapFabRenderer strand: unknown step_type variant '${variant}' at step[${i}]`);
|
|
1045
1045
|
});
|
|
1046
1046
|
|
|
1047
1047
|
// Handle unclosed ForEach after the walk. Mirrors plan_builder.rs:362-428.
|
|
@@ -1936,7 +1936,7 @@ function buildResolvedMachineGraphData(data) {
|
|
|
1936
1936
|
// fail hard rather than silently dropping.
|
|
1937
1937
|
if (seenNodeIds.has(node.id)) {
|
|
1938
1938
|
throw new Error(
|
|
1939
|
-
`
|
|
1939
|
+
`CapFabRenderer machine mode: duplicate node id "${node.id}" in strand ${strandIdx}`
|
|
1940
1940
|
);
|
|
1941
1941
|
}
|
|
1942
1942
|
seenNodeIds.add(node.id);
|
|
@@ -2005,18 +2005,18 @@ function resolvedMachineCytoscapeElements(built) {
|
|
|
2005
2005
|
// Renderer class.
|
|
2006
2006
|
// =============================================================================
|
|
2007
2007
|
|
|
2008
|
-
class
|
|
2008
|
+
class CapFabRenderer {
|
|
2009
2009
|
constructor(containerOrId, options) {
|
|
2010
2010
|
if (options === undefined || options === null) {
|
|
2011
|
-
throw new Error('
|
|
2011
|
+
throw new Error('CapFabRenderer: options object is required');
|
|
2012
2012
|
}
|
|
2013
2013
|
if (typeof options !== 'object') {
|
|
2014
|
-
throw new Error('
|
|
2014
|
+
throw new Error('CapFabRenderer: options must be an object');
|
|
2015
2015
|
}
|
|
2016
2016
|
const mode = options.mode;
|
|
2017
2017
|
if (mode !== 'browse' && mode !== 'strand' && mode !== 'run' && mode !== 'machine' && mode !== 'editor-graph') {
|
|
2018
2018
|
throw new Error(
|
|
2019
|
-
`
|
|
2019
|
+
`CapFabRenderer: options.mode must be one of "browse", "strand", "run", "machine", "editor-graph" (got ${JSON.stringify(mode)})`
|
|
2020
2020
|
);
|
|
2021
2021
|
}
|
|
2022
2022
|
|
|
@@ -2041,12 +2041,12 @@ class CapGraphRenderer {
|
|
|
2041
2041
|
if (typeof containerOrId === 'string') {
|
|
2042
2042
|
container = document.getElementById(containerOrId);
|
|
2043
2043
|
if (!container) {
|
|
2044
|
-
throw new Error(`
|
|
2044
|
+
throw new Error(`CapFabRenderer: container element '${containerOrId}' not found`);
|
|
2045
2045
|
}
|
|
2046
2046
|
} else if (containerOrId instanceof Element) {
|
|
2047
2047
|
container = containerOrId;
|
|
2048
2048
|
} else {
|
|
2049
|
-
throw new Error('
|
|
2049
|
+
throw new Error('CapFabRenderer: first argument must be a container id string or an Element');
|
|
2050
2050
|
}
|
|
2051
2051
|
|
|
2052
2052
|
this.container = container;
|
|
@@ -2070,7 +2070,7 @@ class CapGraphRenderer {
|
|
|
2070
2070
|
this.edges = [];
|
|
2071
2071
|
this.adjacency = new Map();
|
|
2072
2072
|
this.reverseAdj = new Map();
|
|
2073
|
-
this.
|
|
2073
|
+
this.capFab = null;
|
|
2074
2074
|
this.capabilitiesByEdgeId = new Map();
|
|
2075
2075
|
this._mediaTitles = new Map();
|
|
2076
2076
|
this._pendingFocusCap = null;
|
|
@@ -2108,7 +2108,7 @@ class CapGraphRenderer {
|
|
|
2108
2108
|
|
|
2109
2109
|
setNavigator(navigator) {
|
|
2110
2110
|
if (this.mode !== 'browse') {
|
|
2111
|
-
throw new Error(`
|
|
2111
|
+
throw new Error(`CapFabRenderer: setNavigator is only valid in browse mode (current: ${this.mode})`);
|
|
2112
2112
|
}
|
|
2113
2113
|
this.navigator = navigator;
|
|
2114
2114
|
}
|
|
@@ -2124,7 +2124,7 @@ class CapGraphRenderer {
|
|
|
2124
2124
|
this.edges = built.edges;
|
|
2125
2125
|
this.adjacency = built.adjacency;
|
|
2126
2126
|
this.reverseAdj = built.reverseAdj;
|
|
2127
|
-
this.
|
|
2127
|
+
this.capFab = built.capFab;
|
|
2128
2128
|
this._mediaTitles = built.mediaTitles;
|
|
2129
2129
|
this.capabilitiesByEdgeId = built.capabilitiesByEdgeId;
|
|
2130
2130
|
return this;
|
|
@@ -2152,7 +2152,7 @@ class CapGraphRenderer {
|
|
|
2152
2152
|
this._machineBuilt = buildResolvedMachineGraphData(data);
|
|
2153
2153
|
return this;
|
|
2154
2154
|
}
|
|
2155
|
-
throw new Error(`
|
|
2155
|
+
throw new Error(`CapFabRenderer: unreachable mode '${this.mode}'`);
|
|
2156
2156
|
}
|
|
2157
2157
|
|
|
2158
2158
|
// Compatibility shim for capdag-dot-com browse callers: `buildFromCapabilities`
|
|
@@ -2160,7 +2160,7 @@ class CapGraphRenderer {
|
|
|
2160
2160
|
buildFromCapabilities(capabilities) {
|
|
2161
2161
|
if (this.mode !== 'browse') {
|
|
2162
2162
|
throw new Error(
|
|
2163
|
-
`
|
|
2163
|
+
`CapFabRenderer: buildFromCapabilities is only valid in browse mode (current: ${this.mode})`
|
|
2164
2164
|
);
|
|
2165
2165
|
}
|
|
2166
2166
|
return this.setData(capabilities);
|
|
@@ -2172,12 +2172,12 @@ class CapGraphRenderer {
|
|
|
2172
2172
|
|
|
2173
2173
|
render() {
|
|
2174
2174
|
if (!this.container) {
|
|
2175
|
-
throw new Error('
|
|
2175
|
+
throw new Error('CapFabRenderer: container is missing');
|
|
2176
2176
|
}
|
|
2177
2177
|
|
|
2178
2178
|
const elements = this._buildCytoscapeElements();
|
|
2179
2179
|
if (elements.length === 0) {
|
|
2180
|
-
this.container.innerHTML = '<div class="cap-
|
|
2180
|
+
this.container.innerHTML = '<div class="cap-fab-empty"><p>No graph data</p></div>';
|
|
2181
2181
|
return this;
|
|
2182
2182
|
}
|
|
2183
2183
|
|
|
@@ -2258,7 +2258,7 @@ class CapGraphRenderer {
|
|
|
2258
2258
|
if (!this._machineBuilt) return [];
|
|
2259
2259
|
return resolvedMachineCytoscapeElements(this._machineBuilt);
|
|
2260
2260
|
}
|
|
2261
|
-
throw new Error(`
|
|
2261
|
+
throw new Error(`CapFabRenderer: unreachable mode '${this.mode}'`);
|
|
2262
2262
|
}
|
|
2263
2263
|
|
|
2264
2264
|
// ===========================================================================
|
|
@@ -2409,7 +2409,7 @@ class CapGraphRenderer {
|
|
|
2409
2409
|
|
|
2410
2410
|
highlightCapability(cap) {
|
|
2411
2411
|
if (this.mode !== 'browse') {
|
|
2412
|
-
throw new Error(`
|
|
2412
|
+
throw new Error(`CapFabRenderer: highlightCapability is only valid in browse mode (current: ${this.mode})`);
|
|
2413
2413
|
}
|
|
2414
2414
|
if (!this.cy || !this._layoutReady) {
|
|
2415
2415
|
this._pendingFocusCap = cap;
|
|
@@ -2435,10 +2435,10 @@ class CapGraphRenderer {
|
|
|
2435
2435
|
|
|
2436
2436
|
_capUrnString(cap) {
|
|
2437
2437
|
if (!cap || typeof cap !== 'object') {
|
|
2438
|
-
throw new Error('
|
|
2438
|
+
throw new Error('CapFabRenderer: cap must be an object');
|
|
2439
2439
|
}
|
|
2440
2440
|
if (typeof cap.urn !== 'string' || cap.urn.length === 0) {
|
|
2441
|
-
throw new Error('
|
|
2441
|
+
throw new Error('CapFabRenderer: cap.urn must be a non-empty string');
|
|
2442
2442
|
}
|
|
2443
2443
|
return cap.urn;
|
|
2444
2444
|
}
|
|
@@ -2472,7 +2472,7 @@ class CapGraphRenderer {
|
|
|
2472
2472
|
|
|
2473
2473
|
selectEdgeByCapUrn(capUrnString) {
|
|
2474
2474
|
if (this.mode !== 'browse') {
|
|
2475
|
-
throw new Error(`
|
|
2475
|
+
throw new Error(`CapFabRenderer: selectEdgeByCapUrn is only valid in browse mode (current: ${this.mode})`);
|
|
2476
2476
|
}
|
|
2477
2477
|
if (!this.cy || typeof capUrnString !== 'string' || capUrnString.length === 0) return;
|
|
2478
2478
|
const CapUrn = requireHostDependency('CapUrn');
|
|
@@ -2523,7 +2523,7 @@ class CapGraphRenderer {
|
|
|
2523
2523
|
|
|
2524
2524
|
applyEditorGraphActiveTokenIds(tokenIds) {
|
|
2525
2525
|
if (this.mode !== 'editor-graph') {
|
|
2526
|
-
throw new Error(`
|
|
2526
|
+
throw new Error(`CapFabRenderer: applyEditorGraphActiveTokenIds is only valid in editor-graph mode (current: ${this.mode})`);
|
|
2527
2527
|
}
|
|
2528
2528
|
if (!this.cy) return;
|
|
2529
2529
|
const wanted = new Set(tokenIds || []);
|
|
@@ -2545,16 +2545,16 @@ class CapGraphRenderer {
|
|
|
2545
2545
|
|
|
2546
2546
|
enterPathMode(sourceId, targetId) {
|
|
2547
2547
|
if (this.mode !== 'browse') {
|
|
2548
|
-
throw new Error(`
|
|
2548
|
+
throw new Error(`CapFabRenderer: enterPathMode is only valid in browse mode (current: ${this.mode})`);
|
|
2549
2549
|
}
|
|
2550
|
-
if (!this.
|
|
2550
|
+
if (!this.capFab) return;
|
|
2551
2551
|
|
|
2552
2552
|
const MAX_PATHS = 10;
|
|
2553
|
-
let paths = this.
|
|
2553
|
+
let paths = this.capFab.findAllPaths(sourceId, targetId, MAX_PATHS);
|
|
2554
2554
|
let actualSource = sourceId;
|
|
2555
2555
|
let actualTarget = targetId;
|
|
2556
2556
|
if (paths.length === 0) {
|
|
2557
|
-
const reverse = this.
|
|
2557
|
+
const reverse = this.capFab.findAllPaths(targetId, sourceId, MAX_PATHS);
|
|
2558
2558
|
if (reverse.length === 0) return;
|
|
2559
2559
|
paths = reverse;
|
|
2560
2560
|
actualSource = targetId;
|
|
@@ -2594,7 +2594,7 @@ class CapGraphRenderer {
|
|
|
2594
2594
|
for (const pathEdge of pathEdges) {
|
|
2595
2595
|
pathNodeIds.add(canonicalMediaUrn(pathEdge.fromUrn));
|
|
2596
2596
|
pathNodeIds.add(canonicalMediaUrn(pathEdge.toUrn));
|
|
2597
|
-
const idx = this.
|
|
2597
|
+
const idx = this.capFab.edges.indexOf(pathEdge);
|
|
2598
2598
|
if (idx !== -1) pathEdgeIndices.add(idx);
|
|
2599
2599
|
}
|
|
2600
2600
|
|
|
@@ -2605,7 +2605,7 @@ class CapGraphRenderer {
|
|
|
2605
2605
|
if (pathNodeIds.has(node.id())) node.removeClass('faded').addClass('path-highlighted');
|
|
2606
2606
|
});
|
|
2607
2607
|
this.cy.edges().forEach(edge => {
|
|
2608
|
-
const cyIdx = edge.data('
|
|
2608
|
+
const cyIdx = edge.data('capFabEdgeIndex');
|
|
2609
2609
|
if (cyIdx !== undefined && pathEdgeIndices.has(cyIdx)) {
|
|
2610
2610
|
edge.removeClass('faded').addClass('path-highlighted');
|
|
2611
2611
|
}
|
|
@@ -2827,12 +2827,12 @@ class CapGraphRenderer {
|
|
|
2827
2827
|
// =============================================================================
|
|
2828
2828
|
// Module exports — CJS for Node tests. Browser-side the build-browser.js
|
|
2829
2829
|
// concatenation wraps these declarations in an IIFE and assigns
|
|
2830
|
-
// `window.
|
|
2830
|
+
// `window.CapFabRenderer`.
|
|
2831
2831
|
// =============================================================================
|
|
2832
2832
|
|
|
2833
2833
|
if (typeof module !== 'undefined' && module.exports) {
|
|
2834
2834
|
module.exports = {
|
|
2835
|
-
|
|
2835
|
+
CapFabRenderer,
|
|
2836
2836
|
cardinalityLabel,
|
|
2837
2837
|
cardinalityFromCap,
|
|
2838
2838
|
canonicalMediaUrn,
|
package/capdag.js
CHANGED
|
@@ -3142,13 +3142,13 @@ class CapArgumentValue {
|
|
|
3142
3142
|
|
|
3143
3143
|
|
|
3144
3144
|
// ============================================================================
|
|
3145
|
-
//
|
|
3145
|
+
// CAPFAB - Directed graph of capability conversions
|
|
3146
3146
|
// ============================================================================
|
|
3147
3147
|
|
|
3148
3148
|
/**
|
|
3149
3149
|
* An edge in the capability graph representing a conversion from one media URN to another.
|
|
3150
3150
|
*/
|
|
3151
|
-
class
|
|
3151
|
+
class CapFabEdge {
|
|
3152
3152
|
/**
|
|
3153
3153
|
* @param {string} fromUrn - The input media URN
|
|
3154
3154
|
* @param {string} toUrn - The output media URN
|
|
@@ -3168,7 +3168,7 @@ class CapGraphEdge {
|
|
|
3168
3168
|
/**
|
|
3169
3169
|
* Statistics about a capability graph.
|
|
3170
3170
|
*/
|
|
3171
|
-
class
|
|
3171
|
+
class CapFabStats {
|
|
3172
3172
|
/**
|
|
3173
3173
|
* @param {number} nodeCount - Number of unique media URN nodes
|
|
3174
3174
|
* @param {number} edgeCount - Number of edges (capabilities)
|
|
@@ -3187,7 +3187,7 @@ class CapGraphStats {
|
|
|
3187
3187
|
* A directed graph where nodes are media URNs and edges are capabilities.
|
|
3188
3188
|
* This graph enables discovering conversion paths between different media formats.
|
|
3189
3189
|
*/
|
|
3190
|
-
class
|
|
3190
|
+
class CapFab {
|
|
3191
3191
|
constructor() {
|
|
3192
3192
|
this.edges = [];
|
|
3193
3193
|
this.outgoing = new Map(); // fromUrn -> edge indices
|
|
@@ -3211,7 +3211,7 @@ class CapGraph {
|
|
|
3211
3211
|
|
|
3212
3212
|
// Create edge
|
|
3213
3213
|
const edgeIndex = this.edges.length;
|
|
3214
|
-
const edge = new
|
|
3214
|
+
const edge = new CapFabEdge(fromUrn, toUrn, cap, registryName, specificity);
|
|
3215
3215
|
this.edges.push(edge);
|
|
3216
3216
|
|
|
3217
3217
|
// Update outgoing index
|
|
@@ -3237,7 +3237,7 @@ class CapGraph {
|
|
|
3237
3237
|
|
|
3238
3238
|
/**
|
|
3239
3239
|
* Get all edges in the graph.
|
|
3240
|
-
* @returns {
|
|
3240
|
+
* @returns {CapFabEdge[]}
|
|
3241
3241
|
*/
|
|
3242
3242
|
getEdges() {
|
|
3243
3243
|
return [...this.edges];
|
|
@@ -3247,7 +3247,7 @@ class CapGraph {
|
|
|
3247
3247
|
* Get all edges where the provided URN satisfies the edge's input requirement.
|
|
3248
3248
|
* Uses conformsTo-based matching instead of exact string matching.
|
|
3249
3249
|
* @param {string} urn - The media URN
|
|
3250
|
-
* @returns {
|
|
3250
|
+
* @returns {CapFabEdge[]}
|
|
3251
3251
|
*/
|
|
3252
3252
|
getOutgoing(urn) {
|
|
3253
3253
|
// Use TaggedUrn matching: find all edges where the provided URN (instance)
|
|
@@ -3268,7 +3268,7 @@ class CapGraph {
|
|
|
3268
3268
|
/**
|
|
3269
3269
|
* Get all edges targeting a media URN.
|
|
3270
3270
|
* @param {string} urn - The media URN
|
|
3271
|
-
* @returns {
|
|
3271
|
+
* @returns {CapFabEdge[]}
|
|
3272
3272
|
*/
|
|
3273
3273
|
getIncoming(urn) {
|
|
3274
3274
|
const indices = this.incoming.get(urn) || [];
|
|
@@ -3289,7 +3289,7 @@ class CapGraph {
|
|
|
3289
3289
|
* Get all direct edges from one URN to another, sorted by specificity (highest first).
|
|
3290
3290
|
* @param {string} fromUrn - The source media URN
|
|
3291
3291
|
* @param {string} toUrn - The target media URN
|
|
3292
|
-
* @returns {
|
|
3292
|
+
* @returns {CapFabEdge[]}
|
|
3293
3293
|
*/
|
|
3294
3294
|
getDirectEdges(fromUrn, toUrn) {
|
|
3295
3295
|
const edges = this.getOutgoing(fromUrn).filter(edge => edge.toUrn === toUrn);
|
|
@@ -3338,7 +3338,7 @@ class CapGraph {
|
|
|
3338
3338
|
* Find the shortest conversion path from one URN to another.
|
|
3339
3339
|
* @param {string} fromUrn - The source media URN
|
|
3340
3340
|
* @param {string} toUrn - The target media URN
|
|
3341
|
-
* @returns {
|
|
3341
|
+
* @returns {CapFabEdge[]|null} Array of edges representing the path, or null if no path exists
|
|
3342
3342
|
*/
|
|
3343
3343
|
findPath(fromUrn, toUrn) {
|
|
3344
3344
|
if (fromUrn === toUrn) {
|
|
@@ -3393,7 +3393,7 @@ class CapGraph {
|
|
|
3393
3393
|
* @param {string} fromUrn - The source media URN
|
|
3394
3394
|
* @param {string} toUrn - The target media URN
|
|
3395
3395
|
* @param {number} maxDepth - Maximum path length to search
|
|
3396
|
-
* @returns {
|
|
3396
|
+
* @returns {CapFabEdge[][]} Array of paths (each path is an array of edges)
|
|
3397
3397
|
*/
|
|
3398
3398
|
findAllPaths(fromUrn, toUrn, maxDepth) {
|
|
3399
3399
|
if (!this.nodes.has(fromUrn) || !this.nodes.has(toUrn)) {
|
|
@@ -3447,7 +3447,7 @@ class CapGraph {
|
|
|
3447
3447
|
* @param {string} fromUrn - The source media URN
|
|
3448
3448
|
* @param {string} toUrn - The target media URN
|
|
3449
3449
|
* @param {number} maxDepth - Maximum path length to search
|
|
3450
|
-
* @returns {
|
|
3450
|
+
* @returns {CapFabEdge[]|null} Array of edges representing the best path, or null if no path exists
|
|
3451
3451
|
*/
|
|
3452
3452
|
findBestPath(fromUrn, toUrn, maxDepth) {
|
|
3453
3453
|
const allPaths = this.findAllPaths(fromUrn, toUrn, maxDepth);
|
|
@@ -3488,10 +3488,10 @@ class CapGraph {
|
|
|
3488
3488
|
|
|
3489
3489
|
/**
|
|
3490
3490
|
* Get statistics about the graph.
|
|
3491
|
-
* @returns {
|
|
3491
|
+
* @returns {CapFabStats}
|
|
3492
3492
|
*/
|
|
3493
3493
|
stats() {
|
|
3494
|
-
return new
|
|
3494
|
+
return new CapFabStats(
|
|
3495
3495
|
this.nodes.size,
|
|
3496
3496
|
this.edges.length,
|
|
3497
3497
|
this.outgoing.size,
|
|
@@ -5183,16 +5183,16 @@ class MachineBuilder {
|
|
|
5183
5183
|
}
|
|
5184
5184
|
|
|
5185
5185
|
/**
|
|
5186
|
-
* Add a linear chain of edges from
|
|
5186
|
+
* Add a linear chain of edges from CapFabEdge[] (from CapFab.findAllPaths).
|
|
5187
5187
|
*
|
|
5188
|
-
* Each
|
|
5188
|
+
* Each CapFabEdge has fromUrn, toUrn, and cap (with cap.urn).
|
|
5189
5189
|
* This converts the path into a series of MachineEdges.
|
|
5190
5190
|
*
|
|
5191
|
-
* @param {
|
|
5191
|
+
* @param {CapFabEdge[]} capFabEdges - Array of CapFabEdge from pathfinding
|
|
5192
5192
|
* @returns {MachineBuilder} this (for chaining)
|
|
5193
5193
|
*/
|
|
5194
|
-
|
|
5195
|
-
for (const edge of
|
|
5194
|
+
addCapFabPath(capFabEdges) {
|
|
5195
|
+
for (const edge of capFabEdges) {
|
|
5196
5196
|
const source = MediaUrn.fromString(edge.fromUrn);
|
|
5197
5197
|
const target = MediaUrn.fromString(edge.toUrn);
|
|
5198
5198
|
this._edges.push(new MachineEdge([source], edge.cap.urn, target, false));
|
|
@@ -5501,9 +5501,9 @@ module.exports = {
|
|
|
5501
5501
|
mediaUrnForType,
|
|
5502
5502
|
modelAvailabilityUrn,
|
|
5503
5503
|
modelPathUrn,
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5504
|
+
CapFabEdge,
|
|
5505
|
+
CapFabStats,
|
|
5506
|
+
CapFab,
|
|
5507
5507
|
StdinSource,
|
|
5508
5508
|
StdinSourceKind,
|
|
5509
5509
|
// Cartridge Repository
|
package/capdag.test.js
CHANGED
|
@@ -8,7 +8,7 @@ const {
|
|
|
8
8
|
Cap, MediaSpec, MediaSpecError, MediaSpecErrorCodes,
|
|
9
9
|
resolveMediaUrn, buildExtensionIndex, mediaUrnsForExtension, getExtensionMappings,
|
|
10
10
|
CartridgeInfo, CartridgeCapSummary, CartridgeSuggestion, CartridgeRepoClient, CartridgeRepoServer,
|
|
11
|
-
|
|
11
|
+
CapFabEdge, CapFabStats, CapFab,
|
|
12
12
|
StdinSource, StdinSourceKind,
|
|
13
13
|
validateNoMediaSpecRedefinitionSync,
|
|
14
14
|
CapArgumentValue, CapArg, ArgSource, validateCapArgs, ValidationError,
|
|
@@ -1144,12 +1144,12 @@ function test110_multipleExtensions() {
|
|
|
1144
1144
|
}
|
|
1145
1145
|
|
|
1146
1146
|
// ============================================================================
|
|
1147
|
-
//
|
|
1147
|
+
// cap_fab: browse-mode API used by cap-fab-renderer.js
|
|
1148
1148
|
//
|
|
1149
1149
|
// The renderer builds its browse graph by:
|
|
1150
|
-
// const
|
|
1151
|
-
// for each cap in /api/capabilities:
|
|
1152
|
-
// ... then reads
|
|
1150
|
+
// const capFab = new CapFab();
|
|
1151
|
+
// for each cap in /api/capabilities: capFab.addCap(cap, 'registry');
|
|
1152
|
+
// ... then reads capFab.edges / getOutgoing(urn) / etc.
|
|
1153
1153
|
//
|
|
1154
1154
|
// These tests lock in that specific contract. They do NOT cover
|
|
1155
1155
|
// buildFromRegistries / CapMatrix / CapBlock — all deleted with the dead
|
|
@@ -1158,8 +1158,8 @@ function test110_multipleExtensions() {
|
|
|
1158
1158
|
|
|
1159
1159
|
// Add a cap and check it becomes an edge with from/to nodes and carries the
|
|
1160
1160
|
// registry name we passed. This is exactly the shape the renderer depends on.
|
|
1161
|
-
function
|
|
1162
|
-
const graph = new
|
|
1161
|
+
function testCapFabAddCapPopulatesEdgesAndNodes() {
|
|
1162
|
+
const graph = new CapFab();
|
|
1163
1163
|
const cap = makeGraphCap('media:pdf', 'media:textable', 'PDF to Text');
|
|
1164
1164
|
graph.addCap(cap, 'registry');
|
|
1165
1165
|
|
|
@@ -1176,8 +1176,8 @@ function testCapGraphAddCapPopulatesEdgesAndNodes() {
|
|
|
1176
1176
|
|
|
1177
1177
|
// getOutgoing takes a concrete source URN and returns edges whose from_spec
|
|
1178
1178
|
// the source conforms to. It must NOT be a plain string lookup.
|
|
1179
|
-
function
|
|
1180
|
-
const graph = new
|
|
1179
|
+
function testCapFabGetOutgoingConformsToMatching() {
|
|
1180
|
+
const graph = new CapFab();
|
|
1181
1181
|
graph.addCap(makeGraphCap('media:textable', 'media:embedding-vector', 'Embed text'), 'registry');
|
|
1182
1182
|
|
|
1183
1183
|
// 'media:txt;textable' conforms to 'media:textable' — renderer relies on
|
|
@@ -1197,8 +1197,8 @@ function testCapGraphGetOutgoingConformsToMatching() {
|
|
|
1197
1197
|
|
|
1198
1198
|
// Each edge must carry the registry name it was added with. This is how
|
|
1199
1199
|
// the renderer colours/groups edges by provenance in browse mode.
|
|
1200
|
-
function
|
|
1201
|
-
const graph = new
|
|
1200
|
+
function testCapFabDistinctRegistryNames() {
|
|
1201
|
+
const graph = new CapFab();
|
|
1202
1202
|
graph.addCap(makeGraphCap('media:pdf', 'media:textable', 'PDF to Text'), 'providers');
|
|
1203
1203
|
graph.addCap(makeGraphCap('media:textable', 'media:embedding-vector', 'Embed'), 'cartridges');
|
|
1204
1204
|
|
|
@@ -3665,7 +3665,7 @@ function assertThrowsWithCode(fn, expectedCode) {
|
|
|
3665
3665
|
}
|
|
3666
3666
|
|
|
3667
3667
|
// ============================================================================
|
|
3668
|
-
// cap-
|
|
3668
|
+
// cap-fab-renderer helpers — pure functions that do not require a DOM.
|
|
3669
3669
|
// The renderer class itself needs cytoscape + DOM and is exercised by hand
|
|
3670
3670
|
// in the browser; these tests cover the pure data transforms underneath it.
|
|
3671
3671
|
// ============================================================================
|
|
@@ -3688,7 +3688,7 @@ const {
|
|
|
3688
3688
|
validateResolvedMachinePayload: rendererValidateResolvedMachinePayload,
|
|
3689
3689
|
validateStrandStep: rendererValidateStrandStep,
|
|
3690
3690
|
validateBodyOutcome: rendererValidateBodyOutcome,
|
|
3691
|
-
} = require('./cap-
|
|
3691
|
+
} = require('./cap-fab-renderer.js');
|
|
3692
3692
|
|
|
3693
3693
|
// The renderer module reads its dependencies off `window` or `global` at
|
|
3694
3694
|
// call time (it is browser-first). Node has no window, so we install the
|
|
@@ -3700,7 +3700,7 @@ if (typeof global.TaggedUrn === 'undefined') {
|
|
|
3700
3700
|
if (typeof global.MediaUrn === 'undefined') global.MediaUrn = MediaUrn;
|
|
3701
3701
|
if (typeof global.CapUrn === 'undefined') global.CapUrn = CapUrn;
|
|
3702
3702
|
if (typeof global.Cap === 'undefined') global.Cap = Cap;
|
|
3703
|
-
if (typeof global.
|
|
3703
|
+
if (typeof global.CapFab === 'undefined') global.CapFab = CapFab;
|
|
3704
3704
|
// Reference the top-of-file destructured createCap via the module export.
|
|
3705
3705
|
if (typeof global.createCap === 'undefined') {
|
|
3706
3706
|
global.createCap = require('./capdag.js').createCap;
|
|
@@ -5369,13 +5369,13 @@ async function runTests() {
|
|
|
5369
5369
|
runTest('TEST109: extensions_with_metadata_and_validation', test109_extensionsWithMetadataAndValidation);
|
|
5370
5370
|
runTest('TEST110: multiple_extensions', test110_multipleExtensions);
|
|
5371
5371
|
|
|
5372
|
-
// cap-
|
|
5372
|
+
// cap-fab-renderer.js uses CapFab in browse mode (static registry from
|
|
5373
5373
|
// /api/capabilities). These tests guard the minimal API the renderer relies
|
|
5374
|
-
// on: new
|
|
5375
|
-
console.log('\n---
|
|
5376
|
-
runTest('
|
|
5377
|
-
runTest('
|
|
5378
|
-
runTest('
|
|
5374
|
+
// on: new CapFab(), addCap(cap, registryName), getEdges(), getOutgoing().
|
|
5375
|
+
console.log('\n--- cap_fab (browse-mode API used by cap-fab-renderer) ---');
|
|
5376
|
+
runTest('cap_fab: add_cap_populates_edges_and_nodes', testCapFabAddCapPopulatesEdgesAndNodes);
|
|
5377
|
+
runTest('cap_fab: get_outgoing_conforms_to_matching', testCapFabGetOutgoingConformsToMatching);
|
|
5378
|
+
runTest('cap_fab: distinct_registry_names_recorded_per_edge', testCapFabDistinctRegistryNames);
|
|
5379
5379
|
|
|
5380
5380
|
// caller.rs: TEST156-TEST159
|
|
5381
5381
|
console.log('\n--- caller.rs (StdinSource) ---');
|
|
@@ -5591,8 +5591,8 @@ async function runTests() {
|
|
|
5591
5591
|
runTest('REGISTRY: capRegistryClient_construction', testMachine_capRegistryClient_construction);
|
|
5592
5592
|
runTest('REGISTRY: capRegistryEntry_defaults', testMachine_capRegistryEntry_defaults);
|
|
5593
5593
|
|
|
5594
|
-
// cap-
|
|
5595
|
-
console.log('\n--- cap-
|
|
5594
|
+
// cap-fab-renderer pure helpers (no DOM dependency)
|
|
5595
|
+
console.log('\n--- cap-fab-renderer helpers ---');
|
|
5596
5596
|
runTest('RENDERER: cardinalityLabel_allFourCases', testRenderer_cardinalityLabel_allFourCases);
|
|
5597
5597
|
runTest('RENDERER: cardinalityLabel_usesUnicodeArrow', testRenderer_cardinalityLabel_usesUnicodeArrow);
|
|
5598
5598
|
runTest('RENDERER: cardinalityFromCap_findsStdinArg', testRenderer_cardinalityFromCap_findsStdinArgNotFirstArg);
|
|
@@ -5606,7 +5606,7 @@ async function runTests() {
|
|
|
5606
5606
|
runTest('RENDERER: mediaNodeLabel_rejectsUrnDerived', testRenderer_mediaNodeLabel_rejectsUrnDerivedLabels);
|
|
5607
5607
|
runTest('RENDERER: buildBrowse_rejectsMissingMediaTitles', testRenderer_buildBrowseGraphData_rejectsMissingMediaTitles);
|
|
5608
5608
|
|
|
5609
|
-
console.log('\n--- cap-
|
|
5609
|
+
console.log('\n--- cap-fab-renderer strand builder ---');
|
|
5610
5610
|
runTest('RENDERER: validateStrandStep_unknownVariant', testRenderer_validateStrandStep_rejectsUnknownVariant);
|
|
5611
5611
|
runTest('RENDERER: validateStrandStep_booleanIsSequence', testRenderer_validateStrandStep_requiresBooleanIsSequence);
|
|
5612
5612
|
runTest('RENDERER: classifyStrandCapSteps_simple', testRenderer_classifyStrandCapSteps_capFlags);
|
|
@@ -5625,7 +5625,7 @@ async function runTests() {
|
|
|
5625
5625
|
runTest('RENDERER: collapseStrand_plainCapDistinctTarget', testRenderer_collapseStrand_plainCapDistinctTargetNoMerge);
|
|
5626
5626
|
runTest('RENDERER: validateStrand_missingSourceSpec', testRenderer_validateStrandPayload_missingSourceSpec);
|
|
5627
5627
|
|
|
5628
|
-
console.log('\n--- cap-
|
|
5628
|
+
console.log('\n--- cap-fab-renderer run builder ---');
|
|
5629
5629
|
runTest('RENDERER: validateBodyOutcome_negativeIndex', testRenderer_validateBodyOutcome_rejectsNegativeIndex);
|
|
5630
5630
|
runTest('RENDERER: buildRun_pagesSuccessesAndFailures', testRenderer_buildRunGraphData_pagesSuccessesAndFailures);
|
|
5631
5631
|
runTest('RENDERER: buildRun_failureWithoutFailedCap', testRenderer_buildRunGraphData_failureWithoutFailedCapRendersFullTrace);
|
|
@@ -5635,7 +5635,7 @@ async function runTests() {
|
|
|
5635
5635
|
runTest('RENDERER: buildRun_unclosedForeachNoMerge', testRenderer_buildRunGraphData_unclosedForeachSuccessNoMerge);
|
|
5636
5636
|
runTest('RENDERER: buildRun_closedForeachMerges', testRenderer_buildRunGraphData_closedForeachSuccessMergesAtCollectTarget);
|
|
5637
5637
|
|
|
5638
|
-
console.log('\n--- cap-
|
|
5638
|
+
console.log('\n--- cap-fab-renderer editor-graph builder ---');
|
|
5639
5639
|
runTest('RENDERER: validateEditorGraph_unknownKind', testRenderer_validateEditorGraphPayload_rejectsUnknownKind);
|
|
5640
5640
|
runTest('RENDERER: buildEditorGraph_collapsesCapsIntoEdges', testRenderer_buildEditorGraphData_collapsesCapsIntoLabeledEdges);
|
|
5641
5641
|
runTest('RENDERER: buildEditorGraph_loopEdgeGetsClass', testRenderer_buildEditorGraphData_loopMarkedEdgeGetsLoopClass);
|
|
@@ -5643,7 +5643,7 @@ async function runTests() {
|
|
|
5643
5643
|
runTest('RENDERER: buildEditorGraph_incompleteCapDropped', testRenderer_buildEditorGraphData_capWithoutCompleteArgsIsDropped);
|
|
5644
5644
|
runTest('RENDERER: buildEditorGraph_rejectsEdgeMissingSrc', testRenderer_buildEditorGraphData_rejectsEdgeWithMissingSource);
|
|
5645
5645
|
|
|
5646
|
-
console.log('\n--- cap-
|
|
5646
|
+
console.log('\n--- cap-fab-renderer resolved-machine builder ---');
|
|
5647
5647
|
runTest('RENDERER: buildResolvedMachine_singleStrandLinear', testRenderer_buildResolvedMachineGraphData_singleStrandLinearChain);
|
|
5648
5648
|
runTest('RENDERER: buildResolvedMachine_loopGetsLoopClass', testRenderer_buildResolvedMachineGraphData_loopEdgeGetsLoopClass);
|
|
5649
5649
|
runTest('RENDERER: buildResolvedMachine_fanInOneEdgePerSrc', testRenderer_buildResolvedMachineGraphData_fanInProducesEdgePerAssignment);
|
package/package.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"files": [
|
|
12
12
|
"capdag.js",
|
|
13
|
-
"cap-
|
|
13
|
+
"cap-fab-renderer.js",
|
|
14
14
|
"build-browser.js",
|
|
15
15
|
"capdag.test.js",
|
|
16
16
|
"machine.pegjs",
|
|
@@ -40,5 +40,5 @@
|
|
|
40
40
|
"pretest": "npm run build:parser",
|
|
41
41
|
"test": "node capdag.test.js"
|
|
42
42
|
},
|
|
43
|
-
"version": "0.
|
|
43
|
+
"version": "0.157.363"
|
|
44
44
|
}
|