| @@ -5,14 +5,33 @@ import addFormats from "ajv-formats"; · 4 unmodified lines |
| 5 | 5 | import type { StandardSchema } from "./types"; |
| 6 | 6 | import { unknownInputSchema } from "./types"; |
| 7 | 7 | |
| 8 | | −const ajv = new Ajv2020({ |
| 9 | | − allErrors: true, |
| 10 | | − strict: false, |
| 11 | | − validateSchema: false, |
| 12 | | − allowUnionTypes: true, |
| 13 | | −}); |
| 8 | +// Built on first use, not at import. Two reasons, and the second is the one |
| 9 | +// that matters beyond this file: |
| 10 | +// |
| 11 | +// 1. Constructing an Ajv instance and registering every format is real work, |
| 12 | +// and it ran on each cold isolate that so much as imported this package — |
| 13 | +// including the many that never validate a schema. |
| 14 | +// 2. `new Ajv2020(...)` + `addFormats(...)` at module scope are import-time |
| 15 | +// side effects, so a bundler must keep this module (and therefore ajv) |
| 16 | +// whenever anything touches the package barrel. `packages/core/execution` |
| 17 | +// imports exactly one runtime value from it — `CodeExecutionError` — and |
| 18 | +// the rest as erased types, yet still dragged ajv AND sucrase into the |
| 19 | +// server graph. With no module-scope effects left here, `sideEffects: |
| 20 | +// false` in package.json is honest and the unused modules tree-shake out. |
| 21 | +let ajvInstance: Ajv2020 | undefined; |
| 14 | 22 | |
| 15 | | −addFormats(ajv); |
| 23 | +const getAjv = (): Ajv2020 => { |
| 24 | + if (ajvInstance === undefined) { |
| 25 | + ajvInstance = new Ajv2020({ |
| 26 | + allErrors: true, |
| 27 | + strict: false, |
| 28 | + validateSchema: false, |
| 29 | + allowUnionTypes: true, |
| 30 | + }); |
| 31 | + addFormats(ajvInstance); |
| 32 | + } |
| 33 | + return ajvInstance; |
| 34 | +}; |
| 16 | 35 | |
| 17 | 36 | const decodePointerSegment = (segment: string): PropertyKey => { |
| 18 | 37 | const decoded = segment.replaceAll("~1", "/").replaceAll("~0", "~"); |
| @@ -47,7 +66,7 @@ export const standardSchemaFromJsonSchema = ( · 28 unmodified lines |
| 47 | 66 | ): StandardSchema => { |
| 48 | 67 | // oxlint-disable-next-line executor/no-try-catch-or-throw -- boundary: AJV compile throws for invalid schemas and this adapter preserves fallback behavior |
| 49 | 68 | try { |
| 50 | | − const validate = ajv.compile(schema as Record<string, unknown>); |
| 69 | + const validate = getAjv().compile(schema as Record<string, unknown>); |
| 51 | 70 | |
| 52 | 71 | return { |
| 53 | 72 | "~standard": { |