domainkit 0.12.2 → 0.13.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/dist/Server.mjs CHANGED
@@ -381,6 +381,16 @@ const callbackUrlFor = (input) => Effect.suspend(() => {
381
381
  return Effect.succeed(new URL(`${url.origin}${path.slice(0, path.length - input.route.length)}${suffix}`));
382
382
  });
383
383
  });
384
+ /**
385
+ * One refusal for every way a callback can fail to match a flow: no such `state`, one that has
386
+ * expired, one presented at another provider's route, and one presented by a session that did not
387
+ * start it. The text is constant on purpose — a caller holding a leaked `state` must not be able
388
+ * to tell "no such flow" from "not your flow".
389
+ */
390
+ const noSuchFlow = fail(new InvalidInput({
391
+ message: "This callback does not match a connection you started",
392
+ field: "state"
393
+ }));
384
394
  const callbackConfigurationError = (message) => new DomainKitError({ reason: new InvalidInput({
385
395
  message,
386
396
  field: "callbackBaseUrl"
@@ -449,7 +459,7 @@ const layer = (api, options = {}) => HttpApiBuilder.group(api, "domainkit", (han
449
459
  * Run a lifecycle effect as the principal the host derives from this request, once the host
450
460
  * has allowed that principal onto this route.
451
461
  */
452
- const as = (endpoint, request, effect) => Effect.flatMap(identity.principal(request), (principal) => Effect.flatMap(identity.authorize?.(principal, endpoint) ?? Effect.void, () => Effect.provideService(effect, Service, principal)));
462
+ const as = (endpoint, request, effect, context) => Effect.flatMap(identity.principal(request, context), (principal) => Effect.flatMap(identity.authorize?.(principal, endpoint) ?? Effect.void, () => Effect.provideService(effect, Service, principal)));
453
463
  const snapshot = (domain) => Effect.map(connect.inspect(domain), snapshotOf);
454
464
  const discovered = (discovery) => {
455
465
  switch (discovery._tag) {
@@ -573,27 +583,36 @@ const layer = (api, options = {}) => HttpApiBuilder.group(api, "domainkit", (han
573
583
  ...callbackUrl === void 0 ? {} : { callbackUrl }
574
584
  });
575
585
  return yield* startedOf(started, void 0);
576
- }))).handle("callback", ({ params, query, request }) => as("callback", request, Effect.gen(function* () {
577
- const url = yield* absoluteUrl(request);
578
- const base = yield* callbackUrlFor({
579
- request,
580
- provider: params.provider,
581
- options,
582
- route: callbackRoute(params.provider)
583
- });
584
- const requested = (yield* storage.continuations.get(query.state)).returnTo ?? options.defaultReturnTo;
585
- if (requested === void 0) return yield* invalid("The flow carried no returnTo and the server has no defaultReturnTo", "returnTo");
586
- const destination = sameOrigin(requested, base);
587
- if (destination === null) return yield* invalid(`${requested} leaves this application`, "returnTo");
588
- yield* connect.complete({
589
- continuationId: query.state,
590
- callbackUrl: url.toString()
591
- });
592
- return HttpApiSchema.withHeaders({
593
- body: void 0,
594
- headers: { location: destination }
586
+ }))).handle("callback", ({ params, query, request }) => Effect.gen(function* () {
587
+ const header = yield* storage.continuations.header(query.state).pipe(Effect.catch((error) => error.reason._tag === "NotFound" || error.reason._tag === "Expired" ? Effect.succeed(null) : Effect.fail(error)));
588
+ const continuation = header !== null && header.provider === params.provider ? header : null;
589
+ return yield* as("callback", request, Effect.gen(function* () {
590
+ const principal = yield* Service;
591
+ if (continuation === null || principal.ownerId !== continuation.ownerId || principal.actorId !== continuation.actorId) return yield* noSuchFlow;
592
+ const url = yield* absoluteUrl(request);
593
+ const base = yield* callbackUrlFor({
594
+ request,
595
+ provider: params.provider,
596
+ options,
597
+ route: callbackRoute(params.provider)
598
+ });
599
+ const requested = (yield* storage.continuations.get(query.state).pipe(Effect.catch((error) => error.reason._tag === "NotFound" || error.reason._tag === "Expired" ? noSuchFlow : Effect.fail(error)))).returnTo ?? options.defaultReturnTo;
600
+ if (requested === void 0) return yield* invalid("The flow carried no returnTo and the server has no defaultReturnTo", "returnTo");
601
+ const destination = sameOrigin(requested, base);
602
+ if (destination === null) return yield* invalid(`${requested} leaves this application`, "returnTo");
603
+ yield* connect.complete({
604
+ continuationId: query.state,
605
+ callbackUrl: url.toString()
606
+ });
607
+ return HttpApiSchema.withHeaders({
608
+ body: void 0,
609
+ headers: { location: destination }
610
+ });
611
+ }), continuation === null ? void 0 : {
612
+ endpoint: "callback",
613
+ continuation
595
614
  });
596
- }))).handle("attach", ({ params, payload, request }) => as("attach", request, attachAt({
615
+ })).handle("attach", ({ params, payload, request }) => as("attach", request, attachAt({
597
616
  connectionId: params.connectionId,
598
617
  domain: payload.domain,
599
618
  zone: payload.zone
@@ -1 +1 @@
1
- {"version":3,"file":"Server.mjs","names":["Storage.AuthMethod","DnsRecord.Model","Plan.OperationId","Plan.Model","Storage.AttemptStatus","Approval.Model","Receipt.Model","Storage.Rejection","Storage.Overall","Storage.RequirementStatus","Verify.Evidence","Verify.HostEvidence","Plan.PlanId","Approval.ApprovalId","Receipt.ReceiptId","Errors.fail","Reason.InvalidInput","Errors.DomainKitError","Connect.Service","Provision.Service","Cleanup.Service","Verify.Service","Storage.Service","Providers.Service","Principal.Service","Reason.NotFound"],"sources":["../src/Server.ts"],"sourcesContent":["/**\n * domainkit/server — the whole lifecycle as one HttpApi group the host mounts.\n *\n * const Api = HttpApi.make(\"app\").add(Server.group)\n * const ApiLive = HttpApiBuilder.layer(Api).pipe(\n * Layer.provide([Server.layer(Api), DomainKitLive, IdentityLive]),\n * )\n *\n * The host provides `Identity` (request -> `Principal.Interface`). Everything else comes from\n * `DomainKit.layer`. Non-HttpApi hosts use `toWebHandler`. The group carries no prefix of its\n * own, so `group.prefix(\"/internal/dns\")` or a host router mount is all a different base path\n * takes; callback URLs follow the mount.\n */\nimport { Context, Effect, Layer, Option, Schema } from \"effect\";\nimport * as HttpRouter from \"effect/unstable/http/HttpRouter\";\nimport * as HttpServer from \"effect/unstable/http/HttpServer\";\nimport * as HttpServerRequest from \"effect/unstable/http/HttpServerRequest\";\nimport * as HttpApi from \"effect/unstable/httpapi/HttpApi\";\nimport * as HttpApiBuilder from \"effect/unstable/httpapi/HttpApiBuilder\";\nimport * as HttpApiEndpoint from \"effect/unstable/httpapi/HttpApiEndpoint\";\nimport * as HttpApiGroup from \"effect/unstable/httpapi/HttpApiGroup\";\nimport * as HttpApiSchema from \"effect/unstable/httpapi/HttpApiSchema\";\n\nimport * as Approval from \"./Approval.ts\";\nimport * as Cleanup from \"./Cleanup.ts\";\nimport * as Connect from \"./Connect.ts\";\nimport * as DnsRecord from \"./DnsRecord.ts\";\nimport * as Errors from \"./internal/error.ts\";\nimport * as Plan from \"./Plan.ts\";\nimport * as Principal from \"./Principal.ts\";\nimport * as Providers from \"./Providers.ts\";\nimport * as Provision from \"./Provision.ts\";\nimport * as Reason from \"./Reason.ts\";\nimport * as Receipt from \"./Receipt.ts\";\nimport * as Storage from \"./Storage.ts\";\nimport * as Verify from \"./Verify.ts\";\n\n/** The only service a host must implement for the server. */\nexport interface IdentityService {\n readonly principal: (\n request: HttpServerRequest.HttpServerRequest,\n ) => Effect.Effect<Principal.Interface, Errors.DomainKitError>;\n /**\n * Which routes this principal may reach, checked after `principal` on every request. Fail with\n * reason `Forbidden` for the 403 a UI expects. Without it every authenticated principal reaches\n * every route, which is the right default for a host whose own middleware already gates the\n * mount.\n */\n readonly authorize?: (\n principal: Principal.Interface,\n endpoint: EndpointName,\n ) => Effect.Effect<void, Errors.DomainKitError>;\n}\n\nexport class Identity extends Context.Service<Identity, IdentityService>()(\n \"@domainkit/server/Identity\",\n) {}\n\n// ---------------------------------------------------------------------------------------------\n// Wire schemas (the transport contract `domainkit/client` and `@domainkit/react` consume)\n// ---------------------------------------------------------------------------------------------\n\nexport const ConnectionStatus = Schema.Literals([\"disconnected\", \"connected\", \"reconnect\"]);\nexport type ConnectionStatus = typeof ConnectionStatus.Type;\n\n/** One value a token method needs, so a form renders without knowing the provider. */\nexport const Field = Schema.Struct({\n name: Schema.String,\n required: Schema.Boolean,\n secret: Schema.Boolean,\n});\nexport type Field = typeof Field.Type;\n\n/** How a provider can be connected; `fields` is `null` for the interactive methods. */\nexport const MethodDescriptor = Schema.Struct({\n kind: Storage.AuthMethod,\n label: Schema.String,\n docsUrl: Schema.NullOr(Schema.String),\n fields: Schema.NullOr(Schema.Array(Field)),\n});\nexport type MethodDescriptor = typeof MethodDescriptor.Type;\n\n/** Everything the UI needs about a domain, flattened; provider context never crosses the wire. */\n/** The attachment a domain holds, with the zone label the provider gave when it was created. */\nexport const Attachment = Schema.Struct({\n id: Schema.String,\n label: Schema.String,\n});\nexport type Attachment = typeof Attachment.Type;\n\nexport const Snapshot = Schema.Struct({\n domain: Schema.String,\n attachment: Schema.NullOr(Attachment),\n connectionId: Schema.NullOr(Schema.String),\n provider: Schema.NullOr(Schema.String),\n method: Schema.NullOr(Storage.AuthMethod),\n status: ConnectionStatus,\n lastReceiptId: Schema.NullOr(Schema.String),\n /** How many domains the connection serves, including this one; `0` without a connection. */\n connectionDomains: Schema.Number,\n reusable: Schema.Array(\n Schema.Struct({\n connectionId: Schema.String,\n provider: Schema.String,\n method: Storage.AuthMethod,\n }),\n ),\n providers: Schema.Array(\n Schema.Struct({\n id: Schema.String,\n name: Schema.String,\n methods: Schema.Array(MethodDescriptor),\n }),\n ),\n});\nexport type Snapshot = typeof Snapshot.Type;\n\n/**\n * How the client asks to connect. `values` is keyed by the provider's declared token fields, as\n * `Snapshot.providers[].methods[].fields` names them. Secrets are plaintext in transit and never\n * come back out.\n */\nexport class Token extends Schema.TaggedClass<Token>(\"@domainkit/server/Method/Token\")(\"Token\", {\n values: Schema.Record(Schema.String, Schema.String),\n}) {}\nexport class OAuth extends Schema.TaggedClass<OAuth>(\"@domainkit/server/Method/OAuth\")(\"OAuth\", {\n returnTo: Schema.optionalKey(Schema.String),\n}) {}\nexport class Integration extends Schema.TaggedClass<Integration>(\n \"@domainkit/server/Method/Integration\",\n)(\"Integration\", { returnTo: Schema.optionalKey(Schema.String) }) {}\nexport const Method = Schema.Union([Token, OAuth, Integration]);\nexport type Method = typeof Method.Type;\n\n/** A start without a domain connects the account alone; the customer picks a zone afterwards. */\nexport const StartPayload = Schema.Struct({\n domain: Schema.optionalKey(Schema.String),\n provider: Schema.String,\n method: Method,\n});\nexport type StartPayload = typeof StartPayload.Type;\n\n/** How the customer proves the account again. The provider is the connection's own. */\nexport const ReconnectPayload = Schema.Struct({ method: Method });\nexport type ReconnectPayload = typeof ReconnectPayload.Type;\n\n/** A zone the connection can serve, named for a customer to pick from. */\nexport class Candidate extends Schema.Class<Candidate>(\"@domainkit/server/Candidate\")({\n zone: Schema.String,\n label: Schema.String,\n}) {}\n\n/**\n * The connection the start produced. `label` names the account the way the customer will read it:\n * the attached zone's label, or the provider's name when the start carried no domain. `snapshot`\n * is the domain's state, and is null for a start that attached none.\n */\nexport class Connected extends Schema.TaggedClass<Connected>(\"@domainkit/server/Started/Connected\")(\n \"Connected\",\n {\n connectionId: Schema.String,\n provider: Schema.String,\n label: Schema.String,\n snapshot: Schema.NullOr(Snapshot),\n },\n) {}\nexport class Redirect extends Schema.TaggedClass<Redirect>(\"@domainkit/server/Started/Redirect\")(\n \"Redirect\",\n { authorizationUrl: Schema.String },\n) {}\nexport class SelectionRequired extends Schema.TaggedClass<SelectionRequired>(\n \"@domainkit/server/Started/SelectionRequired\",\n)(\"SelectionRequired\", { connectionId: Schema.String, candidates: Schema.Array(Candidate) }) {}\nexport const Started = Schema.Union([Connected, Redirect, SelectionRequired]);\nexport type Started = typeof Started.Type;\n\n/**\n * Which of the principal's connections already reaches a domain. The class names carry a\n * `Discovery` prefix because `Started` owns the unprefixed `SelectionRequired`; the wire tags are\n * the ones `Connect.Discovery` uses.\n */\nexport class DiscoveryResolved extends Schema.TaggedClass<DiscoveryResolved>(\n \"@domainkit/server/Discovery/Resolved\",\n)(\"Resolved\", { connectionId: Schema.String, zone: Schema.String, label: Schema.String }) {}\nexport class DiscoverySelectionRequired extends Schema.TaggedClass<DiscoverySelectionRequired>(\n \"@domainkit/server/Discovery/SelectionRequired\",\n)(\"SelectionRequired\", {\n candidates: Schema.Array(\n Schema.Struct({ connectionId: Schema.String, zone: Schema.String, label: Schema.String }),\n ),\n}) {}\n/** `host` names the registered provider whose nameservers serve the domain, by definition id. */\nexport class DiscoveryNotFound extends Schema.TaggedClass<DiscoveryNotFound>(\n \"@domainkit/server/Discovery/NotFound\",\n)(\"NotFound\", {\n nameservers: Schema.Array(Schema.String),\n host: Schema.NullOr(Schema.Struct({ provider: Schema.String })),\n}) {}\nexport const Discovery = Schema.Union([\n DiscoveryResolved,\n DiscoverySelectionRequired,\n DiscoveryNotFound,\n]);\nexport type Discovery = typeof Discovery.Type;\n\n/** One zone a connection reaches, named for a customer to pick from. */\nexport const Zone = Schema.Struct({\n connectionId: Schema.String,\n provider: Schema.String,\n zone: Schema.String,\n label: Schema.String,\n nameservers: Schema.optionalKey(Schema.Array(Schema.String)),\n});\nexport type Zone = typeof Zone.Type;\n\n/**\n * Every zone the principal's connections reach, and where each connection stands. A connection\n * marked `reconnect` contributes no zones and needs the customer to authorize it again.\n */\nexport const Zones = Schema.Struct({\n zones: Schema.Array(Zone),\n connections: Schema.Array(\n Schema.Struct({\n connectionId: Schema.String,\n provider: Schema.String,\n status: ConnectionStatus,\n }),\n ),\n /** Every provider a customer could connect, so a picker offers one without a second call. */\n providers: Schema.Array(\n Schema.Struct({\n id: Schema.String,\n name: Schema.String,\n methods: Schema.Array(MethodDescriptor),\n }),\n ),\n});\nexport type Zones = typeof Zones.Type;\n\nexport const AttachPayload = Schema.Struct({\n domain: Schema.String,\n /** Which candidate zone to attach when `SelectionRequired` came back. */\n zone: Schema.optionalKey(Schema.String),\n});\nexport type AttachPayload = typeof AttachPayload.Type;\n\nexport const PlanPayload = Schema.Struct({\n requirements: Schema.Array(DnsRecord.Model),\n});\nexport type PlanPayload = typeof PlanPayload.Type;\n\nexport const ObservePayload = Schema.Struct({\n /** Records to observe instead of the attachment's receipt; required for an unattached domain. */\n requirements: Schema.optionalKey(Schema.Array(DnsRecord.Model)),\n});\nexport type ObservePayload = typeof ObservePayload.Type;\n\nexport const ApprovePayload = Schema.Struct({\n /** A subset of the plan's writes; omit to approve every write. */\n operationIds: Schema.optionalKey(Schema.Array(Plan.OperationId)),\n});\nexport type ApprovePayload = typeof ApprovePayload.Type;\n\nexport const RejectPayload = Schema.Struct({\n /** Why the customer declined, for the audit trail. */\n reason: Schema.optionalKey(Schema.String),\n});\nexport type RejectPayload = typeof RejectPayload.Type;\n\n/** One durable plan -> approval -> receipt lifecycle, as `GET /plans/:planId` returns it. */\nexport const Attempt = Schema.Struct({\n plan: Plan.Model,\n status: Storage.AttemptStatus,\n approval: Schema.NullOr(Approval.Model),\n receipt: Schema.NullOr(Receipt.Model),\n rejection: Schema.NullOr(Storage.Rejection),\n});\nexport type Attempt = typeof Attempt.Type;\n\n/** `Verify.Readiness` on the wire; timestamps encode as ISO strings. */\nexport const Readiness = Schema.Struct({\n domain: Schema.String,\n attachmentId: Schema.NullOr(Schema.String),\n overall: Storage.Overall,\n requirements: Schema.Array(\n Schema.Struct({\n operationId: Schema.NullOr(Plan.OperationId),\n record: DnsRecord.Model,\n status: Storage.RequirementStatus,\n evidence: Schema.Array(Verify.Evidence),\n }),\n ),\n host: Schema.Array(Verify.HostEvidence),\n checkedAt: Schema.DateTimeUtcFromString,\n nextCheckAt: Schema.NullOr(Schema.DateTimeUtcFromString),\n});\nexport type Readiness = typeof Readiness.Type;\n\nconst Redirected = HttpApiSchema.WithHeaders(HttpApiSchema.Empty(302), {\n location: Schema.String,\n});\n\n// ---------------------------------------------------------------------------------------------\n// Errors: one wire body, the status from `DomainKit.Error.httpStatus`\n// ---------------------------------------------------------------------------------------------\n\n/** `DomainKit.Error` narrowed to the reasons that answer with `status`. */\nconst errorAt = (status: number) =>\n Errors.DomainKitError.check(\n Schema.makeFilter((error: Errors.DomainKitError) => error.httpStatus === status),\n ).pipe(HttpApiSchema.status(status));\n\nconst errors = [400, 401, 403, 404, 409, 500, 501, 502, 503].map(errorAt);\n\n// ---------------------------------------------------------------------------------------------\n// The group\n// ---------------------------------------------------------------------------------------------\n\nexport const group = HttpApiGroup.make(\"domainkit\")\n .add(\n HttpApiEndpoint.get(\"inspect\", \"/domains/:domain\", {\n params: { domain: Schema.String },\n success: Snapshot,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.get(\"discover\", \"/domains/:domain/discovery\", {\n params: { domain: Schema.String },\n success: Discovery,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.get(\"zones\", \"/zones\", {\n query: { provider: Schema.optionalKey(Schema.String) },\n success: Zones,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"start\", \"/connections\", {\n payload: StartPayload,\n success: Started,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.get(\"callback\", \"/callback/:provider\", {\n params: { provider: Schema.String },\n // `Connect.complete` reads every callback parameter off the request URL, because a provider\n // may return `error`, `teamId`, or `configurationId` too. These two are declared so the\n // OpenAPI document names what a provider is expected to send back.\n query: { state: Schema.String, code: Schema.optionalKey(Schema.String) },\n success: Redirected,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"reconnect\", \"/connections/:connectionId/reconnections\", {\n params: { connectionId: Schema.String },\n payload: ReconnectPayload,\n success: Started,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"attach\", \"/connections/:connectionId/attachments\", {\n params: { connectionId: Schema.String },\n payload: AttachPayload,\n success: Started,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.delete(\"detach\", \"/attachments/:attachmentId\", {\n params: { attachmentId: Schema.String },\n success: Schema.Void,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.delete(\"disconnect\", \"/connections/:connectionId\", {\n params: { connectionId: Schema.String },\n success: Schema.Void,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"createPlan\", \"/domains/:domain/plans\", {\n params: { domain: Schema.String },\n payload: PlanPayload,\n success: Plan.Model,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"approve\", \"/plans/:planId/approvals\", {\n params: { planId: Plan.PlanId },\n payload: ApprovePayload,\n success: Approval.Model,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"reject\", \"/plans/:planId/rejections\", {\n params: { planId: Plan.PlanId },\n payload: RejectPayload,\n success: Attempt,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"apply\", \"/approvals/:approvalId/apply\", {\n params: { approvalId: Approval.ApprovalId },\n success: Receipt.Model,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.get(\"plan\", \"/plans/:planId\", {\n params: { planId: Plan.PlanId },\n success: Attempt,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.get(\"receipt\", \"/receipts/:receiptId\", {\n params: { receiptId: Receipt.ReceiptId },\n success: Receipt.Model,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"observe\", \"/domains/:domain/observations\", {\n params: { domain: Schema.String },\n payload: ObservePayload,\n success: Readiness,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"cleanupPlan\", \"/receipts/:receiptId/cleanup-plans\", {\n params: { receiptId: Receipt.ReceiptId },\n success: Plan.Model,\n error: errors,\n }),\n );\n// Cleanup approve, reject, and apply reuse those routes: the attempt knows its kind.\n\nexport type Group = typeof group;\n\n/** Every route in the group, by name; what `Identity.authorize` is asked about. */\nexport type EndpointName = HttpApiGroup.Endpoints<Group>[\"identifier\"];\n\nexport interface Options {\n /**\n * Where the callback route is reachable from the provider, without the `/callback/:provider`\n * suffix. Defaults to the incoming request's origin and mount path.\n */\n readonly callbackBaseUrl?: string;\n /** Where interactive flows return when the callback carries no `returnTo`. */\n readonly defaultReturnTo?: string;\n}\n\n/** Everything the handlers need: the lifecycle services, Storage, and the host's `Identity`. */\nexport type Services =\n | Provision.Service\n | Cleanup.Service\n | Connect.Service\n | Verify.Service\n | Providers.Service\n | Storage.Service\n | Identity;\n\n// ---------------------------------------------------------------------------------------------\n// Handlers\n// ---------------------------------------------------------------------------------------------\n\nconst START_ROUTE = \"/connections\";\nconst reconnectRoute = (connectionId: string) =>\n `/connections/${encodeURIComponent(connectionId)}/reconnections`;\nconst callbackRoute = (provider: string) => `/callback/${encodeURIComponent(provider)}`;\n\nconst invalid = (message: string, field?: string) =>\n Errors.fail(new Reason.InvalidInput({ message, ...(field === undefined ? {} : { field }) }));\n\n/** The request as an absolute URL: the web original when there is one, else scheme plus `Host`. */\nconst absoluteUrl = (\n request: HttpServerRequest.HttpServerRequest,\n): Effect.Effect<URL, Errors.DomainKitError> =>\n Effect.suspend(() => {\n try {\n return Effect.succeed(new URL(request.originalUrl));\n } catch {\n const url = HttpServerRequest.toURL(request);\n return Option.isNone(url)\n ? invalid(\"The request has no usable origin; set callbackBaseUrl\", \"callbackBaseUrl\")\n : Effect.succeed(url.value);\n }\n });\n\n/**\n * The absolute URL the provider redirects back to. `callbackBaseUrl` wins when the host set it,\n * because a proxy that rewrites `Host` leaves the request pointing at an origin the browser never\n * sees; otherwise it follows the prefix the group is mounted at, derived by stripping the handling\n * route's own path off the request.\n */\nconst callbackUrlFor = (input: {\n readonly request: HttpServerRequest.HttpServerRequest;\n readonly provider: string;\n readonly options: Options;\n /** The path of the route serving this request, stripped to find the mount. */\n readonly route: string;\n}): Effect.Effect<URL, Errors.DomainKitError> =>\n Effect.suspend(() => {\n const suffix = `/callback/${encodeURIComponent(input.provider)}`;\n const configured = input.options.callbackBaseUrl;\n if (configured !== undefined) {\n return Effect.try({\n try: () => new URL(`${configured.replace(/\\/+$/, \"\")}${suffix}`),\n catch: () => callbackConfigurationError(`${configured} is not a URL`),\n });\n }\n return Effect.flatMap(absoluteUrl(input.request), (url) => {\n const path = url.pathname;\n if (!path.endsWith(input.route)) {\n return invalid(\n `The ${input.route} route is not mounted where it says; set callbackBaseUrl`,\n \"callbackBaseUrl\",\n );\n }\n return Effect.succeed(\n new URL(`${url.origin}${path.slice(0, path.length - input.route.length)}${suffix}`),\n );\n });\n });\n\nconst callbackConfigurationError = (message: string) =>\n new Errors.DomainKitError({\n reason: new Reason.InvalidInput({ message, field: \"callbackBaseUrl\" }),\n });\n\n/**\n * Where the callback may send the customer: a path under the callback's own base, or an absolute\n * URL on its origin. A destination that leaves the application is an open redirect, whoever\n * supplied it.\n */\nconst sameOrigin = (destination: string, callback: URL): string | null => {\n try {\n // Resolving against the callback is what makes this safe: the URL parser normalizes the forms\n // a browser would follow off-origin (`//host`, `/\\host`, `\\/host`, encoded hosts) into a real\n // origin, so comparing origins catches every one of them.\n const url = new URL(destination, callback);\n return url.origin === callback.origin ? url.toString() : null;\n } catch {\n return null;\n }\n};\n\nconst snapshotOf = (snapshot: Connect.Snapshot): Snapshot => ({\n domain: snapshot.domain,\n attachment:\n snapshot.attachment === null\n ? null\n : { id: snapshot.attachment.id, label: snapshot.attachment.label },\n connectionId: snapshot.connection?.id ?? null,\n provider: snapshot.authorization?.provider ?? null,\n method: snapshot.authorization?.method ?? null,\n status:\n snapshot.connection === null\n ? \"disconnected\"\n : snapshot.authorization?.revocation === \"active\"\n ? \"connected\"\n : \"reconnect\",\n lastReceiptId: snapshot.lastReceiptId,\n connectionDomains: snapshot.connectionDomains,\n reusable: snapshot.reusable.map(({ connection, provider, method }) => ({\n connectionId: connection.id,\n provider,\n method,\n })),\n providers: snapshot.providers.map(({ id, name, methods }) => ({ id, name, methods })),\n});\n\n/** The wire method as `Connect` takes it; interactive cases carry the caller's return destination. */\nconst methodOf = (method: Method): Connect.Method => {\n switch (method._tag) {\n case \"Token\":\n return Connect.Method.token(method.values);\n case \"OAuth\":\n return Connect.Method.oauth(\n method.returnTo === undefined ? {} : { returnTo: method.returnTo },\n );\n case \"Integration\":\n return Connect.Method.integration(\n method.returnTo === undefined ? {} : { returnTo: method.returnTo },\n );\n }\n};\n\nconst candidatesOf = (\n candidates: ReadonlyArray<{ readonly zone: string; readonly label: string }>,\n): ReadonlyArray<Candidate> => candidates.map(({ zone, label }) => new Candidate({ zone, label }));\n\n/**\n * Handlers for `group` inside the host's API. Every handler derives `Principal` from `Identity`\n * for the request it is serving and provides it to the lifecycle services.\n */\nexport const layer = <ApiId extends string, Groups extends HttpApiGroup.Constraint>(\n api: HttpApi.HttpApi<ApiId, Groups>,\n options: Options = {},\n): Layer.Layer<HttpApiGroup.Service<ApiId, \"domainkit\">, never, Services> =>\n // The host's API carries its own groups beside this one; only `\"domainkit\"` is implemented here,\n // and `HttpApiBuilder.group` needs the group's own endpoint types to check the handlers.\n HttpApiBuilder.group(api as unknown as HttpApi.HttpApi<ApiId, Group>, \"domainkit\", (handlers) =>\n Effect.gen(function* () {\n const identity = yield* Identity;\n const connect = yield* Connect.Service;\n const provision = yield* Provision.Service;\n const cleanup = yield* Cleanup.Service;\n const verify = yield* Verify.Service;\n const storage = yield* Storage.Service;\n const providers = yield* Providers.Service;\n\n /**\n * Run a lifecycle effect as the principal the host derives from this request, once the host\n * has allowed that principal onto this route.\n */\n const as = <A>(\n endpoint: EndpointName,\n request: HttpServerRequest.HttpServerRequest,\n effect: Effect.Effect<A, Errors.DomainKitError, Principal.Service>,\n ): Effect.Effect<A, Errors.DomainKitError> =>\n Effect.flatMap(identity.principal(request), (principal) =>\n Effect.flatMap(identity.authorize?.(principal, endpoint) ?? Effect.void, () =>\n Effect.provideService(effect, Principal.Service, principal),\n ),\n );\n\n const snapshot = (domain: string) => Effect.map(connect.inspect(domain), snapshotOf);\n\n const discovered = (discovery: Connect.Discovery): Discovery => {\n switch (discovery._tag) {\n case \"Resolved\":\n return new DiscoveryResolved({\n connectionId: discovery.connectionId,\n zone: discovery.target.zone,\n label: discovery.target.label,\n });\n case \"SelectionRequired\":\n return new DiscoverySelectionRequired({\n candidates: discovery.candidates.map(({ connectionId, target }) => ({\n connectionId,\n zone: target.zone,\n label: target.label,\n })),\n });\n case \"NotFound\":\n return new DiscoveryNotFound({\n nameservers: discovery.nameservers,\n host: discovery.host,\n });\n }\n };\n\n /**\n * The connection a start or an attach produced. `label` is the zone the attachment named,\n * or the provider itself where the connection carries no domain yet.\n */\n const connected = (input: {\n readonly connection: Storage.Connection;\n readonly attachment: Storage.Attachment | null;\n readonly domain: string | undefined;\n }): Effect.Effect<Connected, Errors.DomainKitError, Principal.Service> =>\n Effect.gen(function* () {\n const authorization = yield* storage.authorizations.get(input.connection.authorizationId);\n const definition = yield* providers.get(authorization.provider);\n return new Connected({\n connectionId: input.connection.id,\n provider: authorization.provider,\n label: input.attachment?.label ?? definition.name,\n snapshot: input.domain === undefined ? null : yield* snapshot(input.domain),\n });\n });\n\n const startedOf = (\n started: Connect.Started,\n domain: string | undefined,\n ): Effect.Effect<Started, Errors.DomainKitError, Principal.Service> => {\n switch (started._tag) {\n case \"Connected\":\n return connected({\n attachment: started.attachment,\n connection: started.connection,\n domain,\n });\n case \"Redirect\":\n return Effect.succeed(new Redirect({ authorizationUrl: started.authorizationUrl }));\n case \"SelectionRequired\":\n return Effect.succeed(\n new SelectionRequired({\n connectionId: started.connection.id,\n candidates: candidatesOf(started.candidates),\n }),\n );\n }\n };\n\n /**\n * Attach with a zone the client picked out of a `SelectionRequired`. The provider target\n * behind a zone never crosses the wire, so resolve the candidates again and match by zone.\n */\n const attachAt = (input: {\n readonly connectionId: string;\n readonly domain: string;\n readonly zone: string | undefined;\n }): Effect.Effect<Started, Errors.DomainKitError, Principal.Service> =>\n Effect.gen(function* () {\n const connection = yield* storage.connections.get(input.connectionId);\n const attached = (attachment: Storage.Attachment) =>\n connected({ attachment, connection, domain: input.domain });\n const first = yield* connect.attach({\n connectionId: input.connectionId,\n domain: input.domain,\n });\n if (!(\"_tag\" in first)) return yield* attached(first);\n if (input.zone === undefined) {\n return new SelectionRequired({\n connectionId: input.connectionId,\n candidates: candidatesOf(first.candidates),\n });\n }\n const target = first.candidates.find(({ zone }) => zone === input.zone);\n if (target === undefined) {\n return yield* Errors.fail(new Reason.NotFound({ entity: \"zone\", id: input.zone }));\n }\n const second = yield* connect.attach({\n connectionId: input.connectionId,\n domain: input.domain,\n target,\n });\n return \"_tag\" in second\n ? yield* Errors.fail(new Reason.NotFound({ entity: \"zone\", id: input.zone }))\n : yield* attached(second);\n });\n\n /** Provisioning and cleanup share the approve and apply routes; the attempt knows its kind. */\n const kindOf = (attempt: Storage.Attempt) =>\n attempt.kind === \"cleanup\" ? cleanup : provision;\n\n return handlers\n .handle(\"inspect\", ({ params, request }) => as(\"inspect\", request, snapshot(params.domain)))\n .handle(\"discover\", ({ params, request }) =>\n as(\"discover\", request, Effect.map(connect.discover(params.domain), discovered)),\n )\n .handle(\"zones\", ({ query, request }) =>\n as(\n \"zones\",\n request,\n Effect.map(\n connect.zones(query.provider === undefined ? {} : { provider: query.provider }),\n (listing): Zones => ({\n zones: listing.zones.map(({ connectionId, provider, target }) => ({\n connectionId,\n provider,\n zone: target.zone,\n label: target.label,\n ...(target.nameservers === undefined ? {} : { nameservers: target.nameservers }),\n })),\n connections: listing.connections,\n providers: listing.providers,\n }),\n ),\n ),\n )\n .handle(\"start\", ({ payload, request }) =>\n as(\n \"start\",\n request,\n Effect.gen(function* () {\n // Token methods connect in one call; the interactive ones need somewhere to land.\n const callbackUrl =\n payload.method._tag === \"Token\"\n ? undefined\n : (yield* callbackUrlFor({\n request,\n provider: payload.provider,\n options,\n route: START_ROUTE,\n })).toString();\n const started = yield* connect.start({\n provider: payload.provider,\n ...(payload.domain === undefined ? {} : { domain: payload.domain }),\n method: methodOf(payload.method),\n ...(callbackUrl === undefined ? {} : { callbackUrl }),\n });\n return yield* startedOf(started, payload.domain);\n }),\n ),\n )\n .handle(\"reconnect\", ({ params, payload, request }) =>\n as(\n \"reconnect\",\n request,\n Effect.gen(function* () {\n const connection = yield* storage.connections.get(params.connectionId);\n const authorization = yield* storage.authorizations.get(connection.authorizationId);\n const callbackUrl =\n payload.method._tag === \"Token\"\n ? undefined\n : (yield* callbackUrlFor({\n request,\n provider: authorization.provider,\n options,\n route: reconnectRoute(params.connectionId),\n })).toString();\n const started = yield* connect.reconnect({\n connectionId: params.connectionId,\n method: methodOf(payload.method),\n ...(callbackUrl === undefined ? {} : { callbackUrl }),\n });\n return yield* startedOf(started, undefined);\n }),\n ),\n )\n .handle(\"callback\", ({ params, query, request }) =>\n as(\n \"callback\",\n request,\n Effect.gen(function* () {\n const url = yield* absoluteUrl(request);\n // Resolve the destination against the callback's public base, not the request: a\n // proxy that rewrites `Host` leaves the request on an origin the browser never sees.\n const base = yield* callbackUrlFor({\n request,\n provider: params.provider,\n options,\n route: callbackRoute(params.provider),\n });\n // Where the customer lands comes from the continuation this owner started, never\n // from the provider's query string, and it is resolved before the callback is spent:\n // a server with nowhere to send them must not connect the provider and then fail.\n const continuation = yield* storage.continuations.get(query.state);\n const requested = continuation.returnTo ?? options.defaultReturnTo;\n if (requested === undefined) {\n return yield* invalid(\n \"The flow carried no returnTo and the server has no defaultReturnTo\",\n \"returnTo\",\n );\n }\n const destination = sameOrigin(requested, base);\n if (destination === null) {\n return yield* invalid(`${requested} leaves this application`, \"returnTo\");\n }\n yield* connect.complete({\n continuationId: query.state,\n callbackUrl: url.toString(),\n });\n return HttpApiSchema.withHeaders({\n body: undefined as void,\n headers: { location: destination },\n });\n }),\n ),\n )\n .handle(\"attach\", ({ params, payload, request }) =>\n as(\n \"attach\",\n request,\n attachAt({\n connectionId: params.connectionId,\n domain: payload.domain,\n zone: payload.zone,\n }),\n ),\n )\n .handle(\"detach\", ({ params, request }) =>\n as(\"detach\", request, connect.detach(params.attachmentId)),\n )\n .handle(\"disconnect\", ({ params, request }) =>\n as(\"disconnect\", request, connect.disconnect(params.connectionId)),\n )\n .handle(\"createPlan\", ({ params, payload, request }) =>\n as(\n \"createPlan\",\n request,\n provision.plan({ domain: params.domain, requirements: payload.requirements }),\n ),\n )\n .handle(\"approve\", ({ params, payload, request }) =>\n as(\n \"approve\",\n request,\n Effect.gen(function* () {\n const attempt = yield* storage.attempts.get(params.planId);\n return yield* kindOf(attempt).approve(params.planId, {\n ...(payload.operationIds === undefined\n ? {}\n : { operationIds: payload.operationIds, allowPartial: true }),\n });\n }),\n ),\n )\n .handle(\"reject\", ({ params, payload, request }) =>\n as(\n \"reject\",\n request,\n Effect.gen(function* () {\n const attempt = yield* storage.attempts.get(params.planId);\n return yield* kindOf(attempt).reject(params.planId, {\n ...(payload.reason === undefined ? {} : { reason: payload.reason }),\n });\n }),\n ),\n )\n .handle(\"apply\", ({ params, request }) =>\n as(\n \"apply\",\n request,\n Effect.gen(function* () {\n const attempt = yield* storage.attempts.byApproval(params.approvalId);\n return yield* kindOf(attempt).apply(params.approvalId);\n }),\n ),\n )\n .handle(\"plan\", ({ params, request }) =>\n as(\n \"plan\",\n request,\n Effect.map(storage.attempts.get(params.planId), (attempt) => ({\n plan: attempt.plan,\n status: attempt.status,\n approval: attempt.approval,\n receipt: attempt.receipt,\n rejection: attempt.rejection,\n })),\n ),\n )\n .handle(\"receipt\", ({ params, request }) =>\n as(\n \"receipt\",\n request,\n Effect.flatMap(storage.attempts.byReceipt(params.receiptId), (attempt) =>\n attempt.receipt === null\n ? Errors.fail(new Reason.NotFound({ entity: \"receipt\", id: params.receiptId }))\n : Effect.succeed(attempt.receipt),\n ),\n ),\n )\n .handle(\"observe\", ({ params, payload, request }) =>\n as(\n \"observe\",\n request,\n verify.observe({\n domain: params.domain,\n ...(payload.requirements === undefined ? {} : { requirements: payload.requirements }),\n }),\n ),\n )\n .handle(\"cleanupPlan\", ({ params, request }) =>\n as(\"cleanupPlan\", request, cleanup.plan({ receiptId: params.receiptId })),\n );\n }),\n ) as Layer.Layer<HttpApiGroup.Service<ApiId, \"domainkit\">, never, Services>;\n\n/** A standalone API for hosts not on HttpApi. Mount at any prefix. */\nexport const api: HttpApi.HttpApi<\"domainkit\", Group> = HttpApi.make(\"domainkit\").add(group);\n\nexport interface WebHandlerOptions extends Options {\n /** Mount every route under this path, e.g. `/api/domainkit`. */\n readonly prefix?: `/${string}`;\n}\n\n/** The Promise edge: a `fetch`-shaped handler over the group for hosts that are not Effect-native. */\nexport const toWebHandler = (\n services: Layer.Layer<Services, Errors.DomainKitError>,\n options: WebHandlerOptions = {},\n): {\n readonly handler: (request: Request) => Promise<Response>;\n readonly dispose: () => Promise<void>;\n} => {\n // `group.prefix` widens the endpoint paths in the type, which `Group` states unprefixed; the\n // handlers are keyed by endpoint identifier, so the routes still line up.\n const prefixed =\n options.prefix === undefined\n ? api\n : (HttpApi.make(\"domainkit\").add(group.prefix(options.prefix)) as unknown as HttpApi.HttpApi<\n \"domainkit\",\n Group\n >);\n const app = HttpApiBuilder.layer(prefixed).pipe(\n Layer.provide(layer(prefixed, options)),\n Layer.provide(services),\n Layer.provide(HttpServer.layerServices),\n );\n return HttpRouter.toWebHandler(app, { disableLogger: true });\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"Server.mjs","names":["Storage.AuthMethod","DnsRecord.Model","Plan.OperationId","Plan.Model","Storage.AttemptStatus","Approval.Model","Receipt.Model","Storage.Rejection","Storage.Overall","Storage.RequirementStatus","Verify.Evidence","Verify.HostEvidence","Plan.PlanId","Approval.ApprovalId","Receipt.ReceiptId","Errors.fail","Reason.InvalidInput","Errors.DomainKitError","Connect.Service","Provision.Service","Cleanup.Service","Verify.Service","Storage.Service","Providers.Service","Principal.Service","Reason.NotFound"],"sources":["../src/Server.ts"],"sourcesContent":["/**\n * domainkit/server — the whole lifecycle as one HttpApi group the host mounts.\n *\n * const Api = HttpApi.make(\"app\").add(Server.group)\n * const ApiLive = HttpApiBuilder.layer(Api).pipe(\n * Layer.provide([Server.layer(Api), DomainKitLive, IdentityLive]),\n * )\n *\n * The host provides `Identity` (request -> `Principal.Interface`). Everything else comes from\n * `DomainKit.layer`. Non-HttpApi hosts use `toWebHandler`. The group carries no prefix of its\n * own, so `group.prefix(\"/internal/dns\")` or a host router mount is all a different base path\n * takes; callback URLs follow the mount.\n */\nimport { Context, Effect, Layer, Option, Schema } from \"effect\";\nimport * as HttpRouter from \"effect/unstable/http/HttpRouter\";\nimport * as HttpServer from \"effect/unstable/http/HttpServer\";\nimport * as HttpServerRequest from \"effect/unstable/http/HttpServerRequest\";\nimport * as HttpApi from \"effect/unstable/httpapi/HttpApi\";\nimport * as HttpApiBuilder from \"effect/unstable/httpapi/HttpApiBuilder\";\nimport * as HttpApiEndpoint from \"effect/unstable/httpapi/HttpApiEndpoint\";\nimport * as HttpApiGroup from \"effect/unstable/httpapi/HttpApiGroup\";\nimport * as HttpApiSchema from \"effect/unstable/httpapi/HttpApiSchema\";\n\nimport * as Approval from \"./Approval.ts\";\nimport * as Cleanup from \"./Cleanup.ts\";\nimport * as Connect from \"./Connect.ts\";\nimport * as DnsRecord from \"./DnsRecord.ts\";\nimport * as Errors from \"./internal/error.ts\";\nimport * as Plan from \"./Plan.ts\";\nimport * as Principal from \"./Principal.ts\";\nimport * as Providers from \"./Providers.ts\";\nimport * as Provision from \"./Provision.ts\";\nimport * as Reason from \"./Reason.ts\";\nimport * as Receipt from \"./Receipt.ts\";\nimport * as Storage from \"./Storage.ts\";\nimport * as Verify from \"./Verify.ts\";\n\n/**\n * What DomainKit already knows about the request when it asks the host who is making it. Only\n * `/callback/:provider` passes one, because it is the only route that points at a flow DomainKit\n * recorded earlier; every other route calls `principal` with the request alone.\n */\nexport interface IdentityContext {\n /** The route being served. */\n readonly endpoint: EndpointName;\n /** The flow this callback claims to finish, as DomainKit recorded it when the flow started. */\n readonly continuation: Storage.ContinuationHeader;\n}\n\n/** The only service a host must implement for the server. */\nexport interface IdentityService {\n /**\n * `context` is present only on the callback, where a host that keeps per-request tenancy can use\n * `context.continuation.ownerId` to pick the right tenant out of a session that holds several.\n * Ignoring it is safe everywhere: DomainKit checks the principal against the recorded flow on\n * that route regardless, so a host that answers with the wrong one is refused rather than\n * trusted.\n */\n readonly principal: (\n request: HttpServerRequest.HttpServerRequest,\n context?: IdentityContext,\n ) => Effect.Effect<Principal.Interface, Errors.DomainKitError>;\n /**\n * Which routes this principal may reach, checked after `principal` on every request. Fail with\n * reason `Forbidden` for the 403 a UI expects. Without it every authenticated principal reaches\n * every route, which is the right default for a host whose own middleware already gates the\n * mount.\n */\n readonly authorize?: (\n principal: Principal.Interface,\n endpoint: EndpointName,\n ) => Effect.Effect<void, Errors.DomainKitError>;\n}\n\nexport class Identity extends Context.Service<Identity, IdentityService>()(\n \"@domainkit/server/Identity\",\n) {}\n\n// ---------------------------------------------------------------------------------------------\n// Wire schemas (the transport contract `domainkit/client` and `@domainkit/react` consume)\n// ---------------------------------------------------------------------------------------------\n\nexport const ConnectionStatus = Schema.Literals([\"disconnected\", \"connected\", \"reconnect\"]);\nexport type ConnectionStatus = typeof ConnectionStatus.Type;\n\n/** One value a token method needs, so a form renders without knowing the provider. */\nexport const Field = Schema.Struct({\n name: Schema.String,\n required: Schema.Boolean,\n secret: Schema.Boolean,\n});\nexport type Field = typeof Field.Type;\n\n/** How a provider can be connected; `fields` is `null` for the interactive methods. */\nexport const MethodDescriptor = Schema.Struct({\n kind: Storage.AuthMethod,\n label: Schema.String,\n docsUrl: Schema.NullOr(Schema.String),\n fields: Schema.NullOr(Schema.Array(Field)),\n});\nexport type MethodDescriptor = typeof MethodDescriptor.Type;\n\n/** Everything the UI needs about a domain, flattened; provider context never crosses the wire. */\n/** The attachment a domain holds, with the zone label the provider gave when it was created. */\nexport const Attachment = Schema.Struct({\n id: Schema.String,\n label: Schema.String,\n});\nexport type Attachment = typeof Attachment.Type;\n\nexport const Snapshot = Schema.Struct({\n domain: Schema.String,\n attachment: Schema.NullOr(Attachment),\n connectionId: Schema.NullOr(Schema.String),\n provider: Schema.NullOr(Schema.String),\n method: Schema.NullOr(Storage.AuthMethod),\n status: ConnectionStatus,\n lastReceiptId: Schema.NullOr(Schema.String),\n /** How many domains the connection serves, including this one; `0` without a connection. */\n connectionDomains: Schema.Number,\n reusable: Schema.Array(\n Schema.Struct({\n connectionId: Schema.String,\n provider: Schema.String,\n method: Storage.AuthMethod,\n }),\n ),\n providers: Schema.Array(\n Schema.Struct({\n id: Schema.String,\n name: Schema.String,\n methods: Schema.Array(MethodDescriptor),\n }),\n ),\n});\nexport type Snapshot = typeof Snapshot.Type;\n\n/**\n * How the client asks to connect. `values` is keyed by the provider's declared token fields, as\n * `Snapshot.providers[].methods[].fields` names them. Secrets are plaintext in transit and never\n * come back out.\n */\nexport class Token extends Schema.TaggedClass<Token>(\"@domainkit/server/Method/Token\")(\"Token\", {\n values: Schema.Record(Schema.String, Schema.String),\n}) {}\nexport class OAuth extends Schema.TaggedClass<OAuth>(\"@domainkit/server/Method/OAuth\")(\"OAuth\", {\n returnTo: Schema.optionalKey(Schema.String),\n}) {}\nexport class Integration extends Schema.TaggedClass<Integration>(\n \"@domainkit/server/Method/Integration\",\n)(\"Integration\", { returnTo: Schema.optionalKey(Schema.String) }) {}\nexport const Method = Schema.Union([Token, OAuth, Integration]);\nexport type Method = typeof Method.Type;\n\n/** A start without a domain connects the account alone; the customer picks a zone afterwards. */\nexport const StartPayload = Schema.Struct({\n domain: Schema.optionalKey(Schema.String),\n provider: Schema.String,\n method: Method,\n});\nexport type StartPayload = typeof StartPayload.Type;\n\n/** How the customer proves the account again. The provider is the connection's own. */\nexport const ReconnectPayload = Schema.Struct({ method: Method });\nexport type ReconnectPayload = typeof ReconnectPayload.Type;\n\n/** A zone the connection can serve, named for a customer to pick from. */\nexport class Candidate extends Schema.Class<Candidate>(\"@domainkit/server/Candidate\")({\n zone: Schema.String,\n label: Schema.String,\n}) {}\n\n/**\n * The connection the start produced. `label` names the account the way the customer will read it:\n * the attached zone's label, or the provider's name when the start carried no domain. `snapshot`\n * is the domain's state, and is null for a start that attached none.\n */\nexport class Connected extends Schema.TaggedClass<Connected>(\"@domainkit/server/Started/Connected\")(\n \"Connected\",\n {\n connectionId: Schema.String,\n provider: Schema.String,\n label: Schema.String,\n snapshot: Schema.NullOr(Snapshot),\n },\n) {}\nexport class Redirect extends Schema.TaggedClass<Redirect>(\"@domainkit/server/Started/Redirect\")(\n \"Redirect\",\n { authorizationUrl: Schema.String },\n) {}\nexport class SelectionRequired extends Schema.TaggedClass<SelectionRequired>(\n \"@domainkit/server/Started/SelectionRequired\",\n)(\"SelectionRequired\", { connectionId: Schema.String, candidates: Schema.Array(Candidate) }) {}\nexport const Started = Schema.Union([Connected, Redirect, SelectionRequired]);\nexport type Started = typeof Started.Type;\n\n/**\n * Which of the principal's connections already reaches a domain. The class names carry a\n * `Discovery` prefix because `Started` owns the unprefixed `SelectionRequired`; the wire tags are\n * the ones `Connect.Discovery` uses.\n */\nexport class DiscoveryResolved extends Schema.TaggedClass<DiscoveryResolved>(\n \"@domainkit/server/Discovery/Resolved\",\n)(\"Resolved\", { connectionId: Schema.String, zone: Schema.String, label: Schema.String }) {}\nexport class DiscoverySelectionRequired extends Schema.TaggedClass<DiscoverySelectionRequired>(\n \"@domainkit/server/Discovery/SelectionRequired\",\n)(\"SelectionRequired\", {\n candidates: Schema.Array(\n Schema.Struct({ connectionId: Schema.String, zone: Schema.String, label: Schema.String }),\n ),\n}) {}\n/** `host` names the registered provider whose nameservers serve the domain, by definition id. */\nexport class DiscoveryNotFound extends Schema.TaggedClass<DiscoveryNotFound>(\n \"@domainkit/server/Discovery/NotFound\",\n)(\"NotFound\", {\n nameservers: Schema.Array(Schema.String),\n host: Schema.NullOr(Schema.Struct({ provider: Schema.String })),\n}) {}\nexport const Discovery = Schema.Union([\n DiscoveryResolved,\n DiscoverySelectionRequired,\n DiscoveryNotFound,\n]);\nexport type Discovery = typeof Discovery.Type;\n\n/** One zone a connection reaches, named for a customer to pick from. */\nexport const Zone = Schema.Struct({\n connectionId: Schema.String,\n provider: Schema.String,\n zone: Schema.String,\n label: Schema.String,\n nameservers: Schema.optionalKey(Schema.Array(Schema.String)),\n});\nexport type Zone = typeof Zone.Type;\n\n/**\n * Every zone the principal's connections reach, and where each connection stands. A connection\n * marked `reconnect` contributes no zones and needs the customer to authorize it again.\n */\nexport const Zones = Schema.Struct({\n zones: Schema.Array(Zone),\n connections: Schema.Array(\n Schema.Struct({\n connectionId: Schema.String,\n provider: Schema.String,\n status: ConnectionStatus,\n }),\n ),\n /** Every provider a customer could connect, so a picker offers one without a second call. */\n providers: Schema.Array(\n Schema.Struct({\n id: Schema.String,\n name: Schema.String,\n methods: Schema.Array(MethodDescriptor),\n }),\n ),\n});\nexport type Zones = typeof Zones.Type;\n\nexport const AttachPayload = Schema.Struct({\n domain: Schema.String,\n /** Which candidate zone to attach when `SelectionRequired` came back. */\n zone: Schema.optionalKey(Schema.String),\n});\nexport type AttachPayload = typeof AttachPayload.Type;\n\nexport const PlanPayload = Schema.Struct({\n requirements: Schema.Array(DnsRecord.Model),\n});\nexport type PlanPayload = typeof PlanPayload.Type;\n\nexport const ObservePayload = Schema.Struct({\n /** Records to observe instead of the attachment's receipt; required for an unattached domain. */\n requirements: Schema.optionalKey(Schema.Array(DnsRecord.Model)),\n});\nexport type ObservePayload = typeof ObservePayload.Type;\n\nexport const ApprovePayload = Schema.Struct({\n /** A subset of the plan's writes; omit to approve every write. */\n operationIds: Schema.optionalKey(Schema.Array(Plan.OperationId)),\n});\nexport type ApprovePayload = typeof ApprovePayload.Type;\n\nexport const RejectPayload = Schema.Struct({\n /** Why the customer declined, for the audit trail. */\n reason: Schema.optionalKey(Schema.String),\n});\nexport type RejectPayload = typeof RejectPayload.Type;\n\n/** One durable plan -> approval -> receipt lifecycle, as `GET /plans/:planId` returns it. */\nexport const Attempt = Schema.Struct({\n plan: Plan.Model,\n status: Storage.AttemptStatus,\n approval: Schema.NullOr(Approval.Model),\n receipt: Schema.NullOr(Receipt.Model),\n rejection: Schema.NullOr(Storage.Rejection),\n});\nexport type Attempt = typeof Attempt.Type;\n\n/** `Verify.Readiness` on the wire; timestamps encode as ISO strings. */\nexport const Readiness = Schema.Struct({\n domain: Schema.String,\n attachmentId: Schema.NullOr(Schema.String),\n overall: Storage.Overall,\n requirements: Schema.Array(\n Schema.Struct({\n operationId: Schema.NullOr(Plan.OperationId),\n record: DnsRecord.Model,\n status: Storage.RequirementStatus,\n evidence: Schema.Array(Verify.Evidence),\n }),\n ),\n host: Schema.Array(Verify.HostEvidence),\n checkedAt: Schema.DateTimeUtcFromString,\n nextCheckAt: Schema.NullOr(Schema.DateTimeUtcFromString),\n});\nexport type Readiness = typeof Readiness.Type;\n\nconst Redirected = HttpApiSchema.WithHeaders(HttpApiSchema.Empty(302), {\n location: Schema.String,\n});\n\n// ---------------------------------------------------------------------------------------------\n// Errors: one wire body, the status from `DomainKit.Error.httpStatus`\n// ---------------------------------------------------------------------------------------------\n\n/** `DomainKit.Error` narrowed to the reasons that answer with `status`. */\nconst errorAt = (status: number) =>\n Errors.DomainKitError.check(\n Schema.makeFilter((error: Errors.DomainKitError) => error.httpStatus === status),\n ).pipe(HttpApiSchema.status(status));\n\nconst errors = [400, 401, 403, 404, 409, 500, 501, 502, 503].map(errorAt);\n\n// ---------------------------------------------------------------------------------------------\n// The group\n// ---------------------------------------------------------------------------------------------\n\nexport const group = HttpApiGroup.make(\"domainkit\")\n .add(\n HttpApiEndpoint.get(\"inspect\", \"/domains/:domain\", {\n params: { domain: Schema.String },\n success: Snapshot,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.get(\"discover\", \"/domains/:domain/discovery\", {\n params: { domain: Schema.String },\n success: Discovery,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.get(\"zones\", \"/zones\", {\n query: { provider: Schema.optionalKey(Schema.String) },\n success: Zones,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"start\", \"/connections\", {\n payload: StartPayload,\n success: Started,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.get(\"callback\", \"/callback/:provider\", {\n params: { provider: Schema.String },\n // `Connect.complete` reads every callback parameter off the request URL, because a provider\n // may return `error`, `teamId`, or `configurationId` too. These two are declared so the\n // OpenAPI document names what a provider is expected to send back.\n query: { state: Schema.String, code: Schema.optionalKey(Schema.String) },\n success: Redirected,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"reconnect\", \"/connections/:connectionId/reconnections\", {\n params: { connectionId: Schema.String },\n payload: ReconnectPayload,\n success: Started,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"attach\", \"/connections/:connectionId/attachments\", {\n params: { connectionId: Schema.String },\n payload: AttachPayload,\n success: Started,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.delete(\"detach\", \"/attachments/:attachmentId\", {\n params: { attachmentId: Schema.String },\n success: Schema.Void,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.delete(\"disconnect\", \"/connections/:connectionId\", {\n params: { connectionId: Schema.String },\n success: Schema.Void,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"createPlan\", \"/domains/:domain/plans\", {\n params: { domain: Schema.String },\n payload: PlanPayload,\n success: Plan.Model,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"approve\", \"/plans/:planId/approvals\", {\n params: { planId: Plan.PlanId },\n payload: ApprovePayload,\n success: Approval.Model,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"reject\", \"/plans/:planId/rejections\", {\n params: { planId: Plan.PlanId },\n payload: RejectPayload,\n success: Attempt,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"apply\", \"/approvals/:approvalId/apply\", {\n params: { approvalId: Approval.ApprovalId },\n success: Receipt.Model,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.get(\"plan\", \"/plans/:planId\", {\n params: { planId: Plan.PlanId },\n success: Attempt,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.get(\"receipt\", \"/receipts/:receiptId\", {\n params: { receiptId: Receipt.ReceiptId },\n success: Receipt.Model,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"observe\", \"/domains/:domain/observations\", {\n params: { domain: Schema.String },\n payload: ObservePayload,\n success: Readiness,\n error: errors,\n }),\n )\n .add(\n HttpApiEndpoint.post(\"cleanupPlan\", \"/receipts/:receiptId/cleanup-plans\", {\n params: { receiptId: Receipt.ReceiptId },\n success: Plan.Model,\n error: errors,\n }),\n );\n// Cleanup approve, reject, and apply reuse those routes: the attempt knows its kind.\n\nexport type Group = typeof group;\n\n/** Every route in the group, by name; what `Identity.authorize` is asked about. */\nexport type EndpointName = HttpApiGroup.Endpoints<Group>[\"identifier\"];\n\nexport interface Options {\n /**\n * Where the callback route is reachable from the provider, without the `/callback/:provider`\n * suffix. Defaults to the incoming request's origin and mount path.\n */\n readonly callbackBaseUrl?: string;\n /** Where interactive flows return when the callback carries no `returnTo`. */\n readonly defaultReturnTo?: string;\n}\n\n/** Everything the handlers need: the lifecycle services, Storage, and the host's `Identity`. */\nexport type Services =\n | Provision.Service\n | Cleanup.Service\n | Connect.Service\n | Verify.Service\n | Providers.Service\n | Storage.Service\n | Identity;\n\n// ---------------------------------------------------------------------------------------------\n// Handlers\n// ---------------------------------------------------------------------------------------------\n\nconst START_ROUTE = \"/connections\";\nconst reconnectRoute = (connectionId: string) =>\n `/connections/${encodeURIComponent(connectionId)}/reconnections`;\nconst callbackRoute = (provider: string) => `/callback/${encodeURIComponent(provider)}`;\n\nconst invalid = (message: string, field?: string) =>\n Errors.fail(new Reason.InvalidInput({ message, ...(field === undefined ? {} : { field }) }));\n\n/** The request as an absolute URL: the web original when there is one, else scheme plus `Host`. */\nconst absoluteUrl = (\n request: HttpServerRequest.HttpServerRequest,\n): Effect.Effect<URL, Errors.DomainKitError> =>\n Effect.suspend(() => {\n try {\n return Effect.succeed(new URL(request.originalUrl));\n } catch {\n const url = HttpServerRequest.toURL(request);\n return Option.isNone(url)\n ? invalid(\"The request has no usable origin; set callbackBaseUrl\", \"callbackBaseUrl\")\n : Effect.succeed(url.value);\n }\n });\n\n/**\n * The absolute URL the provider redirects back to. `callbackBaseUrl` wins when the host set it,\n * because a proxy that rewrites `Host` leaves the request pointing at an origin the browser never\n * sees; otherwise it follows the prefix the group is mounted at, derived by stripping the handling\n * route's own path off the request.\n */\nconst callbackUrlFor = (input: {\n readonly request: HttpServerRequest.HttpServerRequest;\n readonly provider: string;\n readonly options: Options;\n /** The path of the route serving this request, stripped to find the mount. */\n readonly route: string;\n}): Effect.Effect<URL, Errors.DomainKitError> =>\n Effect.suspend(() => {\n const suffix = `/callback/${encodeURIComponent(input.provider)}`;\n const configured = input.options.callbackBaseUrl;\n if (configured !== undefined) {\n return Effect.try({\n try: () => new URL(`${configured.replace(/\\/+$/, \"\")}${suffix}`),\n catch: () => callbackConfigurationError(`${configured} is not a URL`),\n });\n }\n return Effect.flatMap(absoluteUrl(input.request), (url) => {\n const path = url.pathname;\n if (!path.endsWith(input.route)) {\n return invalid(\n `The ${input.route} route is not mounted where it says; set callbackBaseUrl`,\n \"callbackBaseUrl\",\n );\n }\n return Effect.succeed(\n new URL(`${url.origin}${path.slice(0, path.length - input.route.length)}${suffix}`),\n );\n });\n });\n\n/**\n * One refusal for every way a callback can fail to match a flow: no such `state`, one that has\n * expired, one presented at another provider's route, and one presented by a session that did not\n * start it. The text is constant on purpose — a caller holding a leaked `state` must not be able\n * to tell \"no such flow\" from \"not your flow\".\n */\nconst noSuchFlow = Errors.fail(\n new Reason.InvalidInput({\n message: \"This callback does not match a connection you started\",\n field: \"state\",\n }),\n);\n\nconst callbackConfigurationError = (message: string) =>\n new Errors.DomainKitError({\n reason: new Reason.InvalidInput({ message, field: \"callbackBaseUrl\" }),\n });\n\n/**\n * Where the callback may send the customer: a path under the callback's own base, or an absolute\n * URL on its origin. A destination that leaves the application is an open redirect, whoever\n * supplied it.\n */\nconst sameOrigin = (destination: string, callback: URL): string | null => {\n try {\n // Resolving against the callback is what makes this safe: the URL parser normalizes the forms\n // a browser would follow off-origin (`//host`, `/\\host`, `\\/host`, encoded hosts) into a real\n // origin, so comparing origins catches every one of them.\n const url = new URL(destination, callback);\n return url.origin === callback.origin ? url.toString() : null;\n } catch {\n return null;\n }\n};\n\nconst snapshotOf = (snapshot: Connect.Snapshot): Snapshot => ({\n domain: snapshot.domain,\n attachment:\n snapshot.attachment === null\n ? null\n : { id: snapshot.attachment.id, label: snapshot.attachment.label },\n connectionId: snapshot.connection?.id ?? null,\n provider: snapshot.authorization?.provider ?? null,\n method: snapshot.authorization?.method ?? null,\n status:\n snapshot.connection === null\n ? \"disconnected\"\n : snapshot.authorization?.revocation === \"active\"\n ? \"connected\"\n : \"reconnect\",\n lastReceiptId: snapshot.lastReceiptId,\n connectionDomains: snapshot.connectionDomains,\n reusable: snapshot.reusable.map(({ connection, provider, method }) => ({\n connectionId: connection.id,\n provider,\n method,\n })),\n providers: snapshot.providers.map(({ id, name, methods }) => ({ id, name, methods })),\n});\n\n/** The wire method as `Connect` takes it; interactive cases carry the caller's return destination. */\nconst methodOf = (method: Method): Connect.Method => {\n switch (method._tag) {\n case \"Token\":\n return Connect.Method.token(method.values);\n case \"OAuth\":\n return Connect.Method.oauth(\n method.returnTo === undefined ? {} : { returnTo: method.returnTo },\n );\n case \"Integration\":\n return Connect.Method.integration(\n method.returnTo === undefined ? {} : { returnTo: method.returnTo },\n );\n }\n};\n\nconst candidatesOf = (\n candidates: ReadonlyArray<{ readonly zone: string; readonly label: string }>,\n): ReadonlyArray<Candidate> => candidates.map(({ zone, label }) => new Candidate({ zone, label }));\n\n/**\n * Handlers for `group` inside the host's API. Every handler derives `Principal` from `Identity`\n * for the request it is serving and provides it to the lifecycle services.\n */\nexport const layer = <ApiId extends string, Groups extends HttpApiGroup.Constraint>(\n api: HttpApi.HttpApi<ApiId, Groups>,\n options: Options = {},\n): Layer.Layer<HttpApiGroup.Service<ApiId, \"domainkit\">, never, Services> =>\n // The host's API carries its own groups beside this one; only `\"domainkit\"` is implemented here,\n // and `HttpApiBuilder.group` needs the group's own endpoint types to check the handlers.\n HttpApiBuilder.group(api as unknown as HttpApi.HttpApi<ApiId, Group>, \"domainkit\", (handlers) =>\n Effect.gen(function* () {\n const identity = yield* Identity;\n const connect = yield* Connect.Service;\n const provision = yield* Provision.Service;\n const cleanup = yield* Cleanup.Service;\n const verify = yield* Verify.Service;\n const storage = yield* Storage.Service;\n const providers = yield* Providers.Service;\n\n /**\n * Run a lifecycle effect as the principal the host derives from this request, once the host\n * has allowed that principal onto this route.\n */\n const as = <A>(\n endpoint: EndpointName,\n request: HttpServerRequest.HttpServerRequest,\n effect: Effect.Effect<A, Errors.DomainKitError, Principal.Service>,\n context?: IdentityContext,\n ): Effect.Effect<A, Errors.DomainKitError> =>\n Effect.flatMap(identity.principal(request, context), (principal) =>\n Effect.flatMap(identity.authorize?.(principal, endpoint) ?? Effect.void, () =>\n Effect.provideService(effect, Principal.Service, principal),\n ),\n );\n\n const snapshot = (domain: string) => Effect.map(connect.inspect(domain), snapshotOf);\n\n const discovered = (discovery: Connect.Discovery): Discovery => {\n switch (discovery._tag) {\n case \"Resolved\":\n return new DiscoveryResolved({\n connectionId: discovery.connectionId,\n zone: discovery.target.zone,\n label: discovery.target.label,\n });\n case \"SelectionRequired\":\n return new DiscoverySelectionRequired({\n candidates: discovery.candidates.map(({ connectionId, target }) => ({\n connectionId,\n zone: target.zone,\n label: target.label,\n })),\n });\n case \"NotFound\":\n return new DiscoveryNotFound({\n nameservers: discovery.nameservers,\n host: discovery.host,\n });\n }\n };\n\n /**\n * The connection a start or an attach produced. `label` is the zone the attachment named,\n * or the provider itself where the connection carries no domain yet.\n */\n const connected = (input: {\n readonly connection: Storage.Connection;\n readonly attachment: Storage.Attachment | null;\n readonly domain: string | undefined;\n }): Effect.Effect<Connected, Errors.DomainKitError, Principal.Service> =>\n Effect.gen(function* () {\n const authorization = yield* storage.authorizations.get(input.connection.authorizationId);\n const definition = yield* providers.get(authorization.provider);\n return new Connected({\n connectionId: input.connection.id,\n provider: authorization.provider,\n label: input.attachment?.label ?? definition.name,\n snapshot: input.domain === undefined ? null : yield* snapshot(input.domain),\n });\n });\n\n const startedOf = (\n started: Connect.Started,\n domain: string | undefined,\n ): Effect.Effect<Started, Errors.DomainKitError, Principal.Service> => {\n switch (started._tag) {\n case \"Connected\":\n return connected({\n attachment: started.attachment,\n connection: started.connection,\n domain,\n });\n case \"Redirect\":\n return Effect.succeed(new Redirect({ authorizationUrl: started.authorizationUrl }));\n case \"SelectionRequired\":\n return Effect.succeed(\n new SelectionRequired({\n connectionId: started.connection.id,\n candidates: candidatesOf(started.candidates),\n }),\n );\n }\n };\n\n /**\n * Attach with a zone the client picked out of a `SelectionRequired`. The provider target\n * behind a zone never crosses the wire, so resolve the candidates again and match by zone.\n */\n const attachAt = (input: {\n readonly connectionId: string;\n readonly domain: string;\n readonly zone: string | undefined;\n }): Effect.Effect<Started, Errors.DomainKitError, Principal.Service> =>\n Effect.gen(function* () {\n const connection = yield* storage.connections.get(input.connectionId);\n const attached = (attachment: Storage.Attachment) =>\n connected({ attachment, connection, domain: input.domain });\n const first = yield* connect.attach({\n connectionId: input.connectionId,\n domain: input.domain,\n });\n if (!(\"_tag\" in first)) return yield* attached(first);\n if (input.zone === undefined) {\n return new SelectionRequired({\n connectionId: input.connectionId,\n candidates: candidatesOf(first.candidates),\n });\n }\n const target = first.candidates.find(({ zone }) => zone === input.zone);\n if (target === undefined) {\n return yield* Errors.fail(new Reason.NotFound({ entity: \"zone\", id: input.zone }));\n }\n const second = yield* connect.attach({\n connectionId: input.connectionId,\n domain: input.domain,\n target,\n });\n return \"_tag\" in second\n ? yield* Errors.fail(new Reason.NotFound({ entity: \"zone\", id: input.zone }))\n : yield* attached(second);\n });\n\n /** Provisioning and cleanup share the approve and apply routes; the attempt knows its kind. */\n const kindOf = (attempt: Storage.Attempt) =>\n attempt.kind === \"cleanup\" ? cleanup : provision;\n\n return handlers\n .handle(\"inspect\", ({ params, request }) => as(\"inspect\", request, snapshot(params.domain)))\n .handle(\"discover\", ({ params, request }) =>\n as(\"discover\", request, Effect.map(connect.discover(params.domain), discovered)),\n )\n .handle(\"zones\", ({ query, request }) =>\n as(\n \"zones\",\n request,\n Effect.map(\n connect.zones(query.provider === undefined ? {} : { provider: query.provider }),\n (listing): Zones => ({\n zones: listing.zones.map(({ connectionId, provider, target }) => ({\n connectionId,\n provider,\n zone: target.zone,\n label: target.label,\n ...(target.nameservers === undefined ? {} : { nameservers: target.nameservers }),\n })),\n connections: listing.connections,\n providers: listing.providers,\n }),\n ),\n ),\n )\n .handle(\"start\", ({ payload, request }) =>\n as(\n \"start\",\n request,\n Effect.gen(function* () {\n // Token methods connect in one call; the interactive ones need somewhere to land.\n const callbackUrl =\n payload.method._tag === \"Token\"\n ? undefined\n : (yield* callbackUrlFor({\n request,\n provider: payload.provider,\n options,\n route: START_ROUTE,\n })).toString();\n const started = yield* connect.start({\n provider: payload.provider,\n ...(payload.domain === undefined ? {} : { domain: payload.domain }),\n method: methodOf(payload.method),\n ...(callbackUrl === undefined ? {} : { callbackUrl }),\n });\n return yield* startedOf(started, payload.domain);\n }),\n ),\n )\n .handle(\"reconnect\", ({ params, payload, request }) =>\n as(\n \"reconnect\",\n request,\n Effect.gen(function* () {\n const connection = yield* storage.connections.get(params.connectionId);\n const authorization = yield* storage.authorizations.get(connection.authorizationId);\n const callbackUrl =\n payload.method._tag === \"Token\"\n ? undefined\n : (yield* callbackUrlFor({\n request,\n provider: authorization.provider,\n options,\n route: reconnectRoute(params.connectionId),\n })).toString();\n const started = yield* connect.reconnect({\n connectionId: params.connectionId,\n method: methodOf(payload.method),\n ...(callbackUrl === undefined ? {} : { callbackUrl }),\n });\n return yield* startedOf(started, undefined);\n }),\n ),\n )\n .handle(\"callback\", ({ params, query, request }) =>\n Effect.gen(function* () {\n // The one route that establishes its identity from something other than the request.\n // A provider callback is a top-level navigation carrying a `state` the provider echoed\n // back and whatever credential the browser attaches by itself, so the flow DomainKit\n // recorded is the only authority on whose it is. Read its header first — a read, never\n // a spend, so a leaked `state` cannot burn the flow it names — tell the host which\n // flow is being finished, and then hold the host's answer against the record.\n const header = yield* storage.continuations\n .header(query.state)\n .pipe(\n Effect.catch((error) =>\n error.reason._tag === \"NotFound\" || error.reason._tag === \"Expired\"\n ? Effect.succeed(null)\n : Effect.fail(error),\n ),\n );\n const continuation =\n header !== null && header.provider === params.provider ? header : null;\n // The host authenticates even when there is nothing to finish, so the status cannot\n // tell a caller whether the `state` it holds names a flow at all.\n return yield* as(\n \"callback\",\n request,\n Effect.gen(function* () {\n // The owner comes from the record and the actor from the host's own session;\n // nothing in the query string contributes either. The scoped `get` below keeps\n // its owner filter, so a host that answers with the wrong principal still fails\n // closed even if this check were ever removed.\n const principal = yield* Principal.Service;\n if (\n continuation === null ||\n principal.ownerId !== continuation.ownerId ||\n principal.actorId !== continuation.actorId\n ) {\n return yield* noSuchFlow;\n }\n const url = yield* absoluteUrl(request);\n // Resolve the destination against the callback's public base, not the request: a\n // proxy that rewrites `Host` leaves the request on an origin the browser never\n // sees.\n const base = yield* callbackUrlFor({\n request,\n provider: params.provider,\n options,\n route: callbackRoute(params.provider),\n });\n // Where the customer lands comes from the continuation this owner started, never\n // from the provider's query string, and it is resolved before the callback is\n // spent: a server with nowhere to send them must not connect the provider and\n // then fail.\n // The host's authentication sits between the header read and this one, so the\n // flow can expire or be spent in that window. It refuses the same way it would\n // have a moment earlier, rather than changing shape because of when it happened.\n // This is also where the owner filter fails closed when a host resolves the wrong\n // principal, and that refusal must look like every other one.\n const flow = yield* storage.continuations\n .get(query.state)\n .pipe(\n Effect.catch((error) =>\n error.reason._tag === \"NotFound\" || error.reason._tag === \"Expired\"\n ? noSuchFlow\n : Effect.fail(error),\n ),\n );\n const requested = flow.returnTo ?? options.defaultReturnTo;\n if (requested === undefined) {\n return yield* invalid(\n \"The flow carried no returnTo and the server has no defaultReturnTo\",\n \"returnTo\",\n );\n }\n const destination = sameOrigin(requested, base);\n if (destination === null) {\n return yield* invalid(`${requested} leaves this application`, \"returnTo\");\n }\n yield* connect.complete({\n continuationId: query.state,\n callbackUrl: url.toString(),\n });\n return HttpApiSchema.withHeaders({\n body: undefined as void,\n headers: { location: destination },\n });\n }),\n continuation === null ? undefined : { endpoint: \"callback\", continuation },\n );\n }),\n )\n .handle(\"attach\", ({ params, payload, request }) =>\n as(\n \"attach\",\n request,\n attachAt({\n connectionId: params.connectionId,\n domain: payload.domain,\n zone: payload.zone,\n }),\n ),\n )\n .handle(\"detach\", ({ params, request }) =>\n as(\"detach\", request, connect.detach(params.attachmentId)),\n )\n .handle(\"disconnect\", ({ params, request }) =>\n as(\"disconnect\", request, connect.disconnect(params.connectionId)),\n )\n .handle(\"createPlan\", ({ params, payload, request }) =>\n as(\n \"createPlan\",\n request,\n provision.plan({ domain: params.domain, requirements: payload.requirements }),\n ),\n )\n .handle(\"approve\", ({ params, payload, request }) =>\n as(\n \"approve\",\n request,\n Effect.gen(function* () {\n const attempt = yield* storage.attempts.get(params.planId);\n return yield* kindOf(attempt).approve(params.planId, {\n ...(payload.operationIds === undefined\n ? {}\n : { operationIds: payload.operationIds, allowPartial: true }),\n });\n }),\n ),\n )\n .handle(\"reject\", ({ params, payload, request }) =>\n as(\n \"reject\",\n request,\n Effect.gen(function* () {\n const attempt = yield* storage.attempts.get(params.planId);\n return yield* kindOf(attempt).reject(params.planId, {\n ...(payload.reason === undefined ? {} : { reason: payload.reason }),\n });\n }),\n ),\n )\n .handle(\"apply\", ({ params, request }) =>\n as(\n \"apply\",\n request,\n Effect.gen(function* () {\n const attempt = yield* storage.attempts.byApproval(params.approvalId);\n return yield* kindOf(attempt).apply(params.approvalId);\n }),\n ),\n )\n .handle(\"plan\", ({ params, request }) =>\n as(\n \"plan\",\n request,\n Effect.map(storage.attempts.get(params.planId), (attempt) => ({\n plan: attempt.plan,\n status: attempt.status,\n approval: attempt.approval,\n receipt: attempt.receipt,\n rejection: attempt.rejection,\n })),\n ),\n )\n .handle(\"receipt\", ({ params, request }) =>\n as(\n \"receipt\",\n request,\n Effect.flatMap(storage.attempts.byReceipt(params.receiptId), (attempt) =>\n attempt.receipt === null\n ? Errors.fail(new Reason.NotFound({ entity: \"receipt\", id: params.receiptId }))\n : Effect.succeed(attempt.receipt),\n ),\n ),\n )\n .handle(\"observe\", ({ params, payload, request }) =>\n as(\n \"observe\",\n request,\n verify.observe({\n domain: params.domain,\n ...(payload.requirements === undefined ? {} : { requirements: payload.requirements }),\n }),\n ),\n )\n .handle(\"cleanupPlan\", ({ params, request }) =>\n as(\"cleanupPlan\", request, cleanup.plan({ receiptId: params.receiptId })),\n );\n }),\n ) as Layer.Layer<HttpApiGroup.Service<ApiId, \"domainkit\">, never, Services>;\n\n/** A standalone API for hosts not on HttpApi. Mount at any prefix. */\nexport const api: HttpApi.HttpApi<\"domainkit\", Group> = HttpApi.make(\"domainkit\").add(group);\n\nexport interface WebHandlerOptions extends Options {\n /** Mount every route under this path, e.g. `/api/domainkit`. */\n readonly prefix?: `/${string}`;\n}\n\n/** The Promise edge: a `fetch`-shaped handler over the group for hosts that are not Effect-native. */\nexport const toWebHandler = (\n services: Layer.Layer<Services, Errors.DomainKitError>,\n options: WebHandlerOptions = {},\n): {\n readonly handler: (request: Request) => Promise<Response>;\n readonly dispose: () => Promise<void>;\n} => {\n // `group.prefix` widens the endpoint paths in the type, which `Group` states unprefixed; the\n // handlers are keyed by endpoint identifier, so the routes still line up.\n const prefixed =\n options.prefix === undefined\n ? api\n : (HttpApi.make(\"domainkit\").add(group.prefix(options.prefix)) as unknown as HttpApi.HttpApi<\n \"domainkit\",\n Group\n >);\n const app = HttpApiBuilder.layer(prefixed).pipe(\n Layer.provide(layer(prefixed, options)),\n Layer.provide(services),\n Layer.provide(HttpServer.layerServices),\n );\n return HttpRouter.toWebHandler(app, { disableLogger: true });\n};\n"],"mappings":"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"}
package/dist/Storage.mjs CHANGED
@@ -29,6 +29,7 @@ var Storage_exports = /* @__PURE__ */ __exportAll({
29
29
  Capability: () => Capability,
30
30
  Connection: () => Connection,
31
31
  Continuation: () => Continuation,
32
+ ContinuationHeader: () => ContinuationHeader,
32
33
  Credential: () => Credential,
33
34
  Overall: () => Overall,
34
35
  Readiness: () => Readiness,
@@ -99,6 +100,16 @@ var Continuation = class extends Schema.Class("@domainkit/Storage/Continuation")
99
100
  returnTo: Schema.NullOr(Schema.String),
100
101
  expiresAt: Schema.DateTimeUtcFromString
101
102
  }) {};
103
+ /**
104
+ * Who a continuation belongs to, without the continuation itself. `payload` has no place here: it
105
+ * carries the PKCE `codeVerifier`, which only the owner-scoped read may reach.
106
+ */
107
+ var ContinuationHeader = class extends Schema.Class("@domainkit/Storage/ContinuationHeader")({
108
+ ownerId: Schema.String,
109
+ actorId: Schema.String,
110
+ provider: Schema.String,
111
+ expiresAt: Schema.DateTimeUtcFromString
112
+ }) {};
102
113
  const AttemptStatus = Schema.Literals([
103
114
  "planned",
104
115
  "approved",
@@ -210,6 +221,10 @@ const fromAsync = (service) => {
210
221
  continuations: {
211
222
  put: (continuation) => call("continuations.put", (p) => service.continuations.put(p, continuation)),
212
223
  get: (id) => call("continuations.get", (p) => service.continuations.get(p, id)),
224
+ header: (id) => Effect.tryPromise({
225
+ try: () => service.continuations.header(id),
226
+ catch: storageFailed("continuations.header")
227
+ }),
213
228
  consume: (id) => call("continuations.consume", (p) => service.continuations.consume(p, id))
214
229
  },
215
230
  attempts: {
@@ -233,6 +248,6 @@ const fromAsync = (service) => {
233
248
  };
234
249
  const layerFromAsync = (service) => Layer.succeed(Service)(fromAsync(service));
235
250
  //#endregion
236
- export { Attachment, Attempt, AttemptStatus, AuthMethod, Authorization, Capability, Connection, Continuation, Credential, Overall, Readiness, Rejection, RequirementStatus, Service, Storage_exports, fromAsync, layerFromAsync, layerMemory, layerMemoryWith, makeMemory };
251
+ export { Attachment, Attempt, AttemptStatus, AuthMethod, Authorization, Capability, Connection, Continuation, ContinuationHeader, Credential, Overall, Readiness, Rejection, RequirementStatus, Service, Storage_exports, fromAsync, layerFromAsync, layerMemory, layerMemoryWith, makeMemory };
237
252
 
238
253
  //# sourceMappingURL=Storage.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Storage.mjs","names":["Plan.PlanId","Plan.Kind","Plan.Model","Approval.Model","Receipt.Model","Receipt.ReceiptId","Plan.OperationId","DnsRecord.Model","Errors.isDomainKitError","Errors.DomainKitError","Reason.StorageFailed","Principal.Service","Errors.fail","Reason.Busy"],"sources":["../src/Storage.ts"],"sourcesContent":["/**\n * Everything DomainKit persists, behind one service the host installs once. Every method requires\n * `Principal` and is scoped by it. `@domainkit/capsuledb` ships the Postgres implementation;\n * `domainkit/testing` ships the memory one; both pass `Testing.conformance.storage`.\n *\n * Storage never sees plaintext credentials: `Connect` seals secrets through `Custody` before\n * writing a `Credential` row and opens them after reading one.\n *\n * Grouped by noun so the 25-odd methods stay navigable. Implementations must satisfy the\n * invariants documented per group; the conformance suite checks them.\n */\nimport { Context, DateTime, Effect, Layer, Option, Schema } from \"effect\";\n\nimport * as Approval from \"./Approval.ts\";\nimport * as DnsRecord from \"./DnsRecord.ts\";\nimport * as Errors from \"./internal/error.ts\";\nimport * as Reason from \"./Reason.ts\";\nimport { makeMemory } from \"./internal/storage-memory.ts\";\nimport * as Plan from \"./Plan.ts\";\nimport * as Principal from \"./Principal.ts\";\nimport * as Receipt from \"./Receipt.ts\";\n\nexport type Fx<A> = Effect.Effect<A, Errors.DomainKitError, Principal.Service>;\n\n// ---------------------------------------------------------------------------------------------\n// Rows\n// ---------------------------------------------------------------------------------------------\n\nexport const Capability = Schema.Literals([\"dns:read\", \"dns:write\"]);\nexport type Capability = typeof Capability.Type;\n\nexport const AuthMethod = Schema.Literals([\"token\", \"oauth\", \"integration\"]);\nexport type AuthMethod = typeof AuthMethod.Type;\n\n/** Sealed provider credential plus the metadata needed without unsealing it. */\nexport class Credential extends Schema.Class<Credential>(\"@domainkit/Storage/Credential\")({\n ciphertext: Schema.String,\n expiresAt: Schema.NullOr(Schema.DateTimeUtcFromString),\n rotatedAt: Schema.DateTimeUtcFromString,\n}) {}\n\n/** One provider authorization (an OAuth grant or a token) owned by a principal. */\nexport class Authorization extends Schema.Class<Authorization>(\"@domainkit/Storage/Authorization\")({\n id: Schema.String,\n ownerId: Schema.String,\n provider: Schema.String,\n method: AuthMethod,\n capabilities: Schema.Array(Capability),\n /** Provider-specific account context (account id, team id, ...) decoded by the provider's `context` schema. */\n context: Schema.Unknown,\n revocation: Schema.Literals([\"active\", \"pending\", \"revoked\"]),\n createdBy: Schema.String,\n createdAt: Schema.DateTimeUtcFromString,\n}) {}\n\n/** A connection is the principal-facing handle over an authorization; one authorization, many domains. */\nexport class Connection extends Schema.Class<Connection>(\"@domainkit/Storage/Connection\")({\n id: Schema.String,\n ownerId: Schema.String,\n authorizationId: Schema.String,\n createdAt: Schema.DateTimeUtcFromString,\n}) {}\n\nexport class Attachment extends Schema.Class<Attachment>(\"@domainkit/Storage/Attachment\")({\n id: Schema.String,\n ownerId: Schema.String,\n connectionId: Schema.String,\n domain: Schema.String,\n zone: Schema.String,\n /** The target's label as the provider gave it at attach time, so a UI names the account without a provider call. */\n label: Schema.String,\n /** Provider zone identity (zone id, account id) decoded by the provider's `context` schema. */\n target: Schema.Unknown,\n createdAt: Schema.DateTimeUtcFromString,\n}) {}\n\n/** Interactive-flow state between `Connect.start` and `Connect.complete`. */\nexport class Continuation extends Schema.Class<Continuation>(\"@domainkit/Storage/Continuation\")({\n id: Schema.String,\n ownerId: Schema.String,\n actorId: Schema.String,\n provider: Schema.String,\n payload: Schema.Unknown,\n returnTo: Schema.NullOr(Schema.String),\n expiresAt: Schema.DateTimeUtcFromString,\n}) {}\n\nexport const AttemptStatus = Schema.Literals([\n \"planned\",\n \"approved\",\n \"applying\",\n \"complete\",\n \"partial\",\n \"failed\",\n \"expired\",\n \"rejected\",\n]);\nexport type AttemptStatus = typeof AttemptStatus.Type;\n\n/** Who declined the plan, why, and when. Terminal: the domain needs a new plan. */\nexport const Rejection = Schema.Struct({\n actorId: Schema.String,\n reason: Schema.NullOr(Schema.String),\n at: Schema.DateTimeUtcFromString,\n});\nexport type Rejection = typeof Rejection.Type;\n\n/** One durable plan -> approval -> receipt lifecycle. */\nexport class Attempt extends Schema.Class<Attempt>(\"@domainkit/Storage/Attempt\")({\n id: Plan.PlanId,\n ownerId: Schema.String,\n attachmentId: Schema.String,\n kind: Plan.Kind,\n status: AttemptStatus,\n plan: Plan.Model,\n approval: Schema.NullOr(Approval.Model),\n receipt: Schema.NullOr(Receipt.Model),\n rejection: Schema.NullOr(Rejection),\n /** Cleanup attempts point at the provisioning receipt they undo. */\n sourceReceiptId: Schema.NullOr(Receipt.ReceiptId),\n leaseExpiresAt: Schema.NullOr(Schema.DateTimeUtcFromString),\n /** Why the last apply stopped before any write; cleared on the next claim. */\n failure: Schema.NullOr(Schema.String),\n updatedAt: Schema.DateTimeUtcFromString,\n}) {}\n\nexport const RequirementStatus = Schema.Literals([\"satisfied\", \"missing\", \"mismatch\", \"unknown\"]);\nexport type RequirementStatus = typeof RequirementStatus.Type;\n\nexport const Overall = Schema.Literals([\"ready\", \"pending\", \"failed\"]);\nexport type Overall = typeof Overall.Type;\n\n/**\n * Latest observed readiness for one domain, written by `Verify.observe`. Keyed by domain so\n * observe-only hosts (no attachment, public DNS alone) get the same row; `attachmentId` links the\n * attachment when one exists.\n */\nexport class Readiness extends Schema.Class<Readiness>(\"@domainkit/Storage/Readiness\")({\n domain: Schema.String,\n attachmentId: Schema.NullOr(Schema.String),\n ownerId: Schema.String,\n overall: Overall,\n requirements: Schema.Array(\n Schema.Struct({\n operationId: Schema.NullOr(Plan.OperationId),\n record: DnsRecord.Model,\n status: RequirementStatus,\n /** Encoded `Verify.Evidence` values. */\n evidence: Schema.Array(Schema.Unknown),\n }),\n ),\n /** Encoded `Verify.HostEvidence` values, keyed by `source` on merge. */\n host: Schema.Array(Schema.Unknown),\n /** When the current pending streak began; drives the backoff ladder. */\n pendingSince: Schema.NullOr(Schema.DateTimeUtcFromString),\n checkedAt: Schema.DateTimeUtcFromString,\n nextCheckAt: Schema.NullOr(Schema.DateTimeUtcFromString),\n}) {}\n\n// ---------------------------------------------------------------------------------------------\n// Service\n// ---------------------------------------------------------------------------------------------\n\nexport interface Interface {\n readonly authorizations: {\n /**\n * Insert, or CAS-replace when `expectedId` is set: the row must exist under this owner with an\n * active revocation state, else `NotFound` / `Busy`.\n */\n readonly upsert: (input: {\n readonly authorization: Authorization;\n readonly credential: Credential;\n readonly expectedId?: string;\n }) => Fx<Authorization>;\n readonly get: (id: string) => Fx<Authorization>;\n readonly credential: (id: string) => Fx<Credential>;\n readonly rotate: (id: string, credential: Credential) => Fx<void>;\n readonly promoteCapabilities: (id: string, capabilities: ReadonlyArray<Capability>) => Fx<void>;\n /**\n * Two-phase: mark `pending`, run `revoke` outside any transaction, then delete. A failed\n * `revoke` leaves the row pending; `recoverRevocations` finishes it later.\n */\n readonly revoke: <E, R>(\n id: string,\n revoke: Effect.Effect<void, E, R>,\n ) => Effect.Effect<void, Errors.DomainKitError | E, Principal.Service | R>;\n readonly recoverRevocations: <E, R>(\n revoke: (authorization: Authorization) => Effect.Effect<void, E, R>,\n ) => Effect.Effect<number, Errors.DomainKitError | E, Principal.Service | R>;\n };\n readonly connections: {\n readonly create: (authorizationId: string) => Fx<Connection>;\n readonly get: (id: string) => Fx<Connection>;\n readonly list: (filter?: { readonly provider?: string }) => Fx<ReadonlyArray<Connection>>;\n /** Fails `InvalidInput` while attachments still reference the connection. */\n readonly remove: (id: string) => Fx<void>;\n };\n readonly attachments: {\n /** One attachment per (owner, domain); a duplicate fails `InvalidInput`. */\n readonly create: (input: {\n readonly connectionId: string;\n readonly domain: string;\n readonly zone: string;\n readonly label: string;\n readonly target: unknown;\n }) => Fx<Attachment>;\n readonly get: (id: string) => Fx<Attachment>;\n readonly byDomain: (domain: string) => Fx<Option.Option<Attachment>>;\n readonly list: (connectionId: string) => Fx<ReadonlyArray<Attachment>>;\n readonly remove: (id: string) => Fx<void>;\n };\n readonly continuations: {\n readonly put: (continuation: Continuation) => Fx<void>;\n /** Read without spending; a late read fails `Expired`. */\n readonly get: (id: string) => Fx<Continuation>;\n /** Exactly-once: the second consume of the same id fails `NotFound`; a late one fails `Expired`. */\n readonly consume: (id: string) => Fx<Continuation>;\n };\n readonly attempts: {\n readonly create: (attempt: Attempt) => Fx<Attempt>;\n readonly get: (id: Plan.PlanId) => Fx<Attempt>;\n readonly byApproval: (id: Approval.ApprovalId) => Fx<Attempt>;\n readonly byReceipt: (id: Receipt.ReceiptId) => Fx<Attempt>;\n readonly latest: (attachmentId: string, kind: Plan.Kind) => Fx<Option.Option<Attempt>>;\n /** `planned` -> `approved`; approving again with the same approval id is a no-op, anything else fails `Stale`. */\n readonly approve: (id: Plan.PlanId, approval: Approval.Model) => Fx<Attempt>;\n /**\n * `planned` -> `rejected` (terminal). Rejecting again returns the row unchanged; any other\n * status fails `Stale`, `expired` fails `Expired`, and a digest mismatch fails `Stale`.\n */\n readonly reject: (\n id: Plan.PlanId,\n input: {\n readonly digest: Plan.Digest;\n readonly actorId: string;\n readonly reason: string | null;\n },\n ) => Fx<Attempt>;\n /**\n * Atomic transition to `applying` with a lease from `approved`, `failed`, or an expired\n * `applying`; fails `Busy` while a lease is live and `Stale` from any other status.\n */\n readonly claim: (id: Plan.PlanId, lease: DateTime.Utc) => Fx<Attempt>;\n readonly complete: (id: Plan.PlanId, receipt: Receipt.Model) => Fx<Attempt>;\n readonly fail: (id: Plan.PlanId, message: string) => Fx<Attempt>;\n };\n readonly readiness: {\n /** One row per (owner, domain); `attachmentId`, when set, must exist for the owner. */\n readonly put: (readiness: Readiness) => Fx<void>;\n readonly get: (domain: string) => Fx<Option.Option<Readiness>>;\n };\n /** Single-flight guard keyed by string (credential refresh, apply). Fails `Busy` rather than waiting. */\n readonly withLock: <A, E, R>(\n key: string,\n effect: Effect.Effect<A, E, R>,\n ) => Effect.Effect<A, E | Errors.DomainKitError, R | Principal.Service>;\n}\n\nexport class Service extends Context.Service<Service, Interface>()(\"@domainkit/Storage\") {}\n\nexport interface MemoryOptions {\n /** Fault injection: runs before every mutation commits; failing here leaves state untouched. */\n readonly beforeCommit?: (operation: string) => Effect.Effect<void, Errors.DomainKitError>;\n}\n\n/** In-memory implementation for tests and local development. Not for production. */\nexport const layerMemory: Layer.Layer<Service> = Layer.sync(Service)(() => makeMemory());\n\n/** `layerMemory` with fault injection for storage-failure tests. */\nexport const layerMemoryWith = (options: MemoryOptions): Layer.Layer<Service> =>\n Layer.sync(Service)(() => makeMemory(options));\n\nexport { makeMemory };\n\n// ---------------------------------------------------------------------------------------------\n// Async adapter\n// ---------------------------------------------------------------------------------------------\n\n/**\n * A Promise-shaped implementation for hosts that persist without Effect. Every method takes the\n * principal explicitly. Rejections become `StorageFailed`; reject with a `DomainKit.Error` to keep\n * a typed reason. Two-phase revocation and locking are split into plain steps so no callback has\n * to run an Effect.\n */\nexport interface AsyncInterface {\n readonly authorizations: {\n readonly upsert: (\n principal: Principal.Interface,\n input: {\n readonly authorization: Authorization;\n readonly credential: Credential;\n readonly expectedId?: string;\n },\n ) => Promise<Authorization>;\n readonly get: (principal: Principal.Interface, id: string) => Promise<Authorization>;\n readonly credential: (principal: Principal.Interface, id: string) => Promise<Credential>;\n readonly rotate: (\n principal: Principal.Interface,\n id: string,\n credential: Credential,\n ) => Promise<void>;\n readonly promoteCapabilities: (\n principal: Principal.Interface,\n id: string,\n capabilities: ReadonlyArray<Capability>,\n ) => Promise<void>;\n /** Mark `pending` durably; must be idempotent. */\n readonly prepareRevocation: (\n principal: Principal.Interface,\n id: string,\n ) => Promise<Authorization>;\n /** Delete the authorization after the provider confirmed revocation. */\n readonly completeRevocation: (principal: Principal.Interface, id: string) => Promise<void>;\n readonly pendingRevocations: (\n principal: Principal.Interface,\n ) => Promise<ReadonlyArray<Authorization>>;\n };\n readonly connections: {\n readonly create: (\n principal: Principal.Interface,\n authorizationId: string,\n ) => Promise<Connection>;\n readonly get: (principal: Principal.Interface, id: string) => Promise<Connection>;\n readonly list: (\n principal: Principal.Interface,\n filter?: { readonly provider?: string },\n ) => Promise<ReadonlyArray<Connection>>;\n readonly remove: (principal: Principal.Interface, id: string) => Promise<void>;\n };\n readonly attachments: {\n readonly create: (\n principal: Principal.Interface,\n input: {\n readonly connectionId: string;\n readonly domain: string;\n readonly zone: string;\n readonly label: string;\n readonly target: unknown;\n },\n ) => Promise<Attachment>;\n readonly get: (principal: Principal.Interface, id: string) => Promise<Attachment>;\n readonly byDomain: (\n principal: Principal.Interface,\n domain: string,\n ) => Promise<Attachment | null>;\n readonly list: (\n principal: Principal.Interface,\n connectionId: string,\n ) => Promise<ReadonlyArray<Attachment>>;\n readonly remove: (principal: Principal.Interface, id: string) => Promise<void>;\n };\n readonly continuations: {\n readonly put: (principal: Principal.Interface, continuation: Continuation) => Promise<void>;\n readonly get: (principal: Principal.Interface, id: string) => Promise<Continuation>;\n readonly consume: (principal: Principal.Interface, id: string) => Promise<Continuation>;\n };\n readonly attempts: {\n readonly create: (principal: Principal.Interface, attempt: Attempt) => Promise<Attempt>;\n readonly get: (principal: Principal.Interface, id: Plan.PlanId) => Promise<Attempt>;\n readonly byApproval: (\n principal: Principal.Interface,\n id: Approval.ApprovalId,\n ) => Promise<Attempt>;\n readonly byReceipt: (principal: Principal.Interface, id: Receipt.ReceiptId) => Promise<Attempt>;\n readonly latest: (\n principal: Principal.Interface,\n attachmentId: string,\n kind: Plan.Kind,\n ) => Promise<Attempt | null>;\n readonly approve: (\n principal: Principal.Interface,\n id: Plan.PlanId,\n approval: Approval.Model,\n ) => Promise<Attempt>;\n readonly reject: (\n principal: Principal.Interface,\n id: Plan.PlanId,\n input: {\n readonly digest: Plan.Digest;\n readonly actorId: string;\n readonly reason: string | null;\n },\n ) => Promise<Attempt>;\n readonly claim: (\n principal: Principal.Interface,\n id: Plan.PlanId,\n lease: DateTime.Utc,\n ) => Promise<Attempt>;\n readonly complete: (\n principal: Principal.Interface,\n id: Plan.PlanId,\n receipt: Receipt.Model,\n ) => Promise<Attempt>;\n readonly fail: (\n principal: Principal.Interface,\n id: Plan.PlanId,\n message: string,\n ) => Promise<Attempt>;\n };\n readonly readiness: {\n readonly put: (principal: Principal.Interface, readiness: Readiness) => Promise<void>;\n readonly get: (principal: Principal.Interface, domain: string) => Promise<Readiness | null>;\n };\n /** Return `false` when another holder has the key. */\n readonly acquireLock: (principal: Principal.Interface, key: string) => Promise<boolean>;\n readonly releaseLock: (principal: Principal.Interface, key: string) => Promise<void>;\n}\n\nconst storageFailed = (operation: string) => (cause: unknown) =>\n Errors.isDomainKitError(cause)\n ? cause\n : new Errors.DomainKitError({\n reason: new Reason.StorageFailed({\n operation,\n message: cause instanceof Error ? cause.message : String(cause),\n }),\n });\n\n/** Wraps a Promise-shaped implementation. Rejections become `StorageFailed`. */\nexport const fromAsync = (service: AsyncInterface): Interface => {\n const call = <A>(operation: string, run: (principal: Principal.Interface) => Promise<A>): Fx<A> =>\n Effect.flatMap(Principal.Service, (principal) =>\n Effect.tryPromise({ try: () => run(principal), catch: storageFailed(operation) }),\n );\n const option = <A>(\n operation: string,\n run: (principal: Principal.Interface) => Promise<A | null>,\n ) => call(operation, run).pipe(Effect.map(Option.fromNullishOr));\n return {\n authorizations: {\n upsert: (input) =>\n call(\"authorizations.upsert\", (p) => service.authorizations.upsert(p, input)),\n get: (id) => call(\"authorizations.get\", (p) => service.authorizations.get(p, id)),\n credential: (id) =>\n call(\"authorizations.credential\", (p) => service.authorizations.credential(p, id)),\n rotate: (id, credential) =>\n call(\"authorizations.rotate\", (p) => service.authorizations.rotate(p, id, credential)),\n promoteCapabilities: (id, capabilities) =>\n call(\"authorizations.promoteCapabilities\", (p) =>\n service.authorizations.promoteCapabilities(p, id, capabilities),\n ),\n revoke: (id, revoke) =>\n call(\"authorizations.prepareRevocation\", (p) =>\n service.authorizations.prepareRevocation(p, id),\n ).pipe(\n Effect.flatMap(() => revoke),\n Effect.flatMap(() =>\n call(\"authorizations.completeRevocation\", (p) =>\n service.authorizations.completeRevocation(p, id),\n ),\n ),\n ),\n recoverRevocations: (revoke) =>\n call(\"authorizations.pendingRevocations\", (p) =>\n service.authorizations.pendingRevocations(p),\n ).pipe(\n Effect.flatMap((pending) =>\n Effect.forEach(pending, (authorization) =>\n revoke(authorization).pipe(\n Effect.flatMap(() =>\n call(\"authorizations.completeRevocation\", (p) =>\n service.authorizations.completeRevocation(p, authorization.id),\n ),\n ),\n ),\n ),\n ),\n Effect.map((completed) => completed.length),\n ),\n },\n connections: {\n create: (authorizationId) =>\n call(\"connections.create\", (p) => service.connections.create(p, authorizationId)),\n get: (id) => call(\"connections.get\", (p) => service.connections.get(p, id)),\n list: (filter) => call(\"connections.list\", (p) => service.connections.list(p, filter)),\n remove: (id) => call(\"connections.remove\", (p) => service.connections.remove(p, id)),\n },\n attachments: {\n create: (input) => call(\"attachments.create\", (p) => service.attachments.create(p, input)),\n get: (id) => call(\"attachments.get\", (p) => service.attachments.get(p, id)),\n byDomain: (domain) =>\n option(\"attachments.byDomain\", (p) => service.attachments.byDomain(p, domain)),\n list: (connectionId) =>\n call(\"attachments.list\", (p) => service.attachments.list(p, connectionId)),\n remove: (id) => call(\"attachments.remove\", (p) => service.attachments.remove(p, id)),\n },\n continuations: {\n put: (continuation) =>\n call(\"continuations.put\", (p) => service.continuations.put(p, continuation)),\n get: (id) => call(\"continuations.get\", (p) => service.continuations.get(p, id)),\n consume: (id) => call(\"continuations.consume\", (p) => service.continuations.consume(p, id)),\n },\n attempts: {\n create: (attempt) => call(\"attempts.create\", (p) => service.attempts.create(p, attempt)),\n get: (id) => call(\"attempts.get\", (p) => service.attempts.get(p, id)),\n byApproval: (id) => call(\"attempts.byApproval\", (p) => service.attempts.byApproval(p, id)),\n byReceipt: (id) => call(\"attempts.byReceipt\", (p) => service.attempts.byReceipt(p, id)),\n latest: (attachmentId, kind) =>\n option(\"attempts.latest\", (p) => service.attempts.latest(p, attachmentId, kind)),\n approve: (id, approval) =>\n call(\"attempts.approve\", (p) => service.attempts.approve(p, id, approval)),\n reject: (id, input) => call(\"attempts.reject\", (p) => service.attempts.reject(p, id, input)),\n claim: (id, lease) => call(\"attempts.claim\", (p) => service.attempts.claim(p, id, lease)),\n complete: (id, receipt) =>\n call(\"attempts.complete\", (p) => service.attempts.complete(p, id, receipt)),\n fail: (id, message) => call(\"attempts.fail\", (p) => service.attempts.fail(p, id, message)),\n },\n readiness: {\n put: (readiness) => call(\"readiness.put\", (p) => service.readiness.put(p, readiness)),\n get: (domain) => option(\"readiness.get\", (p) => service.readiness.get(p, domain)),\n },\n withLock: (key, effect) =>\n Effect.acquireRelease(\n call(\"acquireLock\", (p) => service.acquireLock(p, key)).pipe(\n Effect.flatMap((acquired) =>\n acquired ? Effect.void : Errors.fail(new Reason.Busy({ key })),\n ),\n ),\n () => call(\"releaseLock\", (p) => service.releaseLock(p, key)).pipe(Effect.ignore),\n ).pipe(\n Effect.flatMap(() => effect),\n Effect.scoped,\n ),\n };\n};\n\nexport const layerFromAsync = (service: AsyncInterface): Layer.Layer<Service> =>\n Layer.succeed(Service)(fromAsync(service));\n"],"mappings":"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"}
1
+ {"version":3,"file":"Storage.mjs","names":["Plan.PlanId","Plan.Kind","Plan.Model","Approval.Model","Receipt.Model","Receipt.ReceiptId","Plan.OperationId","DnsRecord.Model","Errors.isDomainKitError","Errors.DomainKitError","Reason.StorageFailed","Principal.Service","Errors.fail","Reason.Busy"],"sources":["../src/Storage.ts"],"sourcesContent":["/**\n * Everything DomainKit persists, behind one service the host installs once. Every method requires\n * `Principal` and is scoped by it. `@domainkit/capsuledb` ships the Postgres implementation;\n * `domainkit/testing` ships the memory one; both pass `Testing.conformance.storage`.\n *\n * Storage never sees plaintext credentials: `Connect` seals secrets through `Custody` before\n * writing a `Credential` row and opens them after reading one.\n *\n * Grouped by noun so the 25-odd methods stay navigable. Implementations must satisfy the\n * invariants documented per group; the conformance suite checks them.\n */\nimport { Context, DateTime, Effect, Layer, Option, Schema } from \"effect\";\n\nimport * as Approval from \"./Approval.ts\";\nimport * as DnsRecord from \"./DnsRecord.ts\";\nimport * as Errors from \"./internal/error.ts\";\nimport * as Reason from \"./Reason.ts\";\nimport { makeMemory } from \"./internal/storage-memory.ts\";\nimport * as Plan from \"./Plan.ts\";\nimport * as Principal from \"./Principal.ts\";\nimport * as Receipt from \"./Receipt.ts\";\n\nexport type Fx<A> = Effect.Effect<A, Errors.DomainKitError, Principal.Service>;\n\n// ---------------------------------------------------------------------------------------------\n// Rows\n// ---------------------------------------------------------------------------------------------\n\nexport const Capability = Schema.Literals([\"dns:read\", \"dns:write\"]);\nexport type Capability = typeof Capability.Type;\n\nexport const AuthMethod = Schema.Literals([\"token\", \"oauth\", \"integration\"]);\nexport type AuthMethod = typeof AuthMethod.Type;\n\n/** Sealed provider credential plus the metadata needed without unsealing it. */\nexport class Credential extends Schema.Class<Credential>(\"@domainkit/Storage/Credential\")({\n ciphertext: Schema.String,\n expiresAt: Schema.NullOr(Schema.DateTimeUtcFromString),\n rotatedAt: Schema.DateTimeUtcFromString,\n}) {}\n\n/** One provider authorization (an OAuth grant or a token) owned by a principal. */\nexport class Authorization extends Schema.Class<Authorization>(\"@domainkit/Storage/Authorization\")({\n id: Schema.String,\n ownerId: Schema.String,\n provider: Schema.String,\n method: AuthMethod,\n capabilities: Schema.Array(Capability),\n /** Provider-specific account context (account id, team id, ...) decoded by the provider's `context` schema. */\n context: Schema.Unknown,\n revocation: Schema.Literals([\"active\", \"pending\", \"revoked\"]),\n createdBy: Schema.String,\n createdAt: Schema.DateTimeUtcFromString,\n}) {}\n\n/** A connection is the principal-facing handle over an authorization; one authorization, many domains. */\nexport class Connection extends Schema.Class<Connection>(\"@domainkit/Storage/Connection\")({\n id: Schema.String,\n ownerId: Schema.String,\n authorizationId: Schema.String,\n createdAt: Schema.DateTimeUtcFromString,\n}) {}\n\nexport class Attachment extends Schema.Class<Attachment>(\"@domainkit/Storage/Attachment\")({\n id: Schema.String,\n ownerId: Schema.String,\n connectionId: Schema.String,\n domain: Schema.String,\n zone: Schema.String,\n /** The target's label as the provider gave it at attach time, so a UI names the account without a provider call. */\n label: Schema.String,\n /** Provider zone identity (zone id, account id) decoded by the provider's `context` schema. */\n target: Schema.Unknown,\n createdAt: Schema.DateTimeUtcFromString,\n}) {}\n\n/** Interactive-flow state between `Connect.start` and `Connect.complete`. */\nexport class Continuation extends Schema.Class<Continuation>(\"@domainkit/Storage/Continuation\")({\n id: Schema.String,\n ownerId: Schema.String,\n actorId: Schema.String,\n provider: Schema.String,\n payload: Schema.Unknown,\n returnTo: Schema.NullOr(Schema.String),\n expiresAt: Schema.DateTimeUtcFromString,\n}) {}\n\n/**\n * Who a continuation belongs to, without the continuation itself. `payload` has no place here: it\n * carries the PKCE `codeVerifier`, which only the owner-scoped read may reach.\n */\nexport class ContinuationHeader extends Schema.Class<ContinuationHeader>(\n \"@domainkit/Storage/ContinuationHeader\",\n)({\n ownerId: Schema.String,\n actorId: Schema.String,\n provider: Schema.String,\n expiresAt: Schema.DateTimeUtcFromString,\n}) {}\n\nexport const AttemptStatus = Schema.Literals([\n \"planned\",\n \"approved\",\n \"applying\",\n \"complete\",\n \"partial\",\n \"failed\",\n \"expired\",\n \"rejected\",\n]);\nexport type AttemptStatus = typeof AttemptStatus.Type;\n\n/** Who declined the plan, why, and when. Terminal: the domain needs a new plan. */\nexport const Rejection = Schema.Struct({\n actorId: Schema.String,\n reason: Schema.NullOr(Schema.String),\n at: Schema.DateTimeUtcFromString,\n});\nexport type Rejection = typeof Rejection.Type;\n\n/** One durable plan -> approval -> receipt lifecycle. */\nexport class Attempt extends Schema.Class<Attempt>(\"@domainkit/Storage/Attempt\")({\n id: Plan.PlanId,\n ownerId: Schema.String,\n attachmentId: Schema.String,\n kind: Plan.Kind,\n status: AttemptStatus,\n plan: Plan.Model,\n approval: Schema.NullOr(Approval.Model),\n receipt: Schema.NullOr(Receipt.Model),\n rejection: Schema.NullOr(Rejection),\n /** Cleanup attempts point at the provisioning receipt they undo. */\n sourceReceiptId: Schema.NullOr(Receipt.ReceiptId),\n leaseExpiresAt: Schema.NullOr(Schema.DateTimeUtcFromString),\n /** Why the last apply stopped before any write; cleared on the next claim. */\n failure: Schema.NullOr(Schema.String),\n updatedAt: Schema.DateTimeUtcFromString,\n}) {}\n\nexport const RequirementStatus = Schema.Literals([\"satisfied\", \"missing\", \"mismatch\", \"unknown\"]);\nexport type RequirementStatus = typeof RequirementStatus.Type;\n\nexport const Overall = Schema.Literals([\"ready\", \"pending\", \"failed\"]);\nexport type Overall = typeof Overall.Type;\n\n/**\n * Latest observed readiness for one domain, written by `Verify.observe`. Keyed by domain so\n * observe-only hosts (no attachment, public DNS alone) get the same row; `attachmentId` links the\n * attachment when one exists.\n */\nexport class Readiness extends Schema.Class<Readiness>(\"@domainkit/Storage/Readiness\")({\n domain: Schema.String,\n attachmentId: Schema.NullOr(Schema.String),\n ownerId: Schema.String,\n overall: Overall,\n requirements: Schema.Array(\n Schema.Struct({\n operationId: Schema.NullOr(Plan.OperationId),\n record: DnsRecord.Model,\n status: RequirementStatus,\n /** Encoded `Verify.Evidence` values. */\n evidence: Schema.Array(Schema.Unknown),\n }),\n ),\n /** Encoded `Verify.HostEvidence` values, keyed by `source` on merge. */\n host: Schema.Array(Schema.Unknown),\n /** When the current pending streak began; drives the backoff ladder. */\n pendingSince: Schema.NullOr(Schema.DateTimeUtcFromString),\n checkedAt: Schema.DateTimeUtcFromString,\n nextCheckAt: Schema.NullOr(Schema.DateTimeUtcFromString),\n}) {}\n\n// ---------------------------------------------------------------------------------------------\n// Service\n// ---------------------------------------------------------------------------------------------\n\nexport interface Interface {\n readonly authorizations: {\n /**\n * Insert, or CAS-replace when `expectedId` is set: the row must exist under this owner with an\n * active revocation state, else `NotFound` / `Busy`.\n */\n readonly upsert: (input: {\n readonly authorization: Authorization;\n readonly credential: Credential;\n readonly expectedId?: string;\n }) => Fx<Authorization>;\n readonly get: (id: string) => Fx<Authorization>;\n readonly credential: (id: string) => Fx<Credential>;\n readonly rotate: (id: string, credential: Credential) => Fx<void>;\n readonly promoteCapabilities: (id: string, capabilities: ReadonlyArray<Capability>) => Fx<void>;\n /**\n * Two-phase: mark `pending`, run `revoke` outside any transaction, then delete. A failed\n * `revoke` leaves the row pending; `recoverRevocations` finishes it later.\n */\n readonly revoke: <E, R>(\n id: string,\n revoke: Effect.Effect<void, E, R>,\n ) => Effect.Effect<void, Errors.DomainKitError | E, Principal.Service | R>;\n readonly recoverRevocations: <E, R>(\n revoke: (authorization: Authorization) => Effect.Effect<void, E, R>,\n ) => Effect.Effect<number, Errors.DomainKitError | E, Principal.Service | R>;\n };\n readonly connections: {\n readonly create: (authorizationId: string) => Fx<Connection>;\n readonly get: (id: string) => Fx<Connection>;\n readonly list: (filter?: { readonly provider?: string }) => Fx<ReadonlyArray<Connection>>;\n /** Fails `InvalidInput` while attachments still reference the connection. */\n readonly remove: (id: string) => Fx<void>;\n };\n readonly attachments: {\n /** One attachment per (owner, domain); a duplicate fails `InvalidInput`. */\n readonly create: (input: {\n readonly connectionId: string;\n readonly domain: string;\n readonly zone: string;\n readonly label: string;\n readonly target: unknown;\n }) => Fx<Attachment>;\n readonly get: (id: string) => Fx<Attachment>;\n readonly byDomain: (domain: string) => Fx<Option.Option<Attachment>>;\n readonly list: (connectionId: string) => Fx<ReadonlyArray<Attachment>>;\n readonly remove: (id: string) => Fx<void>;\n };\n readonly continuations: {\n readonly put: (continuation: Continuation) => Fx<void>;\n /** Read without spending; a late read fails `Expired`. */\n readonly get: (id: string) => Fx<Continuation>;\n /**\n * The flow's owner, actor, provider, and expiry, without a principal and without spending the\n * row. The one read in this interface that is not tenant-scoped, because `domainkit/server`\n * has to learn whose flow a provider callback finishes before it can resolve the principal to\n * scope by; the callback carries a continuation id and a session cookie and nothing else.\n *\n * A missing row fails `NotFound` and a stale one fails `Expired`, exactly as `get` does, so an\n * expired id never reaches a host. `payload` is out of reach here by construction.\n */\n readonly header: (id: string) => Effect.Effect<ContinuationHeader, Errors.DomainKitError>;\n /** Exactly-once: the second consume of the same id fails `NotFound`; a late one fails `Expired`. */\n readonly consume: (id: string) => Fx<Continuation>;\n };\n readonly attempts: {\n readonly create: (attempt: Attempt) => Fx<Attempt>;\n readonly get: (id: Plan.PlanId) => Fx<Attempt>;\n readonly byApproval: (id: Approval.ApprovalId) => Fx<Attempt>;\n readonly byReceipt: (id: Receipt.ReceiptId) => Fx<Attempt>;\n readonly latest: (attachmentId: string, kind: Plan.Kind) => Fx<Option.Option<Attempt>>;\n /** `planned` -> `approved`; approving again with the same approval id is a no-op, anything else fails `Stale`. */\n readonly approve: (id: Plan.PlanId, approval: Approval.Model) => Fx<Attempt>;\n /**\n * `planned` -> `rejected` (terminal). Rejecting again returns the row unchanged; any other\n * status fails `Stale`, `expired` fails `Expired`, and a digest mismatch fails `Stale`.\n */\n readonly reject: (\n id: Plan.PlanId,\n input: {\n readonly digest: Plan.Digest;\n readonly actorId: string;\n readonly reason: string | null;\n },\n ) => Fx<Attempt>;\n /**\n * Atomic transition to `applying` with a lease from `approved`, `failed`, or an expired\n * `applying`; fails `Busy` while a lease is live and `Stale` from any other status.\n */\n readonly claim: (id: Plan.PlanId, lease: DateTime.Utc) => Fx<Attempt>;\n readonly complete: (id: Plan.PlanId, receipt: Receipt.Model) => Fx<Attempt>;\n readonly fail: (id: Plan.PlanId, message: string) => Fx<Attempt>;\n };\n readonly readiness: {\n /** One row per (owner, domain); `attachmentId`, when set, must exist for the owner. */\n readonly put: (readiness: Readiness) => Fx<void>;\n readonly get: (domain: string) => Fx<Option.Option<Readiness>>;\n };\n /** Single-flight guard keyed by string (credential refresh, apply). Fails `Busy` rather than waiting. */\n readonly withLock: <A, E, R>(\n key: string,\n effect: Effect.Effect<A, E, R>,\n ) => Effect.Effect<A, E | Errors.DomainKitError, R | Principal.Service>;\n}\n\nexport class Service extends Context.Service<Service, Interface>()(\"@domainkit/Storage\") {}\n\nexport interface MemoryOptions {\n /** Fault injection: runs before every mutation commits; failing here leaves state untouched. */\n readonly beforeCommit?: (operation: string) => Effect.Effect<void, Errors.DomainKitError>;\n}\n\n/** In-memory implementation for tests and local development. Not for production. */\nexport const layerMemory: Layer.Layer<Service> = Layer.sync(Service)(() => makeMemory());\n\n/** `layerMemory` with fault injection for storage-failure tests. */\nexport const layerMemoryWith = (options: MemoryOptions): Layer.Layer<Service> =>\n Layer.sync(Service)(() => makeMemory(options));\n\nexport { makeMemory };\n\n// ---------------------------------------------------------------------------------------------\n// Async adapter\n// ---------------------------------------------------------------------------------------------\n\n/**\n * A Promise-shaped implementation for hosts that persist without Effect. Every method takes the\n * principal explicitly. Rejections become `StorageFailed`; reject with a `DomainKit.Error` to keep\n * a typed reason. Two-phase revocation and locking are split into plain steps so no callback has\n * to run an Effect.\n */\nexport interface AsyncInterface {\n readonly authorizations: {\n readonly upsert: (\n principal: Principal.Interface,\n input: {\n readonly authorization: Authorization;\n readonly credential: Credential;\n readonly expectedId?: string;\n },\n ) => Promise<Authorization>;\n readonly get: (principal: Principal.Interface, id: string) => Promise<Authorization>;\n readonly credential: (principal: Principal.Interface, id: string) => Promise<Credential>;\n readonly rotate: (\n principal: Principal.Interface,\n id: string,\n credential: Credential,\n ) => Promise<void>;\n readonly promoteCapabilities: (\n principal: Principal.Interface,\n id: string,\n capabilities: ReadonlyArray<Capability>,\n ) => Promise<void>;\n /** Mark `pending` durably; must be idempotent. */\n readonly prepareRevocation: (\n principal: Principal.Interface,\n id: string,\n ) => Promise<Authorization>;\n /** Delete the authorization after the provider confirmed revocation. */\n readonly completeRevocation: (principal: Principal.Interface, id: string) => Promise<void>;\n readonly pendingRevocations: (\n principal: Principal.Interface,\n ) => Promise<ReadonlyArray<Authorization>>;\n };\n readonly connections: {\n readonly create: (\n principal: Principal.Interface,\n authorizationId: string,\n ) => Promise<Connection>;\n readonly get: (principal: Principal.Interface, id: string) => Promise<Connection>;\n readonly list: (\n principal: Principal.Interface,\n filter?: { readonly provider?: string },\n ) => Promise<ReadonlyArray<Connection>>;\n readonly remove: (principal: Principal.Interface, id: string) => Promise<void>;\n };\n readonly attachments: {\n readonly create: (\n principal: Principal.Interface,\n input: {\n readonly connectionId: string;\n readonly domain: string;\n readonly zone: string;\n readonly label: string;\n readonly target: unknown;\n },\n ) => Promise<Attachment>;\n readonly get: (principal: Principal.Interface, id: string) => Promise<Attachment>;\n readonly byDomain: (\n principal: Principal.Interface,\n domain: string,\n ) => Promise<Attachment | null>;\n readonly list: (\n principal: Principal.Interface,\n connectionId: string,\n ) => Promise<ReadonlyArray<Attachment>>;\n readonly remove: (principal: Principal.Interface, id: string) => Promise<void>;\n };\n readonly continuations: {\n readonly put: (principal: Principal.Interface, continuation: Continuation) => Promise<void>;\n readonly get: (principal: Principal.Interface, id: string) => Promise<Continuation>;\n /** Takes no principal: see `Interface.continuations.header`. Never returns `payload`. */\n readonly header: (id: string) => Promise<ContinuationHeader>;\n readonly consume: (principal: Principal.Interface, id: string) => Promise<Continuation>;\n };\n readonly attempts: {\n readonly create: (principal: Principal.Interface, attempt: Attempt) => Promise<Attempt>;\n readonly get: (principal: Principal.Interface, id: Plan.PlanId) => Promise<Attempt>;\n readonly byApproval: (\n principal: Principal.Interface,\n id: Approval.ApprovalId,\n ) => Promise<Attempt>;\n readonly byReceipt: (principal: Principal.Interface, id: Receipt.ReceiptId) => Promise<Attempt>;\n readonly latest: (\n principal: Principal.Interface,\n attachmentId: string,\n kind: Plan.Kind,\n ) => Promise<Attempt | null>;\n readonly approve: (\n principal: Principal.Interface,\n id: Plan.PlanId,\n approval: Approval.Model,\n ) => Promise<Attempt>;\n readonly reject: (\n principal: Principal.Interface,\n id: Plan.PlanId,\n input: {\n readonly digest: Plan.Digest;\n readonly actorId: string;\n readonly reason: string | null;\n },\n ) => Promise<Attempt>;\n readonly claim: (\n principal: Principal.Interface,\n id: Plan.PlanId,\n lease: DateTime.Utc,\n ) => Promise<Attempt>;\n readonly complete: (\n principal: Principal.Interface,\n id: Plan.PlanId,\n receipt: Receipt.Model,\n ) => Promise<Attempt>;\n readonly fail: (\n principal: Principal.Interface,\n id: Plan.PlanId,\n message: string,\n ) => Promise<Attempt>;\n };\n readonly readiness: {\n readonly put: (principal: Principal.Interface, readiness: Readiness) => Promise<void>;\n readonly get: (principal: Principal.Interface, domain: string) => Promise<Readiness | null>;\n };\n /** Return `false` when another holder has the key. */\n readonly acquireLock: (principal: Principal.Interface, key: string) => Promise<boolean>;\n readonly releaseLock: (principal: Principal.Interface, key: string) => Promise<void>;\n}\n\nconst storageFailed = (operation: string) => (cause: unknown) =>\n Errors.isDomainKitError(cause)\n ? cause\n : new Errors.DomainKitError({\n reason: new Reason.StorageFailed({\n operation,\n message: cause instanceof Error ? cause.message : String(cause),\n }),\n });\n\n/** Wraps a Promise-shaped implementation. Rejections become `StorageFailed`. */\nexport const fromAsync = (service: AsyncInterface): Interface => {\n const call = <A>(operation: string, run: (principal: Principal.Interface) => Promise<A>): Fx<A> =>\n Effect.flatMap(Principal.Service, (principal) =>\n Effect.tryPromise({ try: () => run(principal), catch: storageFailed(operation) }),\n );\n const option = <A>(\n operation: string,\n run: (principal: Principal.Interface) => Promise<A | null>,\n ) => call(operation, run).pipe(Effect.map(Option.fromNullishOr));\n return {\n authorizations: {\n upsert: (input) =>\n call(\"authorizations.upsert\", (p) => service.authorizations.upsert(p, input)),\n get: (id) => call(\"authorizations.get\", (p) => service.authorizations.get(p, id)),\n credential: (id) =>\n call(\"authorizations.credential\", (p) => service.authorizations.credential(p, id)),\n rotate: (id, credential) =>\n call(\"authorizations.rotate\", (p) => service.authorizations.rotate(p, id, credential)),\n promoteCapabilities: (id, capabilities) =>\n call(\"authorizations.promoteCapabilities\", (p) =>\n service.authorizations.promoteCapabilities(p, id, capabilities),\n ),\n revoke: (id, revoke) =>\n call(\"authorizations.prepareRevocation\", (p) =>\n service.authorizations.prepareRevocation(p, id),\n ).pipe(\n Effect.flatMap(() => revoke),\n Effect.flatMap(() =>\n call(\"authorizations.completeRevocation\", (p) =>\n service.authorizations.completeRevocation(p, id),\n ),\n ),\n ),\n recoverRevocations: (revoke) =>\n call(\"authorizations.pendingRevocations\", (p) =>\n service.authorizations.pendingRevocations(p),\n ).pipe(\n Effect.flatMap((pending) =>\n Effect.forEach(pending, (authorization) =>\n revoke(authorization).pipe(\n Effect.flatMap(() =>\n call(\"authorizations.completeRevocation\", (p) =>\n service.authorizations.completeRevocation(p, authorization.id),\n ),\n ),\n ),\n ),\n ),\n Effect.map((completed) => completed.length),\n ),\n },\n connections: {\n create: (authorizationId) =>\n call(\"connections.create\", (p) => service.connections.create(p, authorizationId)),\n get: (id) => call(\"connections.get\", (p) => service.connections.get(p, id)),\n list: (filter) => call(\"connections.list\", (p) => service.connections.list(p, filter)),\n remove: (id) => call(\"connections.remove\", (p) => service.connections.remove(p, id)),\n },\n attachments: {\n create: (input) => call(\"attachments.create\", (p) => service.attachments.create(p, input)),\n get: (id) => call(\"attachments.get\", (p) => service.attachments.get(p, id)),\n byDomain: (domain) =>\n option(\"attachments.byDomain\", (p) => service.attachments.byDomain(p, domain)),\n list: (connectionId) =>\n call(\"attachments.list\", (p) => service.attachments.list(p, connectionId)),\n remove: (id) => call(\"attachments.remove\", (p) => service.attachments.remove(p, id)),\n },\n continuations: {\n put: (continuation) =>\n call(\"continuations.put\", (p) => service.continuations.put(p, continuation)),\n get: (id) => call(\"continuations.get\", (p) => service.continuations.get(p, id)),\n header: (id) =>\n Effect.tryPromise({\n try: () => service.continuations.header(id),\n catch: storageFailed(\"continuations.header\"),\n }),\n consume: (id) => call(\"continuations.consume\", (p) => service.continuations.consume(p, id)),\n },\n attempts: {\n create: (attempt) => call(\"attempts.create\", (p) => service.attempts.create(p, attempt)),\n get: (id) => call(\"attempts.get\", (p) => service.attempts.get(p, id)),\n byApproval: (id) => call(\"attempts.byApproval\", (p) => service.attempts.byApproval(p, id)),\n byReceipt: (id) => call(\"attempts.byReceipt\", (p) => service.attempts.byReceipt(p, id)),\n latest: (attachmentId, kind) =>\n option(\"attempts.latest\", (p) => service.attempts.latest(p, attachmentId, kind)),\n approve: (id, approval) =>\n call(\"attempts.approve\", (p) => service.attempts.approve(p, id, approval)),\n reject: (id, input) => call(\"attempts.reject\", (p) => service.attempts.reject(p, id, input)),\n claim: (id, lease) => call(\"attempts.claim\", (p) => service.attempts.claim(p, id, lease)),\n complete: (id, receipt) =>\n call(\"attempts.complete\", (p) => service.attempts.complete(p, id, receipt)),\n fail: (id, message) => call(\"attempts.fail\", (p) => service.attempts.fail(p, id, message)),\n },\n readiness: {\n put: (readiness) => call(\"readiness.put\", (p) => service.readiness.put(p, readiness)),\n get: (domain) => option(\"readiness.get\", (p) => service.readiness.get(p, domain)),\n },\n withLock: (key, effect) =>\n Effect.acquireRelease(\n call(\"acquireLock\", (p) => service.acquireLock(p, key)).pipe(\n Effect.flatMap((acquired) =>\n acquired ? Effect.void : Errors.fail(new Reason.Busy({ key })),\n ),\n ),\n () => call(\"releaseLock\", (p) => service.releaseLock(p, key)).pipe(Effect.ignore),\n ).pipe(\n Effect.flatMap(() => effect),\n Effect.scoped,\n ),\n };\n};\n\nexport const layerFromAsync = (service: AsyncInterface): Layer.Layer<Service> =>\n Layer.succeed(Service)(fromAsync(service));\n"],"mappings":"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"}
@@ -219,6 +219,36 @@ const cases = (layer) => {
219
219
  yield* expectReason(storage.continuations.consume("cont-2"), "NotFound", "expired consume is also spent");
220
220
  }).pipe(Effect.provideService(Service, owner)))
221
221
  },
222
+ {
223
+ name: "reads a continuation header under any principal and still refuses an expired one",
224
+ run: run(Effect.gen(function* () {
225
+ const storage = yield* Service$1;
226
+ const now = yield* DateTime.now;
227
+ const continuation = new Continuation({
228
+ id: "cont-header-1",
229
+ ownerId: owner.ownerId,
230
+ actorId: owner.actorId,
231
+ provider: "fake",
232
+ payload: { codeVerifier: "v" },
233
+ returnTo: null,
234
+ expiresAt: DateTime.add(now, { minutes: 15 })
235
+ });
236
+ yield* storage.continuations.put(continuation);
237
+ const foreign = yield* storage.continuations.header("cont-header-1").pipe(Effect.provideService(Service, other));
238
+ yield* expect(foreign.ownerId === owner.ownerId && foreign.actorId === owner.actorId, "header did not report the recorded owner and actor");
239
+ yield* expect(foreign.provider === "fake", "header did not report the provider");
240
+ yield* expect(!("payload" in foreign), "header exposed the continuation payload");
241
+ const still = yield* storage.continuations.get("cont-header-1");
242
+ yield* expect(still.id === "cont-header-1", "the header read spent the continuation");
243
+ yield* expectReason(storage.continuations.header("cont-header-missing"), "NotFound", "header of an unknown continuation");
244
+ yield* storage.continuations.put(new Continuation({
245
+ ...continuation,
246
+ id: "cont-header-2",
247
+ expiresAt: DateTime.subtract(now, { minutes: 1 })
248
+ }));
249
+ yield* expectReason(storage.continuations.header("cont-header-2"), "Expired", "header of an expired continuation");
250
+ }).pipe(Effect.provideService(Service, owner)))
251
+ },
222
252
  {
223
253
  name: "moves attempts through planned, approved, applying, and complete with leases",
224
254
  run: run(Effect.gen(function* () {
@@ -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 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":"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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: \"reads a continuation header under any principal and still refuses an expired one\",\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-header-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 // The deliberate exception to the tenant isolation every other case here enforces, and\n // not a bug. A provider callback is a top-level navigation carrying a continuation id\n // and a session cookie, so the route has to learn whose flow it is before it can name a\n // principal to scope by. The read therefore takes no principal at all — that is a type\n // constraint, and providing a foreign one below changes nothing — and it answers with\n // header fields only. `payload` holds the PKCE verifier and stays behind the\n // owner-scoped `get`. Binding the caller to the flow is the reader's job, and\n // `domainkit/server` does it on the callback route.\n const foreign = yield* storage.continuations\n .header(\"cont-header-1\")\n .pipe(Effect.provideService(Principal.Service, other));\n yield* expect(\n foreign.ownerId === owner.ownerId && foreign.actorId === owner.actorId,\n \"header did not report the recorded owner and actor\",\n );\n yield* expect(foreign.provider === \"fake\", \"header did not report the provider\");\n yield* expect(!(\"payload\" in foreign), \"header exposed the continuation payload\");\n // A read, never a claim: the flow is still there to be spent.\n const still = yield* storage.continuations.get(\"cont-header-1\");\n yield* expect(still.id === \"cont-header-1\", \"the header read spent the continuation\");\n yield* expectReason(\n storage.continuations.header(\"cont-header-missing\"),\n \"NotFound\",\n \"header of an unknown continuation\",\n );\n yield* storage.continuations.put(\n new Storage.Continuation({\n ...continuation,\n id: \"cont-header-2\",\n expiresAt: DateTime.subtract(now, { minutes: 1 }),\n }),\n );\n yield* expectReason(\n storage.continuations.header(\"cont-header-2\"),\n \"Expired\",\n \"header of an expired continuation\",\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":"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"}
@@ -3,7 +3,7 @@ import { Busy, Expired, InvalidInput, NotFound, Stale } from "../Reason.mjs";
3
3
  import { fail } from "./error.mjs";
4
4
  import { fresh } from "./ids.mjs";
5
5
  import { Service } from "../Principal.mjs";
6
- import { Attachment, Attempt, Authorization, Connection } from "../Storage.mjs";
6
+ import { Attachment, Attempt, Authorization, Connection, ContinuationHeader } from "../Storage.mjs";
7
7
  import { DateTime, Effect, Option, Semaphore } from "effect";
8
8
  //#region src/internal/storage-memory.ts
9
9
  const notFound = (entity, id) => fail(new NotFound({
@@ -166,6 +166,21 @@ function makeMemory(options = {}) {
166
166
  }));
167
167
  return row;
168
168
  })),
169
+ header: (id) => Effect.gen(function* () {
170
+ const row = state.continuations.get(id);
171
+ if (row === void 0) return yield* notFound("continuation", id);
172
+ const now = yield* DateTime.now;
173
+ if (DateTime.toEpochMillis(row.expiresAt) <= DateTime.toEpochMillis(now)) return yield* fail(new Expired({
174
+ entity: "continuation",
175
+ id
176
+ }));
177
+ return new ContinuationHeader({
178
+ ownerId: row.ownerId,
179
+ actorId: row.actorId,
180
+ provider: row.provider,
181
+ expiresAt: row.expiresAt
182
+ });
183
+ }),
169
184
  consume: (id) => write((principal) => Effect.gen(function* () {
170
185
  const row = owned(state.continuations, principal, id);
171
186
  if (row === void 0) return yield* notFound("continuation", id);
@@ -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 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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"}
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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"}
package/dist/package.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.12.2";
2
+ var version = "0.13.0";
3
3
  //#endregion
4
4
  export { version };
5
5
 
@@ -30,9 +30,27 @@ import * as Reason from "./Reason.ts";
30
30
  import * as Receipt from "./Receipt.ts";
31
31
  import * as Storage from "./Storage.ts";
32
32
  import * as Verify from "./Verify.ts";
33
+ /**
34
+ * What DomainKit already knows about the request when it asks the host who is making it. Only
35
+ * `/callback/:provider` passes one, because it is the only route that points at a flow DomainKit
36
+ * recorded earlier; every other route calls `principal` with the request alone.
37
+ */
38
+ export interface IdentityContext {
39
+ /** The route being served. */
40
+ readonly endpoint: EndpointName;
41
+ /** The flow this callback claims to finish, as DomainKit recorded it when the flow started. */
42
+ readonly continuation: Storage.ContinuationHeader;
43
+ }
33
44
  /** The only service a host must implement for the server. */
34
45
  export interface IdentityService {
35
- readonly principal: (request: HttpServerRequest.HttpServerRequest) => Effect.Effect<Principal.Interface, Errors.DomainKitError>;
46
+ /**
47
+ * `context` is present only on the callback, where a host that keeps per-request tenancy can use
48
+ * `context.continuation.ownerId` to pick the right tenant out of a session that holds several.
49
+ * Ignoring it is safe everywhere: DomainKit checks the principal against the recorded flow on
50
+ * that route regardless, so a host that answers with the wrong one is refused rather than
51
+ * trusted.
52
+ */
53
+ readonly principal: (request: HttpServerRequest.HttpServerRequest, context?: IdentityContext) => Effect.Effect<Principal.Interface, Errors.DomainKitError>;
36
54
  /**
37
55
  * Which routes this principal may reach, checked after `principal` on every request. Fail with
38
56
  * reason `Forbidden` for the 403 a UI expects. Without it every authenticated principal reaches
@@ -80,6 +80,18 @@ declare const Continuation_base: Schema.Class<Continuation, Schema.Struct<{
80
80
  /** Interactive-flow state between `Connect.start` and `Connect.complete`. */
81
81
  export declare class Continuation extends Continuation_base {
82
82
  }
83
+ declare const ContinuationHeader_base: Schema.Class<ContinuationHeader, Schema.Struct<{
84
+ readonly ownerId: Schema.String;
85
+ readonly actorId: Schema.String;
86
+ readonly provider: Schema.String;
87
+ readonly expiresAt: Schema.DateTimeUtcFromString;
88
+ }>, {}>;
89
+ /**
90
+ * Who a continuation belongs to, without the continuation itself. `payload` has no place here: it
91
+ * carries the PKCE `codeVerifier`, which only the owner-scoped read may reach.
92
+ */
93
+ export declare class ContinuationHeader extends ContinuationHeader_base {
94
+ }
83
95
  export declare const AttemptStatus: Schema.Literals<readonly ["planned", "approved", "applying", "complete", "partial", "failed", "expired", "rejected"]>;
84
96
  export type AttemptStatus = typeof AttemptStatus.Type;
85
97
  /** Who declined the plan, why, and when. Terminal: the domain needs a new plan. */
@@ -192,6 +204,16 @@ export interface Interface {
192
204
  readonly put: (continuation: Continuation) => Fx<void>;
193
205
  /** Read without spending; a late read fails `Expired`. */
194
206
  readonly get: (id: string) => Fx<Continuation>;
207
+ /**
208
+ * The flow's owner, actor, provider, and expiry, without a principal and without spending the
209
+ * row. The one read in this interface that is not tenant-scoped, because `domainkit/server`
210
+ * has to learn whose flow a provider callback finishes before it can resolve the principal to
211
+ * scope by; the callback carries a continuation id and a session cookie and nothing else.
212
+ *
213
+ * A missing row fails `NotFound` and a stale one fails `Expired`, exactly as `get` does, so an
214
+ * expired id never reaches a host. `payload` is out of reach here by construction.
215
+ */
216
+ readonly header: (id: string) => Effect.Effect<ContinuationHeader, Errors.DomainKitError>;
195
217
  /** Exactly-once: the second consume of the same id fails `NotFound`; a late one fails `Expired`. */
196
218
  readonly consume: (id: string) => Fx<Continuation>;
197
219
  };
@@ -287,6 +309,8 @@ export interface AsyncInterface {
287
309
  readonly continuations: {
288
310
  readonly put: (principal: Principal.Interface, continuation: Continuation) => Promise<void>;
289
311
  readonly get: (principal: Principal.Interface, id: string) => Promise<Continuation>;
312
+ /** Takes no principal: see `Interface.continuations.header`. Never returns `payload`. */
313
+ readonly header: (id: string) => Promise<ContinuationHeader>;
290
314
  readonly consume: (principal: Principal.Interface, id: string) => Promise<Continuation>;
291
315
  };
292
316
  readonly attempts: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "domainkit",
3
- "version": "0.12.2",
3
+ "version": "0.13.0",
4
4
  "description": "Build reviewable DNS plans for custom domains in TypeScript",
5
5
  "keywords": [
6
6
  "custom-domains",