llm-chat-msg-compressor 1.0.0 → 1.0.1

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025
3
+ Copyright (c) 2025 Sridharvn
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # llm-chat-msg-compressor 🚀
2
2
 
3
+ [![NPM Version](https://img.shields.io/npm/v/llm-chat-msg-compressor.svg)](https://www.npmjs.com/package/llm-chat-msg-compressor)
4
+ [![License](https://img.shields.io/npm/l/llm-chat-msg-compressor.svg)](https://github.com/Sridharvn/llm-chat-msg-compressor/blob/main/LICENSE)
5
+ [![Build Status](https://github.com/Sridharvn/llm-chat-msg-compressor/actions/workflows/test.yml/badge.svg)](https://github.com/Sridharvn/llm-chat-msg-compressor/actions)
6
+
3
7
  Intelligent JSON optimizer for LLM APIs. Automatically reduces token usage by selecting the best compression strategy for your data payload.
4
8
 
5
9
  ## Features
@@ -66,3 +70,11 @@ optimize(data, {
66
70
  By default, the library is **Safe-by-Default**. It preserves all data types (including booleans), ensuring that downstream code (e.g., in your backend or strictly typed clients) works without modification.
67
71
 
68
72
  If you need maximum compression and your LLM can handle `1`/`0` instead of `true`/`false`, you can enable `unsafe: true`.
73
+
74
+ ## Contributing
75
+
76
+ Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) for our code of conduct.
77
+
78
+ ## License
79
+
80
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
package/dist/analyzer.js CHANGED
@@ -3,8 +3,19 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Analyzer = void 0;
4
4
  class Analyzer {
5
5
  static analyze(data) {
6
- const json = JSON.stringify(data);
7
- const totalBytes = Buffer.byteLength(json, 'utf8');
6
+ // Pre-flight check for primitives or very small objects
7
+ if (data === null || typeof data !== 'object') {
8
+ return {
9
+ totalBytes: data === undefined ? 0 : Buffer.byteLength(JSON.stringify(data), 'utf8'),
10
+ arrayDensity: 0,
11
+ maxExampleArrayLength: 0,
12
+ nestingDepth: 0,
13
+ repeatedKeysEstimate: 0,
14
+ estimatedAbbrevSavings: 0,
15
+ estimatedSchemaSavings: 0
16
+ };
17
+ }
18
+ let totalBytes = 0;
8
19
  let arrayCount = 0;
9
20
  let objectCount = 0;
10
21
  let maxArrLen = 0;
@@ -18,17 +29,40 @@ class Analyzer {
18
29
  if (arr.length < 3 || typeof arr[0] !== 'object' || arr[0] === null)
19
30
  return 0;
20
31
  // Check consistency of first few items
21
- const keys = Object.keys(arr[0]);
22
- const keyStr = keys.sort().join(',');
23
- const sample = arr.slice(0, 5); // Check first 5 for speed
24
- const isConsistent = sample.every(item => item && typeof item === 'object' && !Array.isArray(item) &&
25
- Object.keys(item).sort().join(',') === keyStr);
32
+ const firstItem = arr[0];
33
+ const keys = Object.keys(firstItem);
34
+ const keyCount = keys.length;
35
+ if (keyCount === 0)
36
+ return 0;
37
+ const sampleSize = Math.min(arr.length, 5);
38
+ let isConsistent = true;
39
+ for (let i = 1; i < sampleSize; i++) {
40
+ const item = arr[i];
41
+ if (!item || typeof item !== 'object' || Array.isArray(item)) {
42
+ isConsistent = false;
43
+ break;
44
+ }
45
+ const itemKeys = Object.keys(item);
46
+ if (itemKeys.length !== keyCount) {
47
+ isConsistent = false;
48
+ break;
49
+ }
50
+ // Check if all keys from first item exist in this item
51
+ for (const key of keys) {
52
+ if (!(key in item)) {
53
+ isConsistent = false;
54
+ break;
55
+ }
56
+ }
57
+ if (!isConsistent)
58
+ break;
59
+ }
26
60
  if (isConsistent) {
27
- // Savings = (Items - 1) * (Sum of key lengths + overhead)
28
- // Roughly: For N items, we write keys once instead of N times.
29
- // Savings ~= (N - 1) * (total_key_chars + (keys.length * 3 chars for quotes/colon))
30
- const keysLen = keys.reduce((sum, k) => sum + k.length, 0);
31
- const perItemOverhead = keysLen + (keys.length * 2); // quotes + colon approx
61
+ let keysLen = 0;
62
+ for (const key of keys) {
63
+ keysLen += key.length;
64
+ }
65
+ const perItemOverhead = keysLen + (keyCount * 2); // quotes + colon approx
32
66
  return (arr.length - 1) * perItemOverhead;
33
67
  }
34
68
  return 0;
@@ -38,16 +72,42 @@ class Analyzer {
38
72
  if (Array.isArray(obj)) {
39
73
  arrayCount++;
40
74
  maxArrLen = Math.max(maxArrLen, obj.length);
75
+ totalBytes += 2; // []
76
+ if (obj.length > 1)
77
+ totalBytes += obj.length - 1; // commas
41
78
  // Check if this specific array offers schema savings
42
79
  schemaSavings += calculateArraySchemaSavings(obj);
43
- obj.forEach(i => traverse(i, currentDepth + 1));
80
+ for (let i = 0; i < obj.length; i++) {
81
+ traverse(obj[i], currentDepth + 1);
82
+ }
44
83
  }
45
84
  else if (obj && typeof obj === 'object') {
46
85
  objectCount++;
47
- const keys = Object.keys(obj);
48
- totalKeysCount += keys.length;
49
- totalKeyLength += keys.reduce((sum, k) => sum + k.length, 0);
50
- Object.values(obj).forEach(v => traverse(v, currentDepth + 1));
86
+ totalBytes += 2; // {}
87
+ let first = true;
88
+ for (const key in obj) {
89
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
90
+ if (!first)
91
+ totalBytes += 1; // comma
92
+ first = false;
93
+ totalKeysCount++;
94
+ totalKeyLength += key.length;
95
+ totalBytes += key.length + 3; // "key":
96
+ traverse(obj[key], currentDepth + 1);
97
+ }
98
+ }
99
+ }
100
+ else {
101
+ // Primitive
102
+ if (typeof obj === 'string') {
103
+ totalBytes += Buffer.byteLength(obj, 'utf8') + 2; // quotes
104
+ }
105
+ else if (typeof obj === 'number' || typeof obj === 'boolean') {
106
+ totalBytes += String(obj).length;
107
+ }
108
+ else if (obj === null) {
109
+ totalBytes += 4;
110
+ }
51
111
  }
52
112
  };
53
113
  traverse(data, 0);
package/dist/index.js CHANGED
@@ -33,18 +33,32 @@ function restore(data) {
33
33
  return strat.decompress(data);
34
34
  }
35
35
  // Detect Schema Separation format anywhere in the structure
36
- // We use a string check for existence of keys to decide if we should traverse.
37
- if (JSON.stringify(data).includes('"$s"') && JSON.stringify(data).includes('"$d"')) {
36
+ if (hasSchemaMarker(data)) {
38
37
  const strat = new strategies_1.SchemaDataSeparationStrategy();
39
38
  return strat.decompress(data);
40
39
  }
41
40
  // Default: return as is
42
41
  return data;
43
42
  }
44
- function checkNestedSchema(obj) {
45
- // Simple deep check (expensive, but safe for examples)
46
- // In prod, rely on known structure
47
- return JSON.stringify(obj).includes('"$s":') && JSON.stringify(obj).includes('"$d":');
43
+ function hasSchemaMarker(obj) {
44
+ if (!obj || typeof obj !== 'object')
45
+ return false;
46
+ if (Array.isArray(obj)) {
47
+ for (let i = 0; i < obj.length; i++) {
48
+ if (hasSchemaMarker(obj[i]))
49
+ return true;
50
+ }
51
+ return false;
52
+ }
53
+ if ('$s' in obj && '$d' in obj)
54
+ return true;
55
+ for (const k in obj) {
56
+ if (Object.prototype.hasOwnProperty.call(obj, k)) {
57
+ if (hasSchemaMarker(obj[k]))
58
+ return true;
59
+ }
60
+ }
61
+ return false;
48
62
  }
49
63
  var optimizer_2 = require("./optimizer");
50
64
  Object.defineProperty(exports, "Optimizer", { enumerable: true, get: function () { return optimizer_2.Optimizer; } });
@@ -5,6 +5,10 @@ export interface OptimizerOptions {
5
5
  unsafe?: boolean;
6
6
  }
7
7
  export declare class Optimizer {
8
+ private schemaStrat;
9
+ private abbrevStrat;
10
+ private ultraStratSafe;
11
+ private ultraStratUnsafe;
8
12
  private strategies;
9
13
  /**
10
14
  * Automatically selects and applies the best compression strategy
package/dist/optimizer.js CHANGED
@@ -5,10 +5,14 @@ const strategies_1 = require("./strategies");
5
5
  const analyzer_1 = require("./analyzer");
6
6
  class Optimizer {
7
7
  constructor() {
8
+ this.schemaStrat = new strategies_1.SchemaDataSeparationStrategy();
9
+ this.abbrevStrat = new strategies_1.AbbreviatedKeysStrategy();
10
+ this.ultraStratSafe = new strategies_1.UltraCompactStrategy({ unsafe: false });
11
+ this.ultraStratUnsafe = new strategies_1.UltraCompactStrategy({ unsafe: true });
8
12
  this.strategies = [
9
- new strategies_1.SchemaDataSeparationStrategy(),
10
- new strategies_1.UltraCompactStrategy(),
11
- new strategies_1.AbbreviatedKeysStrategy()
13
+ this.schemaStrat,
14
+ this.ultraStratSafe,
15
+ this.abbrevStrat
12
16
  ];
13
17
  }
14
18
  /**
@@ -20,31 +24,23 @@ class Optimizer {
20
24
  const metrics = analyzer_1.Analyzer.analyze(data);
21
25
  // 1. If too small, just minify
22
26
  if (metrics.totalBytes < thresholdBytes) {
23
- console.log(`[Optimizer] Selected: minify (Size ${metrics.totalBytes} < ${thresholdBytes})`);
24
27
  return strategies_1.minify.compress(data);
25
28
  }
26
29
  // 2. Smart Strategy Selection
27
30
  // Compare estimated savings to pick the winner.
28
- console.log(`[Optimizer] Analysis: SchemaSavings=${Math.round(metrics.estimatedSchemaSavings)} bytes, AbbrevSavings=${Math.round(metrics.estimatedAbbrevSavings)} bytes`);
29
31
  // Prefer SchemaSeparation if it saves MORE than AbbreviatedKeys (with a slight buffer for safety)
30
32
  // Schema Separation is "riskier" structure-wise (arrays vs maps), so we want it to be worth it.
31
33
  if (metrics.estimatedSchemaSavings > metrics.estimatedAbbrevSavings * 1.1) {
32
- console.log('[Optimizer] Selected: schema-data-separation (Higher savings)');
33
- const schemaStrat = new strategies_1.SchemaDataSeparationStrategy();
34
- return schemaStrat.compress(data);
34
+ return this.schemaStrat.compress(data);
35
35
  }
36
36
  // 3. Fallback to UltraCompact if aggressive is set
37
37
  if (aggressive) {
38
- console.log('[Optimizer] Selected: ultra-compact');
39
- const ultra = new strategies_1.UltraCompactStrategy({ unsafe });
40
- return ultra.compress(data);
38
+ return unsafe ? this.ultraStratUnsafe.compress(data) : this.ultraStratSafe.compress(data);
41
39
  }
42
40
  // 4. Default: Abbreviated Keys
43
41
  // If Schema Separation isn't significantly better, we default to this.
44
42
  // It handles mixed/nested payloads better and is "safer" structure-wise.
45
- console.log('[Optimizer] Selected: abbreviated-keys');
46
- const abbr = new strategies_1.AbbreviatedKeysStrategy();
47
- return abbr.compress(data);
43
+ return this.abbrevStrat.compress(data);
48
44
  }
49
45
  /**
50
46
  * Helper to get a specific strategy
@@ -52,7 +48,13 @@ class Optimizer {
52
48
  getStrategy(name) {
53
49
  if (name === 'minify')
54
50
  return strategies_1.minify;
55
- return this.strategies.find(s => s.name === name);
51
+ if (name === 'schema-data-separation')
52
+ return this.schemaStrat;
53
+ if (name === 'abbreviated-keys')
54
+ return this.abbrevStrat;
55
+ if (name === 'ultra-compact')
56
+ return this.ultraStratSafe; // Default to safe
57
+ return undefined;
56
58
  }
57
59
  }
58
60
  exports.Optimizer = Optimizer;
@@ -6,6 +6,10 @@ export interface CompressionStrategy {
6
6
  compress(data: any): any;
7
7
  decompress(data: any): any;
8
8
  }
9
+ /**
10
+ * Shared utility for generating short keys (a, b, ... z, aa, ab ...)
11
+ */
12
+ export declare const generateShortKey: (index: number) => string;
9
13
  /**
10
14
  * Strategy 1: Minify (Baseline)
11
15
  * Just standard JSON serialization (handled by default JSON.stringify)
@@ -1,6 +1,19 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.UltraCompactStrategy = exports.SchemaDataSeparationStrategy = exports.AbbreviatedKeysStrategy = exports.minify = void 0;
3
+ exports.UltraCompactStrategy = exports.SchemaDataSeparationStrategy = exports.AbbreviatedKeysStrategy = exports.minify = exports.generateShortKey = void 0;
4
+ /**
5
+ * Shared utility for generating short keys (a, b, ... z, aa, ab ...)
6
+ */
7
+ const generateShortKey = (index) => {
8
+ let shortKey = '';
9
+ let temp = index;
10
+ do {
11
+ shortKey = String.fromCharCode(97 + (temp % 26)) + shortKey;
12
+ temp = Math.floor(temp / 26) - 1;
13
+ } while (temp >= 0);
14
+ return shortKey;
15
+ };
16
+ exports.generateShortKey = generateShortKey;
4
17
  /**
5
18
  * Strategy 1: Minify (Baseline)
6
19
  * Just standard JSON serialization (handled by default JSON.stringify)
@@ -22,30 +35,30 @@ class AbbreviatedKeysStrategy {
22
35
  this.name = 'abbreviated-keys';
23
36
  }
24
37
  compress(data) {
25
- // Implementation that returns { m: map, d: data }
26
38
  const keyMap = new Map();
27
- const reverseMap = new Map();
28
39
  let counter = 0;
29
40
  const getShortKey = (key) => {
30
- if (!keyMap.has(key)) {
31
- let shortKey = '';
32
- let temp = counter++;
33
- do {
34
- shortKey = String.fromCharCode(97 + (temp % 26)) + shortKey;
35
- temp = Math.floor(temp / 26) - 1;
36
- } while (temp >= 0);
41
+ let shortKey = keyMap.get(key);
42
+ if (shortKey === undefined) {
43
+ shortKey = (0, exports.generateShortKey)(counter++);
37
44
  keyMap.set(key, shortKey);
38
- reverseMap.set(shortKey, key);
39
45
  }
40
- return keyMap.get(key);
46
+ return shortKey;
41
47
  };
42
48
  const traverse = (obj) => {
43
- if (Array.isArray(obj))
44
- return obj.map(traverse);
49
+ if (Array.isArray(obj)) {
50
+ const newArr = new Array(obj.length);
51
+ for (let i = 0; i < obj.length; i++) {
52
+ newArr[i] = traverse(obj[i]);
53
+ }
54
+ return newArr;
55
+ }
45
56
  if (obj && typeof obj === 'object') {
46
57
  const newObj = {};
47
58
  for (const k in obj) {
48
- newObj[getShortKey(k)] = traverse(obj[k]);
59
+ if (Object.prototype.hasOwnProperty.call(obj, k)) {
60
+ newObj[getShortKey(k)] = traverse(obj[k]);
61
+ }
49
62
  }
50
63
  return newObj;
51
64
  }
@@ -61,17 +74,26 @@ class AbbreviatedKeysStrategy {
61
74
  if (!pkg || !pkg.m || pkg.d === undefined)
62
75
  return pkg;
63
76
  const reverseMap = new Map();
64
- for (const [k, v] of Object.entries(pkg.m)) {
65
- reverseMap.set(v, k);
77
+ for (const k in pkg.m) {
78
+ if (Object.prototype.hasOwnProperty.call(pkg.m, k)) {
79
+ reverseMap.set(pkg.m[k], k);
80
+ }
66
81
  }
67
82
  const traverse = (obj) => {
68
- if (Array.isArray(obj))
69
- return obj.map(traverse);
83
+ if (Array.isArray(obj)) {
84
+ const newArr = new Array(obj.length);
85
+ for (let i = 0; i < obj.length; i++) {
86
+ newArr[i] = traverse(obj[i]);
87
+ }
88
+ return newArr;
89
+ }
70
90
  if (obj && typeof obj === 'object') {
71
91
  const newObj = {};
72
92
  for (const k in obj) {
73
- const originalKey = reverseMap.get(k) || k;
74
- newObj[originalKey] = traverse(obj[k]);
93
+ if (Object.prototype.hasOwnProperty.call(obj, k)) {
94
+ const originalKey = reverseMap.get(k) || k;
95
+ newObj[originalKey] = traverse(obj[k]);
96
+ }
75
97
  }
76
98
  return newObj;
77
99
  }
@@ -94,11 +116,30 @@ class SchemaDataSeparationStrategy {
94
116
  if (Array.isArray(obj)) {
95
117
  // Check if it's an array of objects
96
118
  if (obj.length > 0 && typeof obj[0] === 'object' && obj[0] !== null && !Array.isArray(obj[0])) {
97
- const keys = Object.keys(obj[0]);
98
- const allMatch = obj.every(item => typeof item === 'object' &&
99
- item !== null &&
100
- !Array.isArray(item) &&
101
- JSON.stringify(Object.keys(item).sort()) === JSON.stringify(keys.sort()));
119
+ const firstItem = obj[0];
120
+ const keys = Object.keys(firstItem);
121
+ const keyCount = keys.length;
122
+ let allMatch = true;
123
+ for (let i = 1; i < obj.length; i++) {
124
+ const item = obj[i];
125
+ if (typeof item !== 'object' || item === null || Array.isArray(item)) {
126
+ allMatch = false;
127
+ break;
128
+ }
129
+ const itemKeys = Object.keys(item);
130
+ if (itemKeys.length !== keyCount) {
131
+ allMatch = false;
132
+ break;
133
+ }
134
+ for (const key of keys) {
135
+ if (!(key in item)) {
136
+ allMatch = false;
137
+ break;
138
+ }
139
+ }
140
+ if (!allMatch)
141
+ break;
142
+ }
102
143
  if (allMatch) {
103
144
  return {
104
145
  $s: keys, // Schema
@@ -106,12 +147,18 @@ class SchemaDataSeparationStrategy {
106
147
  };
107
148
  }
108
149
  }
109
- return obj.map(traverse);
150
+ const newArr = new Array(obj.length);
151
+ for (let i = 0; i < obj.length; i++) {
152
+ newArr[i] = traverse(obj[i]);
153
+ }
154
+ return newArr;
110
155
  }
111
156
  if (obj && typeof obj === 'object') {
112
157
  const newObj = {};
113
158
  for (const k in obj) {
114
- newObj[k] = traverse(obj[k]);
159
+ if (Object.prototype.hasOwnProperty.call(obj, k)) {
160
+ newObj[k] = traverse(obj[k]);
161
+ }
115
162
  }
116
163
  return newObj;
117
164
  }
@@ -124,19 +171,30 @@ class SchemaDataSeparationStrategy {
124
171
  if (obj && typeof obj === 'object') {
125
172
  if (obj.$s && obj.$d && Array.isArray(obj.$s) && Array.isArray(obj.$d)) {
126
173
  const keys = obj.$s;
127
- return obj.$d.map((values) => {
174
+ const dataArr = obj.$d;
175
+ const result = new Array(dataArr.length);
176
+ for (let i = 0; i < dataArr.length; i++) {
177
+ const values = dataArr[i];
128
178
  const item = {};
129
- keys.forEach((k, i) => {
130
- item[k] = traverse(values[i]);
131
- });
132
- return item;
133
- });
179
+ for (let j = 0; j < keys.length; j++) {
180
+ item[keys[j]] = traverse(values[j]);
181
+ }
182
+ result[i] = item;
183
+ }
184
+ return result;
185
+ }
186
+ if (Array.isArray(obj)) {
187
+ const newArr = new Array(obj.length);
188
+ for (let i = 0; i < obj.length; i++) {
189
+ newArr[i] = traverse(obj[i]);
190
+ }
191
+ return newArr;
134
192
  }
135
- if (Array.isArray(obj))
136
- return obj.map(traverse);
137
193
  const newObj = {};
138
194
  for (const k in obj) {
139
- newObj[k] = traverse(obj[k]);
195
+ if (Object.prototype.hasOwnProperty.call(obj, k)) {
196
+ newObj[k] = traverse(obj[k]);
197
+ }
140
198
  }
141
199
  return newObj;
142
200
  }
@@ -159,16 +217,12 @@ class UltraCompactStrategy {
159
217
  const keyMap = new Map();
160
218
  let counter = 0;
161
219
  const getShortKey = (key) => {
162
- if (!keyMap.has(key)) {
163
- let shortKey = '';
164
- let temp = counter++;
165
- do {
166
- shortKey = String.fromCharCode(97 + (temp % 26)) + shortKey;
167
- temp = Math.floor(temp / 26) - 1;
168
- } while (temp >= 0);
220
+ let shortKey = keyMap.get(key);
221
+ if (shortKey === undefined) {
222
+ shortKey = (0, exports.generateShortKey)(counter++);
169
223
  keyMap.set(key, shortKey);
170
224
  }
171
- return keyMap.get(key);
225
+ return shortKey;
172
226
  };
173
227
  const traverse = (obj) => {
174
228
  // Bool optimization: Only if unsafe mode is enabled
@@ -178,12 +232,19 @@ class UltraCompactStrategy {
178
232
  if (obj === false)
179
233
  return 0;
180
234
  }
181
- if (Array.isArray(obj))
182
- return obj.map(traverse);
235
+ if (Array.isArray(obj)) {
236
+ const newArr = new Array(obj.length);
237
+ for (let i = 0; i < obj.length; i++) {
238
+ newArr[i] = traverse(obj[i]);
239
+ }
240
+ return newArr;
241
+ }
183
242
  if (obj && typeof obj === 'object') {
184
243
  const newObj = {};
185
244
  for (const k in obj) {
186
- newObj[getShortKey(k)] = traverse(obj[k]);
245
+ if (Object.prototype.hasOwnProperty.call(obj, k)) {
246
+ newObj[getShortKey(k)] = traverse(obj[k]);
247
+ }
187
248
  }
188
249
  return newObj;
189
250
  }
@@ -199,32 +260,26 @@ class UltraCompactStrategy {
199
260
  if (!pkg || !pkg.m || pkg.d === undefined)
200
261
  return pkg;
201
262
  const reverseMap = new Map();
202
- for (const [k, v] of Object.entries(pkg.m)) {
203
- reverseMap.set(v, k);
263
+ for (const k in pkg.m) {
264
+ if (Object.prototype.hasOwnProperty.call(pkg.m, k)) {
265
+ reverseMap.set(pkg.m[k], k);
266
+ }
204
267
  }
205
268
  const traverse = (obj) => {
206
- // Removed automatic 1->true conversion to avoid corrupting numbers
207
- // if (obj === 1) return true;
208
- // NOTE: Ultra Compact assumes usage where boolean restoration is preferred.
209
- // Strictly speaking, we lose type info between 1 and true if we just blindly map.
210
- // For LLM context, usually 1/0 is fine. But for exact restoration, this is lossy for numbers.
211
- // Let's make it smarter: logic handled by downstream consumers or accept fuzzy types.
212
- // For now, let's NOT automatically convert 1->true to keep it safer,
213
- // unless we store type info effectively.
214
- // Actually, for LLM optimization, sending 1/0 is enough.
215
- // If we want exact restoration, we need a schema.
216
- // Let's stick to key restoration for now, and leave values as is (1/0) or maybe keep booleans as is if size diff is minimal?
217
- // "true" is 4 bytes, "1" is 1 byte.
218
- // If we want to support full roundtrip without schema, we might skip bool optimization OR live with the type change.
219
- // Let's keep 1/0 and NOT restore to boolean automatically to avoid corrupting actual numbers.
220
- // The user will receive 1/0 instead of true/false.
221
- if (Array.isArray(obj))
222
- return obj.map(traverse);
269
+ if (Array.isArray(obj)) {
270
+ const newArr = new Array(obj.length);
271
+ for (let i = 0; i < obj.length; i++) {
272
+ newArr[i] = traverse(obj[i]);
273
+ }
274
+ return newArr;
275
+ }
223
276
  if (obj && typeof obj === 'object') {
224
277
  const newObj = {};
225
278
  for (const k in obj) {
226
- const originalKey = reverseMap.get(k) || k;
227
- newObj[originalKey] = traverse(obj[k]);
279
+ if (Object.prototype.hasOwnProperty.call(obj, k)) {
280
+ const originalKey = reverseMap.get(k) || k;
281
+ newObj[originalKey] = traverse(obj[k]);
282
+ }
228
283
  }
229
284
  return newObj;
230
285
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "llm-chat-msg-compressor",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Intelligent JSON compression for LLM API optimization",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -35,9 +35,15 @@
35
35
  "type": "git",
36
36
  "url": "git+https://github.com/Sridharvn/llm-chat-msg-compressor.git"
37
37
  },
38
+ "bugs": {
39
+ "url": "https://github.com/Sridharvn/llm-chat-msg-compressor/issues"
40
+ },
41
+ "homepage": "https://github.com/Sridharvn/llm-chat-msg-compressor#readme",
42
+ "engines": {
43
+ "node": ">=18.0.0"
44
+ },
38
45
  "devDependencies": {
39
46
  "@types/jest": "^30.0.0",
40
- "gpt-3-encoder": "^1.1.4",
41
47
  "jest": "^30.2.0",
42
48
  "ts-jest": "^29.4.6",
43
49
  "ts-node": "^10.9.2",