@memberjunction/core-actions 2.113.2 → 2.115.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 (31) hide show
  1. package/dist/custom/ai/find-candidate-agents.action.js +3 -3
  2. package/dist/custom/ai/find-candidate-agents.action.js.map +1 -1
  3. package/dist/custom/ai/load-agent-spec.action.d.ts +10 -0
  4. package/dist/custom/ai/load-agent-spec.action.d.ts.map +1 -1
  5. package/dist/custom/ai/load-agent-spec.action.js +44 -3
  6. package/dist/custom/ai/load-agent-spec.action.js.map +1 -1
  7. package/dist/custom/code-execution/execute-code.action.d.ts +1 -0
  8. package/dist/custom/code-execution/execute-code.action.d.ts.map +1 -1
  9. package/dist/custom/code-execution/execute-code.action.js +6 -2
  10. package/dist/custom/code-execution/execute-code.action.js.map +1 -1
  11. package/dist/custom/crud/base-record-mutation.action.d.ts.map +1 -1
  12. package/dist/custom/crud/base-record-mutation.action.js +83 -14
  13. package/dist/custom/crud/base-record-mutation.action.js.map +1 -1
  14. package/dist/custom/crud/get-records.action.d.ts +57 -0
  15. package/dist/custom/crud/get-records.action.d.ts.map +1 -0
  16. package/dist/custom/crud/get-records.action.js +245 -0
  17. package/dist/custom/crud/get-records.action.js.map +1 -0
  18. package/dist/custom/crud/index.d.ts +1 -0
  19. package/dist/custom/crud/index.d.ts.map +1 -1
  20. package/dist/custom/crud/index.js +1 -0
  21. package/dist/custom/crud/index.js.map +1 -1
  22. package/dist/custom/visualization/create-svg-chart.action.d.ts +88 -24
  23. package/dist/custom/visualization/create-svg-chart.action.d.ts.map +1 -1
  24. package/dist/custom/visualization/create-svg-chart.action.js +779 -79
  25. package/dist/custom/visualization/create-svg-chart.action.js.map +1 -1
  26. package/dist/index.d.ts +1 -0
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +6 -0
  29. package/dist/index.js.map +1 -1
  30. package/package.json +17 -17
  31. package/readme.md +52 -0
@@ -1,20 +1,44 @@
1
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
+ });
2
18
  var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
19
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
20
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
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;
6
22
  return c > 3 && r && Object.defineProperty(target, key, r), r;
7
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
+ };
8
31
  Object.defineProperty(exports, "__esModule", { value: true });
9
32
  exports.LoadCreateSVGChartAction = exports.CreateSVGChartAction = void 0;
10
33
  const actions_1 = require("@memberjunction/actions");
11
34
  const global_1 = require("@memberjunction/global");
12
- // import * as vega from 'vega';
13
- // import * as vegaLite from 'vega-lite';
14
- // import { TopLevelSpec as VegaLiteSpec } from 'vega-lite';
35
+ const d3Scale = __importStar(require("d3-scale"));
36
+ const d3Shape = __importStar(require("d3-shape"));
37
+ const svg_utils_1 = require("./shared/svg-utils");
38
+ const svg_theming_1 = require("./shared/svg-theming");
15
39
  /**
16
- * Action that generates SVG charts from data using Vega-Lite.
17
- * Supports various chart types including bar, line, pie, scatter, area, and more.
40
+ * Action that generates SVG charts from data using D3.
41
+ * Supports bar, line, pie, scatter, and area charts.
18
42
  *
19
43
  * This action is designed for AI agents and workflows to create publication-quality
20
44
  * visualizations from structured data without writing visualization code.
@@ -27,29 +51,36 @@ const global_1 = require("@memberjunction/global");
27
51
  * Params: [
28
52
  * { Name: 'ChartType', Value: 'bar' },
29
53
  * { Name: 'Data', Value: JSON.stringify([
30
- * { category: 'A', value: 28 },
31
- * { category: 'B', value: 55 },
32
- * { category: 'C', value: 43 }
54
+ * { label: 'A', value: 28 },
55
+ * { label: 'B', value: 55 },
56
+ * { label: 'C', value: 43 }
33
57
  * ]) },
34
- * { Name: 'XField', Value: 'category' },
35
- * { Name: 'YField', Value: 'value' },
36
58
  * { Name: 'Title', Value: 'Sample Bar Chart' }
37
59
  * ]
38
60
  * });
39
61
  *
62
+ * // Line chart with X/Y data
63
+ * await runAction({
64
+ * ActionName: 'Create SVG Chart',
65
+ * Params: [
66
+ * { Name: 'ChartType', Value: 'line' },
67
+ * { Name: 'Data', Value: JSON.stringify([
68
+ * { x: 1, y: 10 },
69
+ * { x: 2, y: 25 },
70
+ * { x: 3, y: 15 }
71
+ * ]) }
72
+ * ]
73
+ * });
74
+ *
40
75
  * // Pie chart
41
76
  * await runAction({
42
77
  * ActionName: 'Create SVG Chart',
43
78
  * Params: [
44
79
  * { Name: 'ChartType', Value: 'pie' },
45
80
  * { Name: 'Data', Value: JSON.stringify([
46
- * { category: 'LLM', count: 67 },
47
- * { category: 'Embeddings', count: 10 }
48
- * ]) },
49
- * { Name: 'ThetaField', Value: 'count' },
50
- * { Name: 'ColorField', Value: 'category' },
51
- * { Name: 'Width', Value: '400' },
52
- * { Name: 'Height', Value: '300' }
81
+ * { label: 'LLM', value: 67 },
82
+ * { label: 'Embeddings', value: 10 }
83
+ * ]) }
53
84
  * ]
54
85
  * });
55
86
  * ```
@@ -59,16 +90,16 @@ let CreateSVGChartAction = class CreateSVGChartAction extends actions_1.BaseActi
59
90
  * Generates an SVG chart from the provided data and configuration
60
91
  *
61
92
  * @param params - The action parameters containing:
62
- * - ChartType: Type of chart (bar, line, pie, scatter, area, etc.)
93
+ * - ChartType: Type of chart (bar, line, pie, scatter, area)
63
94
  * - Data: JSON array of data objects
64
- * - XField: Field name for X axis (for bar, line, scatter, area)
65
- * - YField: Field name for Y axis (for bar, line, scatter, area)
66
- * - ThetaField: Field name for pie slice size (for pie charts)
67
- * - ColorField: Field name for color encoding (optional)
68
95
  * - Title: Chart title (optional)
69
- * - Width: Chart width in pixels (optional, default: 400)
70
- * - Height: Chart height in pixels (optional, default: 300)
71
- * - VegaLiteSpec: Full Vega-Lite JSON spec (optional, overrides other params)
96
+ * - XAxisLabel: X-axis label (optional)
97
+ * - YAxisLabel: Y-axis label (optional)
98
+ * - Width: Chart width in pixels (optional, default: 800)
99
+ * - Height: Chart height in pixels (optional, default: 600)
100
+ * - Palette: Color palette name (optional, default: 'mjDefault')
101
+ * - ShowGrid: Show grid lines (optional, default: false)
102
+ * - ShowLegend: Show legend (optional, default: false)
72
103
  *
73
104
  * @returns A promise resolving to an ActionResultSimple with:
74
105
  * - Success: true if chart was generated successfully
@@ -76,60 +107,729 @@ let CreateSVGChartAction = class CreateSVGChartAction extends actions_1.BaseActi
76
107
  * - Message: The SVG string or error message
77
108
  */
78
109
  async InternalRunAction(params) {
79
- throw new Error("Not yet implemented");
80
- // try {
81
- // // Check if a full Vega-Lite spec was provided
82
- // const specParam = this.getParamValue(params, 'VegaLiteSpec');
83
- // let vlSpec: VegaLiteSpec;
84
- // if (specParam) {
85
- // // Use the provided spec directly
86
- // vlSpec = this.parseJSON<VegaLiteSpec>(specParam, 'VegaLiteSpec');
87
- // } else {
88
- // // Build spec from individual parameters
89
- // const chartType = this.getParamValue(params, 'ChartType');
90
- // const dataParam = this.getParamValue(params, 'Data');
91
- // if (!chartType) {
92
- // return {
93
- // Success: false,
94
- // Message: "ChartType parameter is required (or provide VegaLiteSpec)",
95
- // ResultCode: "MISSING_PARAMETERS"
96
- // };
97
- // }
98
- // if (!dataParam) {
99
- // return {
100
- // Success: false,
101
- // Message: "Data parameter is required (or provide VegaLiteSpec)",
102
- // ResultCode: "MISSING_PARAMETERS"
103
- // };
104
- // }
105
- // const data = this.parseJSON<any[]>(dataParam, 'Data');
106
- // if (!Array.isArray(data)) {
107
- // return {
108
- // Success: false,
109
- // Message: "Data parameter must be a JSON array",
110
- // ResultCode: "INVALID_DATA"
111
- // };
112
- // }
113
- // // Build the Vega-Lite spec
114
- // vlSpec = this.buildVegaLiteSpec(params, chartType.toLowerCase(), data);
115
- // }
116
- // // Compile Vega-Lite to Vega
117
- // const vegaSpec = vegaLite.compile(vlSpec).spec;
118
- // // Create a headless view and render to SVG
119
- // const view = new vega.View(vega.parse(vegaSpec), { renderer: 'none' });
120
- // const svg = await view.toSVG();
121
- // return {
122
- // Success: true,
123
- // ResultCode: "SUCCESS",
124
- // Message: svg
125
- // };
126
- // } catch (error) {
127
- // return {
128
- // Success: false,
129
- // Message: `Failed to generate chart: ${error instanceof Error ? error.message : String(error)}`,
130
- // ResultCode: "CHART_GENERATION_FAILED"
131
- // };
132
- // }
110
+ try {
111
+ // Extract chart type
112
+ const chartTypeParam = this.getParamValue(params, 'ChartType');
113
+ if (!chartTypeParam) {
114
+ return {
115
+ Success: false,
116
+ Message: "ChartType parameter is required (bar, line, pie, scatter, area)",
117
+ ResultCode: "MISSING_PARAMETERS"
118
+ };
119
+ }
120
+ const chartType = this.ensureString(chartTypeParam, 'ChartType').toLowerCase();
121
+ const validTypes = ['bar', 'line', 'pie', 'scatter', 'area'];
122
+ if (!validTypes.includes(chartType)) {
123
+ return {
124
+ Success: false,
125
+ Message: `Invalid ChartType: ${chartType}. Valid types: ${validTypes.join(', ')}`,
126
+ ResultCode: "INVALID_CHART_TYPE"
127
+ };
128
+ }
129
+ // Extract data
130
+ const dataParam = this.getParamValue(params, 'Data');
131
+ if (!dataParam) {
132
+ return {
133
+ Success: false,
134
+ Message: "Data parameter is required",
135
+ ResultCode: "MISSING_PARAMETERS"
136
+ };
137
+ }
138
+ const data = this.parseJSON(dataParam, 'Data');
139
+ if (!Array.isArray(data) || data.length === 0) {
140
+ return {
141
+ Success: false,
142
+ Message: "Data must be a non-empty JSON array",
143
+ ResultCode: "INVALID_DATA"
144
+ };
145
+ }
146
+ // Parse common parameters
147
+ const width = parseInt(this.ensureString(this.getParamValue(params, 'Width') || '800', 'Width'));
148
+ const height = parseInt(this.ensureString(this.getParamValue(params, 'Height') || '600', 'Height'));
149
+ const title = this.ensureString(this.getParamValue(params, 'Title') || '', 'Title');
150
+ const xAxisLabel = this.ensureString(this.getParamValue(params, 'XAxisLabel') || '', 'XAxisLabel');
151
+ const yAxisLabel = this.ensureString(this.getParamValue(params, 'YAxisLabel') || '', 'YAxisLabel');
152
+ const paletteName = this.ensureString(this.getParamValue(params, 'Palette') || 'mjDefault', 'Palette');
153
+ const showGridParam = this.getParamValue(params, 'ShowGrid');
154
+ const showGrid = showGridParam ? this.ensureString(showGridParam, 'ShowGrid').toLowerCase() === 'true' : false;
155
+ const showLegendParam = this.getParamValue(params, 'ShowLegend');
156
+ const showLegend = showLegendParam ? this.ensureString(showLegendParam, 'ShowLegend').toLowerCase() === 'true' : false;
157
+ // Create branding configuration
158
+ const branding = {
159
+ palette: { type: 'named', name: paletteName }
160
+ };
161
+ // Create viewBox configuration
162
+ const viewBox = {
163
+ width,
164
+ height,
165
+ padding: { top: title ? 60 : 40, right: 40, bottom: xAxisLabel ? 80 : 60, left: yAxisLabel ? 80 : 60 }
166
+ };
167
+ // Generate chart based on type
168
+ let svg;
169
+ switch (chartType) {
170
+ case 'bar':
171
+ svg = await this.renderBarChart(data, viewBox, branding, title, xAxisLabel, yAxisLabel, showGrid, showLegend);
172
+ break;
173
+ case 'line':
174
+ svg = await this.renderLineChart(data, viewBox, branding, title, xAxisLabel, yAxisLabel, showGrid, showLegend);
175
+ break;
176
+ case 'area':
177
+ svg = await this.renderAreaChart(data, viewBox, branding, title, xAxisLabel, yAxisLabel, showGrid, showLegend);
178
+ break;
179
+ case 'pie':
180
+ svg = await this.renderPieChart(data, viewBox, branding, title, showLegend);
181
+ break;
182
+ case 'scatter':
183
+ svg = await this.renderScatterChart(data, viewBox, branding, title, xAxisLabel, yAxisLabel, showGrid, showLegend);
184
+ break;
185
+ default:
186
+ return {
187
+ Success: false,
188
+ Message: `Unsupported chart type: ${chartType}`,
189
+ ResultCode: "INVALID_CHART_TYPE"
190
+ };
191
+ }
192
+ return {
193
+ Success: true,
194
+ ResultCode: "SUCCESS",
195
+ Message: svg,
196
+ svg,
197
+ width,
198
+ height
199
+ };
200
+ }
201
+ catch (error) {
202
+ return {
203
+ Success: false,
204
+ Message: `Failed to generate chart: ${error instanceof Error ? error.message : String(error)}`,
205
+ ResultCode: "CHART_GENERATION_FAILED"
206
+ };
207
+ }
208
+ }
209
+ /**
210
+ * Renders a bar chart
211
+ */
212
+ async renderBarChart(data, viewBox, branding, title, xAxisLabel, yAxisLabel, showGrid, _showLegend) {
213
+ const vb = svg_utils_1.SVGUtils.calculateViewBox(viewBox);
214
+ const doc = svg_utils_1.SVGUtils.createSVG(viewBox.width, viewBox.height, 'bar-chart');
215
+ const svg = doc.querySelector('svg');
216
+ const ns = svg.namespaceURI;
217
+ // Add accessibility and styles
218
+ if (title) {
219
+ svg_utils_1.SVGUtils.addA11y(svg, { title, ariaRole: 'img' });
220
+ }
221
+ svg_utils_1.SVGUtils.addStyles(svg, (0, svg_theming_1.generateCSS)(branding));
222
+ const palette = (0, svg_theming_1.getPalette)(branding.palette);
223
+ const font = (0, svg_theming_1.getFontSpec)(branding.font);
224
+ // Extract labels and values
225
+ const labels = data.map(d => d.label || String(d.x || ''));
226
+ const values = data.map(d => d.value || d.y || 0);
227
+ const maxValue = Math.max(...values);
228
+ const minValue = Math.min(0, Math.min(...values)); // Include 0 in scale
229
+ // Create scales
230
+ const xScale = d3Scale.scaleBand()
231
+ .domain(labels)
232
+ .range([vb.x, vb.x + vb.contentWidth])
233
+ .padding(0.2);
234
+ const yScale = d3Scale.scaleLinear()
235
+ .domain([minValue, maxValue])
236
+ .range([vb.y + vb.contentHeight, vb.y])
237
+ .nice();
238
+ // Create chart group
239
+ const chartGroup = doc.createElementNS(ns, 'g');
240
+ chartGroup.setAttribute('id', 'chart-content');
241
+ svg.appendChild(chartGroup);
242
+ // Draw grid if requested
243
+ if (showGrid) {
244
+ this.drawGrid(doc, chartGroup, xScale, yScale, vb, palette, 'vertical');
245
+ }
246
+ // Draw bars
247
+ data.forEach((d, i) => {
248
+ const value = d.value || d.y || 0;
249
+ const label = d.label || String(d.x || '');
250
+ const x = xScale(label);
251
+ const barWidth = xScale.bandwidth();
252
+ const barHeight = Math.abs(yScale(value) - yScale(0));
253
+ const y = value >= 0 ? yScale(value) : yScale(0);
254
+ const rect = doc.createElementNS(ns, 'rect');
255
+ rect.setAttribute('x', String(x));
256
+ rect.setAttribute('y', String(y));
257
+ rect.setAttribute('width', String(barWidth));
258
+ rect.setAttribute('height', String(barHeight));
259
+ rect.setAttribute('fill', (0, svg_theming_1.getColorForIndex)(i, branding.palette));
260
+ rect.setAttribute('stroke', 'none');
261
+ rect.setAttribute('rx', '2');
262
+ chartGroup.appendChild(rect);
263
+ // Add value label on top of bar
264
+ const valueText = doc.createElementNS(ns, 'text');
265
+ valueText.setAttribute('x', String(x + barWidth / 2));
266
+ valueText.setAttribute('y', String(y - 5));
267
+ valueText.setAttribute('text-anchor', 'middle');
268
+ valueText.setAttribute('font-family', font.family);
269
+ valueText.setAttribute('font-size', String(font.size - 2));
270
+ valueText.setAttribute('fill', palette.foreground);
271
+ valueText.textContent = String(Math.round(value * 100) / 100);
272
+ chartGroup.appendChild(valueText);
273
+ });
274
+ // Draw axes
275
+ this.drawXAxis(doc, svg, xScale, vb, palette, font, xAxisLabel);
276
+ this.drawYAxis(doc, svg, yScale, vb, palette, font, yAxisLabel);
277
+ // Add title
278
+ if (title) {
279
+ this.addTitle(doc, svg, title, viewBox.width, font);
280
+ }
281
+ return svg_utils_1.SVGUtils.sanitizeSVG(svg.outerHTML);
282
+ }
283
+ /**
284
+ * Renders a line chart
285
+ */
286
+ async renderLineChart(data, viewBox, branding, title, xAxisLabel, yAxisLabel, showGrid, _showLegend) {
287
+ const vb = svg_utils_1.SVGUtils.calculateViewBox(viewBox);
288
+ const doc = svg_utils_1.SVGUtils.createSVG(viewBox.width, viewBox.height, 'line-chart');
289
+ const svg = doc.querySelector('svg');
290
+ const ns = svg.namespaceURI;
291
+ // Add accessibility and styles
292
+ if (title) {
293
+ svg_utils_1.SVGUtils.addA11y(svg, { title, ariaRole: 'img' });
294
+ }
295
+ svg_utils_1.SVGUtils.addStyles(svg, (0, svg_theming_1.generateCSS)(branding));
296
+ const palette = (0, svg_theming_1.getPalette)(branding.palette);
297
+ const font = (0, svg_theming_1.getFontSpec)(branding.font);
298
+ // Extract x and y values
299
+ const xValues = data.map(d => typeof d.x === 'number' ? d.x : parseFloat(String(d.x || 0)));
300
+ const yValues = data.map(d => d.y || d.value || 0);
301
+ // Create scales
302
+ const xScale = d3Scale.scaleLinear()
303
+ .domain([Math.min(...xValues), Math.max(...xValues)])
304
+ .range([vb.x, vb.x + vb.contentWidth])
305
+ .nice();
306
+ const yScale = d3Scale.scaleLinear()
307
+ .domain([Math.min(...yValues), Math.max(...yValues)])
308
+ .range([vb.y + vb.contentHeight, vb.y])
309
+ .nice();
310
+ // Create chart group
311
+ const chartGroup = doc.createElementNS(ns, 'g');
312
+ chartGroup.setAttribute('id', 'chart-content');
313
+ svg.appendChild(chartGroup);
314
+ // Draw grid if requested
315
+ if (showGrid) {
316
+ this.drawGrid(doc, chartGroup, xScale, yScale, vb, palette, 'both');
317
+ }
318
+ // Create line generator
319
+ const lineGenerator = d3Shape.line()
320
+ .x(d => xScale(typeof d.x === 'number' ? d.x : parseFloat(String(d.x || 0))))
321
+ .y(d => yScale(d.y || d.value || 0));
322
+ // Draw line
323
+ const linePath = doc.createElementNS(ns, 'path');
324
+ linePath.setAttribute('d', lineGenerator(data) || '');
325
+ linePath.setAttribute('fill', 'none');
326
+ linePath.setAttribute('stroke', (0, svg_theming_1.getColorForIndex)(0, branding.palette));
327
+ linePath.setAttribute('stroke-width', '3');
328
+ linePath.setAttribute('stroke-linecap', 'round');
329
+ linePath.setAttribute('stroke-linejoin', 'round');
330
+ chartGroup.appendChild(linePath);
331
+ // Draw data points
332
+ data.forEach((d) => {
333
+ const xVal = typeof d.x === 'number' ? d.x : parseFloat(String(d.x || 0));
334
+ const yVal = d.y || d.value || 0;
335
+ const circle = doc.createElementNS(ns, 'circle');
336
+ circle.setAttribute('cx', String(xScale(xVal)));
337
+ circle.setAttribute('cy', String(yScale(yVal)));
338
+ circle.setAttribute('r', '5');
339
+ circle.setAttribute('fill', (0, svg_theming_1.getColorForIndex)(0, branding.palette));
340
+ circle.setAttribute('stroke', '#fff');
341
+ circle.setAttribute('stroke-width', '2');
342
+ chartGroup.appendChild(circle);
343
+ });
344
+ // Draw axes
345
+ this.drawXAxisNumeric(doc, svg, xScale, vb, palette, font, xAxisLabel);
346
+ this.drawYAxis(doc, svg, yScale, vb, palette, font, yAxisLabel);
347
+ // Add title
348
+ if (title) {
349
+ this.addTitle(doc, svg, title, viewBox.width, font);
350
+ }
351
+ return svg_utils_1.SVGUtils.sanitizeSVG(svg.outerHTML);
352
+ }
353
+ /**
354
+ * Renders an area chart
355
+ */
356
+ async renderAreaChart(data, viewBox, branding, title, xAxisLabel, yAxisLabel, showGrid, _showLegend) {
357
+ const vb = svg_utils_1.SVGUtils.calculateViewBox(viewBox);
358
+ const doc = svg_utils_1.SVGUtils.createSVG(viewBox.width, viewBox.height, 'area-chart');
359
+ const svg = doc.querySelector('svg');
360
+ const ns = svg.namespaceURI;
361
+ // Add accessibility and styles
362
+ if (title) {
363
+ svg_utils_1.SVGUtils.addA11y(svg, { title, ariaRole: 'img' });
364
+ }
365
+ svg_utils_1.SVGUtils.addStyles(svg, (0, svg_theming_1.generateCSS)(branding));
366
+ const palette = (0, svg_theming_1.getPalette)(branding.palette);
367
+ const font = (0, svg_theming_1.getFontSpec)(branding.font);
368
+ // Extract x and y values
369
+ const xValues = data.map(d => typeof d.x === 'number' ? d.x : parseFloat(String(d.x || 0)));
370
+ const yValues = data.map(d => d.y || d.value || 0);
371
+ // Create scales
372
+ const xScale = d3Scale.scaleLinear()
373
+ .domain([Math.min(...xValues), Math.max(...xValues)])
374
+ .range([vb.x, vb.x + vb.contentWidth])
375
+ .nice();
376
+ const yScale = d3Scale.scaleLinear()
377
+ .domain([0, Math.max(...yValues)]) // Start from 0 for area charts
378
+ .range([vb.y + vb.contentHeight, vb.y])
379
+ .nice();
380
+ // Create chart group
381
+ const chartGroup = doc.createElementNS(ns, 'g');
382
+ chartGroup.setAttribute('id', 'chart-content');
383
+ svg.appendChild(chartGroup);
384
+ // Draw grid if requested
385
+ if (showGrid) {
386
+ this.drawGrid(doc, chartGroup, xScale, yScale, vb, palette, 'both');
387
+ }
388
+ // Create area generator
389
+ const areaGenerator = d3Shape.area()
390
+ .x(d => xScale(typeof d.x === 'number' ? d.x : parseFloat(String(d.x || 0))))
391
+ .y0(yScale(0))
392
+ .y1(d => yScale(d.y || d.value || 0));
393
+ // Draw area
394
+ const areaPath = doc.createElementNS(ns, 'path');
395
+ areaPath.setAttribute('d', areaGenerator(data) || '');
396
+ const areaColor = (0, svg_theming_1.getColorForIndex)(0, branding.palette);
397
+ areaPath.setAttribute('fill', areaColor);
398
+ areaPath.setAttribute('fill-opacity', '0.3');
399
+ areaPath.setAttribute('stroke', 'none');
400
+ chartGroup.appendChild(areaPath);
401
+ // Draw line on top
402
+ const lineGenerator = d3Shape.line()
403
+ .x(d => xScale(typeof d.x === 'number' ? d.x : parseFloat(String(d.x || 0))))
404
+ .y(d => yScale(d.y || d.value || 0));
405
+ const linePath = doc.createElementNS(ns, 'path');
406
+ linePath.setAttribute('d', lineGenerator(data) || '');
407
+ linePath.setAttribute('fill', 'none');
408
+ linePath.setAttribute('stroke', areaColor);
409
+ linePath.setAttribute('stroke-width', '2');
410
+ chartGroup.appendChild(linePath);
411
+ // Draw axes
412
+ this.drawXAxisNumeric(doc, svg, xScale, vb, palette, font, xAxisLabel);
413
+ this.drawYAxis(doc, svg, yScale, vb, palette, font, yAxisLabel);
414
+ // Add title
415
+ if (title) {
416
+ this.addTitle(doc, svg, title, viewBox.width, font);
417
+ }
418
+ return svg_utils_1.SVGUtils.sanitizeSVG(svg.outerHTML);
419
+ }
420
+ /**
421
+ * Renders a pie chart
422
+ */
423
+ async renderPieChart(data, viewBox, branding, title, showLegend) {
424
+ const vb = svg_utils_1.SVGUtils.calculateViewBox(viewBox);
425
+ const doc = svg_utils_1.SVGUtils.createSVG(viewBox.width, viewBox.height, 'pie-chart');
426
+ const svg = doc.querySelector('svg');
427
+ const ns = svg.namespaceURI;
428
+ // Add accessibility and styles
429
+ if (title) {
430
+ svg_utils_1.SVGUtils.addA11y(svg, { title, ariaRole: 'img' });
431
+ }
432
+ svg_utils_1.SVGUtils.addStyles(svg, (0, svg_theming_1.generateCSS)(branding));
433
+ const font = (0, svg_theming_1.getFontSpec)(branding.font);
434
+ // Calculate center and radius
435
+ const centerX = vb.x + vb.contentWidth / 2;
436
+ const centerY = vb.y + vb.contentHeight / 2;
437
+ const radius = Math.min(vb.contentWidth, vb.contentHeight) / 2 - 20;
438
+ // Create pie layout
439
+ const pieGenerator = d3Shape.pie()
440
+ .value(d => d.value || 0)
441
+ .sort(null);
442
+ const arcGenerator = d3Shape.arc()
443
+ .innerRadius(0)
444
+ .outerRadius(radius);
445
+ const labelArcGenerator = d3Shape.arc()
446
+ .innerRadius(radius * 0.6)
447
+ .outerRadius(radius * 0.6);
448
+ // Create chart group
449
+ const chartGroup = doc.createElementNS(ns, 'g');
450
+ chartGroup.setAttribute('transform', `translate(${centerX}, ${centerY})`);
451
+ svg.appendChild(chartGroup);
452
+ // Generate pie slices
453
+ const arcs = pieGenerator(data);
454
+ const total = data.reduce((sum, d) => sum + (d.value || 0), 0);
455
+ arcs.forEach((arc, i) => {
456
+ // Draw slice
457
+ const path = doc.createElementNS(ns, 'path');
458
+ path.setAttribute('d', arcGenerator(arc) || '');
459
+ path.setAttribute('fill', (0, svg_theming_1.getColorForIndex)(i, branding.palette));
460
+ path.setAttribute('stroke', '#fff');
461
+ path.setAttribute('stroke-width', '2');
462
+ chartGroup.appendChild(path);
463
+ // Add percentage label if slice is large enough
464
+ const percentage = ((arc.data.value || 0) / total) * 100;
465
+ if (percentage > 5) {
466
+ const centroid = labelArcGenerator.centroid(arc);
467
+ const text = doc.createElementNS(ns, 'text');
468
+ text.setAttribute('x', String(centroid[0]));
469
+ text.setAttribute('y', String(centroid[1]));
470
+ text.setAttribute('text-anchor', 'middle');
471
+ text.setAttribute('dominant-baseline', 'middle');
472
+ text.setAttribute('font-family', font.family);
473
+ text.setAttribute('font-size', String(font.size));
474
+ text.setAttribute('font-weight', 'bold');
475
+ text.setAttribute('fill', '#fff');
476
+ text.textContent = `${Math.round(percentage)}%`;
477
+ chartGroup.appendChild(text);
478
+ }
479
+ });
480
+ // Add legend if requested
481
+ if (showLegend) {
482
+ this.addLegend(doc, svg, data, branding, vb, font);
483
+ }
484
+ // Add title
485
+ if (title) {
486
+ this.addTitle(doc, svg, title, viewBox.width, font);
487
+ }
488
+ return svg_utils_1.SVGUtils.sanitizeSVG(svg.outerHTML);
489
+ }
490
+ /**
491
+ * Renders a scatter plot
492
+ */
493
+ async renderScatterChart(data, viewBox, branding, title, xAxisLabel, yAxisLabel, showGrid, _showLegend) {
494
+ const vb = svg_utils_1.SVGUtils.calculateViewBox(viewBox);
495
+ const doc = svg_utils_1.SVGUtils.createSVG(viewBox.width, viewBox.height, 'scatter-chart');
496
+ const svg = doc.querySelector('svg');
497
+ const ns = svg.namespaceURI;
498
+ // Add accessibility and styles
499
+ if (title) {
500
+ svg_utils_1.SVGUtils.addA11y(svg, { title, ariaRole: 'img' });
501
+ }
502
+ svg_utils_1.SVGUtils.addStyles(svg, (0, svg_theming_1.generateCSS)(branding));
503
+ const palette = (0, svg_theming_1.getPalette)(branding.palette);
504
+ const font = (0, svg_theming_1.getFontSpec)(branding.font);
505
+ // Extract x and y values
506
+ const xValues = data.map(d => typeof d.x === 'number' ? d.x : parseFloat(String(d.x || 0)));
507
+ const yValues = data.map(d => d.y || d.value || 0);
508
+ // Create scales
509
+ const xScale = d3Scale.scaleLinear()
510
+ .domain([Math.min(...xValues), Math.max(...xValues)])
511
+ .range([vb.x, vb.x + vb.contentWidth])
512
+ .nice();
513
+ const yScale = d3Scale.scaleLinear()
514
+ .domain([Math.min(...yValues), Math.max(...yValues)])
515
+ .range([vb.y + vb.contentHeight, vb.y])
516
+ .nice();
517
+ // Create chart group
518
+ const chartGroup = doc.createElementNS(ns, 'g');
519
+ chartGroup.setAttribute('id', 'chart-content');
520
+ svg.appendChild(chartGroup);
521
+ // Draw grid if requested
522
+ if (showGrid) {
523
+ this.drawGrid(doc, chartGroup, xScale, yScale, vb, palette, 'both');
524
+ }
525
+ // Draw scatter points
526
+ data.forEach((d, i) => {
527
+ const xVal = typeof d.x === 'number' ? d.x : parseFloat(String(d.x || 0));
528
+ const yVal = d.y || d.value || 0;
529
+ const circle = doc.createElementNS(ns, 'circle');
530
+ circle.setAttribute('cx', String(xScale(xVal)));
531
+ circle.setAttribute('cy', String(yScale(yVal)));
532
+ circle.setAttribute('r', '6');
533
+ circle.setAttribute('fill', (0, svg_theming_1.getColorForIndex)(i, branding.palette));
534
+ circle.setAttribute('fill-opacity', '0.7');
535
+ circle.setAttribute('stroke', (0, svg_theming_1.getColorForIndex)(i, branding.palette));
536
+ circle.setAttribute('stroke-width', '2');
537
+ chartGroup.appendChild(circle);
538
+ });
539
+ // Draw axes
540
+ this.drawXAxisNumeric(doc, svg, xScale, vb, palette, font, xAxisLabel);
541
+ this.drawYAxis(doc, svg, yScale, vb, palette, font, yAxisLabel);
542
+ // Add title
543
+ if (title) {
544
+ this.addTitle(doc, svg, title, viewBox.width, font);
545
+ }
546
+ return svg_utils_1.SVGUtils.sanitizeSVG(svg.outerHTML);
547
+ }
548
+ /**
549
+ * Draws grid lines
550
+ */
551
+ drawGrid(doc, container, xScale, yScale, vb, palette, direction) {
552
+ const ns = container.namespaceURI;
553
+ const gridGroup = doc.createElementNS(ns, 'g');
554
+ gridGroup.setAttribute('class', 'grid');
555
+ container.appendChild(gridGroup);
556
+ // Horizontal grid lines (for Y axis)
557
+ if (direction === 'horizontal' || direction === 'both') {
558
+ const yTicks = yScale.ticks(5);
559
+ yTicks.forEach(tick => {
560
+ const line = doc.createElementNS(ns, 'line');
561
+ line.setAttribute('x1', String(vb.x));
562
+ line.setAttribute('y1', String(yScale(tick)));
563
+ line.setAttribute('x2', String(vb.x + vb.contentWidth));
564
+ line.setAttribute('y2', String(yScale(tick)));
565
+ line.setAttribute('stroke', palette.foreground);
566
+ line.setAttribute('stroke-opacity', '0.1');
567
+ line.setAttribute('stroke-dasharray', '2,2');
568
+ gridGroup.appendChild(line);
569
+ });
570
+ }
571
+ // Vertical grid lines (for X axis)
572
+ if (direction === 'vertical' || direction === 'both') {
573
+ if (typeof xScale.ticks === 'function') {
574
+ const xTicks = xScale.ticks(5);
575
+ xTicks.forEach((tick) => {
576
+ const line = doc.createElementNS(ns, 'line');
577
+ line.setAttribute('x1', String(xScale(tick)));
578
+ line.setAttribute('y1', String(vb.y));
579
+ line.setAttribute('x2', String(xScale(tick)));
580
+ line.setAttribute('y2', String(vb.y + vb.contentHeight));
581
+ line.setAttribute('stroke', palette.foreground);
582
+ line.setAttribute('stroke-opacity', '0.1');
583
+ line.setAttribute('stroke-dasharray', '2,2');
584
+ gridGroup.appendChild(line);
585
+ });
586
+ }
587
+ }
588
+ }
589
+ /**
590
+ * Draws X-axis for categorical data
591
+ */
592
+ drawXAxis(doc, svg, xScale, vb, palette, font, label) {
593
+ const ns = svg.namespaceURI;
594
+ const axisGroup = doc.createElementNS(ns, 'g');
595
+ axisGroup.setAttribute('class', 'x-axis');
596
+ svg.appendChild(axisGroup);
597
+ // Draw axis line
598
+ const axisLine = doc.createElementNS(ns, 'line');
599
+ axisLine.setAttribute('x1', String(vb.x));
600
+ axisLine.setAttribute('y1', String(vb.y + vb.contentHeight));
601
+ axisLine.setAttribute('x2', String(vb.x + vb.contentWidth));
602
+ axisLine.setAttribute('y2', String(vb.y + vb.contentHeight));
603
+ axisLine.setAttribute('stroke', palette.foreground);
604
+ axisLine.setAttribute('stroke-width', '2');
605
+ axisGroup.appendChild(axisLine);
606
+ // Draw labels
607
+ xScale.domain().forEach(label => {
608
+ const x = xScale(label) + xScale.bandwidth() / 2;
609
+ const text = doc.createElementNS(ns, 'text');
610
+ text.setAttribute('x', String(x));
611
+ text.setAttribute('y', String(vb.y + vb.contentHeight + 20));
612
+ text.setAttribute('text-anchor', 'middle');
613
+ text.setAttribute('font-family', font.family);
614
+ text.setAttribute('font-size', String(font.size));
615
+ text.setAttribute('fill', palette.foreground);
616
+ text.textContent = label;
617
+ axisGroup.appendChild(text);
618
+ });
619
+ // Add axis label if provided
620
+ if (label) {
621
+ const labelText = doc.createElementNS(ns, 'text');
622
+ labelText.setAttribute('x', String(vb.x + vb.contentWidth / 2));
623
+ labelText.setAttribute('y', String(vb.y + vb.contentHeight + 50));
624
+ labelText.setAttribute('text-anchor', 'middle');
625
+ labelText.setAttribute('font-family', font.family);
626
+ labelText.setAttribute('font-size', String(font.size + 2));
627
+ labelText.setAttribute('font-weight', '600');
628
+ labelText.setAttribute('fill', palette.foreground);
629
+ labelText.textContent = label;
630
+ axisGroup.appendChild(labelText);
631
+ }
632
+ }
633
+ /**
634
+ * Draws X-axis for numeric data
635
+ */
636
+ drawXAxisNumeric(doc, svg, xScale, vb, palette, font, label) {
637
+ const ns = svg.namespaceURI;
638
+ const axisGroup = doc.createElementNS(ns, 'g');
639
+ axisGroup.setAttribute('class', 'x-axis');
640
+ svg.appendChild(axisGroup);
641
+ // Draw axis line
642
+ const axisLine = doc.createElementNS(ns, 'line');
643
+ axisLine.setAttribute('x1', String(vb.x));
644
+ axisLine.setAttribute('y1', String(vb.y + vb.contentHeight));
645
+ axisLine.setAttribute('x2', String(vb.x + vb.contentWidth));
646
+ axisLine.setAttribute('y2', String(vb.y + vb.contentHeight));
647
+ axisLine.setAttribute('stroke', palette.foreground);
648
+ axisLine.setAttribute('stroke-width', '2');
649
+ axisGroup.appendChild(axisLine);
650
+ // Draw ticks and labels
651
+ const ticks = xScale.ticks(5);
652
+ ticks.forEach(tick => {
653
+ const x = xScale(tick);
654
+ // Tick mark
655
+ const tickLine = doc.createElementNS(ns, 'line');
656
+ tickLine.setAttribute('x1', String(x));
657
+ tickLine.setAttribute('y1', String(vb.y + vb.contentHeight));
658
+ tickLine.setAttribute('x2', String(x));
659
+ tickLine.setAttribute('y2', String(vb.y + vb.contentHeight + 5));
660
+ tickLine.setAttribute('stroke', palette.foreground);
661
+ tickLine.setAttribute('stroke-width', '1');
662
+ axisGroup.appendChild(tickLine);
663
+ // Label
664
+ const text = doc.createElementNS(ns, 'text');
665
+ text.setAttribute('x', String(x));
666
+ text.setAttribute('y', String(vb.y + vb.contentHeight + 20));
667
+ text.setAttribute('text-anchor', 'middle');
668
+ text.setAttribute('font-family', font.family);
669
+ text.setAttribute('font-size', String(font.size));
670
+ text.setAttribute('fill', palette.foreground);
671
+ text.textContent = String(Math.round(tick * 100) / 100);
672
+ axisGroup.appendChild(text);
673
+ });
674
+ // Add axis label if provided
675
+ if (label) {
676
+ const labelText = doc.createElementNS(ns, 'text');
677
+ labelText.setAttribute('x', String(vb.x + vb.contentWidth / 2));
678
+ labelText.setAttribute('y', String(vb.y + vb.contentHeight + 50));
679
+ labelText.setAttribute('text-anchor', 'middle');
680
+ labelText.setAttribute('font-family', font.family);
681
+ labelText.setAttribute('font-size', String(font.size + 2));
682
+ labelText.setAttribute('font-weight', '600');
683
+ labelText.setAttribute('fill', palette.foreground);
684
+ labelText.textContent = label;
685
+ axisGroup.appendChild(labelText);
686
+ }
687
+ }
688
+ /**
689
+ * Draws Y-axis
690
+ */
691
+ drawYAxis(doc, svg, yScale, vb, palette, font, label) {
692
+ const ns = svg.namespaceURI;
693
+ const axisGroup = doc.createElementNS(ns, 'g');
694
+ axisGroup.setAttribute('class', 'y-axis');
695
+ svg.appendChild(axisGroup);
696
+ // Draw axis line
697
+ const axisLine = doc.createElementNS(ns, 'line');
698
+ axisLine.setAttribute('x1', String(vb.x));
699
+ axisLine.setAttribute('y1', String(vb.y));
700
+ axisLine.setAttribute('x2', String(vb.x));
701
+ axisLine.setAttribute('y2', String(vb.y + vb.contentHeight));
702
+ axisLine.setAttribute('stroke', palette.foreground);
703
+ axisLine.setAttribute('stroke-width', '2');
704
+ axisGroup.appendChild(axisLine);
705
+ // Draw ticks and labels
706
+ const ticks = yScale.ticks(5);
707
+ ticks.forEach(tick => {
708
+ const y = yScale(tick);
709
+ // Tick mark
710
+ const tickLine = doc.createElementNS(ns, 'line');
711
+ tickLine.setAttribute('x1', String(vb.x - 5));
712
+ tickLine.setAttribute('y1', String(y));
713
+ tickLine.setAttribute('x2', String(vb.x));
714
+ tickLine.setAttribute('y2', String(y));
715
+ tickLine.setAttribute('stroke', palette.foreground);
716
+ tickLine.setAttribute('stroke-width', '1');
717
+ axisGroup.appendChild(tickLine);
718
+ // Label
719
+ const text = doc.createElementNS(ns, 'text');
720
+ text.setAttribute('x', String(vb.x - 10));
721
+ text.setAttribute('y', String(y));
722
+ text.setAttribute('text-anchor', 'end');
723
+ text.setAttribute('dominant-baseline', 'middle');
724
+ text.setAttribute('font-family', font.family);
725
+ text.setAttribute('font-size', String(font.size));
726
+ text.setAttribute('fill', palette.foreground);
727
+ text.textContent = String(Math.round(tick * 100) / 100);
728
+ axisGroup.appendChild(text);
729
+ });
730
+ // Add axis label if provided
731
+ if (label) {
732
+ const labelText = doc.createElementNS(ns, 'text');
733
+ labelText.setAttribute('x', String(vb.x - 50));
734
+ labelText.setAttribute('y', String(vb.y + vb.contentHeight / 2));
735
+ labelText.setAttribute('text-anchor', 'middle');
736
+ labelText.setAttribute('font-family', font.family);
737
+ labelText.setAttribute('font-size', String(font.size + 2));
738
+ labelText.setAttribute('font-weight', '600');
739
+ labelText.setAttribute('fill', palette.foreground);
740
+ labelText.setAttribute('transform', `rotate(-90, ${vb.x - 50}, ${vb.y + vb.contentHeight / 2})`);
741
+ labelText.textContent = label;
742
+ axisGroup.appendChild(labelText);
743
+ }
744
+ }
745
+ /**
746
+ * Adds a title to the chart
747
+ */
748
+ addTitle(doc, svg, title, width, font) {
749
+ const ns = svg.namespaceURI;
750
+ const text = doc.createElementNS(ns, 'text');
751
+ text.setAttribute('x', String(width / 2));
752
+ text.setAttribute('y', '30');
753
+ text.setAttribute('text-anchor', 'middle');
754
+ text.setAttribute('font-family', font.family);
755
+ text.setAttribute('font-size', String(font.size + 6));
756
+ text.setAttribute('font-weight', 'bold');
757
+ text.textContent = title;
758
+ svg.appendChild(text);
759
+ }
760
+ /**
761
+ * Adds a legend for pie charts
762
+ */
763
+ addLegend(doc, svg, data, branding, vb, font) {
764
+ const ns = svg.namespaceURI;
765
+ const legendGroup = doc.createElementNS(ns, 'g');
766
+ legendGroup.setAttribute('class', 'legend');
767
+ legendGroup.setAttribute('transform', `translate(${vb.x + vb.contentWidth + 20}, ${vb.y})`);
768
+ svg.appendChild(legendGroup);
769
+ data.forEach((d, i) => {
770
+ const g = doc.createElementNS(ns, 'g');
771
+ g.setAttribute('transform', `translate(0, ${i * 25})`);
772
+ // Color box
773
+ const rect = doc.createElementNS(ns, 'rect');
774
+ rect.setAttribute('width', '15');
775
+ rect.setAttribute('height', '15');
776
+ rect.setAttribute('fill', (0, svg_theming_1.getColorForIndex)(i, branding.palette));
777
+ g.appendChild(rect);
778
+ // Label
779
+ const text = doc.createElementNS(ns, 'text');
780
+ text.setAttribute('x', '20');
781
+ text.setAttribute('y', '12');
782
+ text.setAttribute('font-family', font.family);
783
+ text.setAttribute('font-size', String(font.size));
784
+ text.textContent = d.label || `Item ${i + 1}`;
785
+ g.appendChild(text);
786
+ legendGroup.appendChild(g);
787
+ });
788
+ }
789
+ /**
790
+ * Helper to safely parse JSON
791
+ */
792
+ parseJSON(value, paramName) {
793
+ if (typeof value === 'object' && value !== null) {
794
+ return value;
795
+ }
796
+ if (typeof value === 'string') {
797
+ try {
798
+ return JSON.parse(value);
799
+ }
800
+ catch (error) {
801
+ throw new Error(`Parameter '${paramName}' contains invalid JSON: ${error instanceof Error ? error.message : String(error)}`);
802
+ }
803
+ }
804
+ throw new Error(`Parameter '${paramName}' must be a JSON string or object. Received ${typeof value}.`);
805
+ }
806
+ /**
807
+ * Helper to ensure a parameter value is a string
808
+ */
809
+ ensureString(value, paramName) {
810
+ if (value == null) {
811
+ return '';
812
+ }
813
+ if (typeof value === 'string') {
814
+ return value;
815
+ }
816
+ if (typeof value === 'number' || typeof value === 'boolean') {
817
+ return String(value);
818
+ }
819
+ throw new Error(`Parameter '${paramName}' must be a string, number, or boolean. ` +
820
+ `Received ${typeof value}. If providing JSON data, ensure it's passed as a string.`);
821
+ }
822
+ /**
823
+ * Helper to get parameter value by name (case-insensitive)
824
+ */
825
+ getParamValue(params, paramName) {
826
+ const param = params.Params.find(p => p.Name.trim().toLowerCase() === paramName.toLowerCase());
827
+ if (param?.Value && typeof param.Value === 'string') {
828
+ return param?.Value?.trim() || null;
829
+ }
830
+ else {
831
+ return param?.Value || null;
832
+ }
133
833
  }
134
834
  };
135
835
  exports.CreateSVGChartAction = CreateSVGChartAction;