@adcp/sdk API Reference - v14.3.0
    Preparing search index...

    Interface IdempotencyBackend

    Storage backend interface. Swap implementations for memory, Postgres, Redis, etc.

    Keys are already composed as {principal}\u001f{key} (or with extra scope segments for per-session tools) before reaching the backend — backends don't need to know about scoping. The separator is U+001F (unit separator) rather than NUL because Postgres TEXT columns reject NUL bytes. The store rejects this separator in principals, keys, and extra-scope segments before backend access; protocol middleware additionally enforces the narrower wire key pattern (^[A-Za-z0-9_.:-]{16,255}$).

    Object-identity contract. Implementations MUST NOT return the same object reference on subsequent get calls — the middleware injects envelope fields (replayed: true, echo-back context) onto the returned value, and a shared reference would leak those mutations across requests. Implementations that store values by reference (e.g., memoryBackend) MUST deep-clone on read; implementations that serialize (e.g., pgBackend via JSON) get this for free.

    interface IdempotencyBackend {
        legacyRetentionGraceSeconds?: number;
        validateClockSkewSeconds?(clockSkewSeconds: number): void;
        get(scopedKey: string): Promise<IdempotencyCacheEntry | null>;
        putIfAbsent(
            scopedKey: string,
            entry: IdempotencyCacheEntry,
        ): Promise<boolean>;
        replaceIfPayloadHash(
            scopedKey: string,
            expectedPayloadHash: string,
            entry: IdempotencyCacheEntry,
        ): Promise<boolean>;
        replaceIfPayloadHashAndExpired(
            scopedKey: string,
            expectedPayloadHash: string,
            entry: IdempotencyCacheEntry,
        ): Promise<boolean>;
        deleteIfPayloadHash(
            scopedKey: string,
            expectedPayloadHash: string,
        ): Promise<boolean>;
        put(scopedKey: string, entry: IdempotencyCacheEntry): Promise<void>;
        delete(scopedKey: string): Promise<void>;
        probe?(): Promise<void>;
        close?(): Promise<void>;
        clearAll?(): Promise<void>;
    }
    Index
    legacyRetentionGraceSeconds?: number

    Minimum physical grace applied to legacy records that do not carry an explicit retainUntil. When present, store construction rejects a larger logical clock-skew window instead of allowing early pruning.

    • Validate backend-specific physical retention against the logical skew.

      Parameters

      • clockSkewSeconds: number

      Returns void

    • Atomically install an entry only if no entry exists for scopedKey. Used as a claim step by the middleware to close the concurrent-miss race (two parallel requests with the same fresh key both seeing miss and both executing side effects). Returns true if the caller "won" the claim and should proceed to run the handler; false if another request claimed first (the caller should treat the result as a replay or conflict on re-check). Callers that need to reclaim a logically expired entry MUST use replaceIfPayloadHashAndExpired() rather than this primitive or a read-derived CAS. Treating expiry as absence lets a stale absent read overwrite a newer generation after its short lease expires.

      Parameters

      Returns Promise<boolean>

    • Atomically replace an entry only when its current payload hash matches and it is logically expired according to backend time. An entry expiring exactly now remains live.

      Parameters

      Returns Promise<boolean>

    • Atomically delete an entry only when its current payload hash matches (used by webhook dedup claims).

      Parameters

      • scopedKey: string
      • expectedPayloadHash: string

      Returns Promise<boolean>

    • Optional startup probe. Implementations that wrap an external store (e.g., pgBackend) should implement this to eagerly validate the connection before the server starts accepting traffic. Called by probeIdempotencyStore() and by serve() when readinessCheck is wired. Throws a descriptive error when the backend is unreachable or the required schema is missing.

      Returns Promise<void>

    • Optional hook for implementations that need to release resources they created and own (for example internal timers or an internally constructed client). Called by store.close(). Backends must not close caller-owned pools or clients that were passed into the backend constructor.

      Returns Promise<void>

    • Optional test-harness hook that drops every cached entry without releasing backend resources. Used by AdcpServer.compliance.reset() between storyboards so idempotency cache hits from one storyboard don't replay into the next (shared brand domain, same key prefix).

      Production backends that can't cheaply flush everything (e.g., a shared Postgres cluster) should leave this undefined — the reset hook refuses to run when this method is missing unless the caller explicitly opts in with { force: true }.

      Returns Promise<void>