@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.
Files changed (52) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +260 -0
  3. package/dist/chunk-2IQAGI7Q.js +54 -0
  4. package/dist/chunk-33GBS5YN.js +288 -0
  5. package/dist/chunk-33RRCCAT.js +287 -0
  6. package/dist/chunk-3ECVBELU.js +49 -0
  7. package/dist/chunk-D4CFTFM3.js +105 -0
  8. package/dist/chunk-EUFLW42L.js +13 -0
  9. package/dist/chunk-GQ62V6ZK.js +61 -0
  10. package/dist/chunk-HKCPRPJL.js +294 -0
  11. package/dist/chunk-IAV75SZA.js +36 -0
  12. package/dist/chunk-JBKETSTK.js +270 -0
  13. package/dist/chunk-SURFMVNH.js +285 -0
  14. package/dist/chunk-TM6L7YF2.js +9 -0
  15. package/dist/chunk-YMOSWQGE.js +22 -0
  16. package/dist/cli.cjs +352 -0
  17. package/dist/cli.d.cts +1 -0
  18. package/dist/cli.d.ts +1 -0
  19. package/dist/cli.js +51 -0
  20. package/dist/core/analyze.cjs +181 -0
  21. package/dist/core/analyze.d.cts +13 -0
  22. package/dist/core/analyze.d.ts +13 -0
  23. package/dist/core/analyze.js +11 -0
  24. package/dist/core/compact.cjs +68 -0
  25. package/dist/core/compact.d.cts +3 -0
  26. package/dist/core/compact.d.ts +3 -0
  27. package/dist/core/compact.js +7 -0
  28. package/dist/core/flatten.cjs +46 -0
  29. package/dist/core/flatten.d.cts +18 -0
  30. package/dist/core/flatten.d.ts +18 -0
  31. package/dist/core/flatten.js +6 -0
  32. package/dist/core/natural.cjs +129 -0
  33. package/dist/core/natural.d.cts +28 -0
  34. package/dist/core/natural.d.ts +28 -0
  35. package/dist/core/natural.js +6 -0
  36. package/dist/core/prune.cjs +60 -0
  37. package/dist/core/prune.d.cts +11 -0
  38. package/dist/core/prune.d.ts +11 -0
  39. package/dist/core/prune.js +6 -0
  40. package/dist/core/token.cjs +33 -0
  41. package/dist/core/token.d.cts +12 -0
  42. package/dist/core/token.d.ts +12 -0
  43. package/dist/core/token.js +6 -0
  44. package/dist/core/toon.cjs +73 -0
  45. package/dist/core/toon.d.cts +25 -0
  46. package/dist/core/toon.d.ts +25 -0
  47. package/dist/core/toon.js +6 -0
  48. package/dist/index.cjs +344 -0
  49. package/dist/index.d.cts +40 -0
  50. package/dist/index.d.ts +40 -0
  51. package/dist/index.js +49 -0
  52. package/package.json +78 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 tokenwise
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,260 @@
1
+ # TokenWise
2
+
3
+ TokenWise is a lightweight utility for preparing JSON before sending it to AI models. It helps reduce payload noise, shrink token usage, and turn structured data into formats that are easier for LLMs to consume.
4
+
5
+ ---
6
+
7
+ ## 🧠 What is tokenwise?
8
+
9
+ **tokenwise** is a lightweight utility that optimizes your data before sending it to AI models.
10
+
11
+ Raw JSON is:
12
+
13
+ * ❌ verbose
14
+ * ❌ token-heavy
15
+ * ❌ expensive for LLMs
16
+
17
+ **tokenwise** helps you:
18
+
19
+ * 📉 reduce token usage
20
+ * ⚡ improve response speed
21
+ * 💰 lower API costs
22
+
23
+ ---
24
+
25
+ ## Installation
26
+
27
+ ```bash
28
+ npm install tokenwise
29
+ ```
30
+
31
+ ## Quick Usage
32
+
33
+ ```js
34
+ import ai, { compact, flatten, toNatural, toTOON } from "tokenwise";
35
+
36
+ const product = {
37
+ product: {
38
+ name: "Wireless Headphones",
39
+ price: 79.99
40
+ }
41
+ };
42
+
43
+ console.log(compact(product));
44
+ // {"product":{"name":"Wireless Headphones","price":79.99}}
45
+
46
+ console.log(flatten(product));
47
+ // {
48
+ // "product.name": "Wireless Headphones",
49
+ // "product.price": 79.99
50
+ // }
51
+
52
+ console.log(ai(product).compact().value());
53
+ // {"product":{"name":"Wireless Headphones","price":79.99}}
54
+ ```
55
+
56
+ ## Core Functions
57
+
58
+ ### `prune()`
59
+
60
+ Removes fields you do not want to send to the model. By default it also removes `null`, `undefined`, and empty objects.
61
+
62
+ ```js
63
+ import { prune } from "tokenwise";
64
+
65
+ const input = {
66
+ user: { name: "John", age: 28 },
67
+ debug: true,
68
+ internal: { apiKey: "secret" }
69
+ };
70
+
71
+ console.log(prune(input, ["debug", "internal"]));
72
+ // { user: { name: "John", age: 28 } }
73
+ ```
74
+
75
+ ### `compact()`
76
+
77
+ Prunes empty/noisy values, then returns minified JSON.
78
+
79
+ ```js
80
+ compact({
81
+ product: {
82
+ name: "Wireless Headphones",
83
+ price: 79.99
84
+ }
85
+ });
86
+ // {"product":{"name":"Wireless Headphones","price":79.99}}
87
+ ```
88
+
89
+ ### `flatten()`
90
+
91
+ Converts nested objects and arrays into one object with dot-notation keys.
92
+
93
+ ```js
94
+ flatten({
95
+ policy: {
96
+ claims: [
97
+ { status: "approved", amount: 1200 },
98
+ { status: "pending", amount: 500 }
99
+ ]
100
+ }
101
+ });
102
+ // {
103
+ // "policy.claims.0.status": "approved",
104
+ // "policy.claims.0.amount": 1200,
105
+ // "policy.claims.1.status": "pending",
106
+ // "policy.claims.1.amount": 500
107
+ // }
108
+ ```
109
+
110
+ ### `toNatural()`
111
+
112
+ Converts JSON into readable sentences or numbered pointers.
113
+
114
+ ```js
115
+ toNatural([
116
+ {
117
+ user: {
118
+ name: "Alice Johnson",
119
+ email: "alice@example.com",
120
+ skills: ["Python", "JavaScript"]
121
+ }
122
+ }
123
+ ]);
124
+ // 1. User Alice Johnson (email: alice@example.com, Having Python and JavaScript).
125
+ ```
126
+
127
+ Medical and insurance-style data also becomes readable:
128
+
129
+ ```js
130
+ toNatural({
131
+ policy: {
132
+ holderName: "Carlos Rivera",
133
+ policyNumber: "HLT-2048",
134
+ claim: {
135
+ status: "under review",
136
+ requestedAmount: 64000
137
+ }
138
+ }
139
+ });
140
+ // policy: holder name Carlos Rivera, policy number HLT-2048, claim: status under review, requested amount 64000.
141
+ ```
142
+
143
+ ### `toTOON()`
144
+
145
+ Converts JSON into a compact TOON-like text format. Arrays of objects become table-style rows.
146
+
147
+ ```js
148
+ toTOON({
149
+ users: [
150
+ { id: 1, name: "Ali" },
151
+ { id: 2, name: "John" }
152
+ ]
153
+ });
154
+ // users:
155
+ // [2]{id,name}:
156
+ // 1,Ali
157
+ // 2,John
158
+ ```
159
+
160
+ If later rows contain extra keys, the schema includes them:
161
+
162
+ ```js
163
+ toTOON({
164
+ claims: [
165
+ { id: "C-1", status: "approved" },
166
+ { id: "C-2", amount: 500 }
167
+ ]
168
+ });
169
+ // claims:
170
+ // [2]{id,status,amount}:
171
+ // C-1,approved,
172
+ // C-2,,500
173
+ ```
174
+
175
+ ## Tested Use Cases
176
+
177
+ TokenWise currently has coverage for:
178
+
179
+ - Product JSON minification
180
+ - Dot-notation flattening
181
+ - Arrays and arrays of objects
182
+ - Medical patient and appointment data
183
+ - Insurance policy and claim data
184
+ - Natural-language user pointers
185
+ - TOON table formatting
186
+ - Null, undefined, and empty values
187
+
188
+ ## Why It Helps
189
+
190
+ LLMs charge and reason over tokens. Sending raw JSON often includes repeated keys, unnecessary metadata, and formatting whitespace. TokenWise gives you multiple ways to reshape the same data depending on your prompt:
191
+
192
+ - Use `compact()` when you need valid JSON with minimal whitespace.
193
+ - Use `flatten()` when retrieval, search, or simple key-value context is better.
194
+ - Use `toNatural()` when the model should read the data like human-friendly notes.
195
+ - Use `toTOON()` when arrays of objects should be shorter than repeated JSON.
196
+
197
+ ## CLI
198
+
199
+ ```bash
200
+ node demo.js
201
+ ```
202
+
203
+ You can visualize token optimization results using planned CLI/Web visual tools.
204
+
205
+ ---
206
+
207
+ ## 🚀 Roadmap
208
+
209
+ * [x] CLI support *(Coming Soon)*
210
+ * [ ] NPM Support
211
+ * [ ] Streaming support (GB+ data)
212
+ * [ ] Schema-aware optimization
213
+ * [ ] SaaS API
214
+
215
+ ---
216
+
217
+ ## 🛠 Comparison to Existing Tools *(Future Section)*
218
+
219
+ Highlight where **tokenwise** stands out, offering better compacting and token estimation features compared to other libraries.
220
+
221
+ ---
222
+
223
+ ## 🤝 Contributing
224
+
225
+ PRs are welcome,Feel free to open issues or submit PRs, Ideas are welcome for:
226
+
227
+ - Better token compression strategies
228
+ - Multi-model optimization
229
+ - Streaming CLI support
230
+
231
+ note: Explore the `CONTRIBUTING.md` for more details for the contributons.
232
+
233
+ ---
234
+
235
+ ## 📄 License
236
+
237
+ MIT License
238
+
239
+ ---
240
+
241
+ ## ❤️ Support This Project
242
+
243
+ TokenWise is built to help developers reduce LLM cost and improve efficiency.
244
+
245
+ If it helps you, consider supporting its development:
246
+
247
+ [![Support](https://img.shields.io/badge/❤️%20Support%20TokenWise-View-black?style=for-the-badge)](https://github.com/sponsors/sohqureshi)
248
+
249
+
250
+ ![npm version](https://img.shields.io/npm/v/tokenwise)
251
+ ![downloads](https://img.shields.io/npm/dw/tokenwise)
252
+ ![license](https://img.shields.io/github/license/sohqureshi/tokenwise)
253
+ ![stars](https://img.shields.io/github/stars/sohqureshi/tokenwise?style=social)
254
+ [![Demo](https://img.shields.io/badge/Live%20Demo-Visit-brightgreen)](https://sohqureshi.github.io/tokenwise/)
255
+ [![Support](https://img.shields.io/badge/❤️%20Support%20Tokenwise-View-black?style=for-the-badge)](https://github.com/sponsors/sohqureshi)
256
+ ---
257
+
258
+ ## 💡 Vision
259
+
260
+ Make AI cheaper and faster by optimizing data before it reaches the model.
@@ -0,0 +1,54 @@
1
+ import {
2
+ analyze
3
+ } from "./chunk-GQ62V6ZK.js";
4
+ import {
5
+ toTOON
6
+ } from "./chunk-3ECVBELU.js";
7
+ import {
8
+ compact
9
+ } from "./chunk-EUFLW42L.js";
10
+ import {
11
+ flatten
12
+ } from "./chunk-YMOSWQGE.js";
13
+ import {
14
+ toNatural
15
+ } from "./chunk-D4CFTFM3.js";
16
+ import {
17
+ prune
18
+ } from "./chunk-IAV75SZA.js";
19
+
20
+ // src/chain.ts
21
+ var AIChain = class {
22
+ constructor(data) {
23
+ this.data = data;
24
+ }
25
+ prune(options) {
26
+ this.data = prune(this.data, options);
27
+ return this;
28
+ }
29
+ compact() {
30
+ this.data = compact(this.data);
31
+ return this;
32
+ }
33
+ flatten() {
34
+ this.data = flatten(this.data);
35
+ return this;
36
+ }
37
+ toTOON() {
38
+ this.data = toTOON(this.data);
39
+ return this;
40
+ }
41
+ toNatural() {
42
+ return toNatural(this.data);
43
+ }
44
+ analyze() {
45
+ return analyze(this.data);
46
+ }
47
+ value() {
48
+ return this.data;
49
+ }
50
+ };
51
+
52
+ export {
53
+ AIChain
54
+ };
@@ -0,0 +1,288 @@
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
+ if (typeof v === "object" && v !== null) {
199
+ return formatPropertyClause(k, v, depth + 1);
200
+ }
201
+ const propValue = toNatural(v, depth + 1);
202
+ if (propValue === "yes" || propValue === "no") {
203
+ return `${camelToWords(k)} ${propValue === "yes" ? "enabled" : "disabled"}`;
204
+ }
205
+ return `${camelToWords(k)} ${propValue}`;
206
+ }).filter((c) => c && c.trim()).join(", ");
207
+ if (!nested) return null;
208
+ return `${naturalKey}: ${nested}`;
209
+ }
210
+ if (Array.isArray(value)) {
211
+ if (value.length === 0) return null;
212
+ if (["skills", "hobbies", "interests", "tags", "languages", "items"].includes(key)) {
213
+ const items2 = value.map((item) => {
214
+ const str = String(item);
215
+ return str.charAt(0).toUpperCase() + str.slice(1);
216
+ });
217
+ return `Having ${joinNaturalList(items2)}`;
218
+ }
219
+ const items = joinNaturalList(value.map((item) => toNatural(item, depth + 1)));
220
+ return `${naturalKey}: ${items}`;
221
+ }
222
+ const naturalValue = toNatural(value, depth + 1);
223
+ if (naturalValue === "yes") return `${naturalKey} enabled`;
224
+ if (naturalValue === "no") return `${naturalKey} disabled`;
225
+ if (key === "theme") return `prefers the ${naturalValue} ${key}`;
226
+ if (key === "age") return `age: ${naturalValue}`;
227
+ if (key === "email") return `email: ${naturalValue}`;
228
+ if (key === "city") return `address: ${naturalValue}`;
229
+ if (key === "debug" && naturalValue === "yes") return `debug enabled`;
230
+ return `${naturalKey}: ${naturalValue}`;
231
+ }
232
+ function camelToWords(str) {
233
+ return str.replace(/([A-Z])/g, " $1").toLowerCase().trim();
234
+ }
235
+ function isPlainObject(value) {
236
+ return typeof value === "object" && value !== null && !Array.isArray(value);
237
+ }
238
+ function joinNaturalList(items) {
239
+ if (items.length === 0) return "";
240
+ if (items.length === 1) return items[0];
241
+ if (items.length === 2) return `${items[0]} and ${items[1]}`;
242
+ const lastItem = items[items.length - 1];
243
+ return `${items.slice(0, -1).join(", ")} and ${lastItem}`;
244
+ }
245
+ function stripTrailingPeriod(value) {
246
+ return value.replace(/\.+$/, "");
247
+ }
248
+
249
+ // src/chain.ts
250
+ var AIChain = class {
251
+ constructor(data) {
252
+ this.data = data;
253
+ }
254
+ prune(options) {
255
+ this.data = prune(this.data, options);
256
+ return this;
257
+ }
258
+ compact() {
259
+ this.data = compact(this.data);
260
+ return this;
261
+ }
262
+ flatten() {
263
+ this.data = flatten(this.data);
264
+ return this;
265
+ }
266
+ toTOON() {
267
+ this.data = toTOON(this.data);
268
+ return this;
269
+ }
270
+ toNatural() {
271
+ return toNatural(this.data);
272
+ }
273
+ analyze() {
274
+ return analyze(this.data);
275
+ }
276
+ value() {
277
+ return this.data;
278
+ }
279
+ };
280
+
281
+ export {
282
+ prune,
283
+ compact,
284
+ toTOON,
285
+ analyze,
286
+ toNatural,
287
+ AIChain
288
+ };