@domainkit/react 0.3.1 → 0.5.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/index.mjs CHANGED
@@ -1,14 +1,17 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
- import { Dialog } from "@base-ui/react/dialog";
2
+ import { AlertDialog } from "@base-ui/react/alert-dialog";
3
3
  import { RegistryProvider, useAtomSet, useAtomValue } from "@effect/atom-react";
4
- import { Transport } from "domainkit";
4
+ import { DomainName, Transport } from "domainkit";
5
5
  import * as Data from "effect/Data";
6
6
  import * as Effect from "effect/Effect";
7
7
  import * as Atom from "effect/unstable/reactivity/Atom";
8
- import { createContext, useCallback, useContext, useEffect, useId, useMemo, useRef, useState, useSyncExternalStore } from "react";
8
+ import { createContext, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
9
9
  import { useRender } from "@base-ui/react/use-render";
10
10
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
11
+ import { Dialog } from "@base-ui/react/dialog";
11
12
  import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
13
+ import { Menu } from "@base-ui/react/menu";
14
+ import { Popover } from "@base-ui/react/popover";
12
15
  //#region src/atom.ts
13
16
  const Failure = Data.taggedEnum();
14
17
  const failureFromError = (error) => Failure.Failure({
@@ -96,12 +99,12 @@ const english = {
96
99
  applyCleanup: "Remove records",
97
100
  applyDns: "Add records",
98
101
  applyingDns: "Adding records…",
102
+ authenticationAlternative: "or",
99
103
  automaticUnavailable: "Automatic connection is not available for this domain",
100
104
  cancel: "Cancel",
101
105
  checkAgain: "Check again",
102
106
  checkingDns: "Checking DNS…",
103
107
  checkDns: "Check DNS",
104
- cleanupComplete: "DNS cleanup complete",
105
108
  cleanupConsent: "Only records proven by the apply receipt will be deleted.",
106
109
  cleanupPartial: "Some DNS records could not be deleted. Review and retry safely.",
107
110
  cleaningDns: "Removing records…",
@@ -112,20 +115,25 @@ const english = {
112
115
  close: "Close",
113
116
  detectingProvider: "Detecting DNS provider…",
114
117
  disconnectDomain: "Disconnect",
118
+ disconnectConsent: "This domain will be detached from the provider. Existing DNS records will be preserved.",
115
119
  disconnecting: "Disconnecting…",
116
- domainDisconnected: "Domain disconnected. DNS records were preserved.",
120
+ detaching: "Detaching domain…",
121
+ disconnectTitle: (provider) => `Detach ${provider}?`,
122
+ domainDisconnected: "Domain detached. DNS records were preserved.",
117
123
  dialogDescription: (domain) => `Authorize DNS changes for ${domain}.`,
118
124
  dialogTitle: (provider) => `Connect ${provider}`,
119
125
  openingAuthorization: "Opening provider authorization…",
126
+ moreActions: "More connection actions",
120
127
  planConsent: "Review the exact DNS operations before approving this plan.",
121
128
  planningCleanup: "Preparing cleanup…",
122
129
  planningDns: "Preparing DNS changes…",
123
- recordsApplied: "DNS changes applied",
124
130
  recordsPartiallyApplied: "Some DNS changes failed. Review and retry safely.",
125
131
  reviewCleanup: "Remove records",
126
132
  reviewDns: "Review changes",
127
133
  retry: "Try again",
128
- reuseConnection: (connection) => `Use ${connection}`,
134
+ existingConnection: "Existing provider connection",
135
+ targetUnavailable: "No provider targets are available for this connection.",
136
+ attachTarget: (target) => `Use ${target}`,
129
137
  tokenLabel: "API token"
130
138
  };
131
139
  const merge = (overrides = {}) => ({
@@ -138,9 +146,11 @@ var theme_exports = /* @__PURE__ */ __exportAll({ toStyle: () => toStyle });
138
146
  const variables = {
139
147
  accent: "--domainkit-accent",
140
148
  accentContrast: "--domainkit-accent-contrast",
149
+ backdrop: "--domainkit-backdrop",
141
150
  background: "--domainkit-background",
142
151
  border: "--domainkit-border",
143
152
  danger: "--domainkit-danger",
153
+ dangerContrast: "--domainkit-danger-contrast",
144
154
  fill: "--domainkit-fill",
145
155
  fontFamily: "--domainkit-font-family",
146
156
  muted: "--domainkit-muted",
@@ -181,8 +191,17 @@ const usePortalContainer = (container) => {
181
191
  const getSnapshot = useCallback(() => resolvePortalContainer(container, ownerDocument), [container, ownerDocument]);
182
192
  return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
183
193
  };
184
- function Root$6({ children, colorScheme = "inherit", icons, marks = {}, messages, navigate = navigateInBrowser, portalContainer = null, theme, transport, ...props }) {
194
+ function Root$6({ children, colorScheme = "inherit", icons, marks = {}, messages, navigate = navigateInBrowser, onEvent, portalContainer = null, theme, transport, ...props }) {
185
195
  const runtime = useMemo(() => Atom.runtime(transport), [transport]);
196
+ const onEventRef = useRef(onEvent);
197
+ useLayoutEffect(() => {
198
+ onEventRef.current = onEvent;
199
+ }, [onEvent]);
200
+ const emit = useCallback((event) => {
201
+ try {
202
+ onEventRef.current?.(event);
203
+ } catch {}
204
+ }, []);
186
205
  const themeStyle = toStyle(theme);
187
206
  const resolvedPortalContainer = usePortalContainer(portalContainer);
188
207
  const portalContainerRef = useMemo(() => ({ current: resolvedPortalContainer }), [resolvedPortalContainer]);
@@ -195,6 +214,7 @@ function Root$6({ children, colorScheme = "inherit", icons, marks = {}, messages
195
214
  return /* @__PURE__ */ jsx(RegistryProvider, { children: /* @__PURE__ */ jsx(Context.Provider, {
196
215
  value: {
197
216
  colorScheme,
217
+ emit,
198
218
  marks,
199
219
  messages: merge(messages),
200
220
  navigate,
@@ -214,6 +234,10 @@ function useDomainKit() {
214
234
  return value;
215
235
  }
216
236
  //#endregion
237
+ //#region src/lifecycle.ts
238
+ var lifecycle_exports = /* @__PURE__ */ __exportAll({ Event: () => Event });
239
+ const Event = Data.taggedEnum();
240
+ //#endregion
217
241
  //#region src/operations.tsx
218
242
  var operations_exports = /* @__PURE__ */ __exportAll({
219
243
  Item: () => Item,
@@ -288,52 +312,74 @@ var cleanup_exports = /* @__PURE__ */ __exportAll({
288
312
  });
289
313
  const State$3 = Data.taggedEnum();
290
314
  const Command$3 = Data.taggedEnum();
291
- function useModel$3(connection, receiptId) {
292
- const { runtime } = useDomainKit();
315
+ function useModel$3(connection, receiptId, onCleaned) {
316
+ const { emit, runtime } = useDomainKit();
317
+ const onCleanedRef = useRef(onCleaned);
318
+ onCleanedRef.current = onCleaned;
293
319
  return useMemo(() => {
294
320
  const state = Atom.make(State$3.Idle());
295
321
  return {
296
322
  command: runtime.fn()((command, get) => {
323
+ const current = get(state);
297
324
  if (command._tag === "Dismiss") {
325
+ if (current._tag === "Planning" || current._tag === "Cleaning") return Effect.void;
298
326
  get.set(state, State$3.Idle());
299
327
  return Effect.void;
300
328
  }
301
329
  if (command._tag === "Plan") {
330
+ if (current._tag === "Planning" || current._tag === "Cleaning") return Effect.void;
302
331
  get.set(state, State$3.Planning());
303
332
  return Effect.flatMap(Transport.Service, (transport) => transport.cleanup.plan({
304
- connectionId: connection.connectionId,
305
- domain: connection.domain,
333
+ attachmentId: connection.attachment.id,
334
+ domain: connection.attachment.domain,
306
335
  receiptId
307
336
  })).pipe(Effect.tap((plan) => Effect.sync(() => get.set(state, State$3.Review({ plan })))), Effect.catch((error) => Effect.sync(() => get.set(state, failureFromError(error)))), Effect.asVoid);
308
337
  }
309
- const current = get(state);
310
338
  if (current._tag !== "Review" || current.plan.operations.some((operation) => operation._tag === "Blocked")) return Effect.void;
311
339
  const plan = current.plan;
312
340
  get.set(state, State$3.Cleaning({ plan }));
313
341
  return Effect.flatMap(Transport.Service, (transport) => transport.cleanup.apply({
314
- connectionId: connection.connectionId,
315
- domain: connection.domain,
342
+ attachmentId: connection.attachment.id,
343
+ domain: connection.attachment.domain,
316
344
  planDigest: plan.digest,
317
345
  receiptId
318
- })).pipe(Effect.tap((result) => Effect.sync(() => get.set(state, result._tag === "Cleaned" ? State$3.Cleaned({ result }) : result._tag === "Partial" ? State$3.Partial({ result }) : result))), Effect.catch((error) => Effect.sync(() => get.set(state, failureFromError(error)))), Effect.asVoid);
346
+ })).pipe(Effect.tap((result) => Effect.sync(() => {
347
+ if (result._tag === "Cleaned") {
348
+ get.set(state, State$3.Cleaned({ result }));
349
+ onCleanedRef.current?.(result);
350
+ emit(Event.RecordsCleaned({
351
+ connection,
352
+ result
353
+ }));
354
+ } else if (result._tag === "Partial") {
355
+ get.set(state, State$3.Partial({ result }));
356
+ onCleanedRef.current?.(result);
357
+ emit(Event.RecordsPartiallyCleaned({
358
+ connection,
359
+ result
360
+ }));
361
+ } else get.set(state, result);
362
+ })), Effect.catch((error) => Effect.sync(() => get.set(state, failureFromError(error)))), Effect.asVoid);
319
363
  }),
320
364
  state
321
365
  };
322
366
  }, [
323
- connection.connectionId,
324
- connection.domain,
367
+ connection.attachment.domain,
368
+ connection.attachment.id,
369
+ emit,
325
370
  receiptId,
326
371
  runtime
327
372
  ]);
328
373
  }
329
- function useController$3(connection, receiptId) {
330
- const model = useModel$3(connection, receiptId);
374
+ function useController$3(connection, receiptId, onCleaned) {
375
+ const model = useModel$3(connection, receiptId, onCleaned);
331
376
  const state = useAtomValue(model.state);
332
377
  const execute = useAtomSet(model.command);
333
378
  return {
334
379
  apply: () => execute(Command$3.Apply()),
335
380
  dismiss: () => execute(Command$3.Dismiss()),
336
381
  plan: () => execute(Command$3.Plan()),
382
+ retry: () => execute(Command$3.Plan()),
337
383
  state
338
384
  };
339
385
  }
@@ -345,11 +391,6 @@ function Root$4({ status, ...props }) {
345
391
  }
346
392
  function Outcome$2({ state }) {
347
393
  const { messages } = useDomainKit();
348
- if (state._tag === "Cleaned") return /* @__PURE__ */ jsx("p", {
349
- "data-domainkit-part": "flow-outcome",
350
- "data-tone": "success",
351
- children: messages.cleanupComplete
352
- });
353
394
  if (state._tag === "Partial" || state._tag === "Failure" || state._tag === "Stale") return /* @__PURE__ */ jsx("p", {
354
395
  "data-domainkit-part": "flow-outcome",
355
396
  "data-tone": "danger",
@@ -358,23 +399,40 @@ function Outcome$2({ state }) {
358
399
  });
359
400
  return null;
360
401
  }
361
- function Dialog$3({ controller }) {
402
+ function Dialog$3({ controller, onOpenChange, onOpenChangeComplete, trigger }) {
362
403
  const { colorScheme, messages, portalContainer, themeStyle } = useDomainKit();
363
404
  const state = controller.state;
364
- const plan = state._tag === "Review" || state._tag === "Cleaning" ? state.plan : void 0;
365
- return /* @__PURE__ */ jsxs(Dialog.Root, {
366
- onOpenChange: (open) => {
367
- if (!open) controller.dismiss();
405
+ const planRef = useRef(void 0);
406
+ if (state._tag === "Idle") planRef.current = void 0;
407
+ if (state._tag === "Review" || state._tag === "Cleaning") planRef.current = state.plan;
408
+ const plan = planRef.current;
409
+ const busy = state._tag === "Planning" || state._tag === "Cleaning";
410
+ const open = state._tag !== "Idle" && state._tag !== "Cleaned";
411
+ return /* @__PURE__ */ jsxs(AlertDialog.Root, {
412
+ onOpenChange: (nextOpen, eventDetails) => {
413
+ if (!nextOpen && busy) {
414
+ eventDetails.cancel();
415
+ return;
416
+ }
417
+ if (!nextOpen) controller.dismiss();
418
+ onOpenChange?.(nextOpen);
368
419
  },
369
- open: plan !== void 0,
370
- children: [/* @__PURE__ */ jsx(Dialog.Trigger, {
420
+ onOpenChangeComplete,
421
+ open,
422
+ children: [trigger === null ? null : /* @__PURE__ */ jsx(AlertDialog.Trigger, {
371
423
  "data-domainkit-part": "cleanup-trigger",
372
- disabled: state._tag === "Planning" || state._tag === "Cleaning",
424
+ disabled: state._tag !== "Idle",
373
425
  onClick: () => controller.plan(),
374
- children: state._tag === "Planning" ? messages.planningCleanup : messages.reviewCleanup
375
- }), plan === void 0 ? null : /* @__PURE__ */ jsxs(Dialog.Portal, {
376
- container: portalContainer ?? void 0,
377
- children: [/* @__PURE__ */ jsx(Dialog.Backdrop, { "data-domainkit-part": "dialog-backdrop" }), /* @__PURE__ */ jsxs(Dialog.Popup, {
426
+ ...trigger === void 0 ? {} : { render: trigger },
427
+ children: messages.reviewCleanup
428
+ }), /* @__PURE__ */ jsxs(AlertDialog.Portal, {
429
+ container: portalContainer,
430
+ children: [/* @__PURE__ */ jsx(AlertDialog.Backdrop, {
431
+ "data-color-scheme": colorScheme,
432
+ "data-domainkit-part": "dialog-backdrop",
433
+ "data-domainkit-root": "",
434
+ style: themeStyle
435
+ }), /* @__PURE__ */ jsxs(AlertDialog.Popup, {
378
436
  "data-color-scheme": colorScheme,
379
437
  "data-domainkit-part": "cleanup-dialog",
380
438
  "data-domainkit-root": "",
@@ -384,138 +442,108 @@ function Dialog$3({ controller }) {
384
442
  "data-domainkit-part": "dialog-header",
385
443
  children: [/* @__PURE__ */ jsxs("div", {
386
444
  "data-domainkit-part": "dialog-heading",
387
- children: [/* @__PURE__ */ jsx(Dialog.Title, {
445
+ children: [/* @__PURE__ */ jsx(AlertDialog.Title, {
388
446
  "data-domainkit-part": "dialog-title",
389
447
  children: messages.reviewCleanup
390
- }), /* @__PURE__ */ jsx(Dialog.Description, {
448
+ }), /* @__PURE__ */ jsx(AlertDialog.Description, {
391
449
  "data-domainkit-part": "dialog-description",
392
450
  children: messages.cleanupConsent
393
451
  })]
394
- }), /* @__PURE__ */ jsx(Dialog.Close, {
452
+ }), busy ? null : /* @__PURE__ */ jsx(AlertDialog.Close, {
395
453
  "aria-label": messages.close,
396
454
  "data-domainkit-part": "dialog-close",
397
455
  children: "×"
398
456
  })]
399
457
  }),
400
- /* @__PURE__ */ jsx("div", {
458
+ /* @__PURE__ */ jsxs("div", {
401
459
  "data-domainkit-part": "dialog-body",
402
- children: /* @__PURE__ */ jsx(List, {
460
+ children: [plan === void 0 ? /* @__PURE__ */ jsx("p", {
461
+ "data-domainkit-part": "dialog-progress",
462
+ role: "status",
463
+ children: messages.planningCleanup
464
+ }) : /* @__PURE__ */ jsx(List, {
403
465
  lifecycle: "cleanup",
404
466
  operations: plan.operations
405
- })
467
+ }), /* @__PURE__ */ jsx(Outcome$2, { state })]
406
468
  }),
407
- /* @__PURE__ */ jsxs("div", {
469
+ /* @__PURE__ */ jsx("div", {
408
470
  "data-domainkit-part": "dialog-footer",
409
- children: [/* @__PURE__ */ jsx(Dialog.Close, {
471
+ children: busy ? /* @__PURE__ */ jsx("button", {
472
+ "data-domainkit-part": "cleanup-apply",
473
+ disabled: true,
474
+ type: "button",
475
+ children: state._tag === "Cleaning" ? messages.cleaningDns : messages.planningCleanup
476
+ }) : state._tag === "Review" && plan !== void 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(AlertDialog.Close, {
410
477
  "data-domainkit-part": "dialog-cancel",
411
478
  children: messages.cancel
412
479
  }), /* @__PURE__ */ jsx("button", {
413
480
  "data-domainkit-part": "cleanup-apply",
414
- disabled: state._tag === "Cleaning" || plan.operations.some((operation) => operation._tag === "Blocked"),
481
+ disabled: plan.operations.some((operation) => operation._tag === "Blocked"),
415
482
  onClick: () => controller.apply(),
416
483
  type: "button",
417
- children: state._tag === "Cleaning" ? messages.cleaningDns : messages.applyCleanup
418
- })]
484
+ children: messages.applyCleanup
485
+ })] }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(AlertDialog.Close, {
486
+ "data-domainkit-part": "dialog-cancel",
487
+ children: messages.close
488
+ }), /* @__PURE__ */ jsx("button", {
489
+ "data-domainkit-part": "cleanup-retry",
490
+ onClick: () => controller.retry(),
491
+ type: "button",
492
+ children: messages.retry
493
+ })] })
419
494
  })
420
495
  ]
421
496
  })]
422
497
  })]
423
498
  });
424
499
  }
425
- function Flow$3({ connection, receiptId, ...props }) {
426
- const controller = useController$3(connection, receiptId);
500
+ function Flow$3({ connection, onCleaned, receiptId, trigger, ...props }) {
501
+ const controller = useController$3(connection, receiptId, onCleaned);
427
502
  const state = controller.state;
428
- return /* @__PURE__ */ jsxs(Root$4, {
503
+ return /* @__PURE__ */ jsx(Root$4, {
429
504
  status: state._tag,
430
505
  ...props,
431
- children: [/* @__PURE__ */ jsx(Outcome$2, { state }), /* @__PURE__ */ jsx(Dialog$3, { controller })]
506
+ children: /* @__PURE__ */ jsx(Dialog$3, {
507
+ controller,
508
+ ...trigger === void 0 ? {} : { trigger }
509
+ })
432
510
  });
433
511
  }
434
512
  //#endregion
435
513
  //#region src/provider.tsx
436
514
  var provider_exports = /* @__PURE__ */ __exportAll({ Mark: () => Mark });
437
- const logoHosts = {
438
- aws: "amazonaws.com",
439
- bluehost: "bluehost.com",
440
- cloudflare: "cloudflare.com",
441
- digitalocean: "digitalocean.com",
442
- dnsimple: "dnsimple.com",
443
- dreamhost: "dreamhost.com",
444
- dynadot: "dynadot.com",
445
- gandi: "gandi.net",
446
- godaddy: "godaddy.com",
447
- google: "cloud.google.com",
448
- googlecloud: "cloud.google.com",
449
- googleclouddns: "cloud.google.com",
450
- hostgator: "hostgator.com",
451
- hostinger: "hostinger.com",
452
- hover: "hover.com",
453
- ionos: "ionos.com",
454
- namecheap: "namecheap.com",
455
- namecom: "name.com",
456
- netlify: "netlify.com",
457
- ns1: "ns1.com",
458
- ovh: "ovh.com",
459
- porkbun: "porkbun.com",
460
- spaceship: "spaceship.com",
461
- squarespace: "squarespace.com",
462
- vercel: "vercel.com"
463
- };
464
515
  const logoKey = (providerId) => providerId.trim().toLowerCase().replace(/[^a-z0-9]+/g, "");
465
- const logoHost = (providerId) => logoHosts[logoKey(providerId)];
466
516
  const letterMark = (provider) => provider.name.trim().charAt(0).toUpperCase() || "?";
467
- /** Official AWS architecture glyph (white "53" in nested circles). */
468
- const route53Glyph = "M48.692,40.6795109 C49.431,41.3627732 49.801,42.2781246 49.801,43.4275659 C49.801,44.678046 49.351,45.6764293 48.452,46.4227159 C47.552,47.1700028 46.343,47.543146 44.826,47.543146 C43.627,47.543146 42.44,47.2870477 41.266,46.7768518 L41.266,45.2452638 C42.657,45.7054405 43.843,45.9345284 44.826,45.9345284 C45.796,45.9345284 46.542,45.7174451 47.065,45.2842788 C47.588,44.8501121 47.849,44.2308744 47.849,43.4275659 C47.849,41.8829729 46.874,41.1106764 44.921,41.1106764 C44.309,41.1106764 43.703,41.1426887 43.103,41.2057129 L43.103,39.9432282 L47.123,35.5585448 L41.458,35.5585448 L41.458,33.9889422 L49.247,33.9889422 L49.247,35.5015229 L45.304,39.6751253 C45.369,39.6621203 45.431,39.656118 45.496,39.656118 L45.687,39.656118 C46.951,39.656118 47.952,39.9972489 48.692,40.6795109 M37.574,40.3453826 C38.351,41.0726618 38.74,42.0740463 38.74,43.349536 C38.74,44.6010165 38.288,45.6124048 37.381,46.3847013 C36.475,47.1569978 35.283,47.543146 33.803,47.543146 C32.503,47.543146 31.284,47.2870477 30.148,46.7768518 L30.148,45.2452638 C31.564,45.7054405 32.776,45.9345284 33.784,45.9345284 C34.754,45.9345284 35.497,45.7154443 36.013,45.2742749 C36.53,44.834106 36.789,44.1998624 36.789,43.3695437 C36.789,42.4631957 36.546,41.8059434 36.061,41.3977867 C35.576,40.9896299 34.785,40.7845512 33.688,40.7845512 C32.897,40.7845512 31.909,40.8495762 30.722,40.976625 L30.722,39.7131399 L31.086,33.9889422 L38.071,33.9889422 L38.071,35.5585448 L32.693,35.5585448 L32.444,39.4450369 C33.146,39.3179882 33.777,39.2539636 34.338,39.2539636 C35.716,39.2539636 36.795,39.617103 37.574,40.3453826 M51.957,54.37777 C47.103,55.250105 42.852,57.2268639 40,58.8374822 C37.147,57.2268639 32.896,55.250105 28.043,54.37777 C26.677,54.1326759 19.869,52.7111302 19.869,48.7756192 C19.869,46.951919 20.522,45.7424547 21.776,43.5826255 C23.274,40.9996338 25.138,37.7843994 25.138,33.1556222 C25.138,29.8473521 24.271,26.6751342 22.559,23.7149977 C22.76,23.4669025 22.965,23.2158061 23.171,22.9627089 C25.707,24.2301955 28.347,24.8724421 31.031,24.8724421 C34.311,24.8724421 37.325,24.0111114 40,22.3114589 C42.674,24.0111114 45.688,24.8724421 48.968,24.8724421 C51.652,24.8724421 54.293,24.2301955 56.829,22.9627089 C57.034,23.2158061 57.239,23.4669025 57.44,23.7149977 C55.728,26.6751342 54.861,29.8473521 54.861,33.1556222 C54.861,37.7843994 56.725,40.9996338 58.226,43.586627 C59.477,45.7424547 60.13,46.951919 60.13,48.7756192 C60.13,52.7111302 53.322,54.1326759 51.957,54.37777 M56.861,33.1556222 C56.861,29.9974097 57.752,26.9702475 59.507,24.1581679 C59.735,23.7950284 59.706,23.3278491 59.434,22.9957216 C58.926,22.3734827 58.393,21.7182311 57.867,21.0669811 C57.562,20.6898363 57.032,20.5857963 56.606,20.8198862 C54.143,22.1814089 51.574,22.8716739 48.968,22.8716739 C45.824,22.8716739 43.077,22.0263494 40.569,20.2886823 C40.227,20.0515912 39.772,20.0515912 39.43,20.2886823 C36.922,22.0263494 34.175,22.8716739 31.031,22.8716739 C28.425,22.8716739 25.856,22.1814089 23.393,20.8198862 C22.968,20.5857963 22.437,20.6898363 22.132,21.0669811 C21.606,21.7182311 21.073,22.3734827 20.565,22.9957216 C20.294,23.3278491 20.264,23.7950284 20.492,24.1581679 C22.248,26.9702475 23.138,29.9974097 23.138,33.1556222 C23.138,37.2461927 21.423,40.2023277 20.045,42.581241 C18.695,44.9041328 17.869,46.4467251 17.869,48.7756192 C17.869,54.1646882 25.385,55.9343677 27.689,56.3475263 C32.549,57.2208616 36.793,59.2806524 39.497,60.8542565 C39.652,60.9452915 39.826,60.9903088 40,60.9903088 C40.173,60.9903088 40.347,60.9452915 40.503,60.8542565 C43.207,59.2806524 47.45,57.2208616 52.31,56.3475263 C54.614,55.9343677 62.13,54.1646882 62.13,48.7756192 C62.13,46.4467251 61.304,44.9041328 59.954,42.5782399 C58.576,40.2023277 56.861,37.2461927 56.861,33.1556222 M52.994,60.1569888 C46.863,61.2594121 41.723,64.556678 40,65.7641416 C38.276,64.556678 33.136,61.2594121 27.005,60.1569888 C14.937,57.9891566 14,50.8974338 14,48.7756192 C14,45.2852792 15.371,42.9233724 16.697,40.6374948 C17.961,38.4596586 19.268,36.2067937 19.268,33.1556222 C19.268,28.3357718 16.485,24.9314648 15.112,23.5459328 C16.556,21.7892584 20.204,17.3415508 22.003,15.0236609 C24.729,17.5966487 27.898,19.0001876 31.031,19.0001876 C34.509,19.0001876 37.385,17.5846441 40,14.5644846 C42.614,17.5846441 45.49,19.0001876 48.968,19.0001876 C52.101,19.0001876 55.27,17.5966487 57.997,15.0236609 C59.796,17.3415508 63.443,21.7892584 64.887,23.5459328 C63.514,24.9314648 60.731,28.3357718 60.731,33.1556222 C60.731,36.2067937 62.039,38.4596586 63.302,40.6374948 C64.629,42.9233724 66,45.2852792 66,48.7756192 C66,50.8974338 65.062,57.9891566 52.994,60.1569888 M65.032,39.6341095 C63.797,37.5052922 62.731,35.6675867 62.731,33.1556222 C62.731,27.867592 66.847,24.4472789 66.887,24.4152666 C67.094,24.2472021 67.226,24.0031083 67.254,23.7370062 C67.28,23.470904 67.2,23.2058022 67.03,22.9997231 C66.963,22.9196924 60.408,14.9726413 58.906,12.9138509 C58.73,12.6727583 58.457,12.5227007 58.159,12.5046938 C57.86,12.4826853 57.572,12.601731 57.368,12.8198148 C54.848,15.5148495 51.866,16.9994194 48.968,16.9994194 C45.764,16.9994194 43.244,15.5718713 40.794,12.3706423 C40.415,11.8764526 39.585,11.8764526 39.206,12.3706423 C36.755,15.5718713 34.235,16.9994194 31.031,16.9994194 C28.133,16.9994194 25.151,15.5148495 22.631,12.8198148 C22.427,12.601731 22.137,12.4776834 21.84,12.5046938 C21.543,12.5227007 21.269,12.6727583 21.093,12.9138509 C19.591,14.9726413 13.036,22.9196924 12.969,22.9997231 C12.8,23.2058022 12.72,23.470904 12.747,23.7360058 C12.773,24.0011076 12.904,24.2452013 13.11,24.4132658 C13.152,24.4472789 17.268,27.867592 17.268,33.1556222 C17.268,35.6675867 16.202,37.5052922 14.967,39.6341095 C13.576,42.0310298 12,44.7470725 12,48.7756192 C12,55.487196 17.477,60.4781121 26.652,62.1267451 C33.559,63.3682217 39.334,67.7489036 39.391,67.7939209 C39.57,67.9309735 39.785,68 40,68 C40.214,68 40.429,67.9309735 40.609,67.7929205 C40.667,67.7489036 46.422,63.3712229 53.347,62.1267451 C62.522,60.4781121 68,55.487196 68,48.7756192 C68,44.7470725 66.423,42.0310298 65.032,39.6341095";
469
- function Route53Mark() {
470
- const gradientId = `dk-r53-${useId().replaceAll(":", "")}`;
517
+ function CloudflareMark() {
471
518
  return /* @__PURE__ */ jsxs("svg", {
472
519
  "aria-hidden": "true",
473
- viewBox: "0 0 80 80",
474
- children: [
475
- /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("linearGradient", {
476
- id: gradientId,
477
- x1: "0%",
478
- x2: "100%",
479
- y1: "100%",
480
- y2: "0%",
481
- children: [/* @__PURE__ */ jsx("stop", {
482
- offset: "0%",
483
- stopColor: "#4D27A8"
484
- }), /* @__PURE__ */ jsx("stop", {
485
- offset: "100%",
486
- stopColor: "#A166FF"
487
- })]
488
- }) }),
489
- /* @__PURE__ */ jsx("rect", {
490
- fill: `url(#${gradientId})`,
491
- height: "80",
492
- width: "80"
493
- }),
494
- /* @__PURE__ */ jsx("path", {
495
- d: route53Glyph,
496
- fill: "#fff"
497
- })
498
- ]
520
+ viewBox: "0 0 48 32",
521
+ children: [/* @__PURE__ */ jsx("path", {
522
+ d: "M30.2 24.6H7.7a6.2 6.2 0 0 1 .5-12.4 9.7 9.7 0 0 1 18.5-2.8 7.7 7.7 0 0 1 3.5 15.2Z",
523
+ fill: "#f48120"
524
+ }), /* @__PURE__ */ jsx("path", {
525
+ d: "M33.1 16.2h1.3a5.2 5.2 0 0 1 .3 10.4H18.2l1.2-3.4h12.9l.8-7Z",
526
+ fill: "#faad3d"
527
+ })]
528
+ });
529
+ }
530
+ function VercelMark() {
531
+ return /* @__PURE__ */ jsx("svg", {
532
+ "aria-hidden": "true",
533
+ viewBox: "0 0 32 32",
534
+ children: /* @__PURE__ */ jsx("path", {
535
+ d: "m16 5 13 22H3L16 5Z",
536
+ fill: "currentColor"
537
+ })
499
538
  });
500
539
  }
501
- const logoMarks = {
502
- amazonroute53: Route53Mark,
503
- route53: Route53Mark
540
+ const builtInMarks = {
541
+ cloudflare: CloudflareMark,
542
+ vercel: VercelMark
504
543
  };
505
544
  function DefaultMark({ provider }) {
506
- const BuiltIn = logoMarks[logoKey(provider.id)];
507
- const host = logoHost(provider.id);
508
- const [failed, setFailed] = useState(false);
509
- if (BuiltIn !== void 0) return /* @__PURE__ */ jsx(BuiltIn, {});
510
- if (host === void 0 || failed) return letterMark(provider);
511
- return /* @__PURE__ */ jsx("img", {
512
- alt: "",
513
- height: 32,
514
- onError: () => setFailed(true),
515
- referrerPolicy: "no-referrer",
516
- src: `https://integrations.sh/logo/${host}?sz=64`,
517
- width: 32
518
- });
545
+ const BuiltIn = builtInMarks[logoKey(provider.id)];
546
+ return BuiltIn === void 0 ? letterMark(provider) : /* @__PURE__ */ jsx(BuiltIn, {});
519
547
  }
520
548
  function Mark({ provider, ...props }) {
521
549
  const { marks } = useDomainKit();
@@ -531,28 +559,32 @@ function Mark({ provider, ...props }) {
531
559
  //#endregion
532
560
  //#region src/connection.tsx
533
561
  var connection_exports = /* @__PURE__ */ __exportAll({
562
+ AttachAction: () => AttachAction,
563
+ Card: () => Card$1,
564
+ CardActions: () => CardActions,
565
+ CardIdentity: () => CardIdentity,
534
566
  Command: () => Command$2,
535
567
  ConnectTrigger: () => ConnectTrigger,
536
568
  Dialog: () => Dialog$2,
537
- DisconnectAction: () => DisconnectAction,
538
- DisconnectState: () => DisconnectState,
569
+ DisconnectDialog: () => DisconnectDialog,
539
570
  Flow: () => Flow$2,
571
+ IntegrationAction: () => IntegrationAction,
540
572
  OAuthAction: () => OAuthAction,
541
573
  RetryAction: () => RetryAction$1,
542
- ReuseAction: () => ReuseAction,
543
574
  Root: () => Root$3,
544
575
  State: () => State$2,
545
576
  Status: () => Status$2,
546
577
  TokenAction: () => TokenAction,
547
578
  Trigger: () => Trigger,
579
+ targetLabel: () => targetLabel,
548
580
  useController: () => useController$2,
549
- useDisconnect: () => useDisconnect,
550
581
  useModel: () => useModel$2
551
582
  });
552
583
  const State$2 = Data.taggedEnum();
584
+ const sameTarget = (left, right) => left.accountId === right.accountId && left.accountKind === right.accountKind && left.zoneId === right.zoneId && left.zoneName === right.zoneName;
553
585
  const Command$2 = Data.taggedEnum();
554
586
  function useModel$2(domain) {
555
- const { navigate, runtime } = useDomainKit();
587
+ const { emit, navigate, runtime } = useDomainKit();
556
588
  return useMemo(() => {
557
589
  const actionState = Atom.make(void 0);
558
590
  const inspect = runtime.atom(Effect.flatMap(Transport.Service, (transport) => transport.connection.inspect({ domain })));
@@ -575,31 +607,66 @@ function useModel$2(domain) {
575
607
  return Effect.void;
576
608
  }
577
609
  const snapshot = get(state);
610
+ if (input._tag === "Detach") {
611
+ if (snapshot._tag !== "Connected") return Effect.void;
612
+ get.set(actionState, State$2.Detaching({ snapshot }));
613
+ return Effect.flatMap(Transport.Service, (transport) => Effect.flatMap(transport.connection.detach({
614
+ attachmentId: snapshot.attachment.id,
615
+ preserveDns: true
616
+ }), (result) => {
617
+ const detached = {
618
+ _tag: "Disconnected",
619
+ domain: snapshot.attachment.domain,
620
+ provider: snapshot.provider,
621
+ reusableConnections: []
622
+ };
623
+ return Effect.flatMap(Effect.sync(() => {
624
+ get.set(actionState, detached);
625
+ emit(Event.DomainDetached({
626
+ connection: snapshot,
627
+ result
628
+ }));
629
+ }), () => transport.connection.inspect({ domain }).pipe(Effect.catch(() => Effect.succeed(detached)), Effect.tap((refreshed) => Effect.sync(() => get.set(actionState, refreshed)))));
630
+ })).pipe(Effect.catch((error) => Effect.sync(() => get.set(actionState, failureFromError(error)))), Effect.asVoid);
631
+ }
578
632
  if (snapshot._tag !== "Disconnected") return Effect.void;
579
- const reusableConnection = snapshot.reusableConnection;
580
- if (input._tag === "Reuse" && reusableConnection === void 0) return Effect.void;
581
- get.set(actionState, State$2.Submitting({ snapshot }));
582
- const complete = (request) => request.pipe(Effect.tap((result) => Effect.sync(() => {
633
+ const complete = (request, source) => request.pipe(Effect.tap((result) => Effect.sync(() => {
583
634
  if (result._tag === "Redirect") {
584
635
  get.set(actionState, State$2.Redirecting({ snapshot }));
585
636
  navigate(result.authorizationUrl);
586
- } else get.set(actionState, result);
637
+ } else {
638
+ get.set(actionState, result);
639
+ emit(Event.ConnectionEstablished({
640
+ connection: result,
641
+ source
642
+ }));
643
+ }
587
644
  })), Effect.catch((error) => Effect.sync(() => get.set(actionState, failureFromError(error)))), Effect.asVoid);
645
+ if (input._tag === "Attach") {
646
+ const reusableConnection = snapshot.reusableConnections.find(({ connection }) => connection.id === input.connectionId);
647
+ if (reusableConnection === void 0) return Effect.void;
648
+ const target = reusableConnection.targets.find((candidate) => sameTarget(candidate, input.target));
649
+ if (target === void 0) return Effect.void;
650
+ get.set(actionState, State$2.Submitting({ snapshot }));
651
+ return complete(Effect.flatMap(Transport.Service, (transport) => transport.connection.attach({
652
+ connectionId: input.connectionId,
653
+ domain,
654
+ target
655
+ })), "attach");
656
+ }
657
+ get.set(actionState, State$2.Submitting({ snapshot }));
588
658
  if (input._tag === "Connect") return complete(Effect.flatMap(Transport.Service, (transport) => transport.connection.connect({
589
659
  domain,
590
660
  method: input.method,
591
661
  providerId: snapshot.provider.id
592
- })));
593
- if (reusableConnection === void 0) return Effect.void;
594
- return complete(Effect.flatMap(Transport.Service, (transport) => transport.connection.reuse({
595
- connectionId: reusableConnection.connectionId,
596
- domain
597
- })));
662
+ })), "connect");
663
+ return Effect.void;
598
664
  }),
599
665
  state
600
666
  };
601
667
  }, [
602
668
  domain,
669
+ emit,
603
670
  navigate,
604
671
  runtime
605
672
  ]);
@@ -609,9 +676,13 @@ function useController$2(domain) {
609
676
  const state = useAtomValue(model.state);
610
677
  const execute = useAtomSet(model.command);
611
678
  return {
679
+ attach: (connection, target) => execute(Command$2.Attach({
680
+ connectionId: connection.connection.id,
681
+ target
682
+ })),
612
683
  connect: (method) => execute(Command$2.Connect({ method })),
684
+ detach: () => execute(Command$2.Detach()),
613
685
  retry: () => execute(Command$2.Retry()),
614
- reuse: () => execute(Command$2.Reuse()),
615
686
  state
616
687
  };
617
688
  }
@@ -623,7 +694,7 @@ function Root$3({ children, status = "Loading", ...props }) {
623
694
  }
624
695
  function Status$2({ children, state, ...props }) {
625
696
  const { messages } = useDomainKit();
626
- const text = state._tag === "Loading" ? messages.detectingProvider : state._tag === "Connected" ? messages.connected(state.provider.name) : state._tag === "Unsupported" ? messages.automaticUnavailable : state._tag === "Failure" ? state.message : state._tag === "Redirecting" ? messages.openingAuthorization : state._tag === "Submitting" ? messages.connecting : messages.providerAvailable(state.provider.name);
697
+ const text = state._tag === "Loading" ? messages.detectingProvider : state._tag === "Connected" ? messages.connected(state.provider.name) : state._tag === "Detaching" ? messages.detaching : state._tag === "Unsupported" ? messages.automaticUnavailable : state._tag === "Failure" ? state.message : state._tag === "Redirecting" ? messages.openingAuthorization : state._tag === "Submitting" ? messages.connecting : messages.providerAvailable(state.provider.name);
627
698
  return usePart("div", props, { status: state._tag }, {
628
699
  children: children ?? text,
629
700
  "data-domainkit-part": "connection-status",
@@ -652,20 +723,32 @@ function OAuthAction({ controller, label, ...props }) {
652
723
  return usePart("button", props, { authentication: "oauth" }, {
653
724
  children: label,
654
725
  "data-domainkit-part": "oauth-connect",
726
+ disabled: controller.state._tag === "Submitting" || controller.state._tag === "Redirecting",
655
727
  onClick: () => controller.connect(Transport.Method.OAuth()),
656
728
  type: "button"
657
729
  });
658
730
  }
659
- function ReuseAction({ controller, label, ...props }) {
660
- return usePart("button", props, { authentication: "reuse" }, {
731
+ function IntegrationAction({ controller, label, ...props }) {
732
+ return usePart("button", props, { authentication: "integration" }, {
661
733
  children: label,
662
- "data-domainkit-part": "reuse-connection",
663
- onClick: () => void controller.reuse(),
734
+ "data-domainkit-part": "integration-connect",
735
+ disabled: controller.state._tag === "Submitting" || controller.state._tag === "Redirecting",
736
+ onClick: () => controller.connect(Transport.Method.Integration()),
737
+ type: "button"
738
+ });
739
+ }
740
+ function AttachAction({ connection, controller, label, target, ...props }) {
741
+ return usePart("button", props, { authentication: "attach" }, {
742
+ children: label,
743
+ "data-domainkit-part": "attach-target",
744
+ disabled: controller.state._tag === "Submitting" || controller.state._tag === "Redirecting",
745
+ onClick: () => controller.attach(connection, target),
664
746
  type: "button"
665
747
  });
666
748
  }
667
749
  function TokenAction({ controller, method, ...props }) {
668
750
  const { messages } = useDomainKit();
751
+ const pending = controller.state._tag === "Submitting" || controller.state._tag === "Redirecting";
669
752
  const [token, setToken] = useState("");
670
753
  const [parameters, setParameters] = useState({});
671
754
  const submit = (event) => {
@@ -685,6 +768,7 @@ function TokenAction({ controller, method, ...props }) {
685
768
  parameter.label,
686
769
  /* @__PURE__ */ jsx("input", {
687
770
  name: parameter.key,
771
+ disabled: pending,
688
772
  onChange: (event) => {
689
773
  const value = event.currentTarget.value;
690
774
  setParameters((current) => ({
@@ -701,6 +785,7 @@ function TokenAction({ controller, method, ...props }) {
701
785
  ] }, parameter.key)),
702
786
  /* @__PURE__ */ jsxs("label", { children: [messages.tokenLabel, /* @__PURE__ */ jsx("input", {
703
787
  autoComplete: "off",
788
+ disabled: pending,
704
789
  name: "token",
705
790
  onChange: (event) => setToken(event.currentTarget.value),
706
791
  placeholder: method.placeholder,
@@ -708,7 +793,7 @@ function TokenAction({ controller, method, ...props }) {
708
793
  value: token
709
794
  })] }),
710
795
  /* @__PURE__ */ jsx("button", {
711
- disabled: token.length === 0 || missingRequiredParameter === true,
796
+ disabled: pending || token.length === 0 || missingRequiredParameter === true,
712
797
  type: "submit",
713
798
  children: method.label
714
799
  })
@@ -717,11 +802,22 @@ function TokenAction({ controller, method, ...props }) {
717
802
  onSubmit: submit
718
803
  });
719
804
  }
805
+ const targetLabel = (target) => `${target.evidence?.accountName ?? target.accountId} · ${target.zoneName}`;
720
806
  function Dialog$2({ controller, snapshot }) {
721
807
  const { colorScheme, messages, portalContainer, themeStyle } = useDomainKit();
808
+ const pending = controller.state._tag === "Submitting" || controller.state._tag === "Redirecting";
809
+ const integrationMethods = snapshot.provider.authentication.filter((method) => method._tag === "Integration");
810
+ const oauthMethods = snapshot.provider.authentication.filter((method) => method._tag === "OAuth");
811
+ const tokenMethods = snapshot.provider.authentication.filter((method) => method._tag === "Token");
812
+ const hasProviderAccountPath = integrationMethods.length > 0 || oauthMethods.length > 0 || snapshot.reusableConnections.length > 0;
722
813
  return /* @__PURE__ */ jsxs(Dialog.Portal, {
723
814
  container: portalContainer,
724
- children: [/* @__PURE__ */ jsx(Dialog.Backdrop, { "data-domainkit-part": "dialog-backdrop" }), /* @__PURE__ */ jsxs(Dialog.Popup, {
815
+ children: [/* @__PURE__ */ jsx(Dialog.Backdrop, {
816
+ "data-color-scheme": colorScheme,
817
+ "data-domainkit-part": "dialog-backdrop",
818
+ "data-domainkit-root": "",
819
+ style: themeStyle
820
+ }), /* @__PURE__ */ jsxs(Dialog.Popup, {
725
821
  "data-domainkit-part": "connection-dialog",
726
822
  "data-domainkit-root": "",
727
823
  "data-color-scheme": colorScheme,
@@ -741,7 +837,7 @@ function Dialog$2({ controller, snapshot }) {
741
837
  children: messages.dialogDescription(snapshot.domain)
742
838
  })]
743
839
  }),
744
- /* @__PURE__ */ jsx(Dialog.Close, {
840
+ pending ? null : /* @__PURE__ */ jsx(Dialog.Close, {
745
841
  "aria-label": messages.close,
746
842
  "data-domainkit-part": "dialog-close",
747
843
  children: "×"
@@ -751,21 +847,55 @@ function Dialog$2({ controller, snapshot }) {
751
847
  /* @__PURE__ */ jsxs("div", {
752
848
  "data-domainkit-part": "dialog-body",
753
849
  children: [
754
- snapshot.provider.authentication.map((method) => method._tag === "OAuth" ? /* @__PURE__ */ jsx(OAuthAction, {
755
- controller,
756
- label: method.label
757
- }, method._tag) : null),
758
- snapshot.reusableConnection === void 0 ? null : /* @__PURE__ */ jsx(ReuseAction, {
759
- controller,
760
- label: messages.reuseConnection(snapshot.reusableConnection.label)
761
- }),
762
- snapshot.provider.authentication.map((method) => method._tag === "Token" ? /* @__PURE__ */ jsx(TokenAction, {
850
+ hasProviderAccountPath ? /* @__PURE__ */ jsxs("div", {
851
+ "data-domainkit-part": "provider-authentication",
852
+ children: [
853
+ integrationMethods.map((method) => /* @__PURE__ */ jsx(IntegrationAction, {
854
+ controller,
855
+ label: method.label
856
+ }, method._tag)),
857
+ oauthMethods.map((method) => /* @__PURE__ */ jsx(OAuthAction, {
858
+ controller,
859
+ label: method.label
860
+ }, method._tag)),
861
+ snapshot.reusableConnections.map((connection) => /* @__PURE__ */ jsxs("section", {
862
+ "data-connection-id": connection.connection.id,
863
+ "data-domainkit-part": "reusable-connection",
864
+ children: [/* @__PURE__ */ jsxs("div", {
865
+ "data-domainkit-part": "reusable-connection-heading",
866
+ children: [/* @__PURE__ */ jsx("strong", { children: messages.existingConnection }), /* @__PURE__ */ jsx("span", { children: connection.connection.providerId })]
867
+ }), /* @__PURE__ */ jsx("div", {
868
+ "data-domainkit-part": "target-list",
869
+ "data-state": connection.targets.length === 0 ? "unavailable" : connection.targets.length === 1 ? "unique" : "ambiguous",
870
+ children: connection.targets.length === 0 ? /* @__PURE__ */ jsx("p", {
871
+ "data-domainkit-part": "target-unavailable",
872
+ children: messages.targetUnavailable
873
+ }) : connection.targets.map((target) => /* @__PURE__ */ jsx(AttachAction, {
874
+ connection,
875
+ controller,
876
+ label: messages.attachTarget(targetLabel(target)),
877
+ target
878
+ }, `${target.accountId}:${target.zoneId}`))
879
+ })]
880
+ }, connection.connection.id))
881
+ ]
882
+ }) : null,
883
+ hasProviderAccountPath && tokenMethods.length > 0 ? /* @__PURE__ */ jsx("div", {
884
+ "aria-label": messages.authenticationAlternative,
885
+ "data-domainkit-part": "authentication-separator",
886
+ role: "separator",
887
+ children: /* @__PURE__ */ jsx("span", {
888
+ "aria-hidden": "true",
889
+ children: messages.authenticationAlternative
890
+ })
891
+ }) : null,
892
+ tokenMethods.map((method) => /* @__PURE__ */ jsx(TokenAction, {
763
893
  controller,
764
894
  method
765
- }, method._tag) : null)
895
+ }, method._tag))
766
896
  ]
767
897
  }),
768
- /* @__PURE__ */ jsx("div", {
898
+ pending ? null : /* @__PURE__ */ jsx("div", {
769
899
  "data-domainkit-part": "dialog-footer",
770
900
  children: /* @__PURE__ */ jsx(Dialog.Close, {
771
901
  "data-domainkit-part": "dialog-cancel",
@@ -789,10 +919,15 @@ function Flow$2({ domain, ...props }) {
789
919
  controller,
790
920
  kind: state.retry === "after-user-action" ? "after-user-action" : "retry"
791
921
  }) : null,
792
- snapshot === void 0 ? null : /* @__PURE__ */ jsxs(Dialog.Root, { children: [/* @__PURE__ */ jsx(ConnectTrigger, { provider: snapshot.provider }), /* @__PURE__ */ jsx(Dialog$2, {
793
- controller,
794
- snapshot
795
- })] })
922
+ snapshot === void 0 ? null : /* @__PURE__ */ jsxs(Dialog.Root, {
923
+ onOpenChange: (open, eventDetails) => {
924
+ if (!open && (state._tag === "Submitting" || state._tag === "Redirecting")) eventDetails.cancel();
925
+ },
926
+ children: [/* @__PURE__ */ jsx(ConnectTrigger, { provider: snapshot.provider }), /* @__PURE__ */ jsx(Dialog$2, {
927
+ controller,
928
+ snapshot
929
+ })]
930
+ })
796
931
  ]
797
932
  });
798
933
  }
@@ -805,60 +940,89 @@ function RetryAction$1({ controller, kind = "retry", ...props }) {
805
940
  type: "button"
806
941
  });
807
942
  }
808
- const DisconnectState = Data.taggedEnum();
809
- function useDisconnect(connection) {
810
- const { runtime } = useDomainKit();
811
- const controller = useMemo(() => {
812
- const state = Atom.make(DisconnectState.Idle());
813
- return {
814
- disconnect: runtime.fn()((_, get) => {
815
- get.set(state, DisconnectState.Disconnecting());
816
- return Effect.flatMap(Transport.Service, (transport) => transport.connection.removeDomain({
817
- connectionId: connection.connectionId,
818
- domain: connection.domain,
819
- preserveDns: true
820
- })).pipe(Effect.tap((result) => Effect.sync(() => get.set(state, result))), Effect.catch((error) => Effect.sync(() => get.set(state, failureFromError(error)))), Effect.asVoid);
821
- }),
822
- state
823
- };
824
- }, [
825
- connection.connectionId,
826
- connection.domain,
827
- runtime
828
- ]);
829
- const state = useAtomValue(controller.state);
830
- const disconnect = useAtomSet(controller.disconnect);
831
- return {
832
- disconnect: () => disconnect(),
833
- state
834
- };
943
+ function Card$1({ status, ...props }) {
944
+ return usePart("div", props, { status }, {
945
+ "data-domainkit-part": "connected-card",
946
+ "data-state": status
947
+ });
835
948
  }
836
- function DisconnectAction({ connection, ...props }) {
837
- const controller = useDisconnect(connection);
838
- const { messages } = useDomainKit();
839
- const state = controller.state;
840
- return usePart("div", props, { status: state._tag }, {
841
- children: /* @__PURE__ */ jsxs(Fragment, { children: [
842
- state._tag === "Removed" ? /* @__PURE__ */ jsx("p", {
843
- "data-domainkit-part": "flow-outcome",
844
- "data-tone": "success",
845
- children: messages.domainDisconnected
846
- }) : null,
847
- state._tag === "Failure" ? /* @__PURE__ */ jsx("p", {
848
- "data-domainkit-part": "flow-outcome",
849
- "data-tone": "danger",
850
- role: "alert",
851
- children: state.message
852
- }) : null,
853
- state._tag === "Removed" ? null : /* @__PURE__ */ jsx("button", {
854
- "data-domainkit-part": "disconnect-action",
855
- disabled: state._tag === "Disconnecting",
856
- onClick: () => void controller.disconnect(),
857
- type: "button",
858
- children: state._tag === "Disconnecting" ? messages.disconnecting : messages.disconnectDomain
859
- })
860
- ] }),
861
- "data-domainkit-part": "disconnect"
949
+ function CardIdentity({ status, ...props }) {
950
+ return usePart("div", props, { status }, {
951
+ "data-domainkit-part": "connected-identity",
952
+ "data-state": status
953
+ });
954
+ }
955
+ function CardActions({ status, ...props }) {
956
+ return usePart("div", props, { status }, {
957
+ "data-domainkit-part": "connected-actions",
958
+ "data-state": status
959
+ });
960
+ }
961
+ function DisconnectDialog({ connection, controller, onOpenChange, open: openProp, trigger }) {
962
+ const { colorScheme, messages, portalContainer, themeStyle } = useDomainKit();
963
+ const [internalOpen, setInternalOpen] = useState(false);
964
+ const open = openProp ?? internalOpen;
965
+ const setOpen = (nextOpen) => {
966
+ if (openProp === void 0) setInternalOpen(nextOpen);
967
+ onOpenChange?.(nextOpen);
968
+ };
969
+ const detaching = controller.state._tag === "Detaching";
970
+ return /* @__PURE__ */ jsxs(AlertDialog.Root, {
971
+ onOpenChange: (nextOpen, eventDetails) => {
972
+ if (!nextOpen && detaching) {
973
+ eventDetails.cancel();
974
+ return;
975
+ }
976
+ setOpen(nextOpen);
977
+ },
978
+ open,
979
+ children: [trigger === null ? null : /* @__PURE__ */ jsx(AlertDialog.Trigger, {
980
+ "data-domainkit-part": "disconnect-trigger",
981
+ ...trigger === void 0 ? {} : { render: trigger },
982
+ children: messages.disconnectDomain
983
+ }), /* @__PURE__ */ jsxs(AlertDialog.Portal, {
984
+ container: portalContainer,
985
+ children: [/* @__PURE__ */ jsx(AlertDialog.Backdrop, {
986
+ "data-color-scheme": colorScheme,
987
+ "data-domainkit-part": "dialog-backdrop",
988
+ "data-domainkit-root": "",
989
+ style: themeStyle
990
+ }), /* @__PURE__ */ jsxs(AlertDialog.Popup, {
991
+ "data-color-scheme": colorScheme,
992
+ "data-domainkit-part": "disconnect-dialog",
993
+ "data-domainkit-root": "",
994
+ style: themeStyle,
995
+ children: [/* @__PURE__ */ jsxs("div", {
996
+ "data-domainkit-part": "dialog-header",
997
+ children: [/* @__PURE__ */ jsxs("div", {
998
+ "data-domainkit-part": "dialog-heading",
999
+ children: [/* @__PURE__ */ jsx(AlertDialog.Title, {
1000
+ "data-domainkit-part": "dialog-title",
1001
+ children: messages.disconnectTitle(connection.provider.name)
1002
+ }), /* @__PURE__ */ jsx(AlertDialog.Description, {
1003
+ "data-domainkit-part": "dialog-description",
1004
+ children: messages.disconnectConsent
1005
+ })]
1006
+ }), detaching ? null : /* @__PURE__ */ jsx(AlertDialog.Close, {
1007
+ "aria-label": messages.close,
1008
+ "data-domainkit-part": "dialog-close",
1009
+ children: "×"
1010
+ })]
1011
+ }), /* @__PURE__ */ jsxs("div", {
1012
+ "data-domainkit-part": "dialog-footer",
1013
+ children: [detaching ? null : /* @__PURE__ */ jsx(AlertDialog.Close, {
1014
+ "data-domainkit-part": "dialog-cancel",
1015
+ children: messages.cancel
1016
+ }), /* @__PURE__ */ jsx("button", {
1017
+ "data-domainkit-part": "disconnect-action",
1018
+ disabled: detaching,
1019
+ onClick: () => controller.detach(),
1020
+ type: "button",
1021
+ children: detaching ? messages.detaching : messages.disconnectDomain
1022
+ })]
1023
+ })]
1024
+ })]
1025
+ })]
862
1026
  });
863
1027
  }
864
1028
  //#endregion
@@ -1128,7 +1292,7 @@ var provisioning_exports = /* @__PURE__ */ __exportAll({
1128
1292
  const State$1 = Data.taggedEnum();
1129
1293
  const Command$1 = Data.taggedEnum();
1130
1294
  function useModel$1(connection, records, onApplied) {
1131
- const { runtime } = useDomainKit();
1295
+ const { emit, runtime } = useDomainKit();
1132
1296
  const recordKey = recordsIdentity(records);
1133
1297
  const onAppliedRef = useRef(onApplied);
1134
1298
  onAppliedRef.current = onApplied;
@@ -1136,32 +1300,42 @@ function useModel$1(connection, records, onApplied) {
1136
1300
  const state = Atom.make(State$1.Idle());
1137
1301
  return {
1138
1302
  command: runtime.fn()((command, get) => {
1303
+ const current = get(state);
1139
1304
  if (command._tag === "Dismiss") {
1305
+ if (current._tag === "Planning" || current._tag === "Applying") return Effect.void;
1140
1306
  get.set(state, State$1.Idle());
1141
1307
  return Effect.void;
1142
1308
  }
1143
1309
  if (command._tag === "Plan") {
1310
+ if (current._tag === "Planning" || current._tag === "Applying") return Effect.void;
1144
1311
  get.set(state, State$1.Planning());
1145
1312
  return Effect.flatMap(Transport.Service, (transport) => transport.provisioning.plan({
1146
- connectionId: connection.connectionId,
1147
- domain: connection.domain,
1313
+ attachmentId: connection.attachment.id,
1314
+ domain: connection.attachment.domain,
1148
1315
  records
1149
1316
  })).pipe(Effect.tap((plan) => Effect.sync(() => get.set(state, State$1.Review({ plan })))), Effect.catch((error) => Effect.sync(() => get.set(state, failureFromError(error)))), Effect.asVoid);
1150
1317
  }
1151
- const current = get(state);
1152
1318
  if (current._tag !== "Review" || current.plan.operations.some((operation) => operation._tag === "Conflict")) return Effect.void;
1153
1319
  const plan = current.plan;
1154
1320
  get.set(state, State$1.Applying({ plan }));
1155
1321
  return Effect.flatMap(Transport.Service, (transport) => transport.provisioning.apply({
1156
- connectionId: connection.connectionId,
1157
- domain: connection.domain,
1322
+ attachmentId: connection.attachment.id,
1323
+ domain: connection.attachment.domain,
1158
1324
  planDigest: plan.digest
1159
1325
  })).pipe(Effect.tap((result) => Effect.sync(() => {
1160
1326
  if (result._tag === "Applied") {
1161
1327
  get.set(state, State$1.Complete({ result }));
1328
+ emit(Event.RecordsApplied({
1329
+ connection,
1330
+ result
1331
+ }));
1162
1332
  onAppliedRef.current?.(result);
1163
1333
  } else if (result._tag === "Partial") {
1164
1334
  get.set(state, State$1.Partial({ result }));
1335
+ emit(Event.RecordsPartiallyApplied({
1336
+ connection,
1337
+ result
1338
+ }));
1165
1339
  onAppliedRef.current?.(result);
1166
1340
  } else get.set(state, result);
1167
1341
  })), Effect.catch((error) => Effect.sync(() => get.set(state, failureFromError(error)))), Effect.asVoid);
@@ -1169,8 +1343,9 @@ function useModel$1(connection, records, onApplied) {
1169
1343
  state
1170
1344
  };
1171
1345
  }, [
1172
- connection.connectionId,
1173
- connection.domain,
1346
+ connection.attachment.domain,
1347
+ connection.attachment.id,
1348
+ emit,
1174
1349
  recordKey,
1175
1350
  runtime
1176
1351
  ]);
@@ -1195,11 +1370,6 @@ function Root$1({ status, ...props }) {
1195
1370
  }
1196
1371
  function Outcome$1({ state }) {
1197
1372
  const { messages } = useDomainKit();
1198
- if (state._tag === "Complete") return /* @__PURE__ */ jsx("p", {
1199
- "data-domainkit-part": "flow-outcome",
1200
- "data-tone": "success",
1201
- children: messages.recordsApplied
1202
- });
1203
1373
  if (state._tag === "Partial" || state._tag === "Failure" || state._tag === "Stale") return /* @__PURE__ */ jsx("p", {
1204
1374
  "data-domainkit-part": "flow-outcome",
1205
1375
  "data-tone": "danger",
@@ -1208,23 +1378,38 @@ function Outcome$1({ state }) {
1208
1378
  });
1209
1379
  return null;
1210
1380
  }
1211
- function Dialog$1({ controller }) {
1381
+ function Dialog$1({ controller, trigger }) {
1212
1382
  const { colorScheme, messages, portalContainer, themeStyle } = useDomainKit();
1213
1383
  const state = controller.state;
1214
- const plan = state._tag === "Review" || state._tag === "Applying" ? state.plan : void 0;
1384
+ const planRef = useRef(void 0);
1385
+ if (state._tag === "Idle") planRef.current = void 0;
1386
+ if (state._tag === "Review" || state._tag === "Applying") planRef.current = state.plan;
1387
+ const plan = planRef.current;
1388
+ const busy = state._tag === "Planning" || state._tag === "Applying";
1389
+ const open = state._tag !== "Idle" && state._tag !== "Complete";
1215
1390
  return /* @__PURE__ */ jsxs(Dialog.Root, {
1216
- onOpenChange: (open) => {
1217
- if (!open) controller.dismiss();
1391
+ onOpenChange: (nextOpen, eventDetails) => {
1392
+ if (!nextOpen && busy) {
1393
+ eventDetails.cancel();
1394
+ return;
1395
+ }
1396
+ if (!nextOpen) controller.dismiss();
1218
1397
  },
1219
- open: plan !== void 0,
1398
+ open,
1220
1399
  children: [/* @__PURE__ */ jsx(Dialog.Trigger, {
1221
1400
  "data-domainkit-part": "plan-trigger",
1222
- disabled: state._tag === "Planning" || state._tag === "Applying",
1401
+ disabled: state._tag !== "Idle",
1223
1402
  onClick: () => controller.plan(),
1224
- children: state._tag === "Planning" ? messages.planningDns : messages.reviewDns
1225
- }), plan === void 0 ? null : /* @__PURE__ */ jsxs(Dialog.Portal, {
1226
- container: portalContainer ?? void 0,
1227
- children: [/* @__PURE__ */ jsx(Dialog.Backdrop, { "data-domainkit-part": "dialog-backdrop" }), /* @__PURE__ */ jsxs(Dialog.Popup, {
1403
+ ...trigger === void 0 ? {} : { render: trigger },
1404
+ children: messages.reviewDns
1405
+ }), /* @__PURE__ */ jsxs(Dialog.Portal, {
1406
+ container: portalContainer,
1407
+ children: [/* @__PURE__ */ jsx(Dialog.Backdrop, {
1408
+ "data-color-scheme": colorScheme,
1409
+ "data-domainkit-part": "dialog-backdrop",
1410
+ "data-domainkit-root": "",
1411
+ style: themeStyle
1412
+ }), /* @__PURE__ */ jsxs(Dialog.Popup, {
1228
1413
  "data-color-scheme": colorScheme,
1229
1414
  "data-domainkit-part": "plan-dialog",
1230
1415
  "data-domainkit-root": "",
@@ -1241,31 +1426,48 @@ function Dialog$1({ controller }) {
1241
1426
  "data-domainkit-part": "dialog-description",
1242
1427
  children: messages.planConsent
1243
1428
  })]
1244
- }), /* @__PURE__ */ jsx(Dialog.Close, {
1429
+ }), busy ? null : /* @__PURE__ */ jsx(Dialog.Close, {
1245
1430
  "aria-label": messages.close,
1246
1431
  "data-domainkit-part": "dialog-close",
1247
1432
  children: "×"
1248
1433
  })]
1249
1434
  }),
1250
- /* @__PURE__ */ jsx("div", {
1435
+ /* @__PURE__ */ jsxs("div", {
1251
1436
  "data-domainkit-part": "dialog-body",
1252
- children: /* @__PURE__ */ jsx(List, {
1437
+ children: [plan === void 0 ? /* @__PURE__ */ jsx("p", {
1438
+ "data-domainkit-part": "dialog-progress",
1439
+ role: "status",
1440
+ children: messages.planningDns
1441
+ }) : /* @__PURE__ */ jsx(List, {
1253
1442
  lifecycle: "provisioning",
1254
1443
  operations: plan.operations
1255
- })
1444
+ }), /* @__PURE__ */ jsx(Outcome$1, { state })]
1256
1445
  }),
1257
- /* @__PURE__ */ jsxs("div", {
1446
+ /* @__PURE__ */ jsx("div", {
1258
1447
  "data-domainkit-part": "dialog-footer",
1259
- children: [/* @__PURE__ */ jsx(Dialog.Close, {
1448
+ children: busy ? /* @__PURE__ */ jsx("button", {
1449
+ "data-domainkit-part": "plan-apply",
1450
+ disabled: true,
1451
+ type: "button",
1452
+ children: state._tag === "Applying" ? messages.applyingDns : messages.planningDns
1453
+ }) : state._tag === "Review" && plan !== void 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Dialog.Close, {
1260
1454
  "data-domainkit-part": "dialog-cancel",
1261
1455
  children: messages.cancel
1262
1456
  }), /* @__PURE__ */ jsx("button", {
1263
1457
  "data-domainkit-part": "plan-apply",
1264
- disabled: state._tag === "Applying" || plan.operations.some((operation) => operation._tag === "Conflict"),
1458
+ disabled: plan.operations.some((operation) => operation._tag === "Conflict"),
1265
1459
  onClick: () => controller.apply(),
1266
1460
  type: "button",
1267
- children: state._tag === "Applying" ? messages.applyingDns : messages.applyDns
1268
- })]
1461
+ children: messages.applyDns
1462
+ })] }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Dialog.Close, {
1463
+ "data-domainkit-part": "dialog-cancel",
1464
+ children: messages.close
1465
+ }), /* @__PURE__ */ jsx("button", {
1466
+ "data-domainkit-part": "provisioning-retry",
1467
+ onClick: () => controller.retry(),
1468
+ type: "button",
1469
+ children: messages.retry
1470
+ })] })
1269
1471
  })
1270
1472
  ]
1271
1473
  })]
@@ -1283,18 +1485,16 @@ function RetryAction({ controller }) {
1283
1485
  children: messages.retry
1284
1486
  });
1285
1487
  }
1286
- function Flow$1({ connection, onApplied, records, showRecords = true, ...props }) {
1488
+ function Flow$1({ connection, onApplied, records, showRecords = true, trigger, ...props }) {
1287
1489
  const controller = useController$1(connection, records, onApplied);
1288
1490
  const state = controller.state;
1289
1491
  return /* @__PURE__ */ jsxs(Root$1, {
1290
1492
  status: state._tag,
1291
1493
  ...props,
1292
- children: [
1293
- showRecords ? /* @__PURE__ */ jsx(Table, { records }) : null,
1294
- /* @__PURE__ */ jsx(Outcome$1, { state }),
1295
- /* @__PURE__ */ jsx(Dialog$1, { controller }),
1296
- /* @__PURE__ */ jsx(RetryAction, { controller })
1297
- ]
1494
+ children: [showRecords ? /* @__PURE__ */ jsx(Table, { records }) : null, /* @__PURE__ */ jsx(Dialog$1, {
1495
+ controller,
1496
+ ...trigger === void 0 ? {} : { trigger }
1497
+ })]
1298
1498
  });
1299
1499
  }
1300
1500
  //#endregion
@@ -1325,21 +1525,27 @@ function useModel(config) {
1325
1525
  get.set(state, State.Idle());
1326
1526
  return Effect.void;
1327
1527
  }
1328
- get.set(state, State.Observing());
1528
+ const current = get(state);
1529
+ if (current._tag === "Observing") return Effect.void;
1530
+ const previous = current._tag === "Observation" ? current : current._tag === "Failed" ? current.previous : void 0;
1531
+ get.set(state, State.Observing({ ...previous === void 0 ? {} : { previous } }));
1329
1532
  return Effect.flatMap(Transport.Service, (transport) => transport.verification.observe({
1330
- ...config.connection === void 0 ? {} : { connectionId: config.connection.connectionId },
1533
+ ...config.connection === void 0 ? {} : { attachmentId: config.connection.attachment.id },
1331
1534
  domain: config.domain,
1332
1535
  records: config.records,
1333
1536
  sources: {
1334
1537
  provider,
1335
1538
  publicDns
1336
1539
  }
1337
- })).pipe(Effect.tap((observation) => Effect.sync(() => get.set(state, observation))), Effect.catch((error) => Effect.sync(() => get.set(state, failureFromError(error)))), Effect.asVoid);
1540
+ })).pipe(Effect.tap((observation) => Effect.sync(() => get.set(state, observation))), Effect.catch((error) => Effect.sync(() => get.set(state, State.Failed({
1541
+ failure: failureFromError(error),
1542
+ ...previous === void 0 ? {} : { previous }
1543
+ })))), Effect.asVoid);
1338
1544
  }),
1339
1545
  state
1340
1546
  };
1341
1547
  }, [
1342
- config.connection?.connectionId,
1548
+ config.connection?.attachment.id,
1343
1549
  config.domain,
1344
1550
  provider,
1345
1551
  publicDns,
@@ -1386,7 +1592,7 @@ function Evidence({ observation }) {
1386
1592
  "data-domainkit-part": "observation-list",
1387
1593
  children: observationGroups(observation).map((group) => /* @__PURE__ */ jsxs("section", {
1388
1594
  "data-domainkit-part": "observation-group",
1389
- children: [/* @__PURE__ */ jsx("p", {
1595
+ children: [/* @__PURE__ */ jsx("h3", {
1390
1596
  "data-domainkit-part": "observation-source",
1391
1597
  children: group.label
1392
1598
  }), /* @__PURE__ */ jsx("ul", { children: group.evidence.map((evidence, index) => {
@@ -1406,122 +1612,271 @@ function Evidence({ observation }) {
1406
1612
  });
1407
1613
  }
1408
1614
  function Outcome({ state }) {
1409
- if (state._tag !== "Failure") return null;
1615
+ if (state._tag !== "Failed") return null;
1410
1616
  return /* @__PURE__ */ jsx("p", {
1411
1617
  "data-domainkit-part": "flow-outcome",
1412
1618
  "data-tone": "danger",
1413
1619
  role: "alert",
1414
- children: state.message
1620
+ children: state.failure.message
1415
1621
  });
1416
1622
  }
1417
- function ObserveAction({ controller }) {
1623
+ function ObserveAction({ controller, ...props }) {
1418
1624
  const { messages } = useDomainKit();
1419
- return /* @__PURE__ */ jsx("button", {
1625
+ const observing = controller.state._tag === "Observing";
1626
+ return usePart("button", props, { status: controller.state._tag }, {
1627
+ "aria-busy": observing,
1628
+ children: messages.checkDns,
1420
1629
  "data-domainkit-part": "observe-action",
1421
- disabled: controller.state._tag === "Observing",
1630
+ "data-loading": observing ? "" : void 0,
1631
+ disabled: observing,
1422
1632
  onClick: () => controller.observe(),
1423
- type: "button",
1424
- children: controller.state._tag === "Observing" ? messages.checkingDns : messages.checkDns
1633
+ type: "button"
1425
1634
  });
1426
1635
  }
1427
1636
  function Status({ config, ...props }) {
1637
+ const { colorScheme, messages, portalContainer, themeStyle } = useDomainKit();
1428
1638
  const controller = useController(config);
1429
1639
  const state = controller.state;
1430
- return /* @__PURE__ */ jsxs(Root, {
1640
+ const observation = state._tag === "Observation" ? state : state._tag === "Observing" || state._tag === "Failed" ? state.previous : void 0;
1641
+ return /* @__PURE__ */ jsx(Root, {
1431
1642
  status: state._tag,
1432
1643
  ...props,
1433
- children: [
1434
- state._tag === "Observation" ? /* @__PURE__ */ jsx(Evidence, { observation: state }) : null,
1435
- /* @__PURE__ */ jsx(Outcome, { state }),
1436
- /* @__PURE__ */ jsx(ObserveAction, { controller })
1437
- ]
1644
+ children: /* @__PURE__ */ jsxs(Popover.Root, { children: [/* @__PURE__ */ jsx(Popover.Trigger, { render: /* @__PURE__ */ jsx(ObserveAction, { controller }) }), /* @__PURE__ */ jsx(Popover.Portal, {
1645
+ container: portalContainer,
1646
+ children: /* @__PURE__ */ jsx(Popover.Positioner, {
1647
+ align: "end",
1648
+ "data-domainkit-part": "verification-positioner",
1649
+ sideOffset: 6,
1650
+ children: /* @__PURE__ */ jsxs(Popover.Popup, {
1651
+ "aria-label": messages.checkDns,
1652
+ "aria-busy": state._tag === "Observing",
1653
+ "data-color-scheme": colorScheme,
1654
+ "data-domainkit-part": "verification-popover",
1655
+ "data-domainkit-root": "",
1656
+ style: themeStyle,
1657
+ children: [
1658
+ /* @__PURE__ */ jsx(Popover.Arrow, { "data-domainkit-part": "verification-arrow" }),
1659
+ state._tag === "Observing" ? /* @__PURE__ */ jsx("p", {
1660
+ "data-domainkit-part": "verification-progress",
1661
+ role: "status",
1662
+ children: messages.checkingDns
1663
+ }) : null,
1664
+ observation === void 0 ? null : /* @__PURE__ */ jsx(Evidence, { observation }),
1665
+ /* @__PURE__ */ jsx(Outcome, { state })
1666
+ ]
1667
+ })
1668
+ })
1669
+ })] })
1438
1670
  });
1439
1671
  }
1440
1672
  //#endregion
1441
1673
  //#region src/domain.tsx
1442
1674
  var domain_exports = /* @__PURE__ */ __exportAll({ Flow: () => Flow });
1443
1675
  function Flow({ domain, receiptId, records }) {
1444
- const connection = useController$2(domain);
1445
- const state = connection.state;
1676
+ const controller = useController$2(domain);
1677
+ const state = controller.state;
1678
+ const connected = state._tag === "Connected" ? state : state._tag === "Detaching" ? state.snapshot : void 0;
1446
1679
  return /* @__PURE__ */ jsxs(Root$3, {
1447
1680
  status: state._tag,
1448
1681
  children: [
1449
- state._tag === "Connected" ? null : /* @__PURE__ */ jsx(Status$2, { state }),
1450
- state._tag === "Failure" && (state.retry === "safe" || state.retry === "unknown") ? /* @__PURE__ */ jsx(RetryAction$1, { controller: connection }) : null,
1451
- state._tag === "Disconnected" ? /* @__PURE__ */ jsxs(Dialog.Root, { children: [/* @__PURE__ */ jsx(ConnectTrigger, { provider: state.provider }), /* @__PURE__ */ jsx(Dialog$2, {
1452
- controller: connection,
1453
- snapshot: state
1454
- })] }) : null,
1455
- state._tag === "Connected" ? /* @__PURE__ */ jsx("div", {
1456
- "data-domainkit-part": "connected-shell",
1457
- children: /* @__PURE__ */ jsx(ConnectedFlow, {
1458
- connection: state,
1459
- ...receiptId === void 0 ? {} : { initialReceiptId: receiptId },
1460
- records
1461
- })
1462
- }) : null
1682
+ state._tag === "Connected" || state._tag === "Detaching" ? /* @__PURE__ */ jsx(ConnectedCard, {
1683
+ connection: state._tag === "Connected" ? state : state.snapshot,
1684
+ controller,
1685
+ ...receiptId === void 0 ? {} : { initialReceiptId: receiptId },
1686
+ records,
1687
+ status: state._tag
1688
+ }) : /* @__PURE__ */ jsx(AvailableCard, {
1689
+ controller,
1690
+ state
1691
+ }),
1692
+ /* @__PURE__ */ jsx(Table, { records }),
1693
+ /* @__PURE__ */ jsx(Status, { config: {
1694
+ ...connected === void 0 ? {} : { connection: connected },
1695
+ domain,
1696
+ records
1697
+ } })
1463
1698
  ]
1464
1699
  });
1465
1700
  }
1466
- function ConnectedFlow({ connection, initialReceiptId, records }) {
1467
- const [appliedReceipt, setAppliedReceipt] = useState();
1468
- const receiptSource = initialReceiptId ?? null;
1469
- const receiptEpoch = useRef({
1470
- source: receiptSource,
1471
- value: 0
1701
+ function AvailableCard({ controller, state }) {
1702
+ const { messages } = useDomainKit();
1703
+ const snapshot = state._tag === "Disconnected" ? state : state._tag === "Submitting" || state._tag === "Redirecting" ? state.snapshot : void 0;
1704
+ return /* @__PURE__ */ jsxs(Card$1, {
1705
+ status: state._tag,
1706
+ children: [/* @__PURE__ */ jsxs(CardIdentity, {
1707
+ status: state._tag,
1708
+ children: [snapshot === void 0 ? null : /* @__PURE__ */ jsx(Mark, { provider: snapshot.provider }), /* @__PURE__ */ jsx(Status$2, { state })]
1709
+ }), /* @__PURE__ */ jsxs(CardActions, {
1710
+ status: state._tag,
1711
+ children: [state._tag === "Failure" && state.retry !== "never" ? /* @__PURE__ */ jsx(RetryAction$1, {
1712
+ controller,
1713
+ kind: state.retry === "after-user-action" ? "after-user-action" : "retry"
1714
+ }) : null, snapshot === void 0 ? null : /* @__PURE__ */ jsxs(Dialog.Root, {
1715
+ onOpenChange: (open, eventDetails) => {
1716
+ if (!open && (state._tag === "Submitting" || state._tag === "Redirecting")) eventDetails.cancel();
1717
+ },
1718
+ children: [/* @__PURE__ */ jsx(ConnectTrigger, {
1719
+ disabled: state._tag === "Submitting" || state._tag === "Redirecting",
1720
+ provider: snapshot.provider,
1721
+ children: messages.connectProvider(snapshot.provider.name)
1722
+ }), /* @__PURE__ */ jsx(Dialog$2, {
1723
+ controller,
1724
+ snapshot
1725
+ })]
1726
+ })]
1727
+ })]
1472
1728
  });
1473
- const epoch = receiptEpoch.current.source === receiptSource ? receiptEpoch.current.value : receiptEpoch.current.value + 1;
1474
- useEffect(() => {
1475
- if (receiptEpoch.current.source === receiptSource) return;
1476
- receiptEpoch.current = {
1477
- source: receiptSource,
1478
- value: receiptEpoch.current.value + 1
1479
- };
1480
- setAppliedReceipt(void 0);
1481
- }, [receiptSource]);
1482
- const receiptId = appliedReceipt !== void 0 && appliedReceipt.epoch === epoch ? appliedReceipt.receiptId : initialReceiptId;
1483
- const rememberReceipt = (result) => {
1484
- setAppliedReceipt({
1485
- epoch,
1486
- receiptId: result.receiptId
1487
- });
1488
- };
1729
+ }
1730
+ function ConnectedCard({ connection, controller, initialReceiptId, records, status }) {
1731
+ const [receipt, setReceipt] = useState({
1732
+ host: initialReceiptId,
1733
+ value: initialReceiptId
1734
+ });
1735
+ if (receipt.host !== initialReceiptId) setReceipt({
1736
+ host: initialReceiptId,
1737
+ value: initialReceiptId
1738
+ });
1739
+ const receiptId = receipt.host === initialReceiptId ? receipt.value : initialReceiptId;
1740
+ const disabled = status === "Detaching";
1741
+ const actionsTriggerRef = useRef(null);
1742
+ return /* @__PURE__ */ jsxs(Card$1, {
1743
+ status,
1744
+ children: [/* @__PURE__ */ jsxs(CardIdentity, {
1745
+ status,
1746
+ children: [/* @__PURE__ */ jsx(Mark, { provider: connection.provider }), /* @__PURE__ */ jsx(Status$2, { state: status === "Connected" ? connection : controller.state })]
1747
+ }), /* @__PURE__ */ jsxs(CardActions, {
1748
+ status,
1749
+ children: [/* @__PURE__ */ jsx(Flow$1, {
1750
+ connection,
1751
+ onApplied: (result) => setReceipt({
1752
+ host: initialReceiptId,
1753
+ value: result.receiptId
1754
+ }),
1755
+ records,
1756
+ showRecords: false
1757
+ }), receiptId === void 0 ? /* @__PURE__ */ jsx(ProviderActions, {
1758
+ actionsTriggerRef,
1759
+ connection,
1760
+ controller,
1761
+ disabled
1762
+ }) : /* @__PURE__ */ jsx(ProviderActionsWithCleanup, {
1763
+ actionsTriggerRef,
1764
+ connection,
1765
+ controller,
1766
+ disabled,
1767
+ onCleaned: () => setReceipt({
1768
+ host: initialReceiptId,
1769
+ value: void 0
1770
+ }),
1771
+ receiptId
1772
+ }, receiptId)]
1773
+ })]
1774
+ });
1775
+ }
1776
+ function ProviderActionsWithCleanup({ actionsTriggerRef, connection, controller, disabled, onCleaned, receiptId }) {
1777
+ const [cleaned, setCleaned] = useState(false);
1778
+ const cleanup = useController$3(connection, receiptId, (result) => {
1779
+ if (result._tag === "Cleaned") setCleaned(true);
1780
+ });
1781
+ return /* @__PURE__ */ jsx(ProviderActions, {
1782
+ actionsTriggerRef,
1783
+ cleanup,
1784
+ connection,
1785
+ controller,
1786
+ disabled,
1787
+ onCleanupCloseComplete: () => {
1788
+ if (cleaned) onCleaned();
1789
+ }
1790
+ });
1791
+ }
1792
+ function ProviderActions({ actionsTriggerRef: actionsTriggerRefProp, cleanup, connection, controller, disabled, onCleanupCloseComplete }) {
1793
+ const { colorScheme, messages, portalContainer, themeStyle } = useDomainKit();
1794
+ const fallbackActionsTriggerRef = useRef(null);
1795
+ const actionsTriggerRef = actionsTriggerRefProp ?? fallbackActionsTriggerRef;
1796
+ const [menuOpen, setMenuOpen] = useState(false);
1797
+ const [disconnectOpen, setDisconnectOpen] = useState(false);
1489
1798
  return /* @__PURE__ */ jsxs(Fragment, { children: [
1490
- /* @__PURE__ */ jsxs("div", {
1491
- "data-domainkit-part": "connected-card",
1492
- children: [/* @__PURE__ */ jsxs("div", {
1493
- "data-domainkit-part": "connected-identity",
1494
- children: [/* @__PURE__ */ jsx(Mark, { provider: connection.provider }), /* @__PURE__ */ jsx(Status$2, { state: connection })]
1495
- }), /* @__PURE__ */ jsxs("div", {
1496
- "data-domainkit-part": "connected-actions",
1497
- children: [
1498
- /* @__PURE__ */ jsx(Flow$1, {
1499
- connection,
1500
- onApplied: rememberReceipt,
1501
- records,
1502
- showRecords: false
1503
- }),
1504
- /* @__PURE__ */ jsx(Status, { config: {
1505
- connection,
1506
- domain: connection.domain,
1507
- records
1508
- } }),
1509
- /* @__PURE__ */ jsx(DisconnectAction, { connection })
1510
- ]
1799
+ /* @__PURE__ */ jsxs(Menu.Root, {
1800
+ disabled,
1801
+ modal: false,
1802
+ onOpenChange: setMenuOpen,
1803
+ open: menuOpen,
1804
+ children: [/* @__PURE__ */ jsx(Menu.Trigger, {
1805
+ "aria-label": messages.moreActions,
1806
+ "data-domainkit-part": "actions-trigger",
1807
+ ref: actionsTriggerRef,
1808
+ children: /* @__PURE__ */ jsx("span", {
1809
+ "aria-hidden": "true",
1810
+ children: "⋯"
1811
+ })
1812
+ }), /* @__PURE__ */ jsx(Menu.Portal, {
1813
+ container: portalContainer,
1814
+ keepMounted: true,
1815
+ children: /* @__PURE__ */ jsx(Menu.Positioner, {
1816
+ align: "end",
1817
+ sideOffset: 6,
1818
+ children: /* @__PURE__ */ jsxs(Menu.Popup, {
1819
+ "data-color-scheme": colorScheme,
1820
+ "data-domainkit-part": "actions-menu",
1821
+ "data-domainkit-root": "",
1822
+ style: themeStyle,
1823
+ children: [cleanup === void 0 ? null : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Menu.Item, {
1824
+ closeOnClick: false,
1825
+ "data-domainkit-part": "actions-item",
1826
+ nativeButton: true,
1827
+ onClickCapture: () => {
1828
+ setMenuOpen(false);
1829
+ queueMicrotask(() => cleanup.plan());
1830
+ },
1831
+ render: /* @__PURE__ */ jsx("button", { type: "button" }),
1832
+ children: messages.reviewCleanup
1833
+ }), /* @__PURE__ */ jsx(Menu.Separator, { "data-domainkit-part": "actions-separator" })] }), /* @__PURE__ */ jsx(Menu.Item, {
1834
+ closeOnClick: false,
1835
+ "data-domainkit-part": "actions-item",
1836
+ "data-tone": "danger",
1837
+ nativeButton: true,
1838
+ onClickCapture: () => {
1839
+ setMenuOpen(false);
1840
+ queueMicrotask(() => setDisconnectOpen(true));
1841
+ },
1842
+ render: /* @__PURE__ */ jsx("button", { type: "button" }),
1843
+ children: messages.disconnectDomain
1844
+ })]
1845
+ })
1846
+ })
1511
1847
  })]
1512
1848
  }),
1513
- /* @__PURE__ */ jsx(Table, { records }),
1514
- receiptId === void 0 ? null : /* @__PURE__ */ jsx(Flow$3, {
1849
+ cleanup === void 0 ? null : /* @__PURE__ */ jsx(Dialog$3, {
1850
+ controller: cleanup,
1851
+ onOpenChangeComplete: (open) => {
1852
+ if (!open) {
1853
+ onCleanupCloseComplete?.();
1854
+ queueMicrotask(() => actionsTriggerRef.current?.focus());
1855
+ }
1856
+ },
1857
+ trigger: null
1858
+ }),
1859
+ /* @__PURE__ */ jsx(DisconnectDialog, {
1515
1860
  connection,
1516
- receiptId
1861
+ controller,
1862
+ onOpenChange: (open) => {
1863
+ setDisconnectOpen(open);
1864
+ if (!open) queueMicrotask(() => actionsTriggerRef.current?.focus());
1865
+ },
1866
+ open: disconnectOpen,
1867
+ trigger: null
1517
1868
  })
1518
1869
  ] });
1519
1870
  }
1520
1871
  //#endregion
1521
1872
  //#region src/testing.ts
1522
1873
  var testing_exports = /* @__PURE__ */ __exportAll({
1874
+ attachment: () => attachment,
1875
+ connected: () => connected,
1876
+ connection: () => connection,
1523
1877
  makeFakeTransport: () => makeFakeTransport,
1524
- provider: () => provider
1878
+ provider: () => provider,
1879
+ target: () => target
1525
1880
  });
1526
1881
  const provider = (overrides = {}) => ({
1527
1882
  authentication: [{
@@ -1536,17 +1891,48 @@ const provider = (overrides = {}) => ({
1536
1891
  name: "Cloudflare",
1537
1892
  ...overrides
1538
1893
  });
1894
+ const target = (overrides = {}) => ({
1895
+ accountId: "account-1",
1896
+ accountKind: "account",
1897
+ zoneId: "zone-1",
1898
+ zoneName: DomainName.parse("example.com"),
1899
+ ...overrides
1900
+ });
1901
+ const connection = (overrides = {}) => ({
1902
+ createdAt: /* @__PURE__ */ new Date("2026-08-30T00:00:00.000Z"),
1903
+ id: "connection-1",
1904
+ method: "oauth2",
1905
+ ownerId: "organization-1",
1906
+ providerId: "cloudflare",
1907
+ status: "active",
1908
+ ...overrides
1909
+ });
1910
+ const attachment = (overrides = {}) => ({
1911
+ connectionId: "connection-1",
1912
+ createdAt: /* @__PURE__ */ new Date("2026-08-30T00:00:00.000Z"),
1913
+ domain: DomainName.parse("mail.example.com"),
1914
+ id: "attachment-1",
1915
+ target: target(),
1916
+ ...overrides
1917
+ });
1918
+ const connected = (overrides = {}) => ({
1919
+ _tag: "Connected",
1920
+ attachment: attachment(),
1921
+ connection: connection(),
1922
+ provider: provider(),
1923
+ ...overrides
1924
+ });
1539
1925
  function makeFakeTransport(options) {
1540
1926
  const calls = {
1541
1927
  apply: [],
1928
+ attach: [],
1542
1929
  cleanupApply: [],
1543
1930
  cleanupPlan: [],
1544
1931
  connect: [],
1932
+ detach: [],
1545
1933
  inspect: [],
1546
1934
  observe: [],
1547
- plan: [],
1548
- removeDomain: [],
1549
- reuse: []
1935
+ plan: []
1550
1936
  };
1551
1937
  const inspections = Array.isArray(options.inspect) ? [...options.inspect] : [options.inspect];
1552
1938
  const transport = {
@@ -1574,8 +1960,8 @@ function makeFakeTransport(options) {
1574
1960
  calls.connect.push(input);
1575
1961
  return options.connect ?? {
1576
1962
  _tag: "Connected",
1577
- connectionId: "connection-1",
1578
- domain: input.domain,
1963
+ attachment: attachment({ domain: DomainName.parse(input.domain) }),
1964
+ connection: connection(),
1579
1965
  provider: provider()
1580
1966
  };
1581
1967
  },
@@ -1590,22 +1976,26 @@ function makeFakeTransport(options) {
1590
1976
  });
1591
1977
  return snapshot;
1592
1978
  },
1593
- reuse: async (input) => {
1594
- calls.reuse.push(input);
1595
- return options.reuse ?? {
1979
+ attach: async (input) => {
1980
+ calls.attach.push(input);
1981
+ return options.attach ?? {
1596
1982
  _tag: "Connected",
1597
- connectionId: input.connectionId,
1598
- domain: input.domain,
1983
+ attachment: attachment({
1984
+ connectionId: input.connectionId,
1985
+ domain: DomainName.parse(input.domain),
1986
+ target: input.target
1987
+ }),
1988
+ connection: connection({ id: input.connectionId }),
1599
1989
  provider: provider()
1600
1990
  };
1601
1991
  },
1602
- removeDomain: async (input) => {
1603
- calls.removeDomain.push(input);
1604
- return options.removeDomain ?? {
1605
- _tag: "Removed",
1606
- connectionId: input.connectionId,
1607
- domain: input.domain,
1608
- remainingDomainCount: 0
1992
+ detach: async (input) => {
1993
+ calls.detach.push(input);
1994
+ return options.detach ?? {
1995
+ _tag: "Detached",
1996
+ attachment: attachment(),
1997
+ connection: connection(),
1998
+ remainingAttachments: 0
1609
1999
  };
1610
2000
  }
1611
2001
  },
@@ -1651,6 +2041,6 @@ function makeFakeTransport(options) {
1651
2041
  return Object.assign(Transport.layerFromAsync(transport), transport, { calls });
1652
2042
  }
1653
2043
  //#endregion
1654
- export { cleanup_exports as Cleanup, connection_exports as Connection, domain_exports as Domain, domain_kit_exports as DomainKit, messages_exports as Messages, operations_exports as Operations, provider_exports as Provider, provisioning_exports as Provisioning, records_exports as Records, testing_exports as Testing, theme_exports as Theme, verification_exports as Verification };
2044
+ export { cleanup_exports as Cleanup, connection_exports as Connection, domain_exports as Domain, domain_kit_exports as DomainKit, lifecycle_exports as Lifecycle, messages_exports as Messages, operations_exports as Operations, provider_exports as Provider, provisioning_exports as Provisioning, records_exports as Records, testing_exports as Testing, theme_exports as Theme, verification_exports as Verification };
1655
2045
 
1656
2046
  //# sourceMappingURL=index.mjs.map