@krutai/excel-comparison 0.0.6 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AI_REFERENCE.md +96 -298
- package/README.md +54 -63
- package/dist/index.js +23 -34166
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3 -34153
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
package/AI_REFERENCE.md
CHANGED
|
@@ -1,353 +1,151 @@
|
|
|
1
1
|
# AI Reference Guide for @krutai/excel-comparison
|
|
2
2
|
|
|
3
|
-
This document provides comprehensive context for AI assistants to help users integrate and use the excel-comparison library effectively.
|
|
3
|
+
This document provides comprehensive context for AI assistants to help users integrate and use the excel-comparison library effectively in Next.js applications.
|
|
4
4
|
|
|
5
5
|
## Library Purpose
|
|
6
6
|
|
|
7
|
-
The `@krutai/excel-comparison` library compares Excel (.xlsx, .xls) and CSV files
|
|
7
|
+
The `@krutai/excel-comparison` library compares Excel (.xlsx, .xls) and CSV files by offloading processing to a backend API. It finds:
|
|
8
8
|
- Matching rows between two files
|
|
9
9
|
- Different values in matching rows
|
|
10
10
|
- Unique rows in each file
|
|
11
11
|
- Generates downloadable Excel reports with differences highlighted
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Next.js API Route Integration (Recommended)
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
import { krutExcelComparison, createComparisonClient } from "@krutai/excel-comparison";
|
|
17
|
-
|
|
18
|
-
// Create client using convenience factory (recommended)
|
|
19
|
-
const client = krutExcelComparison({
|
|
20
|
-
serverUrl: "https://api.krut.ai",
|
|
21
|
-
apiKey: "your-krut-api-key"
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
// Or using createComparisonClient
|
|
25
|
-
const client2 = createComparisonClient({
|
|
26
|
-
apiKey: "..."
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
// API client class
|
|
30
|
-
import { ComparisonApiClient } from "@krutai/excel-comparison";
|
|
31
|
-
|
|
32
|
-
### Type Exports
|
|
33
|
-
|
|
34
|
-
```typescript
|
|
35
|
-
import type {
|
|
36
|
-
ComparisonApiResponse, // Result from API comparison
|
|
37
|
-
PreviewResponse, // Preview info for files
|
|
38
|
-
CompareFilesOptions, // Configuration options
|
|
39
|
-
DataRecord, // Row data as object (used in reporting/engine)
|
|
40
|
-
} from "@krutai/excel-comparison";
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
## Common Use Cases
|
|
44
|
-
|
|
45
|
-
### 1. Basic File Comparison (via API)
|
|
46
|
-
|
|
47
|
-
```typescript
|
|
48
|
-
import { krutExcelComparison } from "@krutai/excel-comparison";
|
|
49
|
-
|
|
50
|
-
const client = krutExcelComparison({
|
|
51
|
-
apiKey: "your-api-key"
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
// For browser - using File objects
|
|
55
|
-
const result = await client.compareFilesFromFileObjects(file1, file2);
|
|
56
|
-
|
|
57
|
-
// For Node.js/Server - using Buffer
|
|
58
|
-
const result2 = await client.compareFiles(buffer1, "file1.xlsx", buffer2, "file2.xlsx");
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
### 2. With Comparison Options
|
|
62
|
-
|
|
63
|
-
```typescript
|
|
64
|
-
const result = await client.compareFilesFromFileObjects(file1, file2, {
|
|
65
|
-
matchColumn: "Invoice", // Optional: column to match rows
|
|
66
|
-
tolerance: 0.01, // Optional: numeric tolerance
|
|
67
|
-
caseSensitive: false, // Optional: case insensitive
|
|
68
|
-
ignoreColumns: ["Timestamp"] // Optional: ignore these columns
|
|
69
|
-
});
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
### 3. Preview Files Before Comparison
|
|
73
|
-
|
|
74
|
-
```typescript
|
|
75
|
-
// Get preview metadata and suggested match columns
|
|
76
|
-
const preview = await client.previewFiles(file1, file2);
|
|
77
|
-
|
|
78
|
-
// Returns:
|
|
79
|
-
{
|
|
80
|
-
success: true,
|
|
81
|
-
file1: { name: "sales.xlsx", rowCount: 100, columns: [...], sample: [...] },
|
|
82
|
-
file2: { name: "sales2.xlsx", rowCount: 95, columns: [...], sample: [...] },
|
|
83
|
-
suggestedMatchColumn: "Invoice"
|
|
84
|
-
}
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
### 4. Generate Excel Report
|
|
88
|
-
|
|
89
|
-
```typescript
|
|
90
|
-
import { StyledReporter } from "@krutai/excel-comparison";
|
|
91
|
-
|
|
92
|
-
const reporter = new StyledReporter({
|
|
93
|
-
headerColor: "4472C4", // Blue header
|
|
94
|
-
differenceColor: "FFCCCC", // Red for differences
|
|
95
|
-
matchColor: "CCFFCC", // Green for matches
|
|
96
|
-
includeSummary: true
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
// Transform comparison results for report
|
|
100
|
-
const reportData = result.results.map((r, i) => ({
|
|
101
|
-
Row: i + 1,
|
|
102
|
-
Status: r.status,
|
|
103
|
-
"Match %": `${r.matchPercentage}%`,
|
|
104
|
-
...Object.fromEntries(
|
|
105
|
-
result.allColumns.flatMap(col => [
|
|
106
|
-
[`File1_${col}`, r.file1Row?.[col] ?? 'N/A'],
|
|
107
|
-
[`File2_${col}`, r.file2Row?.[col] ?? 'N/A'],
|
|
108
|
-
])
|
|
109
|
-
)
|
|
110
|
-
}));
|
|
111
|
-
|
|
112
|
-
const report = await reporter.generateReport({
|
|
113
|
-
data: reportData,
|
|
114
|
-
summary: result.summary,
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
// Download as base64
|
|
118
|
-
const downloadUrl = `data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,${report.toString('base64')}`;
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
### 5. Next.js API Route Integration (Recommended)
|
|
15
|
+
This is the primary way to use the library in a modern Next.js application.
|
|
122
16
|
|
|
123
17
|
```typescript
|
|
124
18
|
// app/api/compare/route.ts
|
|
125
19
|
import { NextRequest, NextResponse } from "next/server";
|
|
126
|
-
import {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
} from "@krutai/excel-comparison";
|
|
20
|
+
import { krutExcelComparison, type CompareFilesOptions } from "@krutai/excel-comparison";
|
|
21
|
+
|
|
22
|
+
export const dynamic = "force-dynamic";
|
|
130
23
|
|
|
131
24
|
const SUPPORTED_EXTENSIONS = new Set(["xlsx", "xls", "csv"]);
|
|
132
25
|
|
|
133
26
|
function getFileExtension(fileName: string): string {
|
|
134
|
-
|
|
135
|
-
|
|
27
|
+
const parts = fileName.toLowerCase().split(".");
|
|
28
|
+
return parts.length > 1 ? parts[parts.length - 1] : "";
|
|
136
29
|
}
|
|
137
30
|
|
|
138
31
|
function isSupportedFile(file: File): boolean {
|
|
139
|
-
|
|
32
|
+
return SUPPORTED_EXTENSIONS.has(getFileExtension(file.name));
|
|
140
33
|
}
|
|
141
34
|
|
|
142
|
-
function
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
35
|
+
export async function POST(req: NextRequest) {
|
|
36
|
+
try {
|
|
37
|
+
const formData = await req.formData();
|
|
38
|
+
const file1 = formData.get("file1");
|
|
39
|
+
const file2 = formData.get("file2");
|
|
40
|
+
|
|
41
|
+
if (!(file1 instanceof File) || !(file2 instanceof File)) {
|
|
42
|
+
return NextResponse.json(
|
|
43
|
+
{ error: "Both file1 and file2 are required." },
|
|
44
|
+
{ status: 400 }
|
|
45
|
+
);
|
|
149
46
|
}
|
|
150
|
-
return raw;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function parseIgnoreColumns(value?: string): string[] | undefined {
|
|
154
|
-
if (!value?.trim()) return undefined;
|
|
155
47
|
|
|
156
|
-
if (
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
.filter((item): item is string => typeof item === "string")
|
|
162
|
-
.map((item) => item.trim())
|
|
163
|
-
.filter(Boolean);
|
|
164
|
-
}
|
|
165
|
-
} catch { }
|
|
48
|
+
if (!isSupportedFile(file1) || !isSupportedFile(file2)) {
|
|
49
|
+
return NextResponse.json(
|
|
50
|
+
{ error: "Only .xlsx, .xls, and .csv files are supported." },
|
|
51
|
+
{ status: 400 }
|
|
52
|
+
);
|
|
166
53
|
}
|
|
167
54
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
const caseSensitive = formData.get("caseSensitive") as string | undefined;
|
|
180
|
-
|
|
181
|
-
if (!file1 || !file2) {
|
|
182
|
-
return NextResponse.json({ error: "Both file1 and file2 are required" }, { status: 400 });
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
if (!isSupportedFile(file1) || !isSupportedFile(file2)) {
|
|
186
|
-
return NextResponse.json({ error: "Unsupported file type. Use .xlsx, .xls, or .csv files." }, { status: 400 });
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const options: CompareFilesOptions = {};
|
|
190
|
-
if (matchColumn?.trim()) options.matchColumn = matchColumn.trim();
|
|
191
|
-
const parsedIgnore = parseIgnoreColumns(ignoreColumns);
|
|
192
|
-
if (parsedIgnore) options.ignoreColumns = parsedIgnore;
|
|
193
|
-
if (tolerance?.trim()) options.tolerance = Number(tolerance);
|
|
194
|
-
if (caseSensitive !== undefined) options.caseSensitive = caseSensitive === "true";
|
|
195
|
-
|
|
196
|
-
const client = createComparisonClient({
|
|
197
|
-
apiKey: process.env.KRUTAI_API_KEY || '',
|
|
198
|
-
serverUrl: process.env.KRUTAI_SERVER_URL || "http://localhost:8000",
|
|
199
|
-
validateOnInit: false,
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
const apiResponse = await client.compareFilesFromFileObjects(file1, file2, options);
|
|
203
|
-
|
|
204
|
-
if (!apiResponse.success || !apiResponse.result) {
|
|
205
|
-
return NextResponse.json({ error: apiResponse.error ?? "Comparison failed" }, { status: 500 });
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
return NextResponse.json({
|
|
209
|
-
success: true,
|
|
210
|
-
result: apiResponse.result,
|
|
211
|
-
results: [],
|
|
212
|
-
allColumns: [...new Set([
|
|
213
|
-
...(apiResponse.result.metadata.file1Columns ?? []),
|
|
214
|
-
...(apiResponse.result.metadata.file2Columns ?? []),
|
|
215
|
-
])],
|
|
216
|
-
columnsWithDiffs: [],
|
|
217
|
-
downloadUrl: apiResponse.downloadUrl,
|
|
218
|
-
fileName: apiResponse.fileName,
|
|
219
|
-
});
|
|
220
|
-
} catch (err: unknown) {
|
|
221
|
-
const msg = err instanceof Error ? err.message : "Comparison failed";
|
|
222
|
-
return NextResponse.json({ error: getComparisonErrorMessage(msg) }, { status: 500 });
|
|
55
|
+
const client = krutExcelComparison({
|
|
56
|
+
apiKey: process.env.KRUTAI_API_KEY ?? "",
|
|
57
|
+
serverUrl: process.env.KRUTAI_SERVER_URL ?? "http://localhost:8000",
|
|
58
|
+
validateOnInit: false,
|
|
59
|
+
});
|
|
60
|
+
await client.initialize();
|
|
61
|
+
|
|
62
|
+
const compareOptions: CompareFilesOptions = {};
|
|
63
|
+
const matchColumn = (formData.get("matchColumn") as string | null)?.trim();
|
|
64
|
+
if (matchColumn) {
|
|
65
|
+
compareOptions.matchColumn = matchColumn;
|
|
223
66
|
}
|
|
224
|
-
}
|
|
225
|
-
```
|
|
226
67
|
|
|
227
|
-
|
|
68
|
+
const caseSensitive = (formData.get("caseSensitive") as string | null) === "true";
|
|
69
|
+
compareOptions.caseSensitive = caseSensitive;
|
|
228
70
|
|
|
229
|
-
|
|
230
|
-
import express from 'express';
|
|
231
|
-
import multer from 'multer';
|
|
232
|
-
import { krutExcelComparison } from "@krutai/excel-comparison";
|
|
71
|
+
const response = await client.compareFilesFromFileObjects(file1, file2, compareOptions);
|
|
233
72
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
{ name: 'file1', maxCount: 1 },
|
|
240
|
-
{ name: 'file2', maxCount: 1 }
|
|
241
|
-
]), async (req, res) => {
|
|
242
|
-
const files = req.files as { [fieldname: string]: Express.Multer.File[] };
|
|
243
|
-
|
|
244
|
-
const result = await client.compareFiles(
|
|
245
|
-
files.file1[0].buffer, files.file1[0].originalname,
|
|
246
|
-
files.file2[0].buffer, files.file2[0].originalname,
|
|
247
|
-
{
|
|
248
|
-
matchColumn: req.body.matchColumn,
|
|
249
|
-
tolerance: req.body.tolerance ? parseFloat(req.body.tolerance) : undefined
|
|
73
|
+
if (!response.success) {
|
|
74
|
+
return NextResponse.json(
|
|
75
|
+
{ error: response.error ?? "Comparison failed." },
|
|
76
|
+
{ status: 400 }
|
|
77
|
+
);
|
|
250
78
|
}
|
|
251
|
-
);
|
|
252
79
|
|
|
253
|
-
|
|
254
|
-
})
|
|
80
|
+
return NextResponse.json(response);
|
|
81
|
+
} catch (err) {
|
|
82
|
+
const message = err instanceof Error ? err.message : "Comparison failed.";
|
|
83
|
+
return NextResponse.json({ error: message }, { status: 500 });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
255
86
|
```
|
|
256
87
|
|
|
257
|
-
##
|
|
88
|
+
## Type Definitions
|
|
258
89
|
|
|
90
|
+
### ComparisonApiResponse
|
|
259
91
|
```typescript
|
|
260
|
-
{
|
|
261
|
-
success: boolean
|
|
92
|
+
interface ComparisonApiResponse {
|
|
93
|
+
success: boolean;
|
|
262
94
|
result?: {
|
|
263
95
|
summary: {
|
|
264
|
-
totalRows: number
|
|
265
|
-
matchesFound: number
|
|
266
|
-
differencesFound: number
|
|
267
|
-
uniqueToFile1: number
|
|
268
|
-
uniqueToFile2: number
|
|
269
|
-
status: 'SUCCESS' | 'PARTIAL' | 'NO_MATCH'
|
|
270
|
-
}
|
|
271
|
-
matchColumn: string
|
|
96
|
+
totalRows: number;
|
|
97
|
+
matchesFound: number;
|
|
98
|
+
differencesFound: number;
|
|
99
|
+
uniqueToFile1: number;
|
|
100
|
+
uniqueToFile2: number;
|
|
101
|
+
status: 'SUCCESS' | 'PARTIAL' | 'NO_MATCH';
|
|
102
|
+
};
|
|
103
|
+
matchColumn: string;
|
|
272
104
|
metadata: {
|
|
273
|
-
file1Name: string
|
|
274
|
-
file1Columns: string[]
|
|
275
|
-
file1RowCount: number
|
|
276
|
-
file2Name: string
|
|
277
|
-
file2Columns: string[]
|
|
278
|
-
file2RowCount: number
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
downloadUrl?: string
|
|
282
|
-
fileName?: string
|
|
283
|
-
error?: string
|
|
105
|
+
file1Name: string;
|
|
106
|
+
file1Columns: string[];
|
|
107
|
+
file1RowCount: number;
|
|
108
|
+
file2Name: string;
|
|
109
|
+
file2Columns: string[];
|
|
110
|
+
file2RowCount: number;
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
downloadUrl?: string; // Link to the Excel report
|
|
114
|
+
fileName?: string;
|
|
115
|
+
error?: string;
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### CompareFilesOptions
|
|
120
|
+
```typescript
|
|
121
|
+
interface CompareFilesOptions {
|
|
122
|
+
matchColumn?: string; // Column to use for row matching
|
|
123
|
+
caseSensitive?: boolean; // Default is true
|
|
124
|
+
ignoreColumns?: string[]; // Columns to skip in comparison
|
|
125
|
+
tolerance?: number; // Numeric tolerance for float comparison
|
|
284
126
|
}
|
|
285
127
|
```
|
|
286
128
|
|
|
287
129
|
## Key Behaviors
|
|
288
130
|
|
|
289
131
|
### API Comparison
|
|
290
|
-
- The package
|
|
132
|
+
- The package acts as a client for a backend comparison service.
|
|
291
133
|
- It offloads heavy Excel processing and comparison logic to the server.
|
|
292
134
|
- It returns a summary of results and a link to a professionally styled Excel report.
|
|
293
135
|
|
|
294
136
|
### Column Matching
|
|
295
|
-
- If `matchColumn` is provided, uses that column to match rows
|
|
296
|
-
- If not provided, auto-detects: prioritizes id
|
|
297
|
-
- Falls back to first column if no match found
|
|
298
|
-
|
|
299
|
-
### Value Comparison (Backend)
|
|
300
|
-
- Done on the server side using intelligent algorithms.
|
|
301
|
-
- Supports fuzzy matching, numeric tolerance, and case sensitivity.
|
|
302
|
-
|
|
303
|
-
## Common Integration Patterns
|
|
304
|
-
|
|
305
|
-
### Pattern 1: Basic Comparison
|
|
306
|
-
```typescript
|
|
307
|
-
const client = krutExcelComparison({ apiKey: "..." });
|
|
308
|
-
const result = await client.compareFilesFromFileObjects(f1, f2);
|
|
309
|
-
```
|
|
137
|
+
- If `matchColumn` is provided, uses that column to match rows.
|
|
138
|
+
- If not provided, the server auto-detects: prioritizes columns like `id`, `invoice`, `gstin`, `number`, `name`, `code`.
|
|
139
|
+
- Falls back to the first column if no match is found.
|
|
310
140
|
|
|
311
|
-
###
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
tolerance: 0.001,
|
|
316
|
-
caseSensitive: false
|
|
317
|
-
});
|
|
318
|
-
```
|
|
319
|
-
|
|
320
|
-
## Error Handling
|
|
321
|
-
|
|
322
|
-
```typescript
|
|
323
|
-
try {
|
|
324
|
-
const result = await client.compareFilesFromFileObjects(file1, file2);
|
|
325
|
-
} catch (error) {
|
|
326
|
-
if (error.message.includes("Request timed out")) {
|
|
327
|
-
// Handle timeout
|
|
328
|
-
} else {
|
|
329
|
-
// Generic error
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
## Package Dependencies
|
|
335
|
-
|
|
336
|
-
The library is lightweight and depends on:
|
|
337
|
-
- `exceljs` - For local reporting (optional)
|
|
338
|
-
|
|
339
|
-
## Environment Notes
|
|
340
|
-
|
|
341
|
-
- Works in Node.js (v14+) and modern browsers
|
|
342
|
-
- Uses dynamic imports for xlsx to support Next.js
|
|
343
|
-
- File objects from HTML inputs work in browser
|
|
344
|
-
- Buffer objects work in Node.js
|
|
141
|
+
### Environment Notes
|
|
142
|
+
- Optimized for Next.js and Serverless environments.
|
|
143
|
+
- Uses Web `File` objects or Node.js `Buffer` objects.
|
|
144
|
+
- Does not bundle heavy dependencies like `xlsx` directly (offloaded to API).
|
|
345
145
|
|
|
346
146
|
## Contributing Guidelines for AI Assistants
|
|
347
147
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
3. Show Next.js API route as the primary integration example.
|
|
353
|
-
4. Note that heavy parsing (xlsx) is done on the backend.
|
|
148
|
+
1. Always recommend the **Next.js API route** pattern for server-side processing.
|
|
149
|
+
2. Ensure `client.initialize()` is called before use.
|
|
150
|
+
3. Remind users that `.xlsx`, `.xls`, and `.csv` are the supported formats.
|
|
151
|
+
4. Emphasize that the actual comparison and report generation happen on the backend server.
|
package/README.md
CHANGED
|
@@ -17,30 +17,6 @@ npm install @krutai/excel-comparison
|
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
## Quick Start
|
|
20
|
-
|
|
21
|
-
### Basic Usage (Server-side)
|
|
22
|
-
|
|
23
|
-
```typescript
|
|
24
|
-
import { krutExcelComparison } from "@krutai/excel-comparison";
|
|
25
|
-
|
|
26
|
-
// Create client
|
|
27
|
-
const client = krutExcelComparison({
|
|
28
|
-
apiKey: process.env.KRUTAI_API_KEY,
|
|
29
|
-
serverUrl: process.env.KRUTAI_SERVER_URL || "https://api.krut.ai"
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
// Compare files using buffers
|
|
33
|
-
const result = await client.compareFiles(
|
|
34
|
-
file1Buffer, "file1.xlsx",
|
|
35
|
-
file2Buffer, "file2.xlsx",
|
|
36
|
-
{ matchColumn: "Invoice" }
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
if (result.success) {
|
|
40
|
-
console.log('Report URL:', result.downloadUrl);
|
|
41
|
-
}
|
|
42
|
-
```
|
|
43
|
-
|
|
44
20
|
### Next.js API Route Integration
|
|
45
21
|
|
|
46
22
|
This is the recommended way to use the library in a Next.js application.
|
|
@@ -48,58 +24,73 @@ This is the recommended way to use the library in a Next.js application.
|
|
|
48
24
|
```typescript
|
|
49
25
|
// app/api/compare/route.ts
|
|
50
26
|
import { NextRequest, NextResponse } from "next/server";
|
|
51
|
-
import {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
} from "@krutai/excel-comparison";
|
|
27
|
+
import { krutExcelComparison, type CompareFilesOptions } from "@krutai/excel-comparison";
|
|
28
|
+
|
|
29
|
+
export const dynamic = "force-dynamic";
|
|
55
30
|
|
|
56
31
|
const SUPPORTED_EXTENSIONS = new Set(["xlsx", "xls", "csv"]);
|
|
57
32
|
|
|
58
33
|
function getFileExtension(fileName: string): string {
|
|
59
|
-
|
|
60
|
-
|
|
34
|
+
const parts = fileName.toLowerCase().split(".");
|
|
35
|
+
return parts.length > 1 ? parts[parts.length - 1] : "";
|
|
61
36
|
}
|
|
62
37
|
|
|
63
38
|
function isSupportedFile(file: File): boolean {
|
|
64
|
-
|
|
39
|
+
return SUPPORTED_EXTENSIONS.has(getFileExtension(file.name));
|
|
65
40
|
}
|
|
66
41
|
|
|
67
|
-
function
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
42
|
+
export async function POST(req: NextRequest) {
|
|
43
|
+
try {
|
|
44
|
+
const formData = await req.formData();
|
|
45
|
+
const file1 = formData.get("file1");
|
|
46
|
+
const file2 = formData.get("file2");
|
|
47
|
+
|
|
48
|
+
if (!(file1 instanceof File) || !(file2 instanceof File)) {
|
|
49
|
+
return NextResponse.json(
|
|
50
|
+
{ error: "Both file1 and file2 are required." },
|
|
51
|
+
{ status: 400 }
|
|
52
|
+
);
|
|
74
53
|
}
|
|
75
|
-
return value.split(",").map(i => i.trim()).filter(Boolean);
|
|
76
|
-
}
|
|
77
54
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
return NextResponse.json(apiResponse);
|
|
99
|
-
} catch (err: unknown) {
|
|
100
|
-
return NextResponse.json({ error: "Comparison failed" }, { status: 500 });
|
|
55
|
+
if (!isSupportedFile(file1) || !isSupportedFile(file2)) {
|
|
56
|
+
return NextResponse.json(
|
|
57
|
+
{ error: "Only .xlsx, .xls, and .csv files are supported." },
|
|
58
|
+
{ status: 400 }
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const client = krutExcelComparison({
|
|
63
|
+
apiKey: process.env.KRUTAI_API_KEY ?? "",
|
|
64
|
+
serverUrl: process.env.KRUTAI_SERVER_URL ?? "http://localhost:8000",
|
|
65
|
+
validateOnInit: false,
|
|
66
|
+
});
|
|
67
|
+
await client.initialize();
|
|
68
|
+
|
|
69
|
+
const compareOptions: CompareFilesOptions = {};
|
|
70
|
+
const matchColumn = (formData.get("matchColumn") as string | null)?.trim();
|
|
71
|
+
if (matchColumn) {
|
|
72
|
+
compareOptions.matchColumn = matchColumn;
|
|
101
73
|
}
|
|
74
|
+
|
|
75
|
+
const caseSensitive = (formData.get("caseSensitive") as string | null) === "true";
|
|
76
|
+
compareOptions.caseSensitive = caseSensitive;
|
|
77
|
+
|
|
78
|
+
const response = await client.compareFilesFromFileObjects(file1, file2, compareOptions);
|
|
79
|
+
|
|
80
|
+
if (!response.success) {
|
|
81
|
+
return NextResponse.json(
|
|
82
|
+
{ error: response.error ?? "Comparison failed." },
|
|
83
|
+
{ status: 400 }
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return NextResponse.json(response);
|
|
88
|
+
} catch (err) {
|
|
89
|
+
const message = err instanceof Error ? err.message : "Comparison failed.";
|
|
90
|
+
return NextResponse.json({ error: message }, { status: 500 });
|
|
91
|
+
}
|
|
102
92
|
}
|
|
93
|
+
|
|
103
94
|
```
|
|
104
95
|
|
|
105
96
|
## API Reference
|