Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Value.Clone corrupts Object instance #1186

Open
bitofbreeze opened this issue Feb 22, 2025 · 1 comment
Open

Value.Clone corrupts Object instance #1186

bitofbreeze opened this issue Feb 22, 2025 · 1 comment

Comments

@bitofbreeze
Copy link

I am trying to validate a Cloudflare Worker env context, which binds a ProxyStub instance to it . I have a schema like:

Type.Object({
  KV: Type.Unsafe<KVNamespace>(Type.Object({})),
})

and then I proceed to do the Parse steps:

console.log("original", value.KV);
const cloned = Value.Clone(value); // clone because value ops can be mutable
console.log("cloned", cloned.KV);
const defaulted = Value.Default(EnvType, cloned); // initialize defaults for value
console.log("defaulted", defaulted.KV);
const converted = Value.Convert(EnvType, defaulted); // convert mismatched types for value
console.log("converted", converted.KV);
const cleaned = Value.Clean(EnvType, converted); // remove unknown properties
console.log("cleaned", cleaned.KV);
const decoded = Value.Decode(EnvType, cleaned); // run decode transforms (optional)
console.log("decoded", decoded.KV);

which outputs:

[app] original ProxyStub { name: 'KvNamespace', poisoned: false }
[app] cloned {}
[app] defaulted {}
[app] converted {}
[app] cleaned {}
[app] decoded {}

As you can see, the cloning step seems to corrupt the value. I have tested doing a simple shallow clone in JS via the spread operator, and it preserves the binding correctly, so I am not sure what Value.Clone does that destroys it.

Simply removing the Clone step outputs the following:

[app] original ProxyStub { name: 'KvNamespace', poisoned: false }
[app] defaulted ProxyStub { name: 'KvNamespace', poisoned: false }
[app] converted ProxyStub { name: 'KvNamespace', poisoned: false }
[app] cleaned ProxyStub { name: 'KvNamespace', poisoned: false }
[app] decoded ProxyStub { name: 'KvNamespace', poisoned: false }

which confirms the Clone step is the offender.

@sinclairzx81
Copy link
Owner

sinclairzx81 commented Feb 23, 2025

@bitofbreeze Hi,

I have tested doing a simple shallow clone in JS via the spread operator

Interesting. The minimum requirements for Clone is that the properties and symbols of an object are enumerable

Try the following and let me know what it prints.

const cloned = structuredClone(value.KV)
const symbols = Object.getOwnPropertySymbols(value.KV)
const keys = Object.getOwnPropertyNames(value.KV)

console.log({ cloned, symbols, keys })

If keys returns an empty array, it means the KV object is non-enumerable (so probably can't be cloned), but this seems at odds with you being able to use the spread operator to apply a shallow property clone. This said, if you don't need to Clone the value before applying operations to it, you can use a configurable Parse and omit the Clone operation.

const result = Value.Parse([  
  // 'Clone', // omit
  'Clean',    // ... and keep these
  'Default',
  'Convert',
  'Assert',
  'Decode'
], schema, value.KV)

Let me know what you see for the above cloned, symbols and keys return values.
Keep me posted
S

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants