tapedb/worker
Gegenereerd uit de TypeScript-declaraties van het publieke tapedb/worker-entrypoint. Signatures en comments zijn het API-contract voor deze preview.
Functions
Section titled “Functions”authenticateEngineRequest
Section titled “authenticateEngineRequest”Extracts and verifies the claim attached to an engine request.
declare function authenticateEngineRequest(request: Request, env: ClaimVerificationEnv, workspaceId: string): Promise<RoleClaim>;defineTapeDB
Section titled “defineTapeDB”declare function defineTapeDB(options: DefineTapeDBOptions): TapeDBWorker;encodeTrustedRoleClaim
Section titled “encodeTrustedRoleClaim”Serialises the verified claim for the Durable Object. Only the fields the engine needs are forwarded; signature material is never re-sent.
declare function encodeTrustedRoleClaim(claim: RoleClaim): string;extractClaimToken
Section titled “extractClaimToken”Reads a claim token from the request.
Authorization: Bearer <jwt>is the preferred transport.?claim=<jwt>is a temporary fallback for WebSocket upgrades, where browser clients cannot set anAuthorizationheader. The mint endpoint should prefer a header wherever the client supports it.
A present-but-non-Bearer Authorization header returns null instead of
falling back to the query string, so the two transports cannot be confused.
declare function extractClaimToken(request: Request): string | null;mintClaim
Section titled “mintClaim”POST /_tapedb/claim: session to workspace role to a short-lived claim.
Rate limiting is the deployment’s call. If you expose this on the public
internet, back allowRequest with a Cloudflare rate-limit binding.
declare function mintClaim(request: Request, deps: MintClaimDependencies): Promise<Response>;mintDevClaim
Section titled “mintDevClaim”Development-only issuer. Mints an OWNER claim for any workspace without a
session so tapedb dev works with zero configuration.
The caller is responsible for refusing this in production; defineTapeDB
enforces that before this function is reachable.
declare function mintDevClaim(workspaceId: string, secret: string): Promise<MintedClaim>;signRoleClaim
Section titled “signRoleClaim”Signs the compact HS256 claim the engine verifier accepts.
declare function signRoleClaim(secret: string, input: MintClaimInput, now?: number): Promise<MintedClaim>;verifyRoleClaim
Section titled “verifyRoleClaim”Verifies a compact JWS against the frozen RoleClaim contract.
Throws ClaimError for malformed tokens, bad signatures, expiry, workspace
mismatches and invalid claim values. A missing server secret throws a plain
Error: that is a deployment problem, not a client failure.
declare function verifyRoleClaim(token: string, env: ClaimVerificationEnv, expectedWorkspaceId: string): Promise<RoleClaim>;Classes
Section titled “Classes”ClaimError
Section titled “ClaimError”Typed verification failure; code is safe to return to the caller.
declare class ClaimError extends Error { readonly code: ClaimErrorCode; constructor(code: ClaimErrorCode, message: string);}WorkspaceRoom
Section titled “WorkspaceRoom”One workspace engine, addressed by idFromName(workspaceId).
Owns its SQLite schema, applies bundled Atlas migrations on boot, commits transactions, and flushes/snapshots to R2 on a 30-second alarm.
declare class WorkspaceRoom extends DurableObject<Env> { /** * Last emitted result per socket client id and subscription id. This is a * cache, not a source of truth: hibernation clears it and the next pass * falls back to a full `query:data` payload. */ private readonly subscriptionResults; /** Namespaces touched since the current debounce window opened. */ private pendingInvalidationNamespaces; private invalidationTimer; constructor(ctx: DurableObjectState, env: Env); private get workspaceId(); /** * Applies pending migrations before the object serves any request. Each * migration and its `_migrations` row commit in the same transaction, so a * failed boot cannot leave a half-migrated schema, and each runs with * foreign keys off and checked before commit (see * `applyMigrationWithoutCascades`). */ private applyMigrations; private applyMigration; fetch(request: Request): Promise<Response>; private route; private transact; /** * Runs a query document against this workspace. The claim is read from the * Worker-forwarded header; `executeQuery` requires `view` on every requested * and traversed namespace before touching SQL. */ private query; private handleWebSocket; webSocketMessage(ws: WebSocket, message: string | ArrayBuffer): Promise<void>; webSocketClose(ws: WebSocket, code: number, reason: string): void; webSocketError(ws: WebSocket): void; private handlePing; private topicError; private handleTopicJoin; private handleTopicLeave; /** * Delivers to every other socket joined to the topic. The sender need not * be joined; it never receives its own message. */ private handleTopicSend; private handlePresenceSet; private handlePresenceClear; private handleCursorSet; private handleDragSet; private handleDragClear; private broadcastPresence; private broadcastToTarget; private presencePeers; private socketsForTarget; private removePresence; private handleSubscribe; private handleUnsubscribe; private send; private closeSocket; private resultsFor; private dropSubscriptionResults; private scheduleInvalidation; private flushInvalidations; /** One subscription's re-run for this flush: its result, or why it failed. */ private runSubscription; /** * Runs the durability pass (flush then snapshot) and schedules the next * pass only while there is work left. The 30s delay lets transactions * accumulate into fewer, larger log segments. */ alarm(): Promise<void>; /** * Schedules an alarm for the next due durability pass. An existing alarm is * never pushed later: a write must not delay a flush that is already due. */ private scheduleNextDurabilityAlarm; private scheduleDurabilityAlarm; /** * File metadata rows. Bytes live in R2 and never enter the transaction log; * this table is the queryable index over them. */ private listFiles; private recordFile; private forgetFile; /** File-index entries are log rows, so they ship to R2 like any write. */ private scheduleFileFlush; /** Registry snapshot for tooling: API names, attribute types and links. */ private schemaDocument; private status;}Interfaces
Section titled “Interfaces”ClaimIdentity
Section titled “ClaimIdentity”The two methods a deployment implements. Everything else about identity (passwords, magic codes, OAuth, cookies) stays in the host application.
interface ClaimIdentity { /** Resolve the caller's subject from the request. Null means "not signed in". */ getSubject(headers: Headers): Promise<{ sub: string; } | null>; /** Resolve the caller's role in a workspace. Null means "not a member". */ getRole(workspaceId: string, sub: string): Promise<WorkspaceRole | null>;}DefineTapeDBOptions
Section titled “DefineTapeDBOptions”interface DefineTapeDBOptions { schema: TapeDBSchema; /** * The app's committed migrations, from `tapedb/migrations.gen.ts`. Omit * only before the first `tapedb migrate dev`: the schema's generated SQL * is used, which cannot evolve. */ migrations?: readonly AppMigration[]; /** * Identity adapter. Implement `getSubject` and `getRole` to serve * `POST /_tapedb/claim`. Without it that route returns 501 and the consumer * must supply tokens to the client itself. */ identity?: ClaimIdentity; /** * Development-only issuer: mints an OWNER claim for any workspace with no * session. Always refused when `ENVIRONMENT` is `production`. */ devClaims?: boolean; /** * Broadcast topics. `authorize` decides whether a caller may join a topic * or send to it; without it any member of the workspace may, as with * presence. */ topics?: { authorize?: TopicAuthorizer; };}Worker bindings and vars. tapedb init writes exactly these.
Three resources: one Durable Object namespace, two R2 buckets. There is no D1 and no identity database: the engine consumes a verified claim and never reads a session.
interface Env { /** The database room. One instance per workspace. */ WORKSPACE: DurableObjectNamespace<WorkspaceRoom>; /** Transaction log segments and snapshots. */ DATA: R2Bucket; /** Uploaded files. Optional unless `db.$files` is used. */ FILES?: R2Bucket; ENVIRONMENT?: string; SERVICE_VERSION?: string; ENABLE_SMOKE?: string; ALLOWED_ORIGINS?: string; /** Shared HS256 secret used to verify role claims. */ TAPEDB_CLAIM_SECRET: string; /** Optional expected `iss` claim; checked only when set. */ TAPEDB_CLAIM_ISSUER?: string; /** Optional expected `aud` claim; checked only when set. */ TAPEDB_CLAIM_AUDIENCE?: string; /** * Enables the development claim issuer. Set to `"true"` by `tapedb dev` on * the command line and never written to `wrangler.jsonc`, so a plain * `wrangler deploy` has no dev claim route. */ TAPEDB_DEV_CLAIMS?: string;}MintClaimDependencies
Section titled “MintClaimDependencies”interface MintClaimDependencies { identity: ClaimIdentity; secret: string; issuer?: string; audience?: string; ttlSeconds?: number; now?: () => number; /** Optional per-caller limiter; return false to reject with 429. */ allowRequest?: (key: string) => Promise<boolean>;}TapeDBWorker
Section titled “TapeDBWorker”interface TapeDBWorker { /** The database room class. Export it from the Worker entry. */ WorkspaceRoom: typeof WorkspaceRoom; worker: { fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response>; };}AppMigration
Section titled “AppMigration”One app migration, as tapedb migrate bundles it.
type AppMigration = BundledWorkspaceMigration;TopicAction
Section titled “TopicAction”Broadcast topics: fire-and-forget messages between the sockets of one workspace, on the socket they already hold.
A socket joins topics; a message sent to a topic reaches every other socket joined to it, with the sender’s client id and subject. Nothing is stored or replayed: a socket that is not joined when a message is sent never sees it. Joined topics live in the socket attachment, so they survive hibernation, and the client rejoins them after a reconnect.
Who may join or send is the deployment’s call (defineTapeDB({ topics }));
by default any member of the workspace may, as with presence.
type TopicAction = "join" | "send";TopicAuthorizer
Section titled “TopicAuthorizer”Decides whether a caller may join a topic or send to it. Return false (or
throw) to refuse; the socket gets a topic:error and nothing else happens.
type TopicAuthorizer = (context: { auth: { sub: string; role: number; workspaceId: string; }; topic: string; action: TopicAction;}) => boolean;Constants
Section titled “Constants”MAX_UPLOAD_BYTES
Section titled “MAX_UPLOAD_BYTES”Worker entry.
defineTapeDB() installs the deployment schema and returns the Durable
Object class plus the fetch handler. The consumer’s Worker is then two
exports and nothing else:
const tapedb = defineTapeDB({ schema }); export const WorkspaceRoom = tapedb.WorkspaceRoom; export default tapedb.worker;
declare const MAX_UPLOAD_BYTES: number;TRUSTED_CLAIM_HEADER
Section titled “TRUSTED_CLAIM_HEADER”Trusted header forwarded to the Durable Object after verification.
declare const TRUSTED_CLAIM_HEADER = "x-tapedb-claims";WORKSPACE_ROLE_LEVELS
Section titled “WORKSPACE_ROLE_LEVELS”Shared role-claim contract between the Worker (JWT verification) and the workspace engine (permission checks). consumes this shape.
declare const WORKSPACE_ROLE_LEVELS: { readonly GUEST: 0; readonly USER: 1; readonly MEMBER: 2; readonly EDITOR: 3; readonly MODERATOR: 4; readonly ADMIN: 5; readonly OWNER: 6; readonly FOUNDER: 7; readonly DEVELOPER: 8;};