blob: 3d2e4214041af4b8c6f8caf7b8bc1967196a6e90 [file]
diff --git a/node_modules/zod/src/v4/core/memoizer.ts b/node_modules/zod/src/v4/core/memoizer.ts
index c2145c0..6fc6cde 100644
--- a/node_modules/zod/src/v4/core/memoizer.ts
+++ b/node_modules/zod/src/v4/core/memoizer.ts
@@ -240,9 +240,6 @@ const memo: $ZodMemoizer = {
attach(inst) {
let isRecursiveInst: boolean | undefined;
let rechecked = false;
- // a recursive schema is re-entered many times per parse and its bucket never changes
- let lastCtx: object | undefined;
- let lastBucket: WeakMap<object, Entry> | undefined;
// Wraps `parse` in a deferred so it sees the container's final parse. Core's own deferred copies `parse` into `run` when there are no checks, and it ran first, so `run` is patched to match; with checks, `run` reads `parse` dynamically.
inst._zod.deferred ??= [];
@@ -272,14 +269,7 @@ const memo: $ZodMemoizer = {
(ctx as WithState)[STATE] = state;
}
- let bucket: WeakMap<object, Entry>;
- if (lastCtx === ctx) {
- bucket = lastBucket!;
- } else {
- bucket = bucketFor(state, inst);
- lastCtx = ctx;
- lastBucket = bucket;
- }
+ const bucket = bucketFor(state, inst);
const hit = bucket.get(input);
if (hit) {
@@ -295,12 +285,18 @@ const memo: $ZodMemoizer = {
return payload;
}
+ const previousHandoff = handoff;
handoff = bucket;
const depth = open.length;
- const result = base(payload, ctx);
- handoff = undefined;
- // A container that rejected its input outright allocated nothing.
- const entry = open.length > depth ? open.pop()! : undefined;
+ let result: util.MaybeAsync<ParsePayload>;
+ let entry: Entry | undefined;
+ try {
+ result = base(payload, ctx);
+ } finally {
+ handoff = previousHandoff;
+ // A container that rejected its input outright allocated nothing.
+ entry = open.length > depth ? open.pop()! : undefined;
+ }
// Both paths written out so the sync one allocates no closure. It runs once per node, and capturing here cost more than everything else combined.
if (result instanceof Promise) {
diff --git a/node_modules/zod/v4/core/memoizer.cjs b/node_modules/zod/v4/core/memoizer.cjs
index cab19c2..732d2f4 100644
--- a/node_modules/zod/v4/core/memoizer.cjs
+++ b/node_modules/zod/v4/core/memoizer.cjs
@@ -251,9 +251,6 @@ const memo = {
var _a;
let isRecursiveInst;
let rechecked = false;
- // a recursive schema is re-entered many times per parse and its bucket never changes
- let lastCtx;
- let lastBucket;
// Wraps `parse` in a deferred so it sees the container's final parse. Core's own deferred copies `parse` into `run` when there are no checks, and it ran first, so `run` is patched to match; with checks, `run` reads `parse` dynamically.
(_a = inst._zod).deferred ?? (_a.deferred = []);
inst._zod.deferred.push(() => {
@@ -282,15 +279,7 @@ const memo = {
state = { buckets: new WeakMap(), backEdges: undefined };
ctx[STATE] = state;
}
- let bucket;
- if (lastCtx === ctx) {
- bucket = lastBucket;
- }
- else {
- bucket = bucketFor(state, inst);
- lastCtx = ctx;
- lastBucket = bucket;
- }
+ const bucket = bucketFor(state, inst);
const hit = bucket.get(input);
if (hit) {
payload.value = hit.value;
@@ -306,12 +295,19 @@ const memo = {
}
return payload;
}
+ const previousHandoff = handoff;
handoff = bucket;
const depth = open.length;
- const result = base(payload, ctx);
- handoff = undefined;
- // A container that rejected its input outright allocated nothing.
- const entry = open.length > depth ? open.pop() : undefined;
+ let result;
+ let entry;
+ try {
+ result = base(payload, ctx);
+ }
+ finally {
+ handoff = previousHandoff;
+ // A container that rejected its input outright allocated nothing.
+ entry = open.length > depth ? open.pop() : undefined;
+ }
// Both paths written out so the sync one allocates no closure. It runs once per node, and capturing here cost more than everything else combined.
if (result instanceof Promise) {
return result.then((r) => {
diff --git a/node_modules/zod/v4/core/memoizer.js b/node_modules/zod/v4/core/memoizer.js
index 393fb06..222d470 100644
--- a/node_modules/zod/v4/core/memoizer.js
+++ b/node_modules/zod/v4/core/memoizer.js
@@ -221,9 +221,6 @@ const memo = {
var _a;
let isRecursiveInst;
let rechecked = false;
- // a recursive schema is re-entered many times per parse and its bucket never changes
- let lastCtx;
- let lastBucket;
// Wraps `parse` in a deferred so it sees the container's final parse. Core's own deferred copies `parse` into `run` when there are no checks, and it ran first, so `run` is patched to match; with checks, `run` reads `parse` dynamically.
(_a = inst._zod).deferred ?? (_a.deferred = []);
inst._zod.deferred.push(() => {
@@ -252,15 +249,7 @@ const memo = {
state = { buckets: new WeakMap(), backEdges: undefined };
ctx[STATE] = state;
}
- let bucket;
- if (lastCtx === ctx) {
- bucket = lastBucket;
- }
- else {
- bucket = bucketFor(state, inst);
- lastCtx = ctx;
- lastBucket = bucket;
- }
+ const bucket = bucketFor(state, inst);
const hit = bucket.get(input);
if (hit) {
payload.value = hit.value;
@@ -276,12 +265,19 @@ const memo = {
}
return payload;
}
+ const previousHandoff = handoff;
handoff = bucket;
const depth = open.length;
- const result = base(payload, ctx);
- handoff = undefined;
- // A container that rejected its input outright allocated nothing.
- const entry = open.length > depth ? open.pop() : undefined;
+ let result;
+ let entry;
+ try {
+ result = base(payload, ctx);
+ }
+ finally {
+ handoff = previousHandoff;
+ // A container that rejected its input outright allocated nothing.
+ entry = open.length > depth ? open.pop() : undefined;
+ }
// Both paths written out so the sync one allocates no closure. It runs once per node, and capturing here cost more than everything else combined.
if (result instanceof Promise) {
return result.then((r) => {