@hardlydifficult/ai-msg 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -0
- package/dist/extractCodeBlock.d.ts +2 -0
- package/dist/extractCodeBlock.d.ts.map +1 -0
- package/dist/extractCodeBlock.js +17 -0
- package/dist/extractCodeBlock.js.map +1 -0
- package/dist/extractJson.d.ts +2 -0
- package/dist/extractJson.d.ts.map +1 -0
- package/dist/extractJson.js +50 -0
- package/dist/extractJson.js.map +1 -0
- package/dist/extractTyped.d.ts +3 -0
- package/dist/extractTyped.d.ts.map +1 -0
- package/dist/extractTyped.js +13 -0
- package/dist/extractTyped.js.map +1 -0
- package/dist/findBalanced.d.ts +2 -0
- package/dist/findBalanced.d.ts.map +1 -0
- package/dist/findBalanced.js +43 -0
- package/dist/findBalanced.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/package.json +33 -0
package/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# @hardlydifficult/ai-msg
|
|
2
|
+
|
|
3
|
+
Opinionated utilities for extracting structured output from AI text responses.
|
|
4
|
+
|
|
5
|
+
LLM responses often wrap structured data in markdown code blocks, preamble text, or trailing commentary. This package reliably extracts JSON and code blocks from messy AI output.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @hardlydifficult/ai-msg
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
For typed extraction with Zod validation:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @hardlydifficult/ai-msg zod
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
### `extractJson(text): unknown | null`
|
|
22
|
+
|
|
23
|
+
Extracts the first valid JSON value from text using a three-pass strategy:
|
|
24
|
+
|
|
25
|
+
1. Direct `JSON.parse` of the trimmed text
|
|
26
|
+
2. Search markdown code blocks (json-tagged first, then any)
|
|
27
|
+
3. Find balanced `{}` or `[]` substrings in prose
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { extractJson } from "@hardlydifficult/ai-msg";
|
|
31
|
+
|
|
32
|
+
extractJson('{"key": "value"}');
|
|
33
|
+
// { key: "value" }
|
|
34
|
+
|
|
35
|
+
extractJson('Here is the result:\n```json\n{"key": "value"}\n```\nDone.');
|
|
36
|
+
// { key: "value" }
|
|
37
|
+
|
|
38
|
+
extractJson('The answer is {"key": "value"} as shown.');
|
|
39
|
+
// { key: "value" }
|
|
40
|
+
|
|
41
|
+
extractJson("no json here");
|
|
42
|
+
// null
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### `extractCodeBlock(text, lang?): string[]`
|
|
46
|
+
|
|
47
|
+
Extracts all markdown code blocks from text. Optionally filters by language tag.
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { extractCodeBlock } from "@hardlydifficult/ai-msg";
|
|
51
|
+
|
|
52
|
+
extractCodeBlock("```ts\nconst x = 1;\n```");
|
|
53
|
+
// ["const x = 1;"]
|
|
54
|
+
|
|
55
|
+
extractCodeBlock("```json\n{}\n```\n```ts\nconst x = 1;\n```", "json");
|
|
56
|
+
// ["{}"]
|
|
57
|
+
|
|
58
|
+
extractCodeBlock("no code blocks");
|
|
59
|
+
// []
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### `extractTyped<T>(text, schema): T | null`
|
|
63
|
+
|
|
64
|
+
Extracts JSON from text and validates it against a Zod schema. Requires `zod` as a peer dependency.
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import { extractTyped } from "@hardlydifficult/ai-msg";
|
|
68
|
+
import { z } from "zod";
|
|
69
|
+
|
|
70
|
+
const Person = z.object({ name: z.string(), age: z.number() });
|
|
71
|
+
|
|
72
|
+
extractTyped('{"name": "Alice", "age": 30}', Person);
|
|
73
|
+
// { name: "Alice", age: 30 }
|
|
74
|
+
|
|
75
|
+
extractTyped('{"name": "Alice", "age": "thirty"}', Person);
|
|
76
|
+
// null
|
|
77
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractCodeBlock.d.ts","sourceRoot":"","sources":["../src/extractCodeBlock.ts"],"names":[],"mappings":"AAEA,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAatE"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractCodeBlock = extractCodeBlock;
|
|
4
|
+
const codeBlockRegex = /^```(\w*)\s*\n([\s\S]*?)^```/gm;
|
|
5
|
+
function extractCodeBlock(text, lang) {
|
|
6
|
+
const results = [];
|
|
7
|
+
for (const match of text.matchAll(codeBlockRegex)) {
|
|
8
|
+
const tag = match[1];
|
|
9
|
+
const content = match[2];
|
|
10
|
+
if (lang !== undefined && tag.toLowerCase() !== lang.toLowerCase()) {
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
results.push(content.trimEnd());
|
|
14
|
+
}
|
|
15
|
+
return results;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=extractCodeBlock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractCodeBlock.js","sourceRoot":"","sources":["../src/extractCodeBlock.ts"],"names":[],"mappings":";;AAEA,4CAaC;AAfD,MAAM,cAAc,GAAG,gCAAgC,CAAC;AAExD,SAAgB,gBAAgB,CAAC,IAAY,EAAE,IAAa;IAC1D,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;QAClD,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACrB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACzB,IAAI,IAAI,KAAK,SAAS,IAAI,GAAG,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YACnE,SAAS;QACX,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IAClC,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractJson.d.ts","sourceRoot":"","sources":["../src/extractJson.ts"],"names":[],"mappings":"AAWA,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAuCjD"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractJson = extractJson;
|
|
4
|
+
const extractCodeBlock_js_1 = require("./extractCodeBlock.js");
|
|
5
|
+
const findBalanced_js_1 = require("./findBalanced.js");
|
|
6
|
+
function tryParse(text) {
|
|
7
|
+
try {
|
|
8
|
+
return JSON.parse(text);
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function extractJson(text) {
|
|
15
|
+
// Pass 1: try the whole text
|
|
16
|
+
const direct = tryParse(text.trim());
|
|
17
|
+
if (direct !== undefined) {
|
|
18
|
+
return direct;
|
|
19
|
+
}
|
|
20
|
+
// Pass 2: code blocks — json-tagged first, then any
|
|
21
|
+
for (const block of (0, extractCodeBlock_js_1.extractCodeBlock)(text, "json")) {
|
|
22
|
+
const parsed = tryParse(block.trim());
|
|
23
|
+
if (parsed !== undefined) {
|
|
24
|
+
return parsed;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
for (const block of (0, extractCodeBlock_js_1.extractCodeBlock)(text)) {
|
|
28
|
+
const parsed = tryParse(block.trim());
|
|
29
|
+
if (parsed !== undefined) {
|
|
30
|
+
return parsed;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Pass 3: balanced braces / brackets
|
|
34
|
+
const obj = (0, findBalanced_js_1.findBalanced)(text, "{", "}");
|
|
35
|
+
if (obj !== null) {
|
|
36
|
+
const parsed = tryParse(obj);
|
|
37
|
+
if (parsed !== undefined) {
|
|
38
|
+
return parsed;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const arr = (0, findBalanced_js_1.findBalanced)(text, "[", "]");
|
|
42
|
+
if (arr !== null) {
|
|
43
|
+
const parsed = tryParse(arr);
|
|
44
|
+
if (parsed !== undefined) {
|
|
45
|
+
return parsed;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=extractJson.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractJson.js","sourceRoot":"","sources":["../src/extractJson.ts"],"names":[],"mappings":";;AAWA,kCAuCC;AAlDD,+DAAyD;AACzD,uDAAiD;AAEjD,SAAS,QAAQ,CAAC,IAAY;IAC5B,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAgB,WAAW,CAAC,IAAY;IACtC,6BAA6B;IAC7B,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACrC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,oDAAoD;IACpD,KAAK,MAAM,KAAK,IAAI,IAAA,sCAAgB,EAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC;QACnD,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QACtC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,OAAO,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,IAAA,sCAAgB,EAAC,IAAI,CAAC,EAAE,CAAC;QAC3C,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QACtC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,OAAO,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IAED,qCAAqC;IACrC,MAAM,GAAG,GAAG,IAAA,8BAAY,EAAC,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IACzC,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACjB,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,OAAO,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IAED,MAAM,GAAG,GAAG,IAAA,8BAAY,EAAC,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IACzC,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACjB,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,OAAO,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractTyped.d.ts","sourceRoot":"","sources":["../src/extractTyped.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,KAAK,CAAC;AAInC,wBAAgB,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,CAQ1E"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractTyped = extractTyped;
|
|
4
|
+
const extractJson_js_1 = require("./extractJson.js");
|
|
5
|
+
function extractTyped(text, schema) {
|
|
6
|
+
const json = (0, extractJson_js_1.extractJson)(text);
|
|
7
|
+
if (json === null) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
const result = schema.safeParse(json);
|
|
11
|
+
return result.success ? result.data : null;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=extractTyped.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractTyped.js","sourceRoot":"","sources":["../src/extractTyped.ts"],"names":[],"mappings":";;AAIA,oCAQC;AAVD,qDAA+C;AAE/C,SAAgB,YAAY,CAAI,IAAY,EAAE,MAAkB;IAC9D,MAAM,IAAI,GAAG,IAAA,4BAAW,EAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACtC,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"findBalanced.d.ts","sourceRoot":"","sources":["../src/findBalanced.ts"],"names":[],"mappings":"AAAA,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,MAAM,GAAG,IAAI,CA6Cf"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.findBalanced = findBalanced;
|
|
4
|
+
function findBalanced(text, openChar, closeChar) {
|
|
5
|
+
const start = text.indexOf(openChar);
|
|
6
|
+
if (start === -1) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
let depth = 0;
|
|
10
|
+
let inString = false;
|
|
11
|
+
let escaped = false;
|
|
12
|
+
for (let i = start; i < text.length; i++) {
|
|
13
|
+
const ch = text[i];
|
|
14
|
+
if (escaped) {
|
|
15
|
+
escaped = false;
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
if (ch === "\\") {
|
|
19
|
+
if (inString) {
|
|
20
|
+
escaped = true;
|
|
21
|
+
}
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (ch === '"') {
|
|
25
|
+
inString = !inString;
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (inString) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (ch === openChar) {
|
|
32
|
+
depth++;
|
|
33
|
+
}
|
|
34
|
+
else if (ch === closeChar) {
|
|
35
|
+
depth--;
|
|
36
|
+
if (depth === 0) {
|
|
37
|
+
return text.slice(start, i + 1);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=findBalanced.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"findBalanced.js","sourceRoot":"","sources":["../src/findBalanced.ts"],"names":[],"mappings":";;AAAA,oCAiDC;AAjDD,SAAgB,YAAY,CAC1B,IAAY,EACZ,QAAgB,EAChB,SAAiB;IAEjB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACrC,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;QACjB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAEnB,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,GAAG,KAAK,CAAC;YAChB,SAAS;QACX,CAAC;QAED,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;YAChB,IAAI,QAAQ,EAAE,CAAC;gBACb,OAAO,GAAG,IAAI,CAAC;YACjB,CAAC;YACD,SAAS;QACX,CAAC;QAED,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACf,QAAQ,GAAG,CAAC,QAAQ,CAAC;YACrB,SAAS;QACX,CAAC;QAED,IAAI,QAAQ,EAAE,CAAC;YACb,SAAS;QACX,CAAC;QAED,IAAI,EAAE,KAAK,QAAQ,EAAE,CAAC;YACpB,KAAK,EAAE,CAAC;QACV,CAAC;aAAM,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;YAC5B,KAAK,EAAE,CAAC;YACR,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;gBAChB,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;YAClC,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractTyped = exports.extractJson = exports.extractCodeBlock = void 0;
|
|
4
|
+
var extractCodeBlock_js_1 = require("./extractCodeBlock.js");
|
|
5
|
+
Object.defineProperty(exports, "extractCodeBlock", { enumerable: true, get: function () { return extractCodeBlock_js_1.extractCodeBlock; } });
|
|
6
|
+
var extractJson_js_1 = require("./extractJson.js");
|
|
7
|
+
Object.defineProperty(exports, "extractJson", { enumerable: true, get: function () { return extractJson_js_1.extractJson; } });
|
|
8
|
+
var extractTyped_js_1 = require("./extractTyped.js");
|
|
9
|
+
Object.defineProperty(exports, "extractTyped", { enumerable: true, get: function () { return extractTyped_js_1.extractTyped; } });
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,6DAAyD;AAAhD,uHAAA,gBAAgB,OAAA;AACzB,mDAA+C;AAAtC,6GAAA,WAAW,OAAA;AACpB,qDAAiD;AAAxC,+GAAA,YAAY,OAAA"}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hardlydifficult/ai-msg",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"main": "./dist/index.js",
|
|
5
|
+
"types": "./dist/index.d.ts",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist"
|
|
8
|
+
],
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"test": "vitest run",
|
|
12
|
+
"test:watch": "vitest",
|
|
13
|
+
"test:coverage": "vitest run --coverage",
|
|
14
|
+
"lint": "tsc --noEmit",
|
|
15
|
+
"clean": "rm -rf dist"
|
|
16
|
+
},
|
|
17
|
+
"peerDependencies": {
|
|
18
|
+
"zod": ">=3.0.0"
|
|
19
|
+
},
|
|
20
|
+
"peerDependenciesMeta": {
|
|
21
|
+
"zod": {
|
|
22
|
+
"optional": true
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"typescript": "5.8.3",
|
|
27
|
+
"vitest": "1.6.1",
|
|
28
|
+
"zod": "3.24.4"
|
|
29
|
+
},
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=18.0.0"
|
|
32
|
+
}
|
|
33
|
+
}
|