capdag 0.153.347 → 0.158.370
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 +455 -23
- 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,
|