@sohqureshi/tokenwise 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/LICENSE +21 -0
- package/README.md +260 -0
- package/dist/chunk-2IQAGI7Q.js +54 -0
- package/dist/chunk-33GBS5YN.js +288 -0
- package/dist/chunk-33RRCCAT.js +287 -0
- package/dist/chunk-3ECVBELU.js +49 -0
- package/dist/chunk-D4CFTFM3.js +105 -0
- package/dist/chunk-EUFLW42L.js +13 -0
- package/dist/chunk-GQ62V6ZK.js +61 -0
- package/dist/chunk-HKCPRPJL.js +294 -0
- package/dist/chunk-IAV75SZA.js +36 -0
- package/dist/chunk-JBKETSTK.js +270 -0
- package/dist/chunk-SURFMVNH.js +285 -0
- package/dist/chunk-TM6L7YF2.js +9 -0
- package/dist/chunk-YMOSWQGE.js +22 -0
- package/dist/cli.cjs +352 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +51 -0
- package/dist/core/analyze.cjs +181 -0
- package/dist/core/analyze.d.cts +13 -0
- package/dist/core/analyze.d.ts +13 -0
- package/dist/core/analyze.js +11 -0
- package/dist/core/compact.cjs +68 -0
- package/dist/core/compact.d.cts +3 -0
- package/dist/core/compact.d.ts +3 -0
- package/dist/core/compact.js +7 -0
- package/dist/core/flatten.cjs +46 -0
- package/dist/core/flatten.d.cts +18 -0
- package/dist/core/flatten.d.ts +18 -0
- package/dist/core/flatten.js +6 -0
- package/dist/core/natural.cjs +129 -0
- package/dist/core/natural.d.cts +28 -0
- package/dist/core/natural.d.ts +28 -0
- package/dist/core/natural.js +6 -0
- package/dist/core/prune.cjs +60 -0
- package/dist/core/prune.d.cts +11 -0
- package/dist/core/prune.d.ts +11 -0
- package/dist/core/prune.js +6 -0
- package/dist/core/token.cjs +33 -0
- package/dist/core/token.d.cts +12 -0
- package/dist/core/token.d.ts +12 -0
- package/dist/core/token.js +6 -0
- package/dist/core/toon.cjs +73 -0
- package/dist/core/toon.d.cts +25 -0
- package/dist/core/toon.d.ts +25 -0
- package/dist/core/toon.js +6 -0
- package/dist/index.cjs +344 -0
- package/dist/index.d.cts +40 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +49 -0
- package/package.json +78 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
// src/core/prune.ts
|
|
2
|
+
function prune(obj, options = {}) {
|
|
3
|
+
const normalizedOptions = Array.isArray(options) ? { removeKeys: options } : options;
|
|
4
|
+
const {
|
|
5
|
+
removeKeys = [],
|
|
6
|
+
removeNull = true,
|
|
7
|
+
removeUndefined = true,
|
|
8
|
+
removeEmptyObjects = true,
|
|
9
|
+
removeEmptyArrays = false
|
|
10
|
+
} = normalizedOptions;
|
|
11
|
+
if (obj === null) return removeNull ? void 0 : obj;
|
|
12
|
+
if (obj === void 0) return removeUndefined ? void 0 : obj;
|
|
13
|
+
if (typeof obj !== "object") return obj;
|
|
14
|
+
if (Array.isArray(obj)) {
|
|
15
|
+
const arr = obj.map((item) => prune(item, normalizedOptions)).filter((item) => item !== void 0);
|
|
16
|
+
if (removeEmptyArrays && arr.length === 0) {
|
|
17
|
+
return void 0;
|
|
18
|
+
}
|
|
19
|
+
return arr;
|
|
20
|
+
}
|
|
21
|
+
const result = {};
|
|
22
|
+
for (const key in obj) {
|
|
23
|
+
if (removeKeys.includes(key)) continue;
|
|
24
|
+
const value = prune(obj[key], normalizedOptions);
|
|
25
|
+
if (value === void 0) continue;
|
|
26
|
+
if (removeEmptyObjects && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 0) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
result[key] = value;
|
|
30
|
+
}
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// src/core/flatten.ts
|
|
35
|
+
function flatten(obj, prefix = "", res = {}) {
|
|
36
|
+
if (obj === null || obj === void 0) return res;
|
|
37
|
+
if (typeof obj !== "object") {
|
|
38
|
+
res[prefix] = obj;
|
|
39
|
+
return res;
|
|
40
|
+
}
|
|
41
|
+
for (const key in obj) {
|
|
42
|
+
const value = obj[key];
|
|
43
|
+
const newKey = prefix ? `${prefix}.${key}` : key;
|
|
44
|
+
if (typeof value === "object" && value !== null) {
|
|
45
|
+
flatten(value, newKey, res);
|
|
46
|
+
} else {
|
|
47
|
+
res[newKey] = value;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return res;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// src/core/compact.ts
|
|
54
|
+
function compact(obj) {
|
|
55
|
+
const pruned = prune(obj);
|
|
56
|
+
const flat = flatten(pruned);
|
|
57
|
+
return Object.entries(flat).map(([k, v]) => `${k}=${v}`).join("|");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// src/core/toon.ts
|
|
61
|
+
function toTOON(data, indent = 0) {
|
|
62
|
+
const space = " ".repeat(indent);
|
|
63
|
+
if (Array.isArray(data)) {
|
|
64
|
+
if (data.length === 0) return "[]";
|
|
65
|
+
if (typeof data[0] === "object") {
|
|
66
|
+
const keys = Object.keys(data[0]);
|
|
67
|
+
let result = `${space}[${data.length}]{${keys.join(",")}}:
|
|
68
|
+
`;
|
|
69
|
+
for (const item of data) {
|
|
70
|
+
result += space + " " + keys.map((k) => formatValue(item[k])).join(",") + "\n";
|
|
71
|
+
}
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
return `${space}[${data.length}]: ${data.join(",")}`;
|
|
75
|
+
}
|
|
76
|
+
if (typeof data === "object" && data !== null) {
|
|
77
|
+
let result = "";
|
|
78
|
+
for (const key in data) {
|
|
79
|
+
const value = data[key];
|
|
80
|
+
if (typeof value === "object") {
|
|
81
|
+
result += `${space}${key}:
|
|
82
|
+
${toTOON(value, indent + 1)}
|
|
83
|
+
`;
|
|
84
|
+
} else {
|
|
85
|
+
result += `${space}${key}: ${formatValue(value)}
|
|
86
|
+
`;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return result.trim();
|
|
90
|
+
}
|
|
91
|
+
return formatValue(data);
|
|
92
|
+
}
|
|
93
|
+
function formatValue(val) {
|
|
94
|
+
if (val === null || val === void 0) return "";
|
|
95
|
+
if (typeof val === "string") return val;
|
|
96
|
+
return String(val);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// src/core/token.ts
|
|
100
|
+
function estimateTokens(text) {
|
|
101
|
+
if (!text) return 0;
|
|
102
|
+
return Math.ceil(text.length / 4);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// src/core/analyze.ts
|
|
106
|
+
function analyze(input, options = {}) {
|
|
107
|
+
if (!input || typeof input === "object" && input !== null && Object.keys(input).length === 0) {
|
|
108
|
+
return {
|
|
109
|
+
originalTokens: 0,
|
|
110
|
+
optimizedTokens: 0,
|
|
111
|
+
savings: 0,
|
|
112
|
+
savingsPercent: 0,
|
|
113
|
+
optimizedData: null,
|
|
114
|
+
reductionRatio: 1
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
let originalTokens = estimateTokens(input);
|
|
118
|
+
if (isNaN(originalTokens) || !isFinite(originalTokens)) originalTokens = 0;
|
|
119
|
+
let optimizedData = input;
|
|
120
|
+
if (options.prune && Array.isArray(options.prune)) {
|
|
121
|
+
optimizedData = prune(optimizedData, options.prune);
|
|
122
|
+
}
|
|
123
|
+
if (options.compact) {
|
|
124
|
+
optimizedData = compact(optimizedData);
|
|
125
|
+
}
|
|
126
|
+
if (options.flatten) {
|
|
127
|
+
optimizedData = flatten(optimizedData);
|
|
128
|
+
}
|
|
129
|
+
if (options.toTOON === true || options.toon === true) {
|
|
130
|
+
optimizedData = toTOON(optimizedData);
|
|
131
|
+
}
|
|
132
|
+
let optimizedTokens = estimateTokens(optimizedData);
|
|
133
|
+
if (isNaN(optimizedTokens) || !isFinite(optimizedTokens)) optimizedTokens = 0;
|
|
134
|
+
const savings = Math.max(0, originalTokens - optimizedTokens);
|
|
135
|
+
const savingsPercent = originalTokens > 0 ? Math.round(savings / originalTokens * 100) : 0;
|
|
136
|
+
const reductionRatio = originalTokens > 0 ? optimizedTokens / originalTokens : 1;
|
|
137
|
+
return {
|
|
138
|
+
originalTokens,
|
|
139
|
+
optimizedTokens,
|
|
140
|
+
savings,
|
|
141
|
+
savingsPercent,
|
|
142
|
+
optimizedData,
|
|
143
|
+
reductionRatio
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// src/core/natural.ts
|
|
148
|
+
function toNatural(data, depth = 0) {
|
|
149
|
+
if (data === null || data === void 0) return "nothing";
|
|
150
|
+
if (typeof data === "string") return data;
|
|
151
|
+
if (typeof data === "number") return String(data);
|
|
152
|
+
if (typeof data === "boolean") return data ? "yes" : "no";
|
|
153
|
+
if (Array.isArray(data)) {
|
|
154
|
+
if (data.length === 0) return "empty list";
|
|
155
|
+
if (data.every(isPlainObject)) {
|
|
156
|
+
return data.map((item, index) => `${index + 1}. ${stripTrailingPeriod(toNatural(item, depth + 1))}.`).join(" ");
|
|
157
|
+
}
|
|
158
|
+
const items = data.map((item) => toNatural(item, depth + 1));
|
|
159
|
+
return joinNaturalList(items);
|
|
160
|
+
}
|
|
161
|
+
if (typeof data === "object") {
|
|
162
|
+
return buildContextualStory(data, depth);
|
|
163
|
+
}
|
|
164
|
+
return String(data);
|
|
165
|
+
}
|
|
166
|
+
function buildContextualStory(obj, depth = 0) {
|
|
167
|
+
let name = obj.name || obj.userName || obj.user;
|
|
168
|
+
if (typeof name === "object" && name !== null && name.name) {
|
|
169
|
+
name = name.name;
|
|
170
|
+
}
|
|
171
|
+
const entries = Object.entries(obj).filter(([key]) => {
|
|
172
|
+
return !["id", "timestamp", "apiKey"].includes(key);
|
|
173
|
+
});
|
|
174
|
+
if (entries.length === 0) return "";
|
|
175
|
+
let story = name && typeof name === "string" ? `User ${name}` : "";
|
|
176
|
+
const clauses = entries.map(([key, value]) => {
|
|
177
|
+
if (key === "user" && name && typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
178
|
+
const userProps = Object.entries(value).filter(([k]) => !["id", "name", "timestamp", "apiKey"].includes(k)).map(([k, v]) => formatPropertyClause(k, v, depth + 1)).filter((c) => c !== null && c !== "").join(", ");
|
|
179
|
+
return userProps ? `(${userProps})` : null;
|
|
180
|
+
}
|
|
181
|
+
return formatPropertyClause(key, value, depth);
|
|
182
|
+
}).filter((c) => c !== null && c !== "");
|
|
183
|
+
if (clauses.length === 0) return story;
|
|
184
|
+
if (story) {
|
|
185
|
+
story += " " + clauses.join(", ");
|
|
186
|
+
} else {
|
|
187
|
+
story = clauses.join(", ");
|
|
188
|
+
}
|
|
189
|
+
return story + ".";
|
|
190
|
+
}
|
|
191
|
+
function formatPropertyClause(key, value, depth) {
|
|
192
|
+
const naturalKey = camelToWords(key);
|
|
193
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
194
|
+
if (["internal", "metadata", "debug"].includes(key)) {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
const nested = Object.entries(value).filter(([k]) => !["id", "timestamp", "apiKey", "createdAt", "updatedAt"].includes(k)).map(([k, v]) => {
|
|
198
|
+
const propValue = toNatural(v, depth + 1);
|
|
199
|
+
if (propValue === "yes" || propValue === "no") {
|
|
200
|
+
return `${camelToWords(k)} ${propValue === "yes" ? "enabled" : "disabled"}`;
|
|
201
|
+
}
|
|
202
|
+
return `${camelToWords(k)} ${propValue}`;
|
|
203
|
+
}).filter((c) => c && c.trim()).join(", ");
|
|
204
|
+
if (!nested) return null;
|
|
205
|
+
return `${naturalKey}: ${nested}`;
|
|
206
|
+
}
|
|
207
|
+
if (Array.isArray(value)) {
|
|
208
|
+
if (value.length === 0) return null;
|
|
209
|
+
if (["skills", "hobbies", "interests", "tags", "languages", "items"].includes(key)) {
|
|
210
|
+
const items2 = value.map((item) => {
|
|
211
|
+
const str = String(item);
|
|
212
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
213
|
+
});
|
|
214
|
+
return `Having ${joinNaturalList(items2)}`;
|
|
215
|
+
}
|
|
216
|
+
const items = joinNaturalList(value.map((item) => toNatural(item, depth + 1)));
|
|
217
|
+
return `${naturalKey}: ${items}`;
|
|
218
|
+
}
|
|
219
|
+
const naturalValue = toNatural(value, depth + 1);
|
|
220
|
+
if (naturalValue === "yes") return `${naturalKey} enabled`;
|
|
221
|
+
if (naturalValue === "no") return `${naturalKey} disabled`;
|
|
222
|
+
if (key === "theme") return `prefers the ${naturalValue} ${key}`;
|
|
223
|
+
if (key === "age") return `age: ${naturalValue}`;
|
|
224
|
+
if (key === "email") return `email: ${naturalValue}`;
|
|
225
|
+
if (key === "city") return `address: ${naturalValue}`;
|
|
226
|
+
if (key === "debug" && naturalValue === "yes") return `debug enabled`;
|
|
227
|
+
return `${naturalKey}: ${naturalValue}`;
|
|
228
|
+
}
|
|
229
|
+
function camelToWords(str) {
|
|
230
|
+
return str.replace(/([A-Z])/g, " $1").toLowerCase().trim();
|
|
231
|
+
}
|
|
232
|
+
function isPlainObject(value) {
|
|
233
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
234
|
+
}
|
|
235
|
+
function joinNaturalList(items) {
|
|
236
|
+
if (items.length === 0) return "";
|
|
237
|
+
if (items.length === 1) return items[0];
|
|
238
|
+
if (items.length === 2) return `${items[0]} and ${items[1]}`;
|
|
239
|
+
const lastItem = items[items.length - 1];
|
|
240
|
+
return `${items.slice(0, -1).join(", ")} and ${lastItem}`;
|
|
241
|
+
}
|
|
242
|
+
function stripTrailingPeriod(value) {
|
|
243
|
+
return value.replace(/\.+$/, "");
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// src/chain.ts
|
|
247
|
+
var AIChain = class {
|
|
248
|
+
constructor(data) {
|
|
249
|
+
this.data = data;
|
|
250
|
+
}
|
|
251
|
+
prune(options) {
|
|
252
|
+
this.data = prune(this.data, options);
|
|
253
|
+
return this;
|
|
254
|
+
}
|
|
255
|
+
compact() {
|
|
256
|
+
this.data = compact(this.data);
|
|
257
|
+
return this;
|
|
258
|
+
}
|
|
259
|
+
flatten() {
|
|
260
|
+
this.data = flatten(this.data);
|
|
261
|
+
return this;
|
|
262
|
+
}
|
|
263
|
+
toTOON() {
|
|
264
|
+
this.data = toTOON(this.data);
|
|
265
|
+
return this;
|
|
266
|
+
}
|
|
267
|
+
toNatural() {
|
|
268
|
+
return toNatural(this.data);
|
|
269
|
+
}
|
|
270
|
+
analyze() {
|
|
271
|
+
return analyze(this.data);
|
|
272
|
+
}
|
|
273
|
+
value() {
|
|
274
|
+
return this.data;
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
export {
|
|
279
|
+
prune,
|
|
280
|
+
compact,
|
|
281
|
+
toTOON,
|
|
282
|
+
analyze,
|
|
283
|
+
toNatural,
|
|
284
|
+
AIChain
|
|
285
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// src/core/flatten.ts
|
|
2
|
+
function flatten(obj, prefix = "", res = {}) {
|
|
3
|
+
if (obj === null || obj === void 0) return res;
|
|
4
|
+
if (typeof obj !== "object") {
|
|
5
|
+
res[prefix] = obj;
|
|
6
|
+
return res;
|
|
7
|
+
}
|
|
8
|
+
for (const key in obj) {
|
|
9
|
+
const value = obj[key];
|
|
10
|
+
const newKey = prefix ? `${prefix}.${key}` : key;
|
|
11
|
+
if (typeof value === "object" && value !== null) {
|
|
12
|
+
flatten(value, newKey, res);
|
|
13
|
+
} else {
|
|
14
|
+
res[newKey] = value;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return res;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export {
|
|
21
|
+
flatten
|
|
22
|
+
};
|
package/dist/cli.cjs
ADDED
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
25
|
+
|
|
26
|
+
// src/cli.ts
|
|
27
|
+
var import_node_fs = __toESM(require("fs"), 1);
|
|
28
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
29
|
+
|
|
30
|
+
// src/core/prune.ts
|
|
31
|
+
function prune(obj, options = {}) {
|
|
32
|
+
const normalizedOptions = Array.isArray(options) ? { removeKeys: options } : options;
|
|
33
|
+
const {
|
|
34
|
+
removeKeys = [],
|
|
35
|
+
removeNull = true,
|
|
36
|
+
removeUndefined = true,
|
|
37
|
+
removeEmptyObjects = true,
|
|
38
|
+
removeEmptyArrays = false
|
|
39
|
+
} = normalizedOptions;
|
|
40
|
+
if (obj === null) return removeNull ? void 0 : obj;
|
|
41
|
+
if (obj === void 0) return removeUndefined ? void 0 : obj;
|
|
42
|
+
if (typeof obj !== "object") return obj;
|
|
43
|
+
if (Array.isArray(obj)) {
|
|
44
|
+
const arr = obj.map((item) => prune(item, normalizedOptions)).filter((item) => item !== void 0);
|
|
45
|
+
if (removeEmptyArrays && arr.length === 0) {
|
|
46
|
+
return void 0;
|
|
47
|
+
}
|
|
48
|
+
return arr;
|
|
49
|
+
}
|
|
50
|
+
const result = {};
|
|
51
|
+
for (const key in obj) {
|
|
52
|
+
if (removeKeys.includes(key)) continue;
|
|
53
|
+
const value = prune(obj[key], normalizedOptions);
|
|
54
|
+
if (value === void 0) continue;
|
|
55
|
+
if (removeEmptyObjects && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 0) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
result[key] = value;
|
|
59
|
+
}
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// src/core/flatten.ts
|
|
64
|
+
function flatten(obj, prefix = "", res = {}) {
|
|
65
|
+
if (obj === null || obj === void 0) return res;
|
|
66
|
+
if (typeof obj !== "object") {
|
|
67
|
+
res[prefix] = obj;
|
|
68
|
+
return res;
|
|
69
|
+
}
|
|
70
|
+
for (const key in obj) {
|
|
71
|
+
const value = obj[key];
|
|
72
|
+
const newKey = prefix ? `${prefix}.${key}` : key;
|
|
73
|
+
if (typeof value === "object" && value !== null) {
|
|
74
|
+
flatten(value, newKey, res);
|
|
75
|
+
} else {
|
|
76
|
+
res[newKey] = value;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return res;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// src/core/compact.ts
|
|
83
|
+
function compact(obj) {
|
|
84
|
+
const pruned = prune(obj);
|
|
85
|
+
return JSON.stringify(pruned) ?? "";
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/core/toon.ts
|
|
89
|
+
function toTOON(data, indent = 0) {
|
|
90
|
+
const space = " ".repeat(indent);
|
|
91
|
+
if (Array.isArray(data)) {
|
|
92
|
+
if (data.length === 0) return "[]";
|
|
93
|
+
if (data.every(isPlainObject)) {
|
|
94
|
+
const keys = Array.from(
|
|
95
|
+
new Set(data.flatMap((item) => Object.keys(item)))
|
|
96
|
+
);
|
|
97
|
+
let result = `${space}[${data.length}]{${keys.join(",")}}:
|
|
98
|
+
`;
|
|
99
|
+
for (const item of data) {
|
|
100
|
+
result += space + " " + keys.map((k) => formatValue(item[k])).join(",") + "\n";
|
|
101
|
+
}
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
return `${space}[${data.length}]: ${data.join(",")}`;
|
|
105
|
+
}
|
|
106
|
+
if (typeof data === "object" && data !== null) {
|
|
107
|
+
let result = "";
|
|
108
|
+
for (const key in data) {
|
|
109
|
+
const value = data[key];
|
|
110
|
+
if (typeof value === "object" && value !== null) {
|
|
111
|
+
result += `${space}${key}:
|
|
112
|
+
${toTOON(value, indent + 1)}
|
|
113
|
+
`;
|
|
114
|
+
} else {
|
|
115
|
+
const formatted = formatValue(value);
|
|
116
|
+
result += formatted ? `${space}${key}: ${formatted}
|
|
117
|
+
` : `${space}${key}:
|
|
118
|
+
`;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return result.trim();
|
|
122
|
+
}
|
|
123
|
+
return formatValue(data);
|
|
124
|
+
}
|
|
125
|
+
function formatValue(val) {
|
|
126
|
+
if (val === null || val === void 0) return "";
|
|
127
|
+
if (typeof val === "string") return val;
|
|
128
|
+
return String(val);
|
|
129
|
+
}
|
|
130
|
+
function isPlainObject(value) {
|
|
131
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// src/core/token.ts
|
|
135
|
+
function estimateTokens(text) {
|
|
136
|
+
if (!text) return 0;
|
|
137
|
+
return Math.ceil(text.length / 4);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// src/core/analyze.ts
|
|
141
|
+
function analyze(input, options = {}) {
|
|
142
|
+
if (!input || typeof input === "object" && input !== null && Object.keys(input).length === 0) {
|
|
143
|
+
return {
|
|
144
|
+
originalTokens: 0,
|
|
145
|
+
optimizedTokens: 0,
|
|
146
|
+
savings: 0,
|
|
147
|
+
savingsPercent: 0,
|
|
148
|
+
optimizedData: null,
|
|
149
|
+
reductionRatio: 1
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
let originalTokens = estimateTokens(input);
|
|
153
|
+
if (isNaN(originalTokens) || !isFinite(originalTokens)) originalTokens = 0;
|
|
154
|
+
let optimizedData = input;
|
|
155
|
+
if (options.prune && Array.isArray(options.prune)) {
|
|
156
|
+
optimizedData = prune(optimizedData, options.prune);
|
|
157
|
+
}
|
|
158
|
+
if (options.compact) {
|
|
159
|
+
optimizedData = compact(optimizedData);
|
|
160
|
+
}
|
|
161
|
+
if (options.flatten) {
|
|
162
|
+
optimizedData = flatten(optimizedData);
|
|
163
|
+
}
|
|
164
|
+
if (options.toTOON === true || options.toon === true) {
|
|
165
|
+
optimizedData = toTOON(optimizedData);
|
|
166
|
+
}
|
|
167
|
+
let optimizedTokens = estimateTokens(optimizedData);
|
|
168
|
+
if (isNaN(optimizedTokens) || !isFinite(optimizedTokens)) optimizedTokens = 0;
|
|
169
|
+
const savings = Math.max(0, originalTokens - optimizedTokens);
|
|
170
|
+
const savingsPercent = originalTokens > 0 ? Math.round(savings / originalTokens * 100) : 0;
|
|
171
|
+
const reductionRatio = originalTokens > 0 ? optimizedTokens / originalTokens : 1;
|
|
172
|
+
return {
|
|
173
|
+
originalTokens,
|
|
174
|
+
optimizedTokens,
|
|
175
|
+
savings,
|
|
176
|
+
savingsPercent,
|
|
177
|
+
optimizedData,
|
|
178
|
+
reductionRatio
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// src/core/natural.ts
|
|
183
|
+
function toNatural(data, depth = 0) {
|
|
184
|
+
if (data === null || data === void 0) return "nothing";
|
|
185
|
+
if (typeof data === "string") return data;
|
|
186
|
+
if (typeof data === "number") return String(data);
|
|
187
|
+
if (typeof data === "boolean") return data ? "yes" : "no";
|
|
188
|
+
if (Array.isArray(data)) {
|
|
189
|
+
if (data.length === 0) return "empty list";
|
|
190
|
+
if (data.every(isPlainObject2)) {
|
|
191
|
+
return data.map((item, index) => `${index + 1}. ${stripTrailingPeriod(toNatural(item, depth + 1))}.`).join(" ");
|
|
192
|
+
}
|
|
193
|
+
const items = data.map((item) => toNatural(item, depth + 1));
|
|
194
|
+
return joinNaturalList(items);
|
|
195
|
+
}
|
|
196
|
+
if (typeof data === "object") {
|
|
197
|
+
return buildContextualStory(data, depth);
|
|
198
|
+
}
|
|
199
|
+
return String(data);
|
|
200
|
+
}
|
|
201
|
+
function buildContextualStory(obj, depth = 0) {
|
|
202
|
+
let name = obj.name || obj.userName || obj.user;
|
|
203
|
+
if (typeof name === "object" && name !== null && name.name) {
|
|
204
|
+
name = name.name;
|
|
205
|
+
}
|
|
206
|
+
const entries = Object.entries(obj).filter(([key]) => {
|
|
207
|
+
return !["id", "timestamp", "apiKey"].includes(key);
|
|
208
|
+
});
|
|
209
|
+
if (entries.length === 0) return "";
|
|
210
|
+
let story = name && typeof name === "string" ? `User ${name}` : "";
|
|
211
|
+
const clauses = entries.map(([key, value]) => {
|
|
212
|
+
if (key === "user" && name && typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
213
|
+
const userProps = Object.entries(value).filter(([k]) => !["id", "name", "timestamp", "apiKey"].includes(k)).map(([k, v]) => formatPropertyClause(k, v, depth + 1)).filter((c) => c !== null && c !== "").join(", ");
|
|
214
|
+
return userProps ? `(${userProps})` : null;
|
|
215
|
+
}
|
|
216
|
+
return formatPropertyClause(key, value, depth);
|
|
217
|
+
}).filter((c) => c !== null && c !== "");
|
|
218
|
+
if (clauses.length === 0) return story;
|
|
219
|
+
if (story) {
|
|
220
|
+
story += " " + clauses.join(", ");
|
|
221
|
+
} else {
|
|
222
|
+
story = clauses.join(", ");
|
|
223
|
+
}
|
|
224
|
+
return story + ".";
|
|
225
|
+
}
|
|
226
|
+
function formatPropertyClause(key, value, depth) {
|
|
227
|
+
const naturalKey = camelToWords(key);
|
|
228
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
229
|
+
if (["internal", "metadata", "debug"].includes(key)) {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
const nested = Object.entries(value).filter(([k]) => !["id", "timestamp", "apiKey", "createdAt", "updatedAt"].includes(k)).map(([k, v]) => {
|
|
233
|
+
if (typeof v === "object" && v !== null) {
|
|
234
|
+
return formatPropertyClause(k, v, depth + 1);
|
|
235
|
+
}
|
|
236
|
+
const propValue = toNatural(v, depth + 1);
|
|
237
|
+
if (propValue === "yes" || propValue === "no") {
|
|
238
|
+
return `${camelToWords(k)} ${propValue === "yes" ? "enabled" : "disabled"}`;
|
|
239
|
+
}
|
|
240
|
+
return `${camelToWords(k)} ${propValue}`;
|
|
241
|
+
}).filter((c) => c && c.trim()).join(", ");
|
|
242
|
+
if (!nested) return null;
|
|
243
|
+
return `${naturalKey}: ${nested}`;
|
|
244
|
+
}
|
|
245
|
+
if (Array.isArray(value)) {
|
|
246
|
+
if (value.length === 0) return null;
|
|
247
|
+
if (["skills", "hobbies", "interests", "tags", "languages", "items"].includes(key)) {
|
|
248
|
+
const items2 = value.map((item) => {
|
|
249
|
+
const str = String(item);
|
|
250
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
251
|
+
});
|
|
252
|
+
return `Having ${joinNaturalList(items2)}`;
|
|
253
|
+
}
|
|
254
|
+
const items = joinNaturalList(value.map((item) => toNatural(item, depth + 1)));
|
|
255
|
+
return `${naturalKey}: ${items}`;
|
|
256
|
+
}
|
|
257
|
+
const naturalValue = toNatural(value, depth + 1);
|
|
258
|
+
if (naturalValue === "yes") return `${naturalKey} enabled`;
|
|
259
|
+
if (naturalValue === "no") return `${naturalKey} disabled`;
|
|
260
|
+
if (key === "theme") return `prefers the ${naturalValue} ${key}`;
|
|
261
|
+
if (key === "age") return `age: ${naturalValue}`;
|
|
262
|
+
if (key === "email") return `email: ${naturalValue}`;
|
|
263
|
+
if (key === "city") return `address: ${naturalValue}`;
|
|
264
|
+
if (key === "debug" && naturalValue === "yes") return `debug enabled`;
|
|
265
|
+
return `${naturalKey}: ${naturalValue}`;
|
|
266
|
+
}
|
|
267
|
+
function camelToWords(str) {
|
|
268
|
+
return str.replace(/([A-Z])/g, " $1").toLowerCase().trim();
|
|
269
|
+
}
|
|
270
|
+
function isPlainObject2(value) {
|
|
271
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
272
|
+
}
|
|
273
|
+
function joinNaturalList(items) {
|
|
274
|
+
if (items.length === 0) return "";
|
|
275
|
+
if (items.length === 1) return items[0];
|
|
276
|
+
if (items.length === 2) return `${items[0]} and ${items[1]}`;
|
|
277
|
+
const lastItem = items[items.length - 1];
|
|
278
|
+
return `${items.slice(0, -1).join(", ")} and ${lastItem}`;
|
|
279
|
+
}
|
|
280
|
+
function stripTrailingPeriod(value) {
|
|
281
|
+
return value.replace(/\.+$/, "");
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// src/chain.ts
|
|
285
|
+
var AIChain = class {
|
|
286
|
+
constructor(data) {
|
|
287
|
+
this.data = data;
|
|
288
|
+
}
|
|
289
|
+
prune(options) {
|
|
290
|
+
this.data = prune(this.data, options);
|
|
291
|
+
return this;
|
|
292
|
+
}
|
|
293
|
+
compact() {
|
|
294
|
+
this.data = compact(this.data);
|
|
295
|
+
return this;
|
|
296
|
+
}
|
|
297
|
+
flatten() {
|
|
298
|
+
this.data = flatten(this.data);
|
|
299
|
+
return this;
|
|
300
|
+
}
|
|
301
|
+
toTOON() {
|
|
302
|
+
this.data = toTOON(this.data);
|
|
303
|
+
return this;
|
|
304
|
+
}
|
|
305
|
+
toNatural() {
|
|
306
|
+
return toNatural(this.data);
|
|
307
|
+
}
|
|
308
|
+
analyze() {
|
|
309
|
+
return analyze(this.data);
|
|
310
|
+
}
|
|
311
|
+
value() {
|
|
312
|
+
return this.data;
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
// src/cli.ts
|
|
317
|
+
var args = process.argv.slice(2);
|
|
318
|
+
if (args.length === 0) {
|
|
319
|
+
console.log(`
|
|
320
|
+
Usage:
|
|
321
|
+
tokenwise <file> [options]
|
|
322
|
+
|
|
323
|
+
Options:
|
|
324
|
+
--toon Convert JSON to TOON format
|
|
325
|
+
--compact Convert JSON to compact format
|
|
326
|
+
--analyze Show token analysis
|
|
327
|
+
`);
|
|
328
|
+
process.exit(0);
|
|
329
|
+
}
|
|
330
|
+
var filePath = import_node_path.default.resolve(process.cwd(), args[0]);
|
|
331
|
+
if (!import_node_fs.default.existsSync(filePath)) {
|
|
332
|
+
console.error("\u274C File not found:", filePath);
|
|
333
|
+
process.exit(1);
|
|
334
|
+
}
|
|
335
|
+
var raw = import_node_fs.default.readFileSync(filePath, "utf-8");
|
|
336
|
+
var json;
|
|
337
|
+
try {
|
|
338
|
+
json = JSON.parse(raw);
|
|
339
|
+
} catch (err) {
|
|
340
|
+
console.error("\u274C Invalid JSON file");
|
|
341
|
+
process.exit(1);
|
|
342
|
+
}
|
|
343
|
+
var tool = new AIChain(json);
|
|
344
|
+
if (args.includes("--toon")) {
|
|
345
|
+
console.log(tool.toTOON().value());
|
|
346
|
+
} else if (args.includes("--compact")) {
|
|
347
|
+
console.log(tool.compact().value());
|
|
348
|
+
} else if (args.includes("--analyze")) {
|
|
349
|
+
console.log(tool.analyze());
|
|
350
|
+
} else {
|
|
351
|
+
console.log("\u26A0\uFE0F No valid option provided. Use --toon, --compact or --analyze");
|
|
352
|
+
}
|
package/dist/cli.d.cts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|