domainkit 0.15.0 → 0.17.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.
- package/README.md +11 -4
- package/dist/Custody.mjs +1 -1
- package/dist/Custody.mjs.map +1 -1
- package/dist/DomainName.mjs +1 -1
- package/dist/DomainName.mjs.map +1 -1
- package/dist/Provision.mjs +209 -19
- package/dist/Provision.mjs.map +1 -1
- package/dist/Reason.mjs +22 -2
- package/dist/Reason.mjs.map +1 -1
- package/dist/Server.mjs +86 -4
- package/dist/Server.mjs.map +1 -1
- package/dist/Storage.mjs +100 -2
- package/dist/Storage.mjs.map +1 -1
- package/dist/internal/attempts.mjs +14 -4
- package/dist/internal/attempts.mjs.map +1 -1
- package/dist/internal/conformance/storage.mjs +183 -0
- package/dist/internal/conformance/storage.mjs.map +1 -1
- package/dist/internal/error.mjs +2 -0
- package/dist/internal/error.mjs.map +1 -1
- package/dist/internal/storage-memory.mjs +185 -2
- package/dist/internal/storage-memory.mjs.map +1 -1
- package/dist/package.mjs +1 -1
- package/dist/types/Provision.d.ts +124 -3
- package/dist/types/Reason.d.ts +18 -2
- package/dist/types/Server.d.ts +3619 -202
- package/dist/types/Storage.d.ts +176 -0
- package/dist/types/internal/attempts.d.ts +1 -0
- package/dist/types/internal/error.d.ts +1 -1
- package/package.json +6 -5
|
@@ -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.ContinuationHeader","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 header: (id) =>\n Effect.gen(function* () {\n // Unscoped on purpose, unlike every other read here: the provider callback has to learn\n // whose flow it is finishing before it can resolve a principal to scope by. Header\n // fields only; `payload` stays behind `get`.\n const row = state.continuations.get(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 new Storage.ContinuationHeader({\n ownerId: row.ownerId,\n actorId: row.actorId,\n provider: row.provider,\n expiresAt: row.expiresAt,\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","Reason.BatchStale","Storage.batchStatusOf","Storage.Batch","Storage.Authorization","Reason.Busy","Storage.Connection","Storage.Attachment","Reason.Expired","Storage.ContinuationHeader","Storage.Attempt","Storage.BatchItem","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 batches: Map<string, Storage.Batch>;\n /** Items by batch id, kept in `position` order. */\n readonly batchItems: Map<string, Array<Storage.BatchItem>>;\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 batches: new Map(),\n batchItems: 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 batch = (principal: Principal.Interface, id: string) =>\n Effect.suspend(() => {\n const row = owned(state.batches, principal, id);\n return row === undefined ? notFound(\"batch\", id) : Effect.succeed(row);\n });\n\n const itemsOf = (batchId: string): ReadonlyArray<Storage.BatchItem> =>\n [...(state.batchItems.get(batchId) ?? [])].sort(\n (left, right) => left.position - right.position,\n );\n\n const aggregateOf = (row: Storage.Batch): Storage.BatchAggregate => ({\n batch: row,\n items: itemsOf(row.id),\n });\n\n const batchStale = (row: Storage.Batch) =>\n Errors.fail(new Reason.BatchStale({ batchId: row.id, status: row.status, digest: row.digest }));\n\n const statusOf = (row: Storage.Batch, items: ReadonlyArray<Storage.BatchItem>) =>\n Storage.batchStatusOf({\n approved: row.approval !== null,\n rejected: row.rejection !== null,\n items: items.map((item) =>\n item.attemptId === null ? null : (state.attempts.get(item.attemptId)?.status ?? null),\n ),\n });\n\n /** Store `row` with its status recomputed from the items' attempts, and return the aggregate. */\n const settle = (row: Storage.Batch, now: DateTime.Utc): Storage.BatchAggregate => {\n const items = itemsOf(row.id);\n const status = statusOf(row, items);\n const next = new Storage.Batch({\n ...row,\n status,\n completedAt: status === \"complete\" || status === \"rejected\" ? now : null,\n updatedAt: now,\n });\n state.batches.set(row.id, next);\n return { batch: next, items };\n };\n\n const replaceItem = (item: Storage.BatchItem) => {\n const items = state.batchItems.get(item.batchId) ?? [];\n const index = items.findIndex((row) => row.attachmentId === item.attachmentId);\n if (index >= 0) items[index] = item;\n };\n\n /** Only a batch still being planned accepts plan state, an approval, or a rejection. */\n const openForPlanning = (row: Storage.Batch) =>\n row.status === \"planning\" || row.status === \"planned\" ? Effect.void : batchStale(row);\n\n const itemAt = (row: Storage.Batch, attachmentId: string) =>\n Effect.suspend(() => {\n const item = itemsOf(row.id).find((entry) => entry.attachmentId === attachmentId);\n return item === undefined ? notFound(\"attachment\", attachmentId) : Effect.succeed(item);\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 header: (id) =>\n Effect.gen(function* () {\n // Unscoped on purpose, unlike every other read here: the provider callback has to learn\n // whose flow it is finishing before it can resolve a principal to scope by. Header\n // fields only; `payload` stays behind `get`.\n const row = state.continuations.get(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 new Storage.ContinuationHeader({\n ownerId: row.ownerId,\n actorId: row.actorId,\n provider: row.provider,\n expiresAt: row.expiresAt,\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 batches: {\n create: (input) =>\n write((principal) =>\n Effect.gen(function* () {\n const replay = ownedRows(state.batches, principal).find(\n (row) => row.idempotencyKey === input.idempotencyKey,\n );\n if (replay !== undefined) return aggregateOf(replay);\n if (input.attachmentIds.length === 0) {\n return yield* invalid(\"A batch needs at least one attachment\", \"attachmentIds\");\n }\n if (new Set(input.attachmentIds).size !== input.attachmentIds.length) {\n return yield* invalid(\"A batch holds one item per attachment\", \"attachmentIds\");\n }\n for (const attachmentId of input.attachmentIds) {\n yield* attachment(principal, attachmentId);\n }\n const now = yield* DateTime.now;\n const id = Storage.BatchId.make(yield* fresh(\"batch\"));\n const row = new Storage.Batch({\n id,\n ownerId: principal.ownerId,\n kind: input.kind,\n status: \"planning\",\n digest: null,\n approval: null,\n rejection: null,\n idempotencyKey: input.idempotencyKey,\n createdBy: principal.actorId,\n createdAt: now,\n updatedAt: now,\n completedAt: null,\n });\n yield* commit(\"batches.create\");\n state.batches.set(id, row);\n state.batchItems.set(\n id,\n input.attachmentIds.map(\n (attachmentId, position) =>\n new Storage.BatchItem({\n batchId: id,\n attachmentId,\n position,\n attemptId: null,\n planFailure: null,\n }),\n ),\n );\n return aggregateOf(row);\n }),\n ),\n get: (id) => read((principal) => Effect.map(batch(principal, id), aggregateOf)),\n byIdempotencyKey: (key) =>\n read((principal) =>\n Effect.sync(() =>\n Option.map(\n Option.fromNullishOr(\n ownedRows(state.batches, principal).find((row) => row.idempotencyKey === key),\n ),\n aggregateOf,\n ),\n ),\n ),\n listUnfinished: () =>\n read((principal) =>\n Effect.sync(() =>\n ownedRows(state.batches, principal)\n .filter((row) => row.status !== \"complete\" && row.status !== \"rejected\")\n .sort(\n (left, right) =>\n DateTime.toEpochMillis(right.updatedAt) - DateTime.toEpochMillis(left.updatedAt),\n )\n .map(aggregateOf),\n ),\n ),\n recordItemPlan: (id, attachmentId, attemptId) =>\n write((principal) =>\n Effect.gen(function* () {\n const row = yield* batch(principal, id);\n yield* openForPlanning(row);\n const item = yield* itemAt(row, attachmentId);\n const planned = yield* attempt(principal, attemptId);\n if (planned.attachmentId !== attachmentId || planned.kind !== row.kind) {\n return yield* invalid(\n `Plan ${attemptId} does not belong to attachment ${attachmentId} in this batch`,\n \"attemptId\",\n );\n }\n yield* commit(\"batches.recordItemPlan\");\n replaceItem(new Storage.BatchItem({ ...item, attemptId, planFailure: null }));\n return settle(row, yield* DateTime.now);\n }),\n ),\n recordItemPlanFailure: (id, attachmentId, message) =>\n write((principal) =>\n Effect.gen(function* () {\n const row = yield* batch(principal, id);\n yield* openForPlanning(row);\n const item = yield* itemAt(row, attachmentId);\n yield* commit(\"batches.recordItemPlanFailure\");\n replaceItem(new Storage.BatchItem({ ...item, attemptId: null, planFailure: message }));\n return settle(row, yield* DateTime.now);\n }),\n ),\n approve: (id, input) =>\n write((principal) =>\n Effect.gen(function* () {\n const row = yield* batch(principal, id);\n // Replaying the same approval is how a retried request stays safe.\n if (row.approval !== null) {\n return row.digest === input.digest ? aggregateOf(row) : yield* batchStale(row);\n }\n if (row.status !== \"planned\") return yield* batchStale(row);\n const items = itemsOf(id);\n const pairs: Array<{\n readonly attempt: Storage.Attempt;\n readonly approval: Approval.Model;\n }> = [];\n for (const item of items) {\n const supplied = input.approvals.find(\n (entry) => entry.attachmentId === item.attachmentId,\n );\n if (supplied === undefined) {\n return yield* invalid(\n `Batch ${id} has no approval for attachment ${item.attachmentId}`,\n \"approvals\",\n );\n }\n // The item has to still point at the attempt this approval names: anything else\n // means the batch was re-planned after the principal reviewed it.\n const stored =\n item.attemptId === null ? undefined : state.attempts.get(item.attemptId);\n if (stored === undefined || stored.id !== supplied.approval.planId) {\n return yield* batchStale(row);\n }\n // The attempt has to still be open: one declined or applied through the\n // single-domain API is not something a batch approval may reopen.\n if (stored.approval?.id !== supplied.approval.id && stored.status !== \"planned\") {\n return yield* stale(stored);\n }\n pairs.push({ attempt: stored, approval: supplied.approval });\n }\n const now = yield* DateTime.now;\n yield* commit(\"batches.approve\");\n for (const { attempt: stored, approval } of pairs) {\n if (stored.approval?.id === approval.id) continue;\n state.attempts.set(\n stored.id,\n new Storage.Attempt({\n ...stored,\n status: \"approved\",\n approval,\n updatedAt: now,\n }),\n );\n }\n return settle(\n new Storage.Batch({\n ...row,\n digest: input.digest,\n approval: { actorId: input.actorId, at: now },\n }),\n now,\n );\n }),\n ),\n reject: (id, input) =>\n write((principal) =>\n Effect.gen(function* () {\n const row = yield* batch(principal, id);\n // Terminal: a second rejection is the same outcome, so it returns the first one.\n if (row.rejection !== null) return aggregateOf(row);\n yield* openForPlanning(row);\n const now = yield* DateTime.now;\n yield* commit(\"batches.reject\");\n for (const item of itemsOf(id)) {\n const stored =\n item.attemptId === null ? undefined : state.attempts.get(item.attemptId);\n if (stored === undefined || stored.status !== \"planned\") continue;\n state.attempts.set(\n stored.id,\n new Storage.Attempt({\n ...stored,\n status: \"rejected\",\n rejection: { actorId: input.actorId, reason: input.reason, at: now },\n updatedAt: now,\n }),\n );\n }\n return settle(\n new Storage.Batch({\n ...row,\n rejection: { actorId: input.actorId, reason: input.reason, at: now },\n }),\n now,\n );\n }),\n ),\n refresh: (id) =>\n write((principal) =>\n Effect.gen(function* () {\n const row = yield* batch(principal, id);\n yield* commit(\"batches.refresh\");\n return settle(row, yield* DateTime.now);\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
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
* plan in one request, collect consent in another, and apply in a third, and a retry of any step
|
|
4
4
|
* is idempotent.
|
|
5
5
|
*/
|
|
6
|
-
import { Context, Effect, Layer } from "effect";
|
|
6
|
+
import { Context, type DateTime, Effect, Layer } from "effect";
|
|
7
7
|
import type * as Approval from "./Approval.ts";
|
|
8
8
|
import * as Connect from "./Connect.ts";
|
|
9
9
|
import type * as DnsRecord from "./DnsRecord.ts";
|
|
10
10
|
import * as Errors from "./internal/error.ts";
|
|
11
11
|
import * as Attempts from "./internal/attempts.ts";
|
|
12
|
-
import
|
|
13
|
-
import
|
|
12
|
+
import * as Plan from "./Plan.ts";
|
|
13
|
+
import * as Principal from "./Principal.ts";
|
|
14
14
|
import type * as Receipt from "./Receipt.ts";
|
|
15
15
|
import * as Storage from "./Storage.ts";
|
|
16
16
|
type Fx<A> = Effect.Effect<A, Errors.DomainKitError, Principal.Service>;
|
|
@@ -24,6 +24,101 @@ export interface Attempt {
|
|
|
24
24
|
export interface RejectOptions {
|
|
25
25
|
readonly reason?: string;
|
|
26
26
|
}
|
|
27
|
+
/** One domain to plan inside a batch. The domain must already be attached. */
|
|
28
|
+
export interface BatchInput {
|
|
29
|
+
readonly domain: string;
|
|
30
|
+
readonly requirements: ReadonlyArray<DnsRecord.Model>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* One domain's place in a batch, with whatever its attempt holds.
|
|
34
|
+
*
|
|
35
|
+
* Everything but `planFailure` is read from the attempt the item points at, so a batch never
|
|
36
|
+
* shows a customer a plan, receipt, or failure that disagrees with the attempt it came from.
|
|
37
|
+
*/
|
|
38
|
+
export interface BatchItem {
|
|
39
|
+
readonly attachmentId: string;
|
|
40
|
+
readonly position: number;
|
|
41
|
+
/** Null while the item has no plan; `planFailure` then says why. */
|
|
42
|
+
readonly plan: Plan.Model | null;
|
|
43
|
+
readonly status: Storage.AttemptStatus | null;
|
|
44
|
+
readonly approval: Approval.Model | null;
|
|
45
|
+
readonly receipt: Receipt.Model | null;
|
|
46
|
+
readonly rejection: Storage.Rejection | null;
|
|
47
|
+
/** Why the last apply stopped before any write. */
|
|
48
|
+
readonly failure: string | null;
|
|
49
|
+
/** Why the last planning pass could not build this item's plan. */
|
|
50
|
+
readonly planFailure: string | null;
|
|
51
|
+
}
|
|
52
|
+
/** Many domains planned together, approved once, and applied with bounded concurrency. */
|
|
53
|
+
export interface Batch {
|
|
54
|
+
readonly id: Storage.BatchId;
|
|
55
|
+
readonly status: Storage.BatchStatus;
|
|
56
|
+
/**
|
|
57
|
+
* What `approve` binds: SHA-256 over the items' sorted `attachmentId:planDigest` pairs. The
|
|
58
|
+
* digest the batch was approved at once it is approved, the digest its current plans produce
|
|
59
|
+
* before that, and null while any item is still unplanned.
|
|
60
|
+
*/
|
|
61
|
+
readonly digest: Plan.Digest | null;
|
|
62
|
+
readonly approval: Storage.BatchApproval | null;
|
|
63
|
+
readonly rejection: Storage.Rejection | null;
|
|
64
|
+
readonly createdAt: DateTime.Utc;
|
|
65
|
+
readonly updatedAt: DateTime.Utc;
|
|
66
|
+
readonly completedAt: DateTime.Utc | null;
|
|
67
|
+
readonly items: ReadonlyArray<BatchItem>;
|
|
68
|
+
}
|
|
69
|
+
/** A batch without its plans, for the index a host renders as a banner. */
|
|
70
|
+
export interface BatchSummary {
|
|
71
|
+
readonly id: Storage.BatchId;
|
|
72
|
+
readonly status: Storage.BatchStatus;
|
|
73
|
+
readonly itemCount: number;
|
|
74
|
+
readonly createdAt: DateTime.Utc;
|
|
75
|
+
readonly updatedAt: DateTime.Utc;
|
|
76
|
+
readonly completedAt: DateTime.Utc | null;
|
|
77
|
+
}
|
|
78
|
+
export interface BatchInterface {
|
|
79
|
+
/**
|
|
80
|
+
* Plan every domain in `items` as one batch, with `Policy.batchConcurrency` in flight.
|
|
81
|
+
*
|
|
82
|
+
* `idempotencyKey` is unique per owner: a retried create returns the batch the first call made,
|
|
83
|
+
* plans whatever is still unplanned, and ignores items that batch does not hold. An item whose
|
|
84
|
+
* plan fails records the reason and leaves the batch `planning` for `resumePlanning`.
|
|
85
|
+
*/
|
|
86
|
+
readonly create: (input: {
|
|
87
|
+
readonly items: ReadonlyArray<BatchInput>;
|
|
88
|
+
readonly idempotencyKey: string;
|
|
89
|
+
}) => Fx<Batch>;
|
|
90
|
+
/**
|
|
91
|
+
* Plan the items that still have none, with the requirements supplied again. A batch stores
|
|
92
|
+
* pointers, not requirements, so the host owns them across the retry.
|
|
93
|
+
*/
|
|
94
|
+
readonly resumePlanning: (id: Storage.BatchId, input: {
|
|
95
|
+
readonly items: ReadonlyArray<BatchInput>;
|
|
96
|
+
}) => Fx<Batch>;
|
|
97
|
+
/**
|
|
98
|
+
* Record the principal's consent to the whole batch. Fails `BatchStale` unless `digest` is the
|
|
99
|
+
* one the batch's current plans produce. Approving an approved batch at its digest returns it.
|
|
100
|
+
*/
|
|
101
|
+
readonly approve: (id: Storage.BatchId, options: {
|
|
102
|
+
readonly digest: Plan.Digest;
|
|
103
|
+
}) => Fx<Batch>;
|
|
104
|
+
/**
|
|
105
|
+
* Apply every approved item that has no receipt yet, with `Policy.batchConcurrency` in flight
|
|
106
|
+
* and each item under its own attempt lease. An item another apply holds is skipped this round,
|
|
107
|
+
* and an item that fails records its failure on its attempt rather than stopping the others, so
|
|
108
|
+
* calling `apply` again resumes the batch. Applying a complete batch returns it unchanged.
|
|
109
|
+
*/
|
|
110
|
+
readonly apply: (id: Storage.BatchId) => Fx<Batch>;
|
|
111
|
+
/**
|
|
112
|
+
* Decline the batch and every plan under it; terminal. Rejecting again returns the same batch;
|
|
113
|
+
* a batch that was approved fails `BatchStale`.
|
|
114
|
+
*/
|
|
115
|
+
readonly reject: (id: Storage.BatchId, options?: RejectOptions) => Fx<Batch>;
|
|
116
|
+
readonly get: (id: Storage.BatchId) => Fx<Batch>;
|
|
117
|
+
/** Every batch this owner still owes a move on, most recently touched first. */
|
|
118
|
+
readonly list: (filter: {
|
|
119
|
+
readonly unfinished: true;
|
|
120
|
+
}) => Fx<ReadonlyArray<BatchSummary>>;
|
|
121
|
+
}
|
|
27
122
|
export interface Interface {
|
|
28
123
|
/** Read provider state and build an additive plan. Fails `NotFound` when the domain is not attached. */
|
|
29
124
|
readonly plan: (input: {
|
|
@@ -47,6 +142,8 @@ export interface Interface {
|
|
|
47
142
|
readonly apply: (approval: Approval.Model | Approval.ApprovalId) => Fx<Receipt.Model>;
|
|
48
143
|
readonly get: (planId: Plan.PlanId) => Fx<Attempt>;
|
|
49
144
|
readonly latest: (domain: string) => Fx<Attempt | null>;
|
|
145
|
+
/** Many domains as one reviewable unit: plan together, approve once, apply bounded. */
|
|
146
|
+
readonly batch: BatchInterface;
|
|
50
147
|
}
|
|
51
148
|
declare const Service_base: Context.ServiceClass<Service, "@domainkit/Provision", Interface>;
|
|
52
149
|
export declare class Service extends Service_base {
|
|
@@ -56,6 +153,8 @@ export interface PolicyShape {
|
|
|
56
153
|
readonly planTtlMs: number;
|
|
57
154
|
/** Apply lease; a crashed apply can be retried after this. Default 2 minutes. */
|
|
58
155
|
readonly applyLeaseMs: number;
|
|
156
|
+
/** How many of a batch's domains are planned or applied at once. Default 4. */
|
|
157
|
+
readonly batchConcurrency: number;
|
|
59
158
|
}
|
|
60
159
|
export declare const defaults: PolicyShape;
|
|
61
160
|
declare const Policy_base: Context.Reference<PolicyShape>;
|
|
@@ -72,4 +171,26 @@ export declare const reject: (plan: Plan.Model | (string & import("effect/Brand"
|
|
|
72
171
|
export declare const apply: (approval: Approval.Model | (string & import("effect/Brand").Brand<"@domainkit/ApprovalId">)) => Effect.Effect<Receipt.Model, Errors.DomainKitError, Principal.Service | Service>;
|
|
73
172
|
export declare const get: (planId: string & import("effect/Brand").Brand<"@domainkit/PlanId">) => Effect.Effect<Attempt, Errors.DomainKitError, Principal.Service | Service>;
|
|
74
173
|
export declare const latest: (domain: string) => Effect.Effect<Attempt | null, Errors.DomainKitError, Principal.Service | Service>;
|
|
174
|
+
/**
|
|
175
|
+
* Many domains as one reviewable unit. Every operation needs `Provision.Service` and `Principal`,
|
|
176
|
+
* exactly like the single-domain accessors above.
|
|
177
|
+
*/
|
|
178
|
+
export declare const batch: {
|
|
179
|
+
create: (input: {
|
|
180
|
+
readonly items: ReadonlyArray<BatchInput>;
|
|
181
|
+
readonly idempotencyKey: string;
|
|
182
|
+
}) => Effect.Effect<Batch, Errors.DomainKitError, Principal.Service | Service>;
|
|
183
|
+
resumePlanning: (id: string & import("effect/Brand").Brand<"@domainkit/BatchId">, input: {
|
|
184
|
+
readonly items: ReadonlyArray<BatchInput>;
|
|
185
|
+
}) => Effect.Effect<Batch, Errors.DomainKitError, Principal.Service | Service>;
|
|
186
|
+
approve: (id: string & import("effect/Brand").Brand<"@domainkit/BatchId">, options: {
|
|
187
|
+
readonly digest: Plan.Digest;
|
|
188
|
+
}) => Effect.Effect<Batch, Errors.DomainKitError, Principal.Service | Service>;
|
|
189
|
+
apply: (id: string & import("effect/Brand").Brand<"@domainkit/BatchId">) => Effect.Effect<Batch, Errors.DomainKitError, Principal.Service | Service>;
|
|
190
|
+
reject: (id: string & import("effect/Brand").Brand<"@domainkit/BatchId">, options?: RejectOptions | undefined) => Effect.Effect<Batch, Errors.DomainKitError, Principal.Service | Service>;
|
|
191
|
+
get: (id: string & import("effect/Brand").Brand<"@domainkit/BatchId">) => Effect.Effect<Batch, Errors.DomainKitError, Principal.Service | Service>;
|
|
192
|
+
list: (filter: {
|
|
193
|
+
readonly unfinished: true;
|
|
194
|
+
}) => Effect.Effect<readonly BatchSummary[], Errors.DomainKitError, Principal.Service | Service>;
|
|
195
|
+
};
|
|
75
196
|
export {};
|
package/dist/types/Reason.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ declare const Forbidden_base: Schema.Class<Forbidden, Schema.TaggedStruct<"Forbi
|
|
|
21
21
|
export declare class Forbidden extends Forbidden_base {
|
|
22
22
|
}
|
|
23
23
|
declare const NotFound_base: Schema.Class<NotFound, Schema.TaggedStruct<"NotFound", {
|
|
24
|
-
readonly entity: Schema.Literals<readonly ["connection", "attachment", "plan", "approval", "receipt", "continuation", "provider", "zone", "authorization", "record"]>;
|
|
24
|
+
readonly entity: Schema.Literals<readonly ["connection", "attachment", "plan", "approval", "receipt", "continuation", "provider", "zone", "authorization", "record", "batch"]>;
|
|
25
25
|
readonly id: Schema.String;
|
|
26
26
|
}>, import("effect/Cause").YieldableError>;
|
|
27
27
|
export declare class NotFound extends NotFound_base {
|
|
@@ -43,6 +43,22 @@ declare const Stale_base: Schema.Class<Stale, Schema.TaggedStruct<"Stale", {
|
|
|
43
43
|
export declare class Stale extends Stale_base {
|
|
44
44
|
get message(): string;
|
|
45
45
|
}
|
|
46
|
+
declare const BatchStale_base: Schema.Class<BatchStale, Schema.TaggedStruct<"BatchStale", {
|
|
47
|
+
readonly batchId: Schema.String;
|
|
48
|
+
readonly status: Schema.String;
|
|
49
|
+
readonly digest: Schema.NullOr<Schema.String>;
|
|
50
|
+
}>, import("effect/Cause").YieldableError>;
|
|
51
|
+
/**
|
|
52
|
+
* The batch moved since it was read: it was declined, it is already approved, or the digest the
|
|
53
|
+
* principal reviewed is no longer the one its plans produce. Read the batch again.
|
|
54
|
+
*
|
|
55
|
+
* The aggregate's own refusal, beside `Stale`: a batch has no plan and no plan digest of its own,
|
|
56
|
+
* so nothing here can name one. `digest` is the batch's current digest, null while its items are
|
|
57
|
+
* still being planned.
|
|
58
|
+
*/
|
|
59
|
+
export declare class BatchStale extends BatchStale_base {
|
|
60
|
+
get message(): string;
|
|
61
|
+
}
|
|
46
62
|
declare const Expired_base: Schema.Class<Expired, Schema.TaggedStruct<"Expired", {
|
|
47
63
|
readonly entity: Schema.Literals<readonly ["plan", "approval", "continuation", "credential"]>;
|
|
48
64
|
readonly id: Schema.String;
|
|
@@ -113,6 +129,6 @@ declare const Unsupported_base: Schema.Class<Unsupported, Schema.TaggedStruct<"U
|
|
|
113
129
|
/** The provider, or this target within it, cannot do the requested operation. */
|
|
114
130
|
export declare class Unsupported extends Unsupported_base {
|
|
115
131
|
}
|
|
116
|
-
export declare const Model: Schema.Union<readonly [typeof InvalidInput, typeof Unauthenticated, typeof Forbidden, typeof NotFound, typeof Conflict, typeof Stale, typeof Expired, typeof Busy, typeof ProviderRejected, typeof ProviderUnavailable, typeof ProviderConflict, typeof Unsupported, typeof Reconnect, typeof StorageFailed, typeof CryptoFailed, typeof ResolverFailed]>;
|
|
132
|
+
export declare const Model: Schema.Union<readonly [typeof InvalidInput, typeof Unauthenticated, typeof Forbidden, typeof NotFound, typeof Conflict, typeof Stale, typeof BatchStale, typeof Expired, typeof Busy, typeof ProviderRejected, typeof ProviderUnavailable, typeof ProviderConflict, typeof Unsupported, typeof Reconnect, typeof StorageFailed, typeof CryptoFailed, typeof ResolverFailed]>;
|
|
117
133
|
export type Model = typeof Model.Type;
|
|
118
134
|
export {};
|