Skip to content

Commit b3b9b6a

Browse files
committed
fix: guard zod sync patching
1 parent 8badb83 commit b3b9b6a

5 files changed

Lines changed: 85 additions & 7 deletions

File tree

.changeset/config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
}
88
],
99
"commit": false,
10-
"fixed": [["@transloadit/node", "transloadit", "@transloadit/types"]],
10+
"fixed": [["@transloadit/node", "transloadit", "@transloadit/types", "@transloadit/zod"]],
1111
"linked": [],
1212
"access": "public",
1313
"baseBranch": "main",

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ jobs:
7676
node-version: ${{ matrix.node }}
7777
- run: corepack yarn
7878
- run: corepack yarn test:unit
79+
if: matrix.node == 24
80+
- run: corepack yarn workspace @transloadit/node test:unit
81+
if: matrix.node != 24
7982
- name: Upload coverage reports artifact
8083
if: matrix.node == 24
8184
uses: actions/upload-artifact@v4

packages/zod/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"sync:v4": "node --experimental-strip-types scripts/sync-v4.ts",
3333
"sync": "yarn sync:v3 && yarn sync:v4",
3434
"lint:ts": "yarn sync && tsc --build tsconfig.build.json",
35-
"test:unit": "node --experimental-strip-types test/scripts-config.test.ts && node --experimental-strip-types test/exports-sync.test.ts && node --experimental-strip-types test/runtime-parity.test.ts",
35+
"test:unit": "node --experimental-strip-types test/scripts-config.test.ts && node --experimental-strip-types test/patch-ai-chat-schema.test.ts && node --experimental-strip-types test/exports-sync.test.ts && node --experimental-strip-types test/runtime-parity.test.ts",
3636
"build": "yarn sync && tsc --build tsconfig.build.json",
3737
"check": "yarn sync && tsc --build tsconfig.build.json && tsc --noEmit --project tsconfig.test.json && yarn test:unit"
3838
},

packages/zod/scripts/sync-v4.ts

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { realpathSync } from 'node:fs'
12
import { cp, mkdir, readdir, readFile, rm, writeFile } from 'node:fs/promises'
23
import { dirname, resolve, sep } from 'node:path'
34
import { fileURLToPath } from 'node:url'
@@ -325,10 +326,34 @@ const patchInterpolatableRobot = (contents: string): string => {
325326
return `${contents.slice(0, start)}${replacement}${contents.slice(end)}`
326327
}
327328

328-
const patchAiChatSchema = (contents: string): string =>
329-
contents
330-
.replace('const jsonValueSchema: z.ZodType =', 'const jsonValueSchema: z.ZodType<any> =')
331-
.replace('result: z.unknown(),', 'result: z.unknown().optional(),')
329+
export const patchAiChatSchema = (contents: string): string => {
330+
const jsonValueToken = 'const jsonValueSchema: z.ZodType ='
331+
const jsonValuePatched = 'const jsonValueSchema: z.ZodType<any> ='
332+
const resultToken = 'result: z.unknown(),'
333+
const resultPatched = 'result: z.unknown().optional(),'
334+
335+
let next = contents
336+
if (next.includes(jsonValueToken)) {
337+
next = next.replace(jsonValueToken, jsonValuePatched)
338+
}
339+
if (next.includes(resultToken)) {
340+
next = next.replace(resultToken, resultPatched)
341+
}
342+
343+
const hasJsonValue = next.includes(jsonValuePatched)
344+
const hasResult = next.includes(resultPatched)
345+
if (!hasJsonValue || !hasResult) {
346+
const missing = [
347+
!hasJsonValue ? 'jsonValueSchema' : null,
348+
!hasResult ? 'result optional' : null,
349+
]
350+
.filter(Boolean)
351+
.join(', ')
352+
throw new Error(`ai-chat schema patch failed (${missing})`)
353+
}
354+
355+
return next
356+
}
332357

333358
const patchFile = (filePath: string, contents: string): string => {
334359
let next = contents
@@ -360,4 +385,17 @@ const main = async () => {
360385
}
361386
}
362387

363-
await main()
388+
const shouldRun = (): boolean => {
389+
if (!process.argv[1]) return false
390+
try {
391+
const current = realpathSync(fileURLToPath(import.meta.url))
392+
const invoked = realpathSync(process.argv[1])
393+
return current === invoked
394+
} catch {
395+
return false
396+
}
397+
}
398+
399+
if (shouldRun()) {
400+
await main()
401+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import assert from 'node:assert/strict'
2+
import { patchAiChatSchema } from '../scripts/sync-v4.ts'
3+
4+
const source = `
5+
const jsonValueSchema: z.ZodType =
6+
z.union([z.string()])
7+
8+
const responseSchema = z.object({
9+
result: z.unknown(),
10+
})
11+
`
12+
13+
const patched = patchAiChatSchema(source)
14+
assert.ok(
15+
patched.includes('const jsonValueSchema: z.ZodType<any> ='),
16+
'should widen jsonValueSchema to ZodType<any>',
17+
)
18+
assert.ok(patched.includes('result: z.unknown().optional(),'), 'should make result optional')
19+
20+
const alreadyPatched = `
21+
const jsonValueSchema: z.ZodType<any> =
22+
z.union([z.string()])
23+
24+
const responseSchema = z.object({
25+
result: z.unknown().optional(),
26+
})
27+
`
28+
29+
assert.equal(
30+
patchAiChatSchema(alreadyPatched),
31+
alreadyPatched,
32+
'should be a no-op when already patched',
33+
)
34+
35+
assert.throws(() => patchAiChatSchema('const jsonValueSchema: z.ZodType = z.string()'), /ai-chat/i)
36+
37+
console.log('ai-chat schema patching: ok')

0 commit comments

Comments
 (0)