/** * @pwngh/economy-lab * * Copyright (c) Preston Neal * * This source code is licensed under the MIT license found in the * LICENSE.md file in the root directory of this source tree. * * @license MIT */ /** * In-process ownership read model: each SKU is interned to a dense int id and each user gets a * bitset, so a warm `owns()` check is a bit test instead of a store round trip. Never the source * of truth — rebuilt from `list()`, invalidated on every write through this store, and expired * after `ttlMs` so writes from other processes surface as bounded staleness. Expiry is encoded * per slot with the same inclusive `now <= expiresAt` rule `owns()` applies in SQL. * * Pass the same `clock` the store uses, or expiry decisions here and in SQL will disagree. * Single-process by design. * * @see {@link https://economy-lab-docs.pages.dev/economy/reference/operations/grant-entitlement/ * Grant entitlement} for the ownership records this read model projects. */ import type { Clock, Store } from '../ports.js'; /** Tuning knobs. Defaults: 30s TTL, 10,000 resident users. */ export interface BitsetOptions { clock?: Clock; /** How long a user's bitmap may serve reads before it must refill from the store. */ ttlMs?: number; /** Resident-user cap; the least recently used bitmap is evicted past it. */ maxUsers?: number; } /** Test-only probe (`__` prefix marks it as not part of the real interface). */ export interface BitsetProbe { /** The synchronous warm path, exposed so tests can pin its zero-allocation property. */ check(userId: string, sku: string, now: number): boolean | null; residentUsers(): number; bytesFor(userId: string): number; } /** * Wraps a store so `entitlements.owns` is served from the bitmap when warm: a warm hit is a * synchronous bit test, a cold or stale user refills once from `list()` and then answers from the * bitmap. Everything else on the store passes through untouched; `transaction` is wrapped only to * observe entitlement writes so their users can be invalidated when the transaction ends. * * The bitmap is never the source of truth. Every `grant` and `revoke` through this store * invalidates the written user (on commit and on rollback); a write from another process surfaces * within `ttlMs` (default 30 seconds) when the user's bitmap expires and refills. Resident users * are capped at `maxUsers` (default 10,000) with least-recently-used eviction. Single-process by * design, and expiry decisions match the store only when both share one clock, so pass the store's * `clock`. * * @example * const store = cachedEntitlements(baseStore, { clock, ttlMs: 30_000 }); * await store.entitlements.owns('usr_42', 'sku_gold_trim'); // cold: fills from list() * await store.entitlements.owns('usr_42', 'sku_gold_trim'); // warm: a bit test, no store call */ export declare function cachedEntitlements(base: Store, options?: BitsetOptions): Store & { __bitset: BitsetProbe; };