domainkit 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/Connect.mjs CHANGED
@@ -33,9 +33,11 @@ var Connect_exports = /* @__PURE__ */ __exportAll({
33
33
  inspect: () => inspect,
34
34
  layer: () => layer,
35
35
  make: () => make,
36
+ reconnect: () => reconnect,
36
37
  refresh: () => refresh,
37
38
  session: () => session,
38
- start: () => start
39
+ start: () => start,
40
+ zones: () => zones
39
41
  });
40
42
  const MethodEnum = Data.taggedEnum();
41
43
  /** `token` accepts a lone string (`{ token }`) or a record; every value is wrapped in `Redacted`. */
@@ -59,7 +61,9 @@ const Payload = Schema.Struct({
59
61
  method: Schema.Literals(["oauth", "integration"]),
60
62
  codeVerifier: Schema.NullOr(Schema.String),
61
63
  callbackUrl: Schema.String,
62
- domain: Schema.NullOr(Schema.String)
64
+ domain: Schema.NullOr(Schema.String),
65
+ /** Set when the flow is re-crediting a connection rather than making one. */
66
+ connectionId: Schema.NullOr(Schema.String)
63
67
  });
64
68
  const Target = Schema.Struct({
65
69
  zone: Schema.String,
@@ -167,6 +171,32 @@ const make = Effect.gen(function* () {
167
171
  session
168
172
  };
169
173
  });
174
+ /**
175
+ * Put a fresh credential behind an authorization the owner already has. The row keeps its id, so
176
+ * every connection over it, and every domain on those connections, stays exactly where it is.
177
+ */
178
+ const replaceWith = (input) => Effect.gen(function* () {
179
+ const principal = yield* Service$2;
180
+ const held = yield* input.definition.session(input.issued).capabilities();
181
+ const missing = input.requiredCapabilities.filter((capability) => !held.includes(capability));
182
+ if (missing.length > 0) return yield* fail(new Forbidden({ message: `${input.definition.name} credential lacks ${missing.join(", ")}` }));
183
+ const authorization = new Authorization({
184
+ id: input.authorizationId,
185
+ ownerId: principal.ownerId,
186
+ provider: input.definition.id,
187
+ method: input.method,
188
+ capabilities: held,
189
+ context: yield* encodeContext(input.definition, input.issued.context),
190
+ revocation: "active",
191
+ createdBy: principal.actorId,
192
+ createdAt: yield* DateTime.now
193
+ });
194
+ yield* storage.authorizations.upsert({
195
+ authorization,
196
+ credential: yield* seal(input.issued),
197
+ expectedId: input.authorizationId
198
+ });
199
+ });
170
200
  const attachWith = (input) => Effect.gen(function* () {
171
201
  const domain = yield* decode(input.domain);
172
202
  const existing = yield* storage.attachments.byDomain(domain);
@@ -185,6 +215,7 @@ const make = Effect.gen(function* () {
185
215
  connectionId: input.connection.id,
186
216
  domain,
187
217
  zone: resolution.target.zone,
218
+ label: resolution.target.label,
188
219
  target: {
189
220
  ...resolution.target,
190
221
  context: yield* encodeContext(input.definition, resolution.target.context)
@@ -251,6 +282,35 @@ const make = Effect.gen(function* () {
251
282
  const [host] = hosts;
252
283
  return hosts.length === 1 && host !== void 0 ? { provider: host.id } : null;
253
284
  };
285
+ const zones = (options = {}) => Effect.gen(function* () {
286
+ const reachable = [];
287
+ const connections = [];
288
+ const filter = options.provider === void 0 ? void 0 : { provider: options.provider };
289
+ for (const connection of yield* storage.connections.list(filter)) {
290
+ const authorization = yield* storage.authorizations.get(connection.authorizationId);
291
+ const targets = yield* sessionFor(connection).pipe(Effect.flatMap((session) => session.listTargets()), Effect.map(Option.some), Effect.catchIf((error) => error.reason._tag === "Reconnect" || error.reason._tag === "Unauthenticated" || error.reason._tag === "Forbidden", () => Effect.succeed(Option.none())));
292
+ connections.push({
293
+ connectionId: connection.id,
294
+ provider: authorization.provider,
295
+ status: Option.isNone(targets) ? "reconnect" : "connected"
296
+ });
297
+ if (Option.isNone(targets)) continue;
298
+ for (const target of targets.value) reachable.push({
299
+ connectionId: connection.id,
300
+ provider: authorization.provider,
301
+ target
302
+ });
303
+ }
304
+ return {
305
+ zones: reachable.sort((left, right) => left.target.zone.localeCompare(right.target.zone) || left.connectionId.localeCompare(right.connectionId)),
306
+ connections,
307
+ providers: providers.list().map((definition) => ({
308
+ id: definition.id,
309
+ name: definition.name,
310
+ methods: describeMethods(definition)
311
+ }))
312
+ };
313
+ });
254
314
  const inspect = (input) => Effect.gen(function* () {
255
315
  const domain = yield* decode(input);
256
316
  const policy = yield* Policy;
@@ -297,8 +357,6 @@ const make = Effect.gen(function* () {
297
357
  });
298
358
  const start = (input) => Effect.gen(function* () {
299
359
  yield* recover;
300
- const principal = yield* Service$2;
301
- const policy = yield* Policy;
302
360
  const definition = yield* providers.get(input.provider);
303
361
  if (input.domain !== void 0) yield* decode(input.domain);
304
362
  switch (input.method._tag) {
@@ -321,39 +379,83 @@ const make = Effect.gen(function* () {
321
379
  return started(connection, attached);
322
380
  }
323
381
  case "OAuth":
324
- case "Integration": {
325
- const interactive = input.method._tag === "OAuth" ? definition.auth.oauth : definition.auth.integration;
326
- if (interactive === void 0) return yield* invalid(`${definition.name} does not offer ${input.method._tag.toLowerCase()} connections`, "method");
327
- if (input.callbackUrl === void 0) return yield* invalid("Interactive connections need a callbackUrl", "callbackUrl");
328
- const continuationId = yield* fresh("cont");
329
- const pkce$1 = input.method._tag === "OAuth" ? yield* pkce() : null;
330
- const redirect = input.method._tag === "OAuth" ? yield* interactive.start({
331
- state: continuationId,
332
- callbackUrl: input.callbackUrl,
333
- codeChallenge: pkce$1?.codeChallenge ?? ""
334
- }) : yield* interactive.start({
335
- state: continuationId,
336
- callbackUrl: input.callbackUrl
382
+ case "Integration": return yield* redirectFor({
383
+ definition,
384
+ method: input.method,
385
+ callbackUrl: input.callbackUrl,
386
+ domain: input.domain ?? null,
387
+ connectionId: null
388
+ });
389
+ }
390
+ });
391
+ /** The redirect an interactive method answers with, and the continuation that finishes it. */
392
+ const redirectFor = (input) => Effect.gen(function* () {
393
+ const principal = yield* Service$2;
394
+ const policy = yield* Policy;
395
+ const interactive = input.method._tag === "OAuth" ? input.definition.auth.oauth : input.definition.auth.integration;
396
+ if (interactive === void 0) return yield* invalid(`${input.definition.name} does not offer ${input.method._tag.toLowerCase()} connections`, "method");
397
+ if (input.callbackUrl === void 0) return yield* invalid("Interactive connections need a callbackUrl", "callbackUrl");
398
+ const callbackUrl = input.callbackUrl;
399
+ const continuationId = yield* fresh("cont");
400
+ const pkce$1 = input.method._tag === "OAuth" ? yield* pkce() : null;
401
+ const redirect = input.method._tag === "OAuth" ? yield* interactive.start({
402
+ state: continuationId,
403
+ callbackUrl,
404
+ codeChallenge: pkce$1?.codeChallenge ?? ""
405
+ }) : yield* interactive.start({
406
+ state: continuationId,
407
+ callbackUrl
408
+ });
409
+ yield* storage.continuations.put(new Continuation({
410
+ id: continuationId,
411
+ ownerId: principal.ownerId,
412
+ actorId: principal.actorId,
413
+ provider: input.definition.id,
414
+ payload: Schema.encodeSync(Payload)({
415
+ method: input.method._tag === "OAuth" ? "oauth" : "integration",
416
+ codeVerifier: pkce$1?.codeVerifier ?? null,
417
+ callbackUrl,
418
+ domain: input.domain,
419
+ connectionId: input.connectionId
420
+ }),
421
+ returnTo: input.method.returnTo ?? null,
422
+ expiresAt: DateTime.addDuration(yield* DateTime.now, Duration.millis(policy.continuationTtlMs))
423
+ }));
424
+ return Started.Redirect({
425
+ authorizationUrl: redirect.authorizationUrl,
426
+ continuationId
427
+ });
428
+ });
429
+ const reconnect = (input) => Effect.gen(function* () {
430
+ yield* recover;
431
+ const connection = yield* storage.connections.get(input.connectionId);
432
+ const existing = yield* storage.authorizations.get(connection.authorizationId);
433
+ const definition = yield* providers.get(existing.provider);
434
+ switch (input.method._tag) {
435
+ case "Token": {
436
+ const token = definition.auth.token;
437
+ if (token === void 0) return yield* invalid(`${definition.name} does not accept tokens`, "method");
438
+ const issued = yield* token.authenticate(input.method.values);
439
+ yield* replaceWith({
440
+ definition,
441
+ authorizationId: existing.id,
442
+ method: "token",
443
+ issued,
444
+ requiredCapabilities: token.requiredCapabilities
337
445
  });
338
- yield* storage.continuations.put(new Continuation({
339
- id: continuationId,
340
- ownerId: principal.ownerId,
341
- actorId: principal.actorId,
342
- provider: definition.id,
343
- payload: Schema.encodeSync(Payload)({
344
- method: input.method._tag === "OAuth" ? "oauth" : "integration",
345
- codeVerifier: pkce$1?.codeVerifier ?? null,
346
- callbackUrl: input.callbackUrl,
347
- domain: input.domain ?? null
348
- }),
349
- returnTo: input.method.returnTo ?? null,
350
- expiresAt: DateTime.addDuration(yield* DateTime.now, Duration.millis(policy.continuationTtlMs))
351
- }));
352
- return Started.Redirect({
353
- authorizationUrl: redirect.authorizationUrl,
354
- continuationId
446
+ return Started.Connected({
447
+ connection,
448
+ attachment: null
355
449
  });
356
450
  }
451
+ case "OAuth":
452
+ case "Integration": return yield* redirectFor({
453
+ definition,
454
+ method: input.method,
455
+ callbackUrl: input.callbackUrl,
456
+ domain: null,
457
+ connectionId: connection.id
458
+ });
357
459
  }
358
460
  });
359
461
  const complete = (input) => Effect.gen(function* () {
@@ -392,6 +494,22 @@ const make = Effect.gen(function* () {
392
494
  params
393
495
  });
394
496
  });
497
+ if (payload.connectionId !== null) {
498
+ const held = yield* storage.connections.get(payload.connectionId);
499
+ const existing = yield* storage.authorizations.get(held.authorizationId);
500
+ yield* replaceWith({
501
+ definition,
502
+ authorizationId: existing.id,
503
+ method: payload.method,
504
+ issued,
505
+ requiredCapabilities: []
506
+ });
507
+ yield* storage.continuations.consume(input.continuationId).pipe(Effect.ignore);
508
+ return Started.Connected({
509
+ connection: held,
510
+ attachment: null
511
+ });
512
+ }
395
513
  const { connection, session } = yield* connectWith({
396
514
  definition,
397
515
  method: payload.method,
@@ -453,8 +571,10 @@ const make = Effect.gen(function* () {
453
571
  });
454
572
  return {
455
573
  inspect,
574
+ zones,
456
575
  discover,
457
576
  start,
577
+ reconnect,
458
578
  complete,
459
579
  attach,
460
580
  detach: (attachmentId) => storage.attachments.remove(attachmentId),
@@ -466,8 +586,10 @@ const make = Effect.gen(function* () {
466
586
  const layer = Layer.effect(Service)(make);
467
587
  const accessor = (pick) => (...args) => Effect.flatMap(Service, (service) => pick(service)(...args));
468
588
  const inspect = accessor((service) => service.inspect);
589
+ const zones = accessor((service) => service.zones);
469
590
  const discover = accessor((service) => service.discover);
470
591
  const start = accessor((service) => service.start);
592
+ const reconnect = accessor((service) => service.reconnect);
471
593
  const complete = accessor((service) => service.complete);
472
594
  const attach = accessor((service) => service.attach);
473
595
  const detach = accessor((service) => service.detach);
@@ -475,6 +597,6 @@ const disconnect = accessor((service) => service.disconnect);
475
597
  const refresh = accessor((service) => service.refresh);
476
598
  const session = accessor((service) => service.session);
477
599
  //#endregion
478
- export { Connect_exports, Discovery, Method, Policy, Selection, Service, Started, attach, complete, defaults, detach, disconnect, discover, inspect, layer, make, refresh, session, start };
600
+ export { Connect_exports, Discovery, Method, Policy, Selection, Service, Started, attach, complete, defaults, detach, disconnect, discover, inspect, layer, make, reconnect, refresh, session, start, zones };
479
601
 
480
602
  //# sourceMappingURL=Connect.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Connect.mjs","names":["Errors.fail","Reason.InvalidInput","Storage.Service","Custody.Service","Providers.Service","Resolver.Service","Storage.Credential","Provider.decodeContext","Reason.Reconnect","Principal.Service","Reason.Forbidden","Storage.Authorization","Provider.encodeContext","DomainName.decode","Reason.NotFound","Storage.Attachment","candidates","DomainName.candidates","Provider.describeMethods","pkce","OAuth.pkce","Storage.Continuation","Errors.DomainKitError","Reason.Unauthenticated","Errors.decode"],"sources":["../src/Connect.ts"],"sourcesContent":["/**\n * Connect a provider, attach domains to it, and keep the credential alive. Interactive methods\n * (OAuth, integration) return a redirect and complete on callback; token methods connect in one\n * call. Reuse of an existing connection for a new domain is `attach`.\n */\nimport { Context, Data, DateTime, Duration, Effect, Layer, Option, Redacted, Schema } from \"effect\";\n\nimport * as Custody from \"./Custody.ts\";\nimport * as Errors from \"./internal/error.ts\";\nimport * as Reason from \"./Reason.ts\";\nimport * as DomainName from \"./DomainName.ts\";\nimport { fresh } from \"./internal/ids.ts\";\nimport * as OAuth from \"./internal/oauth.ts\";\nimport * as Principal from \"./Principal.ts\";\nimport * as Provider from \"./Provider.ts\";\nimport * as Providers from \"./Providers.ts\";\nimport * as Resolver from \"./Resolver.ts\";\nimport * as Storage from \"./Storage.ts\";\n\ntype Fx<A> = Effect.Effect<A, Errors.DomainKitError, Principal.Service>;\n\nexport type Method = Data.TaggedEnum<{\n /** Values keyed by the provider's declared `auth.token.fields`. */\n Token: { readonly values: Provider.TokenValues };\n OAuth: { readonly returnTo?: string };\n Integration: { readonly returnTo?: string };\n}>;\nconst MethodEnum = Data.taggedEnum<Method>();\n/** `token` accepts a lone string (`{ token }`) or a record; every value is wrapped in `Redacted`. */\nexport const Method = {\n ...MethodEnum,\n token: (\n values: string | Readonly<Record<string, Redacted.Redacted<string> | string | undefined>>,\n ): Method =>\n MethodEnum.Token({\n values:\n typeof values === \"string\"\n ? { token: Redacted.make(values) }\n : Object.fromEntries(\n Object.entries(values).map(([name, value]) => [\n name,\n typeof value === \"string\" ? Redacted.make(value) : value,\n ]),\n ),\n }),\n oauth: (options: { readonly returnTo?: string } = {}): Method => MethodEnum.OAuth(options),\n integration: (options: { readonly returnTo?: string } = {}): Method =>\n MethodEnum.Integration(options),\n} as const;\n\n/** Everything the UI needs about a domain's provider state. */\nexport interface Snapshot {\n readonly domain: string;\n readonly attachment: Storage.Attachment | null;\n readonly connection: Storage.Connection | null;\n readonly authorization: Pick<\n Storage.Authorization,\n \"id\" | \"provider\" | \"method\" | \"capabilities\" | \"revocation\"\n > | null;\n readonly lastReceiptId: string | null;\n /** How many domains this domain's connection serves, including this one; `0` without one. */\n readonly connectionDomains: number;\n /** Other connections of this owner that could serve this domain. */\n readonly reusable: ReadonlyArray<{\n readonly connection: Storage.Connection;\n readonly provider: string;\n readonly method: Storage.AuthMethod;\n }>;\n readonly providers: ReadonlyArray<{\n readonly id: string;\n readonly name: string;\n readonly methods: ReadonlyArray<Provider.MethodDescriptor>;\n }>;\n}\n\nexport type Started = Data.TaggedEnum<{\n Connected: {\n readonly connection: Storage.Connection;\n readonly attachment: Storage.Attachment | null;\n };\n Redirect: { readonly authorizationUrl: string; readonly continuationId: string };\n SelectionRequired: {\n readonly connection: Storage.Connection;\n readonly candidates: ReadonlyArray<Provider.Target>;\n };\n}>;\nexport const Started = Data.taggedEnum<Started>();\n\n/** `attach` either attached the domain or needs the caller to pick a zone. */\nexport type Selection = Data.TaggedEnum<{\n SelectionRequired: { readonly candidates: ReadonlyArray<Provider.Target> };\n}>;\nexport const Selection = Data.taggedEnum<Selection>();\n\nexport type Attached = Storage.Attachment | Selection;\n\nexport type Refreshed = \"current\" | \"refreshed\" | \"reconnect\";\n\n/** Which of the principal's connections serves a domain, from nameserver evidence and zone lists. */\nexport type Discovery = Data.TaggedEnum<{\n Resolved: { readonly connectionId: string; readonly target: Provider.Target };\n SelectionRequired: {\n readonly candidates: ReadonlyArray<{\n readonly connectionId: string;\n readonly target: Provider.Target;\n }>;\n };\n /**\n * No connection reaches the domain. `host` names the one registered provider whose declared\n * nameserver suffixes cover every authoritative nameserver; `null` when none or several do.\n */\n NotFound: {\n readonly nameservers: ReadonlyArray<string>;\n readonly host: { readonly provider: string } | null;\n };\n}>;\nexport const Discovery = Data.taggedEnum<Discovery>();\n\nexport interface Interface {\n readonly inspect: (domain: string) => Fx<Snapshot>;\n /**\n * Resolve the domain's authoritative nameservers, then match them against the zones each of\n * the principal's connections can reach: the closest zone wins, a decisive nameserver match\n * breaks ties, and anything else needs a selection.\n */\n readonly discover: (domain: string) => Fx<Discovery>;\n /** Connect a provider and, when `domain` is given, attach it. Token methods finish here. */\n readonly start: (input: {\n readonly provider: string;\n readonly method: Method;\n readonly domain?: string;\n readonly callbackUrl?: string;\n }) => Fx<Started>;\n /** Finish an interactive connection from the provider callback (the full callback URL). */\n readonly complete: (input: {\n readonly continuationId: string;\n readonly callbackUrl: string;\n }) => Fx<Started>;\n readonly attach: (input: {\n readonly connectionId: string;\n readonly domain: string;\n readonly target?: Provider.Target;\n }) => Fx<Attached>;\n /** Records stay in DNS; DomainKit forgets the domain. Use `Cleanup` first to remove records. */\n readonly detach: (attachmentId: string) => Fx<void>;\n /** Detach every domain and revoke the credential at the provider when the method supports it. */\n readonly disconnect: (connectionId: string) => Fx<void>;\n /** Refresh a near-expiry credential, single-flighted. Called automatically before provider use. */\n readonly refresh: (connectionId: string) => Fx<Refreshed>;\n /** Live provider session for an attachment; used by Provision, Cleanup, Verify. */\n readonly session: (\n attachmentId: string,\n ) => Fx<{ readonly session: Provider.Session; readonly target: Provider.Target }>;\n}\n\nexport class Service extends Context.Service<Service, Interface>()(\"@domainkit/Connect\") {}\n\n/** Which owner-scoped reuse is allowed. Default: any connection of the same owner may serve any of its domains. */\nexport interface PolicyShape {\n readonly allowReuse: (input: {\n readonly connection: Storage.Connection;\n readonly domain: string;\n }) => Effect.Effect<boolean, never, Principal.Service>;\n /** Refresh this long before expiry. Default 10 minutes. */\n readonly refreshBeforeMs: number;\n /** Continuation lifetime. Default 15 minutes. */\n readonly continuationTtlMs: number;\n}\nexport const defaults: PolicyShape = {\n allowReuse: () => Effect.succeed(true),\n refreshBeforeMs: 10 * 60_000,\n continuationTtlMs: 15 * 60_000,\n};\nexport class Policy extends Context.Reference<PolicyShape>(\"@domainkit/Connect/Policy\", {\n defaultValue: () => defaults,\n}) {}\n\n// ---------------------------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------------------------\n\nconst Payload = Schema.Struct({\n method: Schema.Literals([\"oauth\", \"integration\"]),\n codeVerifier: Schema.NullOr(Schema.String),\n callbackUrl: Schema.String,\n domain: Schema.NullOr(Schema.String),\n});\n\nconst Target = Schema.Struct({\n zone: Schema.String,\n context: Schema.Unknown,\n label: Schema.String,\n});\n\nconst invalid = (message: string, field?: string) =>\n Errors.fail(new Reason.InvalidInput({ message, ...(field === undefined ? {} : { field }) }));\n\n/** Nameserver hostnames compare case-insensitively and without a trailing dot. */\nconst normalize = (name: string) => name.toLowerCase().replace(/\\.$/, \"\");\n\nexport const make: Effect.Effect<\n Interface,\n never,\n Storage.Service | Custody.Service | Providers.Service | Resolver.Service\n> = Effect.gen(function* () {\n const storage = yield* Storage.Service;\n const custody = yield* Custody.Service;\n const providers = yield* Providers.Service;\n const resolver = yield* Resolver.Service;\n\n const seal = (issued: Provider.IssuedCredential) =>\n Effect.gen(function* () {\n const ciphertext = yield* custody.seal(issued.secret);\n return new Storage.Credential({\n ciphertext,\n expiresAt: issued.expiresAt,\n rotatedAt: yield* DateTime.now,\n });\n });\n\n /** Plaintext credential with the context decoded from its stored envelope. */\n const open = (authorization: Storage.Authorization): Fx<Provider.Credential> =>\n Effect.gen(function* () {\n const definition = yield* providers.get(authorization.provider);\n const row = yield* storage.authorizations.credential(authorization.id);\n const secret = yield* custody.open(row.ciphertext);\n const context = yield* Provider.decodeContext(definition, authorization.context);\n return { secret, context };\n });\n\n const refresherFor = (definition: Provider.Definition, method: Storage.AuthMethod) =>\n method === \"oauth\"\n ? definition.auth.oauth?.refresh\n : method === \"integration\"\n ? definition.auth.integration?.refresh\n : undefined;\n\n const revokerFor = (definition: Provider.Definition, method: Storage.AuthMethod) =>\n method === \"oauth\"\n ? definition.auth.oauth?.revoke\n : method === \"integration\"\n ? definition.auth.integration?.revoke\n : undefined;\n\n const revokeAt = (authorization: Storage.Authorization): Fx<void> =>\n Effect.gen(function* () {\n const definition = yield* providers.get(authorization.provider);\n const revoke = revokerFor(definition, authorization.method);\n if (revoke === undefined) return;\n const credential = yield* open(authorization);\n yield* revoke(credential);\n });\n\n /** Finish revocations a crash interrupted; best effort, never blocks the caller. */\n const recover = storage.authorizations.recoverRevocations(revokeAt).pipe(Effect.ignore);\n\n const refresh: Interface[\"refresh\"] = (connectionId) =>\n Effect.gen(function* () {\n const policy = yield* Policy;\n const connection = yield* storage.connections.get(connectionId);\n const authorization = yield* storage.authorizations.get(connection.authorizationId);\n const definition = yield* providers.get(authorization.provider);\n const refresher = refresherFor(definition, authorization.method);\n const dueAt = (row: Storage.Credential, now: DateTime.Utc) =>\n row.expiresAt !== null &&\n DateTime.toEpochMillis(row.expiresAt) - DateTime.toEpochMillis(now) <=\n policy.refreshBeforeMs;\n const expired = (row: Storage.Credential, now: DateTime.Utc) =>\n row.expiresAt !== null &&\n DateTime.toEpochMillis(row.expiresAt) <= DateTime.toEpochMillis(now);\n const current = yield* storage.authorizations.credential(authorization.id);\n const now = yield* DateTime.now;\n if (!dueAt(current, now)) return \"current\" as const;\n if (refresher === undefined)\n return expired(current, now) ? (\"reconnect\" as const) : (\"current\" as const);\n return yield* storage\n .withLock(\n `refresh:${authorization.id}`,\n Effect.gen(function* () {\n // Disconnect's two-phase revocation does not take this lock: re-read the row here\n // so a revocation that started meanwhile skips the refresh instead of racing it.\n const live = yield* storage.authorizations.get(authorization.id).pipe(\n Effect.map(Option.some),\n Effect.catchIf(\n (error) => error.reason._tag === \"NotFound\",\n () => Effect.succeed(Option.none<Storage.Authorization>()),\n ),\n );\n if (Option.isNone(live) || live.value.revocation !== \"active\") {\n return \"reconnect\" as const;\n }\n const latest = yield* storage.authorizations.credential(authorization.id);\n if (!dueAt(latest, yield* DateTime.now)) return \"current\" as const;\n const secret = yield* custody.open(latest.ciphertext);\n const context = yield* Provider.decodeContext(definition, live.value.context);\n const issued = yield* refresher({ secret, context });\n // A revocation that completed while the provider was issuing leaves no row to\n // rotate; revoke the fresh credential so it does not stay live without a home.\n yield* storage.authorizations\n .rotate(authorization.id, yield* seal(issued))\n .pipe(\n Effect.tapError((error) =>\n error.reason._tag === \"NotFound\"\n ? (revokerFor(definition, authorization.method)?.(issued) ?? Effect.void).pipe(\n Effect.ignore,\n )\n : Effect.void,\n ),\n );\n return \"refreshed\" as const;\n }),\n )\n .pipe(\n Effect.catchIf(\n (error) => error.reason._tag === \"Busy\" && !expired(current, now),\n () => Effect.succeed(\"current\" as const),\n ),\n Effect.catchIf(\n (error) => error.reason._tag === \"Unauthenticated\",\n () => Effect.succeed(\"reconnect\" as const),\n ),\n );\n });\n\n const sessionFor = (connection: Storage.Connection): Fx<Provider.Session> =>\n Effect.gen(function* () {\n const authorization = yield* storage.authorizations.get(connection.authorizationId);\n const reconnect = Errors.fail(\n new Reason.Reconnect({\n provider: authorization.provider,\n connectionId: connection.id,\n }),\n );\n if (authorization.revocation !== \"active\") return yield* reconnect;\n if ((yield* refresh(connection.id)) === \"reconnect\") return yield* reconnect;\n const definition = yield* providers.get(authorization.provider);\n return definition.session(yield* open(authorization));\n });\n\n const connectWith = (input: {\n readonly definition: Provider.Definition;\n readonly method: Storage.AuthMethod;\n readonly issued: Provider.IssuedCredential;\n readonly requiredCapabilities: ReadonlyArray<Storage.Capability>;\n }): Fx<{ readonly connection: Storage.Connection; readonly session: Provider.Session }> =>\n Effect.gen(function* () {\n const principal = yield* Principal.Service;\n const session = input.definition.session(input.issued);\n const held = yield* session.capabilities();\n const missing = input.requiredCapabilities.filter((capability) => !held.includes(capability));\n if (missing.length > 0) {\n return yield* Errors.fail(\n new Reason.Forbidden({\n message: `${input.definition.name} credential lacks ${missing.join(\", \")}`,\n }),\n );\n }\n const authorization = new Storage.Authorization({\n id: yield* fresh(\"auth\"),\n ownerId: principal.ownerId,\n provider: input.definition.id,\n method: input.method,\n capabilities: held,\n context: yield* Provider.encodeContext(input.definition, input.issued.context),\n revocation: \"active\",\n createdBy: principal.actorId,\n createdAt: yield* DateTime.now,\n });\n yield* storage.authorizations.upsert({\n authorization,\n credential: yield* seal(input.issued),\n });\n const connection = yield* storage.connections.create(authorization.id);\n return { connection, session };\n });\n\n const attachWith = (input: {\n readonly definition: Provider.Definition;\n readonly connection: Storage.Connection;\n readonly session: Provider.Session;\n readonly domain: string;\n readonly target?: Provider.Target;\n }): Fx<Attached> =>\n Effect.gen(function* () {\n const domain = yield* DomainName.decode(input.domain);\n const existing = yield* storage.attachments.byDomain(domain);\n if (Option.isSome(existing)) {\n if (existing.value.connectionId === input.connection.id) return existing.value;\n return yield* invalid(`${domain} is already attached to another connection`, \"domain\");\n }\n const resolution: Provider.Resolution =\n input.target === undefined\n ? yield* input.session.resolveTarget(domain)\n : Provider.Resolution.Resolved({ target: input.target });\n switch (resolution._tag) {\n case \"SelectionRequired\":\n return Selection.SelectionRequired({ candidates: resolution.candidates });\n case \"NotFound\":\n return yield* Errors.fail(new Reason.NotFound({ entity: \"zone\", id: domain }));\n case \"Resolved\":\n return yield* storage.attachments.create({\n connectionId: input.connection.id,\n domain,\n zone: resolution.target.zone,\n target: {\n ...resolution.target,\n context: yield* Provider.encodeContext(input.definition, resolution.target.context),\n },\n });\n }\n });\n\n const started = (connection: Storage.Connection, attached: Attached | null): Started =>\n attached === null\n ? Started.Connected({ connection, attachment: null })\n : attached instanceof Storage.Attachment\n ? Started.Connected({ connection, attachment: attached })\n : Started.SelectionRequired({ connection, candidates: attached.candidates });\n\n /** Authoritative nameservers for the closest zone that answers an NS query. */\n const authoritative = (candidates: ReadonlyArray<DomainName.Model>) =>\n Effect.gen(function* () {\n for (const zone of candidates) {\n const outcomes = yield* resolver.resolve(zone, \"NS\");\n const names = outcomes.flatMap((outcome) =>\n outcome._tag === \"Answered\"\n ? outcome.answer.records.flatMap((record) =>\n record._tag === \"NS\" ? [record.nameserver] : [],\n )\n : [],\n );\n if (names.length > 0) return [...new Set(names)].sort();\n }\n return [] as ReadonlyArray<string>;\n });\n\n const discover: Interface[\"discover\"] = (input) =>\n Effect.gen(function* () {\n const domain = yield* DomainName.decode(input);\n const candidates = DomainName.candidates(domain);\n const nameservers = yield* authoritative(candidates);\n const reachable: Array<{\n readonly connectionId: string;\n readonly target: Provider.Target;\n }> = [];\n for (const connection of yield* storage.connections.list()) {\n const targets = yield* sessionFor(connection).pipe(\n Effect.flatMap((session) => session.listTargets()),\n Effect.catch(() => Effect.succeed([] as ReadonlyArray<Provider.Target>)),\n );\n for (const target of targets) {\n if (candidates.includes(target.zone as DomainName.Model)) {\n reachable.push({ connectionId: connection.id, target });\n }\n }\n }\n for (const zone of candidates) {\n const matches = reachable\n .filter(({ target }) => target.zone === zone)\n .sort((left, right) => left.connectionId.localeCompare(right.connectionId));\n if (matches.length === 0) continue;\n const [only] = matches;\n if (matches.length === 1 && only !== undefined) return Discovery.Resolved(only);\n const decisive = matches.filter(({ target }) => {\n const configured = new Set((target.nameservers ?? []).map(normalize));\n return nameservers.length > 0 && nameservers.every((name) => configured.has(name));\n });\n const [winner] = decisive;\n if (decisive.length === 1 && winner !== undefined) return Discovery.Resolved(winner);\n return Discovery.SelectionRequired({ candidates: matches });\n }\n return Discovery.NotFound({ nameservers, host: hostOf(nameservers) });\n });\n\n /** The one registered provider whose declared suffixes cover every nameserver, else `null`. */\n const hostOf = (nameservers: ReadonlyArray<string>): { readonly provider: string } | null => {\n if (nameservers.length === 0) return null;\n const names = nameservers.map(normalize);\n const hosts = providers.list().filter((definition) => {\n const suffixes = (definition.nameservers ?? []).map(normalize);\n return (\n suffixes.length > 0 &&\n names.every((name) =>\n suffixes.some((suffix) => name === suffix || name.endsWith(`.${suffix}`)),\n )\n );\n });\n const [host] = hosts;\n return hosts.length === 1 && host !== undefined ? { provider: host.id } : null;\n };\n\n const inspect: Interface[\"inspect\"] = (input) =>\n Effect.gen(function* () {\n const domain = yield* DomainName.decode(input);\n const policy = yield* Policy;\n const attachment = Option.getOrNull(yield* storage.attachments.byDomain(domain));\n const connection =\n attachment === null ? null : yield* storage.connections.get(attachment.connectionId);\n const authorization =\n connection === null ? null : yield* storage.authorizations.get(connection.authorizationId);\n const latest =\n attachment === null\n ? Option.none()\n : yield* storage.attempts.latest(attachment.id, \"provisioning\");\n // Letting a connection go takes every domain on it, so the UI has to be able to say how many.\n const attached = connection === null ? [] : yield* storage.attachments.list(connection.id);\n const reusable: Array<Snapshot[\"reusable\"][number]> = [];\n for (const candidate of yield* storage.connections.list()) {\n if (candidate.id === connection?.id) continue;\n if (!(yield* policy.allowReuse({ connection: candidate, domain }))) continue;\n const candidateAuthorization = yield* storage.authorizations.get(candidate.authorizationId);\n if (candidateAuthorization.revocation !== \"active\") continue;\n reusable.push({\n connection: candidate,\n provider: candidateAuthorization.provider,\n method: candidateAuthorization.method,\n });\n }\n return {\n domain,\n attachment,\n connection,\n authorization:\n authorization === null\n ? null\n : {\n id: authorization.id,\n provider: authorization.provider,\n method: authorization.method,\n capabilities: authorization.capabilities,\n revocation: authorization.revocation,\n },\n lastReceiptId: Option.isSome(latest) ? (latest.value.receipt?.id ?? null) : null,\n connectionDomains: attached.length,\n reusable,\n providers: providers.list().map((definition) => ({\n id: definition.id,\n name: definition.name,\n methods: Provider.describeMethods(definition),\n })),\n };\n });\n\n const start: Interface[\"start\"] = (input) =>\n Effect.gen(function* () {\n yield* recover;\n const principal = yield* Principal.Service;\n const policy = yield* Policy;\n const definition = yield* providers.get(input.provider);\n if (input.domain !== undefined) yield* DomainName.decode(input.domain);\n switch (input.method._tag) {\n case \"Token\": {\n const token = definition.auth.token;\n if (token === undefined) {\n return yield* invalid(`${definition.name} does not accept tokens`, \"method\");\n }\n const issued = yield* token.authenticate(input.method.values);\n const { connection, session } = yield* connectWith({\n definition,\n method: \"token\",\n issued,\n requiredCapabilities: token.requiredCapabilities,\n });\n const attached =\n input.domain === undefined\n ? null\n : yield* attachWith({ definition, connection, session, domain: input.domain });\n return started(connection, attached);\n }\n case \"OAuth\":\n case \"Integration\": {\n const interactive =\n input.method._tag === \"OAuth\" ? definition.auth.oauth : definition.auth.integration;\n if (interactive === undefined) {\n return yield* invalid(\n `${definition.name} does not offer ${input.method._tag.toLowerCase()} connections`,\n \"method\",\n );\n }\n if (input.callbackUrl === undefined) {\n return yield* invalid(\"Interactive connections need a callbackUrl\", \"callbackUrl\");\n }\n const continuationId = yield* fresh(\"cont\");\n const pkce = input.method._tag === \"OAuth\" ? yield* OAuth.pkce() : null;\n const redirect =\n input.method._tag === \"OAuth\"\n ? yield* (interactive as Provider.OAuthAuth).start({\n state: continuationId,\n callbackUrl: input.callbackUrl,\n codeChallenge: pkce?.codeChallenge ?? \"\",\n })\n : yield* (interactive as Provider.IntegrationAuth).start({\n state: continuationId,\n callbackUrl: input.callbackUrl,\n });\n yield* storage.continuations.put(\n new Storage.Continuation({\n id: continuationId,\n ownerId: principal.ownerId,\n actorId: principal.actorId,\n provider: definition.id,\n payload: Schema.encodeSync(Payload)({\n method: input.method._tag === \"OAuth\" ? \"oauth\" : \"integration\",\n codeVerifier: pkce?.codeVerifier ?? null,\n callbackUrl: input.callbackUrl,\n domain: input.domain ?? null,\n }),\n returnTo: input.method.returnTo ?? null,\n expiresAt: DateTime.addDuration(\n yield* DateTime.now,\n Duration.millis(policy.continuationTtlMs),\n ),\n }),\n );\n return Started.Redirect({\n authorizationUrl: redirect.authorizationUrl,\n continuationId,\n });\n }\n }\n });\n\n const complete: Interface[\"complete\"] = (input) =>\n Effect.gen(function* () {\n // The continuation is spent only after the credential and connection are persisted, so a\n // failure before that (bad callback, provider outage, storage outage) leaves the flow\n // retryable. Concurrent callbacks for the same continuation serialize on a lock; the\n // loser sees Busy or NotFound. A provider code is single-use, so a retry after the\n // provider already redeemed it fails Unauthenticated and the customer starts over.\n const callback = yield* Effect.try({\n try: () => new URL(input.callbackUrl),\n catch: () =>\n new Errors.DomainKitError({\n reason: new Reason.InvalidInput({\n message: \"callbackUrl is not a URL\",\n field: \"callbackUrl\",\n }),\n }),\n });\n const params = Object.fromEntries(callback.searchParams);\n const unauthenticated = (message: string) =>\n Errors.fail(new Reason.Unauthenticated({ message }));\n if (params.error !== undefined) {\n return yield* unauthenticated(`Provider returned ${params.error}`);\n }\n if (params.state !== input.continuationId) {\n return yield* unauthenticated(\"Callback state does not match the continuation\");\n }\n const code = params.code;\n if (code === undefined) return yield* unauthenticated(\"Callback has no code\");\n return yield* storage.withLock(\n `continuation:${input.continuationId}`,\n Effect.gen(function* () {\n const continuation = yield* storage.continuations.get(input.continuationId);\n const payload = yield* Errors.decode(Payload, continuation.payload, \"continuation\");\n const definition = yield* providers.get(continuation.provider);\n const issued =\n payload.method === \"oauth\"\n ? yield* Effect.gen(function* () {\n const oauth = definition.auth.oauth;\n if (oauth === undefined || payload.codeVerifier === null) {\n return yield* invalid(\n `${definition.name} does not offer oauth connections`,\n \"method\",\n );\n }\n return yield* oauth.complete({\n code,\n callbackUrl: payload.callbackUrl,\n codeVerifier: payload.codeVerifier,\n params,\n });\n })\n : yield* Effect.gen(function* () {\n const integration = definition.auth.integration;\n if (integration === undefined) {\n return yield* invalid(\n `${definition.name} does not offer integration connections`,\n \"method\",\n );\n }\n return yield* integration.complete({\n code,\n callbackUrl: payload.callbackUrl,\n params,\n });\n });\n const { connection, session } = yield* connectWith({\n definition,\n method: payload.method,\n issued,\n requiredCapabilities: [],\n });\n const attached =\n payload.domain === null\n ? null\n : yield* attachWith({ definition, connection, session, domain: payload.domain });\n // The connection is durable at this point. Whatever happens to the continuation\n // now (expired, already spent, or a storage failure while spending it) no longer\n // changes the outcome: the row expires on its own and a replayed callback fails at\n // the provider, so the persisted result is returned.\n yield* storage.continuations.consume(input.continuationId).pipe(Effect.ignore);\n return started(connection, attached);\n }),\n );\n });\n\n const attach: Interface[\"attach\"] = (input) =>\n Effect.gen(function* () {\n const connection = yield* storage.connections.get(input.connectionId);\n const authorization = yield* storage.authorizations.get(connection.authorizationId);\n const definition = yield* providers.get(authorization.provider);\n const session = yield* sessionFor(connection);\n return yield* attachWith({\n definition,\n connection,\n session,\n domain: input.domain,\n ...(input.target === undefined ? {} : { target: input.target }),\n });\n });\n\n const disconnect: Interface[\"disconnect\"] = (connectionId) =>\n Effect.gen(function* () {\n yield* recover;\n const connection = yield* storage.connections.get(connectionId);\n const attachments = yield* storage.attachments.list(connectionId);\n yield* Effect.forEach(attachments, (attachment) => storage.attachments.remove(attachment.id));\n yield* storage.connections.remove(connectionId);\n const remaining = (yield* storage.connections.list()).some(\n (candidate) => candidate.authorizationId === connection.authorizationId,\n );\n if (remaining) return;\n const authorization = yield* storage.authorizations.get(connection.authorizationId);\n yield* storage.authorizations.revoke(authorization.id, revokeAt(authorization));\n });\n\n const session: Interface[\"session\"] = (attachmentId) =>\n Effect.gen(function* () {\n const attachment = yield* storage.attachments.get(attachmentId);\n const connection = yield* storage.connections.get(attachment.connectionId);\n const authorization = yield* storage.authorizations.get(connection.authorizationId);\n const definition = yield* providers.get(authorization.provider);\n const stored = yield* Errors.decode(Target, attachment.target, \"target\");\n const target: Provider.Target = {\n ...stored,\n context: yield* Provider.decodeContext(definition, stored.context),\n };\n const built = yield* sessionFor(connection);\n // The zone must still be reachable by this credential: it may have moved account, been\n // deleted, or become a kind the provider cannot host records in.\n const reachable = yield* built.listTargets();\n if (!reachable.some((candidate) => candidate.zone === target.zone)) {\n return yield* Errors.fail(new Reason.NotFound({ entity: \"zone\", id: target.zone }));\n }\n return { session: built, target };\n });\n\n return {\n inspect,\n discover,\n start,\n complete,\n attach,\n detach: (attachmentId) => storage.attachments.remove(attachmentId),\n disconnect,\n refresh,\n session,\n };\n});\n\nexport const layer: Layer.Layer<\n Service,\n never,\n Storage.Service | Custody.Service | Providers.Service | Resolver.Service\n> = Layer.effect(Service)(make);\n\nconst accessor =\n <Args extends ReadonlyArray<unknown>, A>(\n pick: (service: Interface) => (...args: Args) => Fx<A>,\n ): ((...args: Args) => Effect.Effect<A, Errors.DomainKitError, Principal.Service | Service>) =>\n (...args) =>\n Effect.flatMap(Service, (service) => pick(service)(...args));\n\nexport const inspect = accessor((service) => service.inspect);\nexport const discover = accessor((service) => service.discover);\nexport const start = accessor((service) => service.start);\nexport const complete = accessor((service) => service.complete);\nexport const attach = accessor((service) => service.attach);\nexport const detach = accessor((service) => service.detach);\nexport const disconnect = accessor((service) => service.disconnect);\nexport const refresh = accessor((service) => service.refresh);\nexport const session = accessor((service) => service.session);\n"],"mappings":"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"}
1
+ {"version":3,"file":"Connect.mjs","names":["Errors.fail","Reason.InvalidInput","Storage.Service","Custody.Service","Providers.Service","Resolver.Service","Storage.Credential","Provider.decodeContext","Reason.Reconnect","Principal.Service","Reason.Forbidden","Storage.Authorization","Provider.encodeContext","DomainName.decode","Reason.NotFound","Storage.Attachment","candidates","DomainName.candidates","Provider.describeMethods","pkce","OAuth.pkce","Storage.Continuation","Errors.DomainKitError","Reason.Unauthenticated","Errors.decode"],"sources":["../src/Connect.ts"],"sourcesContent":["/**\n * Connect a provider, attach domains to it, and keep the credential alive. Interactive methods\n * (OAuth, integration) return a redirect and complete on callback; token methods connect in one\n * call. Reuse of an existing connection for a new domain is `attach`.\n */\nimport { Context, Data, DateTime, Duration, Effect, Layer, Option, Redacted, Schema } from \"effect\";\n\nimport * as Custody from \"./Custody.ts\";\nimport * as Errors from \"./internal/error.ts\";\nimport * as Reason from \"./Reason.ts\";\nimport * as DomainName from \"./DomainName.ts\";\nimport { fresh } from \"./internal/ids.ts\";\nimport * as OAuth from \"./internal/oauth.ts\";\nimport * as Principal from \"./Principal.ts\";\nimport * as Provider from \"./Provider.ts\";\nimport * as Providers from \"./Providers.ts\";\nimport * as Resolver from \"./Resolver.ts\";\nimport * as Storage from \"./Storage.ts\";\n\ntype Fx<A> = Effect.Effect<A, Errors.DomainKitError, Principal.Service>;\n\nexport type Method = Data.TaggedEnum<{\n /** Values keyed by the provider's declared `auth.token.fields`. */\n Token: { readonly values: Provider.TokenValues };\n OAuth: { readonly returnTo?: string };\n Integration: { readonly returnTo?: string };\n}>;\nconst MethodEnum = Data.taggedEnum<Method>();\n/** `token` accepts a lone string (`{ token }`) or a record; every value is wrapped in `Redacted`. */\nexport const Method = {\n ...MethodEnum,\n token: (\n values: string | Readonly<Record<string, Redacted.Redacted<string> | string | undefined>>,\n ): Method =>\n MethodEnum.Token({\n values:\n typeof values === \"string\"\n ? { token: Redacted.make(values) }\n : Object.fromEntries(\n Object.entries(values).map(([name, value]) => [\n name,\n typeof value === \"string\" ? Redacted.make(value) : value,\n ]),\n ),\n }),\n oauth: (options: { readonly returnTo?: string } = {}): Method => MethodEnum.OAuth(options),\n integration: (options: { readonly returnTo?: string } = {}): Method =>\n MethodEnum.Integration(options),\n} as const;\n\n/** Everything the UI needs about a domain's provider state. */\nexport interface Snapshot {\n readonly domain: string;\n readonly attachment: Storage.Attachment | null;\n readonly connection: Storage.Connection | null;\n readonly authorization: Pick<\n Storage.Authorization,\n \"id\" | \"provider\" | \"method\" | \"capabilities\" | \"revocation\"\n > | null;\n readonly lastReceiptId: string | null;\n /** How many domains this domain's connection serves, including this one; `0` without one. */\n readonly connectionDomains: number;\n /** Other connections of this owner that could serve this domain. */\n readonly reusable: ReadonlyArray<{\n readonly connection: Storage.Connection;\n readonly provider: string;\n readonly method: Storage.AuthMethod;\n }>;\n readonly providers: ReadonlyArray<{\n readonly id: string;\n readonly name: string;\n readonly methods: ReadonlyArray<Provider.MethodDescriptor>;\n }>;\n}\n\nexport type Started = Data.TaggedEnum<{\n Connected: {\n readonly connection: Storage.Connection;\n readonly attachment: Storage.Attachment | null;\n };\n Redirect: { readonly authorizationUrl: string; readonly continuationId: string };\n SelectionRequired: {\n readonly connection: Storage.Connection;\n readonly candidates: ReadonlyArray<Provider.Target>;\n };\n}>;\nexport const Started = Data.taggedEnum<Started>();\n\n/** `attach` either attached the domain or needs the caller to pick a zone. */\nexport type Selection = Data.TaggedEnum<{\n SelectionRequired: { readonly candidates: ReadonlyArray<Provider.Target> };\n}>;\nexport const Selection = Data.taggedEnum<Selection>();\n\nexport type Attached = Storage.Attachment | Selection;\n\nexport type Refreshed = \"current\" | \"refreshed\" | \"reconnect\";\n\n/** Which of the principal's connections serves a domain, from nameserver evidence and zone lists. */\nexport type Discovery = Data.TaggedEnum<{\n Resolved: { readonly connectionId: string; readonly target: Provider.Target };\n SelectionRequired: {\n readonly candidates: ReadonlyArray<{\n readonly connectionId: string;\n readonly target: Provider.Target;\n }>;\n };\n /**\n * No connection reaches the domain. `host` names the one registered provider whose declared\n * nameserver suffixes cover every authoritative nameserver; `null` when none or several do.\n */\n NotFound: {\n readonly nameservers: ReadonlyArray<string>;\n readonly host: { readonly provider: string } | null;\n };\n}>;\nexport const Discovery = Data.taggedEnum<Discovery>();\n\n/** One zone a connection reaches, with the connection that reaches it. */\nexport interface Zone {\n readonly connectionId: string;\n readonly provider: string;\n readonly target: Provider.Target;\n}\n\n/**\n * A connection's standing in a zone listing. `reconnect` is a credential the provider turned down:\n * the connection is still the owner's, and the other connections' zones came back regardless.\n */\nexport interface ZoneConnection {\n readonly connectionId: string;\n readonly provider: string;\n readonly status: \"connected\" | \"reconnect\";\n}\n\n/** Every zone the principal's connections reach, and how each connection is standing. */\nexport interface Zones {\n /** Ordered by zone, then by connection, so two listings of the same state read the same. */\n readonly zones: ReadonlyArray<Zone>;\n readonly connections: ReadonlyArray<ZoneConnection>;\n /** Every provider a customer could connect from here, so an offer needs no second call. */\n readonly providers: Snapshot[\"providers\"];\n}\n\nexport interface Interface {\n readonly inspect: (domain: string) => Fx<Snapshot>;\n /**\n * Every zone the principal's connections can serve, from `Provider.Session.listTargets`. A\n * connection whose credential the provider turned down is marked `reconnect` in `connections`\n * and contributes no zones; the rest still list.\n */\n readonly zones: (options?: { readonly provider?: string }) => Fx<Zones>;\n /**\n * Resolve the domain's authoritative nameservers, then match them against the zones each of\n * the principal's connections can reach: the closest zone wins, a decisive nameserver match\n * breaks ties, and anything else needs a selection.\n */\n readonly discover: (domain: string) => Fx<Discovery>;\n /** Connect a provider and, when `domain` is given, attach it. Token methods finish here. */\n readonly start: (input: {\n readonly provider: string;\n readonly method: Method;\n readonly domain?: string;\n readonly callbackUrl?: string;\n }) => Fx<Started>;\n /**\n * Replace the credential behind a connection the owner already holds, keeping the connection and\n * every domain attached to it. This is what a customer means by reconnecting: starting again\n * would mint a second account and leave the domains on the one the provider turned down. The\n * provider is the connection's own; a credential for a different account is the customer's\n * choice, and the zone check on every session catches a zone it can no longer reach.\n */\n readonly reconnect: (input: {\n readonly connectionId: string;\n readonly method: Method;\n readonly callbackUrl?: string;\n }) => Fx<Started>;\n /** Finish an interactive connection from the provider callback (the full callback URL). */\n readonly complete: (input: {\n readonly continuationId: string;\n readonly callbackUrl: string;\n }) => Fx<Started>;\n readonly attach: (input: {\n readonly connectionId: string;\n readonly domain: string;\n readonly target?: Provider.Target;\n }) => Fx<Attached>;\n /** Records stay in DNS; DomainKit forgets the domain. Use `Cleanup` first to remove records. */\n readonly detach: (attachmentId: string) => Fx<void>;\n /** Detach every domain and revoke the credential at the provider when the method supports it. */\n readonly disconnect: (connectionId: string) => Fx<void>;\n /** Refresh a near-expiry credential, single-flighted. Called automatically before provider use. */\n readonly refresh: (connectionId: string) => Fx<Refreshed>;\n /** Live provider session for an attachment; used by Provision, Cleanup, Verify. */\n readonly session: (\n attachmentId: string,\n ) => Fx<{ readonly session: Provider.Session; readonly target: Provider.Target }>;\n}\n\nexport class Service extends Context.Service<Service, Interface>()(\"@domainkit/Connect\") {}\n\n/** Which owner-scoped reuse is allowed. Default: any connection of the same owner may serve any of its domains. */\nexport interface PolicyShape {\n readonly allowReuse: (input: {\n readonly connection: Storage.Connection;\n readonly domain: string;\n }) => Effect.Effect<boolean, never, Principal.Service>;\n /** Refresh this long before expiry. Default 10 minutes. */\n readonly refreshBeforeMs: number;\n /** Continuation lifetime. Default 15 minutes. */\n readonly continuationTtlMs: number;\n}\nexport const defaults: PolicyShape = {\n allowReuse: () => Effect.succeed(true),\n refreshBeforeMs: 10 * 60_000,\n continuationTtlMs: 15 * 60_000,\n};\nexport class Policy extends Context.Reference<PolicyShape>(\"@domainkit/Connect/Policy\", {\n defaultValue: () => defaults,\n}) {}\n\n// ---------------------------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------------------------\n\nconst Payload = Schema.Struct({\n method: Schema.Literals([\"oauth\", \"integration\"]),\n codeVerifier: Schema.NullOr(Schema.String),\n callbackUrl: Schema.String,\n domain: Schema.NullOr(Schema.String),\n /** Set when the flow is re-crediting a connection rather than making one. */\n connectionId: Schema.NullOr(Schema.String),\n});\n\nconst Target = Schema.Struct({\n zone: Schema.String,\n context: Schema.Unknown,\n label: Schema.String,\n});\n\nconst invalid = (message: string, field?: string) =>\n Errors.fail(new Reason.InvalidInput({ message, ...(field === undefined ? {} : { field }) }));\n\n/** Nameserver hostnames compare case-insensitively and without a trailing dot. */\nconst normalize = (name: string) => name.toLowerCase().replace(/\\.$/, \"\");\n\nexport const make: Effect.Effect<\n Interface,\n never,\n Storage.Service | Custody.Service | Providers.Service | Resolver.Service\n> = Effect.gen(function* () {\n const storage = yield* Storage.Service;\n const custody = yield* Custody.Service;\n const providers = yield* Providers.Service;\n const resolver = yield* Resolver.Service;\n\n const seal = (issued: Provider.IssuedCredential) =>\n Effect.gen(function* () {\n const ciphertext = yield* custody.seal(issued.secret);\n return new Storage.Credential({\n ciphertext,\n expiresAt: issued.expiresAt,\n rotatedAt: yield* DateTime.now,\n });\n });\n\n /** Plaintext credential with the context decoded from its stored envelope. */\n const open = (authorization: Storage.Authorization): Fx<Provider.Credential> =>\n Effect.gen(function* () {\n const definition = yield* providers.get(authorization.provider);\n const row = yield* storage.authorizations.credential(authorization.id);\n const secret = yield* custody.open(row.ciphertext);\n const context = yield* Provider.decodeContext(definition, authorization.context);\n return { secret, context };\n });\n\n const refresherFor = (definition: Provider.Definition, method: Storage.AuthMethod) =>\n method === \"oauth\"\n ? definition.auth.oauth?.refresh\n : method === \"integration\"\n ? definition.auth.integration?.refresh\n : undefined;\n\n const revokerFor = (definition: Provider.Definition, method: Storage.AuthMethod) =>\n method === \"oauth\"\n ? definition.auth.oauth?.revoke\n : method === \"integration\"\n ? definition.auth.integration?.revoke\n : undefined;\n\n const revokeAt = (authorization: Storage.Authorization): Fx<void> =>\n Effect.gen(function* () {\n const definition = yield* providers.get(authorization.provider);\n const revoke = revokerFor(definition, authorization.method);\n if (revoke === undefined) return;\n const credential = yield* open(authorization);\n yield* revoke(credential);\n });\n\n /** Finish revocations a crash interrupted; best effort, never blocks the caller. */\n const recover = storage.authorizations.recoverRevocations(revokeAt).pipe(Effect.ignore);\n\n const refresh: Interface[\"refresh\"] = (connectionId) =>\n Effect.gen(function* () {\n const policy = yield* Policy;\n const connection = yield* storage.connections.get(connectionId);\n const authorization = yield* storage.authorizations.get(connection.authorizationId);\n const definition = yield* providers.get(authorization.provider);\n const refresher = refresherFor(definition, authorization.method);\n const dueAt = (row: Storage.Credential, now: DateTime.Utc) =>\n row.expiresAt !== null &&\n DateTime.toEpochMillis(row.expiresAt) - DateTime.toEpochMillis(now) <=\n policy.refreshBeforeMs;\n const expired = (row: Storage.Credential, now: DateTime.Utc) =>\n row.expiresAt !== null &&\n DateTime.toEpochMillis(row.expiresAt) <= DateTime.toEpochMillis(now);\n const current = yield* storage.authorizations.credential(authorization.id);\n const now = yield* DateTime.now;\n if (!dueAt(current, now)) return \"current\" as const;\n if (refresher === undefined)\n return expired(current, now) ? (\"reconnect\" as const) : (\"current\" as const);\n return yield* storage\n .withLock(\n `refresh:${authorization.id}`,\n Effect.gen(function* () {\n // Disconnect's two-phase revocation does not take this lock: re-read the row here\n // so a revocation that started meanwhile skips the refresh instead of racing it.\n const live = yield* storage.authorizations.get(authorization.id).pipe(\n Effect.map(Option.some),\n Effect.catchIf(\n (error) => error.reason._tag === \"NotFound\",\n () => Effect.succeed(Option.none<Storage.Authorization>()),\n ),\n );\n if (Option.isNone(live) || live.value.revocation !== \"active\") {\n return \"reconnect\" as const;\n }\n const latest = yield* storage.authorizations.credential(authorization.id);\n if (!dueAt(latest, yield* DateTime.now)) return \"current\" as const;\n const secret = yield* custody.open(latest.ciphertext);\n const context = yield* Provider.decodeContext(definition, live.value.context);\n const issued = yield* refresher({ secret, context });\n // A revocation that completed while the provider was issuing leaves no row to\n // rotate; revoke the fresh credential so it does not stay live without a home.\n yield* storage.authorizations\n .rotate(authorization.id, yield* seal(issued))\n .pipe(\n Effect.tapError((error) =>\n error.reason._tag === \"NotFound\"\n ? (revokerFor(definition, authorization.method)?.(issued) ?? Effect.void).pipe(\n Effect.ignore,\n )\n : Effect.void,\n ),\n );\n return \"refreshed\" as const;\n }),\n )\n .pipe(\n Effect.catchIf(\n (error) => error.reason._tag === \"Busy\" && !expired(current, now),\n () => Effect.succeed(\"current\" as const),\n ),\n Effect.catchIf(\n (error) => error.reason._tag === \"Unauthenticated\",\n () => Effect.succeed(\"reconnect\" as const),\n ),\n );\n });\n\n const sessionFor = (connection: Storage.Connection): Fx<Provider.Session> =>\n Effect.gen(function* () {\n const authorization = yield* storage.authorizations.get(connection.authorizationId);\n const reconnect = Errors.fail(\n new Reason.Reconnect({\n provider: authorization.provider,\n connectionId: connection.id,\n }),\n );\n if (authorization.revocation !== \"active\") return yield* reconnect;\n if ((yield* refresh(connection.id)) === \"reconnect\") return yield* reconnect;\n const definition = yield* providers.get(authorization.provider);\n return definition.session(yield* open(authorization));\n });\n\n const connectWith = (input: {\n readonly definition: Provider.Definition;\n readonly method: Storage.AuthMethod;\n readonly issued: Provider.IssuedCredential;\n readonly requiredCapabilities: ReadonlyArray<Storage.Capability>;\n }): Fx<{ readonly connection: Storage.Connection; readonly session: Provider.Session }> =>\n Effect.gen(function* () {\n const principal = yield* Principal.Service;\n const session = input.definition.session(input.issued);\n const held = yield* session.capabilities();\n const missing = input.requiredCapabilities.filter((capability) => !held.includes(capability));\n if (missing.length > 0) {\n return yield* Errors.fail(\n new Reason.Forbidden({\n message: `${input.definition.name} credential lacks ${missing.join(\", \")}`,\n }),\n );\n }\n const authorization = new Storage.Authorization({\n id: yield* fresh(\"auth\"),\n ownerId: principal.ownerId,\n provider: input.definition.id,\n method: input.method,\n capabilities: held,\n context: yield* Provider.encodeContext(input.definition, input.issued.context),\n revocation: \"active\",\n createdBy: principal.actorId,\n createdAt: yield* DateTime.now,\n });\n yield* storage.authorizations.upsert({\n authorization,\n credential: yield* seal(input.issued),\n });\n const connection = yield* storage.connections.create(authorization.id);\n return { connection, session };\n });\n\n /**\n * Put a fresh credential behind an authorization the owner already has. The row keeps its id, so\n * every connection over it, and every domain on those connections, stays exactly where it is.\n */\n const replaceWith = (input: {\n readonly definition: Provider.Definition;\n readonly authorizationId: string;\n readonly method: Storage.AuthMethod;\n readonly issued: Provider.IssuedCredential;\n readonly requiredCapabilities: ReadonlyArray<Storage.Capability>;\n }): Fx<void> =>\n Effect.gen(function* () {\n const principal = yield* Principal.Service;\n const session = input.definition.session(input.issued);\n const held = yield* session.capabilities();\n const missing = input.requiredCapabilities.filter((capability) => !held.includes(capability));\n if (missing.length > 0) {\n return yield* Errors.fail(\n new Reason.Forbidden({\n message: `${input.definition.name} credential lacks ${missing.join(\", \")}`,\n }),\n );\n }\n const authorization = new Storage.Authorization({\n id: input.authorizationId,\n ownerId: principal.ownerId,\n provider: input.definition.id,\n method: input.method,\n capabilities: held,\n context: yield* Provider.encodeContext(input.definition, input.issued.context),\n revocation: \"active\",\n createdBy: principal.actorId,\n createdAt: yield* DateTime.now,\n });\n yield* storage.authorizations.upsert({\n authorization,\n credential: yield* seal(input.issued),\n expectedId: input.authorizationId,\n });\n });\n\n const attachWith = (input: {\n readonly definition: Provider.Definition;\n readonly connection: Storage.Connection;\n readonly session: Provider.Session;\n readonly domain: string;\n readonly target?: Provider.Target;\n }): Fx<Attached> =>\n Effect.gen(function* () {\n const domain = yield* DomainName.decode(input.domain);\n const existing = yield* storage.attachments.byDomain(domain);\n if (Option.isSome(existing)) {\n if (existing.value.connectionId === input.connection.id) return existing.value;\n return yield* invalid(`${domain} is already attached to another connection`, \"domain\");\n }\n const resolution: Provider.Resolution =\n input.target === undefined\n ? yield* input.session.resolveTarget(domain)\n : Provider.Resolution.Resolved({ target: input.target });\n switch (resolution._tag) {\n case \"SelectionRequired\":\n return Selection.SelectionRequired({ candidates: resolution.candidates });\n case \"NotFound\":\n return yield* Errors.fail(new Reason.NotFound({ entity: \"zone\", id: domain }));\n case \"Resolved\":\n return yield* storage.attachments.create({\n connectionId: input.connection.id,\n domain,\n zone: resolution.target.zone,\n label: resolution.target.label,\n target: {\n ...resolution.target,\n context: yield* Provider.encodeContext(input.definition, resolution.target.context),\n },\n });\n }\n });\n\n const started = (connection: Storage.Connection, attached: Attached | null): Started =>\n attached === null\n ? Started.Connected({ connection, attachment: null })\n : attached instanceof Storage.Attachment\n ? Started.Connected({ connection, attachment: attached })\n : Started.SelectionRequired({ connection, candidates: attached.candidates });\n\n /** Authoritative nameservers for the closest zone that answers an NS query. */\n const authoritative = (candidates: ReadonlyArray<DomainName.Model>) =>\n Effect.gen(function* () {\n for (const zone of candidates) {\n const outcomes = yield* resolver.resolve(zone, \"NS\");\n const names = outcomes.flatMap((outcome) =>\n outcome._tag === \"Answered\"\n ? outcome.answer.records.flatMap((record) =>\n record._tag === \"NS\" ? [record.nameserver] : [],\n )\n : [],\n );\n if (names.length > 0) return [...new Set(names)].sort();\n }\n return [] as ReadonlyArray<string>;\n });\n\n const discover: Interface[\"discover\"] = (input) =>\n Effect.gen(function* () {\n const domain = yield* DomainName.decode(input);\n const candidates = DomainName.candidates(domain);\n const nameservers = yield* authoritative(candidates);\n const reachable: Array<{\n readonly connectionId: string;\n readonly target: Provider.Target;\n }> = [];\n for (const connection of yield* storage.connections.list()) {\n const targets = yield* sessionFor(connection).pipe(\n Effect.flatMap((session) => session.listTargets()),\n Effect.catch(() => Effect.succeed([] as ReadonlyArray<Provider.Target>)),\n );\n for (const target of targets) {\n if (candidates.includes(target.zone as DomainName.Model)) {\n reachable.push({ connectionId: connection.id, target });\n }\n }\n }\n for (const zone of candidates) {\n const matches = reachable\n .filter(({ target }) => target.zone === zone)\n .sort((left, right) => left.connectionId.localeCompare(right.connectionId));\n if (matches.length === 0) continue;\n const [only] = matches;\n if (matches.length === 1 && only !== undefined) return Discovery.Resolved(only);\n const decisive = matches.filter(({ target }) => {\n const configured = new Set((target.nameservers ?? []).map(normalize));\n return nameservers.length > 0 && nameservers.every((name) => configured.has(name));\n });\n const [winner] = decisive;\n if (decisive.length === 1 && winner !== undefined) return Discovery.Resolved(winner);\n return Discovery.SelectionRequired({ candidates: matches });\n }\n return Discovery.NotFound({ nameservers, host: hostOf(nameservers) });\n });\n\n /** The one registered provider whose declared suffixes cover every nameserver, else `null`. */\n const hostOf = (nameservers: ReadonlyArray<string>): { readonly provider: string } | null => {\n if (nameservers.length === 0) return null;\n const names = nameservers.map(normalize);\n const hosts = providers.list().filter((definition) => {\n const suffixes = (definition.nameservers ?? []).map(normalize);\n return (\n suffixes.length > 0 &&\n names.every((name) =>\n suffixes.some((suffix) => name === suffix || name.endsWith(`.${suffix}`)),\n )\n );\n });\n const [host] = hosts;\n return hosts.length === 1 && host !== undefined ? { provider: host.id } : null;\n };\n\n const zones: Interface[\"zones\"] = (options = {}) =>\n Effect.gen(function* () {\n const reachable: Array<Zone> = [];\n const connections: Array<ZoneConnection> = [];\n const filter = options.provider === undefined ? undefined : { provider: options.provider };\n for (const connection of yield* storage.connections.list(filter)) {\n const authorization = yield* storage.authorizations.get(connection.authorizationId);\n // A credential the provider turned down is one connection's problem, not the listing's:\n // it is reported as needing a reconnect and every other connection still answers. Any\n // other failure is the listing's own, so it is raised rather than dressed up as an\n // authorization the customer has to grant again.\n const targets = yield* sessionFor(connection).pipe(\n Effect.flatMap((session) => session.listTargets()),\n Effect.map(Option.some),\n Effect.catchIf(\n (error) =>\n error.reason._tag === \"Reconnect\" ||\n error.reason._tag === \"Unauthenticated\" ||\n error.reason._tag === \"Forbidden\",\n () => Effect.succeed(Option.none<ReadonlyArray<Provider.Target>>()),\n ),\n );\n connections.push({\n connectionId: connection.id,\n provider: authorization.provider,\n status: Option.isNone(targets) ? \"reconnect\" : \"connected\",\n });\n if (Option.isNone(targets)) continue;\n for (const target of targets.value) {\n reachable.push({\n connectionId: connection.id,\n provider: authorization.provider,\n target,\n });\n }\n }\n return {\n zones: reachable.sort(\n (left, right) =>\n left.target.zone.localeCompare(right.target.zone) ||\n left.connectionId.localeCompare(right.connectionId),\n ),\n connections,\n providers: providers.list().map((definition) => ({\n id: definition.id,\n name: definition.name,\n methods: Provider.describeMethods(definition),\n })),\n };\n });\n\n const inspect: Interface[\"inspect\"] = (input) =>\n Effect.gen(function* () {\n const domain = yield* DomainName.decode(input);\n const policy = yield* Policy;\n const attachment = Option.getOrNull(yield* storage.attachments.byDomain(domain));\n const connection =\n attachment === null ? null : yield* storage.connections.get(attachment.connectionId);\n const authorization =\n connection === null ? null : yield* storage.authorizations.get(connection.authorizationId);\n const latest =\n attachment === null\n ? Option.none()\n : yield* storage.attempts.latest(attachment.id, \"provisioning\");\n // Letting a connection go takes every domain on it, so the UI has to be able to say how many.\n const attached = connection === null ? [] : yield* storage.attachments.list(connection.id);\n const reusable: Array<Snapshot[\"reusable\"][number]> = [];\n for (const candidate of yield* storage.connections.list()) {\n if (candidate.id === connection?.id) continue;\n if (!(yield* policy.allowReuse({ connection: candidate, domain }))) continue;\n const candidateAuthorization = yield* storage.authorizations.get(candidate.authorizationId);\n if (candidateAuthorization.revocation !== \"active\") continue;\n reusable.push({\n connection: candidate,\n provider: candidateAuthorization.provider,\n method: candidateAuthorization.method,\n });\n }\n return {\n domain,\n attachment,\n connection,\n authorization:\n authorization === null\n ? null\n : {\n id: authorization.id,\n provider: authorization.provider,\n method: authorization.method,\n capabilities: authorization.capabilities,\n revocation: authorization.revocation,\n },\n lastReceiptId: Option.isSome(latest) ? (latest.value.receipt?.id ?? null) : null,\n connectionDomains: attached.length,\n reusable,\n providers: providers.list().map((definition) => ({\n id: definition.id,\n name: definition.name,\n methods: Provider.describeMethods(definition),\n })),\n };\n });\n\n const start: Interface[\"start\"] = (input) =>\n Effect.gen(function* () {\n yield* recover;\n const definition = yield* providers.get(input.provider);\n if (input.domain !== undefined) yield* DomainName.decode(input.domain);\n switch (input.method._tag) {\n case \"Token\": {\n const token = definition.auth.token;\n if (token === undefined) {\n return yield* invalid(`${definition.name} does not accept tokens`, \"method\");\n }\n const issued = yield* token.authenticate(input.method.values);\n const { connection, session } = yield* connectWith({\n definition,\n method: \"token\",\n issued,\n requiredCapabilities: token.requiredCapabilities,\n });\n const attached =\n input.domain === undefined\n ? null\n : yield* attachWith({ definition, connection, session, domain: input.domain });\n return started(connection, attached);\n }\n case \"OAuth\":\n case \"Integration\":\n return yield* redirectFor({\n definition,\n method: input.method,\n callbackUrl: input.callbackUrl,\n domain: input.domain ?? null,\n connectionId: null,\n });\n }\n });\n\n /** The redirect an interactive method answers with, and the continuation that finishes it. */\n const redirectFor = (input: {\n readonly definition: Provider.Definition;\n readonly method: Extract<Method, { readonly _tag: \"OAuth\" | \"Integration\" }>;\n readonly callbackUrl: string | undefined;\n readonly domain: string | null;\n readonly connectionId: string | null;\n }): Fx<Started> =>\n Effect.gen(function* () {\n const principal = yield* Principal.Service;\n const policy = yield* Policy;\n const interactive =\n input.method._tag === \"OAuth\"\n ? input.definition.auth.oauth\n : input.definition.auth.integration;\n if (interactive === undefined) {\n return yield* invalid(\n `${input.definition.name} does not offer ${input.method._tag.toLowerCase()} connections`,\n \"method\",\n );\n }\n if (input.callbackUrl === undefined) {\n return yield* invalid(\"Interactive connections need a callbackUrl\", \"callbackUrl\");\n }\n const callbackUrl = input.callbackUrl;\n const continuationId = yield* fresh(\"cont\");\n const pkce = input.method._tag === \"OAuth\" ? yield* OAuth.pkce() : null;\n const redirect =\n input.method._tag === \"OAuth\"\n ? yield* (interactive as Provider.OAuthAuth).start({\n state: continuationId,\n callbackUrl,\n codeChallenge: pkce?.codeChallenge ?? \"\",\n })\n : yield* (interactive as Provider.IntegrationAuth).start({\n state: continuationId,\n callbackUrl,\n });\n yield* storage.continuations.put(\n new Storage.Continuation({\n id: continuationId,\n ownerId: principal.ownerId,\n actorId: principal.actorId,\n provider: input.definition.id,\n payload: Schema.encodeSync(Payload)({\n method: input.method._tag === \"OAuth\" ? \"oauth\" : \"integration\",\n codeVerifier: pkce?.codeVerifier ?? null,\n callbackUrl,\n domain: input.domain,\n connectionId: input.connectionId,\n }),\n returnTo: input.method.returnTo ?? null,\n expiresAt: DateTime.addDuration(\n yield* DateTime.now,\n Duration.millis(policy.continuationTtlMs),\n ),\n }),\n );\n return Started.Redirect({ authorizationUrl: redirect.authorizationUrl, continuationId });\n });\n\n const reconnect: Interface[\"reconnect\"] = (input) =>\n Effect.gen(function* () {\n yield* recover;\n const connection = yield* storage.connections.get(input.connectionId);\n const existing = yield* storage.authorizations.get(connection.authorizationId);\n const definition = yield* providers.get(existing.provider);\n switch (input.method._tag) {\n case \"Token\": {\n const token = definition.auth.token;\n if (token === undefined) {\n return yield* invalid(`${definition.name} does not accept tokens`, \"method\");\n }\n const issued = yield* token.authenticate(input.method.values);\n yield* replaceWith({\n definition,\n authorizationId: existing.id,\n method: \"token\",\n issued,\n requiredCapabilities: token.requiredCapabilities,\n });\n return Started.Connected({ connection, attachment: null });\n }\n case \"OAuth\":\n case \"Integration\":\n return yield* redirectFor({\n definition,\n method: input.method,\n callbackUrl: input.callbackUrl,\n domain: null,\n connectionId: connection.id,\n });\n }\n });\n\n const complete: Interface[\"complete\"] = (input) =>\n Effect.gen(function* () {\n // The continuation is spent only after the credential and connection are persisted, so a\n // failure before that (bad callback, provider outage, storage outage) leaves the flow\n // retryable. Concurrent callbacks for the same continuation serialize on a lock; the\n // loser sees Busy or NotFound. A provider code is single-use, so a retry after the\n // provider already redeemed it fails Unauthenticated and the customer starts over.\n const callback = yield* Effect.try({\n try: () => new URL(input.callbackUrl),\n catch: () =>\n new Errors.DomainKitError({\n reason: new Reason.InvalidInput({\n message: \"callbackUrl is not a URL\",\n field: \"callbackUrl\",\n }),\n }),\n });\n const params = Object.fromEntries(callback.searchParams);\n const unauthenticated = (message: string) =>\n Errors.fail(new Reason.Unauthenticated({ message }));\n if (params.error !== undefined) {\n return yield* unauthenticated(`Provider returned ${params.error}`);\n }\n if (params.state !== input.continuationId) {\n return yield* unauthenticated(\"Callback state does not match the continuation\");\n }\n const code = params.code;\n if (code === undefined) return yield* unauthenticated(\"Callback has no code\");\n return yield* storage.withLock(\n `continuation:${input.continuationId}`,\n Effect.gen(function* () {\n const continuation = yield* storage.continuations.get(input.continuationId);\n const payload = yield* Errors.decode(Payload, continuation.payload, \"continuation\");\n const definition = yield* providers.get(continuation.provider);\n const issued =\n payload.method === \"oauth\"\n ? yield* Effect.gen(function* () {\n const oauth = definition.auth.oauth;\n if (oauth === undefined || payload.codeVerifier === null) {\n return yield* invalid(\n `${definition.name} does not offer oauth connections`,\n \"method\",\n );\n }\n return yield* oauth.complete({\n code,\n callbackUrl: payload.callbackUrl,\n codeVerifier: payload.codeVerifier,\n params,\n });\n })\n : yield* Effect.gen(function* () {\n const integration = definition.auth.integration;\n if (integration === undefined) {\n return yield* invalid(\n `${definition.name} does not offer integration connections`,\n \"method\",\n );\n }\n return yield* integration.complete({\n code,\n callbackUrl: payload.callbackUrl,\n params,\n });\n });\n if (payload.connectionId !== null) {\n const held = yield* storage.connections.get(payload.connectionId);\n const existing = yield* storage.authorizations.get(held.authorizationId);\n yield* replaceWith({\n definition,\n authorizationId: existing.id,\n method: payload.method,\n issued,\n requiredCapabilities: [],\n });\n yield* storage.continuations.consume(input.continuationId).pipe(Effect.ignore);\n return Started.Connected({ connection: held, attachment: null });\n }\n const { connection, session } = yield* connectWith({\n definition,\n method: payload.method,\n issued,\n requiredCapabilities: [],\n });\n const attached =\n payload.domain === null\n ? null\n : yield* attachWith({ definition, connection, session, domain: payload.domain });\n // The connection is durable at this point. Whatever happens to the continuation\n // now (expired, already spent, or a storage failure while spending it) no longer\n // changes the outcome: the row expires on its own and a replayed callback fails at\n // the provider, so the persisted result is returned.\n yield* storage.continuations.consume(input.continuationId).pipe(Effect.ignore);\n return started(connection, attached);\n }),\n );\n });\n\n const attach: Interface[\"attach\"] = (input) =>\n Effect.gen(function* () {\n const connection = yield* storage.connections.get(input.connectionId);\n const authorization = yield* storage.authorizations.get(connection.authorizationId);\n const definition = yield* providers.get(authorization.provider);\n const session = yield* sessionFor(connection);\n return yield* attachWith({\n definition,\n connection,\n session,\n domain: input.domain,\n ...(input.target === undefined ? {} : { target: input.target }),\n });\n });\n\n const disconnect: Interface[\"disconnect\"] = (connectionId) =>\n Effect.gen(function* () {\n yield* recover;\n const connection = yield* storage.connections.get(connectionId);\n const attachments = yield* storage.attachments.list(connectionId);\n yield* Effect.forEach(attachments, (attachment) => storage.attachments.remove(attachment.id));\n yield* storage.connections.remove(connectionId);\n const remaining = (yield* storage.connections.list()).some(\n (candidate) => candidate.authorizationId === connection.authorizationId,\n );\n if (remaining) return;\n const authorization = yield* storage.authorizations.get(connection.authorizationId);\n yield* storage.authorizations.revoke(authorization.id, revokeAt(authorization));\n });\n\n const session: Interface[\"session\"] = (attachmentId) =>\n Effect.gen(function* () {\n const attachment = yield* storage.attachments.get(attachmentId);\n const connection = yield* storage.connections.get(attachment.connectionId);\n const authorization = yield* storage.authorizations.get(connection.authorizationId);\n const definition = yield* providers.get(authorization.provider);\n const stored = yield* Errors.decode(Target, attachment.target, \"target\");\n const target: Provider.Target = {\n ...stored,\n context: yield* Provider.decodeContext(definition, stored.context),\n };\n const built = yield* sessionFor(connection);\n // The zone must still be reachable by this credential: it may have moved account, been\n // deleted, or become a kind the provider cannot host records in.\n const reachable = yield* built.listTargets();\n if (!reachable.some((candidate) => candidate.zone === target.zone)) {\n return yield* Errors.fail(new Reason.NotFound({ entity: \"zone\", id: target.zone }));\n }\n return { session: built, target };\n });\n\n return {\n inspect,\n zones,\n discover,\n start,\n reconnect,\n complete,\n attach,\n detach: (attachmentId) => storage.attachments.remove(attachmentId),\n disconnect,\n refresh,\n session,\n };\n});\n\nexport const layer: Layer.Layer<\n Service,\n never,\n Storage.Service | Custody.Service | Providers.Service | Resolver.Service\n> = Layer.effect(Service)(make);\n\nconst accessor =\n <Args extends ReadonlyArray<unknown>, A>(\n pick: (service: Interface) => (...args: Args) => Fx<A>,\n ): ((...args: Args) => Effect.Effect<A, Errors.DomainKitError, Principal.Service | Service>) =>\n (...args) =>\n Effect.flatMap(Service, (service) => pick(service)(...args));\n\nexport const inspect = accessor((service) => service.inspect);\nexport const zones = accessor((service) => service.zones);\nexport const discover = accessor((service) => service.discover);\nexport const start = accessor((service) => service.start);\nexport const reconnect = accessor((service) => service.reconnect);\nexport const complete = accessor((service) => service.complete);\nexport const attach = accessor((service) => service.attach);\nexport const detach = accessor((service) => service.detach);\nexport const disconnect = accessor((service) => service.disconnect);\nexport const refresh = accessor((service) => service.refresh);\nexport const session = accessor((service) => service.session);\n"],"mappings":"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"}