From 3bba5fe797f09c06dab6faaf2a516599a67e720f Mon Sep 17 00:00:00 2001 From: Rhys Sullivan <39114868+RhysSullivan@users.noreply.github.com> Date: Thu, 20 Aug 2026 10:41:56 -0700 Subject: [PATCH] Build the Ajv instance on first use and mark codemode-core side-effect free --- packages/kernel/core/package.json | 1 + packages/kernel/core/src/json-schema.ts | 35 +++++++++++++++++++------ 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/packages/kernel/core/package.json b/packages/kernel/core/package.json index be8580478e..657dacd8b2 100644 --- a/packages/kernel/core/package.json +++ b/packages/kernel/core/package.json @@ -15,6 +15,7 @@ "dist" ], "type": "module", + "sideEffects": false, "exports": { ".": "./src/index.ts" }, diff --git a/packages/kernel/core/src/json-schema.ts b/packages/kernel/core/src/json-schema.ts index 3cfdefd156..f7c976284d 100644 --- a/packages/kernel/core/src/json-schema.ts +++ b/packages/kernel/core/src/json-schema.ts @@ -5,14 +5,33 @@ import addFormats from "ajv-formats"; import type { StandardSchema } from "./types"; import { unknownInputSchema } from "./types"; -const ajv = new Ajv2020({ - allErrors: true, - strict: false, - validateSchema: false, - allowUnionTypes: true, -}); +// Built on first use, not at import. Two reasons, and the second is the one +// that matters beyond this file: +// +// 1. Constructing an Ajv instance and registering every format is real work, +// and it ran on each cold isolate that so much as imported this package — +// including the many that never validate a schema. +// 2. `new Ajv2020(...)` + `addFormats(...)` at module scope are import-time +// side effects, so a bundler must keep this module (and therefore ajv) +// whenever anything touches the package barrel. `packages/core/execution` +// imports exactly one runtime value from it — `CodeExecutionError` — and +// the rest as erased types, yet still dragged ajv AND sucrase into the +// server graph. With no module-scope effects left here, `sideEffects: +// false` in package.json is honest and the unused modules tree-shake out. +let ajvInstance: Ajv2020 | undefined; -addFormats(ajv); +const getAjv = (): Ajv2020 => { + if (ajvInstance === undefined) { + ajvInstance = new Ajv2020({ + allErrors: true, + strict: false, + validateSchema: false, + allowUnionTypes: true, + }); + addFormats(ajvInstance); + } + return ajvInstance; +}; const decodePointerSegment = (segment: string): PropertyKey => { const decoded = segment.replaceAll("~1", "/").replaceAll("~0", "~"); @@ -47,7 +66,7 @@ export const standardSchemaFromJsonSchema = ( ): StandardSchema => { // oxlint-disable-next-line executor/no-try-catch-or-throw -- boundary: AJV compile throws for invalid schemas and this adapter preserves fallback behavior try { - const validate = ajv.compile(schema as Record); + const validate = getAjv().compile(schema as Record); return { "~standard": {