domainkit 0.23.1 → 0.23.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"Storage.mjs","names":["Plan.PlanId","Plan.Kind","Plan.Model","Approval.Model","Receipt.Model","Receipt.ReceiptId","Plan.Digest","Plan.OperationId","DnsRecord.Model","Errors.isDomainKitError","Errors.DomainKitError","Reason.StorageFailed","Principal.Service","Errors.fail","Reason.Busy"],"sources":["../src/Storage.ts"],"sourcesContent":["/**\n * Everything DomainKit persists, behind one service the host installs once. Every method requires\n * `Principal` and is scoped by it. `@domainkit/capsuledb` ships the Postgres implementation;\n * `domainkit/testing` ships the memory one; both pass `Testing.conformance.storage`.\n *\n * Storage never sees plaintext credentials: `Connect` seals secrets through `Custody` before\n * writing a `Credential` row and opens them after reading one.\n *\n * Grouped by noun so the 25-odd methods stay navigable. Implementations must satisfy the\n * invariants documented per group; the conformance suite checks them.\n */\nimport { Context, DateTime, Effect, Layer, Option, Schema } from \"effect\";\n\nimport * as Approval from \"./Approval.ts\";\nimport * as DnsRecord from \"./DnsRecord.ts\";\nimport * as Errors from \"./internal/error.ts\";\nimport * as Reason from \"./Reason.ts\";\nimport { makeMemory } from \"./internal/storage-memory.ts\";\nimport * as Plan from \"./Plan.ts\";\nimport * as Principal from \"./Principal.ts\";\nimport * as Receipt from \"./Receipt.ts\";\n\nexport type Fx<A> = Effect.Effect<A, Errors.DomainKitError, Principal.Service>;\n\n// ---------------------------------------------------------------------------------------------\n// Rows\n// ---------------------------------------------------------------------------------------------\n\nexport const Capability = Schema.Literals([\"dns:read\", \"dns:write\"]);\nexport type Capability = typeof Capability.Type;\n\nexport const AuthMethod = Schema.Literals([\"token\", \"oauth\", \"integration\"]);\nexport type AuthMethod = typeof AuthMethod.Type;\n\n/** Sealed provider credential plus the metadata needed without unsealing it. */\nexport class Credential extends Schema.Class<Credential>(\"@domainkit/Storage/Credential\")({\n ciphertext: Schema.String,\n expiresAt: Schema.NullOr(Schema.DateTimeUtcFromString),\n rotatedAt: Schema.DateTimeUtcFromString,\n}) {}\n\n/** One provider authorization (an OAuth grant or a token) owned by a principal. */\nexport class Authorization extends Schema.Class<Authorization>(\"@domainkit/Storage/Authorization\")({\n id: Schema.String,\n ownerId: Schema.String,\n provider: Schema.String,\n method: AuthMethod,\n capabilities: Schema.Array(Capability),\n /** Provider-specific account context (account id, team id, ...) decoded by the provider's `context` schema. */\n context: Schema.Unknown,\n /**\n * What the provider called the account when the credential was issued, so a UI names the\n * connection without a provider call. `null` when the provider names no single account.\n */\n label: Schema.NullOr(Schema.String),\n revocation: Schema.Literals([\"active\", \"pending\", \"revoked\"]),\n createdBy: Schema.String,\n createdAt: Schema.DateTimeUtcFromString,\n}) {}\n\n/** A connection is the principal-facing handle over an authorization; one authorization, many domains. */\nexport class Connection extends Schema.Class<Connection>(\"@domainkit/Storage/Connection\")({\n id: Schema.String,\n ownerId: Schema.String,\n authorizationId: Schema.String,\n createdAt: Schema.DateTimeUtcFromString,\n}) {}\n\nexport class Attachment extends Schema.Class<Attachment>(\"@domainkit/Storage/Attachment\")({\n id: Schema.String,\n ownerId: Schema.String,\n connectionId: Schema.String,\n domain: Schema.String,\n zone: Schema.String,\n /** The target's label as the provider gave it at attach time, so a UI names the account without a provider call. */\n label: Schema.String,\n /** Provider zone identity (zone id, account id) decoded by the provider's `context` schema. */\n target: Schema.Unknown,\n createdAt: Schema.DateTimeUtcFromString,\n}) {}\n\n/** Interactive-flow state between `Connect.start` and `Connect.complete`. */\nexport class Continuation extends Schema.Class<Continuation>(\"@domainkit/Storage/Continuation\")({\n id: Schema.String,\n ownerId: Schema.String,\n actorId: Schema.String,\n provider: Schema.String,\n payload: Schema.Unknown,\n returnTo: Schema.NullOr(Schema.String),\n expiresAt: Schema.DateTimeUtcFromString,\n}) {}\n\n/**\n * Who a continuation belongs to, without the continuation itself. `payload` has no place here: it\n * carries the PKCE `codeVerifier`, which only the owner-scoped read may reach.\n */\nexport class ContinuationHeader extends Schema.Class<ContinuationHeader>(\n \"@domainkit/Storage/ContinuationHeader\",\n)({\n ownerId: Schema.String,\n actorId: Schema.String,\n provider: Schema.String,\n expiresAt: Schema.DateTimeUtcFromString,\n}) {}\n\nexport const AttemptStatus = Schema.Literals([\n \"planned\",\n \"approved\",\n \"applying\",\n \"complete\",\n \"partial\",\n \"failed\",\n \"expired\",\n \"rejected\",\n]);\nexport type AttemptStatus = typeof AttemptStatus.Type;\n\n/** Who declined the plan, why, and when. Terminal: the domain needs a new plan. */\nexport const Rejection = Schema.Struct({\n actorId: Schema.String,\n reason: Schema.NullOr(Schema.String),\n at: Schema.DateTimeUtcFromString,\n});\nexport type Rejection = typeof Rejection.Type;\n\n/** One durable plan -> approval -> receipt lifecycle. */\nexport class Attempt extends Schema.Class<Attempt>(\"@domainkit/Storage/Attempt\")({\n id: Plan.PlanId,\n ownerId: Schema.String,\n attachmentId: Schema.String,\n kind: Plan.Kind,\n status: AttemptStatus,\n plan: Plan.Model,\n approval: Schema.NullOr(Approval.Model),\n receipt: Schema.NullOr(Receipt.Model),\n rejection: Schema.NullOr(Rejection),\n /** Cleanup attempts point at the provisioning receipt they undo. */\n sourceReceiptId: Schema.NullOr(Receipt.ReceiptId),\n leaseExpiresAt: Schema.NullOr(Schema.DateTimeUtcFromString),\n /** Why the last apply stopped before any write; cleared on the next claim. */\n failure: Schema.NullOr(Schema.String),\n updatedAt: Schema.DateTimeUtcFromString,\n}) {}\n\nexport const BatchId = Schema.String.pipe(Schema.brand(\"@domainkit/BatchId\"));\nexport type BatchId = typeof BatchId.Type;\n\n/**\n * Where a batch stands, recomputed from its items' attempts after every transition.\n *\n * `planning` while any item is still without a plan, `planned` once every item carries one,\n * `approved` once the principal bound the batch digest, then the apply states. `partial` and\n * `failed` are resumable; `complete` and `rejected` are terminal and leave the unfinished index.\n */\nexport const BatchStatus = Schema.Literals([\n \"planning\",\n \"planned\",\n \"approved\",\n \"applying\",\n \"complete\",\n \"partial\",\n \"failed\",\n \"rejected\",\n]);\nexport type BatchStatus = typeof BatchStatus.Type;\n\n/** Who bound the batch to its digest, and when. The digest itself is `Batch.digest`. */\nexport const BatchApproval = Schema.Struct({\n actorId: Schema.String,\n at: Schema.DateTimeUtcFromString,\n});\nexport type BatchApproval = typeof BatchApproval.Type;\n\n/**\n * Many domains planned together, approved once, and applied with bounded concurrency.\n *\n * The batch owns the aggregate's own state only: its digest, who approved or declined it, and the\n * status recomputed from its items. Every plan, approval, receipt, lease, and failure stays on the\n * attempt an item points at, so nothing is stored twice.\n */\nexport class Batch extends Schema.Class<Batch>(\"@domainkit/Storage/Batch\")({\n id: BatchId,\n ownerId: Schema.String,\n kind: Plan.Kind,\n status: BatchStatus,\n /** SHA-256 over the items' `attachmentId:planDigest` pairs, bound at approval. */\n digest: Schema.NullOr(Plan.Digest),\n approval: Schema.NullOr(BatchApproval),\n rejection: Schema.NullOr(Rejection),\n /** Unique per owner: a replayed create returns the batch this key already made. */\n idempotencyKey: Schema.String,\n createdBy: Schema.String,\n createdAt: Schema.DateTimeUtcFromString,\n updatedAt: Schema.DateTimeUtcFromString,\n /** When the batch reached `complete` or `rejected`; null while it still owes a move. */\n completedAt: Schema.NullOr(Schema.DateTimeUtcFromString),\n}) {}\n\n/**\n * One domain's place in a batch: a pointer to the attachment and to the attempt carrying its plan.\n *\n * `attemptId` is null until the item is planned, and `planFailure` carries why the last planning\n * pass stopped, cleared the moment a plan lands.\n */\nexport class BatchItem extends Schema.Class<BatchItem>(\"@domainkit/Storage/BatchItem\")({\n batchId: BatchId,\n attachmentId: Schema.String,\n /** The item's place in the batch, as `create` received it. */\n position: Schema.Number,\n attemptId: Schema.NullOr(Plan.PlanId),\n planFailure: Schema.NullOr(Schema.String),\n}) {}\n\n/**\n * The status a batch holds, given whether it was approved or declined and where each item's\n * attempt stands. `null` names an item that has no plan yet.\n *\n * Both `Storage` implementations recompute the stored status through this after every transition,\n * so the owner-scoped unfinished index never disagrees with the attempts it summarizes.\n */\nexport const batchStatusOf = (input: {\n readonly approved: boolean;\n readonly rejected: boolean;\n readonly items: ReadonlyArray<AttemptStatus | null>;\n}): BatchStatus => {\n if (input.rejected) return \"rejected\";\n const items = input.items;\n if (items.length === 0 || items.some((status) => status === null)) return \"planning\";\n const statuses = items as ReadonlyArray<AttemptStatus>;\n const every = (status: AttemptStatus) => statuses.every((item) => item === status);\n if (every(\"planned\")) return \"planned\";\n // An approval the batch never took means an item moved on its own; it is not approvable, and\n // planning is the state a host resumes from.\n if (!input.approved) return \"planning\";\n if (every(\"approved\")) return \"approved\";\n const settled = statuses.every(\n (status) => status !== \"planned\" && status !== \"approved\" && status !== \"applying\",\n );\n if (!settled) return \"applying\";\n if (every(\"complete\")) return \"complete\";\n return statuses.some((status) => status === \"partial\") ? \"partial\" : \"failed\";\n};\n\n/** A batch with its items, in `position` order. Every batch read returns one. */\nexport interface BatchAggregate {\n readonly batch: Batch;\n readonly items: ReadonlyArray<BatchItem>;\n}\n\n/** One item's attempt approval, as `batches.approve` writes it beside the batch's own. */\nexport interface BatchItemApproval {\n readonly attachmentId: string;\n readonly approval: Approval.Model;\n}\n\nexport const RequirementStatus = Schema.Literals([\"satisfied\", \"missing\", \"mismatch\", \"unknown\"]);\nexport type RequirementStatus = typeof RequirementStatus.Type;\n\nexport const Overall = Schema.Literals([\"ready\", \"pending\", \"failed\"]);\nexport type Overall = typeof Overall.Type;\n\n/**\n * Latest observed readiness for one domain, written by `Verify.observe`. Keyed by domain so\n * observe-only hosts (no attachment, public DNS alone) get the same row; `attachmentId` links the\n * attachment when one exists.\n */\nexport class Readiness extends Schema.Class<Readiness>(\"@domainkit/Storage/Readiness\")({\n domain: Schema.String,\n attachmentId: Schema.NullOr(Schema.String),\n ownerId: Schema.String,\n overall: Overall,\n requirements: Schema.Array(\n Schema.Struct({\n operationId: Schema.NullOr(Plan.OperationId),\n record: DnsRecord.Model,\n status: RequirementStatus,\n /** Encoded `Verify.Evidence` values. */\n evidence: Schema.Array(Schema.Unknown),\n }),\n ),\n /** Encoded `Verify.HostEvidence` values, keyed by `source` on merge. */\n host: Schema.Array(Schema.Unknown),\n /** When the current pending streak began; drives the backoff ladder. */\n pendingSince: Schema.NullOr(Schema.DateTimeUtcFromString),\n checkedAt: Schema.DateTimeUtcFromString,\n nextCheckAt: Schema.NullOr(Schema.DateTimeUtcFromString),\n}) {}\n\n// ---------------------------------------------------------------------------------------------\n// Service\n// ---------------------------------------------------------------------------------------------\n\nexport interface Interface {\n readonly authorizations: {\n /**\n * Insert, or CAS-replace when `expectedId` is set: the row must exist under this owner with an\n * active revocation state, else `NotFound` / `Busy`.\n */\n readonly upsert: (input: {\n readonly authorization: Authorization;\n readonly credential: Credential;\n readonly expectedId?: string;\n }) => Fx<Authorization>;\n readonly get: (id: string) => Fx<Authorization>;\n readonly credential: (id: string) => Fx<Credential>;\n readonly rotate: (id: string, credential: Credential) => Fx<void>;\n readonly promoteCapabilities: (id: string, capabilities: ReadonlyArray<Capability>) => Fx<void>;\n /**\n * Two-phase: mark `pending`, run `revoke` outside any transaction, then delete. A failed\n * `revoke` leaves the row pending; `recoverRevocations` finishes it later.\n */\n readonly revoke: <E, R>(\n id: string,\n revoke: Effect.Effect<void, E, R>,\n ) => Effect.Effect<void, Errors.DomainKitError | E, Principal.Service | R>;\n readonly recoverRevocations: <E, R>(\n revoke: (authorization: Authorization) => Effect.Effect<void, E, R>,\n ) => Effect.Effect<number, Errors.DomainKitError | E, Principal.Service | R>;\n };\n readonly connections: {\n readonly create: (authorizationId: string) => Fx<Connection>;\n readonly get: (id: string) => Fx<Connection>;\n readonly list: (filter?: { readonly provider?: string }) => Fx<ReadonlyArray<Connection>>;\n /** Fails `InvalidInput` while attachments still reference the connection. */\n readonly remove: (id: string) => Fx<void>;\n };\n readonly attachments: {\n /** One attachment per (owner, domain); a duplicate fails `InvalidInput`. */\n readonly create: (input: {\n readonly connectionId: string;\n readonly domain: string;\n readonly zone: string;\n readonly label: string;\n readonly target: unknown;\n }) => Fx<Attachment>;\n readonly get: (id: string) => Fx<Attachment>;\n readonly byDomain: (domain: string) => Fx<Option.Option<Attachment>>;\n readonly list: (connectionId: string) => Fx<ReadonlyArray<Attachment>>;\n readonly remove: (id: string) => Fx<void>;\n };\n readonly continuations: {\n readonly put: (continuation: Continuation) => Fx<void>;\n /** Read without spending; a late read fails `Expired`. */\n readonly get: (id: string) => Fx<Continuation>;\n /**\n * The flow's owner, actor, provider, and expiry, without a principal and without spending the\n * row. The one read in this interface that is not tenant-scoped, because `domainkit/server`\n * has to learn whose flow a provider callback finishes before it can resolve the principal to\n * scope by; the callback carries a continuation id and a session cookie and nothing else.\n *\n * A missing row fails `NotFound` and a stale one fails `Expired`, exactly as `get` does, so an\n * expired id never reaches a host. `payload` is out of reach here by construction.\n */\n readonly header: (id: string) => Effect.Effect<ContinuationHeader, Errors.DomainKitError>;\n /** Exactly-once: the second consume of the same id fails `NotFound`; a late one fails `Expired`. */\n readonly consume: (id: string) => Fx<Continuation>;\n };\n readonly attempts: {\n readonly create: (attempt: Attempt) => Fx<Attempt>;\n readonly get: (id: Plan.PlanId) => Fx<Attempt>;\n readonly byApproval: (id: Approval.ApprovalId) => Fx<Attempt>;\n readonly byReceipt: (id: Receipt.ReceiptId) => Fx<Attempt>;\n readonly latest: (attachmentId: string, kind: Plan.Kind) => Fx<Option.Option<Attempt>>;\n /** `planned` -> `approved`; approving again with the same approval id is a no-op, anything else fails `Stale`. */\n readonly approve: (id: Plan.PlanId, approval: Approval.Model) => Fx<Attempt>;\n /**\n * `planned` -> `rejected` (terminal). Rejecting again returns the row unchanged; any other\n * status fails `Stale`, `expired` fails `Expired`, and a digest mismatch fails `Stale`.\n */\n readonly reject: (\n id: Plan.PlanId,\n input: {\n readonly digest: Plan.Digest;\n readonly actorId: string;\n readonly reason: string | null;\n },\n ) => Fx<Attempt>;\n /**\n * Atomic transition to `applying` with a lease from `approved`, `failed`, or an expired\n * `applying`; fails `Busy` while a lease is live and `Stale` from any other status.\n */\n readonly claim: (id: Plan.PlanId, lease: DateTime.Utc) => Fx<Attempt>;\n readonly complete: (id: Plan.PlanId, receipt: Receipt.Model) => Fx<Attempt>;\n readonly fail: (id: Plan.PlanId, message: string) => Fx<Attempt>;\n };\n /**\n * The batch aggregate: many attempts planned together and approved once.\n *\n * Every transition runs in one transaction over the locked batch row and leaves `status`\n * recomputed from the items' attempts, so the owner-scoped unfinished index never disagrees\n * with the attempts it summarizes.\n */\n readonly batches: {\n /**\n * Insert the batch and one item per attachment, or return the batch this owner already\n * created under `idempotencyKey`. A replay ignores `attachmentIds` and returns what is\n * stored, which is what makes a retried create safe.\n */\n readonly create: (input: {\n readonly kind: Plan.Kind;\n readonly idempotencyKey: string;\n /** One item per attachment, in order; empty or duplicated fails `InvalidInput`. */\n readonly attachmentIds: ReadonlyArray<string>;\n }) => Fx<BatchAggregate>;\n readonly get: (id: BatchId) => Fx<BatchAggregate>;\n /**\n * The batch this owner created under `idempotencyKey`, if any.\n *\n * What a caller reads before it validates a create's payload, so a retried request answers\n * with the batch the first one made whatever the retry now says.\n */\n readonly byIdempotencyKey: (key: string) => Fx<Option.Option<BatchAggregate>>;\n /** Every batch that has not reached `complete` or `rejected`, most recently touched first. */\n readonly listUnfinished: () => Fx<ReadonlyArray<BatchAggregate>>;\n /**\n * Point an item at the attempt that now carries its plan and clear its plan failure.\n *\n * Fails `Stale` unless the batch is still `planning` or `planned`. That check is the fence a\n * planner still in flight hits when the principal declines the batch underneath it, so plan\n * state never lands on a rejected aggregate.\n */\n readonly recordItemPlan: (\n id: BatchId,\n attachmentId: string,\n attemptId: Plan.PlanId,\n ) => Fx<BatchAggregate>;\n /** Record why an item could not be planned; the batch stays `planning`. Same fence. */\n readonly recordItemPlanFailure: (\n id: BatchId,\n attachmentId: string,\n message: string,\n ) => Fx<BatchAggregate>;\n /**\n * Bind the batch to `digest` and approve every item's attempt in the same transaction, so a\n * batch is never approved without the per-attempt approvals `attempts.apply` needs.\n *\n * Fails `Stale` unless the batch is `planned` and every item still points at the attempt its\n * approval names. Approving an approved batch under the same digest returns it unchanged; a\n * different digest fails `Stale`.\n */\n readonly approve: (\n id: BatchId,\n input: {\n readonly digest: Plan.Digest;\n readonly actorId: string;\n /** One per item, covering every item in the batch. */\n readonly approvals: ReadonlyArray<BatchItemApproval>;\n },\n ) => Fx<BatchAggregate>;\n /**\n * Decline the batch and every planned attempt under it, in one transaction. Terminal.\n *\n * Rejecting again returns the batch unchanged; a batch that reached `approved` or later\n * fails `Stale`.\n */\n readonly reject: (\n id: BatchId,\n input: { readonly actorId: string; readonly reason: string | null },\n ) => Fx<BatchAggregate>;\n /** Recompute the stored status from the items' attempts, after applying moved them. */\n readonly refresh: (id: BatchId) => Fx<BatchAggregate>;\n };\n readonly readiness: {\n /** One row per (owner, domain); `attachmentId`, when set, must exist for the owner. */\n readonly put: (readiness: Readiness) => Fx<void>;\n readonly get: (domain: string) => Fx<Option.Option<Readiness>>;\n };\n /** Single-flight guard keyed by string (credential refresh, apply). Fails `Busy` rather than waiting. */\n readonly withLock: <A, E, R>(\n key: string,\n effect: Effect.Effect<A, E, R>,\n ) => Effect.Effect<A, E | Errors.DomainKitError, R | Principal.Service>;\n}\n\nexport class Service extends Context.Service<Service, Interface>()(\"@domainkit/Storage\") {}\n\nexport interface MemoryOptions {\n /** Fault injection: runs before every mutation commits; failing here leaves state untouched. */\n readonly beforeCommit?: (operation: string) => Effect.Effect<void, Errors.DomainKitError>;\n}\n\n/** In-memory implementation for tests and local development. Not for production. */\nexport const layerMemory: Layer.Layer<Service> = Layer.sync(Service)(() => makeMemory());\n\n/** `layerMemory` with fault injection for storage-failure tests. */\nexport const layerMemoryWith = (options: MemoryOptions): Layer.Layer<Service> =>\n Layer.sync(Service)(() => makeMemory(options));\n\nexport { makeMemory };\n\n// ---------------------------------------------------------------------------------------------\n// Async adapter\n// ---------------------------------------------------------------------------------------------\n\n/**\n * A Promise-shaped implementation for hosts that persist without Effect. Every method takes the\n * principal explicitly. Rejections become `StorageFailed`; reject with a `DomainKit.Error` to keep\n * a typed reason. Two-phase revocation and locking are split into plain steps so no callback has\n * to run an Effect.\n */\nexport interface AsyncInterface {\n readonly authorizations: {\n readonly upsert: (\n principal: Principal.Interface,\n input: {\n readonly authorization: Authorization;\n readonly credential: Credential;\n readonly expectedId?: string;\n },\n ) => Promise<Authorization>;\n readonly get: (principal: Principal.Interface, id: string) => Promise<Authorization>;\n readonly credential: (principal: Principal.Interface, id: string) => Promise<Credential>;\n readonly rotate: (\n principal: Principal.Interface,\n id: string,\n credential: Credential,\n ) => Promise<void>;\n readonly promoteCapabilities: (\n principal: Principal.Interface,\n id: string,\n capabilities: ReadonlyArray<Capability>,\n ) => Promise<void>;\n /** Mark `pending` durably; must be idempotent. */\n readonly prepareRevocation: (\n principal: Principal.Interface,\n id: string,\n ) => Promise<Authorization>;\n /** Delete the authorization after the provider confirmed revocation. */\n readonly completeRevocation: (principal: Principal.Interface, id: string) => Promise<void>;\n readonly pendingRevocations: (\n principal: Principal.Interface,\n ) => Promise<ReadonlyArray<Authorization>>;\n };\n readonly connections: {\n readonly create: (\n principal: Principal.Interface,\n authorizationId: string,\n ) => Promise<Connection>;\n readonly get: (principal: Principal.Interface, id: string) => Promise<Connection>;\n readonly list: (\n principal: Principal.Interface,\n filter?: { readonly provider?: string },\n ) => Promise<ReadonlyArray<Connection>>;\n readonly remove: (principal: Principal.Interface, id: string) => Promise<void>;\n };\n readonly attachments: {\n readonly create: (\n principal: Principal.Interface,\n input: {\n readonly connectionId: string;\n readonly domain: string;\n readonly zone: string;\n readonly label: string;\n readonly target: unknown;\n },\n ) => Promise<Attachment>;\n readonly get: (principal: Principal.Interface, id: string) => Promise<Attachment>;\n readonly byDomain: (\n principal: Principal.Interface,\n domain: string,\n ) => Promise<Attachment | null>;\n readonly list: (\n principal: Principal.Interface,\n connectionId: string,\n ) => Promise<ReadonlyArray<Attachment>>;\n readonly remove: (principal: Principal.Interface, id: string) => Promise<void>;\n };\n readonly continuations: {\n readonly put: (principal: Principal.Interface, continuation: Continuation) => Promise<void>;\n readonly get: (principal: Principal.Interface, id: string) => Promise<Continuation>;\n /** Takes no principal: see `Interface.continuations.header`. Never returns `payload`. */\n readonly header: (id: string) => Promise<ContinuationHeader>;\n readonly consume: (principal: Principal.Interface, id: string) => Promise<Continuation>;\n };\n readonly attempts: {\n readonly create: (principal: Principal.Interface, attempt: Attempt) => Promise<Attempt>;\n readonly get: (principal: Principal.Interface, id: Plan.PlanId) => Promise<Attempt>;\n readonly byApproval: (\n principal: Principal.Interface,\n id: Approval.ApprovalId,\n ) => Promise<Attempt>;\n readonly byReceipt: (principal: Principal.Interface, id: Receipt.ReceiptId) => Promise<Attempt>;\n readonly latest: (\n principal: Principal.Interface,\n attachmentId: string,\n kind: Plan.Kind,\n ) => Promise<Attempt | null>;\n readonly approve: (\n principal: Principal.Interface,\n id: Plan.PlanId,\n approval: Approval.Model,\n ) => Promise<Attempt>;\n readonly reject: (\n principal: Principal.Interface,\n id: Plan.PlanId,\n input: {\n readonly digest: Plan.Digest;\n readonly actorId: string;\n readonly reason: string | null;\n },\n ) => Promise<Attempt>;\n readonly claim: (\n principal: Principal.Interface,\n id: Plan.PlanId,\n lease: DateTime.Utc,\n ) => Promise<Attempt>;\n readonly complete: (\n principal: Principal.Interface,\n id: Plan.PlanId,\n receipt: Receipt.Model,\n ) => Promise<Attempt>;\n readonly fail: (\n principal: Principal.Interface,\n id: Plan.PlanId,\n message: string,\n ) => Promise<Attempt>;\n };\n readonly batches: {\n readonly create: (\n principal: Principal.Interface,\n input: {\n readonly kind: Plan.Kind;\n readonly idempotencyKey: string;\n readonly attachmentIds: ReadonlyArray<string>;\n },\n ) => Promise<BatchAggregate>;\n readonly get: (principal: Principal.Interface, id: BatchId) => Promise<BatchAggregate>;\n readonly byIdempotencyKey: (\n principal: Principal.Interface,\n key: string,\n ) => Promise<BatchAggregate | null>;\n readonly listUnfinished: (\n principal: Principal.Interface,\n ) => Promise<ReadonlyArray<BatchAggregate>>;\n readonly recordItemPlan: (\n principal: Principal.Interface,\n id: BatchId,\n attachmentId: string,\n attemptId: Plan.PlanId,\n ) => Promise<BatchAggregate>;\n readonly recordItemPlanFailure: (\n principal: Principal.Interface,\n id: BatchId,\n attachmentId: string,\n message: string,\n ) => Promise<BatchAggregate>;\n readonly approve: (\n principal: Principal.Interface,\n id: BatchId,\n input: {\n readonly digest: Plan.Digest;\n readonly actorId: string;\n readonly approvals: ReadonlyArray<BatchItemApproval>;\n },\n ) => Promise<BatchAggregate>;\n readonly reject: (\n principal: Principal.Interface,\n id: BatchId,\n input: { readonly actorId: string; readonly reason: string | null },\n ) => Promise<BatchAggregate>;\n readonly refresh: (principal: Principal.Interface, id: BatchId) => Promise<BatchAggregate>;\n };\n readonly readiness: {\n readonly put: (principal: Principal.Interface, readiness: Readiness) => Promise<void>;\n readonly get: (principal: Principal.Interface, domain: string) => Promise<Readiness | null>;\n };\n /** Return `false` when another holder has the key. */\n readonly acquireLock: (principal: Principal.Interface, key: string) => Promise<boolean>;\n readonly releaseLock: (principal: Principal.Interface, key: string) => Promise<void>;\n}\n\nconst storageFailed = (operation: string) => (cause: unknown) =>\n Errors.isDomainKitError(cause)\n ? cause\n : new Errors.DomainKitError({\n reason: new Reason.StorageFailed({\n operation,\n message: cause instanceof Error ? cause.message : String(cause),\n }),\n });\n\n/** Wraps a Promise-shaped implementation. Rejections become `StorageFailed`. */\nexport const fromAsync = (service: AsyncInterface): Interface => {\n const call = <A>(operation: string, run: (principal: Principal.Interface) => Promise<A>): Fx<A> =>\n Effect.flatMap(Principal.Service, (principal) =>\n Effect.tryPromise({ try: () => run(principal), catch: storageFailed(operation) }),\n );\n const option = <A>(\n operation: string,\n run: (principal: Principal.Interface) => Promise<A | null>,\n ) => call(operation, run).pipe(Effect.map(Option.fromNullishOr));\n return {\n authorizations: {\n upsert: (input) =>\n call(\"authorizations.upsert\", (p) => service.authorizations.upsert(p, input)),\n get: (id) => call(\"authorizations.get\", (p) => service.authorizations.get(p, id)),\n credential: (id) =>\n call(\"authorizations.credential\", (p) => service.authorizations.credential(p, id)),\n rotate: (id, credential) =>\n call(\"authorizations.rotate\", (p) => service.authorizations.rotate(p, id, credential)),\n promoteCapabilities: (id, capabilities) =>\n call(\"authorizations.promoteCapabilities\", (p) =>\n service.authorizations.promoteCapabilities(p, id, capabilities),\n ),\n revoke: (id, revoke) =>\n call(\"authorizations.prepareRevocation\", (p) =>\n service.authorizations.prepareRevocation(p, id),\n ).pipe(\n Effect.flatMap(() => revoke),\n Effect.flatMap(() =>\n call(\"authorizations.completeRevocation\", (p) =>\n service.authorizations.completeRevocation(p, id),\n ),\n ),\n ),\n recoverRevocations: (revoke) =>\n call(\"authorizations.pendingRevocations\", (p) =>\n service.authorizations.pendingRevocations(p),\n ).pipe(\n Effect.flatMap((pending) =>\n Effect.forEach(pending, (authorization) =>\n revoke(authorization).pipe(\n Effect.flatMap(() =>\n call(\"authorizations.completeRevocation\", (p) =>\n service.authorizations.completeRevocation(p, authorization.id),\n ),\n ),\n ),\n ),\n ),\n Effect.map((completed) => completed.length),\n ),\n },\n connections: {\n create: (authorizationId) =>\n call(\"connections.create\", (p) => service.connections.create(p, authorizationId)),\n get: (id) => call(\"connections.get\", (p) => service.connections.get(p, id)),\n list: (filter) => call(\"connections.list\", (p) => service.connections.list(p, filter)),\n remove: (id) => call(\"connections.remove\", (p) => service.connections.remove(p, id)),\n },\n attachments: {\n create: (input) => call(\"attachments.create\", (p) => service.attachments.create(p, input)),\n get: (id) => call(\"attachments.get\", (p) => service.attachments.get(p, id)),\n byDomain: (domain) =>\n option(\"attachments.byDomain\", (p) => service.attachments.byDomain(p, domain)),\n list: (connectionId) =>\n call(\"attachments.list\", (p) => service.attachments.list(p, connectionId)),\n remove: (id) => call(\"attachments.remove\", (p) => service.attachments.remove(p, id)),\n },\n continuations: {\n put: (continuation) =>\n call(\"continuations.put\", (p) => service.continuations.put(p, continuation)),\n get: (id) => call(\"continuations.get\", (p) => service.continuations.get(p, id)),\n header: (id) =>\n Effect.tryPromise({\n try: () => service.continuations.header(id),\n catch: storageFailed(\"continuations.header\"),\n }),\n consume: (id) => call(\"continuations.consume\", (p) => service.continuations.consume(p, id)),\n },\n attempts: {\n create: (attempt) => call(\"attempts.create\", (p) => service.attempts.create(p, attempt)),\n get: (id) => call(\"attempts.get\", (p) => service.attempts.get(p, id)),\n byApproval: (id) => call(\"attempts.byApproval\", (p) => service.attempts.byApproval(p, id)),\n byReceipt: (id) => call(\"attempts.byReceipt\", (p) => service.attempts.byReceipt(p, id)),\n latest: (attachmentId, kind) =>\n option(\"attempts.latest\", (p) => service.attempts.latest(p, attachmentId, kind)),\n approve: (id, approval) =>\n call(\"attempts.approve\", (p) => service.attempts.approve(p, id, approval)),\n reject: (id, input) => call(\"attempts.reject\", (p) => service.attempts.reject(p, id, input)),\n claim: (id, lease) => call(\"attempts.claim\", (p) => service.attempts.claim(p, id, lease)),\n complete: (id, receipt) =>\n call(\"attempts.complete\", (p) => service.attempts.complete(p, id, receipt)),\n fail: (id, message) => call(\"attempts.fail\", (p) => service.attempts.fail(p, id, message)),\n },\n batches: {\n create: (input) => call(\"batches.create\", (p) => service.batches.create(p, input)),\n get: (id) => call(\"batches.get\", (p) => service.batches.get(p, id)),\n byIdempotencyKey: (key) =>\n option(\"batches.byIdempotencyKey\", (p) => service.batches.byIdempotencyKey(p, key)),\n listUnfinished: () =>\n call(\"batches.listUnfinished\", (p) => service.batches.listUnfinished(p)),\n recordItemPlan: (id, attachmentId, attemptId) =>\n call(\"batches.recordItemPlan\", (p) =>\n service.batches.recordItemPlan(p, id, attachmentId, attemptId),\n ),\n recordItemPlanFailure: (id, attachmentId, message) =>\n call(\"batches.recordItemPlanFailure\", (p) =>\n service.batches.recordItemPlanFailure(p, id, attachmentId, message),\n ),\n approve: (id, input) => call(\"batches.approve\", (p) => service.batches.approve(p, id, input)),\n reject: (id, input) => call(\"batches.reject\", (p) => service.batches.reject(p, id, input)),\n refresh: (id) => call(\"batches.refresh\", (p) => service.batches.refresh(p, id)),\n },\n readiness: {\n put: (readiness) => call(\"readiness.put\", (p) => service.readiness.put(p, readiness)),\n get: (domain) => option(\"readiness.get\", (p) => service.readiness.get(p, domain)),\n },\n withLock: (key, effect) =>\n Effect.acquireRelease(\n call(\"acquireLock\", (p) => service.acquireLock(p, key)).pipe(\n Effect.flatMap((acquired) =>\n acquired ? Effect.void : Errors.fail(new Reason.Busy({ key })),\n ),\n ),\n () => call(\"releaseLock\", (p) => service.releaseLock(p, key)).pipe(Effect.ignore),\n ).pipe(\n Effect.flatMap(() => effect),\n Effect.scoped,\n ),\n };\n};\n\nexport const layerFromAsync = (service: AsyncInterface): Layer.Layer<Service> =>\n Layer.succeed(Service)(fromAsync(service));\n"],"mappings":"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"}
1
+ {"version":3,"file":"Storage.mjs","names":["Plan.PlanId","Plan.Kind","Plan.Model","Approval.Model","Receipt.Model","Receipt.ReceiptId","Plan.Digest","Plan.OperationId","DnsRecord.Model","Errors.isDomainKitError","Errors.DomainKitError","Reason.StorageFailed","Principal.Service","Errors.fail","Reason.Busy"],"sources":["../src/Storage.ts"],"sourcesContent":["/**\n * Everything DomainKit persists, behind one service the host installs once. Every method requires\n * `Principal` and is scoped by it. `@domainkit/capsuledb` ships the Postgres implementation;\n * `domainkit/testing` ships the memory one; both pass `Testing.conformance.storage`.\n *\n * Storage never sees plaintext credentials: `Connect` seals secrets through `Custody` before\n * writing a `Credential` row and opens them after reading one.\n *\n * Grouped by noun so the 25-odd methods stay navigable. Implementations must satisfy the\n * invariants documented per group; the conformance suite checks them.\n */\nimport { Context, DateTime, Effect, Layer, Option, Schema } from \"effect\";\n\nimport * as Approval from \"./Approval.ts\";\nimport * as DnsRecord from \"./DnsRecord.ts\";\nimport * as Errors from \"./internal/error.ts\";\nimport * as Reason from \"./Reason.ts\";\nimport { makeMemory } from \"./internal/storage-memory.ts\";\nimport * as Plan from \"./Plan.ts\";\nimport * as Principal from \"./Principal.ts\";\nimport * as Receipt from \"./Receipt.ts\";\n\nexport type Fx<A> = Effect.Effect<A, Errors.DomainKitError, Principal.Service>;\n\n// ---------------------------------------------------------------------------------------------\n// Rows\n// ---------------------------------------------------------------------------------------------\n\nexport const Capability = Schema.Literals([\"dns:read\", \"dns:write\"]);\nexport type Capability = typeof Capability.Type;\n\nexport const AuthMethod = Schema.Literals([\"token\", \"oauth\", \"integration\"]);\nexport type AuthMethod = typeof AuthMethod.Type;\n\n/** Sealed provider credential plus the metadata needed without unsealing it. */\nexport class Credential extends Schema.Class<Credential>(\"@domainkit/Storage/Credential\")({\n ciphertext: Schema.String,\n expiresAt: Schema.NullOr(Schema.DateTimeUtcFromString),\n rotatedAt: Schema.DateTimeUtcFromString,\n}) {}\n\n/** One provider authorization (an OAuth grant or a token) owned by a principal. */\nexport class Authorization extends Schema.Class<Authorization>(\"@domainkit/Storage/Authorization\")({\n id: Schema.String,\n ownerId: Schema.String,\n provider: Schema.String,\n method: AuthMethod,\n capabilities: Schema.Array(Capability),\n /** Provider-specific account context (account id, team id, ...) decoded by the provider's `context` schema. */\n context: Schema.Unknown,\n /**\n * What the provider called the account when the credential was issued, so a UI names the\n * connection without a provider call. `null` when the provider names no single account.\n */\n label: Schema.NullOr(Schema.String),\n revocation: Schema.Literals([\"active\", \"pending\", \"revoked\"]),\n createdBy: Schema.String,\n createdAt: Schema.DateTimeUtcFromString,\n}) {}\n\n/** A connection is the principal-facing handle over an authorization; one authorization, many domains. */\nexport class Connection extends Schema.Class<Connection>(\"@domainkit/Storage/Connection\")({\n id: Schema.String,\n ownerId: Schema.String,\n authorizationId: Schema.String,\n createdAt: Schema.DateTimeUtcFromString,\n}) {}\n\nexport class Attachment extends Schema.Class<Attachment>(\"@domainkit/Storage/Attachment\")({\n id: Schema.String,\n ownerId: Schema.String,\n connectionId: Schema.String,\n domain: Schema.String,\n zone: Schema.String,\n /** The target's label as the provider gave it at attach time, so a UI names the account without a provider call. */\n label: Schema.String,\n /** Provider zone identity (zone id, account id) decoded by the provider's `context` schema. */\n target: Schema.Unknown,\n createdAt: Schema.DateTimeUtcFromString,\n}) {}\n\n/** Interactive-flow state between `Connect.start` and `Connect.complete`. */\nexport class Continuation extends Schema.Class<Continuation>(\"@domainkit/Storage/Continuation\")({\n id: Schema.String,\n ownerId: Schema.String,\n actorId: Schema.String,\n provider: Schema.String,\n payload: Schema.Unknown,\n returnTo: Schema.NullOr(Schema.String),\n expiresAt: Schema.DateTimeUtcFromString,\n}) {}\n\n/**\n * Who a continuation belongs to, without the continuation itself. `payload` has no place here: it\n * carries the PKCE `codeVerifier`, which only the owner-scoped read may reach.\n */\nexport class ContinuationHeader extends Schema.Class<ContinuationHeader>(\n \"@domainkit/Storage/ContinuationHeader\",\n)({\n ownerId: Schema.String,\n actorId: Schema.String,\n provider: Schema.String,\n expiresAt: Schema.DateTimeUtcFromString,\n}) {}\n\nexport const AttemptStatus = Schema.Literals([\n \"planned\",\n \"approved\",\n \"applying\",\n \"complete\",\n \"partial\",\n \"failed\",\n \"expired\",\n \"rejected\",\n]);\nexport type AttemptStatus = typeof AttemptStatus.Type;\n\n/** Who declined the plan, why, and when. Terminal: the domain needs a new plan. */\nexport const Rejection = Schema.Struct({\n actorId: Schema.String,\n reason: Schema.NullOr(Schema.String),\n at: Schema.DateTimeUtcFromString,\n});\nexport type Rejection = typeof Rejection.Type;\n\n/** One durable plan -> approval -> receipt lifecycle. */\nexport class Attempt extends Schema.Class<Attempt>(\"@domainkit/Storage/Attempt\")({\n id: Plan.PlanId,\n ownerId: Schema.String,\n attachmentId: Schema.String,\n kind: Plan.Kind,\n status: AttemptStatus,\n plan: Plan.Model,\n approval: Schema.NullOr(Approval.Model),\n receipt: Schema.NullOr(Receipt.Model),\n rejection: Schema.NullOr(Rejection),\n /** Cleanup attempts point at the provisioning receipt they undo. */\n sourceReceiptId: Schema.NullOr(Receipt.ReceiptId),\n leaseExpiresAt: Schema.NullOr(Schema.DateTimeUtcFromString),\n /** Why the last apply stopped before any write; cleared on the next claim. */\n failure: Schema.NullOr(Schema.String),\n updatedAt: Schema.DateTimeUtcFromString,\n}) {}\n\nexport const BatchId = Schema.String.pipe(Schema.brand(\"@domainkit/BatchId\"));\nexport type BatchId = typeof BatchId.Type;\n\n/**\n * Where a batch stands, recomputed from its items' attempts after every transition.\n *\n * `planning` while any item is still without a plan, `planned` once every item carries one,\n * `approved` once the principal bound the batch digest, then the apply states. `partial` and\n * `failed` are resumable; `complete` and `rejected` are terminal and leave the unfinished index.\n */\nexport const BatchStatus = Schema.Literals([\n \"planning\",\n \"planned\",\n \"approved\",\n \"applying\",\n \"complete\",\n \"partial\",\n \"failed\",\n \"rejected\",\n]);\nexport type BatchStatus = typeof BatchStatus.Type;\n\n/** Who bound the batch to its digest, and when. The digest itself is `Batch.digest`. */\nexport const BatchApproval = Schema.Struct({\n actorId: Schema.String,\n at: Schema.DateTimeUtcFromString,\n});\nexport type BatchApproval = typeof BatchApproval.Type;\n\n/**\n * Many domains planned together, approved once, and applied with bounded concurrency.\n *\n * The batch owns the aggregate's own state only: its digest, who approved or declined it, and the\n * status recomputed from its items. Every plan, approval, receipt, lease, and failure stays on the\n * attempt an item points at, so nothing is stored twice.\n */\nexport class Batch extends Schema.Class<Batch>(\"@domainkit/Storage/Batch\")({\n id: BatchId,\n ownerId: Schema.String,\n kind: Plan.Kind,\n status: BatchStatus,\n /** SHA-256 over the items' `attachmentId:planDigest` pairs, bound at approval. */\n digest: Schema.NullOr(Plan.Digest),\n approval: Schema.NullOr(BatchApproval),\n rejection: Schema.NullOr(Rejection),\n /** Unique per owner: a replayed create returns the batch this key already made. */\n idempotencyKey: Schema.String,\n createdBy: Schema.String,\n createdAt: Schema.DateTimeUtcFromString,\n updatedAt: Schema.DateTimeUtcFromString,\n /** When the batch reached `complete` or `rejected`; null while it still owes a move. */\n completedAt: Schema.NullOr(Schema.DateTimeUtcFromString),\n}) {}\n\n/**\n * One domain's place in a batch: a pointer to the attachment and to the attempt carrying its plan.\n *\n * `attemptId` is null until the item is planned, and `planFailure` carries why the last planning\n * pass stopped, cleared the moment a plan lands.\n */\nexport class BatchItem extends Schema.Class<BatchItem>(\"@domainkit/Storage/BatchItem\")({\n batchId: BatchId,\n attachmentId: Schema.String,\n /** The item's place in the batch, as `create` received it. */\n position: Schema.Number,\n attemptId: Schema.NullOr(Plan.PlanId),\n planFailure: Schema.NullOr(Schema.String),\n}) {}\n\n/**\n * The status a batch holds, given whether it was approved or declined and where each item's\n * attempt stands. `null` names an item that has no plan yet.\n *\n * Both `Storage` implementations recompute the stored status through this after every transition,\n * so the owner-scoped unfinished index never disagrees with the attempts it summarizes.\n */\nexport const batchStatusOf = (input: {\n readonly approved: boolean;\n readonly rejected: boolean;\n readonly items: ReadonlyArray<AttemptStatus | null>;\n}): BatchStatus => {\n if (input.rejected) return \"rejected\";\n const items = input.items;\n if (items.length === 0 || items.some((status) => status === null)) return \"planning\";\n const statuses = items as ReadonlyArray<AttemptStatus>;\n const every = (status: AttemptStatus) => statuses.every((item) => item === status);\n if (every(\"planned\")) return \"planned\";\n // An approval the batch never took means an item moved on its own; it is not approvable, and\n // planning is the state a host resumes from.\n if (!input.approved) return \"planning\";\n if (every(\"approved\")) return \"approved\";\n const settled = statuses.every(\n (status) => status !== \"planned\" && status !== \"approved\" && status !== \"applying\",\n );\n if (!settled) return \"applying\";\n if (every(\"complete\")) return \"complete\";\n return statuses.some((status) => status === \"partial\") ? \"partial\" : \"failed\";\n};\n\n/** A batch with its items, in `position` order. Every batch read returns one. */\nexport interface BatchAggregate {\n readonly batch: Batch;\n readonly items: ReadonlyArray<BatchItem>;\n}\n\n/** One item's attempt approval, as `batches.approve` writes it beside the batch's own. */\nexport interface BatchItemApproval {\n readonly attachmentId: string;\n readonly approval: Approval.Model;\n}\n\nexport const RequirementStatus = Schema.Literals([\"satisfied\", \"missing\", \"mismatch\", \"unknown\"]);\nexport type RequirementStatus = typeof RequirementStatus.Type;\n\nexport const Overall = Schema.Literals([\"ready\", \"pending\", \"failed\"]);\nexport type Overall = typeof Overall.Type;\n\n/**\n * Latest observed readiness for one domain, written by `Verify.observe`. Keyed by domain so\n * observe-only hosts (no attachment, public DNS alone) get the same row; `attachmentId` links the\n * attachment when one exists.\n */\nexport class Readiness extends Schema.Class<Readiness>(\"@domainkit/Storage/Readiness\")({\n domain: Schema.String,\n attachmentId: Schema.NullOr(Schema.String),\n ownerId: Schema.String,\n overall: Overall,\n requirements: Schema.Array(\n Schema.Struct({\n operationId: Schema.NullOr(Plan.OperationId),\n record: DnsRecord.Model,\n status: RequirementStatus,\n /** Encoded `Verify.Evidence` values. */\n evidence: Schema.Array(Schema.Unknown),\n }),\n ),\n /** Encoded `Verify.HostEvidence` values, keyed by `source` on merge. */\n host: Schema.Array(Schema.Unknown),\n /** When the current pending streak began; drives the backoff ladder. */\n pendingSince: Schema.NullOr(Schema.DateTimeUtcFromString),\n checkedAt: Schema.DateTimeUtcFromString,\n nextCheckAt: Schema.NullOr(Schema.DateTimeUtcFromString),\n}) {}\n\n// ---------------------------------------------------------------------------------------------\n// Service\n// ---------------------------------------------------------------------------------------------\n\nexport interface Interface {\n readonly authorizations: {\n /**\n * Insert, or CAS-replace when `expectedId` is set: the row must exist under this owner with an\n * active revocation state, else `NotFound` / `Busy`.\n */\n readonly upsert: (input: {\n readonly authorization: Authorization;\n readonly credential: Credential;\n readonly expectedId?: string;\n }) => Fx<Authorization>;\n readonly get: (id: string) => Fx<Authorization>;\n readonly credential: (id: string) => Fx<Credential>;\n readonly rotate: (id: string, credential: Credential) => Fx<void>;\n readonly promoteCapabilities: (id: string, capabilities: ReadonlyArray<Capability>) => Fx<void>;\n /**\n * Two-phase: mark `pending`, run `revoke` outside any transaction, then delete. A failed\n * `revoke` leaves the row pending; `recoverRevocations` finishes it later.\n */\n readonly revoke: <E, R>(\n id: string,\n revoke: Effect.Effect<void, E, R>,\n ) => Effect.Effect<void, Errors.DomainKitError | E, Principal.Service | R>;\n readonly recoverRevocations: <E, R>(\n revoke: (authorization: Authorization) => Effect.Effect<void, E, R>,\n ) => Effect.Effect<number, Errors.DomainKitError | E, Principal.Service | R>;\n };\n readonly connections: {\n readonly create: (authorizationId: string) => Fx<Connection>;\n readonly get: (id: string) => Fx<Connection>;\n readonly list: (filter?: { readonly provider?: string }) => Fx<ReadonlyArray<Connection>>;\n /** Fails `InvalidInput` while attachments still reference the connection. */\n readonly remove: (id: string) => Fx<void>;\n };\n readonly attachments: {\n /** One attachment per (owner, domain); a duplicate fails `InvalidInput`. */\n readonly create: (input: {\n readonly connectionId: string;\n readonly domain: string;\n readonly zone: string;\n readonly label: string;\n readonly target: unknown;\n }) => Fx<Attachment>;\n readonly get: (id: string) => Fx<Attachment>;\n readonly byDomain: (domain: string) => Fx<Option.Option<Attachment>>;\n readonly list: (connectionId: string) => Fx<ReadonlyArray<Attachment>>;\n readonly remove: (id: string) => Fx<void>;\n };\n readonly continuations: {\n readonly put: (continuation: Continuation) => Fx<void>;\n /** Read without spending; a late read fails `Expired`. */\n readonly get: (id: string) => Fx<Continuation>;\n /**\n * The flow's owner, actor, provider, and expiry, without a principal and without spending the\n * row. The one read in this interface that is not tenant-scoped, because `domainkit/server`\n * has to learn whose flow a provider callback finishes before it can resolve the principal to\n * scope by; the callback carries a continuation id and a session cookie and nothing else.\n *\n * A missing row fails `NotFound` and a stale one fails `Expired`, exactly as `get` does, so an\n * expired id never reaches a host. `payload` is out of reach here by construction.\n */\n readonly header: (id: string) => Effect.Effect<ContinuationHeader, Errors.DomainKitError>;\n /** Exactly-once: the second consume of the same id fails `NotFound`; a late one fails `Expired`. */\n readonly consume: (id: string) => Fx<Continuation>;\n };\n readonly attempts: {\n readonly create: (attempt: Attempt) => Fx<Attempt>;\n readonly get: (id: Plan.PlanId) => Fx<Attempt>;\n readonly byApproval: (id: Approval.ApprovalId) => Fx<Attempt>;\n readonly byReceipt: (id: Receipt.ReceiptId) => Fx<Attempt>;\n readonly latest: (attachmentId: string, kind: Plan.Kind) => Fx<Option.Option<Attempt>>;\n /** `planned` -> `approved`; approving again with the same approval id is a no-op, anything else fails `Stale`. */\n readonly approve: (id: Plan.PlanId, approval: Approval.Model) => Fx<Attempt>;\n /**\n * `planned` -> `rejected` (terminal). Rejecting again returns the row unchanged; any other\n * status fails `Stale`, `expired` fails `Expired`, and a digest mismatch fails `Stale`.\n */\n readonly reject: (\n id: Plan.PlanId,\n input: {\n readonly digest: Plan.Digest;\n readonly actorId: string;\n readonly reason: string | null;\n },\n ) => Fx<Attempt>;\n /**\n * Atomic transition to `applying` with a lease from `approved`, `failed`, or an expired\n * `applying`; fails `Busy` while a lease is live and `Stale` from any other status.\n */\n readonly claim: (id: Plan.PlanId, lease: DateTime.Utc) => Fx<Attempt>;\n readonly complete: (id: Plan.PlanId, receipt: Receipt.Model) => Fx<Attempt>;\n readonly fail: (id: Plan.PlanId, message: string) => Fx<Attempt>;\n };\n /**\n * The batch aggregate: many attempts planned together and approved once.\n *\n * Every transition runs in one transaction over the locked batch row and leaves `status`\n * recomputed from the items' attempts, so the owner-scoped unfinished index never disagrees\n * with the attempts it summarizes.\n */\n readonly batches: {\n /**\n * Insert the batch and one item per attachment, or return the batch this owner already\n * created under `idempotencyKey`. A replay ignores `attachmentIds` and returns what is\n * stored, which is what makes a retried create safe.\n */\n readonly create: (input: {\n readonly kind: Plan.Kind;\n readonly idempotencyKey: string;\n /** One item per attachment, in order; empty or duplicated fails `InvalidInput`. */\n readonly attachmentIds: ReadonlyArray<string>;\n }) => Fx<BatchAggregate>;\n readonly get: (id: BatchId) => Fx<BatchAggregate>;\n /**\n * The batch this owner created under `idempotencyKey`, if any.\n *\n * What a caller reads before it validates a create's payload, so a retried request answers\n * with the batch the first one made whatever the retry now says.\n */\n readonly byIdempotencyKey: (key: string) => Fx<Option.Option<BatchAggregate>>;\n /** Every batch that has not reached `complete` or `rejected`, most recently touched first. */\n readonly listUnfinished: () => Fx<ReadonlyArray<BatchAggregate>>;\n /**\n * Point an item at the attempt that now carries its plan and clear its plan failure.\n *\n * Fails `Stale` unless the batch is still `planning` or `planned`. That check is the fence a\n * planner still in flight hits when the principal declines the batch underneath it, so plan\n * state never lands on a rejected aggregate.\n */\n readonly recordItemPlan: (\n id: BatchId,\n attachmentId: string,\n attemptId: Plan.PlanId,\n ) => Fx<BatchAggregate>;\n /** Record why an item could not be planned; the batch stays `planning`. Same fence. */\n readonly recordItemPlanFailure: (\n id: BatchId,\n attachmentId: string,\n message: string,\n ) => Fx<BatchAggregate>;\n /**\n * Bind the batch to `digest` and approve every item's attempt in the same transaction, so a\n * batch is never approved without the per-attempt approvals `attempts.apply` needs.\n *\n * Fails `Stale` unless the batch is `planned` and every item still points at the attempt its\n * approval names. Approving an approved batch under the same digest returns it unchanged; a\n * different digest fails `Stale`.\n */\n readonly approve: (\n id: BatchId,\n input: {\n readonly digest: Plan.Digest;\n readonly actorId: string;\n /** One per item, covering every item in the batch. */\n readonly approvals: ReadonlyArray<BatchItemApproval>;\n },\n ) => Fx<BatchAggregate>;\n /**\n * Decline the batch and every planned attempt under it, in one transaction. Terminal.\n *\n * Rejecting again returns the batch unchanged; a batch that reached `approved` or later\n * fails `Stale`.\n */\n readonly reject: (\n id: BatchId,\n input: { readonly actorId: string; readonly reason: string | null },\n ) => Fx<BatchAggregate>;\n /** Recompute the stored status from the items' attempts, after applying moved them. */\n readonly refresh: (id: BatchId) => Fx<BatchAggregate>;\n };\n readonly readiness: {\n /** One row per (owner, domain); `attachmentId`, when set, must exist for the owner. */\n readonly put: (readiness: Readiness) => Fx<void>;\n readonly get: (domain: string) => Fx<Option.Option<Readiness>>;\n };\n /**\n * Single-flight guard keyed by string (credential refresh, OAuth completion). Fails `Busy` rather\n * than waiting. A transactional store may run `effect` in one transaction, so a caller must not\n * depend on the writes of a guarded effect that fails.\n */\n readonly withLock: <A, E, R>(\n key: string,\n effect: Effect.Effect<A, E, R>,\n ) => Effect.Effect<A, E | Errors.DomainKitError, R | Principal.Service>;\n}\n\nexport class Service extends Context.Service<Service, Interface>()(\"@domainkit/Storage\") {}\n\nexport interface MemoryOptions {\n /** Fault injection: runs before every mutation commits; failing here leaves state untouched. */\n readonly beforeCommit?: (operation: string) => Effect.Effect<void, Errors.DomainKitError>;\n}\n\n/** In-memory implementation for tests and local development. Not for production. */\nexport const layerMemory: Layer.Layer<Service> = Layer.sync(Service)(() => makeMemory());\n\n/** `layerMemory` with fault injection for storage-failure tests. */\nexport const layerMemoryWith = (options: MemoryOptions): Layer.Layer<Service> =>\n Layer.sync(Service)(() => makeMemory(options));\n\nexport { makeMemory };\n\n// ---------------------------------------------------------------------------------------------\n// Async adapter\n// ---------------------------------------------------------------------------------------------\n\n/**\n * A Promise-shaped implementation for hosts that persist without Effect. Every method takes the\n * principal explicitly. Rejections become `StorageFailed`; reject with a `DomainKit.Error` to keep\n * a typed reason. Two-phase revocation and locking are split into plain steps so no callback has\n * to run an Effect.\n */\nexport interface AsyncInterface {\n readonly authorizations: {\n readonly upsert: (\n principal: Principal.Interface,\n input: {\n readonly authorization: Authorization;\n readonly credential: Credential;\n readonly expectedId?: string;\n },\n ) => Promise<Authorization>;\n readonly get: (principal: Principal.Interface, id: string) => Promise<Authorization>;\n readonly credential: (principal: Principal.Interface, id: string) => Promise<Credential>;\n readonly rotate: (\n principal: Principal.Interface,\n id: string,\n credential: Credential,\n ) => Promise<void>;\n readonly promoteCapabilities: (\n principal: Principal.Interface,\n id: string,\n capabilities: ReadonlyArray<Capability>,\n ) => Promise<void>;\n /** Mark `pending` durably; must be idempotent. */\n readonly prepareRevocation: (\n principal: Principal.Interface,\n id: string,\n ) => Promise<Authorization>;\n /** Delete the authorization after the provider confirmed revocation. */\n readonly completeRevocation: (principal: Principal.Interface, id: string) => Promise<void>;\n readonly pendingRevocations: (\n principal: Principal.Interface,\n ) => Promise<ReadonlyArray<Authorization>>;\n };\n readonly connections: {\n readonly create: (\n principal: Principal.Interface,\n authorizationId: string,\n ) => Promise<Connection>;\n readonly get: (principal: Principal.Interface, id: string) => Promise<Connection>;\n readonly list: (\n principal: Principal.Interface,\n filter?: { readonly provider?: string },\n ) => Promise<ReadonlyArray<Connection>>;\n readonly remove: (principal: Principal.Interface, id: string) => Promise<void>;\n };\n readonly attachments: {\n readonly create: (\n principal: Principal.Interface,\n input: {\n readonly connectionId: string;\n readonly domain: string;\n readonly zone: string;\n readonly label: string;\n readonly target: unknown;\n },\n ) => Promise<Attachment>;\n readonly get: (principal: Principal.Interface, id: string) => Promise<Attachment>;\n readonly byDomain: (\n principal: Principal.Interface,\n domain: string,\n ) => Promise<Attachment | null>;\n readonly list: (\n principal: Principal.Interface,\n connectionId: string,\n ) => Promise<ReadonlyArray<Attachment>>;\n readonly remove: (principal: Principal.Interface, id: string) => Promise<void>;\n };\n readonly continuations: {\n readonly put: (principal: Principal.Interface, continuation: Continuation) => Promise<void>;\n readonly get: (principal: Principal.Interface, id: string) => Promise<Continuation>;\n /** Takes no principal: see `Interface.continuations.header`. Never returns `payload`. */\n readonly header: (id: string) => Promise<ContinuationHeader>;\n readonly consume: (principal: Principal.Interface, id: string) => Promise<Continuation>;\n };\n readonly attempts: {\n readonly create: (principal: Principal.Interface, attempt: Attempt) => Promise<Attempt>;\n readonly get: (principal: Principal.Interface, id: Plan.PlanId) => Promise<Attempt>;\n readonly byApproval: (\n principal: Principal.Interface,\n id: Approval.ApprovalId,\n ) => Promise<Attempt>;\n readonly byReceipt: (principal: Principal.Interface, id: Receipt.ReceiptId) => Promise<Attempt>;\n readonly latest: (\n principal: Principal.Interface,\n attachmentId: string,\n kind: Plan.Kind,\n ) => Promise<Attempt | null>;\n readonly approve: (\n principal: Principal.Interface,\n id: Plan.PlanId,\n approval: Approval.Model,\n ) => Promise<Attempt>;\n readonly reject: (\n principal: Principal.Interface,\n id: Plan.PlanId,\n input: {\n readonly digest: Plan.Digest;\n readonly actorId: string;\n readonly reason: string | null;\n },\n ) => Promise<Attempt>;\n readonly claim: (\n principal: Principal.Interface,\n id: Plan.PlanId,\n lease: DateTime.Utc,\n ) => Promise<Attempt>;\n readonly complete: (\n principal: Principal.Interface,\n id: Plan.PlanId,\n receipt: Receipt.Model,\n ) => Promise<Attempt>;\n readonly fail: (\n principal: Principal.Interface,\n id: Plan.PlanId,\n message: string,\n ) => Promise<Attempt>;\n };\n readonly batches: {\n readonly create: (\n principal: Principal.Interface,\n input: {\n readonly kind: Plan.Kind;\n readonly idempotencyKey: string;\n readonly attachmentIds: ReadonlyArray<string>;\n },\n ) => Promise<BatchAggregate>;\n readonly get: (principal: Principal.Interface, id: BatchId) => Promise<BatchAggregate>;\n readonly byIdempotencyKey: (\n principal: Principal.Interface,\n key: string,\n ) => Promise<BatchAggregate | null>;\n readonly listUnfinished: (\n principal: Principal.Interface,\n ) => Promise<ReadonlyArray<BatchAggregate>>;\n readonly recordItemPlan: (\n principal: Principal.Interface,\n id: BatchId,\n attachmentId: string,\n attemptId: Plan.PlanId,\n ) => Promise<BatchAggregate>;\n readonly recordItemPlanFailure: (\n principal: Principal.Interface,\n id: BatchId,\n attachmentId: string,\n message: string,\n ) => Promise<BatchAggregate>;\n readonly approve: (\n principal: Principal.Interface,\n id: BatchId,\n input: {\n readonly digest: Plan.Digest;\n readonly actorId: string;\n readonly approvals: ReadonlyArray<BatchItemApproval>;\n },\n ) => Promise<BatchAggregate>;\n readonly reject: (\n principal: Principal.Interface,\n id: BatchId,\n input: { readonly actorId: string; readonly reason: string | null },\n ) => Promise<BatchAggregate>;\n readonly refresh: (principal: Principal.Interface, id: BatchId) => Promise<BatchAggregate>;\n };\n readonly readiness: {\n readonly put: (principal: Principal.Interface, readiness: Readiness) => Promise<void>;\n readonly get: (principal: Principal.Interface, domain: string) => Promise<Readiness | null>;\n };\n /** Return `false` when another holder has the key. */\n readonly acquireLock: (principal: Principal.Interface, key: string) => Promise<boolean>;\n readonly releaseLock: (principal: Principal.Interface, key: string) => Promise<void>;\n}\n\nconst storageFailed = (operation: string) => (cause: unknown) =>\n Errors.isDomainKitError(cause)\n ? cause\n : new Errors.DomainKitError({\n reason: new Reason.StorageFailed({\n operation,\n message: cause instanceof Error ? cause.message : String(cause),\n }),\n });\n\n/** Wraps a Promise-shaped implementation. Rejections become `StorageFailed`. */\nexport const fromAsync = (service: AsyncInterface): Interface => {\n const call = <A>(operation: string, run: (principal: Principal.Interface) => Promise<A>): Fx<A> =>\n Effect.flatMap(Principal.Service, (principal) =>\n Effect.tryPromise({ try: () => run(principal), catch: storageFailed(operation) }),\n );\n const option = <A>(\n operation: string,\n run: (principal: Principal.Interface) => Promise<A | null>,\n ) => call(operation, run).pipe(Effect.map(Option.fromNullishOr));\n return {\n authorizations: {\n upsert: (input) =>\n call(\"authorizations.upsert\", (p) => service.authorizations.upsert(p, input)),\n get: (id) => call(\"authorizations.get\", (p) => service.authorizations.get(p, id)),\n credential: (id) =>\n call(\"authorizations.credential\", (p) => service.authorizations.credential(p, id)),\n rotate: (id, credential) =>\n call(\"authorizations.rotate\", (p) => service.authorizations.rotate(p, id, credential)),\n promoteCapabilities: (id, capabilities) =>\n call(\"authorizations.promoteCapabilities\", (p) =>\n service.authorizations.promoteCapabilities(p, id, capabilities),\n ),\n revoke: (id, revoke) =>\n call(\"authorizations.prepareRevocation\", (p) =>\n service.authorizations.prepareRevocation(p, id),\n ).pipe(\n Effect.flatMap(() => revoke),\n Effect.flatMap(() =>\n call(\"authorizations.completeRevocation\", (p) =>\n service.authorizations.completeRevocation(p, id),\n ),\n ),\n ),\n recoverRevocations: (revoke) =>\n call(\"authorizations.pendingRevocations\", (p) =>\n service.authorizations.pendingRevocations(p),\n ).pipe(\n Effect.flatMap((pending) =>\n Effect.forEach(pending, (authorization) =>\n revoke(authorization).pipe(\n Effect.flatMap(() =>\n call(\"authorizations.completeRevocation\", (p) =>\n service.authorizations.completeRevocation(p, authorization.id),\n ),\n ),\n ),\n ),\n ),\n Effect.map((completed) => completed.length),\n ),\n },\n connections: {\n create: (authorizationId) =>\n call(\"connections.create\", (p) => service.connections.create(p, authorizationId)),\n get: (id) => call(\"connections.get\", (p) => service.connections.get(p, id)),\n list: (filter) => call(\"connections.list\", (p) => service.connections.list(p, filter)),\n remove: (id) => call(\"connections.remove\", (p) => service.connections.remove(p, id)),\n },\n attachments: {\n create: (input) => call(\"attachments.create\", (p) => service.attachments.create(p, input)),\n get: (id) => call(\"attachments.get\", (p) => service.attachments.get(p, id)),\n byDomain: (domain) =>\n option(\"attachments.byDomain\", (p) => service.attachments.byDomain(p, domain)),\n list: (connectionId) =>\n call(\"attachments.list\", (p) => service.attachments.list(p, connectionId)),\n remove: (id) => call(\"attachments.remove\", (p) => service.attachments.remove(p, id)),\n },\n continuations: {\n put: (continuation) =>\n call(\"continuations.put\", (p) => service.continuations.put(p, continuation)),\n get: (id) => call(\"continuations.get\", (p) => service.continuations.get(p, id)),\n header: (id) =>\n Effect.tryPromise({\n try: () => service.continuations.header(id),\n catch: storageFailed(\"continuations.header\"),\n }),\n consume: (id) => call(\"continuations.consume\", (p) => service.continuations.consume(p, id)),\n },\n attempts: {\n create: (attempt) => call(\"attempts.create\", (p) => service.attempts.create(p, attempt)),\n get: (id) => call(\"attempts.get\", (p) => service.attempts.get(p, id)),\n byApproval: (id) => call(\"attempts.byApproval\", (p) => service.attempts.byApproval(p, id)),\n byReceipt: (id) => call(\"attempts.byReceipt\", (p) => service.attempts.byReceipt(p, id)),\n latest: (attachmentId, kind) =>\n option(\"attempts.latest\", (p) => service.attempts.latest(p, attachmentId, kind)),\n approve: (id, approval) =>\n call(\"attempts.approve\", (p) => service.attempts.approve(p, id, approval)),\n reject: (id, input) => call(\"attempts.reject\", (p) => service.attempts.reject(p, id, input)),\n claim: (id, lease) => call(\"attempts.claim\", (p) => service.attempts.claim(p, id, lease)),\n complete: (id, receipt) =>\n call(\"attempts.complete\", (p) => service.attempts.complete(p, id, receipt)),\n fail: (id, message) => call(\"attempts.fail\", (p) => service.attempts.fail(p, id, message)),\n },\n batches: {\n create: (input) => call(\"batches.create\", (p) => service.batches.create(p, input)),\n get: (id) => call(\"batches.get\", (p) => service.batches.get(p, id)),\n byIdempotencyKey: (key) =>\n option(\"batches.byIdempotencyKey\", (p) => service.batches.byIdempotencyKey(p, key)),\n listUnfinished: () =>\n call(\"batches.listUnfinished\", (p) => service.batches.listUnfinished(p)),\n recordItemPlan: (id, attachmentId, attemptId) =>\n call(\"batches.recordItemPlan\", (p) =>\n service.batches.recordItemPlan(p, id, attachmentId, attemptId),\n ),\n recordItemPlanFailure: (id, attachmentId, message) =>\n call(\"batches.recordItemPlanFailure\", (p) =>\n service.batches.recordItemPlanFailure(p, id, attachmentId, message),\n ),\n approve: (id, input) => call(\"batches.approve\", (p) => service.batches.approve(p, id, input)),\n reject: (id, input) => call(\"batches.reject\", (p) => service.batches.reject(p, id, input)),\n refresh: (id) => call(\"batches.refresh\", (p) => service.batches.refresh(p, id)),\n },\n readiness: {\n put: (readiness) => call(\"readiness.put\", (p) => service.readiness.put(p, readiness)),\n get: (domain) => option(\"readiness.get\", (p) => service.readiness.get(p, domain)),\n },\n withLock: (key, effect) =>\n Effect.acquireRelease(\n call(\"acquireLock\", (p) => service.acquireLock(p, key)).pipe(\n Effect.flatMap((acquired) =>\n acquired ? Effect.void : Errors.fail(new Reason.Busy({ key })),\n ),\n ),\n () => call(\"releaseLock\", (p) => service.releaseLock(p, key)).pipe(Effect.ignore),\n ).pipe(\n Effect.flatMap(() => effect),\n Effect.scoped,\n ),\n };\n};\n\nexport const layerFromAsync = (service: AsyncInterface): Layer.Layer<Service> =>\n Layer.succeed(Service)(fromAsync(service));\n"],"mappings":"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"}
package/dist/package.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.23.1";
2
+ var version = "0.23.2";
3
3
  //#endregion
4
4
  export { version };
5
5
 
@@ -406,7 +406,11 @@ export interface Interface {
406
406
  readonly put: (readiness: Readiness) => Fx<void>;
407
407
  readonly get: (domain: string) => Fx<Option.Option<Readiness>>;
408
408
  };
409
- /** Single-flight guard keyed by string (credential refresh, apply). Fails `Busy` rather than waiting. */
409
+ /**
410
+ * Single-flight guard keyed by string (credential refresh, OAuth completion). Fails `Busy` rather
411
+ * than waiting. A transactional store may run `effect` in one transaction, so a caller must not
412
+ * depend on the writes of a guarded effect that fails.
413
+ */
410
414
  readonly withLock: <A, E, R>(key: string, effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E | Errors.DomainKitError, R | Principal.Service>;
411
415
  }
412
416
  declare const Service_base: Context.ServiceClass<Service, "@domainkit/Storage", Interface>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "domainkit",
3
- "version": "0.23.1",
3
+ "version": "0.23.2",
4
4
  "description": "Build reviewable DNS plans for custom domains in TypeScript",
5
5
  "keywords": [
6
6
  "cloudflare",