feat(clone): production-grade 1.0.0#37
Merged
Merged
Conversation
Replaces silent `undefined` return with `throw new CloneError('UNSUPPORTED_TYPE', ...)`
for functions, Promises, Intl.*, WeakMap, WeakSet, and constructor functions.
BREAKING CHANGE: previously these inputs returned `undefined`. Calls that relied on
that contract must now wrap in try/catch or filter inputs upstream.
Captures `Buffer` from `globalThis` at module init via a `BufferRef` constant. The Buffer branch only runs when the runtime exposes Buffer; otherwise the path is inert. The `node:buffer` import is type-only — no runtime side-effect on bundlers that strip `node:` externals.
Three new opt-out flags on `CloneOptions`, all default true so existing callers see no behavior change. Composing all three `false` gives a fast plain-JSON clone path. - `cycles: false` skips the WeakMap visited cache. Caller asserts no cycles. - `preservePrototype: false` flattens custom objects to plain objects. - `copyDescriptors: false` uses `for...in` + assign for plain objects, drops symbol keys and non-enumerable descriptors. Errors keep message + name only; boxed wrappers keep their value only.
…otype - TypedArray dispatch now uses one `ArrayBuffer.isView` call instead of a 9-entry Set lookup, after DataView and Buffer have already been claimed. - Plain-object path skips `Object.create(Object.prototype)` and uses a literal, which V8 compiles into a faster object-creation map for the most common case. - `getType` returns a tighter `AnyConstructor` union instead of `unknown`.
Property tests caught a real bug: clone(Object.create(null)) threw CloneError
because the constructor lookup returned undefined for null-prototype objects.
The unsupported-type check now triggers only on functions and explicitly-listed
constructors, so null-prototype objects fall through to the plain-object path
and are cloned with their null prototype preserved.
Restructure tests to match sparkline's "one spec per source module" convention:
fold cycle.test.ts into clone.test.ts and freeze.test.ts under describe('cycles')
blocks; drop cycle.test.ts.
Adds fast-check as a devDependency.
…copy `bench/clone.bench.mjs` runs five fixture buckets — flat-10, nested-100, large-1000, with-cycles, class-instances — across the in-package `clone` (default and fast variants) and the four established cloners. `bench/baseline.md` captures the Apple M1 / Node 22.22.2 numbers as the 1.0.0 baseline with a 10 percent regression budget going forward. Adds `mitata`, `lodash.clonedeep`, `rfdc`, `fast-copy`, and the lodash types as devDependencies; adds `pnpm bench` script.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Production-grade hardening for the
1.0.0release of@coroboros/clone: typedCloneError, browser-safeBuffer, three granular opt-out flags (cycles,preservePrototype,copyDescriptors), amitatabench against the field, afast-checkproperty suite, CI, and a README rewrite.Changes
CloneErrorclass withcode: 'UNSUPPORTED_TYPE'andError.cause. Unsupported types throw instead of silently returningundefined. Three newCloneOptionsflags, all defaulttrue(back-compat with current behavior). Fix: null-prototype objects (Object.create(null)) clone correctly instead of throwing.globalThis.Bufferat module init. Thenode:bufferimport is type-only.{}instead ofObject.create(Object.prototype). TypedArray dispatch via oneArrayBuffer.isViewcall replacing a 9-entry Set lookup. Array branch swapsforEachfor a classicforloop. Plain-object fast path whencopyDescriptors: false.fast-checkproperty suite and a folded cycles spec. Drops the deadishelper and the standalonecycle.test.ts(folded intoclone.test.tsandfreeze.test.ts).bench/clone.bench.mjsruns five fixture buckets vsstructuredClone,lodash.cloneDeep,rfdc, andfast-copy.bench/baseline.mdcaptures the 1.0.0 numbers and the 10 % regression budget..github/workflows/ci.ymlcallscoroboros/ci/.github/workflows/javascript-npm-packages.yml@v0. OIDC Trusted Publisher andnpm provenanceon tag push.CloneErrorand three-flag docs, fast-clone composition example, CI badge. CLAUDE.md adds the bench command,fast-check, the regression budget, and the CI-owns-publish rule.Breaking changes
clone(fn),clone(promise),clone(new WeakMap()),clone(new Intl.Collator()),clone(SomeConstructor)throwCloneError('UNSUPPORTED_TYPE', ...)instead of returningundefined.helpers.tsno longer exports the deadishelper.Upgrade notes
undefinedreturn for unsupported types now throws. Wrap calls intry/catchor filter inputs upstream when feeding non-clonable values.trueso existing call sites keep their current behavior.Test plan
pnpm lintcleanpnpm typecheckcleanpnpm test— 97/97 greenpnpm build— ESM 7.50 kB / gzip 1.93 kB · CJS 7.62 kB / gzip 1.97 kBpnpm bench— runs to completion across all 5 buckets