@domainkit/react 0.9.0 → 0.10.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 +48 -7
- package/dist/index.d.mts +206 -45
- package/dist/index.mjs +581 -158
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +200 -15
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
3
3
|
import { DnsRecord, DomainKit, Reason, Receipt } from "domainkit";
|
|
4
4
|
import * as Effect from "effect/Effect";
|
|
5
|
-
import { createContext, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
5
|
+
import { createContext, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
6
6
|
import * as Data from "effect/Data";
|
|
7
7
|
import { Transport } from "domainkit/client";
|
|
8
8
|
import { useRender } from "@base-ui/react/use-render";
|
|
@@ -84,7 +84,12 @@ const defaultIcons = {
|
|
|
84
84
|
cx: "12",
|
|
85
85
|
cy: "12",
|
|
86
86
|
r: "9"
|
|
87
|
-
}), /* @__PURE__ */ jsx("path", { d: "m8.5 12.5 2.5 2.5 4.5-5" })] })
|
|
87
|
+
}), /* @__PURE__ */ jsx("path", { d: "m8.5 12.5 2.5 2.5 4.5-5" })] }),
|
|
88
|
+
warning: /* @__PURE__ */ jsxs(Glyph, { children: [
|
|
89
|
+
/* @__PURE__ */ jsx("path", { d: "M10.3 3.9 2.4 17.5a2 2 0 0 0 1.7 3h15.8a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z" }),
|
|
90
|
+
/* @__PURE__ */ jsx("path", { d: "M12 9v4" }),
|
|
91
|
+
/* @__PURE__ */ jsx("path", { d: "M12 17h.01" })
|
|
92
|
+
] })
|
|
88
93
|
};
|
|
89
94
|
const IconsContext = createContext(defaultIcons);
|
|
90
95
|
function IconsProvider({ children, icons }) {
|
|
@@ -105,7 +110,8 @@ function useIcons() {
|
|
|
105
110
|
var messages_exports = /* @__PURE__ */ __exportAll({
|
|
106
111
|
english: () => english,
|
|
107
112
|
failure: () => failure,
|
|
108
|
-
merge: () => merge
|
|
113
|
+
merge: () => merge,
|
|
114
|
+
outcome: () => outcome
|
|
109
115
|
});
|
|
110
116
|
const entity = {
|
|
111
117
|
approval: "approval",
|
|
@@ -135,6 +141,10 @@ const recordTypes = {
|
|
|
135
141
|
SRV: "SRV",
|
|
136
142
|
TXT: "TXT"
|
|
137
143
|
};
|
|
144
|
+
/** The provider the customer acted on, or a neutral stand-in when the flow does not know it. */
|
|
145
|
+
const named = (provider) => provider ?? "The provider";
|
|
146
|
+
/** The domain the failure is about, or a neutral stand-in. */
|
|
147
|
+
const where = (domain) => domain ?? "this domain";
|
|
138
148
|
/** `accountId` reads as "Account id" until a host names the field itself. */
|
|
139
149
|
const humanize = (name) => {
|
|
140
150
|
const spaced = name.replaceAll(/([a-z0-9])([A-Z])/g, "$1 $2").replaceAll(/[_-]+/g, " ").trim();
|
|
@@ -142,7 +152,9 @@ const humanize = (name) => {
|
|
|
142
152
|
};
|
|
143
153
|
const english = {
|
|
144
154
|
connect: "Connect",
|
|
145
|
-
|
|
155
|
+
methodToken: "Connect with an API token",
|
|
156
|
+
methodOAuth: (provider) => `Continue with ${provider}`,
|
|
157
|
+
methodIntegration: (provider) => `Install the ${provider} integration`,
|
|
146
158
|
disconnect: "Disconnect",
|
|
147
159
|
detach: "Detach domain",
|
|
148
160
|
cancel: "Cancel",
|
|
@@ -155,6 +167,8 @@ const english = {
|
|
|
155
167
|
checkDns: "Check DNS",
|
|
156
168
|
checkAgain: "Check again",
|
|
157
169
|
moreActions: "More actions",
|
|
170
|
+
moreOptions: "Need an account id?",
|
|
171
|
+
useAnotherProvider: "Use a different provider",
|
|
158
172
|
copy: "Copy",
|
|
159
173
|
copied: "Copied",
|
|
160
174
|
copyZone: "Copy zone file",
|
|
@@ -175,7 +189,8 @@ const english = {
|
|
|
175
189
|
cleaning: "Removing records…",
|
|
176
190
|
observing: "Checking DNS…",
|
|
177
191
|
connectTitle: (provider) => `Connect ${provider}`,
|
|
178
|
-
connectAnyTitle: "Connect
|
|
192
|
+
connectAnyTitle: "Connect a DNS provider",
|
|
193
|
+
hostOwnsZone: "Owns DNS for this domain.",
|
|
179
194
|
connectDescription: (domain) => `Authorize DNS changes for ${domain}.`,
|
|
180
195
|
connectedTo: (provider) => `${provider} connected`,
|
|
181
196
|
notConnected: "No DNS provider is connected.",
|
|
@@ -240,9 +255,11 @@ const english = {
|
|
|
240
255
|
recordType: (record) => record._tag === "Opaque" ? record.type : recordTypes[record._tag],
|
|
241
256
|
applied: (receipt) => receipt.status === "complete" ? "DNS records added." : "Some DNS records were not added.",
|
|
242
257
|
cleaned: (receipt) => receipt.status === "complete" ? "DNS records removed." : "Some DNS records were not removed.",
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
258
|
+
partiallyAppliedTitle: "Some DNS changes did not land",
|
|
259
|
+
partiallyApplied: "Review what applied, then retry. Retrying is safe.",
|
|
260
|
+
partiallyCleanedTitle: "Some DNS records are still there",
|
|
261
|
+
partiallyCleaned: "Review what is left, then retry. Retrying is safe.",
|
|
262
|
+
receiptOutcome: (outcome) => {
|
|
246
263
|
switch (outcome._tag) {
|
|
247
264
|
case "Applied": return "Done";
|
|
248
265
|
case "Skipped": return "Skipped";
|
|
@@ -292,49 +309,102 @@ const english = {
|
|
|
292
309
|
nextCheckAt: (at) => `Next check ${at}`,
|
|
293
310
|
hostEvidence: "Other checks",
|
|
294
311
|
noEvidence: "Nothing has been checked yet.",
|
|
295
|
-
invalidInput: (reason) =>
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
312
|
+
invalidInput: (reason) => ({
|
|
313
|
+
description: reason.message,
|
|
314
|
+
title: reason.field === void 0 ? "Check what you entered" : `Check the ${humanize(reason.field).toLowerCase()}`
|
|
315
|
+
}),
|
|
316
|
+
unauthenticated: (_reason, context) => ({
|
|
317
|
+
description: "Check the token can read and edit DNS for this zone, then try again.",
|
|
318
|
+
title: `${named(context.provider)} didn't accept this token`
|
|
319
|
+
}),
|
|
320
|
+
forbidden: (_reason, context) => ({
|
|
321
|
+
description: `The connection needs permission to edit DNS for ${where(context.domain)}.`,
|
|
322
|
+
title: "This connection can't change DNS here"
|
|
323
|
+
}),
|
|
324
|
+
notFound: (reason) => ({
|
|
325
|
+
description: "Reload the page and start this step again.",
|
|
326
|
+
title: `That ${entity[reason.entity]} no longer exists`
|
|
327
|
+
}),
|
|
328
|
+
conflict: (reason) => ({
|
|
329
|
+
description: "Review what the zone already holds, then build a new plan.",
|
|
330
|
+
title: reason.operations.length === 1 ? "A record is in the way" : `${reason.operations.length} records are in the way`
|
|
331
|
+
}),
|
|
332
|
+
stale: () => ({
|
|
333
|
+
description: "Review the new plan before you apply it.",
|
|
334
|
+
title: "The zone changed since you reviewed"
|
|
335
|
+
}),
|
|
336
|
+
expired: (reason) => ({
|
|
337
|
+
description: "Start this step again.",
|
|
338
|
+
title: `That ${expiredEntity[reason.entity]} expired`
|
|
339
|
+
}),
|
|
340
|
+
busy: () => ({
|
|
341
|
+
description: "Wait for it to finish, then try again.",
|
|
342
|
+
title: "Another change is running"
|
|
343
|
+
}),
|
|
344
|
+
providerRejected: (reason, context) => ({
|
|
345
|
+
description: reason.message,
|
|
346
|
+
title: `${named(context.provider ?? reason.provider)} refused the change`
|
|
347
|
+
}),
|
|
348
|
+
providerUnavailable: (reason, context) => ({
|
|
349
|
+
description: "Nothing changed. Try again in a minute.",
|
|
350
|
+
title: `${named(context.provider ?? reason.provider)} isn't responding`
|
|
351
|
+
}),
|
|
352
|
+
providerConflict: (reason, context) => ({
|
|
353
|
+
description: "Remove or rename that record at the provider, then try again.",
|
|
354
|
+
title: `${named(context.provider ?? reason.provider)} already holds a conflicting record`
|
|
355
|
+
}),
|
|
356
|
+
unsupported: (reason, context) => ({
|
|
357
|
+
description: "Nothing changed. This step needs a provider that can do it.",
|
|
358
|
+
title: `${named(context.provider ?? reason.provider)} cannot ${reason.operation}`
|
|
359
|
+
}),
|
|
360
|
+
reconnect: (reason, context) => ({
|
|
361
|
+
description: "The stored credential no longer works. Connect again to continue.",
|
|
362
|
+
title: `Reconnect ${named(context.provider ?? reason.provider)}`
|
|
363
|
+
}),
|
|
364
|
+
storageFailed: () => ({
|
|
365
|
+
description: "Nothing changed. Try again.",
|
|
366
|
+
title: "The change could not be saved"
|
|
367
|
+
}),
|
|
368
|
+
cryptoFailed: () => ({
|
|
369
|
+
description: "Connect the provider again to replace it.",
|
|
370
|
+
title: "A stored credential could not be read"
|
|
371
|
+
}),
|
|
372
|
+
resolverFailed: (reason) => ({
|
|
373
|
+
description: "The lookup failed, which says nothing about your DNS. Try again in a moment.",
|
|
374
|
+
title: `DNS lookups through ${reason.resolver} failed`
|
|
375
|
+
})
|
|
311
376
|
};
|
|
312
377
|
const merge = (overrides = {}) => ({
|
|
313
378
|
...english,
|
|
314
379
|
...overrides
|
|
315
380
|
});
|
|
316
|
-
/** The
|
|
317
|
-
const
|
|
381
|
+
/** The title and description for a failure, chosen by the error's reason. */
|
|
382
|
+
const outcome = (error, catalog, context = {}) => {
|
|
318
383
|
const reason = error.reason;
|
|
319
384
|
switch (reason._tag) {
|
|
320
|
-
case "InvalidInput": return catalog.invalidInput(reason);
|
|
321
|
-
case "Unauthenticated": return catalog.unauthenticated(reason);
|
|
322
|
-
case "Forbidden": return catalog.forbidden(reason);
|
|
323
|
-
case "NotFound": return catalog.notFound(reason);
|
|
324
|
-
case "Conflict": return catalog.conflict(reason);
|
|
325
|
-
case "Stale": return catalog.stale(reason);
|
|
326
|
-
case "Expired": return catalog.expired(reason);
|
|
327
|
-
case "Busy": return catalog.busy(reason);
|
|
328
|
-
case "ProviderRejected": return catalog.providerRejected(reason);
|
|
329
|
-
case "ProviderUnavailable": return catalog.providerUnavailable(reason);
|
|
330
|
-
case "ProviderConflict": return catalog.providerConflict(reason);
|
|
331
|
-
case "Unsupported": return catalog.unsupported(reason);
|
|
332
|
-
case "Reconnect": return catalog.reconnect(reason);
|
|
333
|
-
case "StorageFailed": return catalog.storageFailed(reason);
|
|
334
|
-
case "CryptoFailed": return catalog.cryptoFailed(reason);
|
|
335
|
-
case "ResolverFailed": return catalog.resolverFailed(reason);
|
|
385
|
+
case "InvalidInput": return catalog.invalidInput(reason, context);
|
|
386
|
+
case "Unauthenticated": return catalog.unauthenticated(reason, context);
|
|
387
|
+
case "Forbidden": return catalog.forbidden(reason, context);
|
|
388
|
+
case "NotFound": return catalog.notFound(reason, context);
|
|
389
|
+
case "Conflict": return catalog.conflict(reason, context);
|
|
390
|
+
case "Stale": return catalog.stale(reason, context);
|
|
391
|
+
case "Expired": return catalog.expired(reason, context);
|
|
392
|
+
case "Busy": return catalog.busy(reason, context);
|
|
393
|
+
case "ProviderRejected": return catalog.providerRejected(reason, context);
|
|
394
|
+
case "ProviderUnavailable": return catalog.providerUnavailable(reason, context);
|
|
395
|
+
case "ProviderConflict": return catalog.providerConflict(reason, context);
|
|
396
|
+
case "Unsupported": return catalog.unsupported(reason, context);
|
|
397
|
+
case "Reconnect": return catalog.reconnect(reason, context);
|
|
398
|
+
case "StorageFailed": return catalog.storageFailed(reason, context);
|
|
399
|
+
case "CryptoFailed": return catalog.cryptoFailed(reason, context);
|
|
400
|
+
case "ResolverFailed": return catalog.resolverFailed(reason, context);
|
|
336
401
|
}
|
|
337
402
|
};
|
|
403
|
+
/** The same failure as one sentence, for a host that renders text rather than a card. */
|
|
404
|
+
const failure = (error, catalog, context = {}) => {
|
|
405
|
+
const { description, title } = outcome(error, catalog, context);
|
|
406
|
+
return `${title}. ${description}`;
|
|
407
|
+
};
|
|
338
408
|
//#endregion
|
|
339
409
|
//#region src/theme.ts
|
|
340
410
|
var theme_exports = /* @__PURE__ */ __exportAll({ toStyle: () => toStyle });
|
|
@@ -352,7 +422,8 @@ const variables = {
|
|
|
352
422
|
radius: "--domainkit-radius",
|
|
353
423
|
shadow: "--domainkit-shadow",
|
|
354
424
|
success: "--domainkit-success",
|
|
355
|
-
text: "--domainkit-text"
|
|
425
|
+
text: "--domainkit-text",
|
|
426
|
+
warning: "--domainkit-warning"
|
|
356
427
|
};
|
|
357
428
|
function toStyle(theme = {}) {
|
|
358
429
|
return Object.fromEntries(Object.entries(theme).flatMap(([key, value]) => value === void 0 ? [] : [[variables[key], value]]));
|
|
@@ -361,13 +432,13 @@ function toStyle(theme = {}) {
|
|
|
361
432
|
//#region src/domain-kit.tsx
|
|
362
433
|
var domain_kit_exports = /* @__PURE__ */ __exportAll({
|
|
363
434
|
ReadOnly: () => ReadOnly,
|
|
364
|
-
Root: () => Root$
|
|
435
|
+
Root: () => Root$7,
|
|
365
436
|
useCapabilities: () => useCapabilities,
|
|
366
437
|
useDomainKit: () => useDomainKit,
|
|
367
438
|
useReadOnly: () => useReadOnly,
|
|
368
439
|
useTransport: () => useTransport
|
|
369
440
|
});
|
|
370
|
-
const Context = createContext(null);
|
|
441
|
+
const Context$1 = createContext(null);
|
|
371
442
|
const navigateInBrowser = (url) => {
|
|
372
443
|
if (typeof window === "undefined") throw new Error("DomainKit provider authorization requires a browser or a `navigate` prop");
|
|
373
444
|
window.location.assign(url);
|
|
@@ -417,7 +488,7 @@ const usePortalContainer = (container) => {
|
|
|
417
488
|
const getSnapshot = useCallback(() => resolvePortalContainer(container, ownerDocument), [container, ownerDocument]);
|
|
418
489
|
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
419
490
|
};
|
|
420
|
-
function Root$
|
|
491
|
+
function Root$7({ children, colorScheme = "inherit", icons, marks = {}, messages, navigate = navigateInBrowser, onEvent, portalContainer = null, readOnly = false, revision = 0, theme, transport, ...props }) {
|
|
421
492
|
const { capabilities, transport: stable } = useStableTransport(transport);
|
|
422
493
|
const onEventRef = useRef(onEvent);
|
|
423
494
|
useLayoutEffect(() => {
|
|
@@ -450,7 +521,7 @@ function Root$6({ children, colorScheme = "inherit", icons, marks = {}, messages
|
|
|
450
521
|
"data-domainkit-root": "",
|
|
451
522
|
style: themeStyle
|
|
452
523
|
});
|
|
453
|
-
return /* @__PURE__ */ jsx(Context.Provider, {
|
|
524
|
+
return /* @__PURE__ */ jsx(Context$1.Provider, {
|
|
454
525
|
value,
|
|
455
526
|
children: /* @__PURE__ */ jsx(IconsProvider, {
|
|
456
527
|
...icons === void 0 ? {} : { icons },
|
|
@@ -459,7 +530,7 @@ function Root$6({ children, colorScheme = "inherit", icons, marks = {}, messages
|
|
|
459
530
|
});
|
|
460
531
|
}
|
|
461
532
|
function useDomainKit() {
|
|
462
|
-
const value = useContext(Context);
|
|
533
|
+
const value = useContext(Context$1);
|
|
463
534
|
if (value === null) throw new Error("DomainKit components must be rendered inside DomainKit.Root");
|
|
464
535
|
return value;
|
|
465
536
|
}
|
|
@@ -760,6 +831,155 @@ function useAttempt(options) {
|
|
|
760
831
|
};
|
|
761
832
|
}
|
|
762
833
|
//#endregion
|
|
834
|
+
//#region src/outcome.tsx
|
|
835
|
+
/**
|
|
836
|
+
* What a flow says when a step ends badly: media, title, description, and the action that follows.
|
|
837
|
+
* Every flow's `Outcome` binds its controller to these parts, so a failure reads the same way in
|
|
838
|
+
* the connect dialog, the review dialog, and the verification popover.
|
|
839
|
+
*
|
|
840
|
+
* <Connect.Outcome controller={connection} /> the default composition
|
|
841
|
+
*
|
|
842
|
+
* <Connect.Outcome controller={connection}> a composition of your own
|
|
843
|
+
* <Outcome.Header>
|
|
844
|
+
* <Outcome.Media><MyIcon /></Outcome.Media>
|
|
845
|
+
* <Outcome.Title />
|
|
846
|
+
* <Outcome.Description />
|
|
847
|
+
* </Outcome.Header>
|
|
848
|
+
* <Outcome.Content />
|
|
849
|
+
* </Connect.Outcome>
|
|
850
|
+
*/
|
|
851
|
+
var outcome_exports = /* @__PURE__ */ __exportAll({
|
|
852
|
+
Composition: () => Composition,
|
|
853
|
+
Content: () => Content,
|
|
854
|
+
Description: () => Description,
|
|
855
|
+
Header: () => Header$1,
|
|
856
|
+
Media: () => Media,
|
|
857
|
+
Provider: () => Provider,
|
|
858
|
+
Root: () => Root$6,
|
|
859
|
+
Title: () => Title,
|
|
860
|
+
useOutcome: () => useOutcome
|
|
861
|
+
});
|
|
862
|
+
const fallback = {
|
|
863
|
+
description: "",
|
|
864
|
+
layout: "card",
|
|
865
|
+
retry: null,
|
|
866
|
+
retryPart: "flow-retry",
|
|
867
|
+
title: "",
|
|
868
|
+
tone: "danger"
|
|
869
|
+
};
|
|
870
|
+
const Context = createContext(null);
|
|
871
|
+
/** What the surrounding flow resolved, for a part of your own inside `X.Outcome`. */
|
|
872
|
+
function useOutcome() {
|
|
873
|
+
return useContext(Context) ?? fallback;
|
|
874
|
+
}
|
|
875
|
+
/** Binds a resolved outcome to the parts below it. Flows render this; hosts rarely do. */
|
|
876
|
+
function Provider({ children, value }) {
|
|
877
|
+
return /* @__PURE__ */ jsx(Context.Provider, {
|
|
878
|
+
value,
|
|
879
|
+
children
|
|
880
|
+
});
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* The outcome itself. It announces, so a customer reading with assistive tech hears it. A `tone`
|
|
884
|
+
* or `layout` of its own reaches every part below it, so the glyph and the words agree with the
|
|
885
|
+
* root's own attributes.
|
|
886
|
+
*/
|
|
887
|
+
function Root$6({ layout, tone, ...props }) {
|
|
888
|
+
const value = useOutcome();
|
|
889
|
+
const resolved = {
|
|
890
|
+
...value,
|
|
891
|
+
layout: layout ?? value.layout,
|
|
892
|
+
tone: tone ?? value.tone
|
|
893
|
+
};
|
|
894
|
+
const element = usePart("div", props, {
|
|
895
|
+
layout: resolved.layout,
|
|
896
|
+
tone: resolved.tone
|
|
897
|
+
}, {
|
|
898
|
+
"data-domainkit-part": "outcome",
|
|
899
|
+
"data-layout": resolved.layout,
|
|
900
|
+
"data-tone": resolved.tone,
|
|
901
|
+
role: "alert"
|
|
902
|
+
});
|
|
903
|
+
return /* @__PURE__ */ jsx(Provider, {
|
|
904
|
+
value: resolved,
|
|
905
|
+
children: element
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
/** Media, title, and description together, so the action below them can move on its own. */
|
|
909
|
+
function Header$1(props) {
|
|
910
|
+
const { layout, tone } = useOutcome();
|
|
911
|
+
return usePart("div", props, {
|
|
912
|
+
layout,
|
|
913
|
+
tone
|
|
914
|
+
}, { "data-domainkit-part": "outcome-header" });
|
|
915
|
+
}
|
|
916
|
+
/** The glyph for the tone. Children replace it; `icons` on `DomainKit.Root` replaces the set. */
|
|
917
|
+
function Media({ variant = "icon", ...props }) {
|
|
918
|
+
const { layout, tone } = useOutcome();
|
|
919
|
+
const icons = useIcons();
|
|
920
|
+
const glyph = tone === "success" ? icons.success : tone === "warning" ? icons.warning : icons.failure;
|
|
921
|
+
return usePart("div", props, {
|
|
922
|
+
layout,
|
|
923
|
+
tone
|
|
924
|
+
}, {
|
|
925
|
+
"aria-hidden": "true",
|
|
926
|
+
children: glyph,
|
|
927
|
+
"data-domainkit-part": "outcome-media",
|
|
928
|
+
"data-variant": variant
|
|
929
|
+
});
|
|
930
|
+
}
|
|
931
|
+
/** The heading from the catalog. Children win, for a host that words it itself. */
|
|
932
|
+
function Title(props) {
|
|
933
|
+
const { layout, title, tone } = useOutcome();
|
|
934
|
+
return usePart("div", props, {
|
|
935
|
+
layout,
|
|
936
|
+
tone
|
|
937
|
+
}, {
|
|
938
|
+
children: title,
|
|
939
|
+
"data-domainkit-part": "outcome-title"
|
|
940
|
+
});
|
|
941
|
+
}
|
|
942
|
+
/** What to do about it, from the catalog. */
|
|
943
|
+
function Description(props) {
|
|
944
|
+
const { description, layout, tone } = useOutcome();
|
|
945
|
+
return usePart("p", props, {
|
|
946
|
+
layout,
|
|
947
|
+
tone
|
|
948
|
+
}, {
|
|
949
|
+
children: description,
|
|
950
|
+
"data-domainkit-part": "outcome-description"
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
/**
|
|
954
|
+
* The actions under the header. Without children it is the flow's retry, and a flow that allows
|
|
955
|
+
* none renders nothing: read-only hides every control that would start a write again.
|
|
956
|
+
*/
|
|
957
|
+
function Content(props) {
|
|
958
|
+
const { messages } = useDomainKit();
|
|
959
|
+
const { layout, retry, retryPart, tone } = useOutcome();
|
|
960
|
+
const element = usePart("div", props, {
|
|
961
|
+
layout,
|
|
962
|
+
tone
|
|
963
|
+
}, {
|
|
964
|
+
children: retry === null ? null : /* @__PURE__ */ jsx("button", {
|
|
965
|
+
"data-domainkit-part": retryPart,
|
|
966
|
+
onClick: retry,
|
|
967
|
+
type: "button",
|
|
968
|
+
children: messages.retry
|
|
969
|
+
}),
|
|
970
|
+
"data-domainkit-part": "outcome-content"
|
|
971
|
+
});
|
|
972
|
+
return retry === null && props.children === void 0 ? null : element;
|
|
973
|
+
}
|
|
974
|
+
/** Media, title, and description in a header, then the action. What `X.Outcome` renders alone. */
|
|
975
|
+
function Composition() {
|
|
976
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(Header$1, { children: [
|
|
977
|
+
/* @__PURE__ */ jsx(Media, {}),
|
|
978
|
+
/* @__PURE__ */ jsx(Title, {}),
|
|
979
|
+
/* @__PURE__ */ jsx(Description, {})
|
|
980
|
+
] }), /* @__PURE__ */ jsx(Content, {})] });
|
|
981
|
+
}
|
|
982
|
+
//#endregion
|
|
763
983
|
//#region src/records.tsx
|
|
764
984
|
var records_exports = /* @__PURE__ */ __exportAll({
|
|
765
985
|
Body: () => Body$1,
|
|
@@ -1103,24 +1323,35 @@ function Status$4({ controller, kind, ...props }) {
|
|
|
1103
1323
|
role: busy(state._tag) ? "status" : void 0
|
|
1104
1324
|
});
|
|
1105
1325
|
}
|
|
1106
|
-
/**
|
|
1107
|
-
|
|
1326
|
+
/**
|
|
1327
|
+
* The failure, or a receipt that only partly landed: media, title, description, and the retry the
|
|
1328
|
+
* flow allows. Children replace the composition and keep the binding.
|
|
1329
|
+
*/
|
|
1330
|
+
function Outcome$4({ children, controller, kind, ...props }) {
|
|
1108
1331
|
const { messages } = useDomainKit();
|
|
1109
1332
|
const readOnly = useReadOnly();
|
|
1110
1333
|
const state = controller.state;
|
|
1111
1334
|
const partial = state._tag === "Applied" && state.receipt.status === "partial";
|
|
1112
|
-
const
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1335
|
+
const retryPart = kind === "provisioning" ? "provisioning-retry" : "cleanup-retry";
|
|
1336
|
+
if (state._tag !== "Failure" && !partial) return null;
|
|
1337
|
+
const words = state._tag === "Failure" ? outcome(state.error, messages) : {
|
|
1338
|
+
description: kind === "provisioning" ? messages.partiallyApplied : messages.partiallyCleaned,
|
|
1339
|
+
title: kind === "provisioning" ? messages.partiallyAppliedTitle : messages.partiallyCleanedTitle
|
|
1340
|
+
};
|
|
1341
|
+
return /* @__PURE__ */ jsx(Provider, {
|
|
1342
|
+
value: {
|
|
1343
|
+
description: words.description,
|
|
1344
|
+
layout: props.layout ?? "card",
|
|
1345
|
+
retry: readOnly || state._tag !== "Failure" ? null : controller.retry,
|
|
1346
|
+
retryPart,
|
|
1347
|
+
title: words.title,
|
|
1348
|
+
tone: state._tag === "Failure" ? "danger" : "warning"
|
|
1349
|
+
},
|
|
1350
|
+
children: /* @__PURE__ */ jsx(Root$6, {
|
|
1351
|
+
...props,
|
|
1352
|
+
children: children ?? /* @__PURE__ */ jsx(Composition, {})
|
|
1353
|
+
})
|
|
1122
1354
|
});
|
|
1123
|
-
return state._tag === "Failure" || partial ? element : null;
|
|
1124
1355
|
}
|
|
1125
1356
|
/**
|
|
1126
1357
|
* Approve and Decline. Approve authorizes the digest and applies it; Decline records the refusal
|
|
@@ -1388,9 +1619,12 @@ var connect_exports = /* @__PURE__ */ __exportAll({
|
|
|
1388
1619
|
Flow: () => Flow$2,
|
|
1389
1620
|
Form: () => Form,
|
|
1390
1621
|
Outcome: () => Outcome$2,
|
|
1622
|
+
Prompt: () => Prompt,
|
|
1391
1623
|
Root: () => Root$2,
|
|
1392
1624
|
State: () => State$1,
|
|
1393
1625
|
Status: () => Status$2,
|
|
1626
|
+
answeredInPlace: () => answeredInPlace,
|
|
1627
|
+
hostProvider: () => hostProvider,
|
|
1394
1628
|
useController: () => useController$2
|
|
1395
1629
|
});
|
|
1396
1630
|
const State$1 = Data.taggedEnum();
|
|
@@ -1403,9 +1637,9 @@ const snapshotOf = (state) => {
|
|
|
1403
1637
|
case "Disconnected": return state.snapshot;
|
|
1404
1638
|
case "Loading":
|
|
1405
1639
|
case "Submitting":
|
|
1406
|
-
case "SelectionRequired":
|
|
1407
|
-
case "
|
|
1408
|
-
case "
|
|
1640
|
+
case "SelectionRequired":
|
|
1641
|
+
case "Failure": return state.snapshot;
|
|
1642
|
+
case "Redirecting": return null;
|
|
1409
1643
|
}
|
|
1410
1644
|
};
|
|
1411
1645
|
const settled = (snapshot, discovery) => {
|
|
@@ -1435,6 +1669,7 @@ function useController$2({ domain, returnTo }) {
|
|
|
1435
1669
|
snapshot: null
|
|
1436
1670
|
});
|
|
1437
1671
|
const lastCommand = useRef(null);
|
|
1672
|
+
const attempted = useRef(null);
|
|
1438
1673
|
const [inspected, setInspected] = useState(domain);
|
|
1439
1674
|
if (inspected !== domain) {
|
|
1440
1675
|
setInspected(domain);
|
|
@@ -1448,7 +1683,12 @@ function useController$2({ domain, returnTo }) {
|
|
|
1448
1683
|
setState(State$1.Loading({ snapshot: null }));
|
|
1449
1684
|
}
|
|
1450
1685
|
const onFailure = useCallback((error) => {
|
|
1451
|
-
setState(State$1.Failure({
|
|
1686
|
+
setState(State$1.Failure({
|
|
1687
|
+
attempt: attempted.current,
|
|
1688
|
+
discovery: held.current.discovery,
|
|
1689
|
+
error,
|
|
1690
|
+
snapshot: held.current.snapshot
|
|
1691
|
+
}));
|
|
1452
1692
|
emit(Event.Failed({
|
|
1453
1693
|
domain,
|
|
1454
1694
|
error
|
|
@@ -1457,6 +1697,7 @@ function useController$2({ domain, returnTo }) {
|
|
|
1457
1697
|
const load = useCallback(() => {
|
|
1458
1698
|
if (connection === void 0) return;
|
|
1459
1699
|
lastCommand.current = null;
|
|
1700
|
+
attempted.current = null;
|
|
1460
1701
|
held.current = {
|
|
1461
1702
|
discovery: null,
|
|
1462
1703
|
domain,
|
|
@@ -1521,8 +1762,9 @@ function useController$2({ domain, returnTo }) {
|
|
|
1521
1762
|
emit,
|
|
1522
1763
|
navigate
|
|
1523
1764
|
]);
|
|
1524
|
-
const submit = useCallback((effect) => {
|
|
1765
|
+
const submit = useCallback((effect, attempt) => {
|
|
1525
1766
|
if (held.current.domain !== domain) return;
|
|
1767
|
+
attempted.current = attempt;
|
|
1526
1768
|
const command = () => {
|
|
1527
1769
|
setState(State$1.Submitting({ snapshot: held.current.snapshot }));
|
|
1528
1770
|
runner.run(effect, {
|
|
@@ -1550,7 +1792,10 @@ function useController$2({ domain, returnTo }) {
|
|
|
1550
1792
|
domain,
|
|
1551
1793
|
method,
|
|
1552
1794
|
provider: input.provider
|
|
1553
|
-
})
|
|
1795
|
+
}), {
|
|
1796
|
+
method: input.method,
|
|
1797
|
+
provider: input.provider
|
|
1798
|
+
});
|
|
1554
1799
|
}, [
|
|
1555
1800
|
connection,
|
|
1556
1801
|
domain,
|
|
@@ -1563,7 +1808,7 @@ function useController$2({ domain, returnTo }) {
|
|
|
1563
1808
|
connectionId: input.connectionId,
|
|
1564
1809
|
domain,
|
|
1565
1810
|
...input.zone === void 0 ? {} : { zone: input.zone }
|
|
1566
|
-
}));
|
|
1811
|
+
}), null);
|
|
1567
1812
|
}, [
|
|
1568
1813
|
connection,
|
|
1569
1814
|
domain,
|
|
@@ -1578,6 +1823,7 @@ function useController$2({ domain, returnTo }) {
|
|
|
1578
1823
|
}, [reuse, state]);
|
|
1579
1824
|
const release = useCallback((effect, event) => {
|
|
1580
1825
|
if (held.current.domain !== domain) return;
|
|
1826
|
+
attempted.current = null;
|
|
1581
1827
|
const command = () => {
|
|
1582
1828
|
setState(State$1.Submitting({ snapshot: held.current.snapshot }));
|
|
1583
1829
|
runner.run(effect, {
|
|
@@ -1634,7 +1880,7 @@ function useController$2({ domain, returnTo }) {
|
|
|
1634
1880
|
connect,
|
|
1635
1881
|
detach,
|
|
1636
1882
|
disconnect,
|
|
1637
|
-
discovery: state._tag === "Disconnected" ? state.discovery : null,
|
|
1883
|
+
discovery: state._tag === "Disconnected" || state._tag === "Failure" ? state.discovery : null,
|
|
1638
1884
|
providers: snapshotOf(state)?.providers ?? [],
|
|
1639
1885
|
refresh: load,
|
|
1640
1886
|
retry,
|
|
@@ -1650,23 +1896,51 @@ function Root$2({ controller, ...props }) {
|
|
|
1650
1896
|
"data-state": controller.state._tag
|
|
1651
1897
|
});
|
|
1652
1898
|
}
|
|
1653
|
-
/**
|
|
1654
|
-
|
|
1899
|
+
/**
|
|
1900
|
+
* Whether a failure is already answered where it was raised: beside the method the customer used.
|
|
1901
|
+
* A flow rendering its own `Outcome` beside the dialog skips it, so the failure announces once.
|
|
1902
|
+
*/
|
|
1903
|
+
const answeredInPlace = (controller) => controller.state._tag === "Failure" && controller.state.attempt !== null;
|
|
1904
|
+
/**
|
|
1905
|
+
* The provider whose nameservers serve this domain, as the snapshot describes it. Discovery names
|
|
1906
|
+
* it by id; without a descriptor there is nothing to draw, so there is no host.
|
|
1907
|
+
*/
|
|
1908
|
+
const hostProvider = (controller) => {
|
|
1909
|
+
const discovery = controller.discovery;
|
|
1910
|
+
if (discovery === null || discovery._tag !== "NotFound" || discovery.host === null) return null;
|
|
1911
|
+
const named = discovery.host.provider;
|
|
1912
|
+
return controller.providers.find((entry) => entry.id === named) ?? null;
|
|
1913
|
+
};
|
|
1914
|
+
/** The provider as the customer knows it, falling back to the id when the snapshot has no name. */
|
|
1915
|
+
const displayName = (controller, provider) => controller.providers.find((entry) => entry.id === provider)?.name ?? provider;
|
|
1916
|
+
/**
|
|
1917
|
+
* The last failure as a card: media, the catalog's title and description, and the retry the flow
|
|
1918
|
+
* allows. Children replace the composition and keep the binding.
|
|
1919
|
+
*/
|
|
1920
|
+
function Outcome$2({ children, controller, ...props }) {
|
|
1655
1921
|
const { messages } = useDomainKit();
|
|
1656
1922
|
const readOnly = useReadOnly();
|
|
1657
1923
|
const state = controller.state;
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1924
|
+
if (state._tag !== "Failure") return null;
|
|
1925
|
+
const acted = state.attempt === null ? void 0 : displayName(controller, state.attempt.provider);
|
|
1926
|
+
const words = outcome(state.error, messages, {
|
|
1927
|
+
...controller.snapshot === null ? {} : { domain: controller.snapshot.domain },
|
|
1928
|
+
...acted === void 0 ? {} : { provider: acted }
|
|
1929
|
+
});
|
|
1930
|
+
return /* @__PURE__ */ jsx(Provider, {
|
|
1931
|
+
value: {
|
|
1932
|
+
description: words.description,
|
|
1933
|
+
layout: props.layout ?? "card",
|
|
1934
|
+
retry: readOnly ? null : controller.retry,
|
|
1935
|
+
retryPart: "connection-retry",
|
|
1936
|
+
title: words.title,
|
|
1937
|
+
tone: "danger"
|
|
1938
|
+
},
|
|
1939
|
+
children: /* @__PURE__ */ jsx(Root$6, {
|
|
1940
|
+
...props,
|
|
1941
|
+
children: children ?? /* @__PURE__ */ jsx(Composition, {})
|
|
1942
|
+
})
|
|
1668
1943
|
});
|
|
1669
|
-
return state._tag === "Failure" ? element : null;
|
|
1670
1944
|
}
|
|
1671
1945
|
function Status$2({ controller, ...props }) {
|
|
1672
1946
|
const { messages } = useDomainKit();
|
|
@@ -1692,14 +1966,78 @@ function Status$2({ controller, ...props }) {
|
|
|
1692
1966
|
});
|
|
1693
1967
|
}
|
|
1694
1968
|
/**
|
|
1969
|
+
* The one field a rejected command was about, if any. A provider that turns down credentials is
|
|
1970
|
+
* answering the secret it was given, and it names no field, so the first secret carries the
|
|
1971
|
+
* answer; anything else is about the request rather than one input.
|
|
1972
|
+
*/
|
|
1973
|
+
const rejected = (reason, fields) => {
|
|
1974
|
+
if (reason === null) return null;
|
|
1975
|
+
if (reason._tag === "InvalidInput") return fields.find((field) => field.name === reason.field)?.name ?? null;
|
|
1976
|
+
if (reason._tag !== "Unauthenticated" && reason._tag !== "Forbidden") return null;
|
|
1977
|
+
return fields.find((field) => field.secret)?.name ?? null;
|
|
1978
|
+
};
|
|
1979
|
+
/**
|
|
1980
|
+
* One declared field in shadcn's `Field` anatomy: the label, the control, the note under it, and
|
|
1981
|
+
* the error the provider answered with. The docs link rides the field it explains.
|
|
1982
|
+
*/
|
|
1983
|
+
function TokenField({ controller, docsUrl, field, invalid }) {
|
|
1984
|
+
const { messages } = useDomainKit();
|
|
1985
|
+
const icons = useIcons();
|
|
1986
|
+
const id = useId();
|
|
1987
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1988
|
+
"data-domainkit-part": "field",
|
|
1989
|
+
"data-invalid": invalid ? "" : void 0,
|
|
1990
|
+
"data-orientation": "vertical",
|
|
1991
|
+
children: [
|
|
1992
|
+
/* @__PURE__ */ jsxs("label", {
|
|
1993
|
+
"data-domainkit-part": "field-label",
|
|
1994
|
+
htmlFor: id,
|
|
1995
|
+
children: [messages.fieldLabel(field.name), field.required ? null : /* @__PURE__ */ jsx("span", {
|
|
1996
|
+
"data-domainkit-part": "field-optional",
|
|
1997
|
+
children: messages.optionalField
|
|
1998
|
+
})]
|
|
1999
|
+
}),
|
|
2000
|
+
/* @__PURE__ */ jsx("input", {
|
|
2001
|
+
"aria-invalid": invalid ? true : void 0,
|
|
2002
|
+
autoComplete: "off",
|
|
2003
|
+
"data-domainkit-part": "field-input",
|
|
2004
|
+
id,
|
|
2005
|
+
name: field.name,
|
|
2006
|
+
required: field.required,
|
|
2007
|
+
type: field.secret ? "password" : "text"
|
|
2008
|
+
}),
|
|
2009
|
+
docsUrl === null ? null : /* @__PURE__ */ jsxs("a", {
|
|
2010
|
+
"data-domainkit-part": "field-description",
|
|
2011
|
+
href: docsUrl,
|
|
2012
|
+
rel: "noreferrer",
|
|
2013
|
+
target: "_blank",
|
|
2014
|
+
children: [messages.getToken, /* @__PURE__ */ jsx("span", {
|
|
2015
|
+
"aria-hidden": "true",
|
|
2016
|
+
"data-icon": "inline-end",
|
|
2017
|
+
children: icons.external
|
|
2018
|
+
})]
|
|
2019
|
+
}),
|
|
2020
|
+
invalid ? /* @__PURE__ */ jsx("div", {
|
|
2021
|
+
"data-domainkit-part": "field-error",
|
|
2022
|
+
children: /* @__PURE__ */ jsx(Outcome$2, {
|
|
2023
|
+
controller,
|
|
2024
|
+
layout: "inline"
|
|
2025
|
+
})
|
|
2026
|
+
}) : null
|
|
2027
|
+
]
|
|
2028
|
+
});
|
|
2029
|
+
}
|
|
2030
|
+
/**
|
|
1695
2031
|
* One auth method, rendered from its descriptor. A token method draws an input per declared
|
|
1696
|
-
* field, so a provider that needs an account id alongside a token needs no code here.
|
|
2032
|
+
* field, so a provider that needs an account id alongside a token needs no code here. The fields
|
|
2033
|
+
* a provider does not need sit behind a disclosure, so the common form is one field.
|
|
1697
2034
|
*/
|
|
1698
2035
|
function Method({ controller, method, provider }) {
|
|
1699
2036
|
const { messages } = useDomainKit();
|
|
1700
|
-
const
|
|
1701
|
-
const busy =
|
|
1702
|
-
|
|
2037
|
+
const state = controller.state;
|
|
2038
|
+
const busy = state._tag === "Submitting";
|
|
2039
|
+
const failed = state._tag === "Failure" && state.attempt !== null && state.attempt.provider === provider.id && state.attempt.method === method.kind;
|
|
2040
|
+
if (method.fields === null) return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("button", {
|
|
1703
2041
|
"data-domainkit-part": method.kind === "oauth" ? "oauth-connect" : "integration-connect",
|
|
1704
2042
|
"data-provider": provider.id,
|
|
1705
2043
|
disabled: busy,
|
|
@@ -1708,9 +2046,22 @@ function Method({ controller, method, provider }) {
|
|
|
1708
2046
|
provider: provider.id
|
|
1709
2047
|
}),
|
|
1710
2048
|
type: "button",
|
|
1711
|
-
children: messages.
|
|
1712
|
-
})
|
|
2049
|
+
children: method.kind === "oauth" ? messages.methodOAuth(provider.name) : messages.methodIntegration(provider.name)
|
|
2050
|
+
}), failed ? /* @__PURE__ */ jsx(Outcome$2, {
|
|
2051
|
+
controller,
|
|
2052
|
+
layout: "inline"
|
|
2053
|
+
}) : null] });
|
|
1713
2054
|
const fields = method.fields;
|
|
2055
|
+
const answered = rejected(failed && state._tag === "Failure" ? state.error.reason : null, fields);
|
|
2056
|
+
const required = fields.filter((field) => field.required);
|
|
2057
|
+
const optional = fields.filter((field) => !field.required);
|
|
2058
|
+
const explains = (required.find((field) => field.secret) ?? required[0] ?? optional[0])?.name;
|
|
2059
|
+
const render = (field) => /* @__PURE__ */ jsx(TokenField, {
|
|
2060
|
+
controller,
|
|
2061
|
+
docsUrl: field.name === explains ? method.docsUrl : null,
|
|
2062
|
+
field,
|
|
2063
|
+
invalid: field.name === answered
|
|
2064
|
+
}, field.name);
|
|
1714
2065
|
return /* @__PURE__ */ jsxs("form", {
|
|
1715
2066
|
"data-domainkit-part": "token-connect",
|
|
1716
2067
|
"data-provider": provider.id,
|
|
@@ -1729,48 +2080,58 @@ function Method({ controller, method, provider }) {
|
|
|
1729
2080
|
});
|
|
1730
2081
|
},
|
|
1731
2082
|
children: [
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
}), /* @__PURE__ */ jsx("input", {
|
|
1741
|
-
autoComplete: "off",
|
|
1742
|
-
"data-domainkit-part": "token-field-input",
|
|
1743
|
-
name: field.name,
|
|
1744
|
-
required: field.required,
|
|
1745
|
-
type: field.secret ? "password" : "text"
|
|
1746
|
-
})]
|
|
1747
|
-
}, field.name)),
|
|
1748
|
-
method.docsUrl === null ? null : /* @__PURE__ */ jsxs("a", {
|
|
1749
|
-
"data-domainkit-part": "token-docs",
|
|
1750
|
-
href: method.docsUrl,
|
|
1751
|
-
rel: "noreferrer",
|
|
1752
|
-
target: "_blank",
|
|
1753
|
-
children: [messages.getToken, /* @__PURE__ */ jsx("span", {
|
|
1754
|
-
"aria-hidden": "true",
|
|
1755
|
-
"data-icon": "inline-end",
|
|
1756
|
-
children: icons.external
|
|
1757
|
-
})]
|
|
2083
|
+
required.map(render),
|
|
2084
|
+
optional.length === 0 ? null : /* @__PURE__ */ jsxs("details", {
|
|
2085
|
+
"data-domainkit-part": "more-options",
|
|
2086
|
+
open: optional.some((field) => field.name === answered) || void 0,
|
|
2087
|
+
children: [/* @__PURE__ */ jsx("summary", {
|
|
2088
|
+
"data-domainkit-part": "more-options-trigger",
|
|
2089
|
+
children: messages.moreOptions
|
|
2090
|
+
}), optional.map(render)]
|
|
1758
2091
|
}),
|
|
2092
|
+
failed && answered === null ? /* @__PURE__ */ jsx(Outcome$2, {
|
|
2093
|
+
controller,
|
|
2094
|
+
layout: "inline"
|
|
2095
|
+
}) : null,
|
|
1759
2096
|
/* @__PURE__ */ jsx("button", {
|
|
1760
2097
|
"data-domainkit-part": "token-submit",
|
|
1761
2098
|
disabled: busy,
|
|
1762
2099
|
type: "submit",
|
|
1763
|
-
children: messages.
|
|
2100
|
+
children: messages.methodToken
|
|
1764
2101
|
})
|
|
1765
2102
|
]
|
|
1766
2103
|
});
|
|
1767
2104
|
}
|
|
1768
2105
|
/**
|
|
2106
|
+
* One provider's methods. With a heading it is a disclosure the customer opens; the provider a
|
|
2107
|
+
* narrowed dialog is about carries no heading, because the dialog title already names it.
|
|
2108
|
+
*/
|
|
2109
|
+
function Authentication({ controller, onOpen, open, provider }) {
|
|
2110
|
+
const methods = provider.methods.map((method) => /* @__PURE__ */ jsx(Method, {
|
|
2111
|
+
controller,
|
|
2112
|
+
method,
|
|
2113
|
+
provider
|
|
2114
|
+
}, `${provider.id}:${method.kind}`));
|
|
2115
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
2116
|
+
"data-domainkit-part": "provider-authentication",
|
|
2117
|
+
"data-provider": provider.id,
|
|
2118
|
+
"data-state": open ? "open" : "closed",
|
|
2119
|
+
children: [onOpen === void 0 ? null : /* @__PURE__ */ jsxs("button", {
|
|
2120
|
+
"aria-expanded": open,
|
|
2121
|
+
"data-domainkit-part": "provider-heading",
|
|
2122
|
+
onClick: onOpen,
|
|
2123
|
+
type: "button",
|
|
2124
|
+
children: [/* @__PURE__ */ jsx(Mark, { provider }), /* @__PURE__ */ jsx("strong", { children: provider.name })]
|
|
2125
|
+
}), open ? methods : null]
|
|
2126
|
+
});
|
|
2127
|
+
}
|
|
2128
|
+
/**
|
|
1769
2129
|
* Everything a disconnected domain can do: a connection discovery already found, the connections
|
|
1770
2130
|
* this owner already has, then each provider's declared methods.
|
|
1771
2131
|
*/
|
|
1772
2132
|
function Form({ controller, ...props }) {
|
|
1773
2133
|
const { messages } = useDomainKit();
|
|
2134
|
+
const [opened, setOpened] = useState(null);
|
|
1774
2135
|
const state = controller.state;
|
|
1775
2136
|
const snapshot = controller.snapshot;
|
|
1776
2137
|
const discovery = controller.discovery;
|
|
@@ -1781,6 +2142,14 @@ function Form({ controller, ...props }) {
|
|
|
1781
2142
|
const resolved = discovery !== null && discovery._tag === "Resolved" ? discovery : null;
|
|
1782
2143
|
const reusable = snapshot?.reusable ?? [];
|
|
1783
2144
|
const providers = controller.providers;
|
|
2145
|
+
const host = hostProvider(controller);
|
|
2146
|
+
const others = providers.filter((provider) => provider.id !== host?.id);
|
|
2147
|
+
const authentication = (provider, open) => /* @__PURE__ */ jsx(Authentication, {
|
|
2148
|
+
controller,
|
|
2149
|
+
onOpen: () => setOpened(open ? "" : provider.id),
|
|
2150
|
+
open,
|
|
2151
|
+
provider
|
|
2152
|
+
}, provider.id);
|
|
1784
2153
|
return usePart("div", props, { status: state._tag }, {
|
|
1785
2154
|
children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1786
2155
|
resolved === null ? null : /* @__PURE__ */ jsxs("section", {
|
|
@@ -1827,17 +2196,17 @@ function Form({ controller, ...props }) {
|
|
|
1827
2196
|
providers.length === 0 ? /* @__PURE__ */ jsx("p", {
|
|
1828
2197
|
"data-domainkit-part": "target-unavailable",
|
|
1829
2198
|
children: messages.noProviders
|
|
1830
|
-
}) :
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
},
|
|
1840
|
-
}
|
|
2199
|
+
}) : host === null ? others.map((provider) => authentication(provider, opened === null ? provider.id === others[0]?.id : opened === provider.id)) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Authentication, {
|
|
2200
|
+
controller,
|
|
2201
|
+
open: true,
|
|
2202
|
+
provider: host
|
|
2203
|
+
}), others.length === 0 ? null : /* @__PURE__ */ jsxs("details", {
|
|
2204
|
+
"data-domainkit-part": "other-providers",
|
|
2205
|
+
children: [/* @__PURE__ */ jsx("summary", {
|
|
2206
|
+
"data-domainkit-part": "other-providers-trigger",
|
|
2207
|
+
children: messages.useAnotherProvider
|
|
2208
|
+
}), others.map((provider) => authentication(provider, opened === provider.id))]
|
|
2209
|
+
})] })
|
|
1841
2210
|
] }),
|
|
1842
2211
|
"data-domainkit-part": "connection-form"
|
|
1843
2212
|
});
|
|
@@ -1847,17 +2216,23 @@ function Dialog$2({ children, controller, render, title, trigger }) {
|
|
|
1847
2216
|
const { colorScheme, messages, portalContainer, themeStyle } = useDomainKit();
|
|
1848
2217
|
const readOnly = useReadOnly();
|
|
1849
2218
|
const [open, setOpen] = useState(false);
|
|
1850
|
-
const
|
|
2219
|
+
const state = controller.state;
|
|
2220
|
+
const busy = state._tag === "Submitting";
|
|
2221
|
+
const unattributed = state._tag === "Failure" && !answeredInPlace(controller);
|
|
1851
2222
|
const snapshot = controller.snapshot;
|
|
1852
|
-
const
|
|
1853
|
-
const
|
|
2223
|
+
const attached = snapshot?.provider ?? null;
|
|
2224
|
+
const host = hostProvider(controller);
|
|
2225
|
+
const named = attached === null ? host?.name ?? null : displayName(controller, attached);
|
|
2226
|
+
const heading = title ?? (named === null ? messages.connectAnyTitle : messages.connectTitle(named));
|
|
2227
|
+
const running = state._tag === "Loading" || state._tag === "Submitting" || state._tag === "Redirecting" || state._tag === "SelectionRequired";
|
|
2228
|
+
const label = named === null ? messages.connectAnyTitle : messages.connect;
|
|
1854
2229
|
const body = children ?? /* @__PURE__ */ jsx(Form, { controller });
|
|
1855
2230
|
if (readOnly) return /* @__PURE__ */ jsx(Fragment, {});
|
|
1856
2231
|
if (render !== void 0) return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("button", {
|
|
1857
2232
|
"data-domainkit-part": "connection-trigger",
|
|
1858
2233
|
onClick: () => setOpen(!open),
|
|
1859
2234
|
type: "button",
|
|
1860
|
-
children: trigger ??
|
|
2235
|
+
children: trigger ?? label
|
|
1861
2236
|
}), render({
|
|
1862
2237
|
children: body,
|
|
1863
2238
|
open
|
|
@@ -1873,7 +2248,7 @@ function Dialog$2({ children, controller, render, title, trigger }) {
|
|
|
1873
2248
|
open,
|
|
1874
2249
|
children: [/* @__PURE__ */ jsx(Dialog.Trigger, {
|
|
1875
2250
|
"data-domainkit-part": "connection-trigger",
|
|
1876
|
-
children: trigger ??
|
|
2251
|
+
children: trigger ?? label
|
|
1877
2252
|
}), /* @__PURE__ */ jsxs(Dialog.Portal, {
|
|
1878
2253
|
container: portalContainer,
|
|
1879
2254
|
children: [/* @__PURE__ */ jsx(Dialog.Backdrop, {
|
|
@@ -1907,9 +2282,9 @@ function Dialog$2({ children, controller, render, title, trigger }) {
|
|
|
1907
2282
|
/* @__PURE__ */ jsxs("div", {
|
|
1908
2283
|
"data-domainkit-part": "dialog-body",
|
|
1909
2284
|
children: [
|
|
1910
|
-
/* @__PURE__ */ jsx(Status$2, { controller }),
|
|
2285
|
+
running ? /* @__PURE__ */ jsx(Status$2, { controller }) : null,
|
|
1911
2286
|
body,
|
|
1912
|
-
/* @__PURE__ */ jsx(Outcome$2, { controller })
|
|
2287
|
+
unattributed ? /* @__PURE__ */ jsx(Outcome$2, { controller }) : null
|
|
1913
2288
|
]
|
|
1914
2289
|
}),
|
|
1915
2290
|
busy ? null : /* @__PURE__ */ jsx("div", {
|
|
@@ -1924,6 +2299,41 @@ function Dialog$2({ children, controller, render, title, trigger }) {
|
|
|
1924
2299
|
})]
|
|
1925
2300
|
});
|
|
1926
2301
|
}
|
|
2302
|
+
/**
|
|
2303
|
+
* The disconnected offer: the provider whose nameservers serve the domain, what that means, and
|
|
2304
|
+
* the trigger that connects it. With no host detected there is nothing to offer, so nothing
|
|
2305
|
+
* renders unless the host application asks for the dialog anyway.
|
|
2306
|
+
*/
|
|
2307
|
+
function Prompt({ connect = "detected", controller, ...props }) {
|
|
2308
|
+
const { messages } = useDomainKit();
|
|
2309
|
+
const readOnly = useReadOnly();
|
|
2310
|
+
const state = controller.state;
|
|
2311
|
+
const host = hostProvider(controller);
|
|
2312
|
+
const discovery = controller.discovery;
|
|
2313
|
+
const offers = host !== null || discovery !== null && discovery._tag !== "NotFound" || (controller.snapshot?.reusable.length ?? 0) > 0;
|
|
2314
|
+
const inFlight = state._tag === "Submitting" || state._tag === "Redirecting" || state._tag === "SelectionRequired" || state._tag === "Reconnect";
|
|
2315
|
+
const element = usePart("div", props, { status: state._tag }, {
|
|
2316
|
+
children: /* @__PURE__ */ jsxs(Fragment, { children: [host === null ? null : /* @__PURE__ */ jsxs("div", {
|
|
2317
|
+
"data-domainkit-part": "host-identity",
|
|
2318
|
+
children: [
|
|
2319
|
+
/* @__PURE__ */ jsx(Mark, { provider: host }),
|
|
2320
|
+
/* @__PURE__ */ jsx("span", {
|
|
2321
|
+
"data-domainkit-part": "host-name",
|
|
2322
|
+
children: host.name
|
|
2323
|
+
}),
|
|
2324
|
+
/* @__PURE__ */ jsx("span", {
|
|
2325
|
+
"data-domainkit-part": "host-statement",
|
|
2326
|
+
children: messages.hostOwnsZone
|
|
2327
|
+
})
|
|
2328
|
+
]
|
|
2329
|
+
}), /* @__PURE__ */ jsx(Dialog$2, { controller })] }),
|
|
2330
|
+
"data-domainkit-part": "connect-prompt",
|
|
2331
|
+
"data-host": host?.id
|
|
2332
|
+
});
|
|
2333
|
+
if (!inFlight && !offers && connect !== "always") return null;
|
|
2334
|
+
if (readOnly && host === null) return null;
|
|
2335
|
+
return element;
|
|
2336
|
+
}
|
|
1927
2337
|
/** A connected domain: which provider holds it, and how to let it go. */
|
|
1928
2338
|
function Card({ controller, ...props }) {
|
|
1929
2339
|
const { messages } = useDomainKit();
|
|
@@ -1958,8 +2368,8 @@ function Card({ controller, ...props }) {
|
|
|
1958
2368
|
"data-domainkit-part": "connected-card"
|
|
1959
2369
|
});
|
|
1960
2370
|
}
|
|
1961
|
-
/** Connection on its own: the card once connected, the
|
|
1962
|
-
function Flow$2({ domain, returnTo, ...props }) {
|
|
2371
|
+
/** Connection on its own: the card once connected, the prompt until then. */
|
|
2372
|
+
function Flow$2({ connect, domain, returnTo, ...props }) {
|
|
1963
2373
|
const controller = useController$2({
|
|
1964
2374
|
domain,
|
|
1965
2375
|
...returnTo === void 0 ? {} : { returnTo }
|
|
@@ -1968,7 +2378,10 @@ function Flow$2({ domain, returnTo, ...props }) {
|
|
|
1968
2378
|
return /* @__PURE__ */ jsx(Root$2, {
|
|
1969
2379
|
controller,
|
|
1970
2380
|
...props,
|
|
1971
|
-
children: state._tag === "Connected" ? /* @__PURE__ */ jsx(Card, { controller }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(
|
|
2381
|
+
children: state._tag === "Connected" ? /* @__PURE__ */ jsx(Card, { controller }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Prompt, {
|
|
2382
|
+
controller,
|
|
2383
|
+
...connect === void 0 ? {} : { connect }
|
|
2384
|
+
}), answeredInPlace(controller) ? null : /* @__PURE__ */ jsx(Outcome$2, { controller })] })
|
|
1972
2385
|
});
|
|
1973
2386
|
}
|
|
1974
2387
|
//#endregion
|
|
@@ -2242,25 +2655,29 @@ function Evidence({ readiness }) {
|
|
|
2242
2655
|
})]
|
|
2243
2656
|
});
|
|
2244
2657
|
}
|
|
2245
|
-
|
|
2658
|
+
/**
|
|
2659
|
+
* The last observation that failed. Observing reads the world rather than changing the domain, so
|
|
2660
|
+
* the retry stays even in read-only.
|
|
2661
|
+
*/
|
|
2662
|
+
function Outcome({ children, controller, ...props }) {
|
|
2246
2663
|
const { messages } = useDomainKit();
|
|
2247
2664
|
const state = controller.state;
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2665
|
+
if (state._tag !== "Failure") return null;
|
|
2666
|
+
const words = outcome(state.error, messages);
|
|
2667
|
+
return /* @__PURE__ */ jsx(Provider, {
|
|
2668
|
+
value: {
|
|
2669
|
+
description: words.description,
|
|
2670
|
+
layout: props.layout ?? "card",
|
|
2671
|
+
retry: controller.retry,
|
|
2672
|
+
retryPart: "verification-retry",
|
|
2673
|
+
title: words.title,
|
|
2674
|
+
tone: "danger"
|
|
2675
|
+
},
|
|
2676
|
+
children: /* @__PURE__ */ jsx(Root$6, {
|
|
2677
|
+
...props,
|
|
2678
|
+
children: children ?? /* @__PURE__ */ jsx(Composition, {})
|
|
2679
|
+
})
|
|
2262
2680
|
});
|
|
2263
|
-
return state._tag === "Failure" ? element : null;
|
|
2264
2681
|
}
|
|
2265
2682
|
/** The default verification slot: a button that observes, with the evidence behind it. */
|
|
2266
2683
|
function Status({ controller, render, ...props }) {
|
|
@@ -2306,11 +2723,15 @@ function Status({ controller, render, ...props }) {
|
|
|
2306
2723
|
//#endregion
|
|
2307
2724
|
//#region src/domain.tsx
|
|
2308
2725
|
var domain_exports = /* @__PURE__ */ __exportAll({ Flow: () => Flow });
|
|
2309
|
-
function DefaultConnection({ controller }) {
|
|
2726
|
+
function DefaultConnection({ connect, controller }) {
|
|
2727
|
+
const stated = controller.state._tag === "Disconnected" && hostProvider(controller) !== null;
|
|
2310
2728
|
return controller.state._tag === "Connected" ? /* @__PURE__ */ jsx(Card, { controller }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2311
|
-
/* @__PURE__ */ jsx(Status$2, { controller }),
|
|
2312
|
-
/* @__PURE__ */ jsx(
|
|
2313
|
-
|
|
2729
|
+
stated ? null : /* @__PURE__ */ jsx(Status$2, { controller }),
|
|
2730
|
+
/* @__PURE__ */ jsx(Prompt, {
|
|
2731
|
+
connect,
|
|
2732
|
+
controller
|
|
2733
|
+
}),
|
|
2734
|
+
answeredInPlace(controller) ? null : /* @__PURE__ */ jsx(Outcome$2, { controller })
|
|
2314
2735
|
] });
|
|
2315
2736
|
}
|
|
2316
2737
|
function DefaultActions({ cleanup, connection, provisioning }) {
|
|
@@ -2353,7 +2774,7 @@ function DefaultActions({ cleanup, connection, provisioning }) {
|
|
|
2353
2774
|
* only what the transport declares, and every part of it is a slot with a default. It adds no
|
|
2354
2775
|
* layout containers of its own, so a host's grid can place the slot output directly.
|
|
2355
2776
|
*/
|
|
2356
|
-
function Flow({ domain, onApplied, onCleaned, readOnly, requirements, returnTo, slots = {}, ...props }) {
|
|
2777
|
+
function Flow({ connect = "detected", domain, onApplied, onCleaned, readOnly, requirements, returnTo, slots = {}, ...props }) {
|
|
2357
2778
|
const { capabilities } = useDomainKit();
|
|
2358
2779
|
const inherited = useReadOnly();
|
|
2359
2780
|
const connection = useController$2({
|
|
@@ -2390,9 +2811,11 @@ function Flow({ domain, onApplied, onCleaned, readOnly, requirements, returnTo,
|
|
|
2390
2811
|
value: readOnly ?? inherited,
|
|
2391
2812
|
children: [
|
|
2392
2813
|
!capabilities.includes("connection") ? null : slots.connection === void 0 ? /* @__PURE__ */ jsx(DefaultConnection, {
|
|
2814
|
+
connect,
|
|
2393
2815
|
controller: connection,
|
|
2394
2816
|
domain
|
|
2395
2817
|
}) : slots.connection({
|
|
2818
|
+
connect,
|
|
2396
2819
|
controller: connection,
|
|
2397
2820
|
domain
|
|
2398
2821
|
}),
|
|
@@ -2438,6 +2861,6 @@ const transport = Testing.transport;
|
|
|
2438
2861
|
//#region src/failure.ts
|
|
2439
2862
|
const Failure = Data.taggedEnum();
|
|
2440
2863
|
//#endregion
|
|
2441
|
-
export { cleanup_exports as Cleanup, connect_exports as Connect, domain_exports as Domain, domain_kit_exports as DomainKit, Event, Failure, messages_exports as Messages, operations_exports as Operations, provider_exports as Provider, provision_exports as Provision, records_exports as Records, testing_exports as Testing, theme_exports as Theme, verify_exports as Verify, defaultIcons, useIcons };
|
|
2864
|
+
export { cleanup_exports as Cleanup, connect_exports as Connect, domain_exports as Domain, domain_kit_exports as DomainKit, Event, Failure, messages_exports as Messages, operations_exports as Operations, outcome_exports as Outcome, provider_exports as Provider, provision_exports as Provision, records_exports as Records, testing_exports as Testing, theme_exports as Theme, verify_exports as Verify, defaultIcons, useIcons };
|
|
2442
2865
|
|
|
2443
2866
|
//# sourceMappingURL=index.mjs.map
|