Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/kernel/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"dist"
],
"type": "module",
"sideEffects": false,
"exports": {
".": "./src/index.ts"
},
Expand Down
35 changes: 27 additions & 8 deletions packages/kernel/core/src/json-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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", "~");
Expand Down Expand Up @@ -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<string, unknown>);
const validate = getAjv().compile(schema as Record<string, unknown>);

return {
"~standard": {
Expand Down
Loading