diffx-js 0.5.4 → 0.5.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/README.md CHANGED
@@ -24,62 +24,73 @@ The appropriate binary is automatically selected at runtime based on your system
24
24
  ## Usage
25
25
 
26
26
  ```javascript
27
- const { diff, diffString } = require('diffx-js');
28
-
29
- async function main() {
30
- // Compare two files
31
- const result = await diff('file1.json', 'file2.json');
32
-
33
- if (result.length === 0) {
34
- console.log("No differences found.");
35
- } else {
36
- console.log("Differences found:");
37
- for (const change of result) {
38
- console.log(`${change.type}: ${change.path} = ${change.new_value}`);
27
+ const { diff } = require('diffx-js');
28
+
29
+ // Compare two JavaScript objects
30
+ const old = { name: "Alice", age: 30, city: "Tokyo" };
31
+ const newObj = { name: "Alice", age: 31, country: "Japan" };
32
+
33
+ const result = diff(old, newObj);
34
+
35
+ if (result.length === 0) {
36
+ console.log("No differences found.");
37
+ } else {
38
+ console.log("Differences found:");
39
+ for (const change of result) {
40
+ console.log(`${change.diffType}: ${change.path}`);
41
+ if (change.oldValue !== undefined) {
42
+ console.log(` Old: ${change.oldValue}`);
43
+ }
44
+ if (change.newValue !== undefined) {
45
+ console.log(` New: ${change.newValue}`);
39
46
  }
40
47
  }
41
-
42
- // Compare with options
43
- const jsonResult = await diff('config1.yaml', 'config2.yaml', {
44
- output: 'json',
45
- ignoreKeysRegex: 'timestamp'
46
- });
47
-
48
- // Compare directory structures
49
- const dirResult = await diff('dir1/', 'dir2/', {
50
- recursive: true,
51
- output: 'json'
52
- });
53
-
54
- // Compare strings directly
55
- const stringResult = await diffString(
56
- '{"a": 1}',
57
- '{"a": 2}',
58
- 'json',
59
- { output: 'json' }
60
- );
61
48
  }
62
49
 
63
- main();
50
+ // Compare with options
51
+ const data1 = {
52
+ values: [1.0001, 2.0002, 3.0003],
53
+ metadata: { timestamp: "2024-01-01" }
54
+ };
55
+ const data2 = {
56
+ values: [1.0002, 2.0003, 3.0004],
57
+ metadata: { timestamp: "2024-01-02" }
58
+ };
59
+
60
+ const preciseResult = diff(data1, data2, {
61
+ epsilon: 0.001,
62
+ ignoreKeysRegex: "timestamp"
63
+ });
64
+
65
+ console.log(`Found ${preciseResult.length} significant differences`);
64
66
  ```
65
67
 
66
68
 
67
69
  ### API Reference
68
70
 
69
- #### `diff(input1, input2, options?)`
70
- - **input1, input2**: File paths or directory paths to compare
71
+ #### `diff(old, new, options?)`
72
+ - **old**: The old JavaScript object, array, or primitive value
73
+ - **new**: The new JavaScript object, array, or primitive value
71
74
  - **options**: Optional configuration object
72
- - `format`: Input format ('json', 'yaml', 'toml', 'xml', 'ini', 'csv')
73
- - `output`: Output format ('cli', 'json', 'yaml', 'unified')
74
- - `recursive`: Compare directories recursively
75
- - `ignoreKeysRegex`: Ignore keys matching regex pattern
76
- - `epsilon`: Tolerance for floating-point comparisons
77
- - `context`: Number of context lines in unified output
78
-
79
- #### `diffString(content1, content2, format, options?)`
80
- - **content1, content2**: String content to compare
81
- - **format**: Data format ('json', 'yaml', 'toml', etc.)
82
- - **options**: Same as `diff()` options
75
+ - `epsilon`: Tolerance for floating-point comparisons (default: 0.0)
76
+ - `arrayIdKey`: Key to use for array element identification
77
+ - `ignoreKeysRegex`: Regex pattern for keys to ignore
78
+ - `pathFilter`: Only show differences in paths containing this string
79
+ - `outputFormat`: Output format ("diffx", "json", "yaml")
80
+ - `showUnchanged`: Show unchanged values as well
81
+ - `showTypes`: Show type information in output
82
+ - `ignoreWhitespace`: Ignore whitespace differences
83
+ - `ignoreCase`: Ignore case differences
84
+ - `briefMode`: Report only whether objects differ
85
+ - `quietMode`: Suppress normal output; return only results
86
+
87
+ #### Return Value
88
+ Returns an array of `JsDiffResult` objects, each containing:
89
+ - `diffType`: Type of difference ('Added', 'Removed', 'Modified', 'TypeChanged')
90
+ - `path`: Path to the changed element
91
+ - `oldValue`: Old value (for Modified/TypeChanged/Removed)
92
+ - `newValue`: New value (for Modified/TypeChanged/Added)
93
+ - `value`: Value (for Removed differences)
83
94
 
84
95
  ## Development
85
96
 
Binary file
package/examples.ts ADDED
@@ -0,0 +1,320 @@
1
+ #!/usr/bin/env tsx
2
+
3
+ /**
4
+ * diffx-js TypeScript Examples - UNIFIED API DESIGN
5
+ *
6
+ * Demonstrates native NAPI-RS API usage for semantic diffing
7
+ * Users parse files themselves and call the unified diff() function
8
+ */
9
+
10
+ import { diff, DiffOptions, DiffResult } from './index';
11
+ import * as fs from 'fs';
12
+ import * as path from 'path';
13
+ import * as os from 'os';
14
+
15
+ // Colors for console output
16
+ const colors = {
17
+ green: '\x1b[32m',
18
+ red: '\x1b[31m',
19
+ yellow: '\x1b[33m',
20
+ blue: '\x1b[34m',
21
+ cyan: '\x1b[36m',
22
+ magenta: '\x1b[35m',
23
+ reset: '\x1b[0m'
24
+ } as const;
25
+
26
+ function log(message: string, color: keyof typeof colors = 'reset'): void {
27
+ console.log(`${colors[color]}${message}${colors.reset}`);
28
+ }
29
+
30
+ function header(message: string): void {
31
+ log(`\n${message}`, 'cyan');
32
+ log('='.repeat(message.length), 'cyan');
33
+ }
34
+
35
+ function example(title: string, description: string): void {
36
+ log(`\n${title}`, 'yellow');
37
+ log(` ${description}`, 'blue');
38
+ }
39
+
40
+ async function runExamples(): Promise<void> {
41
+ header('diffx-js Native API Examples');
42
+
43
+ // Create temporary directory
44
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'diffx-examples-'));
45
+ const oldCwd = process.cwd();
46
+ process.chdir(tempDir);
47
+
48
+ try {
49
+ // Example 1: Basic JSON Configuration Comparison
50
+ header('1. Basic JSON Configuration Comparison');
51
+
52
+ const config1 = {
53
+ app: {
54
+ name: "my-app",
55
+ version: "1.0.0",
56
+ database: {
57
+ host: "localhost",
58
+ port: 5432,
59
+ ssl: false
60
+ }
61
+ },
62
+ features: ["auth", "logging"]
63
+ };
64
+
65
+ const config2 = {
66
+ app: {
67
+ name: "my-app",
68
+ version: "1.1.0",
69
+ database: {
70
+ host: "prod-db.example.com",
71
+ port: 5432,
72
+ ssl: true
73
+ }
74
+ },
75
+ features: ["auth", "logging", "metrics"]
76
+ };
77
+
78
+ example(
79
+ 'Application Configuration Migration',
80
+ 'Compare two versions of app configuration using native API'
81
+ );
82
+
83
+ const results1 = diff(config1, config2);
84
+ log('API Results:', 'green');
85
+ console.log(JSON.stringify(results1, null, 2));
86
+
87
+ // Example 2: YAML Configuration with Options
88
+ header('2. Advanced Options Usage');
89
+
90
+ const oldYaml = `
91
+ name: CI
92
+ on:
93
+ push:
94
+ branches: [main]
95
+ jobs:
96
+ test:
97
+ runs-on: ubuntu-latest
98
+ steps:
99
+ - uses: actions/checkout@v3
100
+ - run: npm test
101
+ `;
102
+
103
+ const newYaml = `
104
+ name: CI
105
+ on:
106
+ push:
107
+ branches: [main, develop]
108
+ jobs:
109
+ test:
110
+ runs-on: ubuntu-latest
111
+ strategy:
112
+ matrix:
113
+ node-version: [16, 18, 20]
114
+ steps:
115
+ - uses: actions/checkout@v4
116
+ - run: npm ci
117
+ - run: npm test
118
+ `;
119
+
120
+ // Parse YAML using js-yaml (users would do this)
121
+ const yaml = await import('js-yaml');
122
+ const oldData = yaml.load(oldYaml) as any;
123
+ const newData = yaml.load(newYaml) as any;
124
+
125
+ const options: DiffOptions = {
126
+ outputFormat: 'json',
127
+ showTypes: true,
128
+ ignoreKeysRegex: '^(timestamp|updated_at)',
129
+ diffxOptions: {
130
+ ignoreWhitespace: true,
131
+ contextLines: 2
132
+ }
133
+ };
134
+
135
+ example(
136
+ 'YAML Comparison with Advanced Options',
137
+ 'Parse YAML yourself and use diffx options for customized output'
138
+ );
139
+
140
+ const results2 = diff(oldData, newData, options);
141
+ log('Filtered Results:', 'green');
142
+ console.log(JSON.stringify(results2, null, 2));
143
+
144
+ // Example 3: Array Comparison with ID Key
145
+ header('3. Smart Array Comparison');
146
+
147
+ const oldUsers = {
148
+ users: [
149
+ { id: 1, name: "Alice", role: "admin" },
150
+ { id: 2, name: "Bob", role: "user" },
151
+ { id: 3, name: "Charlie", role: "user" }
152
+ ]
153
+ };
154
+
155
+ const newUsers = {
156
+ users: [
157
+ { id: 1, name: "Alice", role: "admin" },
158
+ { id: 2, name: "Bob", role: "moderator" },
159
+ { id: 4, name: "David", role: "user" }
160
+ ]
161
+ };
162
+
163
+ const arrayOptions: DiffOptions = {
164
+ arrayIdKey: 'id',
165
+ showUnchanged: false
166
+ };
167
+
168
+ example(
169
+ 'User Management Changes with Array ID Matching',
170
+ 'Track user changes by ID rather than array position'
171
+ );
172
+
173
+ const results3 = diff(oldUsers, newUsers, arrayOptions);
174
+ log('User Changes:', 'green');
175
+ results3.forEach((result: DiffResult) => {
176
+ console.log(`${result.type}: ${result.path}`);
177
+ if (result.oldValue) console.log(` Old: ${JSON.stringify(result.oldValue)}`);
178
+ if (result.newValue) console.log(` New: ${JSON.stringify(result.newValue)}`);
179
+ });
180
+
181
+ // Example 4: Error Handling
182
+ header('4. Error Handling');
183
+
184
+ example(
185
+ 'Handling Invalid Data Gracefully',
186
+ 'Demonstrate proper error handling for malformed data'
187
+ );
188
+
189
+ try {
190
+ // Simulate circular reference (not serializable)
191
+ const circularObj: any = { name: "test" };
192
+ circularObj.self = circularObj;
193
+
194
+ diff({ valid: "data" }, circularObj);
195
+ } catch (error) {
196
+ log(`Caught expected error: ${error}`, 'red');
197
+ }
198
+
199
+ // Example 5: Performance with Large Data
200
+ header('5. Large Data Performance');
201
+
202
+ const largeData1 = {
203
+ items: Array.from({ length: 1000 }, (_, i) => ({
204
+ id: i,
205
+ value: Math.random(),
206
+ category: `cat_${i % 10}`
207
+ }))
208
+ };
209
+
210
+ const largeData2 = {
211
+ items: Array.from({ length: 1000 }, (_, i) => ({
212
+ id: i,
213
+ value: Math.random(),
214
+ category: `cat_${i % 10}`,
215
+ newField: i % 100 === 0 ? "special" : undefined
216
+ })).filter(item => item.newField !== undefined || Math.random() > 0.1)
217
+ };
218
+
219
+ const perfOptions: DiffOptions = {
220
+ useMemoryOptimization: true,
221
+ batchSize: 100,
222
+ arrayIdKey: 'id'
223
+ };
224
+
225
+ example(
226
+ 'Large Dataset Comparison with Memory Optimization',
227
+ 'Handle large datasets efficiently with batching'
228
+ );
229
+
230
+ const startTime = Date.now();
231
+ const results5 = diff(largeData1, largeData2, perfOptions);
232
+ const endTime = Date.now();
233
+
234
+ log(`Processed ${results5.length} differences in ${endTime - startTime}ms`, 'green');
235
+
236
+ // Example 6: TypeScript Integration Patterns
237
+ header('6. TypeScript Integration Patterns');
238
+
239
+ interface ConfigSchema {
240
+ database: {
241
+ host: string;
242
+ port: number;
243
+ ssl: boolean;
244
+ };
245
+ features: string[];
246
+ }
247
+
248
+ const typedConfig1: ConfigSchema = {
249
+ database: { host: "localhost", port: 5432, ssl: false },
250
+ features: ["auth"]
251
+ };
252
+
253
+ const typedConfig2: ConfigSchema = {
254
+ database: { host: "remote", port: 5433, ssl: true },
255
+ features: ["auth", "logging"]
256
+ };
257
+
258
+ example(
259
+ 'Type-Safe Configuration Comparison',
260
+ 'Use TypeScript interfaces for better development experience'
261
+ );
262
+
263
+ const results6 = diff(typedConfig1, typedConfig2);
264
+
265
+ // Type-safe result processing
266
+ results6.forEach((result: DiffResult) => {
267
+ switch (result.type) {
268
+ case 'added':
269
+ log(`➕ Added: ${result.path} = ${JSON.stringify(result.newValue)}`, 'green');
270
+ break;
271
+ case 'removed':
272
+ log(`➖ Removed: ${result.path} = ${JSON.stringify(result.oldValue)}`, 'red');
273
+ break;
274
+ case 'modified':
275
+ log(`🔄 Modified: ${result.path}`, 'yellow');
276
+ log(` Old: ${JSON.stringify(result.oldValue)}`, 'red');
277
+ log(` New: ${JSON.stringify(result.newValue)}`, 'green');
278
+ break;
279
+ case 'typeChanged':
280
+ log(`🔀 Type Changed: ${result.path} (${result.oldType} → ${result.newType})`, 'magenta');
281
+ break;
282
+ }
283
+ });
284
+
285
+ // Summary
286
+ header('Summary');
287
+ log('✅ All examples completed successfully!', 'green');
288
+ log('\nKey Benefits of Native API:', 'cyan');
289
+ log(' • No external CLI dependency', 'blue');
290
+ log(' • Better error handling', 'blue');
291
+ log(' • Type safety with TypeScript', 'blue');
292
+ log(' • Memory efficient for large data', 'blue');
293
+ log(' • Customizable output formats', 'blue');
294
+ log(' • Integration-friendly', 'blue');
295
+
296
+ log('\nNext Steps:', 'cyan');
297
+ log(' • See TypeScript definitions for full API', 'blue');
298
+ log(' • Check documentation for advanced options', 'blue');
299
+ log(' • Integrate into your CI/CD pipeline', 'blue');
300
+
301
+ } catch (error) {
302
+ log(`\nError running examples: ${error}`, 'red');
303
+ console.error(error);
304
+ } finally {
305
+ // Cleanup
306
+ process.chdir(oldCwd);
307
+ try {
308
+ fs.rmSync(tempDir, { recursive: true, force: true });
309
+ } catch (cleanupErr) {
310
+ log(`Cleanup warning: ${cleanupErr}`, 'yellow');
311
+ }
312
+ }
313
+ }
314
+
315
+ // Run examples if called directly
316
+ if (require.main === module) {
317
+ runExamples().catch(console.error);
318
+ }
319
+
320
+ export { runExamples };
package/index.d.ts ADDED
@@ -0,0 +1,157 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+
4
+ /* auto-generated by NAPI-RS */
5
+
6
+ export interface JsDiffOptions {
7
+ /** Numerical comparison tolerance */
8
+ epsilon?: number
9
+ /** Key to use for array element identification */
10
+ arrayIdKey?: string
11
+ /** Regex pattern for keys to ignore */
12
+ ignoreKeysRegex?: string
13
+ /** Only show differences in paths containing this string */
14
+ pathFilter?: string
15
+ /** Output format */
16
+ outputFormat?: string
17
+ /** Show unchanged values as well */
18
+ showUnchanged?: boolean
19
+ /** Show type information in output */
20
+ showTypes?: boolean
21
+ /** Enable memory optimization for large files */
22
+ useMemoryOptimization?: boolean
23
+ /** Batch size for memory optimization */
24
+ batchSize?: number
25
+ /** Ignore whitespace differences */
26
+ ignoreWhitespace?: boolean
27
+ /** Ignore case differences */
28
+ ignoreCase?: boolean
29
+ /** Report only whether files differ */
30
+ briefMode?: boolean
31
+ /** Suppress normal output; return only exit status */
32
+ quietMode?: boolean
33
+ }
34
+ export interface JsDiffResult {
35
+ /** Type of difference ('Added', 'Removed', 'Modified', 'TypeChanged') */
36
+ diffType: string
37
+ /** Path to the changed element */
38
+ path: string
39
+ /** Old value (for Modified/TypeChanged) */
40
+ oldValue?: any
41
+ /** New value (for Modified/TypeChanged/Added) */
42
+ newValue?: any
43
+ /** Value (for Removed) */
44
+ value?: any
45
+ }
46
+ /**
47
+ * Unified diff function for JavaScript/Node.js
48
+ *
49
+ * Compare two JavaScript objects or values and return differences.
50
+ *
51
+ * # Arguments
52
+ *
53
+ * * `old` - The old value (JavaScript object, array, or primitive)
54
+ * * `new` - The new value (JavaScript object, array, or primitive)
55
+ * * `options` - Optional configuration object
56
+ *
57
+ * # Returns
58
+ *
59
+ * Array of difference objects
60
+ *
61
+ * # Example
62
+ *
63
+ * ```javascript
64
+ * const { diff } = require('diffx-js');
65
+ *
66
+ * const old = { a: 1, b: 2 };
67
+ * const new = { a: 1, b: 3 };
68
+ * const result = diff(old, new);
69
+ * console.log(result); // [{ type: 'Modified', path: 'b', oldValue: 2, newValue: 3 }]
70
+ * ```
71
+ */
72
+ export declare function diff(old: any, new: any, options?: JsDiffOptions | undefined | null): Array<JsDiffResult>
73
+ /**
74
+ * Parse JSON string to JavaScript object
75
+ *
76
+ * # Arguments
77
+ *
78
+ * * `content` - JSON string to parse
79
+ *
80
+ * # Returns
81
+ *
82
+ * Parsed JavaScript object
83
+ */
84
+ export declare function parseJson(content: string): any
85
+ /**
86
+ * Parse CSV string to JavaScript array of objects
87
+ *
88
+ * # Arguments
89
+ *
90
+ * * `content` - CSV string to parse
91
+ *
92
+ * # Returns
93
+ *
94
+ * Array of JavaScript objects representing CSV rows
95
+ */
96
+ export declare function parseCsv(content: string): any
97
+ /**
98
+ * Parse YAML string to JavaScript object
99
+ *
100
+ * # Arguments
101
+ *
102
+ * * `content` - YAML string to parse
103
+ *
104
+ * # Returns
105
+ *
106
+ * Parsed JavaScript object
107
+ */
108
+ export declare function parseYaml(content: string): any
109
+ /**
110
+ * Parse TOML string to JavaScript object
111
+ *
112
+ * # Arguments
113
+ *
114
+ * * `content` - TOML string to parse
115
+ *
116
+ * # Returns
117
+ *
118
+ * Parsed JavaScript object
119
+ */
120
+ export declare function parseToml(content: string): any
121
+ /**
122
+ * Parse INI string to JavaScript object
123
+ *
124
+ * # Arguments
125
+ *
126
+ * * `content` - INI string to parse
127
+ *
128
+ * # Returns
129
+ *
130
+ * Parsed JavaScript object
131
+ */
132
+ export declare function parseIni(content: string): any
133
+ /**
134
+ * Parse XML string to JavaScript object
135
+ *
136
+ * # Arguments
137
+ *
138
+ * * `content` - XML string to parse
139
+ *
140
+ * # Returns
141
+ *
142
+ * Parsed JavaScript object
143
+ */
144
+ export declare function parseXml(content: string): any
145
+ /**
146
+ * Format diff results as string
147
+ *
148
+ * # Arguments
149
+ *
150
+ * * `results` - Array of diff results
151
+ * * `format` - Output format ("diffx", "json", "yaml")
152
+ *
153
+ * # Returns
154
+ *
155
+ * Formatted string output
156
+ */
157
+ export declare function formatOutput(results: Array<JsDiffResult>, format: string): string