+
+
Data Format Converter
+
+ Convert between JSON, XML, and CSV formats seamlessly.
+
+
+
+ {/* Format Selection */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Conversion Area */}
+
+ {/* Input Section */}
+
+
+
+
+ Input ({formatLabels[sourceFormat]})
+
+
+
+
+
+
+
+
+ {/* Output Section */}
+
+
+
+
+ Output ({formatLabels[targetFormat]})
+ {isConverting && (
+
+ )}
+
+
+
+
+
+
+
+ {error ? (
+
+ ) : (
+
+ )}
+
+
+
+ {/* Info Section */}
+
+
Conversion Tips
+
+ -
+ •
+ JSON to CSV: Works best with arrays of objects or single objects. Nested objects will be flattened.
+
+ -
+ •
+ CSV to JSON: First row is treated as headers. Numbers are automatically detected and typed.
+
+ -
+ •
+ XML conversions: XML attributes are merged with element content. Arrays are detected automatically.
+
+ -
+ •
+ Auto-conversion: Output updates automatically as you type or change formats.
+
+
+
+
+ );
+}
diff --git a/package-lock.json b/package-lock.json
index 3cb372c..1b910e0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -932,6 +932,16 @@
"undici-types": "~7.16.0"
}
},
+ "node_modules/@types/papaparse": {
+ "version": "5.5.2",
+ "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.5.2.tgz",
+ "integrity": "sha512-gFnFp/JMzLHCwRf7tQHrNnfhN4eYBVYYI897CGX4MY1tzY9l2aLkVyx2IlKZ/SAqDbB3I1AOZW5gTMGGsqWliA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
"node_modules/@types/prettier": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-3.0.0.tgz",
@@ -1568,6 +1578,24 @@
"node": ">= 6"
}
},
+ "node_modules/fast-xml-parser": {
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.3.tgz",
+ "integrity": "sha512-2O3dkPAAC6JavuMm8+4+pgTk+5hoAs+CjZ+sWcQLkX9+/tHRuTkQh/Oaifr8qDmZ8iEHb771Ea6G8CdwkrgvYA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "strnum": "^2.1.0"
+ },
+ "bin": {
+ "fxparser": "src/cli/cli.js"
+ }
+ },
"node_modules/fastq": {
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
@@ -2023,6 +2051,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/papaparse": {
+ "version": "5.5.3",
+ "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.5.3.tgz",
+ "integrity": "sha512-5QvjGxYVjxO59MGU2lHVYpRWBBtKHnlIAcSe1uNFCkkptUh63NFRj0FJQm7nR67puEruUci/ZkjmEFrjCAyP4A==",
+ "license": "MIT"
+ },
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
@@ -2562,6 +2596,18 @@
"url": "https://github.com/sponsors/antfu"
}
},
+ "node_modules/strnum": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.2.tgz",
+ "integrity": "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT"
+ },
"node_modules/sucrase": {
"version": "3.35.1",
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz",
@@ -3036,10 +3082,13 @@
"version": "0.0.1",
"dependencies": {
"diff-match-patch": "^1.0.5",
+ "fast-xml-parser": "^5.3.3",
+ "papaparse": "^5.5.3",
"prettier": "^3.0.0"
},
"devDependencies": {
"@types/diff-match-patch": "^1.0.36",
+ "@types/papaparse": "^5.5.2",
"@types/prettier": "^3.0.0",
"typescript": "^5.0.0",
"vitest": "^0.34.0"
diff --git a/packages/core/README.md b/packages/core/README.md
new file mode 100644
index 0000000..200e9e7
--- /dev/null
+++ b/packages/core/README.md
@@ -0,0 +1,23 @@
+# @dev-assistant/core
+
+Shared core logic and utilities for the Dev Assistant project.
+
+## Features
+
+- **Text Diffing**: Wrapper around `diff-match-patch`.
+- **Formatting**: Shared Prettier configuration and formatting logic.
+
+## Scripts
+
+- `npm run build`: Compiles TypeScript to JavaScript.
+- `npm run test`: Runs unit tests using Vitest.
+
+## Usage
+
+This package is intended to be used as a workspace dependency in other apps or packages within the monorepo.
+
+```json
+"dependencies": {
+ "@dev-assistant/core": "*"
+}
+```
diff --git a/packages/core/dist/data-converter.d.ts b/packages/core/dist/data-converter.d.ts
new file mode 100644
index 0000000..2418042
--- /dev/null
+++ b/packages/core/dist/data-converter.d.ts
@@ -0,0 +1,41 @@
+export interface ConversionResult {
+ success: boolean;
+ data?: string;
+ error?: string;
+}
+/**
+ * Convert JSON string to XML string
+ */
+export declare function jsonToXml(jsonString: string): ConversionResult;
+/**
+ * Convert XML string to JSON string
+ */
+export declare function xmlToJson(xmlString: string): ConversionResult;
+/**
+ * Convert JSON string to CSV string
+ */
+export declare function jsonToCsv(jsonString: string): ConversionResult;
+/**
+ * Convert CSV string to JSON string
+ */
+export declare function csvToJson(csvString: string): ConversionResult;
+/**
+ * Convert XML string to CSV string (via JSON intermediate)
+ */
+export declare function xmlToCsv(xmlString: string): ConversionResult;
+/**
+ * Convert CSV string to XML string (via JSON intermediate)
+ */
+export declare function csvToXml(csvString: string): ConversionResult;
+/**
+ * Validate if string is valid JSON
+ */
+export declare function isValidJson(str: string): boolean;
+/**
+ * Validate if string is valid XML
+ */
+export declare function isValidXml(str: string): boolean;
+/**
+ * Validate if string is valid CSV
+ */
+export declare function isValidCsv(str: string): boolean;
diff --git a/packages/core/dist/data-converter.js b/packages/core/dist/data-converter.js
new file mode 100644
index 0000000..5bd2a41
--- /dev/null
+++ b/packages/core/dist/data-converter.js
@@ -0,0 +1,190 @@
+import { XMLParser, XMLBuilder } from 'fast-xml-parser';
+import Papa from 'papaparse';
+/**
+ * Convert JSON string to XML string
+ */
+export function jsonToXml(jsonString) {
+ try {
+ const jsonData = JSON.parse(jsonString);
+ const builder = new XMLBuilder({
+ format: true,
+ indentBy: ' ',
+ ignoreAttributes: false,
+ suppressEmptyNode: true
+ });
+ // Wrap in root element if not already wrapped
+ const wrappedData = jsonData.root ? jsonData : { root: jsonData };
+ const xml = builder.build(wrappedData);
+ return { success: true, data: xml };
+ }
+ catch (error) {
+ return {
+ success: false,
+ error: error instanceof Error ? error.message : 'Failed to convert JSON to XML'
+ };
+ }
+}
+/**
+ * Convert XML string to JSON string
+ */
+export function xmlToJson(xmlString) {
+ try {
+ const parser = new XMLParser({
+ ignoreAttributes: false,
+ attributeNamePrefix: '',
+ textNodeName: '_text',
+ parseAttributeValue: true,
+ parseTagValue: true,
+ trimValues: true
+ });
+ const result = parser.parse(xmlString);
+ return {
+ success: true,
+ data: JSON.stringify(result, null, 2)
+ };
+ }
+ catch (error) {
+ return {
+ success: false,
+ error: error instanceof Error ? error.message : 'Failed to convert XML to JSON'
+ };
+ }
+}
+/**
+ * Convert JSON string to CSV string
+ */
+export function jsonToCsv(jsonString) {
+ try {
+ const jsonData = JSON.parse(jsonString);
+ // Handle array of objects
+ if (Array.isArray(jsonData)) {
+ if (jsonData.length === 0) {
+ return { success: false, error: 'Empty array cannot be converted to CSV' };
+ }
+ const csv = Papa.unparse(jsonData, {
+ header: true,
+ skipEmptyLines: true
+ });
+ return { success: true, data: csv };
+ }
+ // Handle single object - convert to array with one element
+ if (typeof jsonData === 'object' && jsonData !== null) {
+ const csv = Papa.unparse([jsonData], {
+ header: true,
+ skipEmptyLines: true
+ });
+ return { success: true, data: csv };
+ }
+ return {
+ success: false,
+ error: 'JSON must be an object or array of objects for CSV conversion'
+ };
+ }
+ catch (error) {
+ return {
+ success: false,
+ error: error instanceof Error ? error.message : 'Failed to convert JSON to CSV'
+ };
+ }
+}
+/**
+ * Convert CSV string to JSON string
+ */
+export function csvToJson(csvString) {
+ try {
+ const result = Papa.parse(csvString, {
+ header: true,
+ skipEmptyLines: true,
+ dynamicTyping: true
+ });
+ if (result.errors.length > 0) {
+ return {
+ success: false,
+ error: result.errors[0].message || 'Failed to parse CSV'
+ };
+ }
+ return {
+ success: true,
+ data: JSON.stringify(result.data, null, 2)
+ };
+ }
+ catch (error) {
+ return {
+ success: false,
+ error: error instanceof Error ? error.message : 'Failed to convert CSV to JSON'
+ };
+ }
+}
+/**
+ * Convert XML string to CSV string (via JSON intermediate)
+ */
+export function xmlToCsv(xmlString) {
+ try {
+ const jsonResult = xmlToJson(xmlString);
+ if (!jsonResult.success || !jsonResult.data) {
+ return jsonResult;
+ }
+ return jsonToCsv(jsonResult.data);
+ }
+ catch (error) {
+ return {
+ success: false,
+ error: error instanceof Error ? error.message : 'Failed to convert XML to CSV'
+ };
+ }
+}
+/**
+ * Convert CSV string to XML string (via JSON intermediate)
+ */
+export function csvToXml(csvString) {
+ try {
+ const jsonResult = csvToJson(csvString);
+ if (!jsonResult.success || !jsonResult.data) {
+ return jsonResult;
+ }
+ return jsonToXml(jsonResult.data);
+ }
+ catch (error) {
+ return {
+ success: false,
+ error: error instanceof Error ? error.message : 'Failed to convert CSV to XML'
+ };
+ }
+}
+/**
+ * Validate if string is valid JSON
+ */
+export function isValidJson(str) {
+ try {
+ JSON.parse(str);
+ return true;
+ }
+ catch {
+ return false;
+ }
+}
+/**
+ * Validate if string is valid XML
+ */
+export function isValidXml(str) {
+ try {
+ const parser = new XMLParser();
+ parser.parse(str);
+ return true;
+ }
+ catch {
+ return false;
+ }
+}
+/**
+ * Validate if string is valid CSV
+ */
+export function isValidCsv(str) {
+ try {
+ const result = Papa.parse(str, { header: true });
+ return result.errors.length === 0 && result.data.length > 0;
+ }
+ catch {
+ return false;
+ }
+}
diff --git a/packages/core/dist/data-converter.test.d.ts b/packages/core/dist/data-converter.test.d.ts
new file mode 100644
index 0000000..cb0ff5c
--- /dev/null
+++ b/packages/core/dist/data-converter.test.d.ts
@@ -0,0 +1 @@
+export {};
diff --git a/packages/core/dist/data-converter.test.js b/packages/core/dist/data-converter.test.js
new file mode 100644
index 0000000..e4aa574
--- /dev/null
+++ b/packages/core/dist/data-converter.test.js
@@ -0,0 +1,75 @@
+import { describe, it, expect } from 'vitest';
+import { jsonToXml, xmlToJson, jsonToCsv, csvToJson, isValidJson, isValidXml, isValidCsv } from './data-converter';
+describe('Data Converter', () => {
+ describe('JSON to XML', () => {
+ it('should convert simple JSON object to XML', () => {
+ const json = '{"name":"John","age":30}';
+ const result = jsonToXml(json);
+ expect(result.success).toBe(true);
+ expect(result.data).toContain('