toonify-n8n-node 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.
package/PLAN.md ADDED
@@ -0,0 +1,150 @@
1
+ # n8n TOON Converter Node - Implementation Plan
2
+
3
+ ## Overview
4
+ Create a custom n8n node that converts incoming JSON items to TOON (Token-Oriented Object Notation) format to reduce token usage when sending data to LLMs. Each item is converted separately with metadata about token savings.
5
+
6
+ ## Project Structure
7
+ ```
8
+ toon/
9
+ ├── package.json
10
+ ├── tsconfig.json
11
+ ├── nodes/
12
+ │ └── ToToon/
13
+ │ ├── ToToon.node.ts # Main node implementation
14
+ │ └── toon.svg # Node icon
15
+ └── dist/ # Build output
16
+ ```
17
+
18
+ ## Files to Create
19
+
20
+ ### 1. `package.json`
21
+ ```json
22
+ {
23
+ "name": "n8n-nodes-toon",
24
+ "version": "1.0.0",
25
+ "description": "n8n node to convert JSON to TOON format for reduced LLM token usage",
26
+ "license": "MIT",
27
+ "author": { "name": "Diego" },
28
+ "n8n": {
29
+ "n8nNodesApiVersion": 1,
30
+ "nodes": ["dist/nodes/ToToon/ToToon.node.js"]
31
+ },
32
+ "scripts": {
33
+ "build": "tsc",
34
+ "dev": "tsc --watch",
35
+ "lint": "eslint . --ext .ts"
36
+ },
37
+ "dependencies": {
38
+ "@toon-format/toon": "^3.0.0"
39
+ },
40
+ "devDependencies": {
41
+ "@types/node": "^20.0.0",
42
+ "n8n-workflow": "^1.0.0",
43
+ "typescript": "^5.0.0"
44
+ }
45
+ }
46
+ ```
47
+
48
+ ### 2. `tsconfig.json`
49
+ ```json
50
+ {
51
+ "compilerOptions": {
52
+ "target": "ES2019",
53
+ "module": "CommonJS",
54
+ "strict": true,
55
+ "esModuleInterop": true,
56
+ "outDir": "./dist",
57
+ "rootDir": ".",
58
+ "declaration": true
59
+ },
60
+ "include": ["nodes/**/*.ts"],
61
+ "exclude": ["node_modules", "dist"]
62
+ }
63
+ ```
64
+
65
+ ### 3. `nodes/ToToon/ToToon.node.ts`
66
+ ```typescript
67
+ import {
68
+ IExecuteFunctions,
69
+ INodeExecutionData,
70
+ INodeType,
71
+ INodeTypeDescription,
72
+ } from 'n8n-workflow';
73
+ import { encode } from '@toon-format/toon';
74
+
75
+ export class ToToon implements INodeType {
76
+ description: INodeTypeDescription = {
77
+ displayName: 'To TOON',
78
+ name: 'toToon',
79
+ icon: 'file:toon.svg',
80
+ group: ['transform'],
81
+ version: 1,
82
+ subtitle: 'Convert to TOON format',
83
+ description: 'Convert JSON items to TOON format for reduced LLM token usage',
84
+ defaults: { name: 'To TOON' },
85
+ inputs: ['main'],
86
+ outputs: ['main'],
87
+ properties: [
88
+ {
89
+ displayName: 'Output Field',
90
+ name: 'outputField',
91
+ type: 'string',
92
+ default: 'toon',
93
+ description: 'Field name to store the TOON output',
94
+ },
95
+ ],
96
+ };
97
+
98
+ async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
99
+ const items = this.getInputData();
100
+ const outputField = this.getNodeParameter('outputField', 0) as string;
101
+ const results: INodeExecutionData[] = [];
102
+
103
+ for (let i = 0; i < items.length; i++) {
104
+ const item = items[i];
105
+ const jsonData = item.json;
106
+
107
+ // Calculate original JSON size
108
+ const originalJson = JSON.stringify(jsonData);
109
+ const originalSize = originalJson.length;
110
+
111
+ // Convert to TOON
112
+ const toonOutput = encode(jsonData);
113
+ const toonSize = toonOutput.length;
114
+
115
+ // Calculate savings
116
+ const savedBytes = originalSize - toonSize;
117
+ const savingsPercent = ((savedBytes / originalSize) * 100).toFixed(1);
118
+
119
+ results.push({
120
+ json: {
121
+ [outputField]: toonOutput,
122
+ _meta: {
123
+ originalSizeBytes: originalSize,
124
+ toonSizeBytes: toonSize,
125
+ savedBytes,
126
+ savingsPercent: `${savingsPercent}%`,
127
+ },
128
+ },
129
+ });
130
+ }
131
+
132
+ return [results];
133
+ }
134
+ }
135
+ ```
136
+
137
+ ### 4. `nodes/ToToon/toon.svg`
138
+ Simple icon (purple/blue gradient with "T" letter)
139
+
140
+ ## Implementation Steps
141
+ 1. Create all files above
142
+ 2. Run `npm install`
143
+ 3. Run `npm run build`
144
+ 4. Link to n8n or use `npm run dev`
145
+
146
+ ## Verification
147
+ 1. `npm install` - Install dependencies
148
+ 2. `npm run build` - Compile TypeScript (should produce `dist/` folder)
149
+ 3. Link to local n8n: `npm link` then in n8n folder `npm link n8n-nodes-toon`
150
+ 4. Test workflow: Manual Trigger → Set (sample JSON) → To TOON → Check output has `toon` field + `_meta` with savings info
@@ -0,0 +1,5 @@
1
+ import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
+ export declare class ToToon implements INodeType {
3
+ description: INodeTypeDescription;
4
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
5
+ }
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ToToon = void 0;
4
+ const toon_1 = require("@toon-format/toon");
5
+ class ToToon {
6
+ constructor() {
7
+ this.description = {
8
+ displayName: 'To TOON',
9
+ name: 'toToon',
10
+ icon: 'file:toon.svg',
11
+ group: ['transform'],
12
+ version: 1,
13
+ subtitle: 'Convert to TOON format',
14
+ description: 'Convert JSON items to TOON format for reduced LLM token usage',
15
+ defaults: { name: 'To TOON' },
16
+ inputs: ['main'],
17
+ outputs: ['main'],
18
+ properties: [
19
+ {
20
+ displayName: 'Output Field',
21
+ name: 'outputField',
22
+ type: 'string',
23
+ default: 'toon',
24
+ description: 'Field name to store the TOON output',
25
+ },
26
+ ],
27
+ };
28
+ }
29
+ async execute() {
30
+ const items = this.getInputData();
31
+ const outputField = this.getNodeParameter('outputField', 0);
32
+ const results = [];
33
+ for (let i = 0; i < items.length; i++) {
34
+ const item = items[i];
35
+ const jsonData = item.json;
36
+ // Calculate original JSON size
37
+ const originalJson = JSON.stringify(jsonData);
38
+ const originalSize = originalJson.length;
39
+ // Convert to TOON
40
+ const toonOutput = (0, toon_1.encode)(jsonData);
41
+ const toonSize = toonOutput.length;
42
+ // Calculate savings
43
+ const savedBytes = originalSize - toonSize;
44
+ const savingsPercent = ((savedBytes / originalSize) * 100).toFixed(1);
45
+ results.push({
46
+ json: {
47
+ [outputField]: toonOutput,
48
+ _meta: {
49
+ originalSizeBytes: originalSize,
50
+ toonSizeBytes: toonSize,
51
+ savedBytes,
52
+ savingsPercent: `${savingsPercent}%`,
53
+ },
54
+ },
55
+ });
56
+ }
57
+ return [results];
58
+ }
59
+ }
60
+ exports.ToToon = ToToon;
@@ -0,0 +1,68 @@
1
+ import {
2
+ IExecuteFunctions,
3
+ INodeExecutionData,
4
+ INodeType,
5
+ INodeTypeDescription,
6
+ } from 'n8n-workflow';
7
+ import { encode } from '@toon-format/toon';
8
+
9
+ export class ToToon implements INodeType {
10
+ description: INodeTypeDescription = {
11
+ displayName: 'To TOON',
12
+ name: 'toToon',
13
+ icon: 'file:toon.svg',
14
+ group: ['transform'],
15
+ version: 1,
16
+ subtitle: 'Convert to TOON format',
17
+ description: 'Convert JSON items to TOON format for reduced LLM token usage',
18
+ defaults: { name: 'To TOON' },
19
+ inputs: ['main'],
20
+ outputs: ['main'],
21
+ properties: [
22
+ {
23
+ displayName: 'Output Field',
24
+ name: 'outputField',
25
+ type: 'string',
26
+ default: 'toon',
27
+ description: 'Field name to store the TOON output',
28
+ },
29
+ ],
30
+ };
31
+
32
+ async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
33
+ const items = this.getInputData();
34
+ const outputField = this.getNodeParameter('outputField', 0) as string;
35
+ const results: INodeExecutionData[] = [];
36
+
37
+ for (let i = 0; i < items.length; i++) {
38
+ const item = items[i];
39
+ const jsonData = item.json;
40
+
41
+ // Calculate original JSON size
42
+ const originalJson = JSON.stringify(jsonData);
43
+ const originalSize = originalJson.length;
44
+
45
+ // Convert to TOON
46
+ const toonOutput = encode(jsonData);
47
+ const toonSize = toonOutput.length;
48
+
49
+ // Calculate savings
50
+ const savedBytes = originalSize - toonSize;
51
+ const savingsPercent = ((savedBytes / originalSize) * 100).toFixed(1);
52
+
53
+ results.push({
54
+ json: {
55
+ [outputField]: toonOutput,
56
+ _meta: {
57
+ originalSizeBytes: originalSize,
58
+ toonSizeBytes: toonSize,
59
+ savedBytes,
60
+ savingsPercent: `${savingsPercent}%`,
61
+ },
62
+ },
63
+ });
64
+ }
65
+
66
+ return [results];
67
+ }
68
+ }
@@ -0,0 +1,10 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
2
+ <defs>
3
+ <linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" style="stop-color:#8e2de2;stop-opacity:1" />
5
+ <stop offset="100%" style="stop-color:#4a00e0;stop-opacity:1" />
6
+ </linearGradient>
7
+ </defs>
8
+ <rect width="100" height="100" rx="20" fill="url(#grad1)" />
9
+ <text x="50" y="70" font-family="Arial, sans-serif" font-size="60" font-weight="bold" fill="white" text-anchor="middle">T</text>
10
+ </svg>
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "toonify-n8n-node",
3
+ "version": "1.0.0",
4
+ "description": "n8n node to convert JSON to TOON format for reduced LLM token usage",
5
+ "license": "MIT",
6
+ "author": { "name": "Diego" },
7
+ "keywords": [
8
+ "n8n-community-node-package"
9
+ ],
10
+ "n8n": {
11
+ "n8nNodesApiVersion": 1,
12
+ "nodes": ["dist/nodes/ToToon/ToToon.node.js"]
13
+ },
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "dev": "tsc --watch",
17
+ "lint": "eslint . --ext .ts"
18
+ },
19
+ "dependencies": {
20
+ "@toon-format/toon": "^2.1.0"
21
+ },
22
+ "devDependencies": {
23
+ "@types/node": "^20.0.0",
24
+ "n8n-workflow": "^1.0.0",
25
+ "typescript": "^5.0.0"
26
+ },
27
+ "overrides": {
28
+ "form-data": "^4.0.5",
29
+ "lodash": "^4.17.23"
30
+ }
31
+ }
@@ -0,0 +1,55 @@
1
+ import { ToToon } from '../nodes/ToToon/ToToon.node';
2
+ import { IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
3
+
4
+ // Mock the execution context
5
+ const mockExecuteFunctions = {
6
+ getInputData: () => {
7
+ return [
8
+ {
9
+ json: {
10
+ id: 1,
11
+ name: "Alice",
12
+ roles: ["admin", "editor"]
13
+ }
14
+ },
15
+ {
16
+ json: {
17
+ id: 2,
18
+ name: "Bob",
19
+ roles: ["viewer"]
20
+ }
21
+ }
22
+ ] as INodeExecutionData[];
23
+ },
24
+ getNodeParameter: (parameterName: string) => {
25
+ if (parameterName === 'outputField') return 'myToonField';
26
+ return '';
27
+ }
28
+ } as unknown as IExecuteFunctions;
29
+
30
+ async function runTest() {
31
+ console.log("Starting ToToon Node Test...\n");
32
+
33
+ const node = new ToToon();
34
+
35
+ // Bind the mock context to the execute function
36
+ const execute = node.execute.bind(mockExecuteFunctions);
37
+
38
+ try {
39
+ const results = await execute();
40
+ const outputItems = results[0];
41
+
42
+ outputItems.forEach((item, index) => {
43
+ console.log(`--- Item ${index + 1} ---`);
44
+ console.log("Original JSON:", JSON.stringify(mockExecuteFunctions.getInputData()[index].json));
45
+ console.log("Result:", JSON.stringify(item.json, null, 2));
46
+ console.log("------------------\n");
47
+ });
48
+
49
+ console.log("Test execution successful!");
50
+ } catch (error) {
51
+ console.error("Test failed:", error);
52
+ }
53
+ }
54
+
55
+ runTest();
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2019",
4
+ "module": "CommonJS",
5
+ "strict": true,
6
+ "skipLibCheck": true,
7
+ "esModuleInterop": true,
8
+ "outDir": "./dist",
9
+ "rootDir": ".",
10
+ "declaration": true
11
+ },
12
+ "include": ["nodes/**/*.ts"],
13
+ "exclude": ["node_modules", "dist"]
14
+ }