embed-cluster 0.3.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 (63) hide show
  1. package/README.md +122 -0
  2. package/dist/__tests__/cluster.test.d.ts +2 -0
  3. package/dist/__tests__/cluster.test.d.ts.map +1 -0
  4. package/dist/__tests__/cluster.test.js +202 -0
  5. package/dist/__tests__/cluster.test.js.map +1 -0
  6. package/dist/__tests__/errors.test.d.ts +2 -0
  7. package/dist/__tests__/errors.test.d.ts.map +1 -0
  8. package/dist/__tests__/errors.test.js +68 -0
  9. package/dist/__tests__/errors.test.js.map +1 -0
  10. package/dist/__tests__/fixtures/embeddings-small.json +25 -0
  11. package/dist/__tests__/fixtures.test.d.ts +2 -0
  12. package/dist/__tests__/fixtures.test.d.ts.map +1 -0
  13. package/dist/__tests__/fixtures.test.js +44 -0
  14. package/dist/__tests__/fixtures.test.js.map +1 -0
  15. package/dist/__tests__/kmeans.test.d.ts +2 -0
  16. package/dist/__tests__/kmeans.test.d.ts.map +1 -0
  17. package/dist/__tests__/kmeans.test.js +220 -0
  18. package/dist/__tests__/kmeans.test.js.map +1 -0
  19. package/dist/__tests__/normalize.test.d.ts +2 -0
  20. package/dist/__tests__/normalize.test.d.ts.map +1 -0
  21. package/dist/__tests__/normalize.test.js +92 -0
  22. package/dist/__tests__/normalize.test.js.map +1 -0
  23. package/dist/__tests__/silhouette.test.d.ts +2 -0
  24. package/dist/__tests__/silhouette.test.d.ts.map +1 -0
  25. package/dist/__tests__/silhouette.test.js +126 -0
  26. package/dist/__tests__/silhouette.test.js.map +1 -0
  27. package/dist/__tests__/types.test.d.ts +2 -0
  28. package/dist/__tests__/types.test.d.ts.map +1 -0
  29. package/dist/__tests__/types.test.js +126 -0
  30. package/dist/__tests__/types.test.js.map +1 -0
  31. package/dist/clusterer.d.ts +17 -0
  32. package/dist/clusterer.d.ts.map +1 -0
  33. package/dist/clusterer.js +72 -0
  34. package/dist/clusterer.js.map +1 -0
  35. package/dist/errors.d.ts +7 -0
  36. package/dist/errors.d.ts.map +1 -0
  37. package/dist/errors.js +14 -0
  38. package/dist/errors.js.map +1 -0
  39. package/dist/index.d.ts +9 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +21 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/kmeans.d.ts +6 -0
  44. package/dist/kmeans.d.ts.map +1 -0
  45. package/dist/kmeans.js +250 -0
  46. package/dist/kmeans.js.map +1 -0
  47. package/dist/normalize.d.ts +10 -0
  48. package/dist/normalize.d.ts.map +1 -0
  49. package/dist/normalize.js +21 -0
  50. package/dist/normalize.js.map +1 -0
  51. package/dist/optimal-k.d.ts +11 -0
  52. package/dist/optimal-k.d.ts.map +1 -0
  53. package/dist/optimal-k.js +49 -0
  54. package/dist/optimal-k.js.map +1 -0
  55. package/dist/silhouette.d.ts +16 -0
  56. package/dist/silhouette.d.ts.map +1 -0
  57. package/dist/silhouette.js +95 -0
  58. package/dist/silhouette.js.map +1 -0
  59. package/dist/types.d.ts +74 -0
  60. package/dist/types.d.ts.map +1 -0
  61. package/dist/types.js +3 -0
  62. package/dist/types.js.map +1 -0
  63. package/package.json +48 -0
package/README.md ADDED
@@ -0,0 +1,122 @@
1
+ # embed-cluster
2
+
3
+ Cluster embeddings into topics with automatic labeling.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install embed-cluster
9
+ ```
10
+
11
+ For dimensionality reduction (PCA or UMAP), install the optional peer dependencies:
12
+
13
+ ```bash
14
+ npm install ml-pca # PCA-based dimensionality reduction
15
+ npm install umap-js # UMAP-based dimensionality reduction
16
+ ```
17
+
18
+ ## Quick Start
19
+
20
+ ```ts
21
+ import { cluster, createClusterer } from 'embed-cluster';
22
+
23
+ // Cluster raw embedding vectors
24
+ const result = await cluster(embeddings, { k: 5 });
25
+
26
+ console.log(result.clusters); // 5 clusters with labels, centroids, items
27
+ console.log(result.quality); // silhouette score, inertia, interpretation
28
+
29
+ // Or use a pre-configured clusterer
30
+ const clusterer = createClusterer({
31
+ autoK: true,
32
+ maxK: 15,
33
+ normalize: true,
34
+ });
35
+
36
+ const result = await clusterer.cluster(items);
37
+ const optimal = await clusterer.findOptimalK(items);
38
+ ```
39
+
40
+ ## Available Exports
41
+
42
+ ### Clustering Functions
43
+
44
+ - **`cluster(items, options): Promise<ClusterResult>`** -- Cluster EmbedItems using k-means++ and return a full ClusterResult with silhouette scores. Provide `options.k` for a fixed cluster count or `options.autoK = true` for automatic selection.
45
+ - **`findOptimalK(items, options?): OptimalKResult`** -- Try k from 2 to `maxK` (default min(10, √n)), compute silhouette score for each, and return the k that maximises the score.
46
+ - **`silhouetteScore(result): SilhouetteResult`** -- Compute per-item, per-cluster, and overall mean silhouette coefficients for an existing ClusterResult. Returns a value in [-1, 1]; higher is better.
47
+ - **`createClusterer(config): Clusterer`** -- Create a pre-configured Clusterer instance with `cluster()`, `findOptimalK()`, and `silhouetteScore()` bound to the given config.
48
+
49
+ ### Utility Functions
50
+
51
+ - **`normalizeVector(vec: number[]): number[]`** -- L2-normalize a single vector to unit length. Returns a zero vector unchanged.
52
+ - **`normalizeVectors(vecs: number[][]): number[][]`** -- L2-normalize a batch of vectors independently.
53
+ - **`euclideanDistance(a, b): number`** -- Euclidean distance between two vectors.
54
+ - **`cosineDistance(a, b): number`** -- Cosine distance (1 - cosine similarity) between two vectors.
55
+
56
+ ### Types
57
+
58
+ All TypeScript interfaces are exported for consumer use:
59
+
60
+ - `EmbedItem` -- Input item with id, text, embedding, and optional metadata
61
+ - `ClusterItem` -- An `EmbedItem` assigned to a cluster with distance-to-centroid
62
+ - `Cluster` -- A cluster with centroid, items, label, size, and cohesion metrics
63
+ - `ClusterOptions` -- Configuration for clustering (k, autoK, maxK, tolerance, seed, etc.)
64
+ - `ClusterResult` -- Full result including clusters, quality metrics, iteration count, and timing
65
+ - `Clusterer` -- Interface for a pre-configured clusterer instance
66
+ - `SilhouetteResult` -- Silhouette coefficient scores (overall, per-cluster, per-item)
67
+ - `OptimalKResult` -- Result of automatic k selection with scores per candidate k
68
+ - `ClusterQuality` -- Quality metrics including silhouette, inertia, and optional indices
69
+ - `VisualizationData` -- 2D projected points for visualization (PCA, UMAP, or t-SNE)
70
+ - `LabelerFn` -- Custom labeling function signature
71
+
72
+ ### Error Handling
73
+
74
+ - **`ClusterError`** -- Error class with typed `code` field for programmatic error handling
75
+ - **`ClusterErrorCode`** -- Union type of error codes: `EMPTY_INPUT`, `INCONSISTENT_DIMENSIONS`, `DEGENERATE_INPUT`, `INVALID_K`, `INVALID_OPTIONS`
76
+
77
+ ```ts
78
+ import { ClusterError } from 'embed-cluster';
79
+
80
+ try {
81
+ const result = await cluster([], { k: 3 });
82
+ } catch (err) {
83
+ if (err instanceof ClusterError) {
84
+ console.error(err.code); // 'EMPTY_INPUT'
85
+ }
86
+ }
87
+ ```
88
+
89
+ ## Features
90
+
91
+ - **k-means++ clustering** -- Smart centroid initialization for faster convergence and better results
92
+ - **Silhouette analysis** -- Evaluate cluster quality with per-point and per-cluster silhouette coefficients
93
+ - **Automatic k selection** -- Find the optimal number of clusters using silhouette and elbow methods
94
+ - **PCA/UMAP visualization** -- Reduce high-dimensional embeddings to 2D for visualization (requires optional peer deps)
95
+ - **L2 normalization** -- Built-in vector normalization for cosine-distance clustering
96
+ - **Custom labeling** -- Provide your own labeling function or use built-in TF-IDF topic extraction
97
+ - **Reproducible results** -- Seed-based random number generation for deterministic clustering
98
+ - **TypeScript-first** -- Full type definitions with strict typing throughout
99
+
100
+ ## Configuration
101
+
102
+ | Option | Type | Default | Description |
103
+ |--------|------|---------|-------------|
104
+ | `k` | `number` | -- | Number of clusters (required if `autoK` is false) |
105
+ | `autoK` | `boolean` | `false` | Automatically select optimal k |
106
+ | `maxK` | `number` | `10` | Maximum k to try when `autoK` is true |
107
+ | `maxIterations` | `number` | `100` | Maximum k-means iterations |
108
+ | `tolerance` | `number` | `1e-4` | Convergence tolerance |
109
+ | `seed` | `number` | -- | Random seed for reproducibility |
110
+ | `normalize` | `boolean` | `true` | L2-normalize embeddings before clustering |
111
+ | `labeler` | `LabelerFn` | -- | Custom labeling function |
112
+ | `distanceFn` | `Function` | -- | Custom distance function |
113
+
114
+ ## CLI
115
+
116
+ ```bash
117
+ npx embed-cluster --input embeddings.json --k 5 --format summary
118
+ ```
119
+
120
+ ## License
121
+
122
+ MIT
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cluster.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cluster.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/cluster.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,202 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const vitest_1 = require("vitest");
7
+ const clusterer_1 = require("../clusterer");
8
+ const silhouette_1 = require("../silhouette");
9
+ const errors_1 = require("../errors");
10
+ const embeddings_small_json_1 = __importDefault(require("./fixtures/embeddings-small.json"));
11
+ // ---------------------------------------------------------------------------
12
+ // Helpers
13
+ // ---------------------------------------------------------------------------
14
+ function makeItems(coords) {
15
+ return coords.map((embedding, i) => ({ id: `item-${i}`, text: `doc ${i}`, embedding }));
16
+ }
17
+ // Two clear clusters: group near [1,0] and group near [-1,0]
18
+ const GROUP_A = [[1, 0.05], [1.1, 0], [0.9, -0.05], [1.0, 0.02]];
19
+ const GROUP_B = [[-1, 0.05], [-1.1, 0], [-0.9, -0.05], [-1.0, 0.02]];
20
+ const ITEMS_2 = makeItems([...GROUP_A, ...GROUP_B]);
21
+ // Three clear clusters
22
+ const GROUP_C = [[0, 1.0], [0.05, 1.1], [-0.05, 0.95]];
23
+ const ITEMS_3 = makeItems([...GROUP_A, ...GROUP_B, ...GROUP_C]);
24
+ // ---------------------------------------------------------------------------
25
+ // cluster()
26
+ // ---------------------------------------------------------------------------
27
+ (0, vitest_1.describe)('cluster()', () => {
28
+ (0, vitest_1.it)('returns a ClusterResult with k=2 clusters', async () => {
29
+ const result = await (0, clusterer_1.cluster)(ITEMS_2, { k: 2, normalize: false, seed: 1 });
30
+ (0, vitest_1.expect)(result.k).toBe(2);
31
+ (0, vitest_1.expect)(result.clusters).toHaveLength(2);
32
+ });
33
+ (0, vitest_1.it)('all items appear in result', async () => {
34
+ const result = await (0, clusterer_1.cluster)(ITEMS_2, { k: 2, normalize: false, seed: 1 });
35
+ const total = result.clusters.reduce((s, c) => s + c.items.length, 0);
36
+ (0, vitest_1.expect)(total).toBe(ITEMS_2.length);
37
+ });
38
+ (0, vitest_1.it)('quality.silhouette.score is populated (non-zero for separated data)', async () => {
39
+ const result = await (0, clusterer_1.cluster)(ITEMS_2, { k: 2, normalize: false, seed: 1 });
40
+ (0, vitest_1.expect)(result.quality.silhouette.score).toBeGreaterThan(0.5);
41
+ });
42
+ (0, vitest_1.it)('quality.silhouette.perCluster has length k', async () => {
43
+ const result = await (0, clusterer_1.cluster)(ITEMS_2, { k: 2, normalize: false, seed: 1 });
44
+ (0, vitest_1.expect)(result.quality.silhouette.perCluster).toHaveLength(2);
45
+ });
46
+ (0, vitest_1.it)('throws ClusterError EMPTY_INPUT for empty array', async () => {
47
+ await (0, vitest_1.expect)((0, clusterer_1.cluster)([], { k: 2 })).rejects.toThrow(errors_1.ClusterError);
48
+ await (0, vitest_1.expect)((0, clusterer_1.cluster)([], { k: 2 })).rejects.toMatchObject({ code: 'EMPTY_INPUT' });
49
+ });
50
+ (0, vitest_1.it)('throws ClusterError INVALID_OPTIONS when neither k nor autoK provided', async () => {
51
+ await (0, vitest_1.expect)((0, clusterer_1.cluster)(ITEMS_2, {})).rejects.toThrow(errors_1.ClusterError);
52
+ await (0, vitest_1.expect)((0, clusterer_1.cluster)(ITEMS_2, {})).rejects.toMatchObject({ code: 'INVALID_OPTIONS' });
53
+ });
54
+ (0, vitest_1.it)('autoK=true selects k and returns result', async () => {
55
+ const result = await (0, clusterer_1.cluster)(ITEMS_3, { autoK: true, normalize: false, seed: 1 });
56
+ (0, vitest_1.expect)(result.k).toBeGreaterThanOrEqual(2);
57
+ (0, vitest_1.expect)(result.clusters).toHaveLength(result.k);
58
+ });
59
+ (0, vitest_1.it)('durationMs is populated', async () => {
60
+ const result = await (0, clusterer_1.cluster)(ITEMS_2, { k: 2, normalize: false, seed: 1 });
61
+ (0, vitest_1.expect)(result.durationMs).toBeGreaterThanOrEqual(0);
62
+ });
63
+ (0, vitest_1.it)('labeler is called for each cluster', async () => {
64
+ const labels = [];
65
+ const result = await (0, clusterer_1.cluster)(ITEMS_2, {
66
+ k: 2,
67
+ normalize: false,
68
+ seed: 1,
69
+ labeler: (_items, id) => {
70
+ const label = `cluster-${id}`;
71
+ labels.push(label);
72
+ return label;
73
+ },
74
+ });
75
+ (0, vitest_1.expect)(labels).toHaveLength(2);
76
+ for (const c of result.clusters) {
77
+ (0, vitest_1.expect)(c.label).toBeDefined();
78
+ (0, vitest_1.expect)(c.label).toMatch(/^cluster-\d$/);
79
+ }
80
+ });
81
+ (0, vitest_1.it)('async labeler is supported', async () => {
82
+ const result = await (0, clusterer_1.cluster)(ITEMS_2, {
83
+ k: 2,
84
+ normalize: false,
85
+ seed: 1,
86
+ labeler: async (_items, id) => {
87
+ return `async-label-${id}`;
88
+ },
89
+ });
90
+ for (const c of result.clusters) {
91
+ (0, vitest_1.expect)(c.label).toMatch(/^async-label-\d$/);
92
+ }
93
+ });
94
+ (0, vitest_1.it)('works with fixture data (20 items, 4D embeddings, k=3)', async () => {
95
+ const items = embeddings_small_json_1.default.items;
96
+ const result = await (0, clusterer_1.cluster)(items, { k: 3, normalize: true, seed: 42 });
97
+ (0, vitest_1.expect)(result.k).toBe(3);
98
+ const total = result.clusters.reduce((s, c) => s + c.items.length, 0);
99
+ (0, vitest_1.expect)(total).toBe(20);
100
+ });
101
+ (0, vitest_1.it)('fixture: silhouette score > 0.5 for well-separated clusters', async () => {
102
+ const items = embeddings_small_json_1.default.items;
103
+ const result = await (0, clusterer_1.cluster)(items, { k: 3, normalize: true, seed: 42 });
104
+ (0, vitest_1.expect)(result.quality.silhouette.score).toBeGreaterThan(0.5);
105
+ });
106
+ });
107
+ // ---------------------------------------------------------------------------
108
+ // findOptimalK()
109
+ // ---------------------------------------------------------------------------
110
+ (0, vitest_1.describe)('findOptimalK()', () => {
111
+ (0, vitest_1.it)('returns an OptimalKResult with a k value', () => {
112
+ const result = (0, clusterer_1.findOptimalK)(ITEMS_3, { normalize: false, seed: 1 });
113
+ (0, vitest_1.expect)(result.k).toBeGreaterThanOrEqual(2);
114
+ });
115
+ (0, vitest_1.it)('method is "silhouette"', () => {
116
+ const result = (0, clusterer_1.findOptimalK)(ITEMS_3, { normalize: false, seed: 1 });
117
+ (0, vitest_1.expect)(result.method).toBe('silhouette');
118
+ });
119
+ (0, vitest_1.it)('scores array has an entry per k tried', () => {
120
+ const result = (0, clusterer_1.findOptimalK)(ITEMS_3, { normalize: false, seed: 1, maxK: 4 });
121
+ // kMin=2, kMax=min(4, n-1)
122
+ (0, vitest_1.expect)(result.scores.length).toBeGreaterThanOrEqual(1);
123
+ for (const entry of result.scores) {
124
+ (0, vitest_1.expect)(entry.k).toBeGreaterThanOrEqual(2);
125
+ (0, vitest_1.expect)(typeof entry.silhouette).toBe('number');
126
+ (0, vitest_1.expect)(typeof entry.inertia).toBe('number');
127
+ }
128
+ });
129
+ (0, vitest_1.it)('returns k >= 3 for clearly 3-cluster data (silhouette selects good k)', () => {
130
+ const result = (0, clusterer_1.findOptimalK)(ITEMS_3, { normalize: false, seed: 1, maxK: 5 });
131
+ // The selected k must be at least 3 (the true cluster count); silhouette
132
+ // may also prefer tighter sub-clusters when maxK allows it.
133
+ (0, vitest_1.expect)(result.k).toBeGreaterThanOrEqual(3);
134
+ });
135
+ (0, vitest_1.it)('selected k has the highest silhouette score in scores', () => {
136
+ const result = (0, clusterer_1.findOptimalK)(ITEMS_3, { normalize: false, seed: 1, maxK: 5 });
137
+ const best = result.scores.reduce((a, b) => (a.silhouette > b.silhouette ? a : b));
138
+ (0, vitest_1.expect)(result.k).toBe(best.k);
139
+ });
140
+ (0, vitest_1.it)('fixture: findOptimalK returns k=3 for 3-cluster fixture', () => {
141
+ const items = embeddings_small_json_1.default.items;
142
+ const result = (0, clusterer_1.findOptimalK)(items, { normalize: true, seed: 42, maxK: 6 });
143
+ (0, vitest_1.expect)(result.k).toBe(3);
144
+ });
145
+ });
146
+ // ---------------------------------------------------------------------------
147
+ // createClusterer()
148
+ // ---------------------------------------------------------------------------
149
+ (0, vitest_1.describe)('createClusterer()', () => {
150
+ (0, vitest_1.it)('returns an object with cluster, findOptimalK, silhouetteScore', () => {
151
+ const c = (0, clusterer_1.createClusterer)({ normalize: false, seed: 1 });
152
+ (0, vitest_1.expect)(typeof c.cluster).toBe('function');
153
+ (0, vitest_1.expect)(typeof c.findOptimalK).toBe('function');
154
+ (0, vitest_1.expect)(typeof c.silhouetteScore).toBe('function');
155
+ });
156
+ (0, vitest_1.it)('cluster() uses bound config', async () => {
157
+ const c = (0, clusterer_1.createClusterer)({ normalize: false, seed: 1 });
158
+ const result = await c.cluster(ITEMS_2, { k: 2 });
159
+ (0, vitest_1.expect)(result.k).toBe(2);
160
+ (0, vitest_1.expect)(result.clusters).toHaveLength(2);
161
+ });
162
+ (0, vitest_1.it)('cluster() options override bound config', async () => {
163
+ const c = (0, clusterer_1.createClusterer)({ normalize: false, seed: 1, k: 2 });
164
+ // Override k=2 with k=1
165
+ const result = await c.cluster(ITEMS_2, { k: 1 });
166
+ (0, vitest_1.expect)(result.k).toBe(1);
167
+ });
168
+ (0, vitest_1.it)('findOptimalK() uses bound config', async () => {
169
+ const c = (0, clusterer_1.createClusterer)({ normalize: false, seed: 1 });
170
+ const result = await c.findOptimalK(ITEMS_3, { maxK: 5 });
171
+ (0, vitest_1.expect)(result.k).toBeGreaterThanOrEqual(2);
172
+ });
173
+ (0, vitest_1.it)('silhouetteScore() returns SilhouetteResult', async () => {
174
+ const c = (0, clusterer_1.createClusterer)({ normalize: false, seed: 1 });
175
+ const result = await c.cluster(ITEMS_2, { k: 2 });
176
+ const sil = c.silhouetteScore(result);
177
+ (0, vitest_1.expect)(typeof sil.score).toBe('number');
178
+ (0, vitest_1.expect)(sil.perCluster).toHaveLength(2);
179
+ });
180
+ (0, vitest_1.it)('two clusterers with different seeds can produce different assignments', async () => {
181
+ // Use a case where seed matters: run on data where init order could vary
182
+ const c1 = (0, clusterer_1.createClusterer)({ normalize: false, seed: 1 });
183
+ const c2 = (0, clusterer_1.createClusterer)({ normalize: false, seed: 99999 });
184
+ const r1 = await c1.cluster(ITEMS_3, { k: 3 });
185
+ const r2 = await c2.cluster(ITEMS_3, { k: 3 });
186
+ // Both should still produce 3 clusters — the assignments may differ
187
+ (0, vitest_1.expect)(r1.clusters).toHaveLength(3);
188
+ (0, vitest_1.expect)(r2.clusters).toHaveLength(3);
189
+ });
190
+ });
191
+ // ---------------------------------------------------------------------------
192
+ // silhouetteScore re-export from clusterer
193
+ // ---------------------------------------------------------------------------
194
+ (0, vitest_1.describe)('silhouetteScore re-export', () => {
195
+ (0, vitest_1.it)('silhouetteScore from clusterer matches silhouetteScore from silhouette module', async () => {
196
+ const result = await (0, clusterer_1.cluster)(ITEMS_2, { k: 2, normalize: false, seed: 1 });
197
+ const s1 = (0, clusterer_1.silhouetteScore)(result);
198
+ const s2 = (0, silhouette_1.silhouetteScore)(result);
199
+ (0, vitest_1.expect)(s1.score).toBeCloseTo(s2.score, 10);
200
+ });
201
+ });
202
+ //# sourceMappingURL=cluster.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cluster.test.js","sourceRoot":"","sources":["../../src/__tests__/cluster.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=errors.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/errors.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const errors_1 = require("../errors");
5
+ (0, vitest_1.describe)('ClusterError', () => {
6
+ (0, vitest_1.it)('extends Error', () => {
7
+ const err = new errors_1.ClusterError('something failed', 'EMPTY_INPUT');
8
+ (0, vitest_1.expect)(err).toBeInstanceOf(Error);
9
+ });
10
+ (0, vitest_1.it)('name is ClusterError', () => {
11
+ const err = new errors_1.ClusterError('msg', 'INVALID_K');
12
+ (0, vitest_1.expect)(err.name).toBe('ClusterError');
13
+ });
14
+ (0, vitest_1.it)('code is accessible', () => {
15
+ const err = new errors_1.ClusterError('bad k', 'INVALID_K');
16
+ (0, vitest_1.expect)(err.code).toBe('INVALID_K');
17
+ });
18
+ (0, vitest_1.it)('instanceof ClusterError is correct', () => {
19
+ const err = new errors_1.ClusterError('test', 'INVALID_OPTIONS');
20
+ (0, vitest_1.expect)(err).toBeInstanceOf(errors_1.ClusterError);
21
+ });
22
+ (0, vitest_1.it)('message is preserved', () => {
23
+ const msg = 'embeddings array is empty';
24
+ const err = new errors_1.ClusterError(msg, 'EMPTY_INPUT');
25
+ (0, vitest_1.expect)(err.message).toBe(msg);
26
+ });
27
+ (0, vitest_1.it)('constructs with EMPTY_INPUT code', () => {
28
+ const err = new errors_1.ClusterError('no items', 'EMPTY_INPUT');
29
+ (0, vitest_1.expect)(err.code).toBe('EMPTY_INPUT');
30
+ });
31
+ (0, vitest_1.it)('constructs with INCONSISTENT_DIMENSIONS code', () => {
32
+ const err = new errors_1.ClusterError('dim mismatch', 'INCONSISTENT_DIMENSIONS');
33
+ (0, vitest_1.expect)(err.code).toBe('INCONSISTENT_DIMENSIONS');
34
+ });
35
+ (0, vitest_1.it)('constructs with DEGENERATE_INPUT code', () => {
36
+ const err = new errors_1.ClusterError('all zeros', 'DEGENERATE_INPUT');
37
+ (0, vitest_1.expect)(err.code).toBe('DEGENERATE_INPUT');
38
+ });
39
+ (0, vitest_1.it)('constructs with INVALID_K code', () => {
40
+ const err = new errors_1.ClusterError('k must be >= 2', 'INVALID_K');
41
+ (0, vitest_1.expect)(err.code).toBe('INVALID_K');
42
+ });
43
+ (0, vitest_1.it)('constructs with INVALID_OPTIONS code', () => {
44
+ const err = new errors_1.ClusterError('bad options', 'INVALID_OPTIONS');
45
+ (0, vitest_1.expect)(err.code).toBe('INVALID_OPTIONS');
46
+ });
47
+ (0, vitest_1.it)('all 5 error codes construct correctly', () => {
48
+ const codes = [
49
+ 'EMPTY_INPUT',
50
+ 'INCONSISTENT_DIMENSIONS',
51
+ 'DEGENERATE_INPUT',
52
+ 'INVALID_K',
53
+ 'INVALID_OPTIONS',
54
+ ];
55
+ for (const code of codes) {
56
+ const err = new errors_1.ClusterError(`error: ${code}`, code);
57
+ (0, vitest_1.expect)(err).toBeInstanceOf(errors_1.ClusterError);
58
+ (0, vitest_1.expect)(err).toBeInstanceOf(Error);
59
+ (0, vitest_1.expect)(err.code).toBe(code);
60
+ (0, vitest_1.expect)(err.name).toBe('ClusterError');
61
+ }
62
+ });
63
+ (0, vitest_1.it)('stack trace is populated', () => {
64
+ const err = new errors_1.ClusterError('trace check', 'EMPTY_INPUT');
65
+ (0, vitest_1.expect)(err.stack).toBeDefined();
66
+ });
67
+ });
68
+ //# sourceMappingURL=errors.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.test.js","sourceRoot":"","sources":["../../src/__tests__/errors.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ {
2
+ "description": "20 synthetic 4D embeddings with 3 clear clusters",
3
+ "items": [
4
+ { "id": "c0-0", "text": "doc 0", "embedding": [0.95, 0.1, -0.1, 0.05] },
5
+ { "id": "c0-1", "text": "doc 1", "embedding": [1.0, -0.05, 0.08, -0.03] },
6
+ { "id": "c0-2", "text": "doc 2", "embedding": [0.9, 0.15, -0.12, 0.1] },
7
+ { "id": "c0-3", "text": "doc 3", "embedding": [0.98, 0.0, 0.05, -0.08] },
8
+ { "id": "c0-4", "text": "doc 4", "embedding": [0.92, -0.1, 0.1, 0.05] },
9
+ { "id": "c0-5", "text": "doc 5", "embedding": [0.97, 0.07, -0.07, 0.02] },
10
+ { "id": "c1-0", "text": "doc 6", "embedding": [-0.05, 0.95, 0.1, 0.05] },
11
+ { "id": "c1-1", "text": "doc 7", "embedding": [0.08, 1.0, -0.05, -0.03] },
12
+ { "id": "c1-2", "text": "doc 8", "embedding": [-0.1, 0.9, 0.15, 0.1] },
13
+ { "id": "c1-3", "text": "doc 9", "embedding": [0.0, 0.98, 0.05, -0.08] },
14
+ { "id": "c1-4", "text": "doc 10", "embedding": [0.07, 0.93, -0.1, -0.05] },
15
+ { "id": "c1-5", "text": "doc 11", "embedding": [-0.03, 0.97, 0.07, 0.02] },
16
+ { "id": "c2-0", "text": "doc 12", "embedding": [0.05, -0.05, 0.95, 0.1] },
17
+ { "id": "c2-1", "text": "doc 13", "embedding": [-0.08, 0.05, 1.0, -0.05] },
18
+ { "id": "c2-2", "text": "doc 14", "embedding": [0.1, -0.1, 0.9, 0.15] },
19
+ { "id": "c2-3", "text": "doc 15", "embedding": [-0.0, 0.0, 0.98, 0.05] },
20
+ { "id": "c2-4", "text": "doc 16", "embedding": [0.07, 0.05, 0.93, -0.1] },
21
+ { "id": "c2-5", "text": "doc 17", "embedding": [-0.03, -0.03, 0.97, 0.07] },
22
+ { "id": "extra-0", "text": "doc 18", "embedding": [0.96, 0.12, -0.08, 0.03] },
23
+ { "id": "extra-1", "text": "doc 19", "embedding": [-0.02, 0.96, 0.12, 0.04] }
24
+ ]
25
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=fixtures.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixtures.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/fixtures.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const vitest_1 = require("vitest");
7
+ const embeddings_small_json_1 = __importDefault(require("./fixtures/embeddings-small.json"));
8
+ (0, vitest_1.describe)('embeddings-small.json fixture', () => {
9
+ (0, vitest_1.it)('loads and parses', () => {
10
+ (0, vitest_1.expect)(embeddings_small_json_1.default).toBeDefined();
11
+ (0, vitest_1.expect)(typeof embeddings_small_json_1.default).toBe('object');
12
+ });
13
+ (0, vitest_1.it)('has exactly 20 items', () => {
14
+ (0, vitest_1.expect)(embeddings_small_json_1.default.items).toHaveLength(20);
15
+ });
16
+ (0, vitest_1.it)('each item has id, text, embedding fields', () => {
17
+ for (const item of embeddings_small_json_1.default.items) {
18
+ (0, vitest_1.expect)(typeof item.id).toBe('string');
19
+ (0, vitest_1.expect)(typeof item.text).toBe('string');
20
+ (0, vitest_1.expect)(Array.isArray(item.embedding)).toBe(true);
21
+ }
22
+ });
23
+ (0, vitest_1.it)('all embeddings have length 4', () => {
24
+ for (const item of embeddings_small_json_1.default.items) {
25
+ (0, vitest_1.expect)(item.embedding).toHaveLength(4);
26
+ }
27
+ });
28
+ (0, vitest_1.it)('all embedding values are numbers', () => {
29
+ for (const item of embeddings_small_json_1.default.items) {
30
+ for (const val of item.embedding) {
31
+ (0, vitest_1.expect)(typeof val).toBe('number');
32
+ }
33
+ }
34
+ });
35
+ (0, vitest_1.it)('all item ids are unique', () => {
36
+ const ids = embeddings_small_json_1.default.items.map((i) => i.id);
37
+ const unique = new Set(ids);
38
+ (0, vitest_1.expect)(unique.size).toBe(20);
39
+ });
40
+ (0, vitest_1.it)('has a description field', () => {
41
+ (0, vitest_1.expect)(typeof embeddings_small_json_1.default.description).toBe('string');
42
+ });
43
+ });
44
+ //# sourceMappingURL=fixtures.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixtures.test.js","sourceRoot":"","sources":["../../src/__tests__/fixtures.test.ts"],"names":[],"mappings":";;;;;AAAA,mCAA8C;AAC9C,6FAAuD;AAEvD,IAAA,iBAAQ,EAAC,+BAA+B,EAAE,GAAG,EAAE;IAC7C,IAAA,WAAE,EAAC,kBAAkB,EAAE,GAAG,EAAE;QAC1B,IAAA,eAAM,EAAC,+BAAO,CAAC,CAAC,WAAW,EAAE,CAAC;QAC9B,IAAA,eAAM,EAAC,OAAO,+BAAO,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,sBAAsB,EAAE,GAAG,EAAE;QAC9B,IAAA,eAAM,EAAC,+BAAO,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,KAAK,MAAM,IAAI,IAAI,+BAAO,CAAC,KAAK,EAAE,CAAC;YACjC,IAAA,eAAM,EAAC,OAAO,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACtC,IAAA,eAAM,EAAC,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACxC,IAAA,eAAM,EAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,KAAK,MAAM,IAAI,IAAI,+BAAO,CAAC,KAAK,EAAE,CAAC;YACjC,IAAA,eAAM,EAAC,IAAI,CAAC,SAAS,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACzC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,KAAK,MAAM,IAAI,IAAI,+BAAO,CAAC,KAAK,EAAE,CAAC;YACjC,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;gBACjC,IAAA,eAAM,EAAC,OAAO,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACpC,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,MAAM,GAAG,GAAG,+BAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC3C,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAA,eAAM,EAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,IAAA,eAAM,EAAC,OAAO,+BAAO,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=kmeans.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kmeans.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/kmeans.test.ts"],"names":[],"mappings":""}