domainkit 0.11.0 → 0.12.0

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":["Principal.make","Errors.isDomainKitError","Storage.Authorization","Storage.Credential","Storage.Service","Principal.Service","Plan.Model","Plan.Create","DnsRecord.txt","Storage.Attempt","Approval.Model","Receipt.Model","Receipt.Applied","Storage.Continuation","Storage.Readiness"],"sources":["../../../src/internal/conformance/storage.ts"],"sourcesContent":["import { DateTime, Deferred, Effect, Fiber, Layer, Option } from \"effect\";\n\nimport * as Approval from \"../../Approval.ts\";\nimport * as DnsRecord from \"../../DnsRecord.ts\";\nimport * as Errors from \"../error.ts\";\nimport * as Reason from \"../../Reason.ts\";\nimport * as Plan from \"../../Plan.ts\";\nimport * as Principal from \"../../Principal.ts\";\nimport * as Receipt from \"../../Receipt.ts\";\nimport * as Storage from \"../../Storage.ts\";\n\nexport interface Case {\n readonly name: string;\n readonly run: Effect.Effect<void, unknown>;\n}\n\nexport interface Options {\n /** Register each case with a test runner, e.g. vitest's `it`. */\n readonly it?: (name: string, run: () => Promise<void>) => void;\n}\n\nconst owner = Principal.make({ ownerId: \"conformance-owner-a\", actorId: \"actor-a\" });\nconst other = Principal.make({ ownerId: \"conformance-owner-b\", actorId: \"actor-b\" });\n\nclass Failure extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"StorageConformanceFailure\";\n }\n}\n\nconst expect = (condition: boolean, message: string) =>\n condition ? Effect.void : Effect.fail(new Failure(message));\n\nconst expectReason = <A, R>(\n effect: Effect.Effect<A, unknown, R>,\n tag: Reason.Model[\"_tag\"],\n label: string,\n): Effect.Effect<void, Failure, R> =>\n effect.pipe(\n Effect.flip,\n Effect.mapError(() => new Failure(`${label}: expected failure ${tag}, effect succeeded`)),\n Effect.flatMap((cause) =>\n expect(\n Errors.isDomainKitError(cause) && cause.reason._tag === tag,\n `${label}: expected reason ${tag}, received ${Errors.isDomainKitError(cause) ? cause.reason._tag : String(cause)}`,\n ),\n ),\n );\n\nconst authorizationRow = (principal: Principal.Interface, id: string, now: DateTime.Utc) =>\n new Storage.Authorization({\n id,\n ownerId: principal.ownerId,\n provider: \"fake\",\n method: \"token\",\n capabilities: [\"dns:read\"],\n context: { account: \"acc\" },\n revocation: \"active\",\n createdBy: principal.actorId,\n createdAt: now,\n });\n\nconst credentialRow = (now: DateTime.Utc, ciphertext = \"sealed-1\") =>\n new Storage.Credential({ ciphertext, expiresAt: null, rotatedAt: now });\n\nconst connect = (principal: Principal.Interface, id: string) =>\n Effect.gen(function* () {\n const storage = yield* Storage.Service;\n const now = yield* DateTime.now;\n yield* storage.authorizations.upsert({\n authorization: authorizationRow(principal, id, now),\n credential: credentialRow(now),\n });\n const connection = yield* storage.connections.create(id);\n const attachment = yield* storage.attachments.create({\n connectionId: connection.id,\n domain: `app.${id}.example.com`,\n zone: `${id}.example.com`,\n target: { zoneId: `zone-${id}` },\n });\n return { storage, connection, attachment };\n }).pipe(Effect.provideService(Principal.Service, principal));\n\nconst planRow = (attachmentId: string, now: DateTime.Utc, suffix: string) =>\n new Plan.Model({\n id: Plan.PlanId.make(`plan-${suffix}`),\n version: \"domainkit.plan.v2\",\n kind: \"provisioning\",\n digest: Plan.Digest.make(`digest-${suffix}`),\n domain: \"app.example.com\",\n zone: \"example.com\",\n provider: \"fake\",\n attachmentId,\n operations: [\n new Plan.Create({\n id: Plan.OperationId.make(`op-${suffix}`),\n record: DnsRecord.txt({ name: \"app.example.com\", value: suffix }),\n }),\n ],\n createdAt: now,\n expiresAt: DateTime.add(now, { hours: 1 }),\n });\n\nconst attemptRow = (principal: Principal.Interface, plan: Plan.Model) =>\n new Storage.Attempt({\n id: plan.id,\n ownerId: principal.ownerId,\n attachmentId: plan.attachmentId,\n kind: plan.kind,\n status: \"planned\",\n plan,\n approval: null,\n receipt: null,\n rejection: null,\n sourceReceiptId: null,\n leaseExpiresAt: null,\n failure: null,\n updatedAt: plan.createdAt,\n });\n\nconst approvalRow = (plan: Plan.Model, suffix: string) =>\n new Approval.Model({\n id: Approval.ApprovalId.make(`approval-${suffix}`),\n version: \"domainkit.approval.v2\",\n kind: plan.kind,\n planId: plan.id,\n digest: plan.digest,\n operationIds: plan.operations.map(({ id }) => id),\n approvedBy: \"actor\",\n approvedAt: plan.createdAt,\n expiresAt: plan.expiresAt,\n });\n\nconst receiptRow = (plan: Plan.Model, approval: Approval.Model, suffix: string) =>\n new Receipt.Model({\n id: Receipt.ReceiptId.make(`receipt-${suffix}`),\n version: \"domainkit.receipt.v2\",\n kind: plan.kind,\n planId: plan.id,\n approvalId: approval.id,\n digest: plan.digest,\n provider: plan.provider,\n zone: plan.zone,\n status: \"complete\",\n outcomes: plan.operations.map(\n (operation) => new Receipt.Applied({ operationId: operation.id, providerRecordId: \"r1\" }),\n ),\n appliedAt: plan.createdAt,\n });\n\n/** Every invariant a `Storage` implementation must hold. Each case runs against a fresh layer. */\nexport const cases = (layer: Layer.Layer<Storage.Service, unknown>): ReadonlyArray<Case> => {\n const run = <E>(effect: Effect.Effect<void, E, Storage.Service>): Effect.Effect<void, unknown> =>\n effect.pipe(Effect.provide(layer));\n return [\n {\n name: \"scopes every row by the principal's owner\",\n run: run(\n Effect.gen(function* () {\n const { storage, connection, attachment } = yield* connect(owner, \"auth-a\");\n const asOther = <A, E>(effect: Effect.Effect<A, E, Principal.Service>) =>\n effect.pipe(Effect.provideService(Principal.Service, other));\n yield* expectReason(\n asOther(storage.authorizations.get(\"auth-a\")),\n \"NotFound\",\n \"authorizations.get\",\n );\n yield* expectReason(\n asOther(storage.authorizations.credential(\"auth-a\")),\n \"NotFound\",\n \"authorizations.credential\",\n );\n yield* expectReason(\n asOther(storage.connections.get(connection.id)),\n \"NotFound\",\n \"connections.get\",\n );\n yield* expectReason(\n asOther(storage.attachments.get(attachment.id)),\n \"NotFound\",\n \"attachments.get\",\n );\n yield* expectReason(\n asOther(storage.attachments.list(connection.id)),\n \"NotFound\",\n \"attachments.list\",\n );\n const connections = yield* asOther(storage.connections.list());\n yield* expect(connections.length === 0, \"connections.list leaked across owners\");\n const byDomain = yield* asOther(storage.attachments.byDomain(attachment.domain));\n yield* expect(Option.isNone(byDomain), \"attachments.byDomain leaked across owners\");\n const now = yield* DateTime.now;\n yield* expectReason(\n asOther(\n storage.authorizations.upsert({\n authorization: authorizationRow(owner, \"auth-b\", now),\n credential: credentialRow(now),\n }),\n ),\n \"InvalidInput\",\n \"authorizations.upsert with a foreign ownerId\",\n );\n }),\n ),\n },\n {\n name: \"stores, rotates, and promotes credentials without exposing plaintext\",\n run: run(\n Effect.gen(function* () {\n const { storage } = yield* connect(owner, \"auth-cred\");\n const first = yield* storage.authorizations.credential(\"auth-cred\");\n yield* expect(first.ciphertext === \"sealed-1\", \"credential ciphertext was not stored\");\n const now = yield* DateTime.now;\n yield* storage.authorizations.rotate(\"auth-cred\", credentialRow(now, \"sealed-2\"));\n const rotated = yield* storage.authorizations.credential(\"auth-cred\");\n yield* expect(rotated.ciphertext === \"sealed-2\", \"rotate did not replace the credential\");\n yield* storage.authorizations.promoteCapabilities(\"auth-cred\", [\"dns:write\", \"dns:read\"]);\n const promoted = yield* storage.authorizations.get(\"auth-cred\");\n yield* expect(\n [...promoted.capabilities].sort().join(\",\") === \"dns:read,dns:write\",\n \"promoteCapabilities did not merge capabilities\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"replaces an authorization only through the expected id\",\n run: run(\n Effect.gen(function* () {\n const { storage } = yield* connect(owner, \"auth-cas\");\n const now = yield* DateTime.now;\n const replaced = yield* storage.authorizations.upsert({\n authorization: new Storage.Authorization({\n ...authorizationRow(owner, \"auth-cas\", now),\n method: \"oauth\",\n }),\n credential: credentialRow(now, \"sealed-oauth\"),\n expectedId: \"auth-cas\",\n });\n yield* expect(\n replaced.method === \"oauth\",\n \"upsert with expectedId did not replace the row\",\n );\n const credential = yield* storage.authorizations.credential(\"auth-cas\");\n yield* expect(\n credential.ciphertext === \"sealed-oauth\",\n \"upsert with expectedId did not replace the credential\",\n );\n yield* expectReason(\n storage.authorizations.upsert({\n authorization: authorizationRow(owner, \"auth-missing\", now),\n credential: credentialRow(now),\n expectedId: \"auth-missing\",\n }),\n \"NotFound\",\n \"upsert with an unknown expectedId\",\n );\n yield* expectReason(\n storage.authorizations.upsert({\n authorization: authorizationRow(owner, \"auth-cas\", now),\n credential: credentialRow(now),\n }),\n \"InvalidInput\",\n \"insert of an existing id\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"consumes continuations exactly once and rejects expired ones\",\n run: run(\n Effect.gen(function* () {\n const storage = yield* Storage.Service;\n const now = yield* DateTime.now;\n const continuation = new Storage.Continuation({\n id: \"cont-1\",\n ownerId: owner.ownerId,\n actorId: owner.actorId,\n provider: \"fake\",\n payload: { codeVerifier: \"v\" },\n returnTo: null,\n expiresAt: DateTime.add(now, { minutes: 15 }),\n });\n yield* storage.continuations.put(continuation);\n yield* expectReason(\n storage.continuations\n .consume(\"cont-1\")\n .pipe(Effect.provideService(Principal.Service, other)),\n \"NotFound\",\n \"consume by another owner\",\n );\n const peeked = yield* storage.continuations.get(\"cont-1\");\n yield* expect(peeked.id === \"cont-1\", \"get did not return the continuation\");\n const consumed = yield* storage.continuations.consume(\"cont-1\");\n yield* expect(consumed.id === \"cont-1\", \"consume did not return the continuation\");\n yield* expectReason(storage.continuations.get(\"cont-1\"), \"NotFound\", \"get after consume\");\n yield* expectReason(\n storage.continuations.consume(\"cont-1\"),\n \"NotFound\",\n \"second consume\",\n );\n yield* storage.continuations.put(\n new Storage.Continuation({\n ...continuation,\n id: \"cont-2\",\n expiresAt: DateTime.subtract(now, { minutes: 1 }),\n }),\n );\n yield* expectReason(storage.continuations.get(\"cont-2\"), \"Expired\", \"expired get\");\n yield* expectReason(\n storage.continuations.consume(\"cont-2\"),\n \"Expired\",\n \"expired consume\",\n );\n yield* expectReason(\n storage.continuations.consume(\"cont-2\"),\n \"NotFound\",\n \"expired consume is also spent\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"moves attempts through planned, approved, applying, and complete with leases\",\n run: run(\n Effect.gen(function* () {\n const { storage, attachment } = yield* connect(owner, \"auth-attempt\");\n const now = yield* DateTime.now;\n const plan = planRow(attachment.id, now, \"1\");\n yield* storage.attempts.create(attemptRow(owner, plan));\n yield* expectReason(\n storage.attempts.create(attemptRow(owner, plan)),\n \"InvalidInput\",\n \"duplicate attempt\",\n );\n yield* expectReason(\n storage.attempts.claim(plan.id, DateTime.add(now, { minutes: 2 })),\n \"Stale\",\n \"claim before approval\",\n );\n const approval = approvalRow(plan, \"1\");\n const approved = yield* storage.attempts.approve(plan.id, approval);\n yield* expect(approved.status === \"approved\", \"approve did not move the status\");\n const again = yield* storage.attempts.approve(plan.id, approval);\n yield* expect(\n again.approval?.id === approval.id,\n \"re-approving with the same approval must be idempotent\",\n );\n yield* expectReason(\n storage.attempts.approve(plan.id, approvalRow(plan, \"other\")),\n \"Stale\",\n \"second approval\",\n );\n const byApproval = yield* storage.attempts.byApproval(approval.id);\n yield* expect(byApproval.id === plan.id, \"byApproval did not find the attempt\");\n const claimed = yield* storage.attempts.claim(plan.id, DateTime.add(now, { minutes: 2 }));\n yield* expect(\n claimed.status === \"applying\" && claimed.leaseExpiresAt !== null,\n \"claim did not lease\",\n );\n yield* expectReason(\n storage.attempts.claim(plan.id, DateTime.add(now, { minutes: 2 })),\n \"Busy\",\n \"claim while leased\",\n );\n yield* storage.attempts.fail(plan.id, \"provider down\");\n const failed = yield* storage.attempts.get(plan.id);\n yield* expect(\n failed.status === \"failed\" && failed.failure === \"provider down\",\n \"fail did not record the message\",\n );\n const reclaimed = yield* storage.attempts.claim(\n plan.id,\n DateTime.subtract(now, { minutes: 1 }),\n );\n yield* expect(reclaimed.failure === null, \"claim did not clear the failure\");\n const expiredLease = yield* storage.attempts.claim(\n plan.id,\n DateTime.add(now, { minutes: 2 }),\n );\n yield* expect(expiredLease.status === \"applying\", \"an expired lease must be reclaimable\");\n const receipt = receiptRow(plan, approval, \"1\");\n const completed = yield* storage.attempts.complete(plan.id, receipt);\n yield* expect(\n completed.status === \"complete\" && completed.leaseExpiresAt === null,\n \"complete did not clear the lease\",\n );\n yield* expectReason(\n storage.attempts.claim(plan.id, DateTime.add(now, { minutes: 2 })),\n \"Stale\",\n \"claim after completion\",\n );\n const byReceipt = yield* storage.attempts.byReceipt(receipt.id);\n yield* expect(byReceipt.receipt?.id === receipt.id, \"byReceipt did not find the attempt\");\n const later = planRow(attachment.id, DateTime.add(now, { seconds: 1 }), \"2\");\n yield* storage.attempts.create(attemptRow(owner, later));\n const latest = yield* storage.attempts.latest(attachment.id, \"provisioning\");\n yield* expect(\n Option.isSome(latest) && latest.value.id === later.id,\n \"latest did not return the newest attempt\",\n );\n const cleanup = yield* storage.attempts.latest(attachment.id, \"cleanup\");\n yield* expect(Option.isNone(cleanup), \"latest must filter by kind\");\n yield* expectReason(\n storage.attempts.get(plan.id).pipe(Effect.provideService(Principal.Service, other)),\n \"NotFound\",\n \"attempts.get across owners\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"rejects a planned attempt once and terminally\",\n run: run(\n Effect.gen(function* () {\n const { storage, attachment } = yield* connect(owner, \"auth-reject\");\n const now = yield* DateTime.now;\n const plan = planRow(attachment.id, now, \"r1\");\n yield* storage.attempts.create(attemptRow(owner, plan));\n yield* expectReason(\n storage.attempts.reject(plan.id, {\n digest: Plan.Digest.make(\"other\"),\n actorId: \"a\",\n reason: null,\n }),\n \"Stale\",\n \"reject with a digest mismatch\",\n );\n yield* expectReason(\n storage.attempts\n .reject(plan.id, { digest: plan.digest, actorId: other.actorId, reason: null })\n .pipe(Effect.provideService(Principal.Service, other)),\n \"NotFound\",\n \"reject by another owner\",\n );\n const rejected = yield* storage.attempts.reject(plan.id, {\n digest: plan.digest,\n actorId: owner.actorId,\n reason: \"wrong zone\",\n });\n yield* expect(\n rejected.status === \"rejected\" &&\n rejected.rejection?.actorId === owner.actorId &&\n rejected.rejection.reason === \"wrong zone\" &&\n DateTime.toEpochMillis(rejected.rejection.at) >= DateTime.toEpochMillis(now),\n \"reject did not record the actor, reason, and time\",\n );\n const again = yield* storage.attempts.reject(plan.id, {\n digest: plan.digest,\n actorId: \"someone-else\",\n reason: \"later\",\n });\n yield* expect(\n again.rejection?.reason === \"wrong zone\",\n \"rejecting a rejected attempt must return it unchanged\",\n );\n yield* expectReason(\n storage.attempts.approve(plan.id, approvalRow(plan, \"r1\")),\n \"Stale\",\n \"approve after reject\",\n );\n yield* expectReason(\n storage.attempts.claim(plan.id, DateTime.add(now, { minutes: 2 })),\n \"Stale\",\n \"claim after reject\",\n );\n const approvedPlan = planRow(attachment.id, now, \"r2\");\n yield* storage.attempts.create(attemptRow(owner, approvedPlan));\n yield* storage.attempts.approve(approvedPlan.id, approvalRow(approvedPlan, \"r2\"));\n yield* expectReason(\n storage.attempts.reject(approvedPlan.id, {\n digest: approvedPlan.digest,\n actorId: \"a\",\n reason: null,\n }),\n \"Stale\",\n \"reject after approve\",\n );\n const expiredPlan = planRow(attachment.id, now, \"r3\");\n yield* storage.attempts.create(\n new Storage.Attempt({ ...attemptRow(owner, expiredPlan), status: \"expired\" }),\n );\n yield* expectReason(\n storage.attempts.reject(expiredPlan.id, {\n digest: expiredPlan.digest,\n actorId: \"a\",\n reason: null,\n }),\n \"Expired\",\n \"reject an expired plan\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"keeps a failed revocation pending and finishes it on recovery\",\n run: run(\n Effect.gen(function* () {\n const { storage, connection, attachment } = yield* connect(owner, \"auth-revoke\");\n yield* storage.attachments.remove(attachment.id);\n yield* storage.connections.remove(connection.id);\n const revokeFailure = yield* storage.authorizations\n .revoke(\"auth-revoke\", Effect.fail(new Failure(\"provider unavailable\")))\n .pipe(Effect.flip);\n yield* expect(\n revokeFailure instanceof Failure,\n \"revoke must surface the provider failure\",\n );\n const pending = yield* storage.authorizations.get(\"auth-revoke\");\n yield* expect(\n pending.revocation === \"pending\",\n \"a failed revoke must leave the row pending\",\n );\n const now = yield* DateTime.now;\n yield* expectReason(\n storage.authorizations.upsert({\n authorization: authorizationRow(owner, \"auth-revoke\", now),\n credential: credentialRow(now),\n expectedId: \"auth-revoke\",\n }),\n \"Busy\",\n \"reconnect while revocation is pending\",\n );\n const recoveredByOther = yield* storage.authorizations\n .recoverRevocations(() => Effect.void)\n .pipe(Effect.provideService(Principal.Service, other));\n yield* expect(recoveredByOther === 0, \"recovery must not cross owners\");\n let calls = 0;\n const recovered = yield* storage.authorizations.recoverRevocations(() =>\n Effect.sync(() => void (calls += 1)),\n );\n yield* expect(\n recovered === 1 && calls === 1,\n \"recovery must revoke exactly the pending authorization\",\n );\n yield* expectReason(\n storage.authorizations.get(\"auth-revoke\"),\n \"NotFound\",\n \"authorization after recovery\",\n );\n const again = yield* storage.authorizations.recoverRevocations(() => Effect.void);\n yield* expect(again === 0, \"recovery must be idempotent\");\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"blocks connection removal while attachments exist\",\n run: run(\n Effect.gen(function* () {\n const { storage, connection, attachment } = yield* connect(owner, \"auth-remove\");\n yield* expectReason(\n storage.connections.remove(connection.id),\n \"InvalidInput\",\n \"remove with attachments\",\n );\n yield* expectReason(\n storage.attachments.create({\n connectionId: connection.id,\n domain: attachment.domain,\n zone: attachment.zone,\n target: {},\n }),\n \"InvalidInput\",\n \"duplicate domain attachment\",\n );\n yield* storage.attachments.remove(attachment.id);\n yield* storage.connections.remove(connection.id);\n yield* expectReason(\n storage.connections.get(connection.id),\n \"NotFound\",\n \"connection after remove\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"fails Busy while a lock is held and releases it afterwards\",\n run: run(\n Effect.gen(function* () {\n const storage = yield* Storage.Service;\n const gate = yield* Deferred.make<void>();\n const holder = yield* Effect.forkChild(\n storage.withLock(\"refresh:auth-1\", Deferred.await(gate)),\n );\n yield* Effect.yieldNow;\n yield* expectReason(\n storage.withLock(\"refresh:auth-1\", Effect.void),\n \"Busy\",\n \"second holder\",\n );\n const otherOwner = yield* storage\n .withLock(\"refresh:auth-1\", Effect.succeed(\"ok\"))\n .pipe(Effect.provideService(Principal.Service, other));\n yield* expect(otherOwner === \"ok\", \"locks must be scoped by owner\");\n yield* Deferred.succeed(gate, undefined);\n yield* Fiber.join(holder);\n const after = yield* storage.withLock(\"refresh:auth-1\", Effect.succeed(\"free\"));\n yield* expect(after === \"free\", \"lock was not released\");\n yield* storage\n .withLock(\"refresh:auth-1\", Effect.fail(new Failure(\"inside\")))\n .pipe(Effect.ignore);\n const afterFailure = yield* storage.withLock(\"refresh:auth-1\", Effect.succeed(\"free\"));\n yield* expect(afterFailure === \"free\", \"lock was not released after a failure\");\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"stores readiness per domain, with or without an attachment\",\n run: run(\n Effect.gen(function* () {\n const { storage, attachment } = yield* connect(owner, \"auth-ready\");\n const now = yield* DateTime.now;\n const readiness = new Storage.Readiness({\n domain: attachment.domain,\n attachmentId: attachment.id,\n ownerId: owner.ownerId,\n overall: \"pending\",\n requirements: [\n {\n operationId: null,\n record: DnsRecord.txt({ name: \"app.example.com\", value: \"v\" }),\n status: \"missing\",\n evidence: [],\n },\n ],\n host: [],\n pendingSince: now,\n checkedAt: now,\n nextCheckAt: DateTime.add(now, { seconds: 15 }),\n });\n yield* storage.readiness.put(readiness);\n const stored = yield* storage.readiness.get(attachment.domain);\n yield* expect(\n Option.isSome(stored) && stored.value.overall === \"pending\",\n \"readiness was not stored\",\n );\n const foreign = yield* storage.readiness\n .get(attachment.domain)\n .pipe(Effect.provideService(Principal.Service, other));\n yield* expect(Option.isNone(foreign), \"readiness leaked across owners\");\n yield* storage.readiness.put(\n new Storage.Readiness({\n ...readiness,\n domain: \"observe-only.example.com\",\n attachmentId: null,\n }),\n );\n const observeOnly = yield* storage.readiness.get(\"observe-only.example.com\");\n yield* expect(\n Option.isSome(observeOnly) && observeOnly.value.attachmentId === null,\n \"readiness without an attachment was not stored\",\n );\n yield* expectReason(\n storage.readiness.put(\n new Storage.Readiness({ ...readiness, attachmentId: \"att-missing\" }),\n ),\n \"NotFound\",\n \"readiness with an unknown attachment\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n ];\n};\n\n/** Registers every case with `options.it` when given; always returns the cases. */\nexport const storage = (\n layer: Layer.Layer<Storage.Service, unknown>,\n options: Options = {},\n): ReadonlyArray<Case> => {\n const all = cases(layer);\n if (options.it !== undefined) {\n for (const item of all) options.it(item.name, () => Effect.runPromise(item.run));\n }\n return all;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"storage.mjs","names":["Principal.make","Errors.isDomainKitError","Storage.Authorization","Storage.Credential","Storage.Service","Principal.Service","Plan.Model","Plan.Create","DnsRecord.txt","Storage.Attempt","Approval.Model","Receipt.Model","Receipt.Applied","Storage.Continuation","Storage.Readiness"],"sources":["../../../src/internal/conformance/storage.ts"],"sourcesContent":["import { DateTime, Deferred, Effect, Fiber, Layer, Option } from \"effect\";\n\nimport * as Approval from \"../../Approval.ts\";\nimport * as DnsRecord from \"../../DnsRecord.ts\";\nimport * as Errors from \"../error.ts\";\nimport * as Reason from \"../../Reason.ts\";\nimport * as Plan from \"../../Plan.ts\";\nimport * as Principal from \"../../Principal.ts\";\nimport * as Receipt from \"../../Receipt.ts\";\nimport * as Storage from \"../../Storage.ts\";\n\nexport interface Case {\n readonly name: string;\n readonly run: Effect.Effect<void, unknown>;\n}\n\nexport interface Options {\n /** Register each case with a test runner, e.g. vitest's `it`. */\n readonly it?: (name: string, run: () => Promise<void>) => void;\n}\n\nconst owner = Principal.make({ ownerId: \"conformance-owner-a\", actorId: \"actor-a\" });\nconst other = Principal.make({ ownerId: \"conformance-owner-b\", actorId: \"actor-b\" });\n\nclass Failure extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"StorageConformanceFailure\";\n }\n}\n\nconst expect = (condition: boolean, message: string) =>\n condition ? Effect.void : Effect.fail(new Failure(message));\n\nconst expectReason = <A, R>(\n effect: Effect.Effect<A, unknown, R>,\n tag: Reason.Model[\"_tag\"],\n label: string,\n): Effect.Effect<void, Failure, R> =>\n effect.pipe(\n Effect.flip,\n Effect.mapError(() => new Failure(`${label}: expected failure ${tag}, effect succeeded`)),\n Effect.flatMap((cause) =>\n expect(\n Errors.isDomainKitError(cause) && cause.reason._tag === tag,\n `${label}: expected reason ${tag}, received ${Errors.isDomainKitError(cause) ? cause.reason._tag : String(cause)}`,\n ),\n ),\n );\n\nconst authorizationRow = (principal: Principal.Interface, id: string, now: DateTime.Utc) =>\n new Storage.Authorization({\n id,\n ownerId: principal.ownerId,\n provider: \"fake\",\n method: \"token\",\n capabilities: [\"dns:read\"],\n context: { account: \"acc\" },\n revocation: \"active\",\n createdBy: principal.actorId,\n createdAt: now,\n });\n\nconst credentialRow = (now: DateTime.Utc, ciphertext = \"sealed-1\") =>\n new Storage.Credential({ ciphertext, expiresAt: null, rotatedAt: now });\n\nconst connect = (principal: Principal.Interface, id: string) =>\n Effect.gen(function* () {\n const storage = yield* Storage.Service;\n const now = yield* DateTime.now;\n yield* storage.authorizations.upsert({\n authorization: authorizationRow(principal, id, now),\n credential: credentialRow(now),\n });\n const connection = yield* storage.connections.create(id);\n const attachment = yield* storage.attachments.create({\n connectionId: connection.id,\n domain: `app.${id}.example.com`,\n zone: `${id}.example.com`,\n label: `${id}.example.com (${id})`,\n target: { zoneId: `zone-${id}` },\n });\n return { storage, connection, attachment };\n }).pipe(Effect.provideService(Principal.Service, principal));\n\nconst planRow = (attachmentId: string, now: DateTime.Utc, suffix: string) =>\n new Plan.Model({\n id: Plan.PlanId.make(`plan-${suffix}`),\n version: \"domainkit.plan.v2\",\n kind: \"provisioning\",\n digest: Plan.Digest.make(`digest-${suffix}`),\n domain: \"app.example.com\",\n zone: \"example.com\",\n provider: \"fake\",\n attachmentId,\n operations: [\n new Plan.Create({\n id: Plan.OperationId.make(`op-${suffix}`),\n record: DnsRecord.txt({ name: \"app.example.com\", value: suffix }),\n }),\n ],\n createdAt: now,\n expiresAt: DateTime.add(now, { hours: 1 }),\n });\n\nconst attemptRow = (principal: Principal.Interface, plan: Plan.Model) =>\n new Storage.Attempt({\n id: plan.id,\n ownerId: principal.ownerId,\n attachmentId: plan.attachmentId,\n kind: plan.kind,\n status: \"planned\",\n plan,\n approval: null,\n receipt: null,\n rejection: null,\n sourceReceiptId: null,\n leaseExpiresAt: null,\n failure: null,\n updatedAt: plan.createdAt,\n });\n\nconst approvalRow = (plan: Plan.Model, suffix: string) =>\n new Approval.Model({\n id: Approval.ApprovalId.make(`approval-${suffix}`),\n version: \"domainkit.approval.v2\",\n kind: plan.kind,\n planId: plan.id,\n digest: plan.digest,\n operationIds: plan.operations.map(({ id }) => id),\n approvedBy: \"actor\",\n approvedAt: plan.createdAt,\n expiresAt: plan.expiresAt,\n });\n\nconst receiptRow = (plan: Plan.Model, approval: Approval.Model, suffix: string) =>\n new Receipt.Model({\n id: Receipt.ReceiptId.make(`receipt-${suffix}`),\n version: \"domainkit.receipt.v2\",\n kind: plan.kind,\n planId: plan.id,\n approvalId: approval.id,\n digest: plan.digest,\n provider: plan.provider,\n zone: plan.zone,\n status: \"complete\",\n outcomes: plan.operations.map(\n (operation) => new Receipt.Applied({ operationId: operation.id, providerRecordId: \"r1\" }),\n ),\n appliedAt: plan.createdAt,\n });\n\n/** Every invariant a `Storage` implementation must hold. Each case runs against a fresh layer. */\nexport const cases = (layer: Layer.Layer<Storage.Service, unknown>): ReadonlyArray<Case> => {\n const run = <E>(effect: Effect.Effect<void, E, Storage.Service>): Effect.Effect<void, unknown> =>\n effect.pipe(Effect.provide(layer));\n return [\n {\n name: \"scopes every row by the principal's owner\",\n run: run(\n Effect.gen(function* () {\n const { storage, connection, attachment } = yield* connect(owner, \"auth-a\");\n const asOther = <A, E>(effect: Effect.Effect<A, E, Principal.Service>) =>\n effect.pipe(Effect.provideService(Principal.Service, other));\n yield* expectReason(\n asOther(storage.authorizations.get(\"auth-a\")),\n \"NotFound\",\n \"authorizations.get\",\n );\n yield* expectReason(\n asOther(storage.authorizations.credential(\"auth-a\")),\n \"NotFound\",\n \"authorizations.credential\",\n );\n yield* expectReason(\n asOther(storage.connections.get(connection.id)),\n \"NotFound\",\n \"connections.get\",\n );\n yield* expectReason(\n asOther(storage.attachments.get(attachment.id)),\n \"NotFound\",\n \"attachments.get\",\n );\n yield* expectReason(\n asOther(storage.attachments.list(connection.id)),\n \"NotFound\",\n \"attachments.list\",\n );\n const connections = yield* asOther(storage.connections.list());\n yield* expect(connections.length === 0, \"connections.list leaked across owners\");\n const byDomain = yield* asOther(storage.attachments.byDomain(attachment.domain));\n yield* expect(Option.isNone(byDomain), \"attachments.byDomain leaked across owners\");\n const now = yield* DateTime.now;\n yield* expectReason(\n asOther(\n storage.authorizations.upsert({\n authorization: authorizationRow(owner, \"auth-b\", now),\n credential: credentialRow(now),\n }),\n ),\n \"InvalidInput\",\n \"authorizations.upsert with a foreign ownerId\",\n );\n }),\n ),\n },\n {\n name: \"stores, rotates, and promotes credentials without exposing plaintext\",\n run: run(\n Effect.gen(function* () {\n const { storage } = yield* connect(owner, \"auth-cred\");\n const first = yield* storage.authorizations.credential(\"auth-cred\");\n yield* expect(first.ciphertext === \"sealed-1\", \"credential ciphertext was not stored\");\n const now = yield* DateTime.now;\n yield* storage.authorizations.rotate(\"auth-cred\", credentialRow(now, \"sealed-2\"));\n const rotated = yield* storage.authorizations.credential(\"auth-cred\");\n yield* expect(rotated.ciphertext === \"sealed-2\", \"rotate did not replace the credential\");\n yield* storage.authorizations.promoteCapabilities(\"auth-cred\", [\"dns:write\", \"dns:read\"]);\n const promoted = yield* storage.authorizations.get(\"auth-cred\");\n yield* expect(\n [...promoted.capabilities].sort().join(\",\") === \"dns:read,dns:write\",\n \"promoteCapabilities did not merge capabilities\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"replaces an authorization only through the expected id\",\n run: run(\n Effect.gen(function* () {\n const { storage } = yield* connect(owner, \"auth-cas\");\n const now = yield* DateTime.now;\n const replaced = yield* storage.authorizations.upsert({\n authorization: new Storage.Authorization({\n ...authorizationRow(owner, \"auth-cas\", now),\n method: \"oauth\",\n }),\n credential: credentialRow(now, \"sealed-oauth\"),\n expectedId: \"auth-cas\",\n });\n yield* expect(\n replaced.method === \"oauth\",\n \"upsert with expectedId did not replace the row\",\n );\n const credential = yield* storage.authorizations.credential(\"auth-cas\");\n yield* expect(\n credential.ciphertext === \"sealed-oauth\",\n \"upsert with expectedId did not replace the credential\",\n );\n yield* expectReason(\n storage.authorizations.upsert({\n authorization: authorizationRow(owner, \"auth-missing\", now),\n credential: credentialRow(now),\n expectedId: \"auth-missing\",\n }),\n \"NotFound\",\n \"upsert with an unknown expectedId\",\n );\n yield* expectReason(\n storage.authorizations.upsert({\n authorization: authorizationRow(owner, \"auth-cas\", now),\n credential: credentialRow(now),\n }),\n \"InvalidInput\",\n \"insert of an existing id\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"consumes continuations exactly once and rejects expired ones\",\n run: run(\n Effect.gen(function* () {\n const storage = yield* Storage.Service;\n const now = yield* DateTime.now;\n const continuation = new Storage.Continuation({\n id: \"cont-1\",\n ownerId: owner.ownerId,\n actorId: owner.actorId,\n provider: \"fake\",\n payload: { codeVerifier: \"v\" },\n returnTo: null,\n expiresAt: DateTime.add(now, { minutes: 15 }),\n });\n yield* storage.continuations.put(continuation);\n yield* expectReason(\n storage.continuations\n .consume(\"cont-1\")\n .pipe(Effect.provideService(Principal.Service, other)),\n \"NotFound\",\n \"consume by another owner\",\n );\n const peeked = yield* storage.continuations.get(\"cont-1\");\n yield* expect(peeked.id === \"cont-1\", \"get did not return the continuation\");\n const consumed = yield* storage.continuations.consume(\"cont-1\");\n yield* expect(consumed.id === \"cont-1\", \"consume did not return the continuation\");\n yield* expectReason(storage.continuations.get(\"cont-1\"), \"NotFound\", \"get after consume\");\n yield* expectReason(\n storage.continuations.consume(\"cont-1\"),\n \"NotFound\",\n \"second consume\",\n );\n yield* storage.continuations.put(\n new Storage.Continuation({\n ...continuation,\n id: \"cont-2\",\n expiresAt: DateTime.subtract(now, { minutes: 1 }),\n }),\n );\n yield* expectReason(storage.continuations.get(\"cont-2\"), \"Expired\", \"expired get\");\n yield* expectReason(\n storage.continuations.consume(\"cont-2\"),\n \"Expired\",\n \"expired consume\",\n );\n yield* expectReason(\n storage.continuations.consume(\"cont-2\"),\n \"NotFound\",\n \"expired consume is also spent\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"moves attempts through planned, approved, applying, and complete with leases\",\n run: run(\n Effect.gen(function* () {\n const { storage, attachment } = yield* connect(owner, \"auth-attempt\");\n const now = yield* DateTime.now;\n const plan = planRow(attachment.id, now, \"1\");\n yield* storage.attempts.create(attemptRow(owner, plan));\n yield* expectReason(\n storage.attempts.create(attemptRow(owner, plan)),\n \"InvalidInput\",\n \"duplicate attempt\",\n );\n yield* expectReason(\n storage.attempts.claim(plan.id, DateTime.add(now, { minutes: 2 })),\n \"Stale\",\n \"claim before approval\",\n );\n const approval = approvalRow(plan, \"1\");\n const approved = yield* storage.attempts.approve(plan.id, approval);\n yield* expect(approved.status === \"approved\", \"approve did not move the status\");\n const again = yield* storage.attempts.approve(plan.id, approval);\n yield* expect(\n again.approval?.id === approval.id,\n \"re-approving with the same approval must be idempotent\",\n );\n yield* expectReason(\n storage.attempts.approve(plan.id, approvalRow(plan, \"other\")),\n \"Stale\",\n \"second approval\",\n );\n const byApproval = yield* storage.attempts.byApproval(approval.id);\n yield* expect(byApproval.id === plan.id, \"byApproval did not find the attempt\");\n const claimed = yield* storage.attempts.claim(plan.id, DateTime.add(now, { minutes: 2 }));\n yield* expect(\n claimed.status === \"applying\" && claimed.leaseExpiresAt !== null,\n \"claim did not lease\",\n );\n yield* expectReason(\n storage.attempts.claim(plan.id, DateTime.add(now, { minutes: 2 })),\n \"Busy\",\n \"claim while leased\",\n );\n yield* storage.attempts.fail(plan.id, \"provider down\");\n const failed = yield* storage.attempts.get(plan.id);\n yield* expect(\n failed.status === \"failed\" && failed.failure === \"provider down\",\n \"fail did not record the message\",\n );\n const reclaimed = yield* storage.attempts.claim(\n plan.id,\n DateTime.subtract(now, { minutes: 1 }),\n );\n yield* expect(reclaimed.failure === null, \"claim did not clear the failure\");\n const expiredLease = yield* storage.attempts.claim(\n plan.id,\n DateTime.add(now, { minutes: 2 }),\n );\n yield* expect(expiredLease.status === \"applying\", \"an expired lease must be reclaimable\");\n const receipt = receiptRow(plan, approval, \"1\");\n const completed = yield* storage.attempts.complete(plan.id, receipt);\n yield* expect(\n completed.status === \"complete\" && completed.leaseExpiresAt === null,\n \"complete did not clear the lease\",\n );\n yield* expectReason(\n storage.attempts.claim(plan.id, DateTime.add(now, { minutes: 2 })),\n \"Stale\",\n \"claim after completion\",\n );\n const byReceipt = yield* storage.attempts.byReceipt(receipt.id);\n yield* expect(byReceipt.receipt?.id === receipt.id, \"byReceipt did not find the attempt\");\n const later = planRow(attachment.id, DateTime.add(now, { seconds: 1 }), \"2\");\n yield* storage.attempts.create(attemptRow(owner, later));\n const latest = yield* storage.attempts.latest(attachment.id, \"provisioning\");\n yield* expect(\n Option.isSome(latest) && latest.value.id === later.id,\n \"latest did not return the newest attempt\",\n );\n const cleanup = yield* storage.attempts.latest(attachment.id, \"cleanup\");\n yield* expect(Option.isNone(cleanup), \"latest must filter by kind\");\n yield* expectReason(\n storage.attempts.get(plan.id).pipe(Effect.provideService(Principal.Service, other)),\n \"NotFound\",\n \"attempts.get across owners\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"rejects a planned attempt once and terminally\",\n run: run(\n Effect.gen(function* () {\n const { storage, attachment } = yield* connect(owner, \"auth-reject\");\n const now = yield* DateTime.now;\n const plan = planRow(attachment.id, now, \"r1\");\n yield* storage.attempts.create(attemptRow(owner, plan));\n yield* expectReason(\n storage.attempts.reject(plan.id, {\n digest: Plan.Digest.make(\"other\"),\n actorId: \"a\",\n reason: null,\n }),\n \"Stale\",\n \"reject with a digest mismatch\",\n );\n yield* expectReason(\n storage.attempts\n .reject(plan.id, { digest: plan.digest, actorId: other.actorId, reason: null })\n .pipe(Effect.provideService(Principal.Service, other)),\n \"NotFound\",\n \"reject by another owner\",\n );\n const rejected = yield* storage.attempts.reject(plan.id, {\n digest: plan.digest,\n actorId: owner.actorId,\n reason: \"wrong zone\",\n });\n yield* expect(\n rejected.status === \"rejected\" &&\n rejected.rejection?.actorId === owner.actorId &&\n rejected.rejection.reason === \"wrong zone\" &&\n DateTime.toEpochMillis(rejected.rejection.at) >= DateTime.toEpochMillis(now),\n \"reject did not record the actor, reason, and time\",\n );\n const again = yield* storage.attempts.reject(plan.id, {\n digest: plan.digest,\n actorId: \"someone-else\",\n reason: \"later\",\n });\n yield* expect(\n again.rejection?.reason === \"wrong zone\",\n \"rejecting a rejected attempt must return it unchanged\",\n );\n yield* expectReason(\n storage.attempts.approve(plan.id, approvalRow(plan, \"r1\")),\n \"Stale\",\n \"approve after reject\",\n );\n yield* expectReason(\n storage.attempts.claim(plan.id, DateTime.add(now, { minutes: 2 })),\n \"Stale\",\n \"claim after reject\",\n );\n const approvedPlan = planRow(attachment.id, now, \"r2\");\n yield* storage.attempts.create(attemptRow(owner, approvedPlan));\n yield* storage.attempts.approve(approvedPlan.id, approvalRow(approvedPlan, \"r2\"));\n yield* expectReason(\n storage.attempts.reject(approvedPlan.id, {\n digest: approvedPlan.digest,\n actorId: \"a\",\n reason: null,\n }),\n \"Stale\",\n \"reject after approve\",\n );\n const expiredPlan = planRow(attachment.id, now, \"r3\");\n yield* storage.attempts.create(\n new Storage.Attempt({ ...attemptRow(owner, expiredPlan), status: \"expired\" }),\n );\n yield* expectReason(\n storage.attempts.reject(expiredPlan.id, {\n digest: expiredPlan.digest,\n actorId: \"a\",\n reason: null,\n }),\n \"Expired\",\n \"reject an expired plan\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"keeps a failed revocation pending and finishes it on recovery\",\n run: run(\n Effect.gen(function* () {\n const { storage, connection, attachment } = yield* connect(owner, \"auth-revoke\");\n yield* storage.attachments.remove(attachment.id);\n yield* storage.connections.remove(connection.id);\n const revokeFailure = yield* storage.authorizations\n .revoke(\"auth-revoke\", Effect.fail(new Failure(\"provider unavailable\")))\n .pipe(Effect.flip);\n yield* expect(\n revokeFailure instanceof Failure,\n \"revoke must surface the provider failure\",\n );\n const pending = yield* storage.authorizations.get(\"auth-revoke\");\n yield* expect(\n pending.revocation === \"pending\",\n \"a failed revoke must leave the row pending\",\n );\n const now = yield* DateTime.now;\n yield* expectReason(\n storage.authorizations.upsert({\n authorization: authorizationRow(owner, \"auth-revoke\", now),\n credential: credentialRow(now),\n expectedId: \"auth-revoke\",\n }),\n \"Busy\",\n \"reconnect while revocation is pending\",\n );\n const recoveredByOther = yield* storage.authorizations\n .recoverRevocations(() => Effect.void)\n .pipe(Effect.provideService(Principal.Service, other));\n yield* expect(recoveredByOther === 0, \"recovery must not cross owners\");\n let calls = 0;\n const recovered = yield* storage.authorizations.recoverRevocations(() =>\n Effect.sync(() => void (calls += 1)),\n );\n yield* expect(\n recovered === 1 && calls === 1,\n \"recovery must revoke exactly the pending authorization\",\n );\n yield* expectReason(\n storage.authorizations.get(\"auth-revoke\"),\n \"NotFound\",\n \"authorization after recovery\",\n );\n const again = yield* storage.authorizations.recoverRevocations(() => Effect.void);\n yield* expect(again === 0, \"recovery must be idempotent\");\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"returns an attachment's label from every read\",\n run: run(\n Effect.gen(function* () {\n const { storage, connection, attachment } = yield* connect(owner, \"auth-label\");\n const label = `${attachment.zone} (auth-label)`;\n yield* expect(attachment.label === label, \"create must return the label it was given\");\n const read = yield* storage.attachments.get(attachment.id);\n yield* expect(read.label === label, \"get must return the stored label\");\n const byDomain = yield* storage.attachments.byDomain(attachment.domain);\n yield* expect(\n Option.isSome(byDomain) && byDomain.value.label === label,\n \"byDomain must return the stored label\",\n );\n const listed = yield* storage.attachments.list(connection.id);\n yield* expect(\n listed.every((row) => row.label === label),\n \"list must return the stored label\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"blocks connection removal while attachments exist\",\n run: run(\n Effect.gen(function* () {\n const { storage, connection, attachment } = yield* connect(owner, \"auth-remove\");\n yield* expectReason(\n storage.connections.remove(connection.id),\n \"InvalidInput\",\n \"remove with attachments\",\n );\n yield* expectReason(\n storage.attachments.create({\n connectionId: connection.id,\n domain: attachment.domain,\n zone: attachment.zone,\n label: attachment.label,\n target: {},\n }),\n \"InvalidInput\",\n \"duplicate domain attachment\",\n );\n yield* storage.attachments.remove(attachment.id);\n yield* storage.connections.remove(connection.id);\n yield* expectReason(\n storage.connections.get(connection.id),\n \"NotFound\",\n \"connection after remove\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"fails Busy while a lock is held and releases it afterwards\",\n run: run(\n Effect.gen(function* () {\n const storage = yield* Storage.Service;\n const gate = yield* Deferred.make<void>();\n const holder = yield* Effect.forkChild(\n storage.withLock(\"refresh:auth-1\", Deferred.await(gate)),\n );\n yield* Effect.yieldNow;\n yield* expectReason(\n storage.withLock(\"refresh:auth-1\", Effect.void),\n \"Busy\",\n \"second holder\",\n );\n const otherOwner = yield* storage\n .withLock(\"refresh:auth-1\", Effect.succeed(\"ok\"))\n .pipe(Effect.provideService(Principal.Service, other));\n yield* expect(otherOwner === \"ok\", \"locks must be scoped by owner\");\n yield* Deferred.succeed(gate, undefined);\n yield* Fiber.join(holder);\n const after = yield* storage.withLock(\"refresh:auth-1\", Effect.succeed(\"free\"));\n yield* expect(after === \"free\", \"lock was not released\");\n yield* storage\n .withLock(\"refresh:auth-1\", Effect.fail(new Failure(\"inside\")))\n .pipe(Effect.ignore);\n const afterFailure = yield* storage.withLock(\"refresh:auth-1\", Effect.succeed(\"free\"));\n yield* expect(afterFailure === \"free\", \"lock was not released after a failure\");\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"stores readiness per domain, with or without an attachment\",\n run: run(\n Effect.gen(function* () {\n const { storage, attachment } = yield* connect(owner, \"auth-ready\");\n const now = yield* DateTime.now;\n const readiness = new Storage.Readiness({\n domain: attachment.domain,\n attachmentId: attachment.id,\n ownerId: owner.ownerId,\n overall: \"pending\",\n requirements: [\n {\n operationId: null,\n record: DnsRecord.txt({ name: \"app.example.com\", value: \"v\" }),\n status: \"missing\",\n evidence: [],\n },\n ],\n host: [],\n pendingSince: now,\n checkedAt: now,\n nextCheckAt: DateTime.add(now, { seconds: 15 }),\n });\n yield* storage.readiness.put(readiness);\n const stored = yield* storage.readiness.get(attachment.domain);\n yield* expect(\n Option.isSome(stored) && stored.value.overall === \"pending\",\n \"readiness was not stored\",\n );\n const foreign = yield* storage.readiness\n .get(attachment.domain)\n .pipe(Effect.provideService(Principal.Service, other));\n yield* expect(Option.isNone(foreign), \"readiness leaked across owners\");\n yield* storage.readiness.put(\n new Storage.Readiness({\n ...readiness,\n domain: \"observe-only.example.com\",\n attachmentId: null,\n }),\n );\n const observeOnly = yield* storage.readiness.get(\"observe-only.example.com\");\n yield* expect(\n Option.isSome(observeOnly) && observeOnly.value.attachmentId === null,\n \"readiness without an attachment was not stored\",\n );\n yield* expectReason(\n storage.readiness.put(\n new Storage.Readiness({ ...readiness, attachmentId: \"att-missing\" }),\n ),\n \"NotFound\",\n \"readiness with an unknown attachment\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n ];\n};\n\n/** Registers every case with `options.it` when given; always returns the cases. */\nexport const storage = (\n layer: Layer.Layer<Storage.Service, unknown>,\n options: Options = {},\n): ReadonlyArray<Case> => {\n const all = cases(layer);\n if (options.it !== undefined) {\n for (const item of all) options.it(item.name, () => Effect.runPromise(item.run));\n }\n return all;\n};\n"],"mappings":";;;;;;;;;AAqBA,MAAM,QAAQA,KAAe;CAAE,SAAS;CAAuB,SAAS;AAAU,CAAC;AACnF,MAAM,QAAQA,KAAe;CAAE,SAAS;CAAuB,SAAS;AAAU,CAAC;AAEnF,IAAM,UAAN,cAAsB,MAAM;CAC1B,YAAY,SAAiB;EAC3B,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF;AAEA,MAAM,UAAU,WAAoB,YAClC,YAAY,OAAO,OAAO,OAAO,KAAK,IAAI,QAAQ,OAAO,CAAC;AAE5D,MAAM,gBACJ,QACA,KACA,UAEA,OAAO,KACL,OAAO,MACP,OAAO,eAAe,IAAI,QAAQ,GAAG,MAAM,qBAAqB,IAAI,mBAAmB,CAAC,GACxF,OAAO,SAAS,UACd,OACEC,iBAAwB,KAAK,KAAK,MAAM,OAAO,SAAS,KACxD,GAAG,MAAM,oBAAoB,IAAI,aAAaA,iBAAwB,KAAK,IAAI,MAAM,OAAO,OAAO,OAAO,KAAK,GACjH,CACF,CACF;AAEF,MAAM,oBAAoB,WAAgC,IAAY,QACpE,IAAIC,cAAsB;CACxB;CACA,SAAS,UAAU;CACnB,UAAU;CACV,QAAQ;CACR,cAAc,CAAC,UAAU;CACzB,SAAS,EAAE,SAAS,MAAM;CAC1B,YAAY;CACZ,WAAW,UAAU;CACrB,WAAW;AACb,CAAC;AAEH,MAAM,iBAAiB,KAAmB,aAAa,eACrD,IAAIC,WAAmB;CAAE;CAAY,WAAW;CAAM,WAAW;AAAI,CAAC;AAExE,MAAM,WAAW,WAAgC,OAC/C,OAAO,IAAI,aAAa;CACtB,MAAM,UAAU,OAAOC;CACvB,MAAM,MAAM,OAAO,SAAS;CAC5B,OAAO,QAAQ,eAAe,OAAO;EACnC,eAAe,iBAAiB,WAAW,IAAI,GAAG;EAClD,YAAY,cAAc,GAAG;CAC/B,CAAC;CACD,MAAM,aAAa,OAAO,QAAQ,YAAY,OAAO,EAAE;CAQvD,OAAO;EAAE;EAAS;EAAY,YAAA,OAPJ,QAAQ,YAAY,OAAO;GACnD,cAAc,WAAW;GACzB,QAAQ,OAAO,GAAG;GAClB,MAAM,GAAG,GAAG;GACZ,OAAO,GAAG,GAAG,gBAAgB,GAAG;GAChC,QAAQ,EAAE,QAAQ,QAAQ,KAAK;EACjC,CAAC;CACwC;AAC3C,CAAC,CAAC,CAAC,KAAK,OAAO,eAAeC,SAAmB,SAAS,CAAC;AAE7D,MAAM,WAAW,cAAsB,KAAmB,WACxD,IAAIC,MAAW;CACb,IAAA,OAAgB,KAAK,QAAQ,QAAQ;CACrC,SAAS;CACT,MAAM;CACN,QAAA,OAAoB,KAAK,UAAU,QAAQ;CAC3C,QAAQ;CACR,MAAM;CACN,UAAU;CACV;CACA,YAAY,CACV,IAAIC,OAAY;EACd,IAAA,YAAqB,KAAK,MAAM,QAAQ;EACxC,QAAQC,IAAc;GAAE,MAAM;GAAmB,OAAO;EAAO,CAAC;CAClE,CAAC,CACH;CACA,WAAW;CACX,WAAW,SAAS,IAAI,KAAK,EAAE,OAAO,EAAE,CAAC;AAC3C,CAAC;AAEH,MAAM,cAAc,WAAgC,SAClD,IAAIC,QAAgB;CAClB,IAAI,KAAK;CACT,SAAS,UAAU;CACnB,cAAc,KAAK;CACnB,MAAM,KAAK;CACX,QAAQ;CACR;CACA,UAAU;CACV,SAAS;CACT,WAAW;CACX,iBAAiB;CACjB,gBAAgB;CAChB,SAAS;CACT,WAAW,KAAK;AAClB,CAAC;AAEH,MAAM,eAAe,MAAkB,WACrC,IAAIC,QAAe;CACjB,IAAA,WAAwB,KAAK,YAAY,QAAQ;CACjD,SAAS;CACT,MAAM,KAAK;CACX,QAAQ,KAAK;CACb,QAAQ,KAAK;CACb,cAAc,KAAK,WAAW,KAAK,EAAE,SAAS,EAAE;CAChD,YAAY;CACZ,YAAY,KAAK;CACjB,WAAW,KAAK;AAClB,CAAC;AAEH,MAAM,cAAc,MAAkB,UAA0B,WAC9D,IAAIC,QAAc;CAChB,IAAA,UAAsB,KAAK,WAAW,QAAQ;CAC9C,SAAS;CACT,MAAM,KAAK;CACX,QAAQ,KAAK;CACb,YAAY,SAAS;CACrB,QAAQ,KAAK;CACb,UAAU,KAAK;CACf,MAAM,KAAK;CACX,QAAQ;CACR,UAAU,KAAK,WAAW,KACvB,cAAc,IAAIC,QAAgB;EAAE,aAAa,UAAU;EAAI,kBAAkB;CAAK,CAAC,CAC1F;CACA,WAAW,KAAK;AAClB,CAAC;;AAGH,MAAa,SAAS,UAAsE;CAC1F,MAAM,OAAU,WACd,OAAO,KAAK,OAAO,QAAQ,KAAK,CAAC;CACnC,OAAO;EACL;GACE,MAAM;GACN,KAAK,IACH,OAAO,IAAI,aAAa;IACtB,MAAM,EAAE,SAAS,YAAY,eAAe,OAAO,QAAQ,OAAO,QAAQ;IAC1E,MAAM,WAAiB,WACrB,OAAO,KAAK,OAAO,eAAeP,SAAmB,KAAK,CAAC;IAC7D,OAAO,aACL,QAAQ,QAAQ,eAAe,IAAI,QAAQ,CAAC,GAC5C,YACA,oBACF;IACA,OAAO,aACL,QAAQ,QAAQ,eAAe,WAAW,QAAQ,CAAC,GACnD,YACA,2BACF;IACA,OAAO,aACL,QAAQ,QAAQ,YAAY,IAAI,WAAW,EAAE,CAAC,GAC9C,YACA,iBACF;IACA,OAAO,aACL,QAAQ,QAAQ,YAAY,IAAI,WAAW,EAAE,CAAC,GAC9C,YACA,iBACF;IACA,OAAO,aACL,QAAQ,QAAQ,YAAY,KAAK,WAAW,EAAE,CAAC,GAC/C,YACA,kBACF;IACA,MAAM,cAAc,OAAO,QAAQ,QAAQ,YAAY,KAAK,CAAC;IAC7D,OAAO,OAAO,YAAY,WAAW,GAAG,uCAAuC;IAC/E,MAAM,WAAW,OAAO,QAAQ,QAAQ,YAAY,SAAS,WAAW,MAAM,CAAC;IAC/E,OAAO,OAAO,OAAO,OAAO,QAAQ,GAAG,2CAA2C;IAClF,MAAM,MAAM,OAAO,SAAS;IAC5B,OAAO,aACL,QACE,QAAQ,eAAe,OAAO;KAC5B,eAAe,iBAAiB,OAAO,UAAU,GAAG;KACpD,YAAY,cAAc,GAAG;IAC/B,CAAC,CACH,GACA,gBACA,8CACF;GACF,CAAC,CACH;EACF;EACA;GACE,MAAM;GACN,KAAK,IACH,OAAO,IAAI,aAAa;IACtB,MAAM,EAAE,YAAY,OAAO,QAAQ,OAAO,WAAW;IACrD,MAAM,QAAQ,OAAO,QAAQ,eAAe,WAAW,WAAW;IAClE,OAAO,OAAO,MAAM,eAAe,YAAY,sCAAsC;IACrF,MAAM,MAAM,OAAO,SAAS;IAC5B,OAAO,QAAQ,eAAe,OAAO,aAAa,cAAc,KAAK,UAAU,CAAC;IAChF,MAAM,UAAU,OAAO,QAAQ,eAAe,WAAW,WAAW;IACpE,OAAO,OAAO,QAAQ,eAAe,YAAY,uCAAuC;IACxF,OAAO,QAAQ,eAAe,oBAAoB,aAAa,CAAC,aAAa,UAAU,CAAC;IACxF,MAAM,WAAW,OAAO,QAAQ,eAAe,IAAI,WAAW;IAC9D,OAAO,OACL,CAAC,GAAG,SAAS,YAAY,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,MAAM,sBAChD,gDACF;GACF,CAAC,CAAC,CAAC,KAAK,OAAO,eAAeA,SAAmB,KAAK,CAAC,CACzD;EACF;EACA;GACE,MAAM;GACN,KAAK,IACH,OAAO,IAAI,aAAa;IACtB,MAAM,EAAE,YAAY,OAAO,QAAQ,OAAO,UAAU;IACpD,MAAM,MAAM,OAAO,SAAS;IAC5B,MAAM,WAAW,OAAO,QAAQ,eAAe,OAAO;KACpD,eAAe,IAAIH,cAAsB;MACvC,GAAG,iBAAiB,OAAO,YAAY,GAAG;MAC1C,QAAQ;KACV,CAAC;KACD,YAAY,cAAc,KAAK,cAAc;KAC7C,YAAY;IACd,CAAC;IACD,OAAO,OACL,SAAS,WAAW,SACpB,gDACF;IACA,MAAM,aAAa,OAAO,QAAQ,eAAe,WAAW,UAAU;IACtE,OAAO,OACL,WAAW,eAAe,gBAC1B,uDACF;IACA,OAAO,aACL,QAAQ,eAAe,OAAO;KAC5B,eAAe,iBAAiB,OAAO,gBAAgB,GAAG;KAC1D,YAAY,cAAc,GAAG;KAC7B,YAAY;IACd,CAAC,GACD,YACA,mCACF;IACA,OAAO,aACL,QAAQ,eAAe,OAAO;KAC5B,eAAe,iBAAiB,OAAO,YAAY,GAAG;KACtD,YAAY,cAAc,GAAG;IAC/B,CAAC,GACD,gBACA,0BACF;GACF,CAAC,CAAC,CAAC,KAAK,OAAO,eAAeG,SAAmB,KAAK,CAAC,CACzD;EACF;EACA;GACE,MAAM;GACN,KAAK,IACH,OAAO,IAAI,aAAa;IACtB,MAAM,UAAU,OAAOD;IACvB,MAAM,MAAM,OAAO,SAAS;IAC5B,MAAM,eAAe,IAAIS,aAAqB;KAC5C,IAAI;KACJ,SAAS,MAAM;KACf,SAAS,MAAM;KACf,UAAU;KACV,SAAS,EAAE,cAAc,IAAI;KAC7B,UAAU;KACV,WAAW,SAAS,IAAI,KAAK,EAAE,SAAS,GAAG,CAAC;IAC9C,CAAC;IACD,OAAO,QAAQ,cAAc,IAAI,YAAY;IAC7C,OAAO,aACL,QAAQ,cACL,QAAQ,QAAQ,CAAC,CACjB,KAAK,OAAO,eAAeR,SAAmB,KAAK,CAAC,GACvD,YACA,0BACF;IACA,MAAM,SAAS,OAAO,QAAQ,cAAc,IAAI,QAAQ;IACxD,OAAO,OAAO,OAAO,OAAO,UAAU,qCAAqC;IAC3E,MAAM,WAAW,OAAO,QAAQ,cAAc,QAAQ,QAAQ;IAC9D,OAAO,OAAO,SAAS,OAAO,UAAU,yCAAyC;IACjF,OAAO,aAAa,QAAQ,cAAc,IAAI,QAAQ,GAAG,YAAY,mBAAmB;IACxF,OAAO,aACL,QAAQ,cAAc,QAAQ,QAAQ,GACtC,YACA,gBACF;IACA,OAAO,QAAQ,cAAc,IAC3B,IAAIQ,aAAqB;KACvB,GAAG;KACH,IAAI;KACJ,WAAW,SAAS,SAAS,KAAK,EAAE,SAAS,EAAE,CAAC;IAClD,CAAC,CACH;IACA,OAAO,aAAa,QAAQ,cAAc,IAAI,QAAQ,GAAG,WAAW,aAAa;IACjF,OAAO,aACL,QAAQ,cAAc,QAAQ,QAAQ,GACtC,WACA,iBACF;IACA,OAAO,aACL,QAAQ,cAAc,QAAQ,QAAQ,GACtC,YACA,+BACF;GACF,CAAC,CAAC,CAAC,KAAK,OAAO,eAAeR,SAAmB,KAAK,CAAC,CACzD;EACF;EACA;GACE,MAAM;GACN,KAAK,IACH,OAAO,IAAI,aAAa;IACtB,MAAM,EAAE,SAAS,eAAe,OAAO,QAAQ,OAAO,cAAc;IACpE,MAAM,MAAM,OAAO,SAAS;IAC5B,MAAM,OAAO,QAAQ,WAAW,IAAI,KAAK,GAAG;IAC5C,OAAO,QAAQ,SAAS,OAAO,WAAW,OAAO,IAAI,CAAC;IACtD,OAAO,aACL,QAAQ,SAAS,OAAO,WAAW,OAAO,IAAI,CAAC,GAC/C,gBACA,mBACF;IACA,OAAO,aACL,QAAQ,SAAS,MAAM,KAAK,IAAI,SAAS,IAAI,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC,GACjE,SACA,uBACF;IACA,MAAM,WAAW,YAAY,MAAM,GAAG;IACtC,MAAM,WAAW,OAAO,QAAQ,SAAS,QAAQ,KAAK,IAAI,QAAQ;IAClE,OAAO,OAAO,SAAS,WAAW,YAAY,iCAAiC;IAC/E,MAAM,QAAQ,OAAO,QAAQ,SAAS,QAAQ,KAAK,IAAI,QAAQ;IAC/D,OAAO,OACL,MAAM,UAAU,OAAO,SAAS,IAChC,wDACF;IACA,OAAO,aACL,QAAQ,SAAS,QAAQ,KAAK,IAAI,YAAY,MAAM,OAAO,CAAC,GAC5D,SACA,iBACF;IACA,MAAM,aAAa,OAAO,QAAQ,SAAS,WAAW,SAAS,EAAE;IACjE,OAAO,OAAO,WAAW,OAAO,KAAK,IAAI,qCAAqC;IAC9E,MAAM,UAAU,OAAO,QAAQ,SAAS,MAAM,KAAK,IAAI,SAAS,IAAI,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IACxF,OAAO,OACL,QAAQ,WAAW,cAAc,QAAQ,mBAAmB,MAC5D,qBACF;IACA,OAAO,aACL,QAAQ,SAAS,MAAM,KAAK,IAAI,SAAS,IAAI,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC,GACjE,QACA,oBACF;IACA,OAAO,QAAQ,SAAS,KAAK,KAAK,IAAI,eAAe;IACrD,MAAM,SAAS,OAAO,QAAQ,SAAS,IAAI,KAAK,EAAE;IAClD,OAAO,OACL,OAAO,WAAW,YAAY,OAAO,YAAY,iBACjD,iCACF;IACA,MAAM,YAAY,OAAO,QAAQ,SAAS,MACxC,KAAK,IACL,SAAS,SAAS,KAAK,EAAE,SAAS,EAAE,CAAC,CACvC;IACA,OAAO,OAAO,UAAU,YAAY,MAAM,iCAAiC;IAC3E,MAAM,eAAe,OAAO,QAAQ,SAAS,MAC3C,KAAK,IACL,SAAS,IAAI,KAAK,EAAE,SAAS,EAAE,CAAC,CAClC;IACA,OAAO,OAAO,aAAa,WAAW,YAAY,sCAAsC;IACxF,MAAM,UAAU,WAAW,MAAM,UAAU,GAAG;IAC9C,MAAM,YAAY,OAAO,QAAQ,SAAS,SAAS,KAAK,IAAI,OAAO;IACnE,OAAO,OACL,UAAU,WAAW,cAAc,UAAU,mBAAmB,MAChE,kCACF;IACA,OAAO,aACL,QAAQ,SAAS,MAAM,KAAK,IAAI,SAAS,IAAI,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC,GACjE,SACA,wBACF;IACA,MAAM,YAAY,OAAO,QAAQ,SAAS,UAAU,QAAQ,EAAE;IAC9D,OAAO,OAAO,UAAU,SAAS,OAAO,QAAQ,IAAI,oCAAoC;IACxF,MAAM,QAAQ,QAAQ,WAAW,IAAI,SAAS,IAAI,KAAK,EAAE,SAAS,EAAE,CAAC,GAAG,GAAG;IAC3E,OAAO,QAAQ,SAAS,OAAO,WAAW,OAAO,KAAK,CAAC;IACvD,MAAM,SAAS,OAAO,QAAQ,SAAS,OAAO,WAAW,IAAI,cAAc;IAC3E,OAAO,OACL,OAAO,OAAO,MAAM,KAAK,OAAO,MAAM,OAAO,MAAM,IACnD,0CACF;IACA,MAAM,UAAU,OAAO,QAAQ,SAAS,OAAO,WAAW,IAAI,SAAS;IACvE,OAAO,OAAO,OAAO,OAAO,OAAO,GAAG,4BAA4B;IAClE,OAAO,aACL,QAAQ,SAAS,IAAI,KAAK,EAAE,CAAC,CAAC,KAAK,OAAO,eAAeA,SAAmB,KAAK,CAAC,GAClF,YACA,4BACF;GACF,CAAC,CAAC,CAAC,KAAK,OAAO,eAAeA,SAAmB,KAAK,CAAC,CACzD;EACF;EACA;GACE,MAAM;GACN,KAAK,IACH,OAAO,IAAI,aAAa;IACtB,MAAM,EAAE,SAAS,eAAe,OAAO,QAAQ,OAAO,aAAa;IACnE,MAAM,MAAM,OAAO,SAAS;IAC5B,MAAM,OAAO,QAAQ,WAAW,IAAI,KAAK,IAAI;IAC7C,OAAO,QAAQ,SAAS,OAAO,WAAW,OAAO,IAAI,CAAC;IACtD,OAAO,aACL,QAAQ,SAAS,OAAO,KAAK,IAAI;KAC/B,QAAA,OAAoB,KAAK,OAAO;KAChC,SAAS;KACT,QAAQ;IACV,CAAC,GACD,SACA,+BACF;IACA,OAAO,aACL,QAAQ,SACL,OAAO,KAAK,IAAI;KAAE,QAAQ,KAAK;KAAQ,SAAS,MAAM;KAAS,QAAQ;IAAK,CAAC,CAAC,CAC9E,KAAK,OAAO,eAAeA,SAAmB,KAAK,CAAC,GACvD,YACA,yBACF;IACA,MAAM,WAAW,OAAO,QAAQ,SAAS,OAAO,KAAK,IAAI;KACvD,QAAQ,KAAK;KACb,SAAS,MAAM;KACf,QAAQ;IACV,CAAC;IACD,OAAO,OACL,SAAS,WAAW,cAClB,SAAS,WAAW,YAAY,MAAM,WACtC,SAAS,UAAU,WAAW,gBAC9B,SAAS,cAAc,SAAS,UAAU,EAAE,KAAK,SAAS,cAAc,GAAG,GAC7E,mDACF;IACA,MAAM,QAAQ,OAAO,QAAQ,SAAS,OAAO,KAAK,IAAI;KACpD,QAAQ,KAAK;KACb,SAAS;KACT,QAAQ;IACV,CAAC;IACD,OAAO,OACL,MAAM,WAAW,WAAW,cAC5B,uDACF;IACA,OAAO,aACL,QAAQ,SAAS,QAAQ,KAAK,IAAI,YAAY,MAAM,IAAI,CAAC,GACzD,SACA,sBACF;IACA,OAAO,aACL,QAAQ,SAAS,MAAM,KAAK,IAAI,SAAS,IAAI,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC,GACjE,SACA,oBACF;IACA,MAAM,eAAe,QAAQ,WAAW,IAAI,KAAK,IAAI;IACrD,OAAO,QAAQ,SAAS,OAAO,WAAW,OAAO,YAAY,CAAC;IAC9D,OAAO,QAAQ,SAAS,QAAQ,aAAa,IAAI,YAAY,cAAc,IAAI,CAAC;IAChF,OAAO,aACL,QAAQ,SAAS,OAAO,aAAa,IAAI;KACvC,QAAQ,aAAa;KACrB,SAAS;KACT,QAAQ;IACV,CAAC,GACD,SACA,sBACF;IACA,MAAM,cAAc,QAAQ,WAAW,IAAI,KAAK,IAAI;IACpD,OAAO,QAAQ,SAAS,OACtB,IAAII,QAAgB;KAAE,GAAG,WAAW,OAAO,WAAW;KAAG,QAAQ;IAAU,CAAC,CAC9E;IACA,OAAO,aACL,QAAQ,SAAS,OAAO,YAAY,IAAI;KACtC,QAAQ,YAAY;KACpB,SAAS;KACT,QAAQ;IACV,CAAC,GACD,WACA,wBACF;GACF,CAAC,CAAC,CAAC,KAAK,OAAO,eAAeJ,SAAmB,KAAK,CAAC,CACzD;EACF;EACA;GACE,MAAM;GACN,KAAK,IACH,OAAO,IAAI,aAAa;IACtB,MAAM,EAAE,SAAS,YAAY,eAAe,OAAO,QAAQ,OAAO,aAAa;IAC/E,OAAO,QAAQ,YAAY,OAAO,WAAW,EAAE;IAC/C,OAAO,QAAQ,YAAY,OAAO,WAAW,EAAE;IAC/C,MAAM,gBAAgB,OAAO,QAAQ,eAClC,OAAO,eAAe,OAAO,KAAK,IAAI,QAAQ,sBAAsB,CAAC,CAAC,CAAC,CACvE,KAAK,OAAO,IAAI;IACnB,OAAO,OACL,yBAAyB,SACzB,0CACF;IACA,MAAM,UAAU,OAAO,QAAQ,eAAe,IAAI,aAAa;IAC/D,OAAO,OACL,QAAQ,eAAe,WACvB,4CACF;IACA,MAAM,MAAM,OAAO,SAAS;IAC5B,OAAO,aACL,QAAQ,eAAe,OAAO;KAC5B,eAAe,iBAAiB,OAAO,eAAe,GAAG;KACzD,YAAY,cAAc,GAAG;KAC7B,YAAY;IACd,CAAC,GACD,QACA,uCACF;IACA,MAAM,mBAAmB,OAAO,QAAQ,eACrC,yBAAyB,OAAO,IAAI,CAAC,CACrC,KAAK,OAAO,eAAeA,SAAmB,KAAK,CAAC;IACvD,OAAO,OAAO,qBAAqB,GAAG,gCAAgC;IACtE,IAAI,QAAQ;IACZ,MAAM,YAAY,OAAO,QAAQ,eAAe,yBAC9C,OAAO,WAAW,MAAM,SAAS,EAAE,CACrC;IACA,OAAO,OACL,cAAc,KAAK,UAAU,GAC7B,wDACF;IACA,OAAO,aACL,QAAQ,eAAe,IAAI,aAAa,GACxC,YACA,8BACF;IACA,MAAM,QAAQ,OAAO,QAAQ,eAAe,yBAAyB,OAAO,IAAI;IAChF,OAAO,OAAO,UAAU,GAAG,6BAA6B;GAC1D,CAAC,CAAC,CAAC,KAAK,OAAO,eAAeA,SAAmB,KAAK,CAAC,CACzD;EACF;EACA;GACE,MAAM;GACN,KAAK,IACH,OAAO,IAAI,aAAa;IACtB,MAAM,EAAE,SAAS,YAAY,eAAe,OAAO,QAAQ,OAAO,YAAY;IAC9E,MAAM,QAAQ,GAAG,WAAW,KAAK;IACjC,OAAO,OAAO,WAAW,UAAU,OAAO,2CAA2C;IACrF,MAAM,OAAO,OAAO,QAAQ,YAAY,IAAI,WAAW,EAAE;IACzD,OAAO,OAAO,KAAK,UAAU,OAAO,kCAAkC;IACtE,MAAM,WAAW,OAAO,QAAQ,YAAY,SAAS,WAAW,MAAM;IACtE,OAAO,OACL,OAAO,OAAO,QAAQ,KAAK,SAAS,MAAM,UAAU,OACpD,uCACF;IACA,MAAM,SAAS,OAAO,QAAQ,YAAY,KAAK,WAAW,EAAE;IAC5D,OAAO,OACL,OAAO,OAAO,QAAQ,IAAI,UAAU,KAAK,GACzC,mCACF;GACF,CAAC,CAAC,CAAC,KAAK,OAAO,eAAeA,SAAmB,KAAK,CAAC,CACzD;EACF;EACA;GACE,MAAM;GACN,KAAK,IACH,OAAO,IAAI,aAAa;IACtB,MAAM,EAAE,SAAS,YAAY,eAAe,OAAO,QAAQ,OAAO,aAAa;IAC/E,OAAO,aACL,QAAQ,YAAY,OAAO,WAAW,EAAE,GACxC,gBACA,yBACF;IACA,OAAO,aACL,QAAQ,YAAY,OAAO;KACzB,cAAc,WAAW;KACzB,QAAQ,WAAW;KACnB,MAAM,WAAW;KACjB,OAAO,WAAW;KAClB,QAAQ,CAAC;IACX,CAAC,GACD,gBACA,6BACF;IACA,OAAO,QAAQ,YAAY,OAAO,WAAW,EAAE;IAC/C,OAAO,QAAQ,YAAY,OAAO,WAAW,EAAE;IAC/C,OAAO,aACL,QAAQ,YAAY,IAAI,WAAW,EAAE,GACrC,YACA,yBACF;GACF,CAAC,CAAC,CAAC,KAAK,OAAO,eAAeA,SAAmB,KAAK,CAAC,CACzD;EACF;EACA;GACE,MAAM;GACN,KAAK,IACH,OAAO,IAAI,aAAa;IACtB,MAAM,UAAU,OAAOD;IACvB,MAAM,OAAO,OAAO,SAAS,KAAW;IACxC,MAAM,SAAS,OAAO,OAAO,UAC3B,QAAQ,SAAS,kBAAkB,SAAS,MAAM,IAAI,CAAC,CACzD;IACA,OAAO,OAAO;IACd,OAAO,aACL,QAAQ,SAAS,kBAAkB,OAAO,IAAI,GAC9C,QACA,eACF;IACA,MAAM,aAAa,OAAO,QACvB,SAAS,kBAAkB,OAAO,QAAQ,IAAI,CAAC,CAAC,CAChD,KAAK,OAAO,eAAeC,SAAmB,KAAK,CAAC;IACvD,OAAO,OAAO,eAAe,MAAM,+BAA+B;IAClE,OAAO,SAAS,QAAQ,MAAM,KAAA,CAAS;IACvC,OAAO,MAAM,KAAK,MAAM;IACxB,MAAM,QAAQ,OAAO,QAAQ,SAAS,kBAAkB,OAAO,QAAQ,MAAM,CAAC;IAC9E,OAAO,OAAO,UAAU,QAAQ,uBAAuB;IACvD,OAAO,QACJ,SAAS,kBAAkB,OAAO,KAAK,IAAI,QAAQ,QAAQ,CAAC,CAAC,CAAC,CAC9D,KAAK,OAAO,MAAM;IACrB,MAAM,eAAe,OAAO,QAAQ,SAAS,kBAAkB,OAAO,QAAQ,MAAM,CAAC;IACrF,OAAO,OAAO,iBAAiB,QAAQ,uCAAuC;GAChF,CAAC,CAAC,CAAC,KAAK,OAAO,eAAeA,SAAmB,KAAK,CAAC,CACzD;EACF;EACA;GACE,MAAM;GACN,KAAK,IACH,OAAO,IAAI,aAAa;IACtB,MAAM,EAAE,SAAS,eAAe,OAAO,QAAQ,OAAO,YAAY;IAClE,MAAM,MAAM,OAAO,SAAS;IAC5B,MAAM,YAAY,IAAIS,UAAkB;KACtC,QAAQ,WAAW;KACnB,cAAc,WAAW;KACzB,SAAS,MAAM;KACf,SAAS;KACT,cAAc,CACZ;MACE,aAAa;MACb,QAAQN,IAAc;OAAE,MAAM;OAAmB,OAAO;MAAI,CAAC;MAC7D,QAAQ;MACR,UAAU,CAAC;KACb,CACF;KACA,MAAM,CAAC;KACP,cAAc;KACd,WAAW;KACX,aAAa,SAAS,IAAI,KAAK,EAAE,SAAS,GAAG,CAAC;IAChD,CAAC;IACD,OAAO,QAAQ,UAAU,IAAI,SAAS;IACtC,MAAM,SAAS,OAAO,QAAQ,UAAU,IAAI,WAAW,MAAM;IAC7D,OAAO,OACL,OAAO,OAAO,MAAM,KAAK,OAAO,MAAM,YAAY,WAClD,0BACF;IACA,MAAM,UAAU,OAAO,QAAQ,UAC5B,IAAI,WAAW,MAAM,CAAC,CACtB,KAAK,OAAO,eAAeH,SAAmB,KAAK,CAAC;IACvD,OAAO,OAAO,OAAO,OAAO,OAAO,GAAG,gCAAgC;IACtE,OAAO,QAAQ,UAAU,IACvB,IAAIS,UAAkB;KACpB,GAAG;KACH,QAAQ;KACR,cAAc;IAChB,CAAC,CACH;IACA,MAAM,cAAc,OAAO,QAAQ,UAAU,IAAI,0BAA0B;IAC3E,OAAO,OACL,OAAO,OAAO,WAAW,KAAK,YAAY,MAAM,iBAAiB,MACjE,gDACF;IACA,OAAO,aACL,QAAQ,UAAU,IAChB,IAAIA,UAAkB;KAAE,GAAG;KAAW,cAAc;IAAc,CAAC,CACrE,GACA,YACA,sCACF;GACF,CAAC,CAAC,CAAC,KAAK,OAAO,eAAeT,SAAmB,KAAK,CAAC,CACzD;EACF;CACF;AACF;;AAGA,MAAa,WACX,OACA,UAAmB,CAAC,MACI;CACxB,MAAM,MAAM,MAAM,KAAK;CACvB,IAAI,QAAQ,OAAO,KAAA,GACjB,KAAK,MAAM,QAAQ,KAAK,QAAQ,GAAG,KAAK,YAAY,OAAO,WAAW,KAAK,GAAG,CAAC;CAEjF,OAAO;AACT"}
@@ -133,6 +133,7 @@ function makeMemory(options = {}) {
133
133
  connectionId: input.connectionId,
134
134
  domain: input.domain,
135
135
  zone: input.zone,
136
+ label: input.label,
136
137
  target: input.target,
137
138
  createdAt: yield* DateTime.now
138
139
  });
@@ -1 +1 @@
1
- {"version":3,"file":"storage-memory.mjs","names":["Errors.fail","Reason.NotFound","Reason.InvalidInput","Principal.Service","Storage.Authorization","Reason.Busy","Storage.Connection","Storage.Attachment","Reason.Expired","Storage.Attempt","Reason.Stale"],"sources":["../../src/internal/storage-memory.ts"],"sourcesContent":["import { DateTime, Effect, Option, Semaphore } from \"effect\";\n\nimport type * as Approval from \"../Approval.ts\";\nimport * as Errors from \"./error.ts\";\nimport * as Reason from \"../Reason.ts\";\nimport * as Plan from \"../Plan.ts\";\nimport * as Principal from \"../Principal.ts\";\nimport type * as Receipt from \"../Receipt.ts\";\nimport * as Storage from \"../Storage.ts\";\nimport { fresh } from \"./ids.ts\";\n\ninterface State {\n readonly authorizations: Map<string, Storage.Authorization>;\n readonly credentials: Map<string, Storage.Credential>;\n readonly connections: Map<string, Storage.Connection>;\n readonly attachments: Map<string, Storage.Attachment>;\n readonly continuations: Map<string, Storage.Continuation>;\n readonly attempts: Map<string, Storage.Attempt>;\n readonly readiness: Map<string, Storage.Readiness>;\n readonly locks: Set<string>;\n}\n\nconst notFound = (entity: Reason.NotFound[\"entity\"], id: string) =>\n Errors.fail(new Reason.NotFound({ entity, id }));\nconst invalid = (message: string, field?: string) =>\n Errors.fail(new Reason.InvalidInput({ message, ...(field === undefined ? {} : { field }) }));\n\nexport function makeMemory(options: Storage.MemoryOptions = {}): Storage.Interface {\n const state: State = {\n authorizations: new Map(),\n credentials: new Map(),\n connections: new Map(),\n attachments: new Map(),\n continuations: new Map(),\n attempts: new Map(),\n readiness: new Map(),\n locks: new Set(),\n };\n const mutations = Semaphore.makeUnsafe(1);\n const commit = (operation: string) =>\n options.beforeCommit === undefined ? Effect.void : options.beforeCommit(operation);\n\n const read = <A, E, R = never>(\n run: (principal: Principal.Interface) => Effect.Effect<A, E, R>,\n ): Effect.Effect<A, E, Principal.Service | R> => Effect.flatMap(Principal.Service, run);\n const write = <A, E>(\n run: (principal: Principal.Interface) => Effect.Effect<A, E>,\n ): Effect.Effect<A, E, Principal.Service> =>\n Effect.flatMap(Principal.Service, (principal) => mutations.withPermit(run(principal)));\n\n const owned = <Row extends { readonly ownerId: string }>(\n rows: Map<string, Row>,\n principal: Principal.Interface,\n id: string,\n ): Row | undefined => {\n const row = rows.get(id);\n return row !== undefined && row.ownerId === principal.ownerId ? row : undefined;\n };\n const ownedRows = <Row extends { readonly ownerId: string }>(\n rows: Map<string, Row>,\n principal: Principal.Interface,\n ) => [...rows.values()].filter((row) => row.ownerId === principal.ownerId);\n\n const authorization = (principal: Principal.Interface, id: string) =>\n Effect.suspend(() => {\n const row = owned(state.authorizations, principal, id);\n return row === undefined ? notFound(\"authorization\", id) : Effect.succeed(row);\n });\n const connection = (principal: Principal.Interface, id: string) =>\n Effect.suspend(() => {\n const row = owned(state.connections, principal, id);\n return row === undefined ? notFound(\"connection\", id) : Effect.succeed(row);\n });\n const attachment = (principal: Principal.Interface, id: string) =>\n Effect.suspend(() => {\n const row = owned(state.attachments, principal, id);\n return row === undefined ? notFound(\"attachment\", id) : Effect.succeed(row);\n });\n const attempt = (principal: Principal.Interface, id: string) =>\n Effect.suspend(() => {\n const row = owned(state.attempts, principal, id);\n return row === undefined ? notFound(\"plan\", id) : Effect.succeed(row);\n });\n const attemptWhere = (\n principal: Principal.Interface,\n entity: Reason.NotFound[\"entity\"],\n id: string,\n predicate: (row: Storage.Attempt) => boolean,\n ) =>\n Effect.suspend(() => {\n const row = ownedRows(state.attempts, principal).find(predicate);\n return row === undefined ? notFound(entity, id) : Effect.succeed(row);\n });\n\n const finishRevocation = <E, R>(\n principal: Principal.Interface,\n row: Storage.Authorization,\n revoke: Effect.Effect<void, E, R>,\n ): Effect.Effect<void, Errors.DomainKitError | E, R> =>\n Effect.gen(function* () {\n yield* commit(\"authorizations.prepareRevocation\");\n state.authorizations.set(\n row.id,\n new Storage.Authorization({ ...row, revocation: \"pending\" }),\n );\n yield* revoke;\n yield* commit(\"authorizations.completeRevocation\");\n if (owned(state.authorizations, principal, row.id) !== undefined) {\n state.authorizations.delete(row.id);\n state.credentials.delete(row.id);\n }\n });\n\n return {\n authorizations: {\n upsert: ({ authorization: input, credential, expectedId }) =>\n write((principal) =>\n Effect.gen(function* () {\n if (input.ownerId !== principal.ownerId) {\n return yield* invalid(\"Authorization owner does not match the principal\", \"ownerId\");\n }\n if (expectedId !== undefined) {\n const current = yield* authorization(principal, expectedId);\n if (current.revocation !== \"active\") {\n return yield* Errors.fail(new Reason.Busy({ key: `authorization:${expectedId}` }));\n }\n if (input.id !== expectedId) {\n return yield* invalid(\"Authorization id must match expectedId\", \"id\");\n }\n } else if (state.authorizations.has(input.id)) {\n return yield* invalid(`Authorization ${input.id} already exists`, \"id\");\n }\n yield* commit(\"authorizations.upsert\");\n state.authorizations.set(input.id, input);\n state.credentials.set(input.id, credential);\n return input;\n }),\n ),\n get: (id) => read((principal) => authorization(principal, id)),\n credential: (id) =>\n read((principal) =>\n authorization(principal, id).pipe(\n Effect.flatMap(() => {\n const row = state.credentials.get(id);\n return row === undefined ? notFound(\"authorization\", id) : Effect.succeed(row);\n }),\n ),\n ),\n rotate: (id, credential) =>\n write((principal) =>\n Effect.gen(function* () {\n yield* authorization(principal, id);\n yield* commit(\"authorizations.rotate\");\n state.credentials.set(id, credential);\n }),\n ),\n promoteCapabilities: (id, capabilities) =>\n write((principal) =>\n Effect.gen(function* () {\n const row = yield* authorization(principal, id);\n yield* commit(\"authorizations.promoteCapabilities\");\n state.authorizations.set(\n id,\n new Storage.Authorization({\n ...row,\n capabilities: [...new Set([...row.capabilities, ...capabilities])],\n }),\n );\n }),\n ),\n revoke: (id, revoke) =>\n read((principal) =>\n authorization(principal, id).pipe(\n Effect.flatMap((row) => finishRevocation(principal, row, revoke)),\n ),\n ),\n recoverRevocations: (revoke) =>\n read((principal) =>\n Effect.forEach(\n ownedRows(state.authorizations, principal).filter(\n (row) => row.revocation === \"pending\",\n ),\n (row) => finishRevocation(principal, row, revoke(row)),\n ).pipe(Effect.map((finished) => finished.length)),\n ),\n },\n connections: {\n create: (authorizationId) =>\n write((principal) =>\n Effect.gen(function* () {\n yield* authorization(principal, authorizationId);\n const row = new Storage.Connection({\n id: yield* fresh(\"conn\"),\n ownerId: principal.ownerId,\n authorizationId,\n createdAt: yield* DateTime.now,\n });\n yield* commit(\"connections.create\");\n state.connections.set(row.id, row);\n return row;\n }),\n ),\n get: (id) => read((principal) => connection(principal, id)),\n list: (filter) =>\n read((principal) =>\n Effect.sync(() =>\n ownedRows(state.connections, principal).filter(\n (row) =>\n filter?.provider === undefined ||\n state.authorizations.get(row.authorizationId)?.provider === filter.provider,\n ),\n ),\n ),\n remove: (id) =>\n write((principal) =>\n Effect.gen(function* () {\n yield* connection(principal, id);\n if (ownedRows(state.attachments, principal).some((row) => row.connectionId === id)) {\n return yield* invalid(`Connection ${id} still has attachments`, \"connectionId\");\n }\n yield* commit(\"connections.remove\");\n state.connections.delete(id);\n }),\n ),\n },\n attachments: {\n create: (input) =>\n write((principal) =>\n Effect.gen(function* () {\n yield* connection(principal, input.connectionId);\n if (\n ownedRows(state.attachments, principal).some((row) => row.domain === input.domain)\n ) {\n return yield* invalid(`${input.domain} is already attached`, \"domain\");\n }\n const row = new Storage.Attachment({\n id: yield* fresh(\"att\"),\n ownerId: principal.ownerId,\n connectionId: input.connectionId,\n domain: input.domain,\n zone: input.zone,\n target: input.target,\n createdAt: yield* DateTime.now,\n });\n yield* commit(\"attachments.create\");\n state.attachments.set(row.id, row);\n return row;\n }),\n ),\n get: (id) => read((principal) => attachment(principal, id)),\n byDomain: (domain) =>\n read((principal) =>\n Effect.sync(() =>\n Option.fromNullishOr(\n ownedRows(state.attachments, principal).find((row) => row.domain === domain),\n ),\n ),\n ),\n list: (connectionId) =>\n read((principal) =>\n connection(principal, connectionId).pipe(\n Effect.map(() =>\n ownedRows(state.attachments, principal).filter(\n (row) => row.connectionId === connectionId,\n ),\n ),\n ),\n ),\n remove: (id) =>\n write((principal) =>\n Effect.gen(function* () {\n yield* attachment(principal, id);\n yield* commit(\"attachments.remove\");\n state.attachments.delete(id);\n }),\n ),\n },\n continuations: {\n put: (continuation) =>\n write((principal) =>\n Effect.gen(function* () {\n if (continuation.ownerId !== principal.ownerId) {\n return yield* invalid(\"Continuation owner does not match the principal\", \"ownerId\");\n }\n yield* commit(\"continuations.put\");\n state.continuations.set(continuation.id, continuation);\n }),\n ),\n get: (id) =>\n read((principal) =>\n Effect.gen(function* () {\n const row = owned(state.continuations, principal, id);\n if (row === undefined) return yield* notFound(\"continuation\", id);\n const now = yield* DateTime.now;\n if (DateTime.toEpochMillis(row.expiresAt) <= DateTime.toEpochMillis(now)) {\n return yield* Errors.fail(new Reason.Expired({ entity: \"continuation\", id }));\n }\n return row;\n }),\n ),\n consume: (id) =>\n write((principal) =>\n Effect.gen(function* () {\n const row = owned(state.continuations, principal, id);\n if (row === undefined) return yield* notFound(\"continuation\", id);\n yield* commit(\"continuations.consume\");\n state.continuations.delete(id);\n const now = yield* DateTime.now;\n if (DateTime.toEpochMillis(row.expiresAt) <= DateTime.toEpochMillis(now)) {\n return yield* Errors.fail(new Reason.Expired({ entity: \"continuation\", id }));\n }\n return row;\n }),\n ),\n },\n attempts: {\n create: (input) =>\n write((principal) =>\n Effect.gen(function* () {\n if (input.ownerId !== principal.ownerId) {\n return yield* invalid(\"Attempt owner does not match the principal\", \"ownerId\");\n }\n if (state.attempts.has(input.id)) {\n return yield* invalid(`Plan ${input.id} already exists`, \"id\");\n }\n yield* commit(\"attempts.create\");\n state.attempts.set(input.id, input);\n return input;\n }),\n ),\n get: (id) => read((principal) => attempt(principal, id)),\n byApproval: (id) =>\n read((principal) =>\n attemptWhere(principal, \"approval\", id, (row) => row.approval?.id === id),\n ),\n byReceipt: (id) =>\n read((principal) =>\n attemptWhere(principal, \"receipt\", id, (row) => row.receipt?.id === id),\n ),\n latest: (attachmentId, kind) =>\n read((principal) =>\n Effect.sync(() =>\n Option.fromNullishOr(\n ownedRows(state.attempts, principal)\n .filter((row) => row.attachmentId === attachmentId && row.kind === kind)\n .reduce<Storage.Attempt | undefined>(\n (best, row) =>\n best === undefined ||\n DateTime.toEpochMillis(row.plan.createdAt) >=\n DateTime.toEpochMillis(best.plan.createdAt)\n ? row\n : best,\n undefined,\n ),\n ),\n ),\n ),\n approve: (id, approval: Approval.Model) =>\n write((principal) =>\n Effect.gen(function* () {\n const row = yield* attempt(principal, id);\n if (row.approval?.id === approval.id) return row;\n if (row.status !== \"planned\") return yield* stale(row);\n const next = new Storage.Attempt({\n ...row,\n status: \"approved\",\n approval,\n updatedAt: yield* DateTime.now,\n });\n yield* commit(\"attempts.approve\");\n state.attempts.set(id, next);\n return next;\n }),\n ),\n reject: (id, input) =>\n write((principal) =>\n Effect.gen(function* () {\n const row = yield* attempt(principal, id);\n if (row.status === \"rejected\") return row;\n if (row.status === \"expired\") {\n return yield* Errors.fail(new Reason.Expired({ entity: \"plan\", id }));\n }\n if (row.status !== \"planned\" || row.plan.digest !== input.digest)\n return yield* stale(row);\n const now = yield* DateTime.now;\n const next = new Storage.Attempt({\n ...row,\n status: \"rejected\",\n rejection: { actorId: input.actorId, reason: input.reason, at: now },\n updatedAt: now,\n });\n yield* commit(\"attempts.reject\");\n state.attempts.set(id, next);\n return next;\n }),\n ),\n claim: (id, lease) =>\n write((principal) =>\n Effect.gen(function* () {\n const row = yield* attempt(principal, id);\n const now = yield* DateTime.now;\n if (row.status === \"applying\") {\n const held =\n row.leaseExpiresAt !== null &&\n DateTime.toEpochMillis(row.leaseExpiresAt) > DateTime.toEpochMillis(now);\n if (held) {\n return yield* Errors.fail(new Reason.Busy({ key: `apply:${id}` }));\n }\n } else if (row.status !== \"approved\" && row.status !== \"failed\") {\n return yield* stale(row);\n }\n const next = new Storage.Attempt({\n ...row,\n status: \"applying\",\n leaseExpiresAt: lease,\n failure: null,\n updatedAt: now,\n });\n yield* commit(\"attempts.claim\");\n state.attempts.set(id, next);\n return next;\n }),\n ),\n complete: (id, receipt: Receipt.Model) =>\n write((principal) =>\n Effect.gen(function* () {\n const row = yield* attempt(principal, id);\n if (row.status !== \"applying\") return yield* stale(row);\n const next = new Storage.Attempt({\n ...row,\n status: receipt.status,\n receipt,\n leaseExpiresAt: null,\n updatedAt: yield* DateTime.now,\n });\n yield* commit(\"attempts.complete\");\n state.attempts.set(id, next);\n return next;\n }),\n ),\n fail: (id, message) =>\n write((principal) =>\n Effect.gen(function* () {\n const row = yield* attempt(principal, id);\n if (row.status !== \"applying\") return yield* stale(row);\n const next = new Storage.Attempt({\n ...row,\n status: \"failed\",\n failure: message,\n leaseExpiresAt: null,\n updatedAt: yield* DateTime.now,\n });\n yield* commit(\"attempts.fail\");\n state.attempts.set(id, next);\n return next;\n }),\n ),\n },\n readiness: {\n put: (readiness) =>\n write((principal) =>\n Effect.gen(function* () {\n if (readiness.ownerId !== principal.ownerId) {\n return yield* invalid(\"Readiness owner does not match the principal\", \"ownerId\");\n }\n if (readiness.attachmentId !== null)\n yield* attachment(principal, readiness.attachmentId);\n yield* commit(\"readiness.put\");\n state.readiness.set(`${principal.ownerId}:${readiness.domain}`, readiness);\n }),\n ),\n get: (domain) =>\n read((principal) =>\n Effect.sync(() =>\n Option.fromNullishOr(state.readiness.get(`${principal.ownerId}:${domain}`)),\n ),\n ),\n },\n withLock: (key, effect) =>\n Effect.flatMap(Principal.Service, (principal) => {\n const scoped = `${principal.ownerId}:${key}`;\n return Effect.acquireRelease(\n Effect.suspend(() => {\n if (state.locks.has(scoped)) {\n return Errors.fail(new Reason.Busy({ key }));\n }\n state.locks.add(scoped);\n return Effect.void;\n }),\n () => Effect.sync(() => void state.locks.delete(scoped)),\n ).pipe(\n Effect.flatMap(() => effect),\n Effect.scoped,\n );\n }),\n };\n}\n\nconst stale = (row: Storage.Attempt) =>\n Errors.fail(new Reason.Stale({ planId: Plan.PlanId.make(row.id), digest: row.plan.digest }));\n"],"mappings":"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"}
1
+ {"version":3,"file":"storage-memory.mjs","names":["Errors.fail","Reason.NotFound","Reason.InvalidInput","Principal.Service","Storage.Authorization","Reason.Busy","Storage.Connection","Storage.Attachment","Reason.Expired","Storage.Attempt","Reason.Stale"],"sources":["../../src/internal/storage-memory.ts"],"sourcesContent":["import { DateTime, Effect, Option, Semaphore } from \"effect\";\n\nimport type * as Approval from \"../Approval.ts\";\nimport * as Errors from \"./error.ts\";\nimport * as Reason from \"../Reason.ts\";\nimport * as Plan from \"../Plan.ts\";\nimport * as Principal from \"../Principal.ts\";\nimport type * as Receipt from \"../Receipt.ts\";\nimport * as Storage from \"../Storage.ts\";\nimport { fresh } from \"./ids.ts\";\n\ninterface State {\n readonly authorizations: Map<string, Storage.Authorization>;\n readonly credentials: Map<string, Storage.Credential>;\n readonly connections: Map<string, Storage.Connection>;\n readonly attachments: Map<string, Storage.Attachment>;\n readonly continuations: Map<string, Storage.Continuation>;\n readonly attempts: Map<string, Storage.Attempt>;\n readonly readiness: Map<string, Storage.Readiness>;\n readonly locks: Set<string>;\n}\n\nconst notFound = (entity: Reason.NotFound[\"entity\"], id: string) =>\n Errors.fail(new Reason.NotFound({ entity, id }));\nconst invalid = (message: string, field?: string) =>\n Errors.fail(new Reason.InvalidInput({ message, ...(field === undefined ? {} : { field }) }));\n\nexport function makeMemory(options: Storage.MemoryOptions = {}): Storage.Interface {\n const state: State = {\n authorizations: new Map(),\n credentials: new Map(),\n connections: new Map(),\n attachments: new Map(),\n continuations: new Map(),\n attempts: new Map(),\n readiness: new Map(),\n locks: new Set(),\n };\n const mutations = Semaphore.makeUnsafe(1);\n const commit = (operation: string) =>\n options.beforeCommit === undefined ? Effect.void : options.beforeCommit(operation);\n\n const read = <A, E, R = never>(\n run: (principal: Principal.Interface) => Effect.Effect<A, E, R>,\n ): Effect.Effect<A, E, Principal.Service | R> => Effect.flatMap(Principal.Service, run);\n const write = <A, E>(\n run: (principal: Principal.Interface) => Effect.Effect<A, E>,\n ): Effect.Effect<A, E, Principal.Service> =>\n Effect.flatMap(Principal.Service, (principal) => mutations.withPermit(run(principal)));\n\n const owned = <Row extends { readonly ownerId: string }>(\n rows: Map<string, Row>,\n principal: Principal.Interface,\n id: string,\n ): Row | undefined => {\n const row = rows.get(id);\n return row !== undefined && row.ownerId === principal.ownerId ? row : undefined;\n };\n const ownedRows = <Row extends { readonly ownerId: string }>(\n rows: Map<string, Row>,\n principal: Principal.Interface,\n ) => [...rows.values()].filter((row) => row.ownerId === principal.ownerId);\n\n const authorization = (principal: Principal.Interface, id: string) =>\n Effect.suspend(() => {\n const row = owned(state.authorizations, principal, id);\n return row === undefined ? notFound(\"authorization\", id) : Effect.succeed(row);\n });\n const connection = (principal: Principal.Interface, id: string) =>\n Effect.suspend(() => {\n const row = owned(state.connections, principal, id);\n return row === undefined ? notFound(\"connection\", id) : Effect.succeed(row);\n });\n const attachment = (principal: Principal.Interface, id: string) =>\n Effect.suspend(() => {\n const row = owned(state.attachments, principal, id);\n return row === undefined ? notFound(\"attachment\", id) : Effect.succeed(row);\n });\n const attempt = (principal: Principal.Interface, id: string) =>\n Effect.suspend(() => {\n const row = owned(state.attempts, principal, id);\n return row === undefined ? notFound(\"plan\", id) : Effect.succeed(row);\n });\n const attemptWhere = (\n principal: Principal.Interface,\n entity: Reason.NotFound[\"entity\"],\n id: string,\n predicate: (row: Storage.Attempt) => boolean,\n ) =>\n Effect.suspend(() => {\n const row = ownedRows(state.attempts, principal).find(predicate);\n return row === undefined ? notFound(entity, id) : Effect.succeed(row);\n });\n\n const finishRevocation = <E, R>(\n principal: Principal.Interface,\n row: Storage.Authorization,\n revoke: Effect.Effect<void, E, R>,\n ): Effect.Effect<void, Errors.DomainKitError | E, R> =>\n Effect.gen(function* () {\n yield* commit(\"authorizations.prepareRevocation\");\n state.authorizations.set(\n row.id,\n new Storage.Authorization({ ...row, revocation: \"pending\" }),\n );\n yield* revoke;\n yield* commit(\"authorizations.completeRevocation\");\n if (owned(state.authorizations, principal, row.id) !== undefined) {\n state.authorizations.delete(row.id);\n state.credentials.delete(row.id);\n }\n });\n\n return {\n authorizations: {\n upsert: ({ authorization: input, credential, expectedId }) =>\n write((principal) =>\n Effect.gen(function* () {\n if (input.ownerId !== principal.ownerId) {\n return yield* invalid(\"Authorization owner does not match the principal\", \"ownerId\");\n }\n if (expectedId !== undefined) {\n const current = yield* authorization(principal, expectedId);\n if (current.revocation !== \"active\") {\n return yield* Errors.fail(new Reason.Busy({ key: `authorization:${expectedId}` }));\n }\n if (input.id !== expectedId) {\n return yield* invalid(\"Authorization id must match expectedId\", \"id\");\n }\n } else if (state.authorizations.has(input.id)) {\n return yield* invalid(`Authorization ${input.id} already exists`, \"id\");\n }\n yield* commit(\"authorizations.upsert\");\n state.authorizations.set(input.id, input);\n state.credentials.set(input.id, credential);\n return input;\n }),\n ),\n get: (id) => read((principal) => authorization(principal, id)),\n credential: (id) =>\n read((principal) =>\n authorization(principal, id).pipe(\n Effect.flatMap(() => {\n const row = state.credentials.get(id);\n return row === undefined ? notFound(\"authorization\", id) : Effect.succeed(row);\n }),\n ),\n ),\n rotate: (id, credential) =>\n write((principal) =>\n Effect.gen(function* () {\n yield* authorization(principal, id);\n yield* commit(\"authorizations.rotate\");\n state.credentials.set(id, credential);\n }),\n ),\n promoteCapabilities: (id, capabilities) =>\n write((principal) =>\n Effect.gen(function* () {\n const row = yield* authorization(principal, id);\n yield* commit(\"authorizations.promoteCapabilities\");\n state.authorizations.set(\n id,\n new Storage.Authorization({\n ...row,\n capabilities: [...new Set([...row.capabilities, ...capabilities])],\n }),\n );\n }),\n ),\n revoke: (id, revoke) =>\n read((principal) =>\n authorization(principal, id).pipe(\n Effect.flatMap((row) => finishRevocation(principal, row, revoke)),\n ),\n ),\n recoverRevocations: (revoke) =>\n read((principal) =>\n Effect.forEach(\n ownedRows(state.authorizations, principal).filter(\n (row) => row.revocation === \"pending\",\n ),\n (row) => finishRevocation(principal, row, revoke(row)),\n ).pipe(Effect.map((finished) => finished.length)),\n ),\n },\n connections: {\n create: (authorizationId) =>\n write((principal) =>\n Effect.gen(function* () {\n yield* authorization(principal, authorizationId);\n const row = new Storage.Connection({\n id: yield* fresh(\"conn\"),\n ownerId: principal.ownerId,\n authorizationId,\n createdAt: yield* DateTime.now,\n });\n yield* commit(\"connections.create\");\n state.connections.set(row.id, row);\n return row;\n }),\n ),\n get: (id) => read((principal) => connection(principal, id)),\n list: (filter) =>\n read((principal) =>\n Effect.sync(() =>\n ownedRows(state.connections, principal).filter(\n (row) =>\n filter?.provider === undefined ||\n state.authorizations.get(row.authorizationId)?.provider === filter.provider,\n ),\n ),\n ),\n remove: (id) =>\n write((principal) =>\n Effect.gen(function* () {\n yield* connection(principal, id);\n if (ownedRows(state.attachments, principal).some((row) => row.connectionId === id)) {\n return yield* invalid(`Connection ${id} still has attachments`, \"connectionId\");\n }\n yield* commit(\"connections.remove\");\n state.connections.delete(id);\n }),\n ),\n },\n attachments: {\n create: (input) =>\n write((principal) =>\n Effect.gen(function* () {\n yield* connection(principal, input.connectionId);\n if (\n ownedRows(state.attachments, principal).some((row) => row.domain === input.domain)\n ) {\n return yield* invalid(`${input.domain} is already attached`, \"domain\");\n }\n const row = new Storage.Attachment({\n id: yield* fresh(\"att\"),\n ownerId: principal.ownerId,\n connectionId: input.connectionId,\n domain: input.domain,\n zone: input.zone,\n label: input.label,\n target: input.target,\n createdAt: yield* DateTime.now,\n });\n yield* commit(\"attachments.create\");\n state.attachments.set(row.id, row);\n return row;\n }),\n ),\n get: (id) => read((principal) => attachment(principal, id)),\n byDomain: (domain) =>\n read((principal) =>\n Effect.sync(() =>\n Option.fromNullishOr(\n ownedRows(state.attachments, principal).find((row) => row.domain === domain),\n ),\n ),\n ),\n list: (connectionId) =>\n read((principal) =>\n connection(principal, connectionId).pipe(\n Effect.map(() =>\n ownedRows(state.attachments, principal).filter(\n (row) => row.connectionId === connectionId,\n ),\n ),\n ),\n ),\n remove: (id) =>\n write((principal) =>\n Effect.gen(function* () {\n yield* attachment(principal, id);\n yield* commit(\"attachments.remove\");\n state.attachments.delete(id);\n }),\n ),\n },\n continuations: {\n put: (continuation) =>\n write((principal) =>\n Effect.gen(function* () {\n if (continuation.ownerId !== principal.ownerId) {\n return yield* invalid(\"Continuation owner does not match the principal\", \"ownerId\");\n }\n yield* commit(\"continuations.put\");\n state.continuations.set(continuation.id, continuation);\n }),\n ),\n get: (id) =>\n read((principal) =>\n Effect.gen(function* () {\n const row = owned(state.continuations, principal, id);\n if (row === undefined) return yield* notFound(\"continuation\", id);\n const now = yield* DateTime.now;\n if (DateTime.toEpochMillis(row.expiresAt) <= DateTime.toEpochMillis(now)) {\n return yield* Errors.fail(new Reason.Expired({ entity: \"continuation\", id }));\n }\n return row;\n }),\n ),\n consume: (id) =>\n write((principal) =>\n Effect.gen(function* () {\n const row = owned(state.continuations, principal, id);\n if (row === undefined) return yield* notFound(\"continuation\", id);\n yield* commit(\"continuations.consume\");\n state.continuations.delete(id);\n const now = yield* DateTime.now;\n if (DateTime.toEpochMillis(row.expiresAt) <= DateTime.toEpochMillis(now)) {\n return yield* Errors.fail(new Reason.Expired({ entity: \"continuation\", id }));\n }\n return row;\n }),\n ),\n },\n attempts: {\n create: (input) =>\n write((principal) =>\n Effect.gen(function* () {\n if (input.ownerId !== principal.ownerId) {\n return yield* invalid(\"Attempt owner does not match the principal\", \"ownerId\");\n }\n if (state.attempts.has(input.id)) {\n return yield* invalid(`Plan ${input.id} already exists`, \"id\");\n }\n yield* commit(\"attempts.create\");\n state.attempts.set(input.id, input);\n return input;\n }),\n ),\n get: (id) => read((principal) => attempt(principal, id)),\n byApproval: (id) =>\n read((principal) =>\n attemptWhere(principal, \"approval\", id, (row) => row.approval?.id === id),\n ),\n byReceipt: (id) =>\n read((principal) =>\n attemptWhere(principal, \"receipt\", id, (row) => row.receipt?.id === id),\n ),\n latest: (attachmentId, kind) =>\n read((principal) =>\n Effect.sync(() =>\n Option.fromNullishOr(\n ownedRows(state.attempts, principal)\n .filter((row) => row.attachmentId === attachmentId && row.kind === kind)\n .reduce<Storage.Attempt | undefined>(\n (best, row) =>\n best === undefined ||\n DateTime.toEpochMillis(row.plan.createdAt) >=\n DateTime.toEpochMillis(best.plan.createdAt)\n ? row\n : best,\n undefined,\n ),\n ),\n ),\n ),\n approve: (id, approval: Approval.Model) =>\n write((principal) =>\n Effect.gen(function* () {\n const row = yield* attempt(principal, id);\n if (row.approval?.id === approval.id) return row;\n if (row.status !== \"planned\") return yield* stale(row);\n const next = new Storage.Attempt({\n ...row,\n status: \"approved\",\n approval,\n updatedAt: yield* DateTime.now,\n });\n yield* commit(\"attempts.approve\");\n state.attempts.set(id, next);\n return next;\n }),\n ),\n reject: (id, input) =>\n write((principal) =>\n Effect.gen(function* () {\n const row = yield* attempt(principal, id);\n if (row.status === \"rejected\") return row;\n if (row.status === \"expired\") {\n return yield* Errors.fail(new Reason.Expired({ entity: \"plan\", id }));\n }\n if (row.status !== \"planned\" || row.plan.digest !== input.digest)\n return yield* stale(row);\n const now = yield* DateTime.now;\n const next = new Storage.Attempt({\n ...row,\n status: \"rejected\",\n rejection: { actorId: input.actorId, reason: input.reason, at: now },\n updatedAt: now,\n });\n yield* commit(\"attempts.reject\");\n state.attempts.set(id, next);\n return next;\n }),\n ),\n claim: (id, lease) =>\n write((principal) =>\n Effect.gen(function* () {\n const row = yield* attempt(principal, id);\n const now = yield* DateTime.now;\n if (row.status === \"applying\") {\n const held =\n row.leaseExpiresAt !== null &&\n DateTime.toEpochMillis(row.leaseExpiresAt) > DateTime.toEpochMillis(now);\n if (held) {\n return yield* Errors.fail(new Reason.Busy({ key: `apply:${id}` }));\n }\n } else if (row.status !== \"approved\" && row.status !== \"failed\") {\n return yield* stale(row);\n }\n const next = new Storage.Attempt({\n ...row,\n status: \"applying\",\n leaseExpiresAt: lease,\n failure: null,\n updatedAt: now,\n });\n yield* commit(\"attempts.claim\");\n state.attempts.set(id, next);\n return next;\n }),\n ),\n complete: (id, receipt: Receipt.Model) =>\n write((principal) =>\n Effect.gen(function* () {\n const row = yield* attempt(principal, id);\n if (row.status !== \"applying\") return yield* stale(row);\n const next = new Storage.Attempt({\n ...row,\n status: receipt.status,\n receipt,\n leaseExpiresAt: null,\n updatedAt: yield* DateTime.now,\n });\n yield* commit(\"attempts.complete\");\n state.attempts.set(id, next);\n return next;\n }),\n ),\n fail: (id, message) =>\n write((principal) =>\n Effect.gen(function* () {\n const row = yield* attempt(principal, id);\n if (row.status !== \"applying\") return yield* stale(row);\n const next = new Storage.Attempt({\n ...row,\n status: \"failed\",\n failure: message,\n leaseExpiresAt: null,\n updatedAt: yield* DateTime.now,\n });\n yield* commit(\"attempts.fail\");\n state.attempts.set(id, next);\n return next;\n }),\n ),\n },\n readiness: {\n put: (readiness) =>\n write((principal) =>\n Effect.gen(function* () {\n if (readiness.ownerId !== principal.ownerId) {\n return yield* invalid(\"Readiness owner does not match the principal\", \"ownerId\");\n }\n if (readiness.attachmentId !== null)\n yield* attachment(principal, readiness.attachmentId);\n yield* commit(\"readiness.put\");\n state.readiness.set(`${principal.ownerId}:${readiness.domain}`, readiness);\n }),\n ),\n get: (domain) =>\n read((principal) =>\n Effect.sync(() =>\n Option.fromNullishOr(state.readiness.get(`${principal.ownerId}:${domain}`)),\n ),\n ),\n },\n withLock: (key, effect) =>\n Effect.flatMap(Principal.Service, (principal) => {\n const scoped = `${principal.ownerId}:${key}`;\n return Effect.acquireRelease(\n Effect.suspend(() => {\n if (state.locks.has(scoped)) {\n return Errors.fail(new Reason.Busy({ key }));\n }\n state.locks.add(scoped);\n return Effect.void;\n }),\n () => Effect.sync(() => void state.locks.delete(scoped)),\n ).pipe(\n Effect.flatMap(() => effect),\n Effect.scoped,\n );\n }),\n };\n}\n\nconst stale = (row: Storage.Attempt) =>\n Errors.fail(new Reason.Stale({ planId: Plan.PlanId.make(row.id), digest: row.plan.digest }));\n"],"mappings":"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"}
package/dist/package.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.11.0";
2
+ var version = "0.12.0";
3
3
  //#endregion
4
4
  export { version };
5
5
 
@@ -412,8 +412,39 @@ export declare const Discovery: {
412
412
  }>;
413
413
  }, "_tag">;
414
414
  };
415
+ /** One zone a connection reaches, with the connection that reaches it. */
416
+ export interface Zone {
417
+ readonly connectionId: string;
418
+ readonly provider: string;
419
+ readonly target: Provider.Target;
420
+ }
421
+ /**
422
+ * A connection's standing in a zone listing. `reconnect` is a credential the provider turned down:
423
+ * the connection is still the owner's, and the other connections' zones came back regardless.
424
+ */
425
+ export interface ZoneConnection {
426
+ readonly connectionId: string;
427
+ readonly provider: string;
428
+ readonly status: "connected" | "reconnect";
429
+ }
430
+ /** Every zone the principal's connections reach, and how each connection is standing. */
431
+ export interface Zones {
432
+ /** Ordered by zone, then by connection, so two listings of the same state read the same. */
433
+ readonly zones: ReadonlyArray<Zone>;
434
+ readonly connections: ReadonlyArray<ZoneConnection>;
435
+ /** Every provider a customer could connect from here, so an offer needs no second call. */
436
+ readonly providers: Snapshot["providers"];
437
+ }
415
438
  export interface Interface {
416
439
  readonly inspect: (domain: string) => Fx<Snapshot>;
440
+ /**
441
+ * Every zone the principal's connections can serve, from `Provider.Session.listTargets`. A
442
+ * connection whose credential the provider turned down is marked `reconnect` in `connections`
443
+ * and contributes no zones; the rest still list.
444
+ */
445
+ readonly zones: (options?: {
446
+ readonly provider?: string;
447
+ }) => Fx<Zones>;
417
448
  /**
418
449
  * Resolve the domain's authoritative nameservers, then match them against the zones each of
419
450
  * the principal's connections can reach: the closest zone wins, a decisive nameserver match
@@ -427,6 +458,18 @@ export interface Interface {
427
458
  readonly domain?: string;
428
459
  readonly callbackUrl?: string;
429
460
  }) => Fx<Started>;
461
+ /**
462
+ * Replace the credential behind a connection the owner already holds, keeping the connection and
463
+ * every domain attached to it. This is what a customer means by reconnecting: starting again
464
+ * would mint a second account and leave the domains on the one the provider turned down. The
465
+ * provider is the connection's own; a credential for a different account is the customer's
466
+ * choice, and the zone check on every session catches a zone it can no longer reach.
467
+ */
468
+ readonly reconnect: (input: {
469
+ readonly connectionId: string;
470
+ readonly method: Method;
471
+ readonly callbackUrl?: string;
472
+ }) => Fx<Started>;
430
473
  /** Finish an interactive connection from the provider callback (the full callback URL). */
431
474
  readonly complete: (input: {
432
475
  readonly continuationId: string;
@@ -470,6 +513,9 @@ export declare class Policy extends Policy_base {
470
513
  export declare const make: Effect.Effect<Interface, never, Storage.Service | Custody.Service | Providers.Service | Resolver.Service>;
471
514
  export declare const layer: Layer.Layer<Service, never, Storage.Service | Custody.Service | Providers.Service | Resolver.Service>;
472
515
  export declare const inspect: (domain: string) => Effect.Effect<Snapshot, Errors.DomainKitError, Principal.Service | Service>;
516
+ export declare const zones: (options?: {
517
+ readonly provider?: string;
518
+ } | undefined) => Effect.Effect<Zones, Errors.DomainKitError, Principal.Service | Service>;
473
519
  export declare const discover: (domain: string) => Effect.Effect<{
474
520
  readonly _tag: "NotFound";
475
521
  readonly nameservers: ReadonlyArray<string>;
@@ -505,6 +551,23 @@ export declare const start: (input: {
505
551
  readonly connection: Storage.Connection;
506
552
  readonly candidates: ReadonlyArray<Provider.Target>;
507
553
  }, Errors.DomainKitError, Principal.Service | Service>;
554
+ export declare const reconnect: (input: {
555
+ readonly connectionId: string;
556
+ readonly method: Method;
557
+ readonly callbackUrl?: string;
558
+ }) => Effect.Effect<{
559
+ readonly _tag: "Connected";
560
+ readonly connection: Storage.Connection;
561
+ readonly attachment: Storage.Attachment | null;
562
+ } | {
563
+ readonly _tag: "Redirect";
564
+ readonly authorizationUrl: string;
565
+ readonly continuationId: string;
566
+ } | {
567
+ readonly _tag: "SelectionRequired";
568
+ readonly connection: Storage.Connection;
569
+ readonly candidates: ReadonlyArray<Provider.Target>;
570
+ }, Errors.DomainKitError, Principal.Service | Service>;
508
571
  export declare const complete: (input: {
509
572
  readonly continuationId: string;
510
573
  readonly callbackUrl: string;