Class SnapshotStore

Description

Per-class snapshot store that manages loading, recording, and persisting snapshot data.

Each decorated class gets its own SnapshotStore instance, cached by className + snapshotDir. The store is shared across all instances of the decorated class within the same process.

Features:

  • Streaming JSONL format: header, _t:'s' pooled-string lines, _t:'v' pooled-value lines, and _t:'c' call lines. Incremental append-only flush (no full rewrites after the first flush) keeps record O(N) and memory bounded.
  • Multi-member gzip: each 500-line batch is compressed standalone so appended members stay readable by gunzipSync without re-reading the whole file.
  • String pooling: strings >500 B are deduplicated into a shared _t:'s' dictionary.
  • Value pooling (content-addressable, UNIMOCK_VALUE_POOL*): large serialized subtrees (>100 KB default) are stored once as _t:'v' blobs and referenced from call entries. The dedup index survives flushes, so identical subtrees repeat across records are written only once (Sqquelize include/build/set trees collapse ~4x).
  • Transparent de-pooling on read — replay code never sees pooled_string/pooled_value.

Implements

  • SnapshotStoreEntry

Constructors

  • Parameters

    • className: string

      class name used for the snapshot filename

    • Optional snapshotDir: string

      optional directory override

    • Optional importMeta: ImportMeta

      pass import.meta from calling module to resolve snapshot dir relative to it

      In 'record' mode the store starts from a clean slate: an existing snapshot file is NOT loaded (jsonlOnDisk/jsonlHeaderWritten stay false), so the first flush rewrites the file in full via writeJsonlFull. Outside record mode the file is loaded as before.

    Returns SnapshotStore

Properties

callSeq: Map<string, SnapshotCall[]>

Description

Ordered per-key history of every recorded call event (file order), kept alongside the last-wins data.calls so stateful call sites (iterator next()) can be replayed in order.

className: string

Description

Name of the mocked class (from Base.name).

depth: number = Infinity

Description

Maximum nested wrapping depth (default: Infinity). Set by @Mockable({ depth }).

dirty: boolean = false
jsonlHeaderWritten: boolean = false
jsonlOnDisk: boolean = false
pendingKeys: Set<string>
pendingStrings: Set<string>
pendingValues: Set<string>
poolCounter: number = 0
snapshotPath: string

Description

Absolute path to the snapshot file on disk.

staticReplayCounters: Map<string, number> = ...
staticReplayWarned: Set<string> = ...
stringIndex: Map<string, string>

Description

Reverse index of pooled strings (value → ref) for O(1) lookups.

stringPool: Map<string, string>
symbols: boolean = false

Description

Enable symbol serialization (default: false). Set by @Mockable({ symbols: true }).

valueCounter: number = 0
valueIndex: Map<string, string>
valuePool: Map<string, SerializedValue>
_mode: UnimockMode = ...

Accessors

Methods

  • Returns void

    Description

    Resets all in-memory snapshot state so the next flush rewrites the file in full (lazy truncate). Invoked by setMode on the transition to 'record' so a new record session starts from a clean slate — no cross-session _t:'c' duplicates.

    Unlike release, this also resets poolCounter/valueCounter and jsonlOnDisk/jsonlHeaderWritten (a released store still owns its on-disk file and is lazily re-loaded).

  • Returns void

    Description

    Persists pending changes to the snapshot file; never writes outside record mode. The first flush of a record session rewrites the file in full (writeJsonlFull, lazy truncate), subsequent flushes append incrementally.

  • Parameters

    Returns boolean

    Description

    Decides whether a serialized subtree should be pooled as a _t:'v' blob. Counts the nested nodes cheaply first: subtrees within valuePoolCountLimit() nodes are never pooled (no stringify cost). Larger subtrees are pooled only when the serialised size exceeds valuePoolThreshold (UNIMOCK_VALUE_POOL_THRESHOLD, default 100 KB), so node count guards the stringify cost while the byte threshold guards disk space.

  • Returns Generator<string, any, unknown>

    Description

    Serialised JSONL body of the snapshot file: header, string pool, value pool and call lines — in that deterministic order.

  • Parameters

    • callKey: string

    Returns SnapshotCall

    Description

    Sequence-aware replay lookup for unscoped (static) call keys: the k-th replay call to callKey returns the k-th recorded occurrence (file order), generalising the iterator next() sequence behaviour to statics. When the recorded sequence is exhausted, falls back to the LAST recorded occurrence (warn-once) instead of missing. When a key was recorded exactly once, this is identical to last-wins.

    Returns undefined when the key has no recorded occurrences at all.

  • Returns Buffer

    Description

    Reads the snapshot file into a buffer, decompressing it when gzip-compressed. Scopes the raw bytes so the buffer is garbage-collectable before the (large) jsonl walk.

  • Parameters

    Returns void

    Description

    Records a call entry (last-wins per callKey, plus ordered history for sequence-aware replay). No-op outside record mode (defense in depth): the @Mockable() wrappers never call this in replay/off, and the guard also protects direct API calls.

  • Returns void

    Description

    Frees this store's in-memory snapshot data so it can be garbage-collected once no references to the store remain. Clears data.calls and the string/value pools and pending indexes (all become GC-eligible). After release the store holds no snapshot data; if it is requested again via getSnapshotStore a fresh store is created and lazily re-loaded from disk.

    Call this between host test files to bound memory in a long-running worker (e.g. vitest isolate: false). Make sure the store has been flushed first.

  • Parameters

    • opts: {
          counters: boolean;
          fileState: boolean;
      }
      • counters: boolean
      • fileState: boolean

    Returns void

    Description

    Resets all in-memory snapshot state. fileState additionally forgets the on-disk file (next flush rewrites it in full) and counters resets the pooled string/value counters. Instance config fields (className, snapshotPath, symbols, depth) are left untouched.

  • Parameters

    • target: {
          fd: number;
          gz: boolean;
      }
      • fd: number
      • gz: boolean
    • records: Iterable<string>

    Returns void

    Description

    Serialises records to the file descriptor, batching 500 lines per writeSync (and per gzip member when compression is enabled) to bound memory and I/O syscalls.

  • Parameters

    Returns void

    Description

    Sets the global operating mode. Sweep: the transition into 'record' (from replay/off) resets every cached store to a fresh record session, so the file is rewritten in full on the first flush. Repeating setMode('record') while already in record mode does NOT sweep: the current session stays intact.