@dcyfr/ai-notebooks 1.0.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 (51) hide show
  1. package/.changeset/README.md +8 -0
  2. package/.changeset/config.json +11 -0
  3. package/.env.example +21 -0
  4. package/.github/workflows/ci.yml +33 -0
  5. package/.github/workflows/release.yml +82 -0
  6. package/AGENTS.md +38 -0
  7. package/CHANGELOG.md +58 -0
  8. package/CONTRIBUTING.md +34 -0
  9. package/LICENSE +21 -0
  10. package/README.md +134 -0
  11. package/SECURITY.md +924 -0
  12. package/docs/API.md +1775 -0
  13. package/docs/ARCHITECTURE.md +70 -0
  14. package/docs/DEVELOPMENT.md +70 -0
  15. package/docs/plans/PROMOTION_CHECKLIST_DCYFR_AI_NOTEBOOKS_2026-02-08.md +293 -0
  16. package/eslint.config.mjs +23 -0
  17. package/examples/data-exploration/index.ts +95 -0
  18. package/examples/data-pipeline/index.ts +111 -0
  19. package/examples/model-analysis/index.ts +118 -0
  20. package/package.json +57 -0
  21. package/src/index.ts +208 -0
  22. package/src/notebook/cell.ts +149 -0
  23. package/src/notebook/index.ts +50 -0
  24. package/src/notebook/notebook.ts +232 -0
  25. package/src/notebook/runner.ts +141 -0
  26. package/src/pipeline/dataset.ts +220 -0
  27. package/src/pipeline/index.ts +60 -0
  28. package/src/pipeline/runner.ts +195 -0
  29. package/src/pipeline/statistics.ts +182 -0
  30. package/src/pipeline/transform.ts +187 -0
  31. package/src/types/index.ts +301 -0
  32. package/src/utils/csv.ts +106 -0
  33. package/src/utils/format.ts +78 -0
  34. package/src/utils/index.ts +37 -0
  35. package/src/utils/validation.ts +142 -0
  36. package/src/visualization/chart.ts +149 -0
  37. package/src/visualization/formatter.ts +140 -0
  38. package/src/visualization/index.ts +34 -0
  39. package/src/visualization/themes.ts +60 -0
  40. package/tests/cell.test.ts +158 -0
  41. package/tests/dataset.test.ts +159 -0
  42. package/tests/notebook.test.ts +168 -0
  43. package/tests/pipeline.test.ts +158 -0
  44. package/tests/runner.test.ts +168 -0
  45. package/tests/statistics.test.ts +162 -0
  46. package/tests/transform.test.ts +165 -0
  47. package/tests/types.test.ts +258 -0
  48. package/tests/utils.test.ts +257 -0
  49. package/tests/visualization.test.ts +224 -0
  50. package/tsconfig.json +19 -0
  51. package/vitest.config.ts +19 -0
@@ -0,0 +1,224 @@
1
+ /**
2
+ * Tests for visualization module
3
+ */
4
+
5
+ import { describe, it, expect } from 'vitest';
6
+ import {
7
+ createChart,
8
+ addSeries,
9
+ createSeries,
10
+ barChart,
11
+ lineChart,
12
+ scatterPlot,
13
+ pieChart,
14
+ histogram,
15
+ updateChartConfig,
16
+ renderBarChart,
17
+ renderTable,
18
+ renderDatasetTable,
19
+ renderStatsTable,
20
+ sparkline,
21
+ formatNumber,
22
+ renderSummary,
23
+ getTheme,
24
+ getSeriesColor,
25
+ registerTheme,
26
+ themes,
27
+ createDataset,
28
+ } from '../src/index.js';
29
+
30
+ describe('Chart Creation', () => {
31
+ it('creates a chart with defaults', () => {
32
+ const chart = createChart('bar', 'Test');
33
+ expect(chart.config.type).toBe('bar');
34
+ expect(chart.config.title).toBe('Test');
35
+ expect(chart.config.width).toBe(800);
36
+ expect(chart.config.height).toBe(400);
37
+ expect(chart.config.theme).toBe('dcyfr');
38
+ expect(chart.series).toEqual([]);
39
+ });
40
+
41
+ it('creates a chart with custom options', () => {
42
+ const chart = createChart('line', 'Custom', {
43
+ width: 1200,
44
+ height: 600,
45
+ theme: 'dark',
46
+ xLabel: 'Time',
47
+ yLabel: 'Value',
48
+ });
49
+ expect(chart.config.width).toBe(1200);
50
+ expect(chart.config.theme).toBe('dark');
51
+ expect(chart.config.xLabel).toBe('Time');
52
+ });
53
+
54
+ it('adds series to chart', () => {
55
+ let chart = createChart('bar', 'Test');
56
+ const series = createSeries('Revenue', [{ x: 'Q1', y: 100 }]);
57
+ chart = addSeries(chart, series);
58
+ expect(chart.series).toHaveLength(1);
59
+ expect(chart.series[0].name).toBe('Revenue');
60
+ });
61
+
62
+ it('creates series with color', () => {
63
+ const series = createSeries('Test', [{ x: 0, y: 1 }], '#ff0000');
64
+ expect(series.color).toBe('#ff0000');
65
+ });
66
+ });
67
+
68
+ describe('Chart Shortcuts', () => {
69
+ it('creates a bar chart', () => {
70
+ const chart = barChart('Sales', ['A', 'B', 'C'], [10, 20, 30]);
71
+ expect(chart.config.type).toBe('bar');
72
+ expect(chart.series).toHaveLength(1);
73
+ expect(chart.series[0].data).toHaveLength(3);
74
+ });
75
+
76
+ it('creates a line chart', () => {
77
+ const chart = lineChart('Trend', [1, 2, 3], [10, 20, 30]);
78
+ expect(chart.config.type).toBe('line');
79
+ expect(chart.series[0].data).toHaveLength(3);
80
+ });
81
+
82
+ it('creates a scatter plot', () => {
83
+ const chart = scatterPlot('Scatter', [{ x: 1, y: 2 }, { x: 3, y: 4 }]);
84
+ expect(chart.config.type).toBe('scatter');
85
+ expect(chart.series[0].data).toHaveLength(2);
86
+ });
87
+
88
+ it('creates a pie chart', () => {
89
+ const chart = pieChart('Distribution', ['A', 'B'], [60, 40]);
90
+ expect(chart.config.type).toBe('pie');
91
+ });
92
+
93
+ it('creates a histogram', () => {
94
+ const chart = histogram('Distribution', [1, 2, 2, 3, 3, 3, 4, 5], 4);
95
+ expect(chart.config.type).toBe('histogram');
96
+ expect(chart.series).toHaveLength(1);
97
+ });
98
+
99
+ it('handles empty histogram', () => {
100
+ const chart = histogram('Empty', []);
101
+ expect(chart.series).toEqual([]);
102
+ });
103
+
104
+ it('updates chart config', () => {
105
+ const chart = createChart('bar', 'Original');
106
+ const updated = updateChartConfig(chart, { title: 'Updated', width: 1000 });
107
+ expect(updated.config.title).toBe('Updated');
108
+ expect(updated.config.width).toBe(1000);
109
+ expect(chart.config.title).toBe('Original'); // immutable
110
+ });
111
+ });
112
+
113
+ describe('Text Rendering', () => {
114
+ it('renders a bar chart', () => {
115
+ const chart = barChart('Test', ['A', 'B', 'C'], [10, 20, 30]);
116
+ const output = renderBarChart(chart, 30);
117
+ expect(output).toContain('Test');
118
+ expect(output).toContain('A');
119
+ expect(output).toContain('B');
120
+ expect(output).toContain('C');
121
+ expect(output).toContain('█');
122
+ });
123
+
124
+ it('renders a table', () => {
125
+ const output = renderTable(
126
+ ['Name', 'Age'],
127
+ [['Alice', 25], ['Bob', 30]]
128
+ );
129
+ expect(output).toContain('Name');
130
+ expect(output).toContain('Age');
131
+ expect(output).toContain('Alice');
132
+ expect(output).toContain('30');
133
+ expect(output).toContain('─');
134
+ });
135
+
136
+ it('renders a dataset table', () => {
137
+ const ds = createDataset([
138
+ { name: 'Alice', score: 90 },
139
+ { name: 'Bob', score: 85 },
140
+ ]);
141
+ const output = renderDatasetTable(ds);
142
+ expect(output).toContain('Alice');
143
+ expect(output).toContain('Bob');
144
+ });
145
+
146
+ it('truncates long datasets', () => {
147
+ const rows = Array.from({ length: 50 }, (_, i) => ({ id: i }));
148
+ const ds = createDataset(rows);
149
+ const output = renderDatasetTable(ds, 10);
150
+ expect(output).toContain('more rows');
151
+ });
152
+
153
+ it('renders stats table', () => {
154
+ const stats = [
155
+ { column: 'score', count: 10, mean: 85, median: 84, stddev: 5, min: 70, max: 100, variance: 25, q25: 80, q75: 90, nullCount: 0 },
156
+ ];
157
+ const output = renderStatsTable(stats);
158
+ expect(output).toContain('score');
159
+ expect(output).toContain('85');
160
+ });
161
+
162
+ it('renders sparkline', () => {
163
+ const line = sparkline([1, 3, 5, 7, 9]);
164
+ expect(line.length).toBe(5);
165
+ expect(line).toContain('▁');
166
+ expect(line).toContain('█');
167
+ });
168
+
169
+ it('handles empty sparkline', () => {
170
+ expect(sparkline([])).toBe('');
171
+ });
172
+
173
+ it('formats numbers', () => {
174
+ expect(formatNumber(3.14159, 2)).toBe('3.14');
175
+ expect(formatNumber(100, 0)).toBe('100');
176
+ });
177
+
178
+ it('renders summary', () => {
179
+ const output = renderSummary('Model Stats', { Accuracy: '92%', Loss: '0.08' });
180
+ expect(output).toContain('Model Stats');
181
+ expect(output).toContain('Accuracy');
182
+ expect(output).toContain('92%');
183
+ });
184
+ });
185
+
186
+ describe('Themes', () => {
187
+ it('has built-in themes', () => {
188
+ expect(themes.dcyfr).toBeDefined();
189
+ expect(themes.light).toBeDefined();
190
+ expect(themes.dark).toBeDefined();
191
+ });
192
+
193
+ it('gets theme by name', () => {
194
+ const theme = getTheme('light');
195
+ expect(theme.name).toBe('light');
196
+ expect(theme.background).toBe('#ffffff');
197
+ });
198
+
199
+ it('falls back to dcyfr theme', () => {
200
+ const theme = getTheme('nonexistent');
201
+ expect(theme.name).toBe('dcyfr');
202
+ });
203
+
204
+ it('gets series color with wrapping', () => {
205
+ const theme = getTheme('dcyfr');
206
+ const color0 = getSeriesColor(theme, 0);
207
+ const colorWrapped = getSeriesColor(theme, theme.colors.length);
208
+ expect(color0).toBe(colorWrapped);
209
+ });
210
+
211
+ it('registers a custom theme', () => {
212
+ registerTheme({
213
+ name: 'custom',
214
+ background: '#111',
215
+ foreground: '#eee',
216
+ gridColor: '#333',
217
+ colors: ['#f00', '#0f0'],
218
+ fontFamily: 'monospace',
219
+ });
220
+ const theme = getTheme('custom');
221
+ expect(theme.name).toBe('custom');
222
+ expect(theme.colors).toEqual(['#f00', '#0f0']);
223
+ });
224
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "forceConsistentCasingInFileNames": true,
10
+ "resolveJsonModule": true,
11
+ "declaration": true,
12
+ "declarationMap": true,
13
+ "sourceMap": true,
14
+ "outDir": "dist",
15
+ "rootDir": "."
16
+ },
17
+ "include": ["src/**/*", "tests/**/*"],
18
+ "exclude": ["node_modules", "dist", "examples"]
19
+ }
@@ -0,0 +1,19 @@
1
+ import { defineConfig } from 'vitest/config';
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ globals: false,
6
+ environment: 'node',
7
+ include: ['tests/**/*.test.ts'],
8
+ coverage: {
9
+ provider: 'v8',
10
+ reporter: ['text', 'json', 'html'],
11
+ thresholds: {
12
+ branches: 80,
13
+ functions: 80,
14
+ lines: 80,
15
+ statements: 80,
16
+ },
17
+ },
18
+ },
19
+ });