accounts
Version:
Tempo Accounts SDK
165 lines • 6.44 kB
TypeScript
/**
* Minimal key-value store interface used by the SDK's server primitives
* (e.g. SIWE nonce store, session store).
*
* Values are JSON-serialized when stored. TTLs are optional; consumers that
* need expiry pass `{ ttl }` (in seconds) to `set` and the implementation
* lazily evicts (memory) or relies on the backing store's native expiry
* (Cloudflare KV).
*/
export type Kv = {
/** Read a value by key. Returns `undefined` when missing or expired. */
get: <value = unknown>(key: string) => Promise<value | undefined>;
/** Write a value. When `ttl` is set, the entry expires after the given duration in seconds. */
set: (key: string, value: unknown, options?: set.Options | undefined) => Promise<void>;
/** Delete a value by key. */
delete: (key: string) => Promise<void>;
/**
* Atomic read-and-delete. Returns the value if present, `undefined` if
* missing or expired. Across concurrent callers, exactly one observer
* receives a non-`undefined` return for a given key, and the key is
* removed exactly once.
*
* Optional. Required for one-time-consume semantics (e.g. SIWE
* challenge nonces). Backends without a linearizable read+delete
* primitive (e.g. eventually-consistent stores like Cloudflare KV)
* should leave this undefined; the consuming handler will refuse to
* accept the store at construction time and fall back to a different
* backend (e.g. a Durable Object).
*/
take?: <value = unknown>(key: string) => Promise<value | undefined>;
};
export declare namespace set {
type Options = {
/** Time-to-live in seconds. After this duration, `get` returns `undefined`. */
ttl?: number | undefined;
};
}
/** Wrap an existing `Kv`-shaped object so the SDK accepts it as a `Kv`. */
export declare function from<kv extends Kv>(kv: kv): kv;
/**
* Adapt a Cloudflare Workers KV namespace (or compatible binding) into a
* `Kv`. Uses the underlying store's native `expirationTtl` for TTL.
*
* Cloudflare KV's minimum TTL is 60 seconds; the platform enforces its own
* minimum independent of what's passed here.
*
* **Not safe for one-time-consume semantics.** Cloudflare KV is eventually
* consistent across data centers — concurrent read+delete races can let
* the same key be "consumed" twice. `take` is intentionally NOT
* implemented. Use a Durable Object (or another linearizable backend)
* for the SIWE challenge nonce store.
*/
export declare function cloudflare(kv: cloudflare.Parameters): Kv;
export declare namespace cloudflare {
type Parameters = {
get: <value = unknown>(key: string, format: 'json') => Promise<value | null>;
put: (key: string, value: string, options?: {
expirationTtl?: number;
} | undefined) => Promise<void>;
delete: (key: string) => Promise<void>;
};
}
/**
* Adapt a Cloudflare Durable Object namespace into a `Kv` with atomic
* `take`. Unlike `Kv.cloudflare`, a Durable Object's storage is
* single-actor and linearizable — `take` (read+delete) is guaranteed
* atomic across concurrent callers, which makes this the recommended
* backend for SIWE challenge nonce storage on Cloudflare Workers.
*
* Pair with `Kv.NonceStorage` (or your own DO class implementing the
* same fetch protocol).
*
* Example:
*
* ```ts
* // wrangler.jsonc
* // {
* // "durable_objects": {
* // "bindings": [{ "name": "NONCE_DO", "class_name": "NonceStorage" }]
* // },
* // "migrations": [{ "tag": "v1", "new_classes": ["NonceStorage"] }]
* // }
*
* // worker.ts
* export { NonceStorage } from 'accounts/server'
*
* export default {
* fetch(req, env) {
* const handler = Handler.auth({
* store: Kv.durableObject(env.NONCE_DO),
* origin: 'https://app.example.com',
* })
* return handler.fetch(req)
* }
* }
* ```
*/
export declare function durableObject(namespace: durableObject.Namespace, options?: durableObject.Options): Kv;
export declare namespace durableObject {
/**
* Minimal shape of a Cloudflare Durable Object namespace binding.
* Compatible with `DurableObjectNamespace` from `@cloudflare/workers-types`.
*
* `get`'s parameter is typed as `any` (rather than `unknown`) so the
* stricter `(id: DurableObjectId) => ...` signature on Cloudflare's
* `DurableObjectNamespace` is structurally assignable here without an
* intermediate cast on the caller.
*/
type Namespace = {
idFromName: (name: string) => unknown;
get: (id: any) => {
fetch: (input: string, init?: unknown) => Promise<Response>;
};
};
type Options = {
/**
* Durable Object instance name. Defaults to `'default'` (a single
* shared actor). Use a per-tenant name if you need isolation.
*/
name?: string | undefined;
};
}
/**
* Reference Durable Object class implementing the `Kv.durableObject`
* fetch protocol. Export from your Worker entry and bind it under
* `class_name: "NonceStorage"` in `wrangler.jsonc`.
*
* The class is framework-agnostic — it doesn't import `cloudflare:workers`
* so it works with both the legacy DO API (`fetch(req)` only) and the
* newer `extends DurableObject` API.
*/
export declare class NonceStorage {
state: NonceStorage.State;
constructor(state: NonceStorage.State, _env?: unknown);
fetch(request: Request): Promise<Response>;
}
export declare namespace NonceStorage {
/** Subset of `DurableObjectState` actually used by `NonceStorage`. */
type State = {
storage: {
get: <T = unknown>(key: string) => Promise<T | undefined>;
put: (key: string, value: unknown) => Promise<void>;
delete: (key: string) => Promise<void>;
};
};
/** Internal storage shape: value plus optional absolute expiry timestamp (ms). */
type Entry = {
value: unknown;
expiresAt?: number;
};
}
/**
* In-memory `Kv` for tests and single-process deployments. Lazily evicts
* expired entries on read/write.
*
* Pass `now` to control the clock in tests.
*/
export declare function memory(options?: memory.Options): Kv;
export declare namespace memory {
type Options = {
/** Clock function for TTL accounting. Defaults to `Date.now`. */
now?: (() => number) | undefined;
};
}
//# sourceMappingURL=Kv.d.ts.map