@domainkit/react 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -58,12 +58,33 @@ trigger, and the dialog behind that trigger is narrowed to that provider, with t
58
58
  disclosure.
59
59
 
60
60
  When no registered provider serves the zone there is nothing DomainKit can connect, so the flow
61
- offers nothing. Pass `connect="always"` to offer the all-providers dialog anyway:
61
+ offers nothing. Pass `connect="always"` to offer the all-providers dialog anyway, or `connect="never"`
62
+ for a domain your application has already settled another way. A domain DomainKit holds keeps its
63
+ status and its disconnect whatever the invitation says.
62
64
 
63
65
  ```tsx
64
66
  <Domain.Flow domain="app.example.com" requirements={requirements} connect="always" />
65
67
  ```
66
68
 
69
+ Connecting is the customer saying yes to the records, so the plan opens itself the moment a
70
+ connection lands — after a token connect, and after the customer returns from a provider — and one
71
+ action adds them. `review="manual"` waits for the trigger instead.
72
+
73
+ `onState` reports what DomainKit has to say about the domain, so your own offers can be ordered
74
+ beside it rather than competing with it:
75
+
76
+ ```tsx
77
+ <Domain.Flow
78
+ domain="app.example.com"
79
+ requirements={requirements}
80
+ onState={({ connected, offering, provider }) => setDomainKitOffers(connected || offering)}
81
+ />
82
+ ```
83
+
84
+ Letting a provider go is one dialog: `Disconnect` asks whether to remove the records an apply
85
+ receipt proves DomainKit created, removes them when the option stays checked, then releases the
86
+ connection. `Cleanup.Flow` is still exported for a host that wants removal on its own.
87
+
67
88
  ## Own one piece, keep the rest
68
89
 
69
90
  Every part of the flow is a slot with a default.
@@ -84,7 +105,7 @@ Every part of the flow is a slot with a default.
84
105
  | `connection` | `{ controller, domain, connect }` | `Connect.Card` once connected, `Connect.Prompt` until then |
85
106
  | `records` | `{ records, readiness, controller, domain }` | `Records.Table` |
86
107
  | `verification` | `{ controller, domain }` | `Verify.Status` with per-requirement evidence |
87
- | `actions` | `{ connection, provisioning, cleanup, domain }` | Review changes, Approve, Decline, Remove records |
108
+ | `actions` | `{ connection, provisioning, cleanup, domain }` | Review changes, Approve, Decline |
88
109
 
89
110
  `Domain.Flow` adds no layout container around a slot: its output is a direct child of the flow root,
90
111
  so your own grid can place it without `display: contents`.
package/dist/index.d.mts CHANGED
@@ -553,8 +553,8 @@ interface TitleProps extends PartProps<"div", State$2> {}
553
553
  /** The heading from the catalog. Children win, for a host that words it itself. */
554
554
  declare function Title(props: TitleProps): ReactElement;
555
555
  interface DescriptionProps extends PartProps<"p", State$2> {}
556
- /** What to do about it, from the catalog. */
557
- declare function Description(props: DescriptionProps): ReactElement;
556
+ /** What to do about it, from the catalog. An outcome whose title says everything renders none. */
557
+ declare function Description(props: DescriptionProps): ReactElement | null;
558
558
  interface ContentProps extends PartProps<"div", State$2> {}
559
559
  /**
560
560
  * The actions under the header. Without children it is the flow's retry, and a flow that allows
@@ -642,7 +642,7 @@ interface MarkProps extends PartProps<"span", MarkState> {
642
642
  */
643
643
  declare function Mark({ provider, ...props }: MarkProps): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
644
644
  declare namespace connect_d_exports {
645
- export { Attempt, Candidate, Card$1 as Card, CardProps$1 as CardProps, ConnectInput, Controller$1 as Controller, Descriptor, Dialog$1 as Dialog, DialogProps$1 as DialogProps, Discovery, Field, Flow$2 as Flow, FlowProps$2 as FlowProps, Form, FormProps, Invitation, MethodDescriptor, Options$2 as Options, Outcome$3 as Outcome, OutcomeProps$2 as OutcomeProps, Prompt, PromptProps, Root$5 as Root, RootProps$5 as RootProps, RootState$3 as RootState, Snapshot, State$1 as State, Status$3 as Status, StatusProps$3 as StatusProps, answeredInPlace, hostProvider, useController$2 as useController };
645
+ export { Attempt, Candidate, Card$1 as Card, CardProps$1 as CardProps, ConnectInput, Controller$1 as Controller, Descriptor, Dialog$1 as Dialog, DialogProps$1 as DialogProps, DisconnectDialog, DisconnectDialogProps, Discovery, Field, Flow$2 as Flow, FlowProps$2 as FlowProps, Form, FormProps, Invitation, MethodDescriptor, Options$2 as Options, Outcome$3 as Outcome, OutcomeProps$2 as OutcomeProps, Prompt, PromptProps, Root$5 as Root, RootProps$5 as RootProps, RootState$3 as RootState, Snapshot, State$1 as State, Status$3 as Status, StatusProps$3 as StatusProps, answeredInPlace, holdsConnection, hostProvider, offering, useController$2 as useController };
646
646
  }
647
647
  type Snapshot = Transport.Snapshot;
648
648
  type Discovery = Transport.Discovery;
@@ -671,6 +671,7 @@ type State$1 = Data.TaggedEnum<{
671
671
  };
672
672
  Submitting: {
673
673
  readonly snapshot: Snapshot | null;
674
+ readonly discovery: Discovery | null;
674
675
  };
675
676
  Redirecting: {
676
677
  readonly url: string;
@@ -733,6 +734,7 @@ declare const State$1: {
733
734
  }> | Extract<{
734
735
  readonly _tag: "Submitting";
735
736
  readonly snapshot: Snapshot | null;
737
+ readonly discovery: Discovery | null;
736
738
  }, {
737
739
  readonly _tag: Tag;
738
740
  }>;
@@ -767,6 +769,7 @@ declare const State$1: {
767
769
  } | {
768
770
  readonly _tag: "Submitting";
769
771
  readonly snapshot: Snapshot | null;
772
+ readonly discovery: Discovery | null;
770
773
  }, {
771
774
  readonly _tag: Tag;
772
775
  }>) => any; }>(cases: Cases): (value: {
@@ -799,6 +802,7 @@ declare const State$1: {
799
802
  } | {
800
803
  readonly _tag: "Submitting";
801
804
  readonly snapshot: Snapshot | null;
805
+ readonly discovery: Discovery | null;
802
806
  }) => import("effect/Unify").Unify<ReturnType<Cases["Connected" | "Disconnected" | "Failure" | "Loading" | "Reconnect" | "Redirecting" | "SelectionRequired" | "Submitting"]>>;
803
807
  <Cases extends { readonly [Tag in "Connected" | "Disconnected" | "Failure" | "Loading" | "Reconnect" | "Redirecting" | "SelectionRequired" | "Submitting"]: (args: Extract<{
804
808
  readonly _tag: "Connected";
@@ -830,6 +834,7 @@ declare const State$1: {
830
834
  } | {
831
835
  readonly _tag: "Submitting";
832
836
  readonly snapshot: Snapshot | null;
837
+ readonly discovery: Discovery | null;
833
838
  }, {
834
839
  readonly _tag: Tag;
835
840
  }>) => any; }>(value: {
@@ -862,6 +867,7 @@ declare const State$1: {
862
867
  } | {
863
868
  readonly _tag: "Submitting";
864
869
  readonly snapshot: Snapshot | null;
870
+ readonly discovery: Discovery | null;
865
871
  }, cases: Cases): import("effect/Unify").Unify<ReturnType<Cases["Connected" | "Disconnected" | "Failure" | "Loading" | "Reconnect" | "Redirecting" | "SelectionRequired" | "Submitting"]>>;
866
872
  };
867
873
  readonly Connected: Data.TaggedEnum.ConstructorFrom<{
@@ -901,6 +907,7 @@ declare const State$1: {
901
907
  readonly Submitting: Data.TaggedEnum.ConstructorFrom<{
902
908
  readonly _tag: "Submitting";
903
909
  readonly snapshot: Snapshot | null;
910
+ readonly discovery: Discovery | null;
904
911
  }, "_tag">;
905
912
  };
906
913
  /** Which provider and method the failed command was for, so the form can answer beside it. */
@@ -917,6 +924,13 @@ interface ConnectInput {
917
924
  readonly returnTo?: string;
918
925
  }
919
926
  interface Controller$1 {
927
+ readonly domain: string;
928
+ /**
929
+ * How many times a connection has been established for this domain on this surface: a token
930
+ * connect that landed, or a load that followed this library's own redirect back. It only ever
931
+ * grows, so a flow acts on a change rather than on a state that would fire again every render.
932
+ */
933
+ readonly established: number;
920
934
  readonly state: State$1;
921
935
  readonly snapshot: Snapshot | null;
922
936
  readonly discovery: Discovery | null;
@@ -962,6 +976,12 @@ declare function Root$5({ controller, ...props }: RootProps$5): ReactElement;
962
976
  * A flow rendering its own `Outcome` beside the dialog skips it, so the failure announces once.
963
977
  */
964
978
  declare const answeredInPlace: (controller: Controller$1) => boolean;
979
+ /**
980
+ * Whether the surface should read as connected: it is, or a command it started is in flight over a
981
+ * connection that was. The card and the dialog it opened stay on screen for the length of the
982
+ * command rather than vanishing under the customer half way through it.
983
+ */
984
+ declare const holdsConnection: (controller: Controller$1) => boolean;
965
985
  /**
966
986
  * The provider whose nameservers serve this domain, as the snapshot describes it. Discovery names
967
987
  * it by id; without a descriptor there is nothing to draw, so there is no host.
@@ -981,12 +1001,17 @@ interface StatusProps$3 extends PartProps<"p", RootState$3> {
981
1001
  declare function Status$3({ controller, ...props }: StatusProps$3): ReactElement;
982
1002
  interface FormProps extends PartProps<"div", RootState$3> {
983
1003
  readonly controller: Controller$1;
1004
+ /**
1005
+ * Narrow to this provider whatever discovery found. The dialog's header menu sets it, so a
1006
+ * customer who picks another provider gets the same one-decision surface for it.
1007
+ */
1008
+ readonly provider?: string;
984
1009
  }
985
1010
  /**
986
1011
  * Everything a disconnected domain can do: a connection discovery already found, the connections
987
1012
  * this owner already has, then each provider's declared methods.
988
1013
  */
989
- declare function Form({ controller, ...props }: FormProps): ReactElement;
1014
+ declare function Form({ controller, provider: narrowed, ...props }: FormProps): ReactElement;
990
1015
  interface DialogProps$1 {
991
1016
  readonly controller: Controller$1;
992
1017
  readonly children?: ReactNode;
@@ -1000,13 +1025,26 @@ interface DialogProps$1 {
1000
1025
  }
1001
1026
  /** The connect surface. Without `render` it is a Base UI dialog behind `trigger`. */
1002
1027
  declare function Dialog$1({ children, controller, render, title, trigger }: DialogProps$1): ReactElement;
1003
- /** Whether the flow offers a connect surface when discovery names no host. */
1004
- type Invitation = "always" | "detected";
1028
+ /**
1029
+ * Whether the flow offers a connect surface: only when discovery names a host, always, or never.
1030
+ * `never` is a host that decides for itself, such as a domain already ready without DomainKit; a
1031
+ * connected domain keeps its status and its disconnect either way.
1032
+ */
1033
+ type Invitation = "always" | "detected" | "never";
1034
+ /**
1035
+ * Whether the connect surface has anything to offer for this domain: a provider that serves the
1036
+ * zone, a connection discovery already found, one the owner already holds, or a command already
1037
+ * running. Never while a connection is held, which is what `holdsConnection` answers. `Prompt`
1038
+ * renders on this, and `Domain.Flow` reports it so a host can order its own offers beside
1039
+ * DomainKit's rather than competing with them.
1040
+ */
1041
+ declare const offering: (controller: Controller$1, connect?: Invitation) => boolean;
1005
1042
  interface PromptProps extends PartProps<"div", RootState$3> {
1006
1043
  readonly controller: Controller$1;
1007
1044
  /**
1008
1045
  * `detected` offers nothing when discovery names no host, which is the honest answer for a
1009
- * domain DomainKit cannot write to. `always` offers the all-providers dialog anyway.
1046
+ * domain DomainKit cannot write to. `always` offers the all-providers dialog anyway, and
1047
+ * `never` offers none.
1010
1048
  */
1011
1049
  readonly connect?: Invitation;
1012
1050
  }
@@ -1016,19 +1054,41 @@ interface PromptProps extends PartProps<"div", RootState$3> {
1016
1054
  * renders unless the host application asks for the dialog anyway.
1017
1055
  */
1018
1056
  declare function Prompt({ connect, controller, ...props }: PromptProps): ReactElement | null;
1057
+ interface DisconnectDialogProps {
1058
+ readonly controller: Controller$1;
1059
+ /** The receipt the cleanup this dialog ran produced, for a host that tracks its own state. */
1060
+ readonly onCleaned?: (receipt: Receipt.Model) => void;
1061
+ readonly trigger?: ReactNode;
1062
+ }
1063
+ /**
1064
+ * Letting a provider go, with the records DomainKit added as one decision rather than two.
1065
+ * Confirming removes what the domain's apply receipt proves DomainKit created, then releases the
1066
+ * connection; a plan with nothing in it is not a failure, so it goes straight to the release.
1067
+ *
1068
+ * The cleanup is this dialog's own, so nothing re-reads the snapshot in between: the disconnect
1069
+ * needs the connection the cleanup just used.
1070
+ */
1071
+ declare function DisconnectDialog({ controller, onCleaned, trigger }: DisconnectDialogProps): ReactElement;
1019
1072
  interface CardProps$1 extends PartProps<"div", RootState$3> {
1020
1073
  readonly controller: Controller$1;
1074
+ /** The receipt a cleanup run from the disconnect dialog produced. */
1075
+ readonly onCleaned?: (receipt: Receipt.Model) => void;
1021
1076
  }
1022
- /** A connected domain: which provider holds it, and how to let it go. */
1023
- declare function Card$1({ controller, ...props }: CardProps$1): ReactElement;
1077
+ /**
1078
+ * A connected domain, with the prompt card's anatomy: the provider's mark, the name the customer
1079
+ * knows it by, where the connection stands, and the one action that ends it.
1080
+ */
1081
+ declare function Card$1({ controller, onCleaned, ...props }: CardProps$1): ReactElement;
1024
1082
  interface FlowProps$2 extends Omit<RootProps$5, "controller"> {
1025
1083
  readonly domain: string;
1026
1084
  readonly returnTo?: string | null;
1027
1085
  /** Offer the connect dialog even when discovery names no host. Defaults to `detected`. */
1028
1086
  readonly connect?: Invitation;
1087
+ /** The receipt a cleanup run from the disconnect dialog produced. */
1088
+ readonly onCleaned?: (receipt: Receipt.Model) => void;
1029
1089
  }
1030
1090
  /** Connection on its own: the card once connected, the prompt until then. */
1031
- declare function Flow$2({ connect, domain, returnTo, ...props }: FlowProps$2): ReactElement;
1091
+ declare function Flow$2({ connect, domain, onCleaned, returnTo, ...props }: FlowProps$2): ReactElement;
1032
1092
  declare namespace provision_d_exports {
1033
1093
  export { Actions, ActionsProps, Controller$2 as Controller, Dialog, DialogProps, Flow$1 as Flow, FlowProps$1 as FlowProps, Options$1 as Options, Outcome$2 as Outcome, OutcomeProps$1 as OutcomeProps, Root$4 as Root, RootProps$4 as RootProps, RootState$2 as RootState, State$3 as State, Status$2 as Status, StatusProps$2 as StatusProps, useController$1 as useController };
1034
1094
  }
@@ -1240,7 +1300,7 @@ interface StatusProps$1 extends Omit<RootProps$3, "render"> {
1240
1300
  /** The default verification slot: a button that observes, with the evidence behind it. */
1241
1301
  declare function Status$1({ controller, render, ...props }: StatusProps$1): ReactElement;
1242
1302
  declare namespace domain_d_exports {
1243
- export { ActionsSlotProps, ConnectionSlotProps, Flow, FlowProps, FlowState, RecordsSlotProps, Slots, VerificationSlotProps };
1303
+ export { ActionsSlotProps, ConnectionSlotProps, Flow, FlowProps, FlowState, RecordsSlotProps, Review, Slots, VerificationSlotProps };
1244
1304
  }
1245
1305
  interface RecordsSlotProps {
1246
1306
  readonly records: ReadonlyArray<DnsRecord.Model>;
@@ -1278,9 +1338,31 @@ interface Slots {
1278
1338
  /** Defaults to `Connect.Card` once connected and `Connect.Prompt` until then. */
1279
1339
  readonly connection?: (props: ConnectionSlotProps) => ReactNode;
1280
1340
  }
1341
+ /**
1342
+ * What DomainKit has to say about this domain, on the part's data attributes, in `className` and
1343
+ * `style` callbacks, and through `onState`. A host reads `connected` and `offering` to order its
1344
+ * own offers beside DomainKit's: an offer of its own competes while `offering` is true, and a
1345
+ * domain DomainKit holds needs none.
1346
+ */
1281
1347
  interface FlowState extends Record<string, unknown> {
1282
1348
  readonly connection: State$1["_tag"];
1283
1349
  readonly provisioning: State$3["_tag"];
1350
+ /**
1351
+ * DomainKit holds a connection for this domain, including while a command it started over that
1352
+ * connection is still running. `offering` is never true at the same time.
1353
+ */
1354
+ readonly connected: boolean;
1355
+ /** The connect surface has something to offer, so a host's own offer would compete with it. */
1356
+ readonly offering: boolean;
1357
+ /** Who holds the connection, or whose nameservers serve the zone. */
1358
+ readonly provider: string | null;
1359
+ /**
1360
+ * The apply receipt this domain's latest provisioning attempt produced, which is what cleanup
1361
+ * plans from. A host's own "remove this domain" surface offers cleanup only when there is one.
1362
+ */
1363
+ readonly receiptId: Receipt.ReceiptId | null;
1364
+ /** `receiptId !== null`: DomainKit applied records here and can prove which. */
1365
+ readonly applied: boolean;
1284
1366
  }
1285
1367
  interface FlowProps extends Omit<PartProps<"div", FlowState>, "children"> {
1286
1368
  readonly domain: string;
@@ -1297,16 +1379,26 @@ interface FlowProps extends Omit<PartProps<"div", FlowState>, "children"> {
1297
1379
  readonly readOnly?: boolean;
1298
1380
  /**
1299
1381
  * Offer the connect dialog even when discovery names no host, for a host application that lets
1300
- * a customer connect a provider the domain's nameservers do not point at. Defaults to `detected`.
1382
+ * a customer connect a provider the domain's nameservers do not point at, or `never` for a
1383
+ * domain the host has already settled another way. Defaults to `detected`.
1301
1384
  */
1302
1385
  readonly connect?: Invitation;
1386
+ /** Fires whenever what DomainKit has to say about this domain changes, and once on mount. */
1387
+ readonly onState?: (state: FlowState) => void;
1388
+ /**
1389
+ * `auto` plans and opens the review the moment a connection lands, so connecting and adding the
1390
+ * records is one pass rather than three. `manual` waits for the trigger.
1391
+ */
1392
+ readonly review?: Review;
1303
1393
  }
1394
+ /** Whether the flow opens the plan itself once a connection lands, or waits to be asked. */
1395
+ type Review = "auto" | "manual";
1304
1396
  /**
1305
1397
  * The whole lifecycle for one domain: connect, review, apply, observe, clean up. The flow renders
1306
1398
  * only what the transport declares, and every part of it is a slot with a default. It adds no
1307
1399
  * layout containers of its own, so a host's grid can place the slot output directly.
1308
1400
  */
1309
- declare function Flow({ connect, domain, onApplied, onCleaned, readOnly, requirements, returnTo, slots, ...props }: FlowProps): ReactElement;
1401
+ declare function Flow({ connect, domain, onApplied, onCleaned, onState, readOnly, requirements, returnTo, review, slots, ...props }: FlowProps): ReactElement;
1310
1402
  //#endregion
1311
1403
  //#region src/icons.d.ts
1312
1404
  /**
@@ -1314,6 +1406,8 @@ declare function Flow({ connect, domain, onApplied, onCleaned, readOnly, require
1314
1406
  * an icon prop of its own, so a host swaps the whole set once on `DomainKit.Root`.
1315
1407
  */
1316
1408
  interface Icons {
1409
+ /** Points at what a control opens: the provider menu's own marker. */
1410
+ readonly chevron: ReactNode;
1317
1411
  readonly close: ReactNode;
1318
1412
  readonly copied: ReactNode;
1319
1413
  readonly copy: ReactNode;
@@ -1325,6 +1419,7 @@ interface Icons {
1325
1419
  readonly warning: ReactNode;
1326
1420
  }
1327
1421
  declare const defaultIcons: {
1422
+ readonly chevron: import("react/jsx-runtime").JSX.Element;
1328
1423
  readonly close: import("react/jsx-runtime").JSX.Element;
1329
1424
  readonly copied: import("react/jsx-runtime").JSX.Element;
1330
1425
  readonly copy: import("react/jsx-runtime").JSX.Element;
@@ -1351,7 +1446,11 @@ interface Catalog {
1351
1446
  readonly close: string;
1352
1447
  readonly retry: string;
1353
1448
  readonly approve: string;
1449
+ /** The provisioning dialog's primary action: what it adds, and how many. */
1450
+ readonly addRecords: (count: number) => string;
1354
1451
  readonly decline: string;
1452
+ /** Why the primary action is unavailable: every operation the plan holds is blocked. */
1453
+ readonly everyRecordConflicts: string;
1355
1454
  readonly reviewChanges: string;
1356
1455
  readonly cleanUp: string;
1357
1456
  readonly checkDns: string;
@@ -1361,6 +1460,17 @@ interface Catalog {
1361
1460
  readonly moreOptions: string;
1362
1461
  /** Reveals the providers the dialog narrowed away. */
1363
1462
  readonly useAnotherProvider: string;
1463
+ /** Opens the token form where a provider also offers a method the customer clicks through. */
1464
+ readonly useTokenInstead: string;
1465
+ /** Returns from one method's form to the provider's methods. */
1466
+ readonly back: string;
1467
+ /** The connected card's own line, beside the provider's name. */
1468
+ readonly connected: string;
1469
+ readonly needsReconnect: string;
1470
+ /** The disconnect dialog's scope question, when the connection serves other domains too. */
1471
+ readonly disconnectScope: string;
1472
+ readonly disconnectThisDomain: string;
1473
+ readonly disconnectEveryDomain: (count: number) => string;
1364
1474
  readonly copy: string;
1365
1475
  readonly copied: string;
1366
1476
  readonly copyZone: string;
@@ -1391,7 +1501,18 @@ interface Catalog {
1391
1501
  readonly notConnected: string;
1392
1502
  readonly reconnectRequired: (provider: string) => string;
1393
1503
  readonly disconnectTitle: (provider: string) => string;
1394
- readonly disconnectConsent: string;
1504
+ /**
1505
+ * What disconnecting means, in the present. The default names DomainKit as what stops managing
1506
+ * the records; a host application overrides the key to name itself.
1507
+ */
1508
+ readonly disconnectConsent: (domain: string, provider: string) => string;
1509
+ /**
1510
+ * The option inside the disconnect dialog, over the records it would remove. Only this domain's
1511
+ * apply receipt proves what DomainKit created, so the words say which records go.
1512
+ */
1513
+ readonly disconnectWithCleanup: (count: number) => string;
1514
+ /** What the listed records do when that option is off. */
1515
+ readonly disconnectKeepsRecords: (provider: string) => string;
1395
1516
  readonly detachConsent: string;
1396
1517
  readonly detached: string;
1397
1518
  readonly reusableConnections: string;
@@ -1463,7 +1584,8 @@ interface Catalog {
1463
1584
  /** What an outcome says: a heading the customer reads first, then what to do about it. */
1464
1585
  interface Outcome {
1465
1586
  readonly title: string;
1466
- readonly description: string;
1587
+ /** What to do about it. Absent where the title has already said the whole of it. */
1588
+ readonly description?: string;
1467
1589
  }
1468
1590
  /**
1469
1591
  * What the flow was doing when it failed. The reason alone cannot name the provider a customer