mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 00:02:31 +00:00
perf(rpc): restore compiled Zod request schemas without override (#17374)
* perf(rpc): compile Zod request schemas lazily * chore(deps): pin zod 4.5.4 and except it from the release-age gate 4.5.4 is the first release fixing isRecursiveSchema (upstream 84e416f, #6500), which compile() calls on every schema — on 4.5.0 it fired .default() factories at compile time. Verified: compile-time factory calls 0 on 4.5.4, 1 on 4.5.0.
This commit is contained in:
+1
-1
@@ -169,7 +169,7 @@
|
||||
"tweetnacl": "^1.0.3",
|
||||
"ws": "^8.21.3",
|
||||
"yaml": "^2.8.4",
|
||||
"zod": "~4.4.3"
|
||||
"zod": "~4.5.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
|
||||
Generated
+13
-13
@@ -191,8 +191,8 @@ importers:
|
||||
specifier: ^2.8.4
|
||||
version: 2.8.4
|
||||
zod:
|
||||
specifier: ~4.4.3
|
||||
version: 4.4.3
|
||||
specifier: ~4.5.4
|
||||
version: 4.5.4
|
||||
devDependencies:
|
||||
'@dnd-kit/core':
|
||||
specifier: ^6.3.1
|
||||
@@ -217,7 +217,7 @@ importers:
|
||||
version: 3.2.0
|
||||
'@stablyai/playwright-test':
|
||||
specifier: ^2.1.14
|
||||
version: 2.1.14(@playwright/test@1.59.1)(zod@4.4.3)
|
||||
version: 2.1.14(@playwright/test@1.59.1)(zod@4.5.4)
|
||||
'@tailwindcss/vite':
|
||||
specifier: ^4.2.4
|
||||
version: 4.2.4(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@25.9.5)(jiti@2.7.0)(yaml@2.8.4))
|
||||
@@ -6982,8 +6982,8 @@ packages:
|
||||
zod@3.25.76:
|
||||
resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
|
||||
|
||||
zod@4.4.3:
|
||||
resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==}
|
||||
zod@4.5.4:
|
||||
resolution: {integrity: sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA==}
|
||||
|
||||
zustand@5.0.14:
|
||||
resolution: {integrity: sha512-/8tAspM5LMPr28b3fwLYrtdj77ECpfZviaP75CMTnwO8ISyaE4GDIG/9rDDYq/cH9D2Xw2A2RXglLInmVBQB/g==}
|
||||
@@ -8916,27 +8916,27 @@ snapshots:
|
||||
|
||||
'@sindresorhus/merge-streams@4.0.0': {}
|
||||
|
||||
'@stablyai/playwright-base@2.1.14(@playwright/test@1.59.1)(zod@4.4.3)':
|
||||
'@stablyai/playwright-base@2.1.14(@playwright/test@1.59.1)(zod@4.5.4)':
|
||||
dependencies:
|
||||
'@playwright/test': 1.59.1
|
||||
jpeg-js: 0.4.4
|
||||
p-retry: 4.6.2
|
||||
pngjs: 7.0.0
|
||||
optionalDependencies:
|
||||
zod: 4.4.3
|
||||
zod: 4.5.4
|
||||
|
||||
'@stablyai/playwright-test@2.1.14(@playwright/test@1.59.1)(zod@4.4.3)':
|
||||
'@stablyai/playwright-test@2.1.14(@playwright/test@1.59.1)(zod@4.5.4)':
|
||||
dependencies:
|
||||
'@playwright/test': 1.59.1
|
||||
'@stablyai/playwright': 2.1.14(@playwright/test@1.59.1)(zod@4.4.3)
|
||||
'@stablyai/playwright-base': 2.1.14(@playwright/test@1.59.1)(zod@4.4.3)
|
||||
'@stablyai/playwright': 2.1.14(@playwright/test@1.59.1)(zod@4.5.4)
|
||||
'@stablyai/playwright-base': 2.1.14(@playwright/test@1.59.1)(zod@4.5.4)
|
||||
transitivePeerDependencies:
|
||||
- zod
|
||||
|
||||
'@stablyai/playwright@2.1.14(@playwright/test@1.59.1)(zod@4.4.3)':
|
||||
'@stablyai/playwright@2.1.14(@playwright/test@1.59.1)(zod@4.5.4)':
|
||||
dependencies:
|
||||
'@playwright/test': 1.59.1
|
||||
'@stablyai/playwright-base': 2.1.14(@playwright/test@1.59.1)(zod@4.4.3)
|
||||
'@stablyai/playwright-base': 2.1.14(@playwright/test@1.59.1)(zod@4.5.4)
|
||||
transitivePeerDependencies:
|
||||
- zod
|
||||
|
||||
@@ -13787,7 +13787,7 @@ snapshots:
|
||||
|
||||
zod@3.25.76: {}
|
||||
|
||||
zod@4.4.3: {}
|
||||
zod@4.5.4: {}
|
||||
|
||||
zustand@5.0.14(@types/react@19.2.17)(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)):
|
||||
optionalDependencies:
|
||||
|
||||
@@ -6,8 +6,10 @@ packages: []
|
||||
|
||||
minimumReleaseAge: 4320
|
||||
# 6.3.289 was published 2026-08-29T12:48Z; it clears the 3-day gate on 2026-09-01T12:48Z.
|
||||
# zod 4.5.4 was published 2026-08-29T17:55Z; it clears the 3-day gate on 2026-09-01T17:55Z.
|
||||
minimumReleaseAgeExclude:
|
||||
- pdfjs-dist@6.3.289
|
||||
- zod@4.5.4
|
||||
shamefullyHoist: true
|
||||
|
||||
supportedArchitectures:
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { z } from 'zod'
|
||||
import { defineMethod, type RpcEnvelopeMeta, type RpcRequest } from './core'
|
||||
import { parseRpcRequestParams } from './dispatcher-request-parsing'
|
||||
|
||||
const META: RpcEnvelopeMeta = { runtimeId: 'runtime-1' }
|
||||
|
||||
function request(params: unknown): RpcRequest {
|
||||
return { id: 'req-1', authToken: 'token', method: 'test.parse', params }
|
||||
}
|
||||
|
||||
describe('parseRpcRequestParams', () => {
|
||||
it('parses repeated valid requests through the compiled schema', () => {
|
||||
const method = defineMethod({
|
||||
name: 'test.parse',
|
||||
params: z.object({ requestId: z.string().min(1), count: z.number().int().nonnegative() }),
|
||||
handler: () => null
|
||||
})
|
||||
|
||||
expect(parseRpcRequestParams(request({ requestId: 'a', count: 1 }), method, META)).toEqual({
|
||||
value: { requestId: 'a', count: 1 }
|
||||
})
|
||||
expect(parseRpcRequestParams(request({ requestId: 'b', count: 2 }), method, META)).toEqual({
|
||||
value: { requestId: 'b', count: 2 }
|
||||
})
|
||||
})
|
||||
|
||||
it('preserves validation errors from the runtime fallback', () => {
|
||||
const method = defineMethod({
|
||||
name: 'test.parse',
|
||||
params: z.object({ count: z.number().int('Count must be an integer') }),
|
||||
handler: () => null
|
||||
})
|
||||
|
||||
expect(parseRpcRequestParams(request({ count: 1.5 }), method, META)).toMatchObject({
|
||||
error: {
|
||||
ok: false,
|
||||
error: { code: 'invalid_argument', message: 'Count must be an integer' }
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('keeps transform output when compilation falls back', () => {
|
||||
const method = defineMethod({
|
||||
name: 'test.parse',
|
||||
params: z.object({ value: z.string().transform((value) => value.toUpperCase()) }),
|
||||
handler: () => null
|
||||
})
|
||||
|
||||
expect(parseRpcRequestParams(request({ value: 'ok' }), method, META)).toEqual({
|
||||
value: { value: 'OK' }
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,3 +1,4 @@
|
||||
import { compile, type ZodType } from 'zod'
|
||||
import {
|
||||
formatZodError,
|
||||
type RpcAnyMethod,
|
||||
@@ -7,6 +8,8 @@ import {
|
||||
} from './core'
|
||||
import { invalidArgumentResponse } from './dispatcher-error-response'
|
||||
|
||||
const compiledParams = new WeakMap<ZodType, ZodType>()
|
||||
|
||||
export function parseRpcRequestParams(
|
||||
request: RpcRequest,
|
||||
method: RpcAnyMethod,
|
||||
@@ -15,7 +18,7 @@ export function parseRpcRequestParams(
|
||||
if (method.params === null) {
|
||||
return { value: undefined }
|
||||
}
|
||||
const result = method.params.safeParse(request.params ?? {})
|
||||
const result = getCompiledParams(method.params).safeParse(request.params ?? {})
|
||||
if (!result.success) {
|
||||
return {
|
||||
error: invalidArgumentResponse(request, meta, formatZodError(result.error))
|
||||
@@ -23,3 +26,13 @@ export function parseRpcRequestParams(
|
||||
}
|
||||
return { value: result.data }
|
||||
}
|
||||
|
||||
function getCompiledParams(schema: ZodType): ZodType {
|
||||
const cached = compiledParams.get(schema)
|
||||
if (cached) {
|
||||
return cached
|
||||
}
|
||||
const compiled = compile(schema)
|
||||
compiledParams.set(schema, compiled)
|
||||
return compiled
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user