@memberjunction/core-actions 2.106.0 → 2.108.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/dist/config.d.ts +112 -0
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/config.js +34 -2
  4. package/dist/config.js.map +1 -1
  5. package/dist/custom/ai/find-best-agent.action.d.ts +53 -0
  6. package/dist/custom/ai/find-best-agent.action.d.ts.map +1 -0
  7. package/dist/custom/ai/find-best-agent.action.js +194 -0
  8. package/dist/custom/ai/find-best-agent.action.js.map +1 -0
  9. package/dist/custom/ai/summarize-content.action.d.ts +72 -0
  10. package/dist/custom/ai/summarize-content.action.d.ts.map +1 -0
  11. package/dist/custom/ai/summarize-content.action.js +296 -0
  12. package/dist/custom/ai/summarize-content.action.js.map +1 -0
  13. package/dist/custom/code-execution/execute-code.action.d.ts +61 -0
  14. package/dist/custom/code-execution/execute-code.action.d.ts.map +1 -0
  15. package/dist/custom/code-execution/execute-code.action.js +172 -0
  16. package/dist/custom/code-execution/execute-code.action.js.map +1 -0
  17. package/dist/custom/data/csv-parser.action.js +1 -1
  18. package/dist/custom/data/csv-parser.action.js.map +1 -1
  19. package/dist/custom/data/execute-research-query.action.d.ts +114 -0
  20. package/dist/custom/data/execute-research-query.action.d.ts.map +1 -0
  21. package/dist/custom/data/execute-research-query.action.js +520 -0
  22. package/dist/custom/data/execute-research-query.action.js.map +1 -0
  23. package/dist/custom/data/explore-database-schema.action.d.ts +95 -0
  24. package/dist/custom/data/explore-database-schema.action.d.ts.map +1 -0
  25. package/dist/custom/data/explore-database-schema.action.js +499 -0
  26. package/dist/custom/data/explore-database-schema.action.js.map +1 -0
  27. package/dist/custom/data/get-entity-details.action.d.ts +63 -0
  28. package/dist/custom/data/get-entity-details.action.d.ts.map +1 -0
  29. package/dist/custom/data/get-entity-details.action.js +284 -0
  30. package/dist/custom/data/get-entity-details.action.js.map +1 -0
  31. package/dist/custom/data/get-entity-list.action.d.ts +57 -0
  32. package/dist/custom/data/get-entity-list.action.d.ts.map +1 -0
  33. package/dist/custom/data/get-entity-list.action.js +177 -0
  34. package/dist/custom/data/get-entity-list.action.js.map +1 -0
  35. package/dist/custom/files/index.d.ts +2 -0
  36. package/dist/custom/files/index.d.ts.map +1 -1
  37. package/dist/custom/files/index.js +5 -1
  38. package/dist/custom/files/index.js.map +1 -1
  39. package/dist/custom/files/list-storage-providers.action.d.ts +52 -0
  40. package/dist/custom/files/list-storage-providers.action.d.ts.map +1 -0
  41. package/dist/custom/files/list-storage-providers.action.js +175 -0
  42. package/dist/custom/files/list-storage-providers.action.js.map +1 -0
  43. package/dist/custom/files/search-storage-files.action.d.ts +61 -0
  44. package/dist/custom/files/search-storage-files.action.d.ts.map +1 -0
  45. package/dist/custom/files/search-storage-files.action.js +183 -0
  46. package/dist/custom/files/search-storage-files.action.js.map +1 -0
  47. package/dist/custom/visualization/create-svg-chart.action.d.ts +97 -0
  48. package/dist/custom/visualization/create-svg-chart.action.d.ts.map +1 -0
  49. package/dist/custom/visualization/create-svg-chart.action.js +290 -0
  50. package/dist/custom/visualization/create-svg-chart.action.js.map +1 -0
  51. package/dist/custom/visualization/create-svg-diagram.action.d.ts +151 -0
  52. package/dist/custom/visualization/create-svg-diagram.action.d.ts.map +1 -0
  53. package/dist/custom/visualization/create-svg-diagram.action.js +764 -0
  54. package/dist/custom/visualization/create-svg-diagram.action.js.map +1 -0
  55. package/dist/custom/visualization/create-svg-infographic.action.d.ts +72 -0
  56. package/dist/custom/visualization/create-svg-infographic.action.d.ts.map +1 -0
  57. package/dist/custom/visualization/create-svg-infographic.action.js +343 -0
  58. package/dist/custom/visualization/create-svg-infographic.action.js.map +1 -0
  59. package/dist/custom/visualization/create-svg-network.action.d.ts +106 -0
  60. package/dist/custom/visualization/create-svg-network.action.d.ts.map +1 -0
  61. package/dist/custom/visualization/create-svg-network.action.js +651 -0
  62. package/dist/custom/visualization/create-svg-network.action.js.map +1 -0
  63. package/dist/custom/visualization/create-svg-word-cloud.action.d.ts +94 -0
  64. package/dist/custom/visualization/create-svg-word-cloud.action.d.ts.map +1 -0
  65. package/dist/custom/visualization/create-svg-word-cloud.action.js +422 -0
  66. package/dist/custom/visualization/create-svg-word-cloud.action.js.map +1 -0
  67. package/dist/custom/visualization/shared/svg-theming.d.ts +91 -0
  68. package/dist/custom/visualization/shared/svg-theming.d.ts.map +1 -0
  69. package/dist/custom/visualization/shared/svg-theming.js +235 -0
  70. package/dist/custom/visualization/shared/svg-theming.js.map +1 -0
  71. package/dist/custom/visualization/shared/svg-types.d.ts +338 -0
  72. package/dist/custom/visualization/shared/svg-types.d.ts.map +1 -0
  73. package/dist/custom/visualization/shared/svg-types.js +11 -0
  74. package/dist/custom/visualization/shared/svg-types.js.map +1 -0
  75. package/dist/custom/visualization/shared/svg-utils.d.ts +129 -0
  76. package/dist/custom/visualization/shared/svg-utils.d.ts.map +1 -0
  77. package/dist/custom/visualization/shared/svg-utils.js +287 -0
  78. package/dist/custom/visualization/shared/svg-utils.js.map +1 -0
  79. package/dist/custom/web/google-custom-search.action.d.ts +60 -0
  80. package/dist/custom/web/google-custom-search.action.d.ts.map +1 -0
  81. package/dist/custom/web/google-custom-search.action.js +378 -0
  82. package/dist/custom/web/google-custom-search.action.js.map +1 -0
  83. package/dist/custom/web/web-page-content.action.d.ts +24 -0
  84. package/dist/custom/web/web-page-content.action.d.ts.map +1 -1
  85. package/dist/custom/web/web-page-content.action.js +90 -9
  86. package/dist/custom/web/web-page-content.action.js.map +1 -1
  87. package/dist/generated/action_subclasses.d.ts +51 -3
  88. package/dist/generated/action_subclasses.d.ts.map +1 -1
  89. package/dist/generated/action_subclasses.js +174 -90
  90. package/dist/generated/action_subclasses.js.map +1 -1
  91. package/dist/index.d.ts +14 -0
  92. package/dist/index.d.ts.map +1 -1
  93. package/dist/index.js +44 -0
  94. package/dist/index.js.map +1 -1
  95. package/package.json +30 -11
@@ -0,0 +1,651 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
19
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
20
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
21
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
22
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
23
+ };
24
+ var __importStar = (this && this.__importStar) || function (mod) {
25
+ if (mod && mod.__esModule) return mod;
26
+ var result = {};
27
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
28
+ __setModuleDefault(result, mod);
29
+ return result;
30
+ };
31
+ Object.defineProperty(exports, "__esModule", { value: true });
32
+ exports.LoadCreateSVGNetworkAction = exports.CreateSVGNetworkAction = void 0;
33
+ const actions_1 = require("@memberjunction/actions");
34
+ const global_1 = require("@memberjunction/global");
35
+ const d3Force = __importStar(require("d3-force"));
36
+ const d3Hierarchy = __importStar(require("d3-hierarchy"));
37
+ const svg_utils_1 = require("./shared/svg-utils");
38
+ const svg_theming_1 = require("./shared/svg-theming");
39
+ /**
40
+ * Action that generates SVG network graphs including force-directed layouts, decision trees, and radial networks.
41
+ *
42
+ * This action provides server-side SVG generation for network visualizations, designed for
43
+ * AI agents and workflows to create publication-quality graphs from structured data.
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * // Force-directed network example
48
+ * await runAction({
49
+ * ActionName: 'Create SVG Network',
50
+ * Params: [
51
+ * { Name: 'NetworkType', Value: 'force' },
52
+ * { Name: 'Nodes', Value: JSON.stringify([
53
+ * { id: '1', label: 'Node A', group: 'A' },
54
+ * { id: '2', label: 'Node B', group: 'B' },
55
+ * { id: '3', label: 'Node C', group: 'A' }
56
+ * ]) },
57
+ * { Name: 'Edges', Value: JSON.stringify([
58
+ * { source: '1', target: '2', weight: 1, directed: true },
59
+ * { source: '2', target: '3', weight: 0.5 }
60
+ * ]) },
61
+ * { Name: 'ShowLabels', Value: 'true' },
62
+ * { Name: 'ShowLegend', Value: 'true' }
63
+ * ]
64
+ * });
65
+ *
66
+ * // Decision tree example
67
+ * await runAction({
68
+ * ActionName: 'Create SVG Network',
69
+ * Params: [
70
+ * { Name: 'NetworkType', Value: 'tree' },
71
+ * { Name: 'Nodes', Value: JSON.stringify({
72
+ * id: 'root',
73
+ * label: 'Decision',
74
+ * children: [
75
+ * { id: '1', label: 'Option A', value: 0.6 },
76
+ * { id: '2', label: 'Option B', value: 0.4 }
77
+ * ]
78
+ * }) }
79
+ * ]
80
+ * });
81
+ * ```
82
+ */
83
+ let CreateSVGNetworkAction = class CreateSVGNetworkAction extends actions_1.BaseAction {
84
+ /**
85
+ * Generates an SVG network graph from the provided data and configuration
86
+ *
87
+ * @param params - The action parameters containing:
88
+ * - NetworkType: Type of network ('force' | 'tree' | 'radial')
89
+ * - Nodes: JSON array or object of nodes
90
+ * - Edges: JSON array of edges (for force and radial)
91
+ * - Physics: Physics parameters (charge, linkDistance, iterations)
92
+ * - ShowLabels: Show node labels (default: true)
93
+ * - ShowLegend: Show group legend (default: false)
94
+ * - NodeShape: Shape for tree nodes ('rect' | 'circle' | 'pill')
95
+ * - Width: Width in pixels (optional, default: 800)
96
+ * - Height: Height in pixels (optional, default: 600)
97
+ * - Title: Network title (optional)
98
+ * - Palette: Color palette name (optional)
99
+ * - Seed: Random seed for deterministic layouts (optional)
100
+ *
101
+ * @returns A promise resolving to an SVGActionResult
102
+ */
103
+ async InternalRunAction(params) {
104
+ try {
105
+ const networkTypeParam = this.getParamValue(params, 'NetworkType');
106
+ if (!networkTypeParam) {
107
+ return {
108
+ Success: false,
109
+ Message: 'NetworkType parameter is required. Must be "force", "tree", or "radial"',
110
+ ResultCode: 'MISSING_PARAMETERS',
111
+ };
112
+ }
113
+ const networkType = this.ensureString(networkTypeParam, 'NetworkType').toLowerCase();
114
+ if (!['force', 'tree', 'radial'].includes(networkType)) {
115
+ return {
116
+ Success: false,
117
+ Message: 'NetworkType must be "force", "tree", or "radial"',
118
+ ResultCode: 'INVALID_NETWORK_TYPE',
119
+ };
120
+ }
121
+ // Parse common parameters
122
+ const width = parseInt(this.ensureString(this.getParamValue(params, 'Width') || '800', 'Width'));
123
+ const height = parseInt(this.ensureString(this.getParamValue(params, 'Height') || '600', 'Height'));
124
+ const title = this.ensureString(this.getParamValue(params, 'Title') || '', 'Title');
125
+ const paletteName = this.ensureString(this.getParamValue(params, 'Palette') || 'mjDefault', 'Palette');
126
+ const seed = parseInt(this.ensureString(this.getParamValue(params, 'Seed') || String(Date.now()), 'Seed'));
127
+ const showLabelsParam = this.getParamValue(params, 'ShowLabels');
128
+ const showLabels = showLabelsParam ? this.ensureString(showLabelsParam, 'ShowLabels').toLowerCase() !== 'false' : true;
129
+ const showLegendParam = this.getParamValue(params, 'ShowLegend');
130
+ const showLegend = showLegendParam ? this.ensureString(showLegendParam, 'ShowLegend').toLowerCase() === 'true' : false;
131
+ // Create branding configuration
132
+ const branding = {
133
+ palette: { type: 'named', name: paletteName },
134
+ };
135
+ // Create viewBox configuration
136
+ const viewBox = {
137
+ width,
138
+ height,
139
+ padding: 40,
140
+ };
141
+ // Generate network based on type
142
+ let svg;
143
+ const warnings = [];
144
+ switch (networkType) {
145
+ case 'force':
146
+ svg = await this.renderForceNetwork(params, viewBox, branding, title, seed, showLabels, showLegend, warnings);
147
+ break;
148
+ case 'tree':
149
+ svg = await this.renderDecisionTree(params, viewBox, branding, title, showLabels);
150
+ break;
151
+ case 'radial':
152
+ svg = await this.renderRadialNetwork(params, viewBox, branding, title, seed, showLabels, warnings);
153
+ break;
154
+ default:
155
+ return {
156
+ Success: false,
157
+ Message: `Unsupported network type: ${networkType}`,
158
+ ResultCode: 'INVALID_NETWORK_TYPE',
159
+ };
160
+ }
161
+ return {
162
+ Success: true,
163
+ ResultCode: 'SUCCESS',
164
+ Message: svg,
165
+ svg,
166
+ width,
167
+ height,
168
+ warnings: warnings.length > 0 ? warnings : undefined,
169
+ };
170
+ }
171
+ catch (error) {
172
+ return {
173
+ Success: false,
174
+ Message: `Failed to generate network: ${error instanceof Error ? error.message : String(error)}`,
175
+ ResultCode: 'NETWORK_GENERATION_FAILED',
176
+ };
177
+ }
178
+ }
179
+ /**
180
+ * Renders a force-directed network using d3-force
181
+ */
182
+ async renderForceNetwork(params, viewBox, branding, title, seed, showLabels, showLegend, warnings) {
183
+ // Parse nodes and edges
184
+ const nodesParam = this.getParamValue(params, 'Nodes');
185
+ const edgesParam = this.getParamValue(params, 'Edges');
186
+ if (!nodesParam) {
187
+ throw new Error('Nodes parameter is required for force networks');
188
+ }
189
+ const nodes = this.parseJSON(nodesParam, 'Nodes');
190
+ const edges = edgesParam ? this.parseJSON(edgesParam, 'Edges') : [];
191
+ // Parse physics parameters
192
+ const physicsParam = this.getParamValue(params, 'Physics');
193
+ const physics = physicsParam ? this.parseJSON(physicsParam, 'Physics') : {};
194
+ const charge = physics.charge || -300;
195
+ const linkDistance = physics.linkDistance || 100;
196
+ const iterations = physics.iterations || 300;
197
+ // Warn if graph is too large
198
+ if (nodes.length > 500) {
199
+ warnings.push(`Large graph with ${nodes.length} nodes may have performance issues`);
200
+ }
201
+ // Create seeded random generator
202
+ const random = svg_utils_1.SVGUtils.seededRandom(seed);
203
+ // Initialize node positions randomly
204
+ const vb = svg_utils_1.SVGUtils.calculateViewBox(viewBox);
205
+ const simNodes = nodes.map((n) => ({
206
+ ...n,
207
+ x: vb.x + random() * vb.contentWidth,
208
+ y: vb.y + random() * vb.contentHeight,
209
+ vx: 0,
210
+ vy: 0,
211
+ }));
212
+ // Create force simulation
213
+ const simulation = d3Force
214
+ .forceSimulation(simNodes)
215
+ .force('link', d3Force
216
+ .forceLink(edges)
217
+ .id((d) => d.id)
218
+ .distance(linkDistance))
219
+ .force('charge', d3Force.forceManyBody().strength(charge))
220
+ .force('center', d3Force.forceCenter(vb.x + vb.contentWidth / 2, vb.y + vb.contentHeight / 2))
221
+ .force('collide', d3Force.forceCollide(20))
222
+ .stop();
223
+ // Run simulation headless
224
+ for (let i = 0; i < iterations; i++) {
225
+ simulation.tick();
226
+ }
227
+ // Create SVG
228
+ const doc = svg_utils_1.SVGUtils.createSVG(viewBox.width, viewBox.height, 'force-network');
229
+ const svg = doc.querySelector('svg');
230
+ // Add accessibility
231
+ if (title) {
232
+ svg_utils_1.SVGUtils.addA11y(svg, {
233
+ title,
234
+ ariaRole: 'img',
235
+ });
236
+ }
237
+ // Add styles
238
+ const css = (0, svg_theming_1.generateCSS)(branding);
239
+ svg_utils_1.SVGUtils.addStyles(svg, css);
240
+ // Get palette
241
+ const palette = (0, svg_theming_1.getPalette)(branding.palette);
242
+ const ns = svg.namespaceURI;
243
+ // Build group-to-color mapping
244
+ const groups = [...new Set(nodes.map((n) => n.group).filter((g) => g != null))];
245
+ const groupColorMap = new Map(groups.map((g, i) => [g, (0, svg_theming_1.getColorForIndex)(i, branding.palette)]));
246
+ // Render edges
247
+ const edgesGroup = doc.createElementNS(ns, 'g');
248
+ edgesGroup.setAttribute('id', 'edges');
249
+ svg.appendChild(edgesGroup);
250
+ for (const edge of edges) {
251
+ const sourceSim = simNodes.find((n) => n.id === edge.source.id || n.id === edge.source);
252
+ const targetSim = simNodes.find((n) => n.id === edge.target.id || n.id === edge.target);
253
+ if (!sourceSim || !targetSim)
254
+ continue;
255
+ const line = doc.createElementNS(ns, 'line');
256
+ line.setAttribute('x1', String(sourceSim.x));
257
+ line.setAttribute('y1', String(sourceSim.y));
258
+ line.setAttribute('x2', String(targetSim.x));
259
+ line.setAttribute('y2', String(targetSim.y));
260
+ line.setAttribute('stroke', palette.foreground);
261
+ line.setAttribute('stroke-width', String((edge.weight || 1) * 2));
262
+ line.setAttribute('opacity', '0.6');
263
+ if (edge.directed) {
264
+ const markerId = svg_utils_1.SVGUtils.addArrowMarker(svg, 'arrow-force', palette.foreground);
265
+ line.setAttribute('marker-end', markerId);
266
+ }
267
+ edgesGroup.appendChild(line);
268
+ }
269
+ // Render nodes
270
+ const nodesGroup = doc.createElementNS(ns, 'g');
271
+ nodesGroup.setAttribute('id', 'nodes');
272
+ svg.appendChild(nodesGroup);
273
+ for (const node of simNodes) {
274
+ const g = doc.createElementNS(ns, 'g');
275
+ g.setAttribute('transform', `translate(${node.x}, ${node.y})`);
276
+ const radius = node.size || 10;
277
+ const color = node.group ? groupColorMap.get(node.group) || palette.categorical[0] : palette.categorical[0];
278
+ const circle = doc.createElementNS(ns, 'circle');
279
+ circle.setAttribute('r', String(radius));
280
+ circle.setAttribute('fill', color);
281
+ circle.setAttribute('stroke', '#FFF');
282
+ circle.setAttribute('stroke-width', '2');
283
+ g.appendChild(circle);
284
+ if (showLabels && node.label) {
285
+ const text = doc.createElementNS(ns, 'text');
286
+ text.setAttribute('y', String(radius + 15));
287
+ text.setAttribute('text-anchor', 'middle');
288
+ text.setAttribute('font-size', '11');
289
+ text.setAttribute('fill', palette.foreground);
290
+ text.textContent = node.label;
291
+ g.appendChild(text);
292
+ }
293
+ nodesGroup.appendChild(g);
294
+ }
295
+ // Add legend if requested
296
+ if (showLegend && groups.length > 0) {
297
+ this.addLegend(doc, svg, groups, groupColorMap, vb, (0, svg_theming_1.getFontSpec)(branding.font));
298
+ }
299
+ // Add title if present
300
+ if (title) {
301
+ this.addTitle(doc, svg, title, vb.width, (0, svg_theming_1.getFontSpec)(branding.font));
302
+ }
303
+ // Sanitize and return
304
+ return svg_utils_1.SVGUtils.sanitizeSVG(svg.outerHTML);
305
+ }
306
+ /**
307
+ * Renders a decision tree using d3-hierarchy
308
+ */
309
+ async renderDecisionTree(params, viewBox, branding, title, showLabels) {
310
+ // Parse tree
311
+ const nodesParam = this.getParamValue(params, 'Nodes');
312
+ if (!nodesParam) {
313
+ throw new Error('Nodes parameter is required for decision trees');
314
+ }
315
+ const rootNode = this.parseJSON(nodesParam, 'Nodes');
316
+ const nodeShapeParam = this.getParamValue(params, 'NodeShape');
317
+ const nodeShape = (nodeShapeParam ? this.ensureString(nodeShapeParam, 'NodeShape') : 'rect');
318
+ // Create hierarchy
319
+ const hierarchy = d3Hierarchy.hierarchy(rootNode);
320
+ // Create tree layout
321
+ const vb = svg_utils_1.SVGUtils.calculateViewBox(viewBox);
322
+ const treeLayout = d3Hierarchy.tree().size([vb.contentWidth, vb.contentHeight]);
323
+ // Calculate layout
324
+ const treeData = treeLayout(hierarchy);
325
+ // Create SVG
326
+ const doc = svg_utils_1.SVGUtils.createSVG(viewBox.width, viewBox.height, 'decision-tree');
327
+ const svg = doc.querySelector('svg');
328
+ // Add accessibility
329
+ if (title) {
330
+ svg_utils_1.SVGUtils.addA11y(svg, {
331
+ title,
332
+ ariaRole: 'img',
333
+ });
334
+ }
335
+ // Add styles
336
+ const css = (0, svg_theming_1.generateCSS)(branding);
337
+ svg_utils_1.SVGUtils.addStyles(svg, css);
338
+ // Get palette
339
+ const palette = (0, svg_theming_1.getPalette)(branding.palette);
340
+ const ns = svg.namespaceURI;
341
+ // Create container group with offset for padding
342
+ const container = doc.createElementNS(ns, 'g');
343
+ container.setAttribute('transform', `translate(${vb.x}, ${vb.y})`);
344
+ svg.appendChild(container);
345
+ // Render links
346
+ treeData.links().forEach((link) => {
347
+ const path = doc.createElementNS(ns, 'path');
348
+ // Diagonal path
349
+ const d = `M ${link.source.x},${link.source.y}
350
+ C ${link.source.x},${(link.source.y + link.target.y) / 2}
351
+ ${link.target.x},${(link.source.y + link.target.y) / 2}
352
+ ${link.target.x},${link.target.y}`;
353
+ path.setAttribute('d', d);
354
+ path.setAttribute('fill', 'none');
355
+ path.setAttribute('stroke', palette.foreground);
356
+ path.setAttribute('stroke-width', '2');
357
+ path.setAttribute('opacity', '0.6');
358
+ container.appendChild(path);
359
+ });
360
+ // Render nodes
361
+ treeData.descendants().forEach((node) => {
362
+ const g = doc.createElementNS(ns, 'g');
363
+ g.setAttribute('transform', `translate(${node.x}, ${node.y})`);
364
+ const colorIndex = node.depth % palette.categorical.length;
365
+ const fillColor = palette.categorical[colorIndex];
366
+ // Render shape based on nodeShape
367
+ switch (nodeShape) {
368
+ case 'circle':
369
+ const circle = doc.createElementNS(ns, 'circle');
370
+ circle.setAttribute('r', '25');
371
+ circle.setAttribute('fill', fillColor);
372
+ circle.setAttribute('stroke', '#333');
373
+ circle.setAttribute('stroke-width', '2');
374
+ g.appendChild(circle);
375
+ break;
376
+ case 'pill':
377
+ const pill = doc.createElementNS(ns, 'path');
378
+ pill.setAttribute('d', svg_utils_1.SVGUtils.roundedRectPath(-40, -20, 80, 40, 20));
379
+ pill.setAttribute('fill', fillColor);
380
+ pill.setAttribute('stroke', '#333');
381
+ pill.setAttribute('stroke-width', '2');
382
+ g.appendChild(pill);
383
+ break;
384
+ default: // rect
385
+ const rect = doc.createElementNS(ns, 'rect');
386
+ rect.setAttribute('x', '-30');
387
+ rect.setAttribute('y', '-20');
388
+ rect.setAttribute('width', '60');
389
+ rect.setAttribute('height', '40');
390
+ rect.setAttribute('fill', fillColor);
391
+ rect.setAttribute('stroke', '#333');
392
+ rect.setAttribute('stroke-width', '2');
393
+ rect.setAttribute('rx', '5');
394
+ g.appendChild(rect);
395
+ break;
396
+ }
397
+ // Add label
398
+ if (showLabels) {
399
+ const labelText = doc.createElementNS(ns, 'text');
400
+ labelText.setAttribute('y', node.data.value != null ? '-5' : '0');
401
+ labelText.setAttribute('text-anchor', 'middle');
402
+ labelText.setAttribute('dominant-baseline', 'middle');
403
+ labelText.setAttribute('font-size', '11');
404
+ labelText.setAttribute('font-weight', 'bold');
405
+ labelText.setAttribute('fill', '#FFF');
406
+ labelText.textContent = node.data.label;
407
+ g.appendChild(labelText);
408
+ // Add value if present
409
+ if (node.data.value != null) {
410
+ const valueText = doc.createElementNS(ns, 'text');
411
+ valueText.setAttribute('y', '8');
412
+ valueText.setAttribute('text-anchor', 'middle');
413
+ valueText.setAttribute('font-size', '9');
414
+ valueText.setAttribute('fill', '#FFF');
415
+ valueText.setAttribute('opacity', '0.9');
416
+ valueText.textContent = String(node.data.value);
417
+ g.appendChild(valueText);
418
+ }
419
+ }
420
+ container.appendChild(g);
421
+ });
422
+ // Add title if present
423
+ if (title) {
424
+ this.addTitle(doc, svg, title, vb.width, (0, svg_theming_1.getFontSpec)(branding.font));
425
+ }
426
+ // Sanitize and return
427
+ return svg_utils_1.SVGUtils.sanitizeSVG(svg.outerHTML);
428
+ }
429
+ /**
430
+ * Renders a radial network using d3-force with radial constraint
431
+ */
432
+ async renderRadialNetwork(params, viewBox, branding, title, seed, showLabels, warnings) {
433
+ // Parse nodes and edges
434
+ const nodesParam = this.getParamValue(params, 'Nodes');
435
+ const edgesParam = this.getParamValue(params, 'Edges');
436
+ if (!nodesParam) {
437
+ throw new Error('Nodes parameter is required for radial networks');
438
+ }
439
+ const nodes = this.parseJSON(nodesParam, 'Nodes');
440
+ const edges = edgesParam ? this.parseJSON(edgesParam, 'Edges') : [];
441
+ // Identify central node (first node or node with most connections)
442
+ const degreeMap = new Map();
443
+ for (const edge of edges) {
444
+ degreeMap.set(edge.source, (degreeMap.get(edge.source) || 0) + 1);
445
+ degreeMap.set(edge.target, (degreeMap.get(edge.target) || 0) + 1);
446
+ }
447
+ const centralNodeId = nodes.length > 0 ? [...degreeMap.entries()].sort((a, b) => b[1] - a[1])[0]?.[0] || nodes[0].id : nodes[0].id;
448
+ // Create seeded random generator
449
+ const random = svg_utils_1.SVGUtils.seededRandom(seed);
450
+ // Initialize node positions
451
+ const vb = svg_utils_1.SVGUtils.calculateViewBox(viewBox);
452
+ const centerX = vb.x + vb.contentWidth / 2;
453
+ const centerY = vb.y + vb.contentHeight / 2;
454
+ const simNodes = nodes.map((n, i) => ({
455
+ ...n,
456
+ x: centerX + (random() - 0.5) * 100,
457
+ y: centerY + (random() - 0.5) * 100,
458
+ vx: 0,
459
+ vy: 0,
460
+ isCentral: n.id === centralNodeId,
461
+ }));
462
+ // Create force simulation with radial force
463
+ const simulation = d3Force
464
+ .forceSimulation(simNodes)
465
+ .force('link', d3Force
466
+ .forceLink(edges)
467
+ .id((d) => d.id)
468
+ .distance(80))
469
+ .force('charge', d3Force.forceManyBody().strength(-200))
470
+ .force('radial', d3Force.forceRadial((d) => (d.isCentral ? 0 : 150), centerX, centerY))
471
+ .force('collide', d3Force.forceCollide(25))
472
+ .stop();
473
+ // Run simulation headless
474
+ for (let i = 0; i < 300; i++) {
475
+ simulation.tick();
476
+ }
477
+ // Create SVG (reuse force network rendering logic)
478
+ const doc = svg_utils_1.SVGUtils.createSVG(viewBox.width, viewBox.height, 'radial-network');
479
+ const svg = doc.querySelector('svg');
480
+ // Add accessibility
481
+ if (title) {
482
+ svg_utils_1.SVGUtils.addA11y(svg, {
483
+ title,
484
+ ariaRole: 'img',
485
+ });
486
+ }
487
+ // Add styles
488
+ const css = (0, svg_theming_1.generateCSS)(branding);
489
+ svg_utils_1.SVGUtils.addStyles(svg, css);
490
+ // Get palette
491
+ const palette = (0, svg_theming_1.getPalette)(branding.palette);
492
+ const ns = svg.namespaceURI;
493
+ // Render edges
494
+ const edgesGroup = doc.createElementNS(ns, 'g');
495
+ edgesGroup.setAttribute('id', 'edges');
496
+ svg.appendChild(edgesGroup);
497
+ for (const edge of edges) {
498
+ const sourceSim = simNodes.find((n) => n.id === edge.source.id || n.id === edge.source);
499
+ const targetSim = simNodes.find((n) => n.id === edge.target.id || n.id === edge.target);
500
+ if (!sourceSim || !targetSim)
501
+ continue;
502
+ const line = doc.createElementNS(ns, 'line');
503
+ line.setAttribute('x1', String(sourceSim.x));
504
+ line.setAttribute('y1', String(sourceSim.y));
505
+ line.setAttribute('x2', String(targetSim.x));
506
+ line.setAttribute('y2', String(targetSim.y));
507
+ line.setAttribute('stroke', palette.foreground);
508
+ line.setAttribute('stroke-width', '2');
509
+ line.setAttribute('opacity', '0.4');
510
+ edgesGroup.appendChild(line);
511
+ }
512
+ // Render nodes
513
+ const nodesGroup = doc.createElementNS(ns, 'g');
514
+ nodesGroup.setAttribute('id', 'nodes');
515
+ svg.appendChild(nodesGroup);
516
+ for (const node of simNodes) {
517
+ const g = doc.createElementNS(ns, 'g');
518
+ g.setAttribute('transform', `translate(${node.x}, ${node.y})`);
519
+ const radius = node.isCentral ? 20 : node.size || 12;
520
+ const color = node.isCentral ? palette.categorical[4] : palette.categorical[0];
521
+ const circle = doc.createElementNS(ns, 'circle');
522
+ circle.setAttribute('r', String(radius));
523
+ circle.setAttribute('fill', color);
524
+ circle.setAttribute('stroke', '#FFF');
525
+ circle.setAttribute('stroke-width', node.isCentral ? '3' : '2');
526
+ g.appendChild(circle);
527
+ if (showLabels && node.label) {
528
+ const text = doc.createElementNS(ns, 'text');
529
+ text.setAttribute('y', String(radius + 15));
530
+ text.setAttribute('text-anchor', 'middle');
531
+ text.setAttribute('font-size', node.isCentral ? '12' : '10');
532
+ text.setAttribute('font-weight', node.isCentral ? 'bold' : 'normal');
533
+ text.setAttribute('fill', palette.foreground);
534
+ text.textContent = node.label;
535
+ g.appendChild(text);
536
+ }
537
+ nodesGroup.appendChild(g);
538
+ }
539
+ // Add title if present
540
+ if (title) {
541
+ this.addTitle(doc, svg, title, vb.width, (0, svg_theming_1.getFontSpec)(branding.font));
542
+ }
543
+ // Sanitize and return
544
+ return svg_utils_1.SVGUtils.sanitizeSVG(svg.outerHTML);
545
+ }
546
+ /**
547
+ * Adds a legend to the SVG
548
+ */
549
+ addLegend(doc, svg, groups, groupColorMap, vb, font) {
550
+ const ns = svg.namespaceURI;
551
+ const legendGroup = doc.createElementNS(ns, 'g');
552
+ legendGroup.setAttribute('id', 'legend');
553
+ legendGroup.setAttribute('transform', `translate(${vb.width - 150}, 50)`);
554
+ groups.forEach((group, i) => {
555
+ const g = doc.createElementNS(ns, 'g');
556
+ g.setAttribute('transform', `translate(0, ${i * 25})`);
557
+ const circle = doc.createElementNS(ns, 'circle');
558
+ circle.setAttribute('cx', '10');
559
+ circle.setAttribute('cy', '10');
560
+ circle.setAttribute('r', '8');
561
+ circle.setAttribute('fill', groupColorMap.get(group));
562
+ circle.setAttribute('stroke', '#333');
563
+ g.appendChild(circle);
564
+ const text = doc.createElementNS(ns, 'text');
565
+ text.setAttribute('x', '25');
566
+ text.setAttribute('y', '15');
567
+ text.setAttribute('font-size', String(font.size - 2));
568
+ text.textContent = group;
569
+ g.appendChild(text);
570
+ legendGroup.appendChild(g);
571
+ });
572
+ svg.appendChild(legendGroup);
573
+ }
574
+ /**
575
+ * Adds a title to the SVG
576
+ */
577
+ addTitle(doc, svg, title, width, font) {
578
+ const ns = svg.namespaceURI;
579
+ const text = doc.createElementNS(ns, 'text');
580
+ text.setAttribute('x', String(width / 2));
581
+ text.setAttribute('y', '25');
582
+ text.setAttribute('text-anchor', 'middle');
583
+ text.setAttribute('font-family', font.family);
584
+ text.setAttribute('font-size', String(font.size + 4));
585
+ text.setAttribute('font-weight', 'bold');
586
+ text.textContent = title;
587
+ svg.appendChild(text);
588
+ }
589
+ /**
590
+ * Helper to safely parse JSON that might already be an object
591
+ */
592
+ parseJSON(value, paramName) {
593
+ // If it's already an object/array, return it
594
+ if (typeof value === 'object' && value !== null) {
595
+ return value;
596
+ }
597
+ // If it's a string, parse it
598
+ if (typeof value === 'string') {
599
+ try {
600
+ return JSON.parse(value);
601
+ }
602
+ catch (error) {
603
+ throw new Error(`Parameter '${paramName}' contains invalid JSON: ${error instanceof Error ? error.message : String(error)}`);
604
+ }
605
+ }
606
+ // For other types, error
607
+ throw new Error(`Parameter '${paramName}' must be a JSON string or object. Received ${typeof value}.`);
608
+ }
609
+ /**
610
+ * Helper to ensure a parameter value is a string, with type conversion and validation
611
+ */
612
+ ensureString(value, paramName) {
613
+ if (value == null) {
614
+ return '';
615
+ }
616
+ if (typeof value === 'string') {
617
+ return value;
618
+ }
619
+ // Convert numbers and booleans to strings
620
+ if (typeof value === 'number' || typeof value === 'boolean') {
621
+ return String(value);
622
+ }
623
+ // For objects/arrays, reject with descriptive error
624
+ throw new Error(`Parameter '${paramName}' must be a string, number, or boolean. ` +
625
+ `Received ${typeof value}. If providing JSON data, ensure it's passed as a string.`);
626
+ }
627
+ /**
628
+ * Helper to get parameter value by name (case-insensitive)
629
+ */
630
+ getParamValue(params, paramName) {
631
+ const param = params.Params.find((p) => p.Name.trim().toLowerCase() === paramName.toLowerCase());
632
+ if (param?.Value && typeof param.Value === 'string') {
633
+ return param?.Value?.trim() || null;
634
+ }
635
+ else {
636
+ return param?.Value || null;
637
+ }
638
+ }
639
+ };
640
+ exports.CreateSVGNetworkAction = CreateSVGNetworkAction;
641
+ exports.CreateSVGNetworkAction = CreateSVGNetworkAction = __decorate([
642
+ (0, global_1.RegisterClass)(actions_1.BaseAction, '__CreateSVGNetwork')
643
+ ], CreateSVGNetworkAction);
644
+ /**
645
+ * Loader function to ensure the CreateSVGNetworkAction class is included in the bundle.
646
+ */
647
+ function LoadCreateSVGNetworkAction() {
648
+ // this function is a stub that is used to force the bundler to include the above class in the final bundle and not tree shake them out
649
+ }
650
+ exports.LoadCreateSVGNetworkAction = LoadCreateSVGNetworkAction;
651
+ //# sourceMappingURL=create-svg-network.action.js.map