foon-sdk 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 (219) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +574 -0
  3. package/dist/cache/LRUCache.d.ts +36 -0
  4. package/dist/cache/LRUCache.d.ts.map +1 -0
  5. package/dist/cache/LRUCache.js +54 -0
  6. package/dist/cache/LRUCache.js.map +1 -0
  7. package/dist/cache/cache-key-generator.d.ts +9 -0
  8. package/dist/cache/cache-key-generator.d.ts.map +1 -0
  9. package/dist/cache/cache-key-generator.js +50 -0
  10. package/dist/cache/cache-key-generator.js.map +1 -0
  11. package/dist/cache/index.d.ts +3 -0
  12. package/dist/cache/index.d.ts.map +1 -0
  13. package/dist/cache/index.js +21 -0
  14. package/dist/cache/index.js.map +1 -0
  15. package/dist/engine/MappingEngine.d.ts +29 -0
  16. package/dist/engine/MappingEngine.d.ts.map +1 -0
  17. package/dist/engine/MappingEngine.js +196 -0
  18. package/dist/engine/MappingEngine.js.map +1 -0
  19. package/dist/engine/executor.d.ts +15 -0
  20. package/dist/engine/executor.d.ts.map +1 -0
  21. package/dist/engine/executor.js +137 -0
  22. package/dist/engine/executor.js.map +1 -0
  23. package/dist/engine/index.d.ts +5 -0
  24. package/dist/engine/index.d.ts.map +1 -0
  25. package/dist/engine/index.js +23 -0
  26. package/dist/engine/index.js.map +1 -0
  27. package/dist/engine/jsonpath.d.ts +17 -0
  28. package/dist/engine/jsonpath.d.ts.map +1 -0
  29. package/dist/engine/jsonpath.js +73 -0
  30. package/dist/engine/jsonpath.js.map +1 -0
  31. package/dist/engine/type-converter.d.ts +12 -0
  32. package/dist/engine/type-converter.d.ts.map +1 -0
  33. package/dist/engine/type-converter.js +106 -0
  34. package/dist/engine/type-converter.js.map +1 -0
  35. package/dist/errors/error-factory.d.ts +30 -0
  36. package/dist/errors/error-factory.d.ts.map +1 -0
  37. package/dist/errors/error-factory.js +53 -0
  38. package/dist/errors/error-factory.js.map +1 -0
  39. package/dist/errors/index.d.ts +3 -0
  40. package/dist/errors/index.d.ts.map +1 -0
  41. package/dist/errors/index.js +22 -0
  42. package/dist/errors/index.js.map +1 -0
  43. package/dist/express/FonRouter.d.ts +56 -0
  44. package/dist/express/FonRouter.d.ts.map +1 -0
  45. package/dist/express/FonRouter.js +151 -0
  46. package/dist/express/FonRouter.js.map +1 -0
  47. package/dist/express/error-handler.d.ts +10 -0
  48. package/dist/express/error-handler.d.ts.map +1 -0
  49. package/dist/express/error-handler.js +55 -0
  50. package/dist/express/error-handler.js.map +1 -0
  51. package/dist/express/index.d.ts +33 -0
  52. package/dist/express/index.d.ts.map +1 -0
  53. package/dist/express/index.js +41 -0
  54. package/dist/express/index.js.map +1 -0
  55. package/dist/express/route-registry.d.ts +33 -0
  56. package/dist/express/route-registry.d.ts.map +1 -0
  57. package/dist/express/route-registry.js +52 -0
  58. package/dist/express/route-registry.js.map +1 -0
  59. package/dist/express/transform-middleware.d.ts +21 -0
  60. package/dist/express/transform-middleware.d.ts.map +1 -0
  61. package/dist/express/transform-middleware.js +62 -0
  62. package/dist/express/transform-middleware.js.map +1 -0
  63. package/dist/express/types.d.ts +73 -0
  64. package/dist/express/types.d.ts.map +1 -0
  65. package/dist/express/types.js +3 -0
  66. package/dist/express/types.js.map +1 -0
  67. package/dist/index.d.ts +11 -0
  68. package/dist/index.d.ts.map +1 -0
  69. package/dist/index.js +29 -0
  70. package/dist/index.js.map +1 -0
  71. package/dist/providers/base/Provider.d.ts +17 -0
  72. package/dist/providers/base/Provider.d.ts.map +1 -0
  73. package/dist/providers/base/Provider.js +43 -0
  74. package/dist/providers/base/Provider.js.map +1 -0
  75. package/dist/providers/base/index.d.ts +3 -0
  76. package/dist/providers/base/index.d.ts.map +1 -0
  77. package/dist/providers/base/index.js +21 -0
  78. package/dist/providers/base/index.js.map +1 -0
  79. package/dist/providers/base/prompt-builder.d.ts +22 -0
  80. package/dist/providers/base/prompt-builder.d.ts.map +1 -0
  81. package/dist/providers/base/prompt-builder.js +105 -0
  82. package/dist/providers/base/prompt-builder.js.map +1 -0
  83. package/dist/providers/gemini/GeminiProvider.d.ts +16 -0
  84. package/dist/providers/gemini/GeminiProvider.d.ts.map +1 -0
  85. package/dist/providers/gemini/GeminiProvider.js +48 -0
  86. package/dist/providers/gemini/GeminiProvider.js.map +1 -0
  87. package/dist/providers/gemini/gemini-client.d.ts +16 -0
  88. package/dist/providers/gemini/gemini-client.d.ts.map +1 -0
  89. package/dist/providers/gemini/gemini-client.js +83 -0
  90. package/dist/providers/gemini/gemini-client.js.map +1 -0
  91. package/dist/providers/gemini/index.d.ts +3 -0
  92. package/dist/providers/gemini/index.d.ts.map +1 -0
  93. package/dist/providers/gemini/index.js +8 -0
  94. package/dist/providers/gemini/index.js.map +1 -0
  95. package/dist/providers/index.d.ts +6 -0
  96. package/dist/providers/index.d.ts.map +1 -0
  97. package/dist/providers/index.js +27 -0
  98. package/dist/providers/index.js.map +1 -0
  99. package/dist/providers/ollama/OllamaProvider.d.ts +17 -0
  100. package/dist/providers/ollama/OllamaProvider.d.ts.map +1 -0
  101. package/dist/providers/ollama/OllamaProvider.js +47 -0
  102. package/dist/providers/ollama/OllamaProvider.js.map +1 -0
  103. package/dist/providers/ollama/index.d.ts +3 -0
  104. package/dist/providers/ollama/index.d.ts.map +1 -0
  105. package/dist/providers/ollama/index.js +8 -0
  106. package/dist/providers/ollama/index.js.map +1 -0
  107. package/dist/providers/ollama/ollama-client.d.ts +17 -0
  108. package/dist/providers/ollama/ollama-client.d.ts.map +1 -0
  109. package/dist/providers/ollama/ollama-client.js +83 -0
  110. package/dist/providers/ollama/ollama-client.js.map +1 -0
  111. package/dist/providers/openai/OpenAIProvider.d.ts +16 -0
  112. package/dist/providers/openai/OpenAIProvider.d.ts.map +1 -0
  113. package/dist/providers/openai/OpenAIProvider.js +48 -0
  114. package/dist/providers/openai/OpenAIProvider.js.map +1 -0
  115. package/dist/providers/openai/index.d.ts +3 -0
  116. package/dist/providers/openai/index.d.ts.map +1 -0
  117. package/dist/providers/openai/index.js +8 -0
  118. package/dist/providers/openai/index.js.map +1 -0
  119. package/dist/providers/openai/openai-client.d.ts +16 -0
  120. package/dist/providers/openai/openai-client.d.ts.map +1 -0
  121. package/dist/providers/openai/openai-client.js +85 -0
  122. package/dist/providers/openai/openai-client.js.map +1 -0
  123. package/dist/schema/SchemaAdapter.d.ts +34 -0
  124. package/dist/schema/SchemaAdapter.d.ts.map +1 -0
  125. package/dist/schema/SchemaAdapter.js +59 -0
  126. package/dist/schema/SchemaAdapter.js.map +1 -0
  127. package/dist/schema/field-extractor.d.ts +10 -0
  128. package/dist/schema/field-extractor.d.ts.map +1 -0
  129. package/dist/schema/field-extractor.js +58 -0
  130. package/dist/schema/field-extractor.js.map +1 -0
  131. package/dist/schema/index.d.ts +5 -0
  132. package/dist/schema/index.d.ts.map +1 -0
  133. package/dist/schema/index.js +13 -0
  134. package/dist/schema/index.js.map +1 -0
  135. package/dist/schema/normalizer.d.ts +6 -0
  136. package/dist/schema/normalizer.d.ts.map +1 -0
  137. package/dist/schema/normalizer.js +17 -0
  138. package/dist/schema/normalizer.js.map +1 -0
  139. package/dist/schema/validator.d.ts +17 -0
  140. package/dist/schema/validator.d.ts.map +1 -0
  141. package/dist/schema/validator.js +50 -0
  142. package/dist/schema/validator.js.map +1 -0
  143. package/dist/security/index.d.ts +5 -0
  144. package/dist/security/index.d.ts.map +1 -0
  145. package/dist/security/index.js +21 -0
  146. package/dist/security/index.js.map +1 -0
  147. package/dist/security/input-validator.d.ts +6 -0
  148. package/dist/security/input-validator.d.ts.map +1 -0
  149. package/dist/security/input-validator.js +76 -0
  150. package/dist/security/input-validator.js.map +1 -0
  151. package/dist/security/prompt-sanitizer.d.ts +12 -0
  152. package/dist/security/prompt-sanitizer.d.ts.map +1 -0
  153. package/dist/security/prompt-sanitizer.js +66 -0
  154. package/dist/security/prompt-sanitizer.js.map +1 -0
  155. package/dist/security/redactor.d.ts +5 -0
  156. package/dist/security/redactor.d.ts.map +1 -0
  157. package/dist/security/redactor.js +37 -0
  158. package/dist/security/redactor.js.map +1 -0
  159. package/dist/security/security-config.d.ts +10 -0
  160. package/dist/security/security-config.d.ts.map +1 -0
  161. package/dist/security/security-config.js +26 -0
  162. package/dist/security/security-config.js.map +1 -0
  163. package/dist/trace/TraceBuilder.d.ts +58 -0
  164. package/dist/trace/TraceBuilder.d.ts.map +1 -0
  165. package/dist/trace/TraceBuilder.js +117 -0
  166. package/dist/trace/TraceBuilder.js.map +1 -0
  167. package/dist/trace/confidence-analyzer.d.ts +6 -0
  168. package/dist/trace/confidence-analyzer.d.ts.map +1 -0
  169. package/dist/trace/confidence-analyzer.js +30 -0
  170. package/dist/trace/confidence-analyzer.js.map +1 -0
  171. package/dist/trace/index.d.ts +4 -0
  172. package/dist/trace/index.d.ts.map +1 -0
  173. package/dist/trace/index.js +22 -0
  174. package/dist/trace/index.js.map +1 -0
  175. package/dist/trace/trace-utils.d.ts +20 -0
  176. package/dist/trace/trace-utils.d.ts.map +1 -0
  177. package/dist/trace/trace-utils.js +33 -0
  178. package/dist/trace/trace-utils.js.map +1 -0
  179. package/dist/transform.d.ts +30 -0
  180. package/dist/transform.d.ts.map +1 -0
  181. package/dist/transform.js +62 -0
  182. package/dist/transform.js.map +1 -0
  183. package/dist/types/cache.types.d.ts +41 -0
  184. package/dist/types/cache.types.d.ts.map +1 -0
  185. package/dist/types/cache.types.js +3 -0
  186. package/dist/types/cache.types.js.map +1 -0
  187. package/dist/types/errors.types.d.ts +22 -0
  188. package/dist/types/errors.types.d.ts.map +1 -0
  189. package/dist/types/errors.types.js +31 -0
  190. package/dist/types/errors.types.js.map +1 -0
  191. package/dist/types/index.d.ts +9 -0
  192. package/dist/types/index.d.ts.map +1 -0
  193. package/dist/types/index.js +8 -0
  194. package/dist/types/index.js.map +1 -0
  195. package/dist/types/mapping.types.d.ts +48 -0
  196. package/dist/types/mapping.types.d.ts.map +1 -0
  197. package/dist/types/mapping.types.js +3 -0
  198. package/dist/types/mapping.types.js.map +1 -0
  199. package/dist/types/provider.types.d.ts +62 -0
  200. package/dist/types/provider.types.d.ts.map +1 -0
  201. package/dist/types/provider.types.js +3 -0
  202. package/dist/types/provider.types.js.map +1 -0
  203. package/dist/types/schema.types.d.ts +68 -0
  204. package/dist/types/schema.types.d.ts.map +1 -0
  205. package/dist/types/schema.types.js +3 -0
  206. package/dist/types/schema.types.js.map +1 -0
  207. package/dist/types/security.types.d.ts +32 -0
  208. package/dist/types/security.types.d.ts.map +1 -0
  209. package/dist/types/security.types.js +3 -0
  210. package/dist/types/security.types.js.map +1 -0
  211. package/dist/types/trace.types.d.ts +129 -0
  212. package/dist/types/trace.types.d.ts.map +1 -0
  213. package/dist/types/trace.types.js +3 -0
  214. package/dist/types/trace.types.js.map +1 -0
  215. package/dist/types/transform.types.d.ts +56 -0
  216. package/dist/types/transform.types.d.ts.map +1 -0
  217. package/dist/types/transform.types.js +3 -0
  218. package/dist/types/transform.types.js.map +1 -0
  219. package/package.json +107 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Achraf Achkari
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,574 @@
1
+ # FOON SDK
2
+
3
+ > TypeScript SDK for semantic JSON transformation using LLMs
4
+
5
+ FOON SDK transforms free-form JSON to schema-compliant JSON using AI-powered semantic mapping. It accepts structurally incorrect but semantically correct JSON and transforms it to match your target schema.
6
+
7
+ ## Key Features
8
+
9
+ - **Semantic Mapping**: Uses LLMs to understand intent and map fields intelligently
10
+ - **Schema Validation**: Ensures output always matches your JSON Schema
11
+ - **Explainability**: Comprehensive trace of all mapping decisions with confidence scores
12
+ - **Type Safety**: Full TypeScript support with type definitions
13
+ - **Caching**: Built-in LRU cache reduces API calls for repeated patterns
14
+ - **Security**: Input validation, redaction, and prompt injection protection
15
+ - **Multiple Providers**: Supports Gemini, OpenAI, and Ollama
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install foon-sdk
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ```typescript
26
+ import { transform, OpenAIProvider } from 'foon-sdk';
27
+
28
+ // Define your target schema
29
+ const userSchema = {
30
+ type: 'object',
31
+ properties: {
32
+ name: {
33
+ type: 'object',
34
+ properties: {
35
+ given: { type: 'string' },
36
+ family: { type: 'string' },
37
+ },
38
+ required: ['given', 'family'],
39
+ },
40
+ email: { type: 'string', format: 'email' },
41
+ },
42
+ required: ['name', 'email'],
43
+ };
44
+
45
+ // Transform messy input
46
+ const result = await transform(
47
+ {
48
+ firstname: 'John',
49
+ lastname: 'Doe',
50
+ email: 'john.doe@example.com',
51
+ },
52
+ {
53
+ schema: userSchema,
54
+ provider: new OpenAIProvider({
55
+ apiKey: process.env.OPENAI_API_KEY,
56
+ model: 'gpt-5-nano',
57
+ }),
58
+ confidenceThreshold: 0.85,
59
+ }
60
+ );
61
+
62
+ if (result.ok) {
63
+ console.log('Output:', result.output);
64
+ // Output: { name: { given: 'John', family: 'Doe' }, email: 'john.doe@example.com' }
65
+ } else {
66
+ console.error('Error:', result.error);
67
+ console.log('Trace:', result.trace);
68
+ }
69
+ ```
70
+
71
+ ## Express Middleware
72
+
73
+ FOON SDK includes Express middleware for seamless integration with Express applications.
74
+
75
+ ### Quick Start
76
+
77
+ ```typescript
78
+ import express from 'express';
79
+ import { createFonRouter } from 'foon-sdk/express';
80
+ import { OpenAIProvider } from 'foon-sdk';
81
+
82
+ const app = express();
83
+ app.use(express.json());
84
+
85
+ // Create FOON router
86
+ const fonRouter = createFonRouter({
87
+ provider: new OpenAIProvider({
88
+ apiKey: process.env.OPENAI_API_KEY,
89
+ model: 'gpt-5-nano'
90
+ }),
91
+ prefix: '/foon', // Default: '/foon'
92
+ confidenceThreshold: 0.85, // Default: 0.85
93
+ });
94
+
95
+ // Register routes with schemas
96
+ fonRouter.post('/users', {
97
+ schema: userCreateSchema,
98
+ handler: (req, res) => {
99
+ // req.body is now transformed and validated
100
+ res.json({ success: true, user: req.body });
101
+ },
102
+ });
103
+
104
+ fonRouter.put('/users/:id', {
105
+ schema: userUpdateSchema,
106
+ handler: updateUserHandler,
107
+ });
108
+
109
+ // Mount the router
110
+ app.use(fonRouter.getRouter());
111
+
112
+ app.listen(3000);
113
+ ```
114
+
115
+ ### How It Works
116
+
117
+ The Express middleware creates **two routes** for each registration:
118
+
119
+ 1. **Original route** (e.g., `/users`) - Works normally without transformation
120
+ 2. **FOON route** (e.g., `/foon/users`) - Applies FOON transformation before handler
121
+
122
+ Requests to FOON routes:
123
+
124
+ - Transform `req.body` using the configured schema
125
+ - Replace `req.body` with validated output
126
+ - Add trace ID header (`X-FON-Trace-Id`)
127
+ - Forward to the same handler
128
+
129
+ ### Configuration
130
+
131
+ ```typescript
132
+ interface FonRouterConfig {
133
+ provider: Provider; // LLM provider (required)
134
+ prefix?: string; // Route prefix (default: '/foon')
135
+ methods?: HttpMethod[]; // Methods to transform (default: ['POST', 'PUT', 'PATCH'])
136
+ confidenceThreshold?: number; // Confidence threshold (default: 0.85)
137
+ cache?: Cache; // Cache instance
138
+ security?: SecurityOptions; // Security options
139
+ traceHeader?: string; // Trace header name (default: 'X-FON-Trace-Id')
140
+ onError?: ErrorHandler; // Custom error handler
141
+ createOriginalRoutes?: boolean; // Create original routes (default: true)
142
+ verbose?: boolean; // Verbose logging (default: false)
143
+ }
144
+ ```
145
+
146
+ ### Route Configuration
147
+
148
+ ```typescript
149
+ interface RouteConfig {
150
+ schema: object; // JSON Schema for this route
151
+ handler: RequestHandler; // Express handler
152
+ createOriginal?: boolean; // Override: create original route
153
+ confidenceThreshold?: number; // Override: confidence threshold for this route
154
+ }
155
+ ```
156
+
157
+ ### Examples
158
+
159
+ #### Basic Usage
160
+
161
+ ```typescript
162
+ fonRouter.post('/users', {
163
+ schema: userSchema,
164
+ handler: (req, res) => {
165
+ // req.body is transformed and validated
166
+ res.json(req.body);
167
+ },
168
+ });
169
+
170
+ // Now you have:
171
+ // POST /users - Original route (untransformed)
172
+ // POST /foon/users - FOON route (transformed)
173
+ ```
174
+
175
+ #### Custom Error Handler
176
+
177
+ ```typescript
178
+ const fonRouter = createFonRouter({
179
+ provider,
180
+ onError: (error, req, res, next) => {
181
+ res.status(400).json({
182
+ error: error.category,
183
+ message: error.message,
184
+ traceId: error.traceId,
185
+ // Include trace for debugging
186
+ trace: req.fonTrace,
187
+ });
188
+ },
189
+ });
190
+ ```
191
+
192
+ #### Disable Original Routes
193
+
194
+ ```typescript
195
+ const fonRouter = createFonRouter({
196
+ provider,
197
+ createOriginalRoutes: false, // Only create FOON routes
198
+ });
199
+
200
+ // Now only /foon/users exists, not /users
201
+ ```
202
+
203
+ #### Custom Prefix
204
+
205
+ ```typescript
206
+ const fonRouter = createFonRouter({
207
+ provider,
208
+ prefix: '/api/semantic',
209
+ });
210
+
211
+ fonRouter.post('/users', { schema, handler });
212
+ // Creates: POST /api/semantic/users
213
+ ```
214
+
215
+ #### Per-Route Configuration
216
+
217
+ ```typescript
218
+ fonRouter.post('/users', {
219
+ schema: userSchema,
220
+ handler: createUserHandler,
221
+ createOriginal: false, // Don't create /users for this route
222
+ confidenceThreshold: 0.9, // Higher threshold for this route
223
+ });
224
+ ```
225
+
226
+ ### Trace Headers
227
+
228
+ FOON routes automatically add headers to responses:
229
+
230
+ - `X-FON-Trace-Id`: Unique trace ID for debugging
231
+ - `X-FON-Timing-Total`: Total processing time (verbose mode)
232
+ - `X-FON-Timing-Proposal`: LLM call time (verbose mode)
233
+ - `X-FON-Cache-Hit`: Whether cache was hit (verbose mode)
234
+
235
+ ### Error Handling
236
+
237
+ When transformation fails, the default error handler returns:
238
+
239
+ ```json
240
+ {
241
+ "error": "CONFIDENCE_TOO_LOW",
242
+ "message": "1 assignment(s) below confidence threshold 0.85",
243
+ "traceId": "uuid",
244
+ "details": { ... }
245
+ }
246
+ ```
247
+
248
+ HTTP status codes:
249
+
250
+ - `400` - Bad Request (validation error, confidence too low)
251
+ - `413` - Payload Too Large (security limits exceeded)
252
+ - `500` - Internal Server Error (execution error)
253
+ - `502` - Bad Gateway (provider error)
254
+
255
+ ### Accessing Trace Data
256
+
257
+ The trace is attached to the request object:
258
+
259
+ ```typescript
260
+ fonRouter.post('/users', {
261
+ schema: userSchema,
262
+ handler: (req, res) => {
263
+ const trace = (req as any).fonTrace;
264
+ console.log('Confidence:', trace.confidenceSummary);
265
+ console.log('Timings:', trace.timings);
266
+ res.json(req.body);
267
+ },
268
+ });
269
+ ```
270
+
271
+ ## How It Works
272
+
273
+ 1. **LLM Generates Mapping Plan**: The LLM analyzes your input and target schema, returning a mapping plan (not the final JSON)
274
+ 2. **Deterministic Execution**: The SDK applies the mapping plan using JSONPath resolution and safe type conversions
275
+ 3. **Validation**: Output is validated against your schema with detailed error reporting
276
+ 4. **Trace Output**: Complete trace includes confidence scores, warnings, dropped fields, and timing
277
+
278
+ ### Key Principle
279
+
280
+ FOON SDK does NOT let the LLM generate the final JSON. Instead:
281
+
282
+ - LLM returns a **mapping plan** with confidence scores
283
+ - SDK executes the plan **deterministically**
284
+ - Output is **always validated** against your schema
285
+
286
+ This approach ensures reliability, explainability, and prevents hallucinations.
287
+
288
+ ## API Reference
289
+
290
+ ### `transform(input, options)`
291
+
292
+ Main transformation function.
293
+
294
+ **Parameters:**
295
+
296
+ - `input: unknown` - The input JSON to transform
297
+ - `options: TransformOptions` - Transformation options
298
+
299
+ **Options:**
300
+
301
+ ```typescript
302
+ interface TransformOptions {
303
+ mode?: 'SEMANTIC'; // Only SEMANTIC mode in v1.0
304
+ schema: object | SchemaAdapter; // JSON Schema object
305
+ provider: Provider; // LLM provider instance
306
+ confidenceThreshold?: number; // Default: 0.85
307
+ cache?: Cache; // Optional cache instance
308
+ verbose?: boolean; // Enable verbose output
309
+ security?: SecurityOptions; // Security settings
310
+ hooks?: TransformHooks; // Observability hooks
311
+ }
312
+ ```
313
+
314
+ **Returns:** `Promise<TransformResult>`
315
+
316
+ ```typescript
317
+ interface TransformResult {
318
+ ok: boolean; // Success flag
319
+ output?: unknown; // Transformed output (if successful)
320
+ error?: FONError; // Error details (if failed)
321
+ trace: TraceReport; // Complete trace report
322
+ }
323
+ ```
324
+
325
+ ### Providers
326
+
327
+ #### GeminiProvider
328
+
329
+ ```typescript
330
+ import { GeminiProvider } from 'foon-sdk';
331
+
332
+ const provider = new GeminiProvider({
333
+ apiKey: process.env.GEMINI_API_KEY,
334
+ model: 'gemini-1.5-flash', // Optional, default: gemini-1.5-flash
335
+ timeout: 30000, // Optional, default: 30s
336
+ });
337
+ ```
338
+
339
+ #### OpenAIProvider
340
+
341
+ ```typescript
342
+ import { OpenAIProvider } from 'foon-sdk';
343
+
344
+ const provider = new OpenAIProvider({
345
+ apiKey: process.env.OPENAI_API_KEY,
346
+ model: 'gpt-4o-mini', // Optional, default: gpt-4o-mini
347
+ timeout: 30000, // Optional, default: 30s
348
+ baseUrl: 'https://api.openai.com/v1', // Optional, for custom endpoints
349
+ });
350
+ ```
351
+
352
+ #### OllamaProvider
353
+
354
+ ```typescript
355
+ import { OllamaProvider } from 'foon-sdk';
356
+
357
+ const provider = new OllamaProvider({
358
+ apiKey: process.env.OLLAMA_API_KEY, // Optional for local Ollama
359
+ model: 'llama2', // Optional, default: llama2
360
+ timeout: 60000, // Optional, default: 30s (Ollama can be slower)
361
+ baseUrl: 'http://localhost:11434', // Optional, default: http://localhost:11434
362
+ });
363
+ ```
364
+
365
+ **Note:** Ollama can run locally without an API key. If using a hosted Ollama instance, provide the `apiKey` and `baseUrl`.
366
+
367
+ ### Security Options
368
+
369
+ ```typescript
370
+ const result = await transform(input, {
371
+ schema,
372
+ provider,
373
+ security: {
374
+ maxInputSize: 1024 * 1024, // 1MB default
375
+ maxDepth: 10, // Max nesting depth
376
+ maxKeys: 1000, // Max number of keys
377
+ redactKeys: ['password', 'token'], // Keys to redact
378
+ sanitizePrompt: true, // Prompt injection protection
379
+ includeValues: false, // Don't send values to LLM (structure only)
380
+ },
381
+ });
382
+ ```
383
+
384
+ ## Examples
385
+
386
+ ### Type Conversion
387
+
388
+ ```typescript
389
+ const result = await transform(
390
+ { age: '30' }, // String instead of number
391
+ {
392
+ schema: {
393
+ type: 'object',
394
+ properties: {
395
+ age: { type: 'integer' },
396
+ },
397
+ },
398
+ provider,
399
+ }
400
+ );
401
+ // Output: { age: 30 } - Automatically converted to number
402
+ ```
403
+
404
+ ### Nested Field Mapping
405
+
406
+ ```typescript
407
+ const result = await transform(
408
+ {
409
+ city: 'San Francisco',
410
+ country: 'USA',
411
+ },
412
+ {
413
+ schema: {
414
+ type: 'object',
415
+ properties: {
416
+ address: {
417
+ type: 'object',
418
+ properties: {
419
+ city: { type: 'string' },
420
+ country: { type: 'string' },
421
+ },
422
+ },
423
+ },
424
+ },
425
+ provider,
426
+ }
427
+ );
428
+ // Output: { address: { city: 'San Francisco', country: 'USA' } }
429
+ ```
430
+
431
+ ### Confidence Threshold
432
+
433
+ ```typescript
434
+ const result = await transform(input, {
435
+ schema,
436
+ provider,
437
+ confidenceThreshold: 0.9, // Fail if any mapping < 90% confidence
438
+ });
439
+
440
+ if (!result.ok && result.error?.category === 'CONFIDENCE_TOO_LOW') {
441
+ console.log('Rejected assignments:', result.trace.execution.assignmentsRejected);
442
+ }
443
+ ```
444
+
445
+ ### Using Cache
446
+
447
+ ```typescript
448
+ import { transform, OpenAIProvider, LRUCache } from 'foon-sdk';
449
+
450
+ const cache = new LRUCache({ max: 100, ttl: 3600000 }); // 100 entries, 1 hour TTL
451
+
452
+ const result = await transform(input, {
453
+ schema,
454
+ provider: new OpenAIProvider({
455
+ apiKey: process.env.OPENAI_API_KEY,
456
+ model: 'gpt-5-nano',
457
+ }),
458
+ cache,
459
+ });
460
+
461
+ console.log('Cache hit:', result.trace.cache.hit);
462
+ ```
463
+
464
+ ## Error Handling
465
+
466
+ FOON SDK provides detailed error categories:
467
+
468
+ ```typescript
469
+ if (!result.ok) {
470
+ switch (result.error?.category) {
471
+ case 'SCHEMA_LOAD_ERROR':
472
+ // Invalid schema
473
+ break;
474
+ case 'PROVIDER_ERROR':
475
+ // LLM API error
476
+ break;
477
+ case 'MAPPING_PLAN_PARSE_ERROR':
478
+ // LLM returned invalid mapping plan
479
+ break;
480
+ case 'CONFIDENCE_TOO_LOW':
481
+ // Mapping confidence below threshold
482
+ break;
483
+ case 'EXECUTION_ERROR':
484
+ // Error applying mapping plan
485
+ break;
486
+ case 'VALIDATION_ERROR':
487
+ // Output doesn't match schema
488
+ break;
489
+ case 'SECURITY_LIMIT_EXCEEDED':
490
+ // Input exceeded security limits
491
+ break;
492
+ }
493
+ }
494
+ ```
495
+
496
+ ## Trace Output
497
+
498
+ Every transformation includes a comprehensive trace:
499
+
500
+ ```typescript
501
+ interface TraceReport {
502
+ traceId: string; // Unique trace ID
503
+ mode: 'SEMANTIC';
504
+ provider: string; // Provider name
505
+ promptVersion: string; // Prompt version
506
+ timings: {
507
+ total: number; // Total time (ms)
508
+ proposal: number; // LLM call time
509
+ execution: number; // Execution time
510
+ validation: number; // Validation time
511
+ };
512
+ mappingPlan: {
513
+ raw: string; // Raw LLM response
514
+ parsed: MappingPlan; // Parsed mapping plan
515
+ };
516
+ execution: {
517
+ assignmentsApplied: AssignmentTrace[];
518
+ assignmentsRejected: RejectedAssignment[];
519
+ droppedFields: Drop[];
520
+ warnings: Warning[];
521
+ conflicts: Conflict[];
522
+ };
523
+ validation: {
524
+ success: boolean;
525
+ errors: ValidationError[];
526
+ };
527
+ cache: {
528
+ hit: boolean;
529
+ key: string;
530
+ };
531
+ confidenceSummary: {
532
+ min: number;
533
+ max: number;
534
+ avg: number;
535
+ countBelowThreshold: number;
536
+ threshold: number;
537
+ };
538
+ }
539
+ ```
540
+
541
+ ## Testing
542
+
543
+ ```bash
544
+ # Run deterministic tests (unit + integration, no external providers)
545
+ npm test
546
+
547
+ # Run all tests (including e2e provider tests)
548
+ npm run test:all
549
+
550
+ # Unit tests only
551
+ npm run test:unit
552
+
553
+ # Integration tests
554
+ npm run test:integration
555
+
556
+ # E2E tests (requires provider credentials in .env.local)
557
+ npm run test:e2e
558
+ ```
559
+
560
+ ## Building
561
+
562
+ ```bash
563
+ npm run build
564
+ ```
565
+
566
+ ## License
567
+
568
+ MIT License - Copyright (c) 2026 Achraf Achkari
569
+
570
+ See [LICENSE](LICENSE) for details.
571
+
572
+ ## Contributing
573
+
574
+ Issues and pull requests are welcome.
@@ -0,0 +1,36 @@
1
+ import { Cache as ICache, CachedMappingPlan } from '../types';
2
+ /**
3
+ * LRU Cache implementation
4
+ */
5
+ export declare class LRUCache implements ICache {
6
+ private cache;
7
+ constructor(options?: {
8
+ max?: number;
9
+ ttl?: number;
10
+ });
11
+ /**
12
+ * Get cached mapping plan
13
+ */
14
+ get(key: string): Promise<CachedMappingPlan | null>;
15
+ /**
16
+ * Store mapping plan in cache
17
+ */
18
+ set(key: string, value: CachedMappingPlan, ttl?: number): Promise<void>;
19
+ /**
20
+ * Check if key exists
21
+ */
22
+ has(key: string): Promise<boolean>;
23
+ /**
24
+ * Delete entry
25
+ */
26
+ delete(key: string): Promise<boolean>;
27
+ /**
28
+ * Clear all entries
29
+ */
30
+ clear(): Promise<void>;
31
+ /**
32
+ * Get cache size
33
+ */
34
+ size(): number;
35
+ }
36
+ //# sourceMappingURL=LRUCache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LRUCache.d.ts","sourceRoot":"","sources":["../../src/cache/LRUCache.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,IAAI,MAAM,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAE9D;;GAEG;AACH,qBAAa,QAAS,YAAW,MAAM;IACrC,OAAO,CAAC,KAAK,CAAiC;gBAElC,OAAO,GAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAO;IAOxD;;OAEG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAKzD;;OAEG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7E;;OAEG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIxC;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAI3C;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B;;OAEG;IACH,IAAI,IAAI,MAAM;CAGf"}
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LRUCache = void 0;
4
+ const lru_cache_1 = require("lru-cache");
5
+ /**
6
+ * LRU Cache implementation
7
+ */
8
+ class LRUCache {
9
+ constructor(options = {}) {
10
+ this.cache = new lru_cache_1.LRUCache({
11
+ max: options.max || 100, // Default: 100 entries
12
+ ttl: options.ttl || 1000 * 60 * 60, // Default: 1 hour
13
+ });
14
+ }
15
+ /**
16
+ * Get cached mapping plan
17
+ */
18
+ async get(key) {
19
+ const value = this.cache.get(key);
20
+ return value || null;
21
+ }
22
+ /**
23
+ * Store mapping plan in cache
24
+ */
25
+ async set(key, value, ttl) {
26
+ this.cache.set(key, value, { ttl });
27
+ }
28
+ /**
29
+ * Check if key exists
30
+ */
31
+ async has(key) {
32
+ return this.cache.has(key);
33
+ }
34
+ /**
35
+ * Delete entry
36
+ */
37
+ async delete(key) {
38
+ return this.cache.delete(key);
39
+ }
40
+ /**
41
+ * Clear all entries
42
+ */
43
+ async clear() {
44
+ this.cache.clear();
45
+ }
46
+ /**
47
+ * Get cache size
48
+ */
49
+ size() {
50
+ return this.cache.size;
51
+ }
52
+ }
53
+ exports.LRUCache = LRUCache;
54
+ //# sourceMappingURL=LRUCache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LRUCache.js","sourceRoot":"","sources":["../../src/cache/LRUCache.ts"],"names":[],"mappings":";;;AAAA,yCAA4C;AAG5C;;GAEG;AACH,MAAa,QAAQ;IAGnB,YAAY,UAA0C,EAAE;QACtD,IAAI,CAAC,KAAK,GAAG,IAAI,oBAAG,CAA4B;YAC9C,GAAG,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,EAAE,uBAAuB;YAChD,GAAG,EAAE,OAAO,CAAC,GAAG,IAAI,IAAI,GAAG,EAAE,GAAG,EAAE,EAAE,kBAAkB;SACvD,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,OAAO,KAAK,IAAI,IAAI,CAAC;IACvB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,KAAwB,EAAE,GAAY;QAC3D,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;IACtC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,IAAI;QACF,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACzB,CAAC;CACF;AApDD,4BAoDC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Generate stable cache key from inputs
3
+ */
4
+ export declare function generateCacheKey(schemaVersion: string, inputSignature: string, confidenceThreshold: number, providerName: string, promptVersion: string): string;
5
+ /**
6
+ * Generate input signature (hash of keys + structure, not values)
7
+ */
8
+ export declare function generateInputSignature(input: unknown): string;
9
+ //# sourceMappingURL=cache-key-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-key-generator.d.ts","sourceRoot":"","sources":["../../src/cache/cache-key-generator.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,EACtB,mBAAmB,EAAE,MAAM,EAC3B,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,GACpB,MAAM,CAKR;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAI7D"}