@jterrazz/intelligence 3.0.0 → 3.0.2
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/README.md +15 -8
- package/dist/index.cjs +31 -10
- package/dist/middleware/__tests__/logging.middleware.test.js +77 -4
- package/dist/middleware/__tests__/logging.middleware.test.js.map +1 -1
- package/dist/middleware/logging.middleware.d.ts +10 -2
- package/dist/middleware/logging.middleware.js +7 -5
- package/dist/middleware/logging.middleware.js.map +1 -1
- package/dist/parsing/__tests__/parse-object.test.js +242 -2
- package/dist/parsing/__tests__/parse-object.test.js.map +1 -1
- package/dist/parsing/__tests__/parse-text.test.js +13 -5
- package/dist/parsing/__tests__/parse-text.test.js.map +1 -1
- package/dist/parsing/parse-object.js +23 -0
- package/dist/parsing/parse-object.js.map +1 -1
- package/dist/parsing/parse-text.js +1 -5
- package/dist/parsing/parse-text.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -56,10 +56,15 @@ Removes invisible characters, normalizes typography, and cleans common AI artifa
|
|
|
56
56
|
```typescript
|
|
57
57
|
import { parseText } from '@jterrazz/intelligence';
|
|
58
58
|
|
|
59
|
-
const clean = parseText(messyAiOutput
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
const clean = parseText(messyAiOutput);
|
|
60
|
+
// Removes: BOM, zero-width chars, AI citation markers
|
|
61
|
+
// Normalizes: smart quotes → straight, em dashes → ", ", ellipsis → ...
|
|
62
|
+
// Collapses multiple spaces and trims
|
|
63
|
+
|
|
64
|
+
// Options
|
|
65
|
+
parseText(text, {
|
|
66
|
+
normalizeEmDashesToCommas: true, // Convert em/en dashes to ", " (default: true)
|
|
67
|
+
collapseSpaces: true, // Collapse multiple spaces, trim (default: true)
|
|
63
68
|
});
|
|
64
69
|
```
|
|
65
70
|
|
|
@@ -102,11 +107,13 @@ const model = wrapLanguageModel({
|
|
|
102
107
|
model: provider.model('anthropic/claude-sonnet-4-20250514'),
|
|
103
108
|
middleware: createLoggingMiddleware({
|
|
104
109
|
logger, // Any logger with debug/error methods
|
|
105
|
-
|
|
110
|
+
include: {
|
|
111
|
+
params: false, // Include request params (default: false)
|
|
112
|
+
content: false, // Include response content (default: false)
|
|
113
|
+
usage: true, // Include token usage (default: true)
|
|
114
|
+
},
|
|
106
115
|
}),
|
|
107
116
|
});
|
|
108
117
|
```
|
|
109
118
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
MIT
|
|
119
|
+
Happy coding! 🚀
|
package/dist/index.cjs
CHANGED
|
@@ -191,7 +191,8 @@ function _ts_generator(thisArg, body) {
|
|
|
191
191
|
* });
|
|
192
192
|
* ```
|
|
193
193
|
*/ function createLoggingMiddleware(options) {
|
|
194
|
-
var logger = options.logger,
|
|
194
|
+
var logger = options.logger, _options_include = options.include, include = _options_include === void 0 ? {} : _options_include;
|
|
195
|
+
var includeParams = include.params, includeContent = include.content, tmp = include.usage, includeUsage = tmp === void 0 ? true : tmp;
|
|
195
196
|
return {
|
|
196
197
|
middlewareVersion: 'v2',
|
|
197
198
|
wrapGenerate: function(param) {
|
|
@@ -202,7 +203,7 @@ function _ts_generator(thisArg, body) {
|
|
|
202
203
|
switch(_state.label){
|
|
203
204
|
case 0:
|
|
204
205
|
startTime = Date.now();
|
|
205
|
-
logger.debug('Model request started', _object_spread$1({},
|
|
206
|
+
logger.debug('Model request started', _object_spread$1({}, includeParams && {
|
|
206
207
|
params: params
|
|
207
208
|
}));
|
|
208
209
|
_state.label = 1;
|
|
@@ -221,9 +222,10 @@ function _ts_generator(thisArg, body) {
|
|
|
221
222
|
result = _state.sent();
|
|
222
223
|
logger.debug('Model request completed', _object_spread$1({
|
|
223
224
|
durationMs: Date.now() - startTime,
|
|
224
|
-
finishReason: result.finishReason
|
|
225
|
+
finishReason: result.finishReason
|
|
226
|
+
}, includeUsage && {
|
|
225
227
|
usage: result.usage
|
|
226
|
-
},
|
|
228
|
+
}, includeContent && {
|
|
227
229
|
content: result.content
|
|
228
230
|
}));
|
|
229
231
|
return [
|
|
@@ -253,7 +255,7 @@ function _ts_generator(thisArg, body) {
|
|
|
253
255
|
switch(_state.label){
|
|
254
256
|
case 0:
|
|
255
257
|
startTime = Date.now();
|
|
256
|
-
logger.debug('Model stream started', _object_spread$1({},
|
|
258
|
+
logger.debug('Model stream started', _object_spread$1({}, includeParams && {
|
|
257
259
|
params: params
|
|
258
260
|
}));
|
|
259
261
|
_state.label = 1;
|
|
@@ -587,6 +589,29 @@ function extractBySchemaType(text, schema, originalText) {
|
|
|
587
589
|
if (_instanceof(schema, v4.z.ZodBoolean) || _instanceof(schema, v4.z.ZodNull) || _instanceof(schema, v4.z.ZodNumber) || _instanceof(schema, v4.z.ZodString)) {
|
|
588
590
|
return extractPrimitive(text, schema);
|
|
589
591
|
}
|
|
592
|
+
// Handle union types - extract as object/array and let Zod validate which variant matches
|
|
593
|
+
if (_instanceof(schema, v4.z.ZodUnion) || _instanceof(schema, v4.z.ZodDiscriminatedUnion)) {
|
|
594
|
+
var objectStart = text.indexOf('{');
|
|
595
|
+
if (objectStart !== -1) {
|
|
596
|
+
return extractObject(text, originalText);
|
|
597
|
+
}
|
|
598
|
+
var arrayStart = text.indexOf('[');
|
|
599
|
+
if (arrayStart !== -1) {
|
|
600
|
+
return extractArray(text, originalText);
|
|
601
|
+
}
|
|
602
|
+
throw new ParseObjectError('No object or array found for union type', undefined, originalText);
|
|
603
|
+
}
|
|
604
|
+
// Handle wrapper types - unwrap and delegate to inner type
|
|
605
|
+
if (_instanceof(schema, v4.z.ZodOptional) || _instanceof(schema, v4.z.ZodNullable)) {
|
|
606
|
+
return extractBySchemaType(text, schema.unwrap(), originalText);
|
|
607
|
+
}
|
|
608
|
+
if (_instanceof(schema, v4.z.ZodDefault)) {
|
|
609
|
+
return extractBySchemaType(text, schema.def.innerType, originalText);
|
|
610
|
+
}
|
|
611
|
+
// Handle .transform() which creates a ZodPipe in Zod v4
|
|
612
|
+
if (_instanceof(schema, v4.z.ZodPipe)) {
|
|
613
|
+
return extractBySchemaType(text, schema.def["in"], originalText);
|
|
614
|
+
}
|
|
590
615
|
throw new ParseObjectError('Unsupported schema type', undefined, originalText);
|
|
591
616
|
}
|
|
592
617
|
function extractJsonFromCodeBlock(block) {
|
|
@@ -697,7 +722,7 @@ var ASCII_CTRL_RE = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g;
|
|
|
697
722
|
var MULTIPLE_SPACES_RE = / {2,}/g;
|
|
698
723
|
var CR_RE = /\r\n?/g;
|
|
699
724
|
var CITATION_RE = / *\(oaicite:\d+\)\{index=\d+\}/g;
|
|
700
|
-
var EM_DASH_SEPARATOR_RE =
|
|
725
|
+
var EM_DASH_SEPARATOR_RE = /\s*[—–―‒]\s*/g;
|
|
701
726
|
var TYPOGRAPHY_REPLACEMENTS = [
|
|
702
727
|
{
|
|
703
728
|
pattern: /[\u2018\u2019\u201A]/g,
|
|
@@ -707,10 +732,6 @@ var TYPOGRAPHY_REPLACEMENTS = [
|
|
|
707
732
|
pattern: /[\u201C\u201D\u201E]/g,
|
|
708
733
|
replacement: '"'
|
|
709
734
|
},
|
|
710
|
-
{
|
|
711
|
-
pattern: /[\u2013\u2014]/g,
|
|
712
|
-
replacement: '-'
|
|
713
|
-
},
|
|
714
735
|
{
|
|
715
736
|
pattern: /\u2026/g,
|
|
716
737
|
replacement: '...'
|
|
@@ -230,7 +230,7 @@ describe('createLoggingMiddleware', function() {
|
|
|
230
230
|
});
|
|
231
231
|
})();
|
|
232
232
|
});
|
|
233
|
-
it('includes params
|
|
233
|
+
it('includes params when include.params is true', function() {
|
|
234
234
|
return _async_to_generator(function() {
|
|
235
235
|
var _middleware_wrapGenerate, logger, middleware, mockResult, mockParams, doGenerate;
|
|
236
236
|
return _ts_generator(this, function(_state) {
|
|
@@ -239,7 +239,9 @@ describe('createLoggingMiddleware', function() {
|
|
|
239
239
|
logger = createMockLogger();
|
|
240
240
|
middleware = createLoggingMiddleware({
|
|
241
241
|
logger: logger,
|
|
242
|
-
|
|
242
|
+
include: {
|
|
243
|
+
params: true
|
|
244
|
+
}
|
|
243
245
|
});
|
|
244
246
|
mockResult = createMockGenerateResult();
|
|
245
247
|
mockParams = {
|
|
@@ -260,6 +262,39 @@ describe('createLoggingMiddleware', function() {
|
|
|
260
262
|
expect(logger.debug).toHaveBeenNthCalledWith(1, 'Model request started', expect.objectContaining({
|
|
261
263
|
params: mockParams
|
|
262
264
|
}));
|
|
265
|
+
return [
|
|
266
|
+
2
|
|
267
|
+
];
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
})();
|
|
271
|
+
});
|
|
272
|
+
it('includes content when include.content is true', function() {
|
|
273
|
+
return _async_to_generator(function() {
|
|
274
|
+
var _middleware_wrapGenerate, logger, middleware, mockResult, doGenerate;
|
|
275
|
+
return _ts_generator(this, function(_state) {
|
|
276
|
+
switch(_state.label){
|
|
277
|
+
case 0:
|
|
278
|
+
logger = createMockLogger();
|
|
279
|
+
middleware = createLoggingMiddleware({
|
|
280
|
+
logger: logger,
|
|
281
|
+
include: {
|
|
282
|
+
content: true
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
mockResult = createMockGenerateResult();
|
|
286
|
+
doGenerate = vi.fn().mockResolvedValue(mockResult);
|
|
287
|
+
return [
|
|
288
|
+
4,
|
|
289
|
+
(_middleware_wrapGenerate = middleware.wrapGenerate) === null || _middleware_wrapGenerate === void 0 ? void 0 : _middleware_wrapGenerate.call(middleware, {
|
|
290
|
+
doGenerate: doGenerate,
|
|
291
|
+
doStream: vi.fn(),
|
|
292
|
+
params: {},
|
|
293
|
+
model: {}
|
|
294
|
+
})
|
|
295
|
+
];
|
|
296
|
+
case 1:
|
|
297
|
+
_state.sent();
|
|
263
298
|
expect(logger.debug).toHaveBeenNthCalledWith(2, 'Model request completed', expect.objectContaining({
|
|
264
299
|
content: mockResult.content
|
|
265
300
|
}));
|
|
@@ -270,6 +305,42 @@ describe('createLoggingMiddleware', function() {
|
|
|
270
305
|
});
|
|
271
306
|
})();
|
|
272
307
|
});
|
|
308
|
+
it('excludes usage when include.usage is false', function() {
|
|
309
|
+
return _async_to_generator(function() {
|
|
310
|
+
var _middleware_wrapGenerate, logger, middleware, mockResult, doGenerate;
|
|
311
|
+
return _ts_generator(this, function(_state) {
|
|
312
|
+
switch(_state.label){
|
|
313
|
+
case 0:
|
|
314
|
+
logger = createMockLogger();
|
|
315
|
+
middleware = createLoggingMiddleware({
|
|
316
|
+
logger: logger,
|
|
317
|
+
include: {
|
|
318
|
+
usage: false
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
mockResult = createMockGenerateResult();
|
|
322
|
+
doGenerate = vi.fn().mockResolvedValue(mockResult);
|
|
323
|
+
return [
|
|
324
|
+
4,
|
|
325
|
+
(_middleware_wrapGenerate = middleware.wrapGenerate) === null || _middleware_wrapGenerate === void 0 ? void 0 : _middleware_wrapGenerate.call(middleware, {
|
|
326
|
+
doGenerate: doGenerate,
|
|
327
|
+
doStream: vi.fn(),
|
|
328
|
+
params: {},
|
|
329
|
+
model: {}
|
|
330
|
+
})
|
|
331
|
+
];
|
|
332
|
+
case 1:
|
|
333
|
+
_state.sent();
|
|
334
|
+
expect(logger.debug).toHaveBeenNthCalledWith(2, 'Model request completed', expect.not.objectContaining({
|
|
335
|
+
usage: expect.anything()
|
|
336
|
+
}));
|
|
337
|
+
return [
|
|
338
|
+
2
|
|
339
|
+
];
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
})();
|
|
343
|
+
});
|
|
273
344
|
});
|
|
274
345
|
describe('wrapStream', function() {
|
|
275
346
|
it('logs stream start on success', function() {
|
|
@@ -338,7 +409,7 @@ describe('createLoggingMiddleware', function() {
|
|
|
338
409
|
});
|
|
339
410
|
})();
|
|
340
411
|
});
|
|
341
|
-
it('includes params when
|
|
412
|
+
it('includes params when include.params is true', function() {
|
|
342
413
|
return _async_to_generator(function() {
|
|
343
414
|
var _middleware_wrapStream, logger, middleware, mockResult, mockParams, doStream;
|
|
344
415
|
return _ts_generator(this, function(_state) {
|
|
@@ -347,7 +418,9 @@ describe('createLoggingMiddleware', function() {
|
|
|
347
418
|
logger = createMockLogger();
|
|
348
419
|
middleware = createLoggingMiddleware({
|
|
349
420
|
logger: logger,
|
|
350
|
-
|
|
421
|
+
include: {
|
|
422
|
+
params: true
|
|
423
|
+
}
|
|
351
424
|
});
|
|
352
425
|
mockResult = createMockStreamResult();
|
|
353
426
|
mockParams = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/middleware/__tests__/logging.middleware.test.ts"],"sourcesContent":["import { describe, expect, it, vi } from 'vitest';\n\nimport { createLoggingMiddleware } from '../logging.middleware.js';\n\nfunction createMockLogger() {\n const logger = {\n child: vi.fn(() => logger),\n debug: vi.fn(),\n error: vi.fn(),\n info: vi.fn(),\n warn: vi.fn(),\n };\n return logger;\n}\n\nfunction createMockGenerateResult() {\n return {\n content: [{ type: 'text' as const, text: 'Hello world' }],\n finishReason: 'stop' as const,\n usage: { inputTokens: 10, outputTokens: 20, totalTokens: 30 },\n warnings: [],\n };\n}\n\nfunction createMockStreamResult() {\n return {\n stream: new ReadableStream(),\n warnings: [],\n };\n}\n\ndescribe('createLoggingMiddleware', () => {\n describe('wrapGenerate', () => {\n it('logs request start and completion on success', async () => {\n const logger = createMockLogger();\n const middleware = createLoggingMiddleware({ logger });\n const mockResult = createMockGenerateResult();\n const doGenerate = vi.fn().mockResolvedValue(mockResult);\n\n const result = await middleware.wrapGenerate?.({\n doGenerate,\n doStream: vi.fn(),\n params: {} as never,\n model: {} as never,\n });\n\n expect(logger.debug).toHaveBeenCalledTimes(2);\n expect(logger.debug).toHaveBeenNthCalledWith(1, 'Model request started', {});\n expect(logger.debug).toHaveBeenNthCalledWith(\n 2,\n 'Model request completed',\n expect.objectContaining({\n durationMs: expect.any(Number),\n finishReason: 'stop',\n usage: mockResult.usage,\n }),\n );\n expect(result).toBe(mockResult);\n });\n\n it('logs error on failure', async () => {\n const logger = createMockLogger();\n const middleware = createLoggingMiddleware({ logger });\n const error = new Error('API error');\n const doGenerate = vi.fn().mockRejectedValue(error);\n\n await expect(\n middleware.wrapGenerate?.({\n doGenerate,\n doStream: vi.fn(),\n params: {} as never,\n model: {} as never,\n }),\n ).rejects.toThrow('API error');\n\n expect(logger.debug).toHaveBeenCalledWith('Model request started', {});\n expect(logger.error).toHaveBeenCalledWith(\n 'Model request failed',\n expect.objectContaining({\n durationMs: expect.any(Number),\n error: 'API error',\n }),\n );\n });\n\n it('includes params and content when verbose is true', async () => {\n const logger = createMockLogger();\n const middleware = createLoggingMiddleware({ logger, verbose: true });\n const mockResult = createMockGenerateResult();\n const mockParams = { prompt: 'Hello' };\n const doGenerate = vi.fn().mockResolvedValue(mockResult);\n\n await middleware.wrapGenerate?.({\n doGenerate,\n doStream: vi.fn(),\n params: mockParams as never,\n model: {} as never,\n });\n\n expect(logger.debug).toHaveBeenNthCalledWith(\n 1,\n 'Model request started',\n expect.objectContaining({ params: mockParams }),\n );\n expect(logger.debug).toHaveBeenNthCalledWith(\n 2,\n 'Model request completed',\n expect.objectContaining({ content: mockResult.content }),\n );\n });\n });\n\n describe('wrapStream', () => {\n it('logs stream start on success', async () => {\n const logger = createMockLogger();\n const middleware = createLoggingMiddleware({ logger });\n const mockResult = createMockStreamResult();\n const doStream = vi.fn().mockResolvedValue(mockResult);\n\n const result = await middleware.wrapStream?.({\n doGenerate: vi.fn(),\n doStream,\n params: {} as never,\n model: {} as never,\n });\n\n expect(logger.debug).toHaveBeenCalledWith('Model stream started', {});\n expect(result?.stream).toBe(mockResult.stream);\n });\n\n it('logs error on stream failure', async () => {\n const logger = createMockLogger();\n const middleware = createLoggingMiddleware({ logger });\n const error = new Error('Stream error');\n const doStream = vi.fn().mockRejectedValue(error);\n\n await expect(\n middleware.wrapStream?.({\n doGenerate: vi.fn(),\n doStream,\n params: {} as never,\n model: {} as never,\n }),\n ).rejects.toThrow('Stream error');\n\n expect(logger.error).toHaveBeenCalledWith(\n 'Model stream failed',\n expect.objectContaining({\n durationMs: expect.any(Number),\n error: 'Stream error',\n }),\n );\n });\n\n it('includes params when verbose is true', async () => {\n const logger = createMockLogger();\n const middleware = createLoggingMiddleware({ logger, verbose: true });\n const mockResult = createMockStreamResult();\n const mockParams = { prompt: 'Hello' };\n const doStream = vi.fn().mockResolvedValue(mockResult);\n\n await middleware.wrapStream?.({\n doGenerate: vi.fn(),\n doStream,\n params: mockParams as never,\n model: {} as never,\n });\n\n expect(logger.debug).toHaveBeenCalledWith(\n 'Model stream started',\n expect.objectContaining({ params: mockParams }),\n );\n });\n });\n\n describe('middleware structure', () => {\n it('returns middleware with v2 version', () => {\n const logger = createMockLogger();\n const middleware = createLoggingMiddleware({ logger });\n\n expect(middleware.middlewareVersion).toBe('v2');\n });\n });\n});\n"],"names":["describe","expect","it","vi","createLoggingMiddleware","createMockLogger","logger","child","fn","debug","error","info","warn","createMockGenerateResult","content","type","text","finishReason","usage","inputTokens","outputTokens","totalTokens","warnings","createMockStreamResult","stream","ReadableStream","middleware","mockResult","doGenerate","result","mockResolvedValue","wrapGenerate","doStream","params","model","toHaveBeenCalledTimes","toHaveBeenNthCalledWith","objectContaining","durationMs","any","Number","toBe","Error","mockRejectedValue","rejects","toThrow","toHaveBeenCalledWith","mockParams","verbose","prompt","wrapStream","middlewareVersion"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/middleware/__tests__/logging.middleware.test.ts"],"sourcesContent":["import { describe, expect, it, vi } from 'vitest';\n\nimport { createLoggingMiddleware } from '../logging.middleware.js';\n\nfunction createMockLogger() {\n const logger = {\n child: vi.fn(() => logger),\n debug: vi.fn(),\n error: vi.fn(),\n info: vi.fn(),\n warn: vi.fn(),\n };\n return logger;\n}\n\nfunction createMockGenerateResult() {\n return {\n content: [{ type: 'text' as const, text: 'Hello world' }],\n finishReason: 'stop' as const,\n usage: { inputTokens: 10, outputTokens: 20, totalTokens: 30 },\n warnings: [],\n };\n}\n\nfunction createMockStreamResult() {\n return {\n stream: new ReadableStream(),\n warnings: [],\n };\n}\n\ndescribe('createLoggingMiddleware', () => {\n describe('wrapGenerate', () => {\n it('logs request start and completion on success', async () => {\n const logger = createMockLogger();\n const middleware = createLoggingMiddleware({ logger });\n const mockResult = createMockGenerateResult();\n const doGenerate = vi.fn().mockResolvedValue(mockResult);\n\n const result = await middleware.wrapGenerate?.({\n doGenerate,\n doStream: vi.fn(),\n params: {} as never,\n model: {} as never,\n });\n\n expect(logger.debug).toHaveBeenCalledTimes(2);\n expect(logger.debug).toHaveBeenNthCalledWith(1, 'Model request started', {});\n expect(logger.debug).toHaveBeenNthCalledWith(\n 2,\n 'Model request completed',\n expect.objectContaining({\n durationMs: expect.any(Number),\n finishReason: 'stop',\n usage: mockResult.usage,\n }),\n );\n expect(result).toBe(mockResult);\n });\n\n it('logs error on failure', async () => {\n const logger = createMockLogger();\n const middleware = createLoggingMiddleware({ logger });\n const error = new Error('API error');\n const doGenerate = vi.fn().mockRejectedValue(error);\n\n await expect(\n middleware.wrapGenerate?.({\n doGenerate,\n doStream: vi.fn(),\n params: {} as never,\n model: {} as never,\n }),\n ).rejects.toThrow('API error');\n\n expect(logger.debug).toHaveBeenCalledWith('Model request started', {});\n expect(logger.error).toHaveBeenCalledWith(\n 'Model request failed',\n expect.objectContaining({\n durationMs: expect.any(Number),\n error: 'API error',\n }),\n );\n });\n\n it('includes params when include.params is true', async () => {\n const logger = createMockLogger();\n const middleware = createLoggingMiddleware({ logger, include: { params: true } });\n const mockResult = createMockGenerateResult();\n const mockParams = { prompt: 'Hello' };\n const doGenerate = vi.fn().mockResolvedValue(mockResult);\n\n await middleware.wrapGenerate?.({\n doGenerate,\n doStream: vi.fn(),\n params: mockParams as never,\n model: {} as never,\n });\n\n expect(logger.debug).toHaveBeenNthCalledWith(\n 1,\n 'Model request started',\n expect.objectContaining({ params: mockParams }),\n );\n });\n\n it('includes content when include.content is true', async () => {\n const logger = createMockLogger();\n const middleware = createLoggingMiddleware({ logger, include: { content: true } });\n const mockResult = createMockGenerateResult();\n const doGenerate = vi.fn().mockResolvedValue(mockResult);\n\n await middleware.wrapGenerate?.({\n doGenerate,\n doStream: vi.fn(),\n params: {} as never,\n model: {} as never,\n });\n\n expect(logger.debug).toHaveBeenNthCalledWith(\n 2,\n 'Model request completed',\n expect.objectContaining({ content: mockResult.content }),\n );\n });\n\n it('excludes usage when include.usage is false', async () => {\n const logger = createMockLogger();\n const middleware = createLoggingMiddleware({ logger, include: { usage: false } });\n const mockResult = createMockGenerateResult();\n const doGenerate = vi.fn().mockResolvedValue(mockResult);\n\n await middleware.wrapGenerate?.({\n doGenerate,\n doStream: vi.fn(),\n params: {} as never,\n model: {} as never,\n });\n\n expect(logger.debug).toHaveBeenNthCalledWith(\n 2,\n 'Model request completed',\n expect.not.objectContaining({ usage: expect.anything() }),\n );\n });\n });\n\n describe('wrapStream', () => {\n it('logs stream start on success', async () => {\n const logger = createMockLogger();\n const middleware = createLoggingMiddleware({ logger });\n const mockResult = createMockStreamResult();\n const doStream = vi.fn().mockResolvedValue(mockResult);\n\n const result = await middleware.wrapStream?.({\n doGenerate: vi.fn(),\n doStream,\n params: {} as never,\n model: {} as never,\n });\n\n expect(logger.debug).toHaveBeenCalledWith('Model stream started', {});\n expect(result?.stream).toBe(mockResult.stream);\n });\n\n it('logs error on stream failure', async () => {\n const logger = createMockLogger();\n const middleware = createLoggingMiddleware({ logger });\n const error = new Error('Stream error');\n const doStream = vi.fn().mockRejectedValue(error);\n\n await expect(\n middleware.wrapStream?.({\n doGenerate: vi.fn(),\n doStream,\n params: {} as never,\n model: {} as never,\n }),\n ).rejects.toThrow('Stream error');\n\n expect(logger.error).toHaveBeenCalledWith(\n 'Model stream failed',\n expect.objectContaining({\n durationMs: expect.any(Number),\n error: 'Stream error',\n }),\n );\n });\n\n it('includes params when include.params is true', async () => {\n const logger = createMockLogger();\n const middleware = createLoggingMiddleware({ logger, include: { params: true } });\n const mockResult = createMockStreamResult();\n const mockParams = { prompt: 'Hello' };\n const doStream = vi.fn().mockResolvedValue(mockResult);\n\n await middleware.wrapStream?.({\n doGenerate: vi.fn(),\n doStream,\n params: mockParams as never,\n model: {} as never,\n });\n\n expect(logger.debug).toHaveBeenCalledWith(\n 'Model stream started',\n expect.objectContaining({ params: mockParams }),\n );\n });\n });\n\n describe('middleware structure', () => {\n it('returns middleware with v2 version', () => {\n const logger = createMockLogger();\n const middleware = createLoggingMiddleware({ logger });\n\n expect(middleware.middlewareVersion).toBe('v2');\n });\n });\n});\n"],"names":["describe","expect","it","vi","createLoggingMiddleware","createMockLogger","logger","child","fn","debug","error","info","warn","createMockGenerateResult","content","type","text","finishReason","usage","inputTokens","outputTokens","totalTokens","warnings","createMockStreamResult","stream","ReadableStream","middleware","mockResult","doGenerate","result","mockResolvedValue","wrapGenerate","doStream","params","model","toHaveBeenCalledTimes","toHaveBeenNthCalledWith","objectContaining","durationMs","any","Number","toBe","Error","mockRejectedValue","rejects","toThrow","toHaveBeenCalledWith","mockParams","include","prompt","not","anything","wrapStream","middlewareVersion"],"mappings":"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"}
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import type { LoggerPort } from '@jterrazz/logger';
|
|
2
2
|
import type { LanguageModelMiddleware } from 'ai';
|
|
3
|
+
export interface LoggingMiddlewareInclude {
|
|
4
|
+
/** Include request params in logs */
|
|
5
|
+
params?: boolean;
|
|
6
|
+
/** Include response content in logs */
|
|
7
|
+
content?: boolean;
|
|
8
|
+
/** Include token usage in logs */
|
|
9
|
+
usage?: boolean;
|
|
10
|
+
}
|
|
3
11
|
export interface LoggingMiddlewareOptions {
|
|
4
12
|
logger: LoggerPort;
|
|
5
|
-
/**
|
|
6
|
-
|
|
13
|
+
/** Select which fields to include in logs (default: { usage: true }) */
|
|
14
|
+
include?: LoggingMiddlewareInclude;
|
|
7
15
|
}
|
|
8
16
|
/**
|
|
9
17
|
* Creates AI SDK middleware that logs model requests and responses.
|
|
@@ -190,7 +190,8 @@ function _ts_generator(thisArg, body) {
|
|
|
190
190
|
* });
|
|
191
191
|
* ```
|
|
192
192
|
*/ export function createLoggingMiddleware(options) {
|
|
193
|
-
var logger = options.logger,
|
|
193
|
+
var logger = options.logger, _options_include = options.include, include = _options_include === void 0 ? {} : _options_include;
|
|
194
|
+
var includeParams = include.params, includeContent = include.content, tmp = include.usage, includeUsage = tmp === void 0 ? true : tmp;
|
|
194
195
|
return {
|
|
195
196
|
middlewareVersion: 'v2',
|
|
196
197
|
wrapGenerate: function(param) {
|
|
@@ -201,7 +202,7 @@ function _ts_generator(thisArg, body) {
|
|
|
201
202
|
switch(_state.label){
|
|
202
203
|
case 0:
|
|
203
204
|
startTime = Date.now();
|
|
204
|
-
logger.debug('Model request started', _object_spread({},
|
|
205
|
+
logger.debug('Model request started', _object_spread({}, includeParams && {
|
|
205
206
|
params: params
|
|
206
207
|
}));
|
|
207
208
|
_state.label = 1;
|
|
@@ -220,9 +221,10 @@ function _ts_generator(thisArg, body) {
|
|
|
220
221
|
result = _state.sent();
|
|
221
222
|
logger.debug('Model request completed', _object_spread({
|
|
222
223
|
durationMs: Date.now() - startTime,
|
|
223
|
-
finishReason: result.finishReason
|
|
224
|
+
finishReason: result.finishReason
|
|
225
|
+
}, includeUsage && {
|
|
224
226
|
usage: result.usage
|
|
225
|
-
},
|
|
227
|
+
}, includeContent && {
|
|
226
228
|
content: result.content
|
|
227
229
|
}));
|
|
228
230
|
return [
|
|
@@ -252,7 +254,7 @@ function _ts_generator(thisArg, body) {
|
|
|
252
254
|
switch(_state.label){
|
|
253
255
|
case 0:
|
|
254
256
|
startTime = Date.now();
|
|
255
|
-
logger.debug('Model stream started', _object_spread({},
|
|
257
|
+
logger.debug('Model stream started', _object_spread({}, includeParams && {
|
|
256
258
|
params: params
|
|
257
259
|
}));
|
|
258
260
|
_state.label = 1;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/middleware/logging.middleware.ts"],"sourcesContent":["import type { LoggerPort } from '@jterrazz/logger';\nimport type { LanguageModelMiddleware } from 'ai';\n\nexport interface LoggingMiddlewareOptions {\n logger: LoggerPort;\n /**
|
|
1
|
+
{"version":3,"sources":["../../src/middleware/logging.middleware.ts"],"sourcesContent":["import type { LoggerPort } from '@jterrazz/logger';\nimport type { LanguageModelMiddleware } from 'ai';\n\nexport interface LoggingMiddlewareInclude {\n /** Include request params in logs */\n params?: boolean;\n /** Include response content in logs */\n content?: boolean;\n /** Include token usage in logs */\n usage?: boolean;\n}\n\nexport interface LoggingMiddlewareOptions {\n logger: LoggerPort;\n /** Select which fields to include in logs (default: { usage: true }) */\n include?: LoggingMiddlewareInclude;\n}\n\n/**\n * Creates AI SDK middleware that logs model requests and responses.\n *\n * @example\n * ```ts\n * import { wrapLanguageModel } from 'ai';\n *\n * const model = wrapLanguageModel({\n * model: openrouter('anthropic/claude-sonnet-4-20250514'),\n * middleware: createLoggingMiddleware({ logger }),\n * });\n * ```\n */\nexport function createLoggingMiddleware(\n options: LoggingMiddlewareOptions,\n): LanguageModelMiddleware {\n const { logger, include = {} } = options;\n const { params: includeParams, content: includeContent, usage: includeUsage = true } = include;\n\n return {\n middlewareVersion: 'v2',\n\n wrapGenerate: async ({ doGenerate, params }) => {\n const startTime = Date.now();\n\n logger.debug('Model request started', {\n ...(includeParams && { params }),\n });\n\n try {\n const result = await doGenerate();\n\n logger.debug('Model request completed', {\n durationMs: Date.now() - startTime,\n finishReason: result.finishReason,\n ...(includeUsage && { usage: result.usage }),\n ...(includeContent && { content: result.content }),\n });\n\n return result;\n } catch (error) {\n logger.error('Model request failed', {\n durationMs: Date.now() - startTime,\n error: error instanceof Error ? error.message : 'Unknown error',\n });\n throw error;\n }\n },\n\n wrapStream: async ({ doStream, params }) => {\n const startTime = Date.now();\n\n logger.debug('Model stream started', {\n ...(includeParams && { params }),\n });\n\n try {\n const result = await doStream();\n\n return {\n ...result,\n stream: result.stream,\n };\n } catch (error) {\n logger.error('Model stream failed', {\n durationMs: Date.now() - startTime,\n error: error instanceof Error ? error.message : 'Unknown error',\n });\n throw error;\n }\n },\n };\n}\n"],"names":["createLoggingMiddleware","options","logger","include","params","includeParams","content","includeContent","includeUsage","usage","middlewareVersion","wrapGenerate","doGenerate","startTime","result","error","Date","now","debug","durationMs","finishReason","Error","message","wrapStream","doStream","stream"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkBA;;;;;;;;;;;;CAYC,GACD,OAAO,SAASA,wBACZC,OAAiC;IAEjC,IAAQC,SAAyBD,QAAzBC,2BAAyBD,QAAjBE,SAAAA,wCAAU,CAAC;IAC3B,IAAQC,AAAQC,gBAAuEF,QAA/EC,QAAuBE,AAASC,iBAA+CJ,QAAxDG,SAAgCE,MAAwBL,QAA/BM,OAAOD,eAAAA,iBAAe,OAAfA;IAE/D,OAAO;QACHE,mBAAmB;QAEnBC,cAAc;gBAASC,mBAAAA,YAAYR,eAAAA;;oBACzBS,WAOIC,QAUDC;;;;4BAjBHF,YAAYG,KAAKC,GAAG;4BAE1Bf,OAAOgB,KAAK,CAAC,yBAAyB,mBAC9Bb,iBAAiB;gCAAED,QAAAA;4BAAO;;;;;;;;;4BAIf;;gCAAMQ;;;4BAAfE,SAAS;4BAEfZ,OAAOgB,KAAK,CAAC,2BAA2B;gCACpCC,YAAYH,KAAKC,GAAG,KAAKJ;gCACzBO,cAAcN,OAAOM,YAAY;+BAC7BZ,gBAAgB;gCAAEC,OAAOK,OAAOL,KAAK;4BAAC,GACtCF,kBAAkB;gCAAED,SAASQ,OAAOR,OAAO;4BAAC;4BAGpD;;gCAAOQ;;;4BACFC;4BACLb,OAAOa,KAAK,CAAC,wBAAwB;gCACjCI,YAAYH,KAAKC,GAAG,KAAKJ;gCACzBE,OAAOA,AAAK,YAALA,OAAiBM,SAAQN,MAAMO,OAAO,GAAG;4BACpD;4BACA,MAAMP;;;;;;;YAEd;;QAEAQ,YAAY;gBAASC,iBAAAA,UAAUpB,eAAAA;;oBACrBS,WAOIC,QAMDC;;;;4BAbHF,YAAYG,KAAKC,GAAG;4BAE1Bf,OAAOgB,KAAK,CAAC,wBAAwB,mBAC7Bb,iBAAiB;gCAAED,QAAAA;4BAAO;;;;;;;;;4BAIf;;gCAAMoB;;;4BAAfV,SAAS;4BAEf;;gCAAO,wCACAA;oCACHW,QAAQX,OAAOW,MAAM;;;;4BAEpBV;4BACLb,OAAOa,KAAK,CAAC,uBAAuB;gCAChCI,YAAYH,KAAKC,GAAG,KAAKJ;gCACzBE,OAAOA,AAAK,YAALA,OAAiBM,SAAQN,MAAMO,OAAO,GAAG;4BACpD;4BACA,MAAMP;;;;;;;YAEd;;IACJ;AACJ"}
|
|
@@ -1,3 +1,55 @@
|
|
|
1
|
+
function _define_property(obj, key, value) {
|
|
2
|
+
if (key in obj) {
|
|
3
|
+
Object.defineProperty(obj, key, {
|
|
4
|
+
value: value,
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true
|
|
8
|
+
});
|
|
9
|
+
} else {
|
|
10
|
+
obj[key] = value;
|
|
11
|
+
}
|
|
12
|
+
return obj;
|
|
13
|
+
}
|
|
14
|
+
function _object_spread(target) {
|
|
15
|
+
for(var i = 1; i < arguments.length; i++){
|
|
16
|
+
var source = arguments[i] != null ? arguments[i] : {};
|
|
17
|
+
var ownKeys = Object.keys(source);
|
|
18
|
+
if (typeof Object.getOwnPropertySymbols === "function") {
|
|
19
|
+
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
|
|
20
|
+
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
|
|
21
|
+
}));
|
|
22
|
+
}
|
|
23
|
+
ownKeys.forEach(function(key) {
|
|
24
|
+
_define_property(target, key, source[key]);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return target;
|
|
28
|
+
}
|
|
29
|
+
function ownKeys(object, enumerableOnly) {
|
|
30
|
+
var keys = Object.keys(object);
|
|
31
|
+
if (Object.getOwnPropertySymbols) {
|
|
32
|
+
var symbols = Object.getOwnPropertySymbols(object);
|
|
33
|
+
if (enumerableOnly) {
|
|
34
|
+
symbols = symbols.filter(function(sym) {
|
|
35
|
+
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
keys.push.apply(keys, symbols);
|
|
39
|
+
}
|
|
40
|
+
return keys;
|
|
41
|
+
}
|
|
42
|
+
function _object_spread_props(target, source) {
|
|
43
|
+
source = source != null ? source : {};
|
|
44
|
+
if (Object.getOwnPropertyDescriptors) {
|
|
45
|
+
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
|
|
46
|
+
} else {
|
|
47
|
+
ownKeys(Object(source)).forEach(function(key) {
|
|
48
|
+
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return target;
|
|
52
|
+
}
|
|
1
53
|
import { describe, expect, it } from 'vitest';
|
|
2
54
|
import { z } from 'zod/v4';
|
|
3
55
|
import { parseObject, ParseObjectError } from '../parse-object.js';
|
|
@@ -133,9 +185,20 @@ describe('parseObject', function() {
|
|
|
133
185
|
});
|
|
134
186
|
it('throws ParseObjectError for unsupported schema type', function() {
|
|
135
187
|
var text = 'test';
|
|
188
|
+
var schema = z.date();
|
|
189
|
+
expect(function() {
|
|
190
|
+
return parseObject(text, schema);
|
|
191
|
+
}).toThrow(ParseObjectError);
|
|
192
|
+
});
|
|
193
|
+
it('throws ParseObjectError for union when no object or array found', function() {
|
|
194
|
+
var text = 'just plain text';
|
|
136
195
|
var schema = z.union([
|
|
137
|
-
z.
|
|
138
|
-
|
|
196
|
+
z.object({
|
|
197
|
+
type: z.literal('a')
|
|
198
|
+
}),
|
|
199
|
+
z.object({
|
|
200
|
+
type: z.literal('b')
|
|
201
|
+
})
|
|
139
202
|
]);
|
|
140
203
|
expect(function() {
|
|
141
204
|
return parseObject(text, schema);
|
|
@@ -188,6 +251,183 @@ describe('parseObject', function() {
|
|
|
188
251
|
expect(result).toEqual(validArticle);
|
|
189
252
|
});
|
|
190
253
|
});
|
|
254
|
+
describe('union types', function() {
|
|
255
|
+
it('parses z.union - variant A', function() {
|
|
256
|
+
var schema = z.union([
|
|
257
|
+
z.object({
|
|
258
|
+
type: z.literal('a'),
|
|
259
|
+
value: z.string()
|
|
260
|
+
}),
|
|
261
|
+
z.object({
|
|
262
|
+
type: z.literal('b'),
|
|
263
|
+
count: z.number()
|
|
264
|
+
})
|
|
265
|
+
]);
|
|
266
|
+
var text = '{"type": "a", "value": "hello"}';
|
|
267
|
+
expect(parseObject(text, schema)).toEqual({
|
|
268
|
+
type: 'a',
|
|
269
|
+
value: 'hello'
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
it('parses z.union - variant B', function() {
|
|
273
|
+
var schema = z.union([
|
|
274
|
+
z.object({
|
|
275
|
+
type: z.literal('a'),
|
|
276
|
+
value: z.string()
|
|
277
|
+
}),
|
|
278
|
+
z.object({
|
|
279
|
+
type: z.literal('b'),
|
|
280
|
+
count: z.number()
|
|
281
|
+
})
|
|
282
|
+
]);
|
|
283
|
+
var text = '{"type": "b", "count": 42}';
|
|
284
|
+
expect(parseObject(text, schema)).toEqual({
|
|
285
|
+
type: 'b',
|
|
286
|
+
count: 42
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
it('parses z.discriminatedUnion', function() {
|
|
290
|
+
var schema = z.discriminatedUnion('action', [
|
|
291
|
+
z.object({
|
|
292
|
+
action: z.literal('join'),
|
|
293
|
+
eventId: z.string()
|
|
294
|
+
}),
|
|
295
|
+
z.object({
|
|
296
|
+
action: z.literal('create'),
|
|
297
|
+
name: z.string()
|
|
298
|
+
})
|
|
299
|
+
]);
|
|
300
|
+
var text = '```json\n{"action": "create", "name": "Test"}\n```';
|
|
301
|
+
expect(parseObject(text, schema)).toEqual({
|
|
302
|
+
action: 'create',
|
|
303
|
+
name: 'Test'
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
it('parses discriminated union from surrounding prose', function() {
|
|
307
|
+
var schema = z.discriminatedUnion('action', [
|
|
308
|
+
z.object({
|
|
309
|
+
action: z.literal('join'),
|
|
310
|
+
eventId: z.string()
|
|
311
|
+
}),
|
|
312
|
+
z.object({
|
|
313
|
+
action: z.literal('create'),
|
|
314
|
+
name: z.string()
|
|
315
|
+
})
|
|
316
|
+
]);
|
|
317
|
+
var text = 'Here is the result: {"action": "join", "eventId": "evt-123"} - done';
|
|
318
|
+
expect(parseObject(text, schema)).toEqual({
|
|
319
|
+
action: 'join',
|
|
320
|
+
eventId: 'evt-123'
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
it('parses union of arrays', function() {
|
|
324
|
+
var schema = z.union([
|
|
325
|
+
z.array(z.string()),
|
|
326
|
+
z.array(z.number())
|
|
327
|
+
]);
|
|
328
|
+
var text = '["a", "b", "c"]';
|
|
329
|
+
expect(parseObject(text, schema)).toEqual([
|
|
330
|
+
'a',
|
|
331
|
+
'b',
|
|
332
|
+
'c'
|
|
333
|
+
]);
|
|
334
|
+
});
|
|
335
|
+
it('throws when union variant does not match', function() {
|
|
336
|
+
var schema = z.discriminatedUnion('action', [
|
|
337
|
+
z.object({
|
|
338
|
+
action: z.literal('join'),
|
|
339
|
+
eventId: z.string()
|
|
340
|
+
}),
|
|
341
|
+
z.object({
|
|
342
|
+
action: z.literal('create'),
|
|
343
|
+
name: z.string()
|
|
344
|
+
})
|
|
345
|
+
]);
|
|
346
|
+
var text = '{"action": "delete", "id": "123"}';
|
|
347
|
+
expect(function() {
|
|
348
|
+
return parseObject(text, schema);
|
|
349
|
+
}).toThrow(ParseObjectError);
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
describe('wrapper types', function() {
|
|
353
|
+
it('parses z.optional wrapping an object', function() {
|
|
354
|
+
var schema = z.object({
|
|
355
|
+
name: z.string()
|
|
356
|
+
}).optional();
|
|
357
|
+
var text = '{"name": "test"}';
|
|
358
|
+
expect(parseObject(text, schema)).toEqual({
|
|
359
|
+
name: 'test'
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
it('parses z.nullable wrapping an object', function() {
|
|
363
|
+
var schema = z.object({
|
|
364
|
+
name: z.string()
|
|
365
|
+
}).nullable();
|
|
366
|
+
var text = '{"name": "test"}';
|
|
367
|
+
expect(parseObject(text, schema)).toEqual({
|
|
368
|
+
name: 'test'
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
it('parses z.default wrapping an object', function() {
|
|
372
|
+
var schema = z.object({
|
|
373
|
+
name: z.string()
|
|
374
|
+
})["default"]({
|
|
375
|
+
name: 'default'
|
|
376
|
+
});
|
|
377
|
+
var text = '{"name": "custom"}';
|
|
378
|
+
expect(parseObject(text, schema)).toEqual({
|
|
379
|
+
name: 'custom'
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
it('parses schema with .transform()', function() {
|
|
383
|
+
var schema = z.object({
|
|
384
|
+
value: z.string()
|
|
385
|
+
}).transform(function(obj) {
|
|
386
|
+
return _object_spread_props(_object_spread({}, obj), {
|
|
387
|
+
transformed: true
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
var text = '{"value": "test"}';
|
|
391
|
+
var result = parseObject(text, schema);
|
|
392
|
+
expect(result).toEqual({
|
|
393
|
+
value: 'test',
|
|
394
|
+
transformed: true
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
it('parses schema with .refine()', function() {
|
|
398
|
+
var schema = z.object({
|
|
399
|
+
value: z.number()
|
|
400
|
+
}).refine(function(obj) {
|
|
401
|
+
return obj.value > 0;
|
|
402
|
+
}, {
|
|
403
|
+
message: 'Value must be positive'
|
|
404
|
+
});
|
|
405
|
+
var text = '{"value": 42}';
|
|
406
|
+
expect(parseObject(text, schema)).toEqual({
|
|
407
|
+
value: 42
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
it('parses deeply nested wrapper types', function() {
|
|
411
|
+
var schema = z.object({
|
|
412
|
+
name: z.string()
|
|
413
|
+
}).optional().nullable()["default"]({
|
|
414
|
+
name: 'default'
|
|
415
|
+
});
|
|
416
|
+
var text = '{"name": "nested"}';
|
|
417
|
+
expect(parseObject(text, schema)).toEqual({
|
|
418
|
+
name: 'nested'
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
it('parses optional array', function() {
|
|
422
|
+
var schema = z.array(z.string()).optional();
|
|
423
|
+
var text = '["a", "b", "c"]';
|
|
424
|
+
expect(parseObject(text, schema)).toEqual([
|
|
425
|
+
'a',
|
|
426
|
+
'b',
|
|
427
|
+
'c'
|
|
428
|
+
]);
|
|
429
|
+
});
|
|
430
|
+
});
|
|
191
431
|
});
|
|
192
432
|
|
|
193
433
|
//# sourceMappingURL=parse-object.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/parsing/__tests__/parse-object.test.ts"],"sourcesContent":["import { describe, expect, it } from 'vitest';\nimport { z } from 'zod/v4';\n\nimport { parseObject, ParseObjectError } from '../parse-object.js';\n\nconst articleSchema = z.object({\n content: z.string(),\n tags: z.array(z.string()),\n title: z.string(),\n});\n\nconst validArticle = {\n content: 'Test content',\n tags: ['test', 'ai'],\n title: 'Test Article',\n};\n\nconst validArticleJson = JSON.stringify(validArticle);\n\ndescribe('parseObject', () => {\n describe('object parsing', () => {\n it('parses valid JSON object', () => {\n const text = validArticleJson;\n const result = parseObject(text, articleSchema);\n expect(result).toEqual(validArticle);\n });\n\n it('extracts JSON object from surrounding prose', () => {\n const text = `Here's the article: ${validArticleJson} - end of article`;\n const result = parseObject(text, articleSchema);\n expect(result).toEqual(validArticle);\n });\n\n it('parses JSON from markdown code block', () => {\n const text = `\\`\\`\\`json\\n${validArticleJson}\\n\\`\\`\\``;\n const result = parseObject(text, articleSchema);\n expect(result).toEqual(validArticle);\n });\n\n it('handles newlines in JSON values', () => {\n const text = `{\n \"content\": \"Test\\\\ncontent\\\\nwith\\\\nnewlines\",\n \"tags\": [\"test\", \"ai\"],\n \"title\": \"Test\\\\nArticle\"\n }`;\n const result = parseObject(text, articleSchema);\n expect(result).toEqual({\n content: 'Test\\ncontent\\nwith\\nnewlines',\n tags: ['test', 'ai'],\n title: 'Test\\nArticle',\n });\n });\n\n it('handles escaped characters in JSON', () => {\n const text =\n '{\"content\": \"Test\\\\ncontent\\\\twith\\\\r\\\\nescapes\", \"tags\": [\"test\\\\u0020ai\", \"escaped\\\\\"quotes\\\\\"\"], \"title\": \"Test\\\\\\\\Article\"}';\n const result = parseObject(text, articleSchema);\n expect(result).toEqual({\n content: 'Test\\ncontent\\twith\\r\\nescapes',\n tags: ['test ai', 'escaped\"quotes\"'],\n title: 'Test\\\\Article',\n });\n });\n });\n\n describe('array parsing', () => {\n it('parses JSON array', () => {\n const text = '[\"test\", \"ai\", \"content\"]';\n const schema = z.array(z.string());\n const result = parseObject(text, schema);\n expect(result).toEqual(['test', 'ai', 'content']);\n });\n\n it('parses array of objects from markdown code block', () => {\n const text = `\\`\\`\\`json\\n[${validArticleJson}]\\n\\`\\`\\``;\n const schema = z.array(articleSchema);\n const result = parseObject(text, schema);\n expect(result).toEqual([validArticle]);\n });\n });\n\n describe('primitive parsing', () => {\n it('parses string value', () => {\n const text = '\"test string\"';\n const result = parseObject(text, z.string());\n expect(result).toBe('test string');\n });\n\n it('parses number value', () => {\n const text = '42';\n const result = parseObject(text, z.number());\n expect(result).toBe(42);\n });\n\n it('parses boolean value', () => {\n const text = 'true';\n const result = parseObject(text, z.boolean());\n expect(result).toBe(true);\n });\n\n it('parses null value', () => {\n const text = 'null';\n const result = parseObject(text, z.null());\n expect(result).toBeNull();\n });\n });\n\n describe('error handling', () => {\n it('throws ParseObjectError for invalid JSON', () => {\n const text = '{invalid json}';\n expect(() => parseObject(text, articleSchema)).toThrow(ParseObjectError);\n });\n\n it('throws ParseObjectError when schema validation fails', () => {\n const text = JSON.stringify({\n content: 'Test',\n tags: ['test'],\n title: 123,\n });\n expect(() => parseObject(text, articleSchema)).toThrow(ParseObjectError);\n });\n\n it('throws ParseObjectError when no object found', () => {\n const text = 'No JSON object here';\n expect(() => parseObject(text, articleSchema)).toThrow(ParseObjectError);\n });\n\n it('throws ParseObjectError when no array found', () => {\n const text = 'No array here';\n const schema = z.array(z.string());\n expect(() => parseObject(text, schema)).toThrow(ParseObjectError);\n });\n\n it('throws ParseObjectError for unsupported schema type', () => {\n const text = 'test';\n const schema = z.union([z.string(), z.number()]);\n expect(() => parseObject(text, schema)).toThrow(ParseObjectError);\n });\n\n it('includes original text in error', () => {\n const text = '{invalid json}';\n try {\n parseObject(text, articleSchema);\n throw new Error('Should have thrown');\n } catch (error) {\n expect(error).toBeInstanceOf(ParseObjectError);\n expect((error as ParseObjectError).text).toBe(text);\n }\n });\n });\n\n describe('complex scenarios', () => {\n it('parses complex nested JSON with escaped quotes', () => {\n const schema = z.object({\n category: z.string(),\n countries: z.array(z.string()),\n perspectives: z.array(\n z.object({\n digest: z.string(),\n tags: z.object({\n type: z.string(),\n stance: z.string(),\n }),\n }),\n ),\n synopsis: z.string(),\n });\n\n const text = `\\`\\`\\`json\n{\n \"category\": \"sports\",\n \"countries\": [\"us\"],\n \"perspectives\": [{\n \"digest\": \"The team's \\\\\"Big 3\\\\\" experiment failed.\",\n \"tags\": { \"type\": \"analysis\", \"stance\": \"neutral\" }\n }],\n \"synopsis\": \"A major trade occurred.\"\n}\n\\`\\`\\``;\n\n const result = parseObject(text, schema);\n expect(result.category).toBe('sports');\n expect(result.countries).toEqual(['us']);\n expect(result.perspectives[0]?.digest).toContain('\"Big 3\" experiment');\n expect(result.synopsis).toBe('A major trade occurred.');\n });\n\n it('handles text with multiple whitespace variations', () => {\n const text = `Here's the\\n\\n article: \\n\\n${validArticleJson}\\n\\n`;\n const result = parseObject(text, articleSchema);\n expect(result).toEqual(validArticle);\n });\n\n it('selects largest valid JSON when multiple structures present', () => {\n const smallJson = '{\"title\": \"Small\"}';\n const text = `First: ${smallJson}, Second: ${validArticleJson}`;\n const result = parseObject(text, articleSchema);\n expect(result).toEqual(validArticle);\n });\n });\n});\n"],"names":["describe","expect","it","z","parseObject","ParseObjectError","articleSchema","object","content","string","tags","array","title","validArticle","validArticleJson","JSON","stringify","text","result","toEqual","schema","toBe","number","boolean","null","toBeNull","toThrow","union","Error","error","toBeInstanceOf","category","countries","perspectives","digest","type","stance","synopsis","toContain","smallJson"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/parsing/__tests__/parse-object.test.ts"],"sourcesContent":["import { describe, expect, it } from 'vitest';\nimport { z } from 'zod/v4';\n\nimport { parseObject, ParseObjectError } from '../parse-object.js';\n\nconst articleSchema = z.object({\n content: z.string(),\n tags: z.array(z.string()),\n title: z.string(),\n});\n\nconst validArticle = {\n content: 'Test content',\n tags: ['test', 'ai'],\n title: 'Test Article',\n};\n\nconst validArticleJson = JSON.stringify(validArticle);\n\ndescribe('parseObject', () => {\n describe('object parsing', () => {\n it('parses valid JSON object', () => {\n const text = validArticleJson;\n const result = parseObject(text, articleSchema);\n expect(result).toEqual(validArticle);\n });\n\n it('extracts JSON object from surrounding prose', () => {\n const text = `Here's the article: ${validArticleJson} - end of article`;\n const result = parseObject(text, articleSchema);\n expect(result).toEqual(validArticle);\n });\n\n it('parses JSON from markdown code block', () => {\n const text = `\\`\\`\\`json\\n${validArticleJson}\\n\\`\\`\\``;\n const result = parseObject(text, articleSchema);\n expect(result).toEqual(validArticle);\n });\n\n it('handles newlines in JSON values', () => {\n const text = `{\n \"content\": \"Test\\\\ncontent\\\\nwith\\\\nnewlines\",\n \"tags\": [\"test\", \"ai\"],\n \"title\": \"Test\\\\nArticle\"\n }`;\n const result = parseObject(text, articleSchema);\n expect(result).toEqual({\n content: 'Test\\ncontent\\nwith\\nnewlines',\n tags: ['test', 'ai'],\n title: 'Test\\nArticle',\n });\n });\n\n it('handles escaped characters in JSON', () => {\n const text =\n '{\"content\": \"Test\\\\ncontent\\\\twith\\\\r\\\\nescapes\", \"tags\": [\"test\\\\u0020ai\", \"escaped\\\\\"quotes\\\\\"\"], \"title\": \"Test\\\\\\\\Article\"}';\n const result = parseObject(text, articleSchema);\n expect(result).toEqual({\n content: 'Test\\ncontent\\twith\\r\\nescapes',\n tags: ['test ai', 'escaped\"quotes\"'],\n title: 'Test\\\\Article',\n });\n });\n });\n\n describe('array parsing', () => {\n it('parses JSON array', () => {\n const text = '[\"test\", \"ai\", \"content\"]';\n const schema = z.array(z.string());\n const result = parseObject(text, schema);\n expect(result).toEqual(['test', 'ai', 'content']);\n });\n\n it('parses array of objects from markdown code block', () => {\n const text = `\\`\\`\\`json\\n[${validArticleJson}]\\n\\`\\`\\``;\n const schema = z.array(articleSchema);\n const result = parseObject(text, schema);\n expect(result).toEqual([validArticle]);\n });\n });\n\n describe('primitive parsing', () => {\n it('parses string value', () => {\n const text = '\"test string\"';\n const result = parseObject(text, z.string());\n expect(result).toBe('test string');\n });\n\n it('parses number value', () => {\n const text = '42';\n const result = parseObject(text, z.number());\n expect(result).toBe(42);\n });\n\n it('parses boolean value', () => {\n const text = 'true';\n const result = parseObject(text, z.boolean());\n expect(result).toBe(true);\n });\n\n it('parses null value', () => {\n const text = 'null';\n const result = parseObject(text, z.null());\n expect(result).toBeNull();\n });\n });\n\n describe('error handling', () => {\n it('throws ParseObjectError for invalid JSON', () => {\n const text = '{invalid json}';\n expect(() => parseObject(text, articleSchema)).toThrow(ParseObjectError);\n });\n\n it('throws ParseObjectError when schema validation fails', () => {\n const text = JSON.stringify({\n content: 'Test',\n tags: ['test'],\n title: 123,\n });\n expect(() => parseObject(text, articleSchema)).toThrow(ParseObjectError);\n });\n\n it('throws ParseObjectError when no object found', () => {\n const text = 'No JSON object here';\n expect(() => parseObject(text, articleSchema)).toThrow(ParseObjectError);\n });\n\n it('throws ParseObjectError when no array found', () => {\n const text = 'No array here';\n const schema = z.array(z.string());\n expect(() => parseObject(text, schema)).toThrow(ParseObjectError);\n });\n\n it('throws ParseObjectError for unsupported schema type', () => {\n const text = 'test';\n const schema = z.date();\n expect(() => parseObject(text, schema)).toThrow(ParseObjectError);\n });\n\n it('throws ParseObjectError for union when no object or array found', () => {\n const text = 'just plain text';\n const schema = z.union([\n z.object({ type: z.literal('a') }),\n z.object({ type: z.literal('b') }),\n ]);\n expect(() => parseObject(text, schema)).toThrow(ParseObjectError);\n });\n\n it('includes original text in error', () => {\n const text = '{invalid json}';\n try {\n parseObject(text, articleSchema);\n throw new Error('Should have thrown');\n } catch (error) {\n expect(error).toBeInstanceOf(ParseObjectError);\n expect((error as ParseObjectError).text).toBe(text);\n }\n });\n });\n\n describe('complex scenarios', () => {\n it('parses complex nested JSON with escaped quotes', () => {\n const schema = z.object({\n category: z.string(),\n countries: z.array(z.string()),\n perspectives: z.array(\n z.object({\n digest: z.string(),\n tags: z.object({\n type: z.string(),\n stance: z.string(),\n }),\n }),\n ),\n synopsis: z.string(),\n });\n\n const text = `\\`\\`\\`json\n{\n \"category\": \"sports\",\n \"countries\": [\"us\"],\n \"perspectives\": [{\n \"digest\": \"The team's \\\\\"Big 3\\\\\" experiment failed.\",\n \"tags\": { \"type\": \"analysis\", \"stance\": \"neutral\" }\n }],\n \"synopsis\": \"A major trade occurred.\"\n}\n\\`\\`\\``;\n\n const result = parseObject(text, schema);\n expect(result.category).toBe('sports');\n expect(result.countries).toEqual(['us']);\n expect(result.perspectives[0]?.digest).toContain('\"Big 3\" experiment');\n expect(result.synopsis).toBe('A major trade occurred.');\n });\n\n it('handles text with multiple whitespace variations', () => {\n const text = `Here's the\\n\\n article: \\n\\n${validArticleJson}\\n\\n`;\n const result = parseObject(text, articleSchema);\n expect(result).toEqual(validArticle);\n });\n\n it('selects largest valid JSON when multiple structures present', () => {\n const smallJson = '{\"title\": \"Small\"}';\n const text = `First: ${smallJson}, Second: ${validArticleJson}`;\n const result = parseObject(text, articleSchema);\n expect(result).toEqual(validArticle);\n });\n });\n\n describe('union types', () => {\n it('parses z.union - variant A', () => {\n const schema = z.union([\n z.object({ type: z.literal('a'), value: z.string() }),\n z.object({ type: z.literal('b'), count: z.number() }),\n ]);\n const text = '{\"type\": \"a\", \"value\": \"hello\"}';\n expect(parseObject(text, schema)).toEqual({ type: 'a', value: 'hello' });\n });\n\n it('parses z.union - variant B', () => {\n const schema = z.union([\n z.object({ type: z.literal('a'), value: z.string() }),\n z.object({ type: z.literal('b'), count: z.number() }),\n ]);\n const text = '{\"type\": \"b\", \"count\": 42}';\n expect(parseObject(text, schema)).toEqual({ type: 'b', count: 42 });\n });\n\n it('parses z.discriminatedUnion', () => {\n const schema = z.discriminatedUnion('action', [\n z.object({ action: z.literal('join'), eventId: z.string() }),\n z.object({ action: z.literal('create'), name: z.string() }),\n ]);\n const text = '```json\\n{\"action\": \"create\", \"name\": \"Test\"}\\n```';\n expect(parseObject(text, schema)).toEqual({ action: 'create', name: 'Test' });\n });\n\n it('parses discriminated union from surrounding prose', () => {\n const schema = z.discriminatedUnion('action', [\n z.object({ action: z.literal('join'), eventId: z.string() }),\n z.object({ action: z.literal('create'), name: z.string() }),\n ]);\n const text = 'Here is the result: {\"action\": \"join\", \"eventId\": \"evt-123\"} - done';\n expect(parseObject(text, schema)).toEqual({ action: 'join', eventId: 'evt-123' });\n });\n\n it('parses union of arrays', () => {\n const schema = z.union([z.array(z.string()), z.array(z.number())]);\n const text = '[\"a\", \"b\", \"c\"]';\n expect(parseObject(text, schema)).toEqual(['a', 'b', 'c']);\n });\n\n it('throws when union variant does not match', () => {\n const schema = z.discriminatedUnion('action', [\n z.object({ action: z.literal('join'), eventId: z.string() }),\n z.object({ action: z.literal('create'), name: z.string() }),\n ]);\n const text = '{\"action\": \"delete\", \"id\": \"123\"}';\n expect(() => parseObject(text, schema)).toThrow(ParseObjectError);\n });\n });\n\n describe('wrapper types', () => {\n it('parses z.optional wrapping an object', () => {\n const schema = z.object({ name: z.string() }).optional();\n const text = '{\"name\": \"test\"}';\n expect(parseObject(text, schema)).toEqual({ name: 'test' });\n });\n\n it('parses z.nullable wrapping an object', () => {\n const schema = z.object({ name: z.string() }).nullable();\n const text = '{\"name\": \"test\"}';\n expect(parseObject(text, schema)).toEqual({ name: 'test' });\n });\n\n it('parses z.default wrapping an object', () => {\n const schema = z.object({ name: z.string() }).default({ name: 'default' });\n const text = '{\"name\": \"custom\"}';\n expect(parseObject(text, schema)).toEqual({ name: 'custom' });\n });\n\n it('parses schema with .transform()', () => {\n const schema = z.object({ value: z.string() }).transform((obj) => ({\n ...obj,\n transformed: true,\n }));\n const text = '{\"value\": \"test\"}';\n const result = parseObject(text, schema);\n expect(result).toEqual({ value: 'test', transformed: true });\n });\n\n it('parses schema with .refine()', () => {\n const schema = z.object({ value: z.number() }).refine((obj) => obj.value > 0, {\n message: 'Value must be positive',\n });\n const text = '{\"value\": 42}';\n expect(parseObject(text, schema)).toEqual({ value: 42 });\n });\n\n it('parses deeply nested wrapper types', () => {\n const schema = z\n .object({ name: z.string() })\n .optional()\n .nullable()\n .default({ name: 'default' });\n const text = '{\"name\": \"nested\"}';\n expect(parseObject(text, schema)).toEqual({ name: 'nested' });\n });\n\n it('parses optional array', () => {\n const schema = z.array(z.string()).optional();\n const text = '[\"a\", \"b\", \"c\"]';\n expect(parseObject(text, schema)).toEqual(['a', 'b', 'c']);\n });\n });\n});\n"],"names":["describe","expect","it","z","parseObject","ParseObjectError","articleSchema","object","content","string","tags","array","title","validArticle","validArticleJson","JSON","stringify","text","result","toEqual","schema","toBe","number","boolean","null","toBeNull","toThrow","date","union","type","literal","Error","error","toBeInstanceOf","category","countries","perspectives","digest","stance","synopsis","toContain","smallJson","value","count","discriminatedUnion","action","eventId","name","optional","nullable","default","transform","obj","transformed","refine","message"],"mappings":"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"}
|
|
@@ -80,10 +80,18 @@ describe('parseText', function() {
|
|
|
80
80
|
it('converts figure dash with spaces to comma', function() {
|
|
81
81
|
expect(parseText('hello ‒ world')).toBe('hello, world');
|
|
82
82
|
});
|
|
83
|
+
it('converts em dash without spaces to comma', function() {
|
|
84
|
+
expect(parseText('disparaître—ne laissant')).toBe('disparaître, ne laissant');
|
|
85
|
+
});
|
|
83
86
|
it('can be disabled via options', function() {
|
|
84
87
|
expect(parseText('hello — world', {
|
|
85
88
|
normalizeEmDashesToCommas: false
|
|
86
|
-
})).toBe('hello
|
|
89
|
+
})).toBe('hello — world');
|
|
90
|
+
});
|
|
91
|
+
it('preserves em dash when disabled (no spaces)', function() {
|
|
92
|
+
expect(parseText('word—word', {
|
|
93
|
+
normalizeEmDashesToCommas: false
|
|
94
|
+
})).toBe('word—word');
|
|
87
95
|
});
|
|
88
96
|
});
|
|
89
97
|
describe('space-like character normalization', function() {
|
|
@@ -110,11 +118,11 @@ describe('parseText', function() {
|
|
|
110
118
|
it('converts left double quote to straight quote', function() {
|
|
111
119
|
expect(parseText('\u201CHello\u201D')).toBe('"Hello"');
|
|
112
120
|
});
|
|
113
|
-
it('converts em dash to
|
|
114
|
-
expect(parseText('word\u2014word')).toBe('word
|
|
121
|
+
it('converts em dash to comma', function() {
|
|
122
|
+
expect(parseText('word\u2014word')).toBe('word, word');
|
|
115
123
|
});
|
|
116
|
-
it('converts en dash to
|
|
117
|
-
expect(parseText('2020\u20132021')).toBe('2020
|
|
124
|
+
it('converts en dash to comma', function() {
|
|
125
|
+
expect(parseText('2020\u20132021')).toBe('2020, 2021');
|
|
118
126
|
});
|
|
119
127
|
it('converts ellipsis to three dots', function() {
|
|
120
128
|
expect(parseText('wait\u2026')).toBe('wait...');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/parsing/__tests__/parse-text.test.ts"],"sourcesContent":["import { describe, expect, it } from 'vitest';\n\nimport { parseText } from '../parse-text.js';\n\ndescribe('parseText', () => {\n describe('empty and basic input', () => {\n it('returns empty string for empty input', () => {\n expect(parseText('')).toBe('');\n });\n\n it('returns trimmed text for simple input', () => {\n expect(parseText(' hello world ')).toBe('hello world');\n });\n\n it('preserves newlines', () => {\n expect(parseText('hello\\nworld')).toBe('hello\\nworld');\n });\n });\n\n describe('BOM handling', () => {\n it('removes BOM character at start', () => {\n expect(parseText('\\uFEFFhello')).toBe('hello');\n });\n\n it('removes BOM in middle of text (via invisible char removal)', () => {\n expect(parseText('hello\\uFEFFworld')).toBe('helloworld');\n });\n });\n\n describe('line ending normalization', () => {\n it('converts CRLF to LF', () => {\n expect(parseText('hello\\r\\nworld')).toBe('hello\\nworld');\n });\n\n it('converts standalone CR to LF', () => {\n expect(parseText('hello\\rworld')).toBe('hello\\nworld');\n });\n });\n\n describe('AI citation removal', () => {\n it('removes oaicite markers', () => {\n expect(parseText('Some text (oaicite:0){index=0} more text')).toBe(\n 'Some text more text',\n );\n });\n\n it('removes multiple citation markers', () => {\n expect(parseText('Text (oaicite:1){index=1} and (oaicite:2){index=2} here')).toBe(\n 'Text and here',\n );\n });\n });\n\n describe('invisible character removal', () => {\n it('removes zero-width space', () => {\n expect(parseText('hello\\u200Bworld')).toBe('helloworld');\n });\n\n it('removes zero-width non-joiner', () => {\n expect(parseText('hello\\u200Cworld')).toBe('helloworld');\n });\n\n it('removes soft hyphen', () => {\n expect(parseText('hello\\u00ADworld')).toBe('helloworld');\n });\n\n it('removes direction marks', () => {\n expect(parseText('hello\\u200E\\u200Fworld')).toBe('helloworld');\n });\n\n it('removes word joiner', () => {\n expect(parseText('hello\\u2060world')).toBe('helloworld');\n });\n });\n\n describe('ASCII control character removal', () => {\n it('removes null character', () => {\n expect(parseText('hello\\x00world')).toBe('helloworld');\n });\n\n it('removes bell character', () => {\n expect(parseText('hello\\x07world')).toBe('helloworld');\n });\n\n it('removes delete character', () => {\n expect(parseText('hello\\x7Fworld')).toBe('helloworld');\n });\n\n it('preserves tab and newline', () => {\n expect(parseText('hello\\tworld\\n!')).toBe('hello\\tworld\\n!');\n });\n });\n\n describe('em/en dash normalization', () => {\n it('converts em dash with spaces to comma', () => {\n expect(parseText('hello — world')).toBe('hello, world');\n });\n\n it('converts en dash with spaces to comma', () => {\n expect(parseText('hello – world')).toBe('hello, world');\n });\n\n it('converts horizontal bar with spaces to comma', () => {\n expect(parseText('hello ― world')).toBe('hello, world');\n });\n\n it('converts figure dash with spaces to comma', () => {\n expect(parseText('hello ‒ world')).toBe('hello, world');\n });\n\n it('can be disabled via options', () => {\n expect(parseText('hello — world', { normalizeEmDashesToCommas: false })).toBe(\n 'hello - world',\n );\n });\n });\n\n describe('space-like character normalization', () => {\n it('converts non-breaking space to regular space', () => {\n expect(parseText('hello\\u00A0world')).toBe('hello world');\n });\n\n it('converts em space to regular space', () => {\n expect(parseText('hello\\u2003world')).toBe('hello world');\n });\n\n it('converts narrow no-break space to regular space', () => {\n expect(parseText('hello\\u202Fworld')).toBe('hello world');\n });\n\n it('converts ideographic space to regular space', () => {\n expect(parseText('hello\\u3000world')).toBe('hello world');\n });\n });\n\n describe('typography normalization', () => {\n it('converts left single quote to straight quote', () => {\n expect(parseText('it\\u2018s')).toBe(\"it's\");\n });\n\n it('converts right single quote to straight quote', () => {\n expect(parseText('it\\u2019s')).toBe(\"it's\");\n });\n\n it('converts left double quote to straight quote', () => {\n expect(parseText('\\u201CHello\\u201D')).toBe('\"Hello\"');\n });\n\n it('converts em dash to hyphen', () => {\n expect(parseText('word\\u2014word')).toBe('word-word');\n });\n\n it('converts en dash to hyphen', () => {\n expect(parseText('2020\\u20132021')).toBe('2020-2021');\n });\n\n it('converts ellipsis to three dots', () => {\n expect(parseText('wait\\u2026')).toBe('wait...');\n });\n\n it('converts bullet point to hyphen', () => {\n expect(parseText('\\u2022 item')).toBe('- item');\n });\n });\n\n describe('multiple space collapsing', () => {\n it('collapses multiple spaces to single space', () => {\n expect(parseText('hello world')).toBe('hello world');\n });\n\n it('trims leading and trailing spaces', () => {\n expect(parseText(' hello world ')).toBe('hello world');\n });\n\n it('can be disabled via options', () => {\n expect(parseText('hello world', { collapseSpaces: false })).toBe('hello world');\n });\n });\n\n describe('NFKC normalization', () => {\n it('normalizes fullwidth characters', () => {\n expect(parseText('\\uFF21\\uFF22\\uFF23')).toBe('ABC');\n });\n\n it('normalizes ligatures', () => {\n expect(parseText('\\uFB01le')).toBe('file');\n });\n });\n\n describe('combined scenarios', () => {\n it('handles AI-generated text with multiple issues', () => {\n const input =\n '\\uFEFF Hello\\u2019s world (oaicite:0){index=0} \\u2014 with\\u00A0spaces ';\n expect(parseText(input)).toBe(\"Hello's world, with spaces\");\n });\n\n it('handles markdown with smart quotes and dashes', () => {\n const input = '\\u201CThis is a quote\\u201D \\u2014 Author';\n expect(parseText(input)).toBe('\"This is a quote\", Author');\n });\n });\n});\n"],"names":["describe","expect","it","parseText","toBe","normalizeEmDashesToCommas","collapseSpaces","input"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/parsing/__tests__/parse-text.test.ts"],"sourcesContent":["import { describe, expect, it } from 'vitest';\n\nimport { parseText } from '../parse-text.js';\n\ndescribe('parseText', () => {\n describe('empty and basic input', () => {\n it('returns empty string for empty input', () => {\n expect(parseText('')).toBe('');\n });\n\n it('returns trimmed text for simple input', () => {\n expect(parseText(' hello world ')).toBe('hello world');\n });\n\n it('preserves newlines', () => {\n expect(parseText('hello\\nworld')).toBe('hello\\nworld');\n });\n });\n\n describe('BOM handling', () => {\n it('removes BOM character at start', () => {\n expect(parseText('\\uFEFFhello')).toBe('hello');\n });\n\n it('removes BOM in middle of text (via invisible char removal)', () => {\n expect(parseText('hello\\uFEFFworld')).toBe('helloworld');\n });\n });\n\n describe('line ending normalization', () => {\n it('converts CRLF to LF', () => {\n expect(parseText('hello\\r\\nworld')).toBe('hello\\nworld');\n });\n\n it('converts standalone CR to LF', () => {\n expect(parseText('hello\\rworld')).toBe('hello\\nworld');\n });\n });\n\n describe('AI citation removal', () => {\n it('removes oaicite markers', () => {\n expect(parseText('Some text (oaicite:0){index=0} more text')).toBe(\n 'Some text more text',\n );\n });\n\n it('removes multiple citation markers', () => {\n expect(parseText('Text (oaicite:1){index=1} and (oaicite:2){index=2} here')).toBe(\n 'Text and here',\n );\n });\n });\n\n describe('invisible character removal', () => {\n it('removes zero-width space', () => {\n expect(parseText('hello\\u200Bworld')).toBe('helloworld');\n });\n\n it('removes zero-width non-joiner', () => {\n expect(parseText('hello\\u200Cworld')).toBe('helloworld');\n });\n\n it('removes soft hyphen', () => {\n expect(parseText('hello\\u00ADworld')).toBe('helloworld');\n });\n\n it('removes direction marks', () => {\n expect(parseText('hello\\u200E\\u200Fworld')).toBe('helloworld');\n });\n\n it('removes word joiner', () => {\n expect(parseText('hello\\u2060world')).toBe('helloworld');\n });\n });\n\n describe('ASCII control character removal', () => {\n it('removes null character', () => {\n expect(parseText('hello\\x00world')).toBe('helloworld');\n });\n\n it('removes bell character', () => {\n expect(parseText('hello\\x07world')).toBe('helloworld');\n });\n\n it('removes delete character', () => {\n expect(parseText('hello\\x7Fworld')).toBe('helloworld');\n });\n\n it('preserves tab and newline', () => {\n expect(parseText('hello\\tworld\\n!')).toBe('hello\\tworld\\n!');\n });\n });\n\n describe('em/en dash normalization', () => {\n it('converts em dash with spaces to comma', () => {\n expect(parseText('hello — world')).toBe('hello, world');\n });\n\n it('converts en dash with spaces to comma', () => {\n expect(parseText('hello – world')).toBe('hello, world');\n });\n\n it('converts horizontal bar with spaces to comma', () => {\n expect(parseText('hello ― world')).toBe('hello, world');\n });\n\n it('converts figure dash with spaces to comma', () => {\n expect(parseText('hello ‒ world')).toBe('hello, world');\n });\n\n it('converts em dash without spaces to comma', () => {\n expect(parseText('disparaître—ne laissant')).toBe('disparaître, ne laissant');\n });\n\n it('can be disabled via options', () => {\n expect(parseText('hello — world', { normalizeEmDashesToCommas: false })).toBe(\n 'hello — world',\n );\n });\n\n it('preserves em dash when disabled (no spaces)', () => {\n expect(parseText('word—word', { normalizeEmDashesToCommas: false })).toBe('word—word');\n });\n });\n\n describe('space-like character normalization', () => {\n it('converts non-breaking space to regular space', () => {\n expect(parseText('hello\\u00A0world')).toBe('hello world');\n });\n\n it('converts em space to regular space', () => {\n expect(parseText('hello\\u2003world')).toBe('hello world');\n });\n\n it('converts narrow no-break space to regular space', () => {\n expect(parseText('hello\\u202Fworld')).toBe('hello world');\n });\n\n it('converts ideographic space to regular space', () => {\n expect(parseText('hello\\u3000world')).toBe('hello world');\n });\n });\n\n describe('typography normalization', () => {\n it('converts left single quote to straight quote', () => {\n expect(parseText('it\\u2018s')).toBe(\"it's\");\n });\n\n it('converts right single quote to straight quote', () => {\n expect(parseText('it\\u2019s')).toBe(\"it's\");\n });\n\n it('converts left double quote to straight quote', () => {\n expect(parseText('\\u201CHello\\u201D')).toBe('\"Hello\"');\n });\n\n it('converts em dash to comma', () => {\n expect(parseText('word\\u2014word')).toBe('word, word');\n });\n\n it('converts en dash to comma', () => {\n expect(parseText('2020\\u20132021')).toBe('2020, 2021');\n });\n\n it('converts ellipsis to three dots', () => {\n expect(parseText('wait\\u2026')).toBe('wait...');\n });\n\n it('converts bullet point to hyphen', () => {\n expect(parseText('\\u2022 item')).toBe('- item');\n });\n });\n\n describe('multiple space collapsing', () => {\n it('collapses multiple spaces to single space', () => {\n expect(parseText('hello world')).toBe('hello world');\n });\n\n it('trims leading and trailing spaces', () => {\n expect(parseText(' hello world ')).toBe('hello world');\n });\n\n it('can be disabled via options', () => {\n expect(parseText('hello world', { collapseSpaces: false })).toBe('hello world');\n });\n });\n\n describe('NFKC normalization', () => {\n it('normalizes fullwidth characters', () => {\n expect(parseText('\\uFF21\\uFF22\\uFF23')).toBe('ABC');\n });\n\n it('normalizes ligatures', () => {\n expect(parseText('\\uFB01le')).toBe('file');\n });\n });\n\n describe('combined scenarios', () => {\n it('handles AI-generated text with multiple issues', () => {\n const input =\n '\\uFEFF Hello\\u2019s world (oaicite:0){index=0} \\u2014 with\\u00A0spaces ';\n expect(parseText(input)).toBe(\"Hello's world, with spaces\");\n });\n\n it('handles markdown with smart quotes and dashes', () => {\n const input = '\\u201CThis is a quote\\u201D \\u2014 Author';\n expect(parseText(input)).toBe('\"This is a quote\", Author');\n });\n });\n});\n"],"names":["describe","expect","it","parseText","toBe","normalizeEmDashesToCommas","collapseSpaces","input"],"mappings":"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"}
|
|
@@ -254,6 +254,29 @@ function extractBySchemaType(text, schema, originalText) {
|
|
|
254
254
|
if (_instanceof(schema, z.ZodBoolean) || _instanceof(schema, z.ZodNull) || _instanceof(schema, z.ZodNumber) || _instanceof(schema, z.ZodString)) {
|
|
255
255
|
return extractPrimitive(text, schema);
|
|
256
256
|
}
|
|
257
|
+
// Handle union types - extract as object/array and let Zod validate which variant matches
|
|
258
|
+
if (_instanceof(schema, z.ZodUnion) || _instanceof(schema, z.ZodDiscriminatedUnion)) {
|
|
259
|
+
var objectStart = text.indexOf('{');
|
|
260
|
+
if (objectStart !== -1) {
|
|
261
|
+
return extractObject(text, originalText);
|
|
262
|
+
}
|
|
263
|
+
var arrayStart = text.indexOf('[');
|
|
264
|
+
if (arrayStart !== -1) {
|
|
265
|
+
return extractArray(text, originalText);
|
|
266
|
+
}
|
|
267
|
+
throw new ParseObjectError('No object or array found for union type', undefined, originalText);
|
|
268
|
+
}
|
|
269
|
+
// Handle wrapper types - unwrap and delegate to inner type
|
|
270
|
+
if (_instanceof(schema, z.ZodOptional) || _instanceof(schema, z.ZodNullable)) {
|
|
271
|
+
return extractBySchemaType(text, schema.unwrap(), originalText);
|
|
272
|
+
}
|
|
273
|
+
if (_instanceof(schema, z.ZodDefault)) {
|
|
274
|
+
return extractBySchemaType(text, schema.def.innerType, originalText);
|
|
275
|
+
}
|
|
276
|
+
// Handle .transform() which creates a ZodPipe in Zod v4
|
|
277
|
+
if (_instanceof(schema, z.ZodPipe)) {
|
|
278
|
+
return extractBySchemaType(text, schema.def["in"], originalText);
|
|
279
|
+
}
|
|
257
280
|
throw new ParseObjectError('Unsupported schema type', undefined, originalText);
|
|
258
281
|
}
|
|
259
282
|
function extractJsonFromCodeBlock(block) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/parsing/parse-object.ts"],"sourcesContent":["import { jsonrepair } from 'jsonrepair';\nimport { z } from 'zod/v4';\n\n/**\n * Error thrown when object parsing fails.\n * Contains the original text for debugging purposes.\n */\nexport class ParseObjectError extends Error {\n public readonly name = 'ParseObjectError';\n\n constructor(\n message: string,\n public readonly cause?: unknown,\n public readonly text?: string,\n ) {\n super(message);\n }\n}\n\n/**\n * Parses AI-generated text into structured data validated against a Zod schema.\n *\n * Handles common AI response formats:\n * - JSON wrapped in markdown code blocks\n * - JSON embedded in prose text\n * - Malformed JSON (auto-repaired)\n * - Escaped unicode and special characters\n *\n * @param text - The raw AI response text\n * @param schema - A Zod schema to validate and type the result\n * @returns The parsed and validated data\n * @throws {ParseObjectError} When parsing or validation fails\n *\n * @example\n * ```ts\n * const schema = z.object({ title: z.string(), tags: z.array(z.string()) });\n * const result = parseObject(aiResponse, schema);\n * // result is typed as { title: string; tags: string[] }\n * ```\n */\nexport function parseObject<T>(text: string, schema: z.ZodSchema<T>): T {\n try {\n const jsonString = extractJsonString(text);\n const extracted = extractBySchemaType(jsonString, schema, text);\n const unescaped = unescapeJsonValues(extracted);\n return schema.parse(unescaped);\n } catch (error) {\n if (error instanceof ParseObjectError) {\n throw error;\n }\n if (error instanceof z.ZodError) {\n throw new ParseObjectError('Failed to validate response against schema', error, text);\n }\n throw error;\n }\n}\n\nconst MARKDOWN_CODE_BLOCK_RE = /```(?:json)?\\r?\\n([^`]*?)\\r?\\n```/g;\n\nfunction convertToPrimitive(value: unknown, schema: z.ZodType): unknown {\n if (schema instanceof z.ZodBoolean) return Boolean(value);\n if (schema instanceof z.ZodNull) return null;\n if (schema instanceof z.ZodNumber) return Number(value);\n if (schema instanceof z.ZodString) return String(value);\n return value;\n}\n\nfunction extractArray(text: string, originalText: string): unknown {\n const start = text.indexOf('[');\n const end = text.lastIndexOf(']');\n\n if (start === -1 || end === -1) {\n throw new ParseObjectError('No array found in response', undefined, originalText);\n }\n\n try {\n const raw = text.slice(start, end + 1);\n return JSON.parse(jsonrepair(raw));\n } catch (error) {\n throw new ParseObjectError('Failed to parse array JSON', error, originalText);\n }\n}\n\nfunction extractBySchemaType(text: string, schema: z.ZodType, originalText: string): unknown {\n if (schema instanceof z.ZodArray) {\n return extractArray(text, originalText);\n }\n\n if (schema instanceof z.ZodObject) {\n return extractObject(text, originalText);\n }\n\n if (\n schema instanceof z.ZodBoolean ||\n schema instanceof z.ZodNull ||\n schema instanceof z.ZodNumber ||\n schema instanceof z.ZodString\n ) {\n return extractPrimitive(text, schema);\n }\n\n throw new ParseObjectError('Unsupported schema type', undefined, originalText);\n}\n\nfunction extractJsonFromCodeBlock(block: string): null | string {\n const content = block.replace(/```(?:json)?\\r?\\n([^`]*?)\\r?\\n```/, '$1').trim();\n try {\n JSON.parse(content);\n return content;\n } catch {\n return null;\n }\n}\n\nfunction extractJsonString(text: string): string {\n const codeBlocks = text.match(MARKDOWN_CODE_BLOCK_RE);\n if (codeBlocks && codeBlocks.length > 0) {\n const validBlocks = codeBlocks\n .map((block) => extractJsonFromCodeBlock(block))\n .filter((block): block is string => block !== null);\n\n if (validBlocks.length > 0) {\n return findLongestString(validBlocks);\n }\n }\n\n const structures = findJsonStructures(text);\n if (structures.length > 0) {\n return findLongestString(structures);\n }\n\n return text.replace(/\\s+/g, ' ').trim();\n}\n\nfunction extractObject(text: string, originalText: string): unknown {\n const start = text.indexOf('{');\n const end = text.lastIndexOf('}');\n\n if (start === -1 || end === -1) {\n throw new ParseObjectError('No object found in response', undefined, originalText);\n }\n\n try {\n const raw = text.slice(start, end + 1);\n return JSON.parse(jsonrepair(raw));\n } catch (error) {\n throw new ParseObjectError('Failed to parse object JSON', error, originalText);\n }\n}\n\nfunction extractPrimitive(text: string, schema: z.ZodType): unknown {\n const trimmed = text.trim();\n try {\n return convertToPrimitive(JSON.parse(trimmed), schema);\n } catch {\n return convertToPrimitive(trimmed, schema);\n }\n}\n\nfunction findJsonStructures(text: string): string[] {\n const matches: string[] = [];\n let depth = 0;\n let start = -1;\n\n for (let i = 0; i < text.length; i++) {\n const char = text[i];\n if (char === '{' || char === '[') {\n if (depth === 0) start = i;\n depth++;\n } else if (char === '}' || char === ']') {\n depth--;\n if (depth === 0 && start !== -1) {\n const candidate = text.slice(start, i + 1);\n try {\n JSON.parse(candidate);\n matches.push(candidate);\n } catch {\n // Invalid JSON, skip\n }\n }\n }\n }\n\n return matches;\n}\n\nfunction findLongestString(strings: string[]): string {\n return strings.reduce((longest, current) =>\n current.length > longest.length ? current : longest,\n );\n}\n\nfunction unescapeJsonValues(json: unknown): unknown {\n if (typeof json === 'string') {\n return unescapeString(json);\n }\n if (Array.isArray(json)) {\n return json.map(unescapeJsonValues);\n }\n if (typeof json === 'object' && json !== null) {\n return Object.fromEntries(\n Object.entries(json).map(([key, value]) => [key, unescapeJsonValues(value)]),\n );\n }\n return json;\n}\n\nfunction unescapeString(text: string): string {\n return text\n .replace(/\\\\\"/g, '\"')\n .replace(/\\\\n/g, '\\n')\n .replace(/\\\\r/g, '\\r')\n .replace(/\\\\t/g, '\\t')\n .replace(/\\\\\\\\/g, '\\\\')\n .replace(/\\\\u([0-9a-fA-F]{4})/g, (_, code) =>\n String.fromCharCode(Number.parseInt(code, 16)),\n );\n}\n"],"names":["jsonrepair","z","ParseObjectError","message","cause","text","name","Error","parseObject","schema","jsonString","extractJsonString","extracted","extractBySchemaType","unescaped","unescapeJsonValues","parse","error","ZodError","MARKDOWN_CODE_BLOCK_RE","convertToPrimitive","value","ZodBoolean","Boolean","ZodNull","ZodNumber","Number","ZodString","String","extractArray","originalText","start","indexOf","end","lastIndexOf","undefined","raw","slice","JSON","ZodArray","ZodObject","extractObject","extractPrimitive","extractJsonFromCodeBlock","block","content","replace","trim","codeBlocks","match","length","validBlocks","map","filter","findLongestString","structures","findJsonStructures","trimmed","matches","depth","i","char","candidate","push","strings","reduce","longest","current","json","unescapeString","Array","isArray","Object","fromEntries","entries","key","_","code","fromCharCode","parseInt"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAASA,UAAU,QAAQ,aAAa;AACxC,SAASC,CAAC,QAAQ,SAAS;AAE3B;;;CAGC,GACD,OAAO,IAAA,AAAMC,iCAAN;;cAAMA;aAAAA,iBAILC,OAAe,EACf,AAAgBC,KAAe,EAC/B,AAAgBC,IAAa;gCANxBH;;gBAQL,kBARKA;YAQCC;+FAPV,wBAAgBG,QAAhB,KAAA,UAIoBF,QAAAA,aACAC,OAAAA,YALJC,OAAO;;;WADdJ;qBAAyBK,QAUrC;AAED;;;;;;;;;;;;;;;;;;;;CAoBC,GACD,OAAO,SAASC,YAAeH,IAAY,EAAEI,MAAsB;IAC/D,IAAI;QACA,IAAMC,aAAaC,kBAAkBN;QACrC,IAAMO,YAAYC,oBAAoBH,YAAYD,QAAQJ;QAC1D,IAAMS,YAAYC,mBAAmBH;QACrC,OAAOH,OAAOO,KAAK,CAACF;IACxB,EAAE,OAAOG,OAAO;QACZ,IAAIA,AAAK,YAALA,OAAiBf,mBAAkB;YACnC,MAAMe;QACV;QACA,IAAIA,AAAK,YAALA,OAAiBhB,EAAEiB,QAAQ,GAAE;YAC7B,MAAM,IAAIhB,iBAAiB,8CAA8Ce,OAAOZ;QACpF;QACA,MAAMY;IACV;AACJ;AAEA,IAAME,yBAAyB;AAE/B,SAASC,mBAAmBC,KAAc,EAAEZ,MAAiB;IACzD,IAAIA,AAAM,YAANA,QAAkBR,EAAEqB,UAAU,GAAE,OAAOC,QAAQF;IACnD,IAAIZ,AAAM,YAANA,QAAkBR,EAAEuB,OAAO,GAAE,OAAO;IACxC,IAAIf,AAAM,YAANA,QAAkBR,EAAEwB,SAAS,GAAE,OAAOC,OAAOL;IACjD,IAAIZ,AAAM,YAANA,QAAkBR,EAAE0B,SAAS,GAAE,OAAOC,OAAOP;IACjD,OAAOA;AACX;AAEA,SAASQ,aAAaxB,IAAY,EAAEyB,YAAoB;IACpD,IAAMC,QAAQ1B,KAAK2B,OAAO,CAAC;IAC3B,IAAMC,MAAM5B,KAAK6B,WAAW,CAAC;IAE7B,IAAIH,UAAU,CAAC,KAAKE,QAAQ,CAAC,GAAG;QAC5B,MAAM,IAAI/B,iBAAiB,8BAA8BiC,WAAWL;IACxE;IAEA,IAAI;QACA,IAAMM,MAAM/B,KAAKgC,KAAK,CAACN,OAAOE,MAAM;QACpC,OAAOK,KAAKtB,KAAK,CAAChB,WAAWoC;IACjC,EAAE,OAAOnB,OAAO;QACZ,MAAM,IAAIf,iBAAiB,8BAA8Be,OAAOa;IACpE;AACJ;AAEA,SAASjB,oBAAoBR,IAAY,EAAEI,MAAiB,EAAEqB,YAAoB;IAC9E,IAAIrB,AAAM,YAANA,QAAkBR,EAAEsC,QAAQ,GAAE;QAC9B,OAAOV,aAAaxB,MAAMyB;IAC9B;IAEA,IAAIrB,AAAM,YAANA,QAAkBR,EAAEuC,SAAS,GAAE;QAC/B,OAAOC,cAAcpC,MAAMyB;IAC/B;IAEA,IACIrB,AAAM,YAANA,QAAkBR,EAAEqB,UAAU,KAC9Bb,AAAM,YAANA,QAAkBR,EAAEuB,OAAO,KAC3Bf,AAAM,YAANA,QAAkBR,EAAEwB,SAAS,KAC7BhB,AAAM,YAANA,QAAkBR,EAAE0B,SAAS,GAC/B;QACE,OAAOe,iBAAiBrC,MAAMI;IAClC;IAEA,MAAM,IAAIP,iBAAiB,2BAA2BiC,WAAWL;AACrE;AAEA,SAASa,yBAAyBC,KAAa;IAC3C,IAAMC,UAAUD,MAAME,OAAO,CAAC,qCAAqC,MAAMC,IAAI;IAC7E,IAAI;QACAT,KAAKtB,KAAK,CAAC6B;QACX,OAAOA;IACX,EAAE,UAAM;QACJ,OAAO;IACX;AACJ;AAEA,SAASlC,kBAAkBN,IAAY;IACnC,IAAM2C,aAAa3C,KAAK4C,KAAK,CAAC9B;IAC9B,IAAI6B,cAAcA,WAAWE,MAAM,GAAG,GAAG;QACrC,IAAMC,cAAcH,WACfI,GAAG,CAAC,SAACR;mBAAUD,yBAAyBC;WACxCS,MAAM,CAAC,SAACT;mBAA2BA,UAAU;;QAElD,IAAIO,YAAYD,MAAM,GAAG,GAAG;YACxB,OAAOI,kBAAkBH;QAC7B;IACJ;IAEA,IAAMI,aAAaC,mBAAmBnD;IACtC,IAAIkD,WAAWL,MAAM,GAAG,GAAG;QACvB,OAAOI,kBAAkBC;IAC7B;IAEA,OAAOlD,KAAKyC,OAAO,CAAC,QAAQ,KAAKC,IAAI;AACzC;AAEA,SAASN,cAAcpC,IAAY,EAAEyB,YAAoB;IACrD,IAAMC,QAAQ1B,KAAK2B,OAAO,CAAC;IAC3B,IAAMC,MAAM5B,KAAK6B,WAAW,CAAC;IAE7B,IAAIH,UAAU,CAAC,KAAKE,QAAQ,CAAC,GAAG;QAC5B,MAAM,IAAI/B,iBAAiB,+BAA+BiC,WAAWL;IACzE;IAEA,IAAI;QACA,IAAMM,MAAM/B,KAAKgC,KAAK,CAACN,OAAOE,MAAM;QACpC,OAAOK,KAAKtB,KAAK,CAAChB,WAAWoC;IACjC,EAAE,OAAOnB,OAAO;QACZ,MAAM,IAAIf,iBAAiB,+BAA+Be,OAAOa;IACrE;AACJ;AAEA,SAASY,iBAAiBrC,IAAY,EAAEI,MAAiB;IACrD,IAAMgD,UAAUpD,KAAK0C,IAAI;IACzB,IAAI;QACA,OAAO3B,mBAAmBkB,KAAKtB,KAAK,CAACyC,UAAUhD;IACnD,EAAE,UAAM;QACJ,OAAOW,mBAAmBqC,SAAShD;IACvC;AACJ;AAEA,SAAS+C,mBAAmBnD,IAAY;IACpC,IAAMqD,UAAoB,EAAE;IAC5B,IAAIC,QAAQ;IACZ,IAAI5B,QAAQ,CAAC;IAEb,IAAK,IAAI6B,IAAI,GAAGA,IAAIvD,KAAK6C,MAAM,EAAEU,IAAK;QAClC,IAAMC,QAAOxD,IAAI,CAACuD,EAAE;QACpB,IAAIC,UAAS,OAAOA,UAAS,KAAK;YAC9B,IAAIF,UAAU,GAAG5B,QAAQ6B;YACzBD;QACJ,OAAO,IAAIE,UAAS,OAAOA,UAAS,KAAK;YACrCF;YACA,IAAIA,UAAU,KAAK5B,UAAU,CAAC,GAAG;gBAC7B,IAAM+B,YAAYzD,KAAKgC,KAAK,CAACN,OAAO6B,IAAI;gBACxC,IAAI;oBACAtB,KAAKtB,KAAK,CAAC8C;oBACXJ,QAAQK,IAAI,CAACD;gBACjB,EAAE,UAAM;gBACJ,qBAAqB;gBACzB;YACJ;QACJ;IACJ;IAEA,OAAOJ;AACX;AAEA,SAASJ,kBAAkBU,OAAiB;IACxC,OAAOA,QAAQC,MAAM,CAAC,SAACC,SAASC;eAC5BA,QAAQjB,MAAM,GAAGgB,QAAQhB,MAAM,GAAGiB,UAAUD;;AAEpD;AAEA,SAASnD,mBAAmBqD,IAAa;IACrC,IAAI,OAAOA,SAAS,UAAU;QAC1B,OAAOC,eAAeD;IAC1B;IACA,IAAIE,MAAMC,OAAO,CAACH,OAAO;QACrB,OAAOA,KAAKhB,GAAG,CAACrC;IACpB;IACA,IAAI,CAAA,OAAOqD,qCAAP,SAAOA,KAAG,MAAM,YAAYA,SAAS,MAAM;QAC3C,OAAOI,OAAOC,WAAW,CACrBD,OAAOE,OAAO,CAACN,MAAMhB,GAAG,CAAC;qDAAEuB,iBAAKtD;mBAAW;gBAACsD;gBAAK5D,mBAAmBM;aAAO;;IAEnF;IACA,OAAO+C;AACX;AAEA,SAASC,eAAehE,IAAY;IAChC,OAAOA,KACFyC,OAAO,CAAC,QAAQ,KAChBA,OAAO,CAAC,QAAQ,MAChBA,OAAO,CAAC,QAAQ,MAChBA,OAAO,CAAC,QAAQ,MAChBA,OAAO,CAAC,SAAS,MACjBA,OAAO,CAAC,wBAAwB,SAAC8B,GAAGC;eACjCjD,OAAOkD,YAAY,CAACpD,OAAOqD,QAAQ,CAACF,MAAM;;AAEtD"}
|
|
1
|
+
{"version":3,"sources":["../../src/parsing/parse-object.ts"],"sourcesContent":["import { jsonrepair } from 'jsonrepair';\nimport { z } from 'zod/v4';\n\n/**\n * Error thrown when object parsing fails.\n * Contains the original text for debugging purposes.\n */\nexport class ParseObjectError extends Error {\n public readonly name = 'ParseObjectError';\n\n constructor(\n message: string,\n public readonly cause?: unknown,\n public readonly text?: string,\n ) {\n super(message);\n }\n}\n\n/**\n * Parses AI-generated text into structured data validated against a Zod schema.\n *\n * Handles common AI response formats:\n * - JSON wrapped in markdown code blocks\n * - JSON embedded in prose text\n * - Malformed JSON (auto-repaired)\n * - Escaped unicode and special characters\n *\n * @param text - The raw AI response text\n * @param schema - A Zod schema to validate and type the result\n * @returns The parsed and validated data\n * @throws {ParseObjectError} When parsing or validation fails\n *\n * @example\n * ```ts\n * const schema = z.object({ title: z.string(), tags: z.array(z.string()) });\n * const result = parseObject(aiResponse, schema);\n * // result is typed as { title: string; tags: string[] }\n * ```\n */\nexport function parseObject<T>(text: string, schema: z.ZodSchema<T>): T {\n try {\n const jsonString = extractJsonString(text);\n const extracted = extractBySchemaType(jsonString, schema, text);\n const unescaped = unescapeJsonValues(extracted);\n return schema.parse(unescaped);\n } catch (error) {\n if (error instanceof ParseObjectError) {\n throw error;\n }\n if (error instanceof z.ZodError) {\n throw new ParseObjectError('Failed to validate response against schema', error, text);\n }\n throw error;\n }\n}\n\nconst MARKDOWN_CODE_BLOCK_RE = /```(?:json)?\\r?\\n([^`]*?)\\r?\\n```/g;\n\nfunction convertToPrimitive(value: unknown, schema: z.ZodType): unknown {\n if (schema instanceof z.ZodBoolean) return Boolean(value);\n if (schema instanceof z.ZodNull) return null;\n if (schema instanceof z.ZodNumber) return Number(value);\n if (schema instanceof z.ZodString) return String(value);\n return value;\n}\n\nfunction extractArray(text: string, originalText: string): unknown {\n const start = text.indexOf('[');\n const end = text.lastIndexOf(']');\n\n if (start === -1 || end === -1) {\n throw new ParseObjectError('No array found in response', undefined, originalText);\n }\n\n try {\n const raw = text.slice(start, end + 1);\n return JSON.parse(jsonrepair(raw));\n } catch (error) {\n throw new ParseObjectError('Failed to parse array JSON', error, originalText);\n }\n}\n\nfunction extractBySchemaType(text: string, schema: z.ZodType, originalText: string): unknown {\n if (schema instanceof z.ZodArray) {\n return extractArray(text, originalText);\n }\n\n if (schema instanceof z.ZodObject) {\n return extractObject(text, originalText);\n }\n\n if (\n schema instanceof z.ZodBoolean ||\n schema instanceof z.ZodNull ||\n schema instanceof z.ZodNumber ||\n schema instanceof z.ZodString\n ) {\n return extractPrimitive(text, schema);\n }\n\n // Handle union types - extract as object/array and let Zod validate which variant matches\n if (schema instanceof z.ZodUnion || schema instanceof z.ZodDiscriminatedUnion) {\n const objectStart = text.indexOf('{');\n if (objectStart !== -1) {\n return extractObject(text, originalText);\n }\n const arrayStart = text.indexOf('[');\n if (arrayStart !== -1) {\n return extractArray(text, originalText);\n }\n throw new ParseObjectError(\n 'No object or array found for union type',\n undefined,\n originalText,\n );\n }\n\n // Handle wrapper types - unwrap and delegate to inner type\n if (schema instanceof z.ZodOptional || schema instanceof z.ZodNullable) {\n return extractBySchemaType(text, schema.unwrap() as z.ZodType, originalText);\n }\n\n if (schema instanceof z.ZodDefault) {\n return extractBySchemaType(text, schema.def.innerType as z.ZodType, originalText);\n }\n\n // Handle .transform() which creates a ZodPipe in Zod v4\n if (schema instanceof z.ZodPipe) {\n return extractBySchemaType(text, schema.def.in as z.ZodType, originalText);\n }\n\n throw new ParseObjectError('Unsupported schema type', undefined, originalText);\n}\n\nfunction extractJsonFromCodeBlock(block: string): null | string {\n const content = block.replace(/```(?:json)?\\r?\\n([^`]*?)\\r?\\n```/, '$1').trim();\n try {\n JSON.parse(content);\n return content;\n } catch {\n return null;\n }\n}\n\nfunction extractJsonString(text: string): string {\n const codeBlocks = text.match(MARKDOWN_CODE_BLOCK_RE);\n if (codeBlocks && codeBlocks.length > 0) {\n const validBlocks = codeBlocks\n .map((block) => extractJsonFromCodeBlock(block))\n .filter((block): block is string => block !== null);\n\n if (validBlocks.length > 0) {\n return findLongestString(validBlocks);\n }\n }\n\n const structures = findJsonStructures(text);\n if (structures.length > 0) {\n return findLongestString(structures);\n }\n\n return text.replace(/\\s+/g, ' ').trim();\n}\n\nfunction extractObject(text: string, originalText: string): unknown {\n const start = text.indexOf('{');\n const end = text.lastIndexOf('}');\n\n if (start === -1 || end === -1) {\n throw new ParseObjectError('No object found in response', undefined, originalText);\n }\n\n try {\n const raw = text.slice(start, end + 1);\n return JSON.parse(jsonrepair(raw));\n } catch (error) {\n throw new ParseObjectError('Failed to parse object JSON', error, originalText);\n }\n}\n\nfunction extractPrimitive(text: string, schema: z.ZodType): unknown {\n const trimmed = text.trim();\n try {\n return convertToPrimitive(JSON.parse(trimmed), schema);\n } catch {\n return convertToPrimitive(trimmed, schema);\n }\n}\n\nfunction findJsonStructures(text: string): string[] {\n const matches: string[] = [];\n let depth = 0;\n let start = -1;\n\n for (let i = 0; i < text.length; i++) {\n const char = text[i];\n if (char === '{' || char === '[') {\n if (depth === 0) start = i;\n depth++;\n } else if (char === '}' || char === ']') {\n depth--;\n if (depth === 0 && start !== -1) {\n const candidate = text.slice(start, i + 1);\n try {\n JSON.parse(candidate);\n matches.push(candidate);\n } catch {\n // Invalid JSON, skip\n }\n }\n }\n }\n\n return matches;\n}\n\nfunction findLongestString(strings: string[]): string {\n return strings.reduce((longest, current) =>\n current.length > longest.length ? current : longest,\n );\n}\n\nfunction unescapeJsonValues(json: unknown): unknown {\n if (typeof json === 'string') {\n return unescapeString(json);\n }\n if (Array.isArray(json)) {\n return json.map(unescapeJsonValues);\n }\n if (typeof json === 'object' && json !== null) {\n return Object.fromEntries(\n Object.entries(json).map(([key, value]) => [key, unescapeJsonValues(value)]),\n );\n }\n return json;\n}\n\nfunction unescapeString(text: string): string {\n return text\n .replace(/\\\\\"/g, '\"')\n .replace(/\\\\n/g, '\\n')\n .replace(/\\\\r/g, '\\r')\n .replace(/\\\\t/g, '\\t')\n .replace(/\\\\\\\\/g, '\\\\')\n .replace(/\\\\u([0-9a-fA-F]{4})/g, (_, code) =>\n String.fromCharCode(Number.parseInt(code, 16)),\n );\n}\n"],"names":["jsonrepair","z","ParseObjectError","message","cause","text","name","Error","parseObject","schema","jsonString","extractJsonString","extracted","extractBySchemaType","unescaped","unescapeJsonValues","parse","error","ZodError","MARKDOWN_CODE_BLOCK_RE","convertToPrimitive","value","ZodBoolean","Boolean","ZodNull","ZodNumber","Number","ZodString","String","extractArray","originalText","start","indexOf","end","lastIndexOf","undefined","raw","slice","JSON","ZodArray","ZodObject","extractObject","extractPrimitive","ZodUnion","ZodDiscriminatedUnion","objectStart","arrayStart","ZodOptional","ZodNullable","unwrap","ZodDefault","def","innerType","ZodPipe","in","extractJsonFromCodeBlock","block","content","replace","trim","codeBlocks","match","length","validBlocks","map","filter","findLongestString","structures","findJsonStructures","trimmed","matches","depth","i","char","candidate","push","strings","reduce","longest","current","json","unescapeString","Array","isArray","Object","fromEntries","entries","key","_","code","fromCharCode","parseInt"],"mappings":"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"}
|
|
@@ -5,7 +5,7 @@ var ASCII_CTRL_RE = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g;
|
|
|
5
5
|
var MULTIPLE_SPACES_RE = / {2,}/g;
|
|
6
6
|
var CR_RE = /\r\n?/g;
|
|
7
7
|
var CITATION_RE = / *\(oaicite:\d+\)\{index=\d+\}/g;
|
|
8
|
-
var EM_DASH_SEPARATOR_RE =
|
|
8
|
+
var EM_DASH_SEPARATOR_RE = /\s*[—–―‒]\s*/g;
|
|
9
9
|
var TYPOGRAPHY_REPLACEMENTS = [
|
|
10
10
|
{
|
|
11
11
|
pattern: /[\u2018\u2019\u201A]/g,
|
|
@@ -15,10 +15,6 @@ var TYPOGRAPHY_REPLACEMENTS = [
|
|
|
15
15
|
pattern: /[\u201C\u201D\u201E]/g,
|
|
16
16
|
replacement: '"'
|
|
17
17
|
},
|
|
18
|
-
{
|
|
19
|
-
pattern: /[\u2013\u2014]/g,
|
|
20
|
-
replacement: '-'
|
|
21
|
-
},
|
|
22
18
|
{
|
|
23
19
|
pattern: /\u2026/g,
|
|
24
20
|
replacement: '...'
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/parsing/parse-text.ts"],"sourcesContent":["const INVISIBLE_CHARS_RE =\n /[\\u00AD\\u180E\\u200B-\\u200C\\u200E-\\u200F\\u202A-\\u202E\\u2060-\\u2064\\u2066-\\u2069\\uFEFF]/g;\n\n/* eslint-disable no-control-regex -- intentionally matching control characters */\n// biome-ignore lint/suspicious/noControlCharactersInRegex: intentionally matching control characters for sanitization\nconst ASCII_CTRL_RE = /[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F]/g;\n/* eslint-enable no-control-regex */\n\nconst SPACE_LIKE_RE = /[\\u00A0\\u1680\\u2000-\\u200A\\u202F\\u205F\\u3000]/g;\nconst MULTIPLE_SPACES_RE = / {2,}/g;\nconst CR_RE = /\\r\\n?/g;\nconst CITATION_RE = / *\\(oaicite:\\d+\\)\\{index=\\d+\\}/g;\nconst EM_DASH_SEPARATOR_RE =
|
|
1
|
+
{"version":3,"sources":["../../src/parsing/parse-text.ts"],"sourcesContent":["const INVISIBLE_CHARS_RE =\n /[\\u00AD\\u180E\\u200B-\\u200C\\u200E-\\u200F\\u202A-\\u202E\\u2060-\\u2064\\u2066-\\u2069\\uFEFF]/g;\n\n/* eslint-disable no-control-regex -- intentionally matching control characters */\n// biome-ignore lint/suspicious/noControlCharactersInRegex: intentionally matching control characters for sanitization\nconst ASCII_CTRL_RE = /[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F]/g;\n/* eslint-enable no-control-regex */\n\nconst SPACE_LIKE_RE = /[\\u00A0\\u1680\\u2000-\\u200A\\u202F\\u205F\\u3000]/g;\nconst MULTIPLE_SPACES_RE = / {2,}/g;\nconst CR_RE = /\\r\\n?/g;\nconst CITATION_RE = / *\\(oaicite:\\d+\\)\\{index=\\d+\\}/g;\nconst EM_DASH_SEPARATOR_RE = /\\s*[—–―‒]\\s*/g;\n\nconst TYPOGRAPHY_REPLACEMENTS: Array<{ pattern: RegExp; replacement: string }> = [\n { pattern: /[\\u2018\\u2019\\u201A]/g, replacement: \"'\" },\n { pattern: /[\\u201C\\u201D\\u201E]/g, replacement: '\"' },\n { pattern: /\\u2026/g, replacement: '...' },\n { pattern: /[\\u2022\\u25AA-\\u25AB\\u25B8-\\u25B9\\u25CF]/g, replacement: '-' },\n];\n\nexport interface ParseTextOptions {\n /** Collapse multiple spaces into one (default: true) */\n collapseSpaces?: boolean;\n /** Convert em/en dashes with spaces to commas (default: true) */\n normalizeEmDashesToCommas?: boolean;\n}\n\n/**\n * Parses and sanitizes text by removing AI artifacts and normalizing typography.\n *\n * @param text - The text to parse\n * @param options - Parsing options\n * @returns The cleaned text\n */\nexport function parseText(text: string, options: ParseTextOptions = {}): string {\n const { normalizeEmDashesToCommas = true, collapseSpaces = true } = options;\n\n if (!text) return '';\n\n let result = text;\n\n if (result.charCodeAt(0) === 0xfeff) {\n result = result.slice(1);\n }\n\n result = result.replace(CR_RE, '\\n');\n result = result.replace(CITATION_RE, '');\n result = result.normalize('NFKC');\n result = result.replace(INVISIBLE_CHARS_RE, '');\n result = result.replace(ASCII_CTRL_RE, '');\n\n if (normalizeEmDashesToCommas) {\n result = result.replace(EM_DASH_SEPARATOR_RE, ', ');\n }\n\n result = result.replace(SPACE_LIKE_RE, ' ');\n\n for (const { pattern, replacement } of TYPOGRAPHY_REPLACEMENTS) {\n result = result.replace(pattern, replacement);\n }\n\n if (collapseSpaces) {\n result = result.replace(MULTIPLE_SPACES_RE, ' ').trim();\n }\n\n return result;\n}\n"],"names":["INVISIBLE_CHARS_RE","ASCII_CTRL_RE","SPACE_LIKE_RE","MULTIPLE_SPACES_RE","CR_RE","CITATION_RE","EM_DASH_SEPARATOR_RE","TYPOGRAPHY_REPLACEMENTS","pattern","replacement","parseText","text","options","normalizeEmDashesToCommas","collapseSpaces","result","charCodeAt","slice","replace","normalize","trim"],"mappings":"AAAA,IAAMA,qBACF;AAEJ,gFAAgF,GAChF,sHAAsH;AACtH,IAAMC,gBAAgB;AACtB,kCAAkC,GAElC,IAAMC,gBAAgB;AACtB,IAAMC,qBAAqB;AAC3B,IAAMC,QAAQ;AACd,IAAMC,cAAc;AACpB,IAAMC,uBAAuB;AAE7B,IAAMC,0BAA2E;IAC7E;QAAEC,SAAS;QAAyBC,aAAa;IAAI;IACrD;QAAED,SAAS;QAAyBC,aAAa;IAAI;IACrD;QAAED,SAAS;QAAWC,aAAa;IAAM;IACzC;QAAED,SAAS;QAA6CC,aAAa;IAAI;CAC5E;AASD;;;;;;CAMC,GACD,OAAO,SAASC,UAAUC,IAAY;QAAEC,UAAAA,iEAA4B,CAAC;IACjE,yCAAoEA,QAA5DC,2BAAAA,4EAA4B,qEAAgCD,QAA1BE,gBAAAA,sDAAiB;IAE3D,IAAI,CAACH,MAAM,OAAO;IAElB,IAAII,SAASJ;IAEb,IAAII,OAAOC,UAAU,CAAC,OAAO,QAAQ;QACjCD,SAASA,OAAOE,KAAK,CAAC;IAC1B;IAEAF,SAASA,OAAOG,OAAO,CAACd,OAAO;IAC/BW,SAASA,OAAOG,OAAO,CAACb,aAAa;IACrCU,SAASA,OAAOI,SAAS,CAAC;IAC1BJ,SAASA,OAAOG,OAAO,CAAClB,oBAAoB;IAC5Ce,SAASA,OAAOG,OAAO,CAACjB,eAAe;IAEvC,IAAIY,2BAA2B;QAC3BE,SAASA,OAAOG,OAAO,CAACZ,sBAAsB;IAClD;IAEAS,SAASA,OAAOG,OAAO,CAAChB,eAAe;QAElC,kCAAA,2BAAA;;QAAL,QAAK,YAAkCK,4CAAlC,SAAA,6BAAA,QAAA,yBAAA,iCAA2D;YAA3D,kBAAA,aAAQC,sBAAAA,SAASC,0BAAAA;YAClBM,SAASA,OAAOG,OAAO,CAACV,SAASC;QACrC;;QAFK;QAAA;;;iBAAA,6BAAA;gBAAA;;;gBAAA;sBAAA;;;;IAIL,IAAIK,gBAAgB;QAChBC,SAASA,OAAOG,OAAO,CAACf,oBAAoB,KAAKiB,IAAI;IACzD;IAEA,OAAOL;AACX"}
|