Files
skills/docs/commerce/contacts/audience-rules/src/store/store.md
2026-05-29 08:20:56 +01:00

3.3 KiB

store

(no submodules)

Purpose

store.ts defines the SegmentStore interface that abstracts segment persistence behind a six-method contract (create, getById, update, delete, list, updateCache) and ships InMemorySegmentStore — a single-process, map-backed implementation suitable for development, tests, and lightweight production deployments. The in-memory store is built around injectable Clock and IdGenerator primitives from @suaveplan/types, so production paths never call Date.now() / new Date() directly or mutate a module-scope counter.

Features

  • SegmentStore interface — six async methods, all returning the persisted Segment (or undefined when the identifier does not exist).
  • InMemorySegmentStore — single-process map-backed implementation.
  • InMemorySegmentStoreOptions { clock?, idGenerator? } — both injection points are optional with sensible defaults.
  • Zod boundary on create and update — invalid input raises AudienceRulesError with code AUDIENCE_RULES.INVALID_INPUT (never a raw ZodError).
  • Multi-tenant isolation on list(tenantId) — segments owned by other tenants are invisible.
  • updateCache(id, contactIds) updates cachedContactIds, cachedCount, lastRefreshedAt, and updatedAt atomically.

Usage: Basic Example

import { InMemorySegmentStore } from "@suaveplan/audience-rules";

const store = new InMemorySegmentStore();
const segment = await store.create({
    tenantId: "tenant-1",
    name: "Active VIPs",
    rules: [{ field: "tags", operator: "contains", value: "vip" }],
    refreshMode: "manual",
});

Usage: Advanced Example

import { InMemorySegmentStore } from "@suaveplan/audience-rules";

// Deterministic store for tests: pinned clock + monotonic ID generator
let n = 0;
const store = new InMemorySegmentStore({
    clock: () => 1_700_000_000_000,
    idGenerator: () => {
        n += 1;
        return `test_seg_${n}`;
    },
});

const a = await store.create({
    tenantId: "t1",
    name: "A",
    rules: [],
    refreshMode: "manual",
});
// a.id === "test_seg_1"; a.createdAt.getTime() === 1_700_000_000_000

API Reference

  • interface SegmentStore — see Features.
  • interface InMemorySegmentStoreOptions — clock?: Clock, idGenerator?: IdGenerator (both from @suaveplan/types).
  • class InMemorySegmentStore implements SegmentStore — constructor accepts InMemorySegmentStoreOptions. Throws AudienceRulesError with AUDIENCE_RULES.INVALID_INPUT on Zod failure in create / update, and on list with an empty tenantId.

Implementation Notes

  • The default IdGenerator is a per-instance closure, so two separate stores in the same process produce independent ID sequences. This matches the @suaveplan/contacts InMemoryContactStore convention.
  • create and update validate input with parseOrThrow before mutating the internal map — failure leaves the store untouched.
  • list takes an O(n) scan; the package is a development-tier store and multi-tenant filtering happens in-memory. Production stores backed by a database can index on tenantId directly.
  • Every mutation updates updatedAt via the injected clock, so OTel histograms and snapshot diffs in consumers stay deterministic under FakeClock.