Skip to content
Published alpha · next channelRead as Markdown

Fixture capture, cloning, and promise data ​

captureFixture(value) returns a versioned JSON string. restoreFixture(text) returns unknown, not a falsely inferred application type. Validate restored data with the chosen schema before treating it as application input.

The supported graph contains plain records, null-prototype records, arrays (including holes and enumerable custom properties), dates, maps, sets, regular expressions, ArrayBuffers, and the standard ES2022 numeric/BigInt views and DataView. Cycles, repeated object identities, and shared view backing buffers are preserved. Scalars retain undefined, big integers, NaN, infinities, and negative zero. RegExp source, flags, and nonnegative integer lastIndex are retained.

ts
import { captureFixture, restoreFixture, cloneFixture } from '@mimlet/core';

const owner = { id: 'customer-1' };
const fixture = { owner, orders: [{ customer: owner }] };
const saved = captureFixture(fixture);
const restored = restoreFixture(saved); // unknown; validate before application use
const isolated = cloneFixture(fixture); // retains the known input type
// isolated.orders[0].customer === isolated.owner
// isolated.owner !== fixture.owner

cloneFixture creates mutable fixture data; it does not preserve property writability, sealing, or freezing. Accessors, non-enumerable/symbol properties, custom classes, functions, promises, weak collections, host resources, native subclasses, and custom native-object properties are rejected rather than silently lost. Such values need an explicit application serializer or clone callback. Proxies can execute their traps when inspected and are trusted application code.

The decoder has a fixed constructor whitelist. It never evaluates source, looks up arbitrary globals, or fetches references. Prototype-related names are restored as own data properties, not applied through prototype setters. Capture is not encryption: stored fixtures can contain sensitive data.

Options bound nodes (10,000), token entries (100,000), encoded characters (10,000,000), and backing buffer bytes (1,000,000). Encoder recursion defaults to 100 and cannot exceed 256. Decoding reconstructs a flat graph without recursive edge traversal. Limits bound representation work, not arbitrary Proxy traps or application callbacks. Malformed captures fail with INVALID_CAPTURE; budget failures and unsupported values have separate error codes.

Opt-in builder input cloning ​

ts
import { createBuilder, cloneFixture } from '@mimlet/core';

const shared = { tags: ['baseline'] };
const fixtures = createBuilder(() => shared, { cloneInput: cloneFixture }).transform((value) => {
  value.tags.push('test');
  return value;
});

The composed input is cloned after patches/replacements/omissions and before transforms or validation. This protects shared factory results and overrides from mutation by those later steps. Cloning is opt-in and synchronous. Callbacks that subsequently return a different externally shared value remain responsible for that value's identity; the library does not claim universal output isolation. Custom clone callbacks can support application classes. Existing withFactory and replaceFactory remain convenient ways to create fresh overrides.

Promise or thenable fixture data ​

Use fixtureValue(promise) to distinguish promise-valued data from asynchronous execution:

ts
import { createBuilder, fixtureValue } from '@mimlet/core';
const promise = Promise.resolve(42);
const fixtures = createBuilder(() => fixtureValue(promise));
const data = fixtures.build();
// data.value === promise; the builder does not await the nested value.

The wrapper is explicit and frozen. It is not a promise serializer; a capture of that wrapper still rejects its unsupported promise value.

Mimlet · alpha · MIT licensed