Skip to content

Commit 614efae

Browse files
committed
refactor: migrate from Zod to TypeBox via @socketsecurity/lib validateSchema
Fleet-wide direction: no more Zod. Validation goes through @socketsecurity/lib/validation/validateSchema (shipped in lib 5.20.0) which accepts TypeBox schemas. Two call sites in this repo use Zod: 1. scripts/xport-{schema,emit-schema}.mts + scripts/xport.mts — cross-repo-synced xport harness. Canonical lives in socket-repo-template; sibling migrations landed in socket-btm (60f3dcd), sdxgen (7304945), ultrathink (0bd49c332). This commit syncs the same byte-identical migration here. 2. .claude/hooks/setup-security-tools/index.mts — local tool-config validator. Switch z.object(...) → Type.Object(...) and configSchema.parse(rawConfig) → parseSchema(configSchema, rawConfig). Hook package.json swaps its zod dep for @sinclair/typebox. - xport.schema.json regenerated and oxfmt-formatted. - root package.json + pnpm-workspace.yaml swap zod for @sinclair/typebox.
1 parent 0ca8578 commit 614efae

9 files changed

Lines changed: 570 additions & 518 deletions

File tree

.claude/hooks/setup-security-tools/index.mts

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import process from 'node:process'
1717
import { fileURLToPath } from 'node:url'
1818

1919
import { PackageURL } from '@socketregistry/packageurl-js'
20+
import { Type } from '@sinclair/typebox'
21+
2022
import { whichSync } from '@socketsecurity/lib/bin'
2123
import { downloadBinary } from '@socketsecurity/lib/dlx/binary'
2224
import { downloadPackage } from '@socketsecurity/lib/dlx/package'
@@ -25,37 +27,37 @@ import { getDefaultLogger } from '@socketsecurity/lib/logger'
2527
import { normalizePath } from '@socketsecurity/lib/paths/normalize'
2628
import { getSocketHomePath } from '@socketsecurity/lib/paths/socket'
2729
import { spawn } from '@socketsecurity/lib/spawn'
28-
import { z } from 'zod'
30+
import { parseSchema } from '@socketsecurity/lib/validation/validate-schema'
2931

3032
const logger = getDefaultLogger()
3133

3234
// ── Tool config loaded from external-tools.json (self-contained) ──
3335

34-
const checksumEntrySchema = z.object({
35-
asset: z.string(),
36-
sha256: z.string(),
36+
const checksumEntrySchema = Type.Object({
37+
asset: Type.String(),
38+
sha256: Type.String(),
3739
})
3840

39-
const toolSchema = z.object({
40-
description: z.string().optional(),
41-
version: z.string().optional(),
42-
purl: z.string().optional(),
43-
integrity: z.string().optional(),
44-
repository: z.string().optional(),
45-
release: z.string().optional(),
46-
checksums: z.record(z.string(), checksumEntrySchema).optional(),
47-
ecosystems: z.array(z.string()).optional(),
41+
const toolSchema = Type.Object({
42+
description: Type.Optional(Type.String()),
43+
version: Type.Optional(Type.String()),
44+
purl: Type.Optional(Type.String()),
45+
integrity: Type.Optional(Type.String()),
46+
repository: Type.Optional(Type.String()),
47+
release: Type.Optional(Type.String()),
48+
checksums: Type.Optional(Type.Record(Type.String(), checksumEntrySchema)),
49+
ecosystems: Type.Optional(Type.Array(Type.String())),
4850
})
4951

50-
const configSchema = z.object({
51-
description: z.string().optional(),
52-
tools: z.record(z.string(), toolSchema),
52+
const configSchema = Type.Object({
53+
description: Type.Optional(Type.String()),
54+
tools: Type.Record(Type.String(), toolSchema),
5355
})
5456

5557
const __dirname = path.dirname(fileURLToPath(import.meta.url))
5658
const configPath = path.join(__dirname, 'external-tools.json')
5759
const rawConfig = JSON.parse(readFileSync(configPath, 'utf8'))
58-
const config = configSchema.parse(rawConfig)
60+
const config = parseSchema(configSchema, rawConfig)
5961

6062
const AGENTSHIELD = config.tools['agentshield']!
6163
const ZIZMOR = config.tools['zizmor']!

.claude/hooks/setup-security-tools/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"type": "module",
55
"main": "./index.mts",
66
"dependencies": {
7-
"@socketsecurity/lib": "catalog:",
8-
"zod": "4.1.12"
7+
"@sinclair/typebox": "catalog:",
8+
"@socketsecurity/lib": "catalog:"
99
}
1010
}

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
"@mdn/browser-compat-data": "catalog:",
6767
"@npmcli/package-json": "catalog:",
6868
"@npmcli/promise-spawn": "catalog:",
69+
"@sinclair/typebox": "catalog:",
6970
"@socketregistry/is-unicode-supported": "workspace:*",
7071
"@socketregistry/packageurl-js": "catalog:",
7172
"@socketsecurity/lib": "catalog:",
@@ -129,8 +130,7 @@
129130
"vitest": "catalog:",
130131
"which": "catalog:",
131132
"yargs-parser": "catalog:",
132-
"yoctocolors-cjs": "catalog:",
133-
"zod": "catalog:"
133+
"yoctocolors-cjs": "catalog:"
134134
},
135135
"typeCoverage": {
136136
"cache": true,

pnpm-lock.yaml

Lines changed: 14 additions & 19 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pnpm-workspace.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ catalog:
2626
'@mdn/browser-compat-data': 7.1.5
2727
'@npmcli/package-json': 7.0.0
2828
'@npmcli/promise-spawn': 8.0.3
29+
'@sinclair/typebox': 0.34.49
2930
'@socketregistry/packageurl-js': 1.4.2
3031
'@socketsecurity/lib': 5.20.1
3132
'@types/fs-extra': 11.0.4
@@ -100,7 +101,6 @@ catalog:
100101
which: 5.0.0
101102
yargs-parser: 22.0.0
102103
yoctocolors-cjs: 2.1.3
103-
zod: 4.3.6
104104

105105
# Wait 7 days (10080 minutes) before installing newly published packages.
106106
minimumReleaseAge: 10080

scripts/xport-emit-schema.mts

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
/**
2-
* @fileoverview Emit `xport.schema.json` from the zod schema.
2+
* @fileoverview Emit `xport.schema.json` from the TypeBox schema.
33
*
4-
* The zod schema in `scripts/xport-schema.mts` is the source of truth. This
5-
* script writes the draft-2020-12 JSON Schema that consumers without zod
6-
* (editors, external validators) consume via the `$schema` reference in
7-
* `xport.json`.
4+
* The TypeBox schema in `scripts/xport-schema.mts` is the source of truth.
5+
* TypeBox schemas are JSON Schema natively — no conversion library needed,
6+
* just serialize the schema object and add the draft-2020-12 meta headers.
87
*
9-
* Run via `pnpm run xport:emit-schema` when the zod schema changes.
8+
* Run via `pnpm run xport:emit-schema` when the schema changes.
109
*/
1110

1211
import { writeFileSync } from 'node:fs'
1312
import path from 'node:path'
1413
import { fileURLToPath } from 'node:url'
1514

16-
import { z } from 'zod'
1715
import { getDefaultLogger } from '@socketsecurity/lib/logger'
1816

1917
import { XportManifestSchema } from './xport-schema.mts'
@@ -24,16 +22,15 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url))
2422
const rootDir = path.resolve(__dirname, '..')
2523
const outPath = path.join(rootDir, 'xport.schema.json')
2624

27-
const json = z.toJSONSchema(XportManifestSchema, {
28-
target: 'draft-2020-12',
29-
})
30-
31-
// Add the canonical $id for portability across Socket repos.
25+
// TypeBox schemas carry JSON Schema shape directly, plus a Symbol-keyed
26+
// [Kind] marker that JSON.stringify drops. Spreading the schema first
27+
// then layering the canonical $schema / $id / title on top gives a clean
28+
// draft-2020-12 document with the Socket-specific headers.
3229
const enriched = {
3330
$schema: 'https://json-schema.org/draft/2020-12/schema',
3431
$id: 'https://github.com/SocketDev/xport.schema.json',
3532
title: 'xport lock-step manifest',
36-
...json,
33+
...XportManifestSchema,
3734
}
3835

3936
writeFileSync(outPath, JSON.stringify(enriched, null, 2) + '\n', 'utf8')

0 commit comments

Comments
 (0)