Files
skills/docs/foundation/data/packer/src/runtime/runtime.md
2026-05-29 08:20:56 +01:00

4.1 KiB
Raw Blame History

@suaveplan/packer/runtime — Clock + Rng injection

(no submodules)

Purpose

Every production code path in @suaveplan/packer reads wall-clock time and randomness through an injected PackerRuntime bundle instead of calling Date.now(), performance.now(), or crypto.getRandomValues directly. This module hosts the platform defaults plus the resolveRuntime(opts) factory that every option bundle (PackerOptions, PackChunkedOptions, UnpackChunkedOptions) consumes when it constructs its resolved option bundle. The audit line in enterprise/packer.md (L43) makes this a hard requirement: "no Math.random() / Date.now() at module scope; injectable clock and RNG".

The contracts (Clock, MonotonicClock, Rng) are imported from @suaveplan/types — they are the shared cross-package interface every Suaveplan library agrees on for time and randomness. Defaults wire to real platform implementations; tests pass in FakeClock-backed values and a seeded Rng from @suaveplan/testing to drive deterministic pack output.

Features

  • defaultClock — () => Date.now(); wall-clock source for metadata.timestamp.
  • defaultMonotonicClock — performance.now() cast to nanosecond bigint; used for histogram observations.
  • defaultRng — wraps crypto.getRandomValues and exposes the full Rng triplet (nextUint32, nextFloat, nextBytes).
  • resolveRuntime(opts?) — produces a PackerRuntime bundle, falling back to defaults for absent fields. Upgrades a seeded Rng lacking nextBytes by deriving bytes from nextUint32.
  • elapsedMs(monotonicClock, startNanos) — converts a monotonic-clock delta into milliseconds for histogram observations.

Usage: Basic Example

import { Packer } from "@suaveplan/packer";

// Default — no options needed; defaults pull from the platform.
const packer = new Packer();
const bundle = await packer.pack(new Uint8Array([1, 2, 3]));
console.log(bundle.metadata.timestamp); // current Date.now() value

Usage: Advanced Example

import { Packer } from "@suaveplan/packer";
import { FakeClock, seededRng } from "@suaveplan/testing";

// Deterministic pack: byte-identical output for byte-identical input.
const fake = new FakeClock({ now: 1_700_000_000_000 });
const rng = seededRng(0xdeadbeef);
const packer = new Packer({
    clock: () => fake.now,
    monotonicClock: () => BigInt(fake.now) * 1_000_000n,
    rng,
    encryption: {
        algorithm: "xchacha20-poly1305",
        key: new Uint8Array(32),
    },
});
const a = await packer.pack(new Uint8Array([1, 2, 3]));
const b = await packer.pack(new Uint8Array([1, 2, 3]));
// a.data === b.data byte-by-byte because the RNG is seeded and the clock is fixed.

API Reference

  • defaultClock: Clock — () => Date.now().
  • defaultMonotonicClock: MonotonicClock — performance.now() × 1e6 as bigint.
  • defaultRng: Required<Pick<Rng, "nextBytes">> & Rng — CSPRNG-backed Rng with mandatory nextBytes.
  • resolveRuntime(opts?: RuntimeOptions): PackerRuntime — produces the resolved bundle; opts.clock, opts.monotonicClock, opts.rng each default to the matching platform default.
  • elapsedMs(monotonicClock, startNanos): number — millisecond delta from the monotonic-clock sample at startNanos.

Implementation Notes

  • defaultMonotonicClock rounds performance.now() to the nearest nanosecond integer; the float→bigint cast is lossless for any process uptime under ~584 years.
  • ensureNextBytes (internal) extracts four bytes per nextUint32 call, big-endian, when the injected Rng lacks nextBytes. This keeps the AEAD-nonce derivation deterministic under any seeded RNG.
  • The runtime carries no state between calls — the bundle is a plain object holding the three injected functions. Re-resolving with the same inputs is cheap and side-effect-free.
  • resolveRuntime is the single funnel every production option bundle in the package consumes. The grep audit pinned by PACKER-25's fourth scenario verifies that no other production file references Date.now / Math.random / crypto.getRandomValues / performance.now.