From d2bf2013975dfe63df67be2faeeffc3e9882d11e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20Sol=C3=A1r?= Date: Tue, 17 Mar 2026 13:23:51 +0100 Subject: [PATCH] fix: replace structuredClone with es-toolkit cloneDeep in schema paths Closes #1034. Switches all structuredClone calls in schema-transforms and input_schema to es-toolkit's cloneDeep for faster deep cloning. Co-Authored-By: Claude Opus 4.6 --- package.json | 1 + src/lib/input_schema.ts | 4 +++- src/lib/schema-transforms.ts | 14 ++++++++------ yarn.lock | 3 ++- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 868973429..52fa833b8 100644 --- a/package.json +++ b/package.json @@ -90,6 +90,7 @@ "configparser": "~0.3.10", "cors": "~2.8.5", "detect-indent": "~7.0.1", + "es-toolkit": "^1.45.1", "escape-string-regexp": "~5.0.0", "execa": "^9.5.2", "express": "~5.2.0", diff --git a/src/lib/input_schema.ts b/src/lib/input_schema.ts index 8279fab83..9c515c54a 100644 --- a/src/lib/input_schema.ts +++ b/src/lib/input_schema.ts @@ -1,6 +1,8 @@ import { existsSync, writeFileSync } from 'node:fs'; import { join } from 'node:path'; +import { cloneDeep } from 'es-toolkit'; + import { KEY_VALUE_STORE_KEYS } from '@apify/consts'; import { validateInputSchema } from '@apify/input_schema'; @@ -255,7 +257,7 @@ export const getDefaultsFromInputSchema = (inputSchema: any) => { // Lots of code copied from @apify-packages/actor, this really should be moved to the shared input_schema package export const getAjvValidator = (inputSchema: any, ajvInstance: import('ajv').Ajv) => { - const copyOfSchema = structuredClone(inputSchema); + const copyOfSchema = cloneDeep(inputSchema); copyOfSchema.required = []; for (const [inputSchemaFieldKey, inputSchemaField] of Object.entries(inputSchema.properties)) { diff --git a/src/lib/schema-transforms.ts b/src/lib/schema-transforms.ts index 598b4b38f..ec01f2758 100644 --- a/src/lib/schema-transforms.ts +++ b/src/lib/schema-transforms.ts @@ -1,10 +1,12 @@ +import { cloneDeep } from 'es-toolkit'; + /** * Transforms a JSON schema so that all properties without a `default` value are marked as required. * Properties that have a `default` are left optional, since Apify fills them in at runtime. * Recurses into nested object properties. */ export function makePropertiesRequired(schema: Record): Record { - const clone = structuredClone(schema); + const clone = cloneDeep(schema); if (!clone.properties || typeof clone.properties !== 'object') { return clone; @@ -35,7 +37,7 @@ export function makePropertiesRequired(schema: Record): Record< * making every property optional at all nesting levels. */ export function clearAllRequired(schema: Record): Record { - const clone = structuredClone(schema); + const clone = cloneDeep(schema); delete clone.required; @@ -59,7 +61,7 @@ export function clearAllRequired(schema: Record): Record): Record { - const clone = structuredClone(schema); + const clone = cloneDeep(schema); delete clone.title; @@ -137,7 +139,7 @@ export function prepareFieldsSchemaForCompilation(schema: Record