@domainkit/react 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +100 -101
- package/dist/index.d.mts +612 -466
- package/dist/index.mjs +814 -1751
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -12
- package/dist/styles.css +0 -1530
package/dist/index.mjs
CHANGED
|
@@ -2,110 +2,14 @@
|
|
|
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, useId, useLayoutEffect, useMemo, useRef, useState
|
|
5
|
+
import { createContext, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
6
6
|
import * as Data from "effect/Data";
|
|
7
7
|
import { Transport } from "domainkit/client";
|
|
8
|
-
import {
|
|
9
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
import { jsx } from "react/jsx-runtime";
|
|
10
9
|
import * as Exit from "effect/Exit";
|
|
11
10
|
import * as Option from "effect/Option";
|
|
12
|
-
import { Dialog } from "@base-ui/react/dialog";
|
|
13
|
-
import { Popover } from "@base-ui/react/popover";
|
|
14
11
|
import * as DateTime from "effect/DateTime";
|
|
15
12
|
import { Testing } from "domainkit/testing";
|
|
16
|
-
//#region src/composition.tsx
|
|
17
|
-
function usePart(defaultTagName, componentProps, state, internalProps) {
|
|
18
|
-
const { className, render, style, ...externalProps } = componentProps;
|
|
19
|
-
const parameters = {
|
|
20
|
-
...className === void 0 ? {} : { className },
|
|
21
|
-
defaultTagName,
|
|
22
|
-
props: [internalProps, externalProps],
|
|
23
|
-
...render === void 0 ? {} : { render },
|
|
24
|
-
state,
|
|
25
|
-
...style === void 0 ? {} : { style }
|
|
26
|
-
};
|
|
27
|
-
return useRender(parameters);
|
|
28
|
-
}
|
|
29
|
-
/** A part with no state of its own: one tag, one `data-domainkit-part`, host props on top. */
|
|
30
|
-
function leafPart(defaultTagName, part) {
|
|
31
|
-
return function Leaf(props) {
|
|
32
|
-
return usePart(defaultTagName, props, {}, { "data-domainkit-part": part });
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
//#endregion
|
|
36
|
-
//#region src/icons.tsx
|
|
37
|
-
function Glyph({ children }) {
|
|
38
|
-
return /* @__PURE__ */ jsx("svg", {
|
|
39
|
-
"aria-hidden": "true",
|
|
40
|
-
fill: "none",
|
|
41
|
-
stroke: "currentColor",
|
|
42
|
-
strokeLinecap: "round",
|
|
43
|
-
strokeLinejoin: "round",
|
|
44
|
-
strokeWidth: 2,
|
|
45
|
-
viewBox: "0 0 24 24",
|
|
46
|
-
children
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
const defaultIcons = {
|
|
50
|
-
close: /* @__PURE__ */ jsxs(Glyph, { children: [/* @__PURE__ */ jsx("path", { d: "M18 6 6 18" }), /* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })] }),
|
|
51
|
-
copied: /* @__PURE__ */ jsx(Glyph, { children: /* @__PURE__ */ jsx("path", { d: "M20 6 9 17l-5-5" }) }),
|
|
52
|
-
copy: /* @__PURE__ */ jsxs(Glyph, { children: [/* @__PURE__ */ jsx("rect", {
|
|
53
|
-
height: "14",
|
|
54
|
-
rx: "2",
|
|
55
|
-
width: "14",
|
|
56
|
-
x: "8",
|
|
57
|
-
y: "8"
|
|
58
|
-
}), /* @__PURE__ */ jsx("path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" })] }),
|
|
59
|
-
download: /* @__PURE__ */ jsxs(Glyph, { children: [
|
|
60
|
-
/* @__PURE__ */ jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
|
|
61
|
-
/* @__PURE__ */ jsx("path", { d: "m7 10 5 5 5-5" }),
|
|
62
|
-
/* @__PURE__ */ jsx("path", { d: "M12 15V3" })
|
|
63
|
-
] }),
|
|
64
|
-
external: /* @__PURE__ */ jsxs(Glyph, { children: [
|
|
65
|
-
/* @__PURE__ */ jsx("path", { d: "M15 3h6v6" }),
|
|
66
|
-
/* @__PURE__ */ jsx("path", { d: "M10 14 21 3" }),
|
|
67
|
-
/* @__PURE__ */ jsx("path", { d: "M21 14v5a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5" })
|
|
68
|
-
] }),
|
|
69
|
-
failure: /* @__PURE__ */ jsxs(Glyph, { children: [
|
|
70
|
-
/* @__PURE__ */ jsx("circle", {
|
|
71
|
-
cx: "12",
|
|
72
|
-
cy: "12",
|
|
73
|
-
r: "9"
|
|
74
|
-
}),
|
|
75
|
-
/* @__PURE__ */ jsx("path", { d: "M15 9 9 15" }),
|
|
76
|
-
/* @__PURE__ */ jsx("path", { d: "m9 9 6 6" })
|
|
77
|
-
] }),
|
|
78
|
-
pending: /* @__PURE__ */ jsxs(Glyph, { children: [/* @__PURE__ */ jsx("circle", {
|
|
79
|
-
cx: "12",
|
|
80
|
-
cy: "12",
|
|
81
|
-
r: "9"
|
|
82
|
-
}), /* @__PURE__ */ jsx("path", { d: "M12 7v5l3 2" })] }),
|
|
83
|
-
success: /* @__PURE__ */ jsxs(Glyph, { children: [/* @__PURE__ */ jsx("circle", {
|
|
84
|
-
cx: "12",
|
|
85
|
-
cy: "12",
|
|
86
|
-
r: "9"
|
|
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
|
-
] })
|
|
93
|
-
};
|
|
94
|
-
const IconsContext = createContext(defaultIcons);
|
|
95
|
-
function IconsProvider({ children, icons }) {
|
|
96
|
-
const value = useMemo(() => ({
|
|
97
|
-
...defaultIcons,
|
|
98
|
-
...icons
|
|
99
|
-
}), [icons]);
|
|
100
|
-
return /* @__PURE__ */ jsx(IconsContext.Provider, {
|
|
101
|
-
value,
|
|
102
|
-
children
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
function useIcons() {
|
|
106
|
-
return useContext(IconsContext);
|
|
107
|
-
}
|
|
108
|
-
//#endregion
|
|
109
13
|
//#region src/messages.ts
|
|
110
14
|
var messages_exports = /* @__PURE__ */ __exportAll({
|
|
111
15
|
english: () => english,
|
|
@@ -161,20 +65,34 @@ const english = {
|
|
|
161
65
|
close: "Close",
|
|
162
66
|
retry: "Try again",
|
|
163
67
|
approve: "Approve",
|
|
68
|
+
addRecords: (count) => count === 1 ? "Add 1 record" : `Add ${count} records`,
|
|
164
69
|
decline: "Decline",
|
|
70
|
+
everyRecordConflicts: "Resolve the records above at your provider, then check again.",
|
|
165
71
|
reviewChanges: "Review changes",
|
|
166
72
|
cleanUp: "Remove records",
|
|
167
73
|
checkDns: "Check DNS",
|
|
168
74
|
checkAgain: "Check again",
|
|
169
75
|
moreActions: "More actions",
|
|
170
|
-
moreOptions: "
|
|
76
|
+
moreOptions: "Add an account id",
|
|
171
77
|
useAnotherProvider: "Use a different provider",
|
|
78
|
+
useTokenInstead: "Use an API token instead",
|
|
79
|
+
back: "Back",
|
|
80
|
+
connectedAccount: (provider, account) => `${provider} · ${account}`,
|
|
81
|
+
recordsAdded: (count) => count === 1 ? "1 added" : `${count} added`,
|
|
82
|
+
needsReconnect: "Needs reconnecting",
|
|
83
|
+
disconnectScope: "This connection serves other domains.",
|
|
84
|
+
disconnectThisDomain: "Only this domain",
|
|
85
|
+
disconnectEveryDomain: (count) => `All ${count} domains`,
|
|
172
86
|
copy: "Copy",
|
|
173
87
|
copied: "Copied",
|
|
174
88
|
copyZone: "Copy zone file",
|
|
175
89
|
download: "Download",
|
|
176
90
|
useConnection: (label) => `Use ${label}`,
|
|
177
91
|
chooseZone: "Choose the zone that serves this domain",
|
|
92
|
+
zoneSuggestions: "Zones your connected accounts serve",
|
|
93
|
+
recordsGoTo: (provider, account) => `Records go to ${provider} through ${account}.`,
|
|
94
|
+
notInConnectedAccount: "Not in a connected account.",
|
|
95
|
+
reconnectAccount: (account) => `Reconnect ${account}`,
|
|
178
96
|
getToken: "Where do I find this?",
|
|
179
97
|
loading: "Loading…",
|
|
180
98
|
connecting: "Connecting…",
|
|
@@ -190,13 +108,16 @@ const english = {
|
|
|
190
108
|
observing: "Checking DNS…",
|
|
191
109
|
connectTitle: (provider) => `Connect ${provider}`,
|
|
192
110
|
connectAnyTitle: "Connect a DNS provider",
|
|
193
|
-
|
|
111
|
+
hostDetected: (provider) => `${provider} DNS detected`,
|
|
194
112
|
connectDescription: (domain) => `Authorize DNS changes for ${domain}.`,
|
|
195
113
|
connectedTo: (provider) => `${provider} connected`,
|
|
196
114
|
notConnected: "No DNS provider is connected.",
|
|
115
|
+
administratorConnects: (provider) => `An administrator can connect ${provider}`,
|
|
197
116
|
reconnectRequired: (provider) => `${provider} needs to be reconnected`,
|
|
198
117
|
disconnectTitle: (provider) => `Disconnect ${provider}?`,
|
|
199
|
-
disconnectConsent:
|
|
118
|
+
disconnectConsent: (domain, provider) => `DomainKit stops managing DNS for ${domain} through ${provider}.`,
|
|
119
|
+
disconnectWithCleanup: (count) => count === 1 ? "Remove the 1 record DomainKit added" : `Remove the ${count} records DomainKit added`,
|
|
120
|
+
disconnectKeepsRecords: (provider) => `Records stay in ${provider}.`,
|
|
200
121
|
detachConsent: "This domain is detached from the provider. Existing DNS records are preserved.",
|
|
201
122
|
detached: "Domain detached. DNS records were preserved.",
|
|
202
123
|
reusableConnections: "Connections you already have",
|
|
@@ -232,6 +153,23 @@ const english = {
|
|
|
232
153
|
case "missing": return "The record this plan expected is gone.";
|
|
233
154
|
}
|
|
234
155
|
},
|
|
156
|
+
conflictAdvice: (reason) => {
|
|
157
|
+
switch (reason) {
|
|
158
|
+
case "exclusive-name": return "Remove the record on that name at your provider, then check again.";
|
|
159
|
+
case "cname-collision": return "Remove the other records on that name at your provider, then check again.";
|
|
160
|
+
case "value-mismatch": return "Point that record at this value at your provider, or remove it and check again.";
|
|
161
|
+
case "opaque": return "Remove that record at your provider, then check again.";
|
|
162
|
+
case "missing": return "Check again to build a plan from what is there now.";
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
planStatus: (operation) => {
|
|
166
|
+
switch (operation._tag) {
|
|
167
|
+
case "Create": return "Will add";
|
|
168
|
+
case "Noop": return "Already there";
|
|
169
|
+
case "Delete": return "Will remove";
|
|
170
|
+
case "Conflict": return "In the way";
|
|
171
|
+
}
|
|
172
|
+
},
|
|
235
173
|
attemptStatus: (status) => {
|
|
236
174
|
switch (status) {
|
|
237
175
|
case "planned": return "Awaiting review";
|
|
@@ -313,10 +251,7 @@ const english = {
|
|
|
313
251
|
description: reason.message,
|
|
314
252
|
title: reason.field === void 0 ? "Check what you entered" : `Check the ${humanize(reason.field).toLowerCase()}`
|
|
315
253
|
}),
|
|
316
|
-
unauthenticated: (
|
|
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
|
-
}),
|
|
254
|
+
unauthenticated: () => ({ title: "Token not accepted" }),
|
|
320
255
|
forbidden: (_reason, context) => ({
|
|
321
256
|
description: `The connection needs permission to edit DNS for ${where(context.domain)}.`,
|
|
322
257
|
title: "This connection can't change DNS here"
|
|
@@ -403,42 +338,20 @@ const outcome = (error, catalog, context = {}) => {
|
|
|
403
338
|
/** The same failure as one sentence, for a host that renders text rather than a card. */
|
|
404
339
|
const failure = (error, catalog, context = {}) => {
|
|
405
340
|
const { description, title } = outcome(error, catalog, context);
|
|
406
|
-
return `${title}. ${description}`;
|
|
341
|
+
return description === void 0 ? `${title}.` : `${title}. ${description}`;
|
|
407
342
|
};
|
|
408
343
|
//#endregion
|
|
409
|
-
//#region src/theme.ts
|
|
410
|
-
var theme_exports = /* @__PURE__ */ __exportAll({ toStyle: () => toStyle });
|
|
411
|
-
const variables = {
|
|
412
|
-
accent: "--domainkit-accent",
|
|
413
|
-
accentContrast: "--domainkit-accent-contrast",
|
|
414
|
-
backdrop: "--domainkit-backdrop",
|
|
415
|
-
background: "--domainkit-background",
|
|
416
|
-
border: "--domainkit-border",
|
|
417
|
-
danger: "--domainkit-danger",
|
|
418
|
-
dangerContrast: "--domainkit-danger-contrast",
|
|
419
|
-
fill: "--domainkit-fill",
|
|
420
|
-
fontFamily: "--domainkit-font-family",
|
|
421
|
-
muted: "--domainkit-muted",
|
|
422
|
-
radius: "--domainkit-radius",
|
|
423
|
-
shadow: "--domainkit-shadow",
|
|
424
|
-
success: "--domainkit-success",
|
|
425
|
-
text: "--domainkit-text",
|
|
426
|
-
warning: "--domainkit-warning"
|
|
427
|
-
};
|
|
428
|
-
function toStyle(theme = {}) {
|
|
429
|
-
return Object.fromEntries(Object.entries(theme).flatMap(([key, value]) => value === void 0 ? [] : [[variables[key], value]]));
|
|
430
|
-
}
|
|
431
|
-
//#endregion
|
|
432
344
|
//#region src/domain-kit.tsx
|
|
433
345
|
var domain_kit_exports = /* @__PURE__ */ __exportAll({
|
|
434
346
|
ReadOnly: () => ReadOnly,
|
|
435
|
-
Root: () => Root
|
|
347
|
+
Root: () => Root,
|
|
436
348
|
useCapabilities: () => useCapabilities,
|
|
437
349
|
useDomainKit: () => useDomainKit,
|
|
350
|
+
useMessages: () => useMessages,
|
|
438
351
|
useReadOnly: () => useReadOnly,
|
|
439
352
|
useTransport: () => useTransport
|
|
440
353
|
});
|
|
441
|
-
const Context
|
|
354
|
+
const Context = createContext(null);
|
|
442
355
|
const navigateInBrowser = (url) => {
|
|
443
356
|
if (typeof window === "undefined") throw new Error("DomainKit provider authorization requires a browser or a `navigate` prop");
|
|
444
357
|
window.location.assign(url);
|
|
@@ -470,70 +383,44 @@ const useStableTransport = (transport) => {
|
|
|
470
383
|
};
|
|
471
384
|
}, [signature]);
|
|
472
385
|
};
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
const subscribe = useCallback((notify) => {
|
|
480
|
-
if (ownerDocument === null || typeof MutationObserver === "undefined") return () => {};
|
|
481
|
-
const observer = new MutationObserver(notify);
|
|
482
|
-
observer.observe(ownerDocument, {
|
|
483
|
-
childList: true,
|
|
484
|
-
subtree: true
|
|
485
|
-
});
|
|
486
|
-
return () => observer.disconnect();
|
|
487
|
-
}, [ownerDocument]);
|
|
488
|
-
const getSnapshot = useCallback(() => resolvePortalContainer(container, ownerDocument), [container, ownerDocument]);
|
|
489
|
-
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
490
|
-
};
|
|
491
|
-
function Root$7({ children, colorScheme = "inherit", icons, marks = {}, messages, navigate = navigateInBrowser, onEvent, portalContainer = null, readOnly = false, revision = 0, theme, transport, ...props }) {
|
|
386
|
+
/**
|
|
387
|
+
* The transport, the catalog, and the two host callbacks every hook below reads. It renders no
|
|
388
|
+
* element of its own: `@domainkit/react` supplies behaviour, state, copy, and accessibility, and
|
|
389
|
+
* the host application supplies the markup.
|
|
390
|
+
*/
|
|
391
|
+
function Root({ children, messages, navigate = navigateInBrowser, onEvent, readOnly = false, revision = 0, transport }) {
|
|
492
392
|
const { capabilities, transport: stable } = useStableTransport(transport);
|
|
493
393
|
const onEventRef = useRef(onEvent);
|
|
494
394
|
useLayoutEffect(() => {
|
|
495
395
|
onEventRef.current = onEvent;
|
|
496
396
|
}, [onEvent]);
|
|
497
|
-
const emit = useCallback((event) => {
|
|
498
|
-
try {
|
|
499
|
-
onEventRef.current?.(event);
|
|
500
|
-
} catch {}
|
|
501
|
-
}, []);
|
|
502
|
-
const themeStyle = toStyle(theme);
|
|
503
|
-
const resolvedPortalContainer = usePortalContainer(portalContainer);
|
|
504
|
-
const portalContainerRef = useMemo(() => ({ current: resolvedPortalContainer }), [resolvedPortalContainer]);
|
|
505
397
|
const value = {
|
|
506
398
|
capabilities,
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
399
|
+
emit: useCallback((event) => {
|
|
400
|
+
try {
|
|
401
|
+
onEventRef.current?.(event);
|
|
402
|
+
} catch {}
|
|
403
|
+
}, []),
|
|
510
404
|
messages: merge(messages),
|
|
511
405
|
navigate,
|
|
512
|
-
portalContainer: portalContainerRef,
|
|
513
406
|
readOnly,
|
|
514
407
|
revision,
|
|
515
|
-
themeStyle,
|
|
516
408
|
transport: stable
|
|
517
409
|
};
|
|
518
|
-
|
|
519
|
-
children,
|
|
520
|
-
"data-color-scheme": colorScheme,
|
|
521
|
-
"data-domainkit-root": "",
|
|
522
|
-
style: themeStyle
|
|
523
|
-
});
|
|
524
|
-
return /* @__PURE__ */ jsx(Context$1.Provider, {
|
|
410
|
+
return /* @__PURE__ */ jsx(Context.Provider, {
|
|
525
411
|
value,
|
|
526
|
-
children
|
|
527
|
-
...icons === void 0 ? {} : { icons },
|
|
528
|
-
children: content
|
|
529
|
-
})
|
|
412
|
+
children
|
|
530
413
|
});
|
|
531
414
|
}
|
|
532
415
|
function useDomainKit() {
|
|
533
|
-
const value = useContext(Context
|
|
534
|
-
if (value === null) throw new Error("DomainKit
|
|
416
|
+
const value = useContext(Context);
|
|
417
|
+
if (value === null) throw new Error("DomainKit hooks must be called inside DomainKit.Root");
|
|
535
418
|
return value;
|
|
536
419
|
}
|
|
420
|
+
/** The catalog `DomainKit.Root` holds, with the host's overrides already merged in. */
|
|
421
|
+
function useMessages() {
|
|
422
|
+
return useDomainKit().messages;
|
|
423
|
+
}
|
|
537
424
|
/** The transport `DomainKit.Root` holds, with the identity it keeps for the whole mount. */
|
|
538
425
|
function useTransport() {
|
|
539
426
|
return useDomainKit().transport;
|
|
@@ -546,13 +433,13 @@ function ReadOnly({ children, value }) {
|
|
|
546
433
|
children
|
|
547
434
|
});
|
|
548
435
|
}
|
|
549
|
-
/** Whether this
|
|
436
|
+
/** Whether this surface may offer controls that change the domain. */
|
|
550
437
|
function useReadOnly() {
|
|
551
438
|
const scoped = useContext(ReadOnlyContext);
|
|
552
439
|
const root = useDomainKit().readOnly;
|
|
553
440
|
return scoped ?? root;
|
|
554
441
|
}
|
|
555
|
-
/** Which capability groups the host's transport declares, for gating a
|
|
442
|
+
/** Which capability groups the host's transport declares, for gating a surface of your own. */
|
|
556
443
|
function useCapabilities() {
|
|
557
444
|
return useDomainKit().capabilities;
|
|
558
445
|
}
|
|
@@ -622,10 +509,42 @@ function useRunner() {
|
|
|
622
509
|
const State$2 = Data.taggedEnum();
|
|
623
510
|
/** Reasons that mean the plan itself is gone: retrying the same step would fail the same way. */
|
|
624
511
|
const needsNewPlan = (error) => error.reason._tag === "Stale" || error.reason._tag === "Expired" || error.reason._tag === "Conflict";
|
|
512
|
+
/** The plan an attempt holds, whatever step it is on; `null` before there is one. */
|
|
513
|
+
const planOf = (state) => {
|
|
514
|
+
switch (state._tag) {
|
|
515
|
+
case "Planned":
|
|
516
|
+
case "Approving":
|
|
517
|
+
case "Applying":
|
|
518
|
+
case "Rejecting":
|
|
519
|
+
case "Rejected":
|
|
520
|
+
case "Applied": return state.plan;
|
|
521
|
+
case "Idle":
|
|
522
|
+
case "Planning":
|
|
523
|
+
case "Failure": return null;
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
/**
|
|
527
|
+
* The plan still awaiting its apply, which is what a row of records reports; `null` once one has
|
|
528
|
+
* landed, because from then on an observation answers instead.
|
|
529
|
+
*/
|
|
530
|
+
const pendingPlan = (state) => {
|
|
531
|
+
switch (state._tag) {
|
|
532
|
+
case "Planned":
|
|
533
|
+
case "Approving":
|
|
534
|
+
case "Applying": return state.plan;
|
|
535
|
+
case "Idle":
|
|
536
|
+
case "Planning":
|
|
537
|
+
case "Applied":
|
|
538
|
+
case "Rejecting":
|
|
539
|
+
case "Rejected":
|
|
540
|
+
case "Failure": return null;
|
|
541
|
+
}
|
|
542
|
+
};
|
|
625
543
|
function useAttempt(options) {
|
|
626
544
|
const { domain, done, group, key, onDone } = options;
|
|
627
545
|
const { emit } = useDomainKit();
|
|
628
|
-
const
|
|
546
|
+
const inherited = useReadOnly();
|
|
547
|
+
const readOnly = options.readOnly ?? inherited;
|
|
629
548
|
const runner = useRunner();
|
|
630
549
|
const [state, setState] = useState(State$2.Idle());
|
|
631
550
|
const held = useRef({
|
|
@@ -658,7 +577,7 @@ function useAttempt(options) {
|
|
|
658
577
|
}));
|
|
659
578
|
}, [domain, emit]);
|
|
660
579
|
const applyWith = useCallback((plan, approval) => {
|
|
661
|
-
if (group === void 0 || held.current.key !== key) return;
|
|
580
|
+
if (readOnly || group === void 0 || held.current.key !== key) return;
|
|
662
581
|
const command = () => {
|
|
663
582
|
setState(State$2.Applying({
|
|
664
583
|
approval,
|
|
@@ -688,9 +607,11 @@ function useAttempt(options) {
|
|
|
688
607
|
key,
|
|
689
608
|
onDone,
|
|
690
609
|
onFailure,
|
|
610
|
+
readOnly,
|
|
691
611
|
runner
|
|
692
612
|
]);
|
|
693
613
|
const buildPlan = useCallback(() => {
|
|
614
|
+
if (readOnly) return;
|
|
694
615
|
const effect = build.current();
|
|
695
616
|
if (effect === null) return;
|
|
696
617
|
const command = () => {
|
|
@@ -716,11 +637,12 @@ function useAttempt(options) {
|
|
|
716
637
|
}, [
|
|
717
638
|
key,
|
|
718
639
|
onFailure,
|
|
640
|
+
readOnly,
|
|
719
641
|
runner
|
|
720
642
|
]);
|
|
721
643
|
const approve = useCallback((operationIds) => {
|
|
722
644
|
const plan = held.current.plan;
|
|
723
|
-
if (group === void 0 || plan === null || held.current.key !== key) return;
|
|
645
|
+
if (readOnly || group === void 0 || plan === null || held.current.key !== key) return;
|
|
724
646
|
const command = () => {
|
|
725
647
|
setState(State$2.Approving({ plan }));
|
|
726
648
|
runner.run(group.approve({
|
|
@@ -749,11 +671,12 @@ function useAttempt(options) {
|
|
|
749
671
|
group,
|
|
750
672
|
key,
|
|
751
673
|
onFailure,
|
|
674
|
+
readOnly,
|
|
752
675
|
runner
|
|
753
676
|
]);
|
|
754
677
|
const reject = useCallback((reason) => {
|
|
755
678
|
const plan = held.current.plan;
|
|
756
|
-
if (group === void 0 || plan === null || held.current.key !== key) return;
|
|
679
|
+
if (readOnly || group === void 0 || plan === null || held.current.key !== key) return;
|
|
757
680
|
const command = () => {
|
|
758
681
|
setState(State$2.Rejecting({ plan }));
|
|
759
682
|
runner.run(group.reject({
|
|
@@ -784,11 +707,12 @@ function useAttempt(options) {
|
|
|
784
707
|
group,
|
|
785
708
|
key,
|
|
786
709
|
onFailure,
|
|
710
|
+
readOnly,
|
|
787
711
|
runner
|
|
788
712
|
]);
|
|
789
713
|
const apply = useCallback(() => {
|
|
790
714
|
const { approval, plan } = held.current;
|
|
791
|
-
if (approval === null || plan === null) return;
|
|
715
|
+
if (readOnly || approval === null || plan === null) return;
|
|
792
716
|
applyWith(plan, approval);
|
|
793
717
|
}, [applyWith]);
|
|
794
718
|
const retry = useCallback(() => {
|
|
@@ -831,690 +755,18 @@ function useAttempt(options) {
|
|
|
831
755
|
};
|
|
832
756
|
}
|
|
833
757
|
//#endregion
|
|
834
|
-
//#region src/
|
|
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
|
|
983
|
-
//#region src/records.tsx
|
|
984
|
-
var records_exports = /* @__PURE__ */ __exportAll({
|
|
985
|
-
Body: () => Body$1,
|
|
986
|
-
Caption: () => Caption,
|
|
987
|
-
Card: () => Card$1,
|
|
988
|
-
CardContent: () => CardContent,
|
|
989
|
-
CardHeader: () => CardHeader,
|
|
990
|
-
CardTitle: () => CardTitle,
|
|
991
|
-
Cell: () => Cell,
|
|
992
|
-
CopyValue: () => CopyValue,
|
|
993
|
-
Footer: () => Footer,
|
|
994
|
-
Head: () => Head,
|
|
995
|
-
Header: () => Header,
|
|
996
|
-
Purpose: () => Purpose,
|
|
997
|
-
Root: () => Root$5,
|
|
998
|
-
Row: () => Row,
|
|
999
|
-
Status: () => Status$5,
|
|
1000
|
-
Table: () => Table,
|
|
1001
|
-
Value: () => Value,
|
|
1002
|
-
ZoneFile: () => ZoneFile,
|
|
1003
|
-
copyText: () => copyText,
|
|
1004
|
-
downloadZoneFile: () => downloadZoneFile,
|
|
1005
|
-
identity: () => identity,
|
|
1006
|
-
requirementsKey: () => requirementsKey,
|
|
1007
|
-
statusOf: () => statusOf,
|
|
1008
|
-
toZoneFile: () => toZoneFile,
|
|
1009
|
-
useCopy: () => useCopy
|
|
1010
|
-
});
|
|
1011
|
-
const absolute = (value) => value.endsWith(".") ? value : `${value}.`;
|
|
1012
|
-
const textEncoder = new TextEncoder();
|
|
1013
|
-
const escapeTxtCharacter = (character) => {
|
|
1014
|
-
if (character === "\"" || character === "\\") return `\\${character}`;
|
|
1015
|
-
const codePoint = character.codePointAt(0);
|
|
1016
|
-
if (codePoint !== void 0 && (codePoint < 32 || codePoint === 127)) return `\\${codePoint.toString().padStart(3, "0")}`;
|
|
1017
|
-
return character;
|
|
1018
|
-
};
|
|
1019
|
-
/** TXT strings are capped at 255 bytes each, so long values are split and quoted per chunk. */
|
|
1020
|
-
const txtValue = (value) => {
|
|
1021
|
-
const chunks = [];
|
|
1022
|
-
let chunk = "";
|
|
1023
|
-
let byteLength = 0;
|
|
1024
|
-
for (const character of value) {
|
|
1025
|
-
const characterByteLength = textEncoder.encode(character).byteLength;
|
|
1026
|
-
if (byteLength + characterByteLength > 255) {
|
|
1027
|
-
chunks.push(chunk);
|
|
1028
|
-
chunk = "";
|
|
1029
|
-
byteLength = 0;
|
|
1030
|
-
}
|
|
1031
|
-
chunk += escapeTxtCharacter(character);
|
|
1032
|
-
byteLength += characterByteLength;
|
|
1033
|
-
}
|
|
1034
|
-
chunks.push(chunk);
|
|
1035
|
-
return chunks.map((part) => `"${part}"`).join(" ");
|
|
1036
|
-
};
|
|
1037
|
-
/** The RR type as a zone file spells it. The tag and the type name are the same string. */
|
|
1038
|
-
const rrType = (record) => record._tag;
|
|
1039
|
-
const zoneData = (record) => {
|
|
1040
|
-
switch (record._tag) {
|
|
1041
|
-
case "A":
|
|
1042
|
-
case "AAAA": return record.address;
|
|
1043
|
-
case "CNAME": return absolute(record.target);
|
|
1044
|
-
case "NS": return absolute(record.nameserver);
|
|
1045
|
-
case "TXT": return txtValue(record.value);
|
|
1046
|
-
case "MX": return `${record.priority} ${absolute(record.exchange)}`;
|
|
1047
|
-
case "CAA": return `${record.flags} ${record.tag} "${record.value}"`;
|
|
1048
|
-
case "SRV": return `${record.priority} ${record.weight} ${record.port} ${absolute(record.target)}`;
|
|
1049
|
-
}
|
|
1050
|
-
};
|
|
1051
|
-
const toZoneFile = (records) => `${records.map((record) => `${absolute(record.name)}${record.ttl === null ? "" : ` ${record.ttl}`} IN ${rrType(record)} ${zoneData(record)}`).join("\n")}\n`;
|
|
1052
|
-
const downloadText = (filename, contents) => {
|
|
1053
|
-
if (typeof document === "undefined") return;
|
|
1054
|
-
const blob = new Blob([contents], { type: "text/plain;charset=utf-8" });
|
|
1055
|
-
const url = URL.createObjectURL(blob);
|
|
1056
|
-
const anchor = document.createElement("a");
|
|
1057
|
-
anchor.download = filename;
|
|
1058
|
-
anchor.href = url;
|
|
1059
|
-
document.body.append(anchor);
|
|
1060
|
-
anchor.click();
|
|
1061
|
-
anchor.remove();
|
|
1062
|
-
URL.revokeObjectURL(url);
|
|
1063
|
-
};
|
|
1064
|
-
const downloadZoneFile = (domain, records) => downloadText(`${domain}.txt`, toZoneFile(records));
|
|
1065
|
-
const copyText = (value) => {
|
|
1066
|
-
if (typeof navigator === "undefined" || navigator.clipboard === void 0) return Promise.resolve(false);
|
|
1067
|
-
return navigator.clipboard.writeText(value).then(() => true, () => false);
|
|
1068
|
-
};
|
|
1069
|
-
function useCopy(value, resetAfter = 2e3) {
|
|
1070
|
-
const [copied, setCopied] = useState(false);
|
|
1071
|
-
const reset = useRef(void 0);
|
|
1072
|
-
useEffect(() => () => clearTimeout(reset.current), []);
|
|
1073
|
-
return {
|
|
1074
|
-
copied,
|
|
1075
|
-
copy: () => {
|
|
1076
|
-
copyText(value).then((ok) => {
|
|
1077
|
-
if (!ok) return;
|
|
1078
|
-
setCopied(true);
|
|
1079
|
-
clearTimeout(reset.current);
|
|
1080
|
-
reset.current = setTimeout(() => setCopied(false), resetAfter);
|
|
1081
|
-
});
|
|
1082
|
-
}
|
|
1083
|
-
};
|
|
1084
|
-
}
|
|
1085
|
-
function CopyValue({ value, ...props }) {
|
|
1086
|
-
const { messages } = useDomainKit();
|
|
1087
|
-
const icons = useIcons();
|
|
1088
|
-
const { copied, copy } = useCopy(value);
|
|
1089
|
-
return usePart("span", props, { copied }, {
|
|
1090
|
-
children: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("code", { children: value }), /* @__PURE__ */ jsxs("button", {
|
|
1091
|
-
"aria-label": `${copied ? messages.copied : messages.copy} ${value}`,
|
|
1092
|
-
"data-domainkit-part": "copy-action",
|
|
1093
|
-
onClick: copy,
|
|
1094
|
-
type: "button",
|
|
1095
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
1096
|
-
"aria-hidden": "true",
|
|
1097
|
-
"data-domainkit-part": "copy-glyph",
|
|
1098
|
-
"data-icon": "",
|
|
1099
|
-
"data-state": "idle",
|
|
1100
|
-
children: icons.copy
|
|
1101
|
-
}), /* @__PURE__ */ jsx("span", {
|
|
1102
|
-
"aria-hidden": "true",
|
|
1103
|
-
"data-domainkit-part": "copy-glyph",
|
|
1104
|
-
"data-icon": "",
|
|
1105
|
-
"data-state": "done",
|
|
1106
|
-
children: icons.copied
|
|
1107
|
-
})]
|
|
1108
|
-
})] }),
|
|
1109
|
-
"data-copied": copied ? "" : void 0,
|
|
1110
|
-
"data-domainkit-part": "copy-value"
|
|
1111
|
-
});
|
|
1112
|
-
}
|
|
1113
|
-
function ZoneFile({ domain, records, ...props }) {
|
|
1114
|
-
const { messages } = useDomainKit();
|
|
1115
|
-
const icons = useIcons();
|
|
1116
|
-
const { copied, copy } = useCopy(toZoneFile(records));
|
|
1117
|
-
return usePart("div", props, { count: records.length }, {
|
|
1118
|
-
children: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("button", {
|
|
1119
|
-
"aria-label": copied ? messages.copied : messages.copyZone,
|
|
1120
|
-
"data-domainkit-part": "zone-copy",
|
|
1121
|
-
onClick: copy,
|
|
1122
|
-
type: "button",
|
|
1123
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
1124
|
-
"aria-hidden": "true",
|
|
1125
|
-
"data-icon": "inline-start",
|
|
1126
|
-
children: copied ? icons.copied : icons.copy
|
|
1127
|
-
}), copied ? messages.copied : messages.copyZone]
|
|
1128
|
-
}), /* @__PURE__ */ jsxs("button", {
|
|
1129
|
-
"aria-label": messages.download,
|
|
1130
|
-
"data-domainkit-part": "zone-download",
|
|
1131
|
-
onClick: () => downloadZoneFile(domain, records),
|
|
1132
|
-
type: "button",
|
|
1133
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
1134
|
-
"aria-hidden": "true",
|
|
1135
|
-
"data-icon": "inline-start",
|
|
1136
|
-
children: icons.download
|
|
1137
|
-
}), messages.download]
|
|
1138
|
-
})] }),
|
|
1139
|
-
"data-domainkit-part": "zone-file"
|
|
1140
|
-
});
|
|
1141
|
-
}
|
|
1142
|
-
/** The readiness this record reached, or `null` when nothing has observed it. */
|
|
1143
|
-
const statusOf = (record, readiness) => readiness?.requirements.find((requirement) => DnsRecord.equals(requirement.record, record))?.status ?? null;
|
|
1144
|
-
function Status$5({ status, ...props }) {
|
|
1145
|
-
const { messages } = useDomainKit();
|
|
1146
|
-
const icons = useIcons();
|
|
1147
|
-
const glyph = status === "satisfied" ? icons.success : status === "unknown" ? icons.pending : icons.failure;
|
|
1148
|
-
return usePart("span", props, { status }, {
|
|
1149
|
-
children: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
|
|
1150
|
-
"aria-hidden": "true",
|
|
1151
|
-
"data-icon": "inline-start",
|
|
1152
|
-
children: glyph
|
|
1153
|
-
}), messages.requirementStatus(status)] }),
|
|
1154
|
-
"data-domainkit-part": "record-status",
|
|
1155
|
-
"data-status": status
|
|
1156
|
-
});
|
|
1157
|
-
}
|
|
1158
|
-
const Header = leafPart("thead", "records-header");
|
|
1159
|
-
const Body$1 = leafPart("tbody", "records-body");
|
|
1160
|
-
const Footer = leafPart("tfoot", "records-footer");
|
|
1161
|
-
const Row = leafPart("tr", "records-row");
|
|
1162
|
-
const Head = leafPart("th", "records-head");
|
|
1163
|
-
const Cell = leafPart("td", "records-cell");
|
|
1164
|
-
const Caption = leafPart("caption", "records-caption");
|
|
1165
|
-
const Value = leafPart("span", "record-value");
|
|
1166
|
-
const Purpose = leafPart("span", "record-purpose");
|
|
1167
|
-
const CardHeader = leafPart("div", "record-card-head");
|
|
1168
|
-
const CardTitle = leafPart("strong", "record-card-title");
|
|
1169
|
-
const CardContent = leafPart("dl", "record-card-content");
|
|
1170
|
-
function Root$5({ count = 0, ...props }) {
|
|
1171
|
-
return /* @__PURE__ */ jsx("div", {
|
|
1172
|
-
"data-domainkit-part": "records-panel",
|
|
1173
|
-
children: usePart("table", props, { count }, { "data-domainkit-part": "records-table" })
|
|
1174
|
-
});
|
|
1175
|
-
}
|
|
1176
|
-
/** A stable React key for a requirement: type, name, and data identify a record. */
|
|
1177
|
-
const identity = (record) => [
|
|
1178
|
-
rrType(record),
|
|
1179
|
-
record.name,
|
|
1180
|
-
DnsRecord.data(record)
|
|
1181
|
-
].join(":");
|
|
1182
|
-
/**
|
|
1183
|
-
* A requirement set keyed by everything it carries, for memos that decide whether to re-send it.
|
|
1184
|
-
* `identity` is deliberately narrower: it answers "is this the same record" for a React key, while
|
|
1185
|
-
* a plan and an observation are built from `ttl`, `policy`, and `purpose` as well. `policy` in
|
|
1186
|
-
* particular decides whether a name may hold anything else, so readiness turns on it.
|
|
1187
|
-
*/
|
|
1188
|
-
const requirementsKey = (records) => JSON.stringify(records);
|
|
1189
|
-
function Card$1({ readiness, record, ...props }) {
|
|
1190
|
-
const { messages } = useDomainKit();
|
|
1191
|
-
const status = statusOf(record, readiness);
|
|
1192
|
-
return usePart("section", props, { record: identity(record) }, {
|
|
1193
|
-
children: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(CardHeader, { children: [/* @__PURE__ */ jsx(CardTitle, { children: messages.recordType(record) }), status === null ? null : /* @__PURE__ */ jsx(Status$5, { status })] }), /* @__PURE__ */ jsxs(CardContent, { children: [
|
|
1194
|
-
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", { children: messages.headingName }), /* @__PURE__ */ jsx("dd", { children: /* @__PURE__ */ jsx(CopyValue, { value: record.name }) })] }),
|
|
1195
|
-
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", { children: messages.headingValue }), /* @__PURE__ */ jsx("dd", { children: /* @__PURE__ */ jsx(CopyValue, { value: DnsRecord.data(record) }) })] }),
|
|
1196
|
-
record.purpose === void 0 ? null : /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", { children: messages.headingPurpose }), /* @__PURE__ */ jsx("dd", { children: /* @__PURE__ */ jsx(Purpose, { children: record.purpose }) })] })
|
|
1197
|
-
] })] }),
|
|
1198
|
-
"data-domainkit-part": "record-card"
|
|
1199
|
-
});
|
|
1200
|
-
}
|
|
1201
|
-
/** The default records presentation: one row per requirement, with readiness when there is any. */
|
|
1202
|
-
function Table({ caption, readiness, records, ...props }) {
|
|
1203
|
-
const { messages } = useDomainKit();
|
|
1204
|
-
const withStatus = readiness !== null && readiness !== void 0;
|
|
1205
|
-
return /* @__PURE__ */ jsxs(Root$5, {
|
|
1206
|
-
count: records.length,
|
|
1207
|
-
...props,
|
|
1208
|
-
children: [
|
|
1209
|
-
caption === void 0 ? null : /* @__PURE__ */ jsx(Caption, { children: caption }),
|
|
1210
|
-
/* @__PURE__ */ jsx(Header, { children: /* @__PURE__ */ jsxs(Row, { children: [
|
|
1211
|
-
/* @__PURE__ */ jsx(Head, {
|
|
1212
|
-
scope: "col",
|
|
1213
|
-
children: messages.headingType
|
|
1214
|
-
}),
|
|
1215
|
-
/* @__PURE__ */ jsx(Head, {
|
|
1216
|
-
scope: "col",
|
|
1217
|
-
children: messages.headingName
|
|
1218
|
-
}),
|
|
1219
|
-
/* @__PURE__ */ jsx(Head, {
|
|
1220
|
-
scope: "col",
|
|
1221
|
-
children: messages.headingValue
|
|
1222
|
-
}),
|
|
1223
|
-
withStatus ? /* @__PURE__ */ jsx(Head, {
|
|
1224
|
-
"data-column": "status",
|
|
1225
|
-
scope: "col",
|
|
1226
|
-
children: messages.headingStatus
|
|
1227
|
-
}) : null
|
|
1228
|
-
] }) }),
|
|
1229
|
-
/* @__PURE__ */ jsx(Body$1, { children: records.map((record) => {
|
|
1230
|
-
const status = statusOf(record, readiness);
|
|
1231
|
-
return /* @__PURE__ */ jsxs(Row, { children: [
|
|
1232
|
-
/* @__PURE__ */ jsx(Cell, { children: messages.recordType(record) }),
|
|
1233
|
-
/* @__PURE__ */ jsx(Cell, { children: /* @__PURE__ */ jsx(CopyValue, { value: record.name }) }),
|
|
1234
|
-
/* @__PURE__ */ jsx(Cell, { children: /* @__PURE__ */ jsxs(Value, { children: [/* @__PURE__ */ jsx(CopyValue, { value: DnsRecord.data(record) }), record.purpose === void 0 ? null : /* @__PURE__ */ jsx(Purpose, { children: record.purpose })] }) }),
|
|
1235
|
-
withStatus ? /* @__PURE__ */ jsx(Cell, {
|
|
1236
|
-
"data-column": "status",
|
|
1237
|
-
children: status === null ? null : /* @__PURE__ */ jsx(Status$5, { status })
|
|
1238
|
-
}) : null
|
|
1239
|
-
] }, identity(record));
|
|
1240
|
-
}) })
|
|
1241
|
-
]
|
|
1242
|
-
});
|
|
1243
|
-
}
|
|
1244
|
-
//#endregion
|
|
1245
|
-
//#region src/operations.tsx
|
|
1246
|
-
var operations_exports = /* @__PURE__ */ __exportAll({
|
|
1247
|
-
Item: () => Item,
|
|
1248
|
-
Kind: () => Kind,
|
|
1249
|
-
List: () => List,
|
|
1250
|
-
Name: () => Name,
|
|
1251
|
-
Reason: () => Reason$1,
|
|
1252
|
-
Record: () => Record,
|
|
1253
|
-
Root: () => Root$4,
|
|
1254
|
-
Type: () => Type
|
|
1255
|
-
});
|
|
1256
|
-
const Kind = leafPart("span", "operation-kind");
|
|
1257
|
-
const Type = leafPart("strong", "operation-type");
|
|
1258
|
-
const Record = leafPart("span", "operation-record");
|
|
1259
|
-
const Name = leafPart("span", "operation-name");
|
|
1260
|
-
const Reason$1 = leafPart("span", "operation-reason");
|
|
1261
|
-
function Item({ operation, ...props }) {
|
|
1262
|
-
const { messages } = useDomainKit();
|
|
1263
|
-
return usePart("li", props, { operation: operation._tag }, {
|
|
1264
|
-
children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1265
|
-
/* @__PURE__ */ jsx(Kind, { children: messages.operation(operation) }),
|
|
1266
|
-
" ",
|
|
1267
|
-
/* @__PURE__ */ jsx(Type, { children: messages.recordType(operation.record) }),
|
|
1268
|
-
" ",
|
|
1269
|
-
/* @__PURE__ */ jsxs(Record, { children: [/* @__PURE__ */ jsx(Name, { children: operation.record.name }), /* @__PURE__ */ jsx(CopyValue, { value: DnsRecord.data(operation.record) })] }),
|
|
1270
|
-
operation._tag === "Conflict" ? /* @__PURE__ */ jsx(Reason$1, { children: messages.conflictReason(operation.reason) }) : null
|
|
1271
|
-
] }),
|
|
1272
|
-
"data-domainkit-part": "operation-item",
|
|
1273
|
-
"data-operation": operation._tag
|
|
1274
|
-
});
|
|
1275
|
-
}
|
|
1276
|
-
function Root$4({ count = 0, kind, ...props }) {
|
|
1277
|
-
return usePart("ul", props, {
|
|
1278
|
-
count,
|
|
1279
|
-
kind
|
|
1280
|
-
}, {
|
|
1281
|
-
"data-domainkit-part": kind === "provisioning" ? "plan-operations" : "cleanup-operations",
|
|
1282
|
-
"data-kind": kind
|
|
1283
|
-
});
|
|
1284
|
-
}
|
|
1285
|
-
/** Every operation the plan holds, in plan order, so a customer reviews exactly what will run. */
|
|
1286
|
-
function List({ plan, ...props }) {
|
|
1287
|
-
return /* @__PURE__ */ jsx(Root$4, {
|
|
1288
|
-
count: plan.operations.length,
|
|
1289
|
-
kind: plan.kind,
|
|
1290
|
-
...props,
|
|
1291
|
-
children: plan.operations.map((operation) => /* @__PURE__ */ jsx(Item, { operation }, operation.id))
|
|
1292
|
-
});
|
|
1293
|
-
}
|
|
1294
|
-
//#endregion
|
|
1295
|
-
//#region src/review.tsx
|
|
1296
|
-
/**
|
|
1297
|
-
* The review surface both `Provision` and `Cleanup` render: what the plan will do, the Approve
|
|
1298
|
-
* and Decline actions D35 defines, and the outcome. `kind` picks the wording; nothing else about
|
|
1299
|
-
* the two flows differs on screen.
|
|
1300
|
-
*/
|
|
1301
|
-
const busy = (status) => status === "Planning" || status === "Approving" || status === "Applying" || status === "Rejecting";
|
|
1302
|
-
/** What the flow is doing right now, or what it finished doing. */
|
|
1303
|
-
function Status$4({ controller, kind, ...props }) {
|
|
1304
|
-
const { messages } = useDomainKit();
|
|
1305
|
-
const state = controller.state;
|
|
1306
|
-
const text = () => {
|
|
1307
|
-
switch (state._tag) {
|
|
1308
|
-
case "Idle": return "";
|
|
1309
|
-
case "Planning": return kind === "provisioning" ? messages.planning : messages.planningCleanup;
|
|
1310
|
-
case "Planned": return kind === "provisioning" ? messages.planConsent : messages.cleanupConsent;
|
|
1311
|
-
case "Approving": return messages.approving;
|
|
1312
|
-
case "Applying": return kind === "provisioning" ? messages.applying : messages.cleaning;
|
|
1313
|
-
case "Rejecting": return messages.rejecting;
|
|
1314
|
-
case "Rejected": return messages.declinedBy(state.attempt.rejection?.actorId ?? "");
|
|
1315
|
-
case "Applied": return kind === "provisioning" ? messages.applied(state.receipt) : messages.cleaned(state.receipt);
|
|
1316
|
-
case "Failure": return "";
|
|
1317
|
-
}
|
|
1318
|
-
};
|
|
1319
|
-
return usePart("p", props, { status: state._tag }, {
|
|
1320
|
-
children: text(),
|
|
1321
|
-
"data-domainkit-part": kind === "provisioning" ? "provisioning-status" : "cleanup-status",
|
|
1322
|
-
"data-state": state._tag,
|
|
1323
|
-
role: busy(state._tag) ? "status" : void 0
|
|
1324
|
-
});
|
|
1325
|
-
}
|
|
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 }) {
|
|
1331
|
-
const { messages } = useDomainKit();
|
|
1332
|
-
const readOnly = useReadOnly();
|
|
1333
|
-
const state = controller.state;
|
|
1334
|
-
const partial = state._tag === "Applied" && state.receipt.status === "partial";
|
|
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
|
-
})
|
|
1354
|
-
});
|
|
1355
|
-
}
|
|
1356
|
-
/**
|
|
1357
|
-
* Approve and Decline. Approve authorizes the digest and applies it; Decline records the refusal
|
|
1358
|
-
* and ends the attempt. Neither renders until there is a plan to act on.
|
|
1359
|
-
*/
|
|
1360
|
-
function Actions$2({ controller, kind, ...props }) {
|
|
1361
|
-
const { messages } = useDomainKit();
|
|
1362
|
-
const readOnly = useReadOnly();
|
|
1363
|
-
const state = controller.state;
|
|
1364
|
-
const plan = state._tag === "Planned" ? state.plan : null;
|
|
1365
|
-
const running = busy(state._tag);
|
|
1366
|
-
const element = usePart("div", props, { status: state._tag }, {
|
|
1367
|
-
children: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("button", {
|
|
1368
|
-
"data-domainkit-part": kind === "provisioning" ? "plan-apply" : "cleanup-apply",
|
|
1369
|
-
disabled: running || plan === null || plan.operations.length === 0,
|
|
1370
|
-
onClick: () => controller.approve(),
|
|
1371
|
-
type: "button",
|
|
1372
|
-
children: messages.approve
|
|
1373
|
-
}), /* @__PURE__ */ jsx("button", {
|
|
1374
|
-
"data-domainkit-part": "plan-decline",
|
|
1375
|
-
disabled: running || plan === null,
|
|
1376
|
-
onClick: () => controller.reject(),
|
|
1377
|
-
type: "button",
|
|
1378
|
-
children: messages.decline
|
|
1379
|
-
})] }),
|
|
1380
|
-
"data-domainkit-part": "review-actions"
|
|
1381
|
-
});
|
|
1382
|
-
if (readOnly) return null;
|
|
1383
|
-
return plan === null && !running ? null : element;
|
|
1384
|
-
}
|
|
1385
|
-
/** The operations under review, or the reason there is nothing to review. */
|
|
1386
|
-
function Body({ controller, kind }) {
|
|
1387
|
-
const { messages } = useDomainKit();
|
|
1388
|
-
const state = controller.state;
|
|
1389
|
-
const plan = state._tag === "Planned" || state._tag === "Approving" || state._tag === "Applying" || state._tag === "Rejecting" || state._tag === "Rejected" ? state.plan : state._tag === "Applied" ? state.plan : null;
|
|
1390
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
1391
|
-
"data-domainkit-part": "dialog-body",
|
|
1392
|
-
children: [
|
|
1393
|
-
/* @__PURE__ */ jsx(Status$4, {
|
|
1394
|
-
controller,
|
|
1395
|
-
kind
|
|
1396
|
-
}),
|
|
1397
|
-
plan === null ? null : plan.operations.length === 0 ? /* @__PURE__ */ jsx("p", {
|
|
1398
|
-
"data-domainkit-part": "plan-empty",
|
|
1399
|
-
children: messages.noChanges
|
|
1400
|
-
}) : /* @__PURE__ */ jsx(List, { plan }),
|
|
1401
|
-
state._tag === "Rejected" && state.attempt.rejection?.reason != null ? /* @__PURE__ */ jsx("p", {
|
|
1402
|
-
"data-domainkit-part": "plan-decline-reason",
|
|
1403
|
-
children: messages.declineReason(state.attempt.rejection.reason)
|
|
1404
|
-
}) : null,
|
|
1405
|
-
/* @__PURE__ */ jsx(Outcome$4, {
|
|
1406
|
-
controller,
|
|
1407
|
-
kind
|
|
1408
|
-
})
|
|
1409
|
-
]
|
|
1410
|
-
});
|
|
1411
|
-
}
|
|
1412
|
-
/** The review dialog. `render` swaps the surface; the contents stay the flow's. */
|
|
1413
|
-
function Dialog$4({ children, controller, kind, onOpenChange, open, render, trigger }) {
|
|
1414
|
-
const { colorScheme, messages, portalContainer, themeStyle } = useDomainKit();
|
|
1415
|
-
const readOnly = useReadOnly();
|
|
1416
|
-
const [uncontrolled, setUncontrolled] = useState(false);
|
|
1417
|
-
const isOpen = open ?? uncontrolled;
|
|
1418
|
-
const setOpen = (next) => {
|
|
1419
|
-
if (open === void 0) setUncontrolled(next);
|
|
1420
|
-
onOpenChange?.(next);
|
|
1421
|
-
};
|
|
1422
|
-
const running = busy(controller.state._tag);
|
|
1423
|
-
const body = children ?? /* @__PURE__ */ jsx(Body, {
|
|
1424
|
-
controller,
|
|
1425
|
-
kind
|
|
1426
|
-
});
|
|
1427
|
-
const title = kind === "provisioning" ? messages.planTitle : messages.cleanupTitle;
|
|
1428
|
-
const label = trigger ?? (kind === "provisioning" ? messages.reviewChanges : messages.cleanUp);
|
|
1429
|
-
if (readOnly) return /* @__PURE__ */ jsx(Fragment, {});
|
|
1430
|
-
if (render !== void 0) return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("button", {
|
|
1431
|
-
"data-domainkit-part": kind === "provisioning" ? "plan-trigger" : "cleanup-trigger",
|
|
1432
|
-
onClick: () => {
|
|
1433
|
-
setOpen(true);
|
|
1434
|
-
controller.plan();
|
|
1435
|
-
},
|
|
1436
|
-
type: "button",
|
|
1437
|
-
children: label
|
|
1438
|
-
}), render({
|
|
1439
|
-
children: body,
|
|
1440
|
-
open: isOpen
|
|
1441
|
-
})] });
|
|
1442
|
-
return /* @__PURE__ */ jsxs(Dialog.Root, {
|
|
1443
|
-
onOpenChange: (next, details) => {
|
|
1444
|
-
if (!next && running) {
|
|
1445
|
-
details.cancel();
|
|
1446
|
-
return;
|
|
1447
|
-
}
|
|
1448
|
-
setOpen(next);
|
|
1449
|
-
},
|
|
1450
|
-
open: isOpen,
|
|
1451
|
-
children: [/* @__PURE__ */ jsx(Dialog.Trigger, {
|
|
1452
|
-
"data-domainkit-part": kind === "provisioning" ? "plan-trigger" : "cleanup-trigger",
|
|
1453
|
-
onClick: controller.plan,
|
|
1454
|
-
children: label
|
|
1455
|
-
}), /* @__PURE__ */ jsxs(Dialog.Portal, {
|
|
1456
|
-
container: portalContainer,
|
|
1457
|
-
children: [/* @__PURE__ */ jsx(Dialog.Backdrop, {
|
|
1458
|
-
"data-color-scheme": colorScheme,
|
|
1459
|
-
"data-domainkit-part": "dialog-backdrop",
|
|
1460
|
-
"data-domainkit-root": "",
|
|
1461
|
-
style: themeStyle
|
|
1462
|
-
}), /* @__PURE__ */ jsxs(Dialog.Popup, {
|
|
1463
|
-
"data-color-scheme": colorScheme,
|
|
1464
|
-
"data-domainkit-part": kind === "provisioning" ? "plan-dialog" : "cleanup-dialog",
|
|
1465
|
-
"data-domainkit-root": "",
|
|
1466
|
-
style: themeStyle,
|
|
1467
|
-
children: [
|
|
1468
|
-
/* @__PURE__ */ jsxs("div", {
|
|
1469
|
-
"data-domainkit-part": "dialog-header",
|
|
1470
|
-
children: [/* @__PURE__ */ jsxs("div", {
|
|
1471
|
-
"data-domainkit-part": "dialog-heading",
|
|
1472
|
-
children: [/* @__PURE__ */ jsx(Dialog.Title, {
|
|
1473
|
-
"data-domainkit-part": "dialog-title",
|
|
1474
|
-
children: title
|
|
1475
|
-
}), /* @__PURE__ */ jsx(Dialog.Description, {
|
|
1476
|
-
"data-domainkit-part": "dialog-description",
|
|
1477
|
-
children: kind === "provisioning" ? messages.planConsent : messages.cleanupConsent
|
|
1478
|
-
})]
|
|
1479
|
-
}), running ? null : /* @__PURE__ */ jsx(Dialog.Close, {
|
|
1480
|
-
"aria-label": messages.close,
|
|
1481
|
-
"data-domainkit-part": "dialog-close",
|
|
1482
|
-
children: "×"
|
|
1483
|
-
})]
|
|
1484
|
-
}),
|
|
1485
|
-
body,
|
|
1486
|
-
/* @__PURE__ */ jsxs("div", {
|
|
1487
|
-
"data-domainkit-part": "dialog-footer",
|
|
1488
|
-
children: [/* @__PURE__ */ jsx(Actions$2, {
|
|
1489
|
-
controller,
|
|
1490
|
-
kind
|
|
1491
|
-
}), running ? null : /* @__PURE__ */ jsx(Dialog.Close, {
|
|
1492
|
-
"data-domainkit-part": "dialog-cancel",
|
|
1493
|
-
children: messages.cancel
|
|
1494
|
-
})]
|
|
1495
|
-
})
|
|
1496
|
-
]
|
|
1497
|
-
})]
|
|
1498
|
-
})]
|
|
1499
|
-
});
|
|
1500
|
-
}
|
|
1501
|
-
//#endregion
|
|
1502
|
-
//#region src/cleanup.tsx
|
|
758
|
+
//#region src/cleanup.ts
|
|
1503
759
|
var cleanup_exports = /* @__PURE__ */ __exportAll({
|
|
1504
|
-
Actions: () => Actions$1,
|
|
1505
|
-
Dialog: () => Dialog$3,
|
|
1506
|
-
Flow: () => Flow$3,
|
|
1507
|
-
Outcome: () => Outcome$3,
|
|
1508
|
-
Root: () => Root$3,
|
|
1509
760
|
State: () => State$2,
|
|
1510
|
-
|
|
761
|
+
pendingPlan: () => pendingPlan,
|
|
762
|
+
planOf: () => planOf,
|
|
1511
763
|
useController: () => useController$3
|
|
1512
764
|
});
|
|
1513
765
|
/**
|
|
1514
766
|
* Cleanup is bound to a receipt: it removes only what an apply proved DomainKit created. When the
|
|
1515
767
|
* host does not name one, the controller reads the domain's latest from the snapshot.
|
|
1516
768
|
*/
|
|
1517
|
-
function useController$3({ domain, onCleaned, receiptId }) {
|
|
769
|
+
function useController$3({ domain, onCleaned, readOnly, receiptId }) {
|
|
1518
770
|
const { transport } = useDomainKit();
|
|
1519
771
|
const group = transport.cleanup;
|
|
1520
772
|
const connection = transport.connection;
|
|
@@ -1527,6 +779,7 @@ function useController$3({ domain, onCleaned, receiptId }) {
|
|
|
1527
779
|
}),
|
|
1528
780
|
group,
|
|
1529
781
|
onDone: onCleaned,
|
|
782
|
+
readOnly,
|
|
1530
783
|
plan: useCallback(() => {
|
|
1531
784
|
if (group === void 0) return null;
|
|
1532
785
|
if (receiptId !== void 0) return group.plan(receiptId);
|
|
@@ -1543,91 +796,327 @@ function useController$3({ domain, onCleaned, receiptId }) {
|
|
|
1543
796
|
])
|
|
1544
797
|
});
|
|
1545
798
|
}
|
|
1546
|
-
function Root$3({ controller, ...props }) {
|
|
1547
|
-
return usePart("div", props, { status: controller.state._tag }, {
|
|
1548
|
-
"data-domainkit-part": "cleanup-flow",
|
|
1549
|
-
"data-state": controller.state._tag
|
|
1550
|
-
});
|
|
1551
|
-
}
|
|
1552
|
-
function Dialog$3(props) {
|
|
1553
|
-
return /* @__PURE__ */ jsx(Dialog$4, {
|
|
1554
|
-
...props,
|
|
1555
|
-
kind: "cleanup"
|
|
1556
|
-
});
|
|
1557
|
-
}
|
|
1558
|
-
function Actions$1(props) {
|
|
1559
|
-
return /* @__PURE__ */ jsx(Actions$2, {
|
|
1560
|
-
...props,
|
|
1561
|
-
kind: "cleanup"
|
|
1562
|
-
});
|
|
1563
|
-
}
|
|
1564
|
-
function Outcome$3(props) {
|
|
1565
|
-
return /* @__PURE__ */ jsx(Outcome$4, {
|
|
1566
|
-
...props,
|
|
1567
|
-
kind: "cleanup"
|
|
1568
|
-
});
|
|
1569
|
-
}
|
|
1570
|
-
function Status$3(props) {
|
|
1571
|
-
return /* @__PURE__ */ jsx(Status$4, {
|
|
1572
|
-
...props,
|
|
1573
|
-
kind: "cleanup"
|
|
1574
|
-
});
|
|
1575
|
-
}
|
|
1576
|
-
/** Cleanup on its own: a trigger that plans the removal, then the same review dialog. */
|
|
1577
|
-
function Flow$3({ domain, onCleaned, receiptId, trigger, ...props }) {
|
|
1578
|
-
const controller = useController$3({
|
|
1579
|
-
domain,
|
|
1580
|
-
...onCleaned === void 0 ? {} : { onCleaned },
|
|
1581
|
-
...receiptId === void 0 ? {} : { receiptId }
|
|
1582
|
-
});
|
|
1583
|
-
return /* @__PURE__ */ jsxs(Root$3, {
|
|
1584
|
-
controller,
|
|
1585
|
-
...props,
|
|
1586
|
-
children: [/* @__PURE__ */ jsx(Dialog$3, {
|
|
1587
|
-
controller,
|
|
1588
|
-
...trigger === void 0 ? {} : { trigger }
|
|
1589
|
-
}), /* @__PURE__ */ jsx(Outcome$3, { controller })]
|
|
1590
|
-
});
|
|
1591
|
-
}
|
|
1592
799
|
//#endregion
|
|
1593
|
-
//#region src/
|
|
1594
|
-
|
|
1595
|
-
const
|
|
1596
|
-
|
|
1597
|
-
|
|
800
|
+
//#region src/domain-field.ts
|
|
801
|
+
const ZonesState = Data.taggedEnum();
|
|
802
|
+
const nothing = {
|
|
803
|
+
connections: [],
|
|
804
|
+
providers: [],
|
|
805
|
+
zones: []
|
|
806
|
+
};
|
|
1598
807
|
/**
|
|
1599
|
-
*
|
|
1600
|
-
*
|
|
808
|
+
* Every zone this owner's connections reach. A connection whose credential the provider turned
|
|
809
|
+
* down is listed in `connections` as `reconnect` and contributes no zones, so one dead account
|
|
810
|
+
* never costs the customer the others.
|
|
1601
811
|
*/
|
|
1602
|
-
function
|
|
1603
|
-
const {
|
|
1604
|
-
const
|
|
1605
|
-
const
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
812
|
+
function useZones({ provider } = {}) {
|
|
813
|
+
const { revision, transport } = useDomainKit();
|
|
814
|
+
const connection = transport.connection;
|
|
815
|
+
const runner = useRunner();
|
|
816
|
+
const [state, setState] = useState(ZonesState.Loading());
|
|
817
|
+
const load = useCallback(() => {
|
|
818
|
+
if (connection === void 0) return;
|
|
819
|
+
setState(ZonesState.Loading());
|
|
820
|
+
runner.run(connection.zones(provider === void 0 ? {} : { provider }), {
|
|
821
|
+
onFailure: (error) => setState(ZonesState.Failure({ error })),
|
|
822
|
+
onSuccess: (listing) => setState(ZonesState.Ready({
|
|
823
|
+
connections: listing.connections,
|
|
824
|
+
providers: listing.providers,
|
|
825
|
+
zones: listing.zones
|
|
826
|
+
}))
|
|
827
|
+
});
|
|
828
|
+
}, [
|
|
829
|
+
connection,
|
|
830
|
+
provider,
|
|
831
|
+
runner
|
|
832
|
+
]);
|
|
833
|
+
useEffect(load, [load, revision]);
|
|
834
|
+
const held = state._tag === "Ready" ? state : nothing;
|
|
835
|
+
return {
|
|
836
|
+
connections: held.connections,
|
|
837
|
+
providers: held.providers,
|
|
838
|
+
refresh: load,
|
|
839
|
+
state,
|
|
840
|
+
zones: held.zones
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
const AccountsState = Data.taggedEnum();
|
|
844
|
+
const currentUrl$1 = () => typeof window === "undefined" ? null : window.location.href;
|
|
845
|
+
/**
|
|
846
|
+
* The accounts this owner holds, and the one way to add another: connect a provider without naming
|
|
847
|
+
* a domain. A token method finishes in place; an interactive one redirects and comes back through
|
|
848
|
+
* the library's own callback, which lands on `returnTo`.
|
|
849
|
+
*/
|
|
850
|
+
function useAccounts({ readOnly: refused, returnTo } = {}) {
|
|
851
|
+
const { navigate, transport } = useDomainKit();
|
|
852
|
+
const inherited = useReadOnly();
|
|
853
|
+
const readOnly = refused ?? inherited;
|
|
854
|
+
const connection = transport.connection;
|
|
855
|
+
const runner = useRunner();
|
|
856
|
+
const listing = useZones();
|
|
857
|
+
const [command, setCommand] = useState(null);
|
|
858
|
+
const refresh = listing.refresh;
|
|
859
|
+
const submit = useCallback((input) => {
|
|
860
|
+
if (connection === void 0 || readOnly) return;
|
|
861
|
+
const destination = returnTo === void 0 ? currentUrl$1() : returnTo;
|
|
862
|
+
const interactive = destination === null ? {} : { returnTo: destination };
|
|
863
|
+
const method = input.method === "token" ? Transport.Method.token(input.values ?? {}) : input.method === "oauth" ? Transport.Method.oauth(interactive) : Transport.Method.integration(interactive);
|
|
864
|
+
setCommand(AccountsState.Submitting({ provider: input.provider }));
|
|
865
|
+
const granting = input.connectionId === void 0 ? connection.start({
|
|
866
|
+
method,
|
|
867
|
+
provider: input.provider
|
|
868
|
+
}) : connection.reconnect({
|
|
869
|
+
connectionId: input.connectionId,
|
|
870
|
+
method
|
|
871
|
+
});
|
|
872
|
+
runner.run(granting, {
|
|
873
|
+
onFailure: (error) => setCommand(AccountsState.Failure({ error })),
|
|
874
|
+
onSuccess: (started) => {
|
|
875
|
+
if (started._tag === "Redirect") {
|
|
876
|
+
setCommand(AccountsState.Redirecting({ url: started.authorizationUrl }));
|
|
877
|
+
navigate(started.authorizationUrl);
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
setCommand(null);
|
|
881
|
+
refresh();
|
|
882
|
+
}
|
|
883
|
+
});
|
|
884
|
+
}, [
|
|
885
|
+
connection,
|
|
886
|
+
navigate,
|
|
887
|
+
readOnly,
|
|
888
|
+
refresh,
|
|
889
|
+
returnTo,
|
|
890
|
+
runner
|
|
891
|
+
]);
|
|
892
|
+
const connect = useCallback((input) => submit(input), [submit]);
|
|
893
|
+
const reconnect = useCallback((input) => submit(input), [submit]);
|
|
894
|
+
const state = command ?? (listing.state._tag === "Loading" ? AccountsState.Loading() : listing.state._tag === "Failure" ? AccountsState.Failure({ error: listing.state.error }) : AccountsState.Ready());
|
|
895
|
+
return {
|
|
896
|
+
connect,
|
|
897
|
+
reconnect,
|
|
898
|
+
connections: listing.connections,
|
|
899
|
+
providers: listing.providers,
|
|
900
|
+
refresh,
|
|
901
|
+
state,
|
|
902
|
+
zones: listing.zones
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
const normalize = (value) => value.trim().toLowerCase().replace(/\.$/, "");
|
|
906
|
+
/**
|
|
907
|
+
* The zone a value already sits in: the longest listed zone the value is at or under. Two accounts
|
|
908
|
+
* can serve the same zone, so `preferred` names the one the customer picked and wins among equals;
|
|
909
|
+
* without it the first listed account answers.
|
|
910
|
+
*/
|
|
911
|
+
const placementOf = (value, zones, preferred) => {
|
|
912
|
+
const domain = normalize(value);
|
|
913
|
+
if (domain === "") return null;
|
|
914
|
+
const matching = zones.filter((candidate) => {
|
|
915
|
+
const zone = normalize(candidate.zone);
|
|
916
|
+
return domain === zone || domain.endsWith(`.${zone}`);
|
|
917
|
+
});
|
|
918
|
+
const longest = matching.reduce((best, candidate) => best === null || normalize(candidate.zone).length > normalize(best.zone).length ? candidate : best, null);
|
|
919
|
+
if (longest === null) return null;
|
|
920
|
+
const closest = matching.filter((candidate) => normalize(candidate.zone) === normalize(longest.zone));
|
|
921
|
+
const best = closest.find((candidate) => preferred != null && candidate.connectionId === preferred.connectionId && normalize(candidate.zone) === normalize(preferred.zone)) ?? closest[0] ?? longest;
|
|
922
|
+
return {
|
|
923
|
+
placement: {
|
|
924
|
+
connectionId: best.connectionId,
|
|
925
|
+
zone: best.zone
|
|
926
|
+
},
|
|
927
|
+
zone: best
|
|
928
|
+
};
|
|
929
|
+
};
|
|
930
|
+
/**
|
|
931
|
+
* What the customer typed in front of the zone they are reaching for. "mail.ex" against
|
|
932
|
+
* `example.com` is the subdomain "mail"; "ex" on its own is none.
|
|
933
|
+
*/
|
|
934
|
+
const subdomainOf = (value, zone) => {
|
|
935
|
+
const typed = normalize(value);
|
|
936
|
+
const target = normalize(zone);
|
|
937
|
+
if (typed === "" || target.startsWith(typed)) return "";
|
|
938
|
+
const cut = typed.lastIndexOf(".");
|
|
939
|
+
if (cut < 0) return "";
|
|
940
|
+
const head = typed.slice(0, cut);
|
|
941
|
+
const tail = typed.slice(cut + 1);
|
|
942
|
+
return target.startsWith(tail) ? head : "";
|
|
943
|
+
};
|
|
944
|
+
/** Zones the typed value is reaching for: a prefix of the zone, or a subdomain of one. */
|
|
945
|
+
const suggestionsFor = (value, zones) => {
|
|
946
|
+
const typed = normalize(value);
|
|
947
|
+
if (typed === "") return zones;
|
|
948
|
+
return zones.filter((candidate) => {
|
|
949
|
+
const zone = normalize(candidate.zone);
|
|
950
|
+
if (zone.startsWith(typed)) return true;
|
|
951
|
+
const cut = typed.lastIndexOf(".");
|
|
952
|
+
if (cut < 0) return false;
|
|
953
|
+
const tail = typed.slice(cut + 1);
|
|
954
|
+
return tail !== "" && zone.startsWith(tail);
|
|
955
|
+
});
|
|
956
|
+
};
|
|
957
|
+
/** The value a suggestion completes to: whatever subdomain was typed, then the zone. */
|
|
958
|
+
const completionOf = (value, zone) => {
|
|
959
|
+
const subdomain = subdomainOf(value, zone.zone);
|
|
960
|
+
return subdomain === "" ? zone.zone : `${subdomain}.${zone.zone}`;
|
|
961
|
+
};
|
|
962
|
+
const optionId = (listId, zone) => `${listId}-${zone.connectionId}-${zone.zone}`;
|
|
963
|
+
/**
|
|
964
|
+
* One input over every zone the workspace's accounts reach, as props rather than markup. Arrow
|
|
965
|
+
* keys move the highlight, Tab or Enter completes it, Escape closes the list, and `found` says
|
|
966
|
+
* where the records will go.
|
|
967
|
+
*/
|
|
968
|
+
function useDomainField({ id, onChange, onResolve, value, zones }) {
|
|
969
|
+
const messages = useMessages();
|
|
970
|
+
const generated = useId();
|
|
971
|
+
const inputId = id ?? generated;
|
|
972
|
+
const listId = `${inputId}-suggestions`;
|
|
973
|
+
const [open, setOpen] = useState(false);
|
|
974
|
+
const [active, setActive] = useState(0);
|
|
975
|
+
const [chosen, setChosen] = useState(null);
|
|
976
|
+
const suggestions = suggestionsFor(value, zones);
|
|
977
|
+
const highlighted = suggestions[Math.min(active, Math.max(suggestions.length - 1, 0))] ?? null;
|
|
978
|
+
const found = placementOf(value, zones, chosen);
|
|
979
|
+
const showing = open && suggestions.length > 0;
|
|
980
|
+
const placement = found?.placement ?? null;
|
|
981
|
+
const connectionId = placement?.connectionId ?? null;
|
|
982
|
+
const placedIn = placement?.zone ?? null;
|
|
983
|
+
const announced = useRef(null);
|
|
984
|
+
useEffect(() => {
|
|
985
|
+
const signature = `${value}|${connectionId ?? ""}|${placedIn ?? ""}`;
|
|
986
|
+
if (announced.current === signature) return;
|
|
987
|
+
announced.current = signature;
|
|
988
|
+
onResolve?.({
|
|
989
|
+
connection: connectionId === null || placedIn === null ? null : {
|
|
990
|
+
connectionId,
|
|
991
|
+
zone: placedIn
|
|
992
|
+
},
|
|
993
|
+
domain: value
|
|
994
|
+
});
|
|
995
|
+
}, [
|
|
996
|
+
connectionId,
|
|
997
|
+
onResolve,
|
|
998
|
+
placedIn,
|
|
999
|
+
value
|
|
1000
|
+
]);
|
|
1001
|
+
const complete = (zone) => {
|
|
1002
|
+
onChange(completionOf(value, zone));
|
|
1003
|
+
setChosen({
|
|
1004
|
+
connectionId: zone.connectionId,
|
|
1005
|
+
zone: zone.zone
|
|
1006
|
+
});
|
|
1007
|
+
setOpen(false);
|
|
1008
|
+
setActive(0);
|
|
1009
|
+
};
|
|
1010
|
+
return {
|
|
1011
|
+
close: () => setOpen(false),
|
|
1012
|
+
complete,
|
|
1013
|
+
found,
|
|
1014
|
+
highlighted,
|
|
1015
|
+
inputProps: {
|
|
1016
|
+
"aria-activedescendant": showing && highlighted !== null ? optionId(listId, highlighted) : void 0,
|
|
1017
|
+
"aria-autocomplete": "list",
|
|
1018
|
+
"aria-controls": listId,
|
|
1019
|
+
"aria-expanded": showing,
|
|
1020
|
+
autoComplete: "off",
|
|
1021
|
+
id: inputId,
|
|
1022
|
+
onChange: (event) => {
|
|
1023
|
+
onChange(event.target.value);
|
|
1024
|
+
setActive(0);
|
|
1025
|
+
setOpen(true);
|
|
1026
|
+
},
|
|
1027
|
+
onFocus: () => setOpen(true),
|
|
1028
|
+
onKeyDown: (event) => {
|
|
1029
|
+
if (event.key === "Escape") {
|
|
1030
|
+
setOpen(false);
|
|
1031
|
+
return;
|
|
1032
|
+
}
|
|
1033
|
+
if (suggestions.length === 0) return;
|
|
1034
|
+
if (event.key === "ArrowDown") {
|
|
1035
|
+
event.preventDefault();
|
|
1036
|
+
setOpen(true);
|
|
1037
|
+
setActive((index) => (index + 1) % suggestions.length);
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
if (event.key === "ArrowUp") {
|
|
1041
|
+
event.preventDefault();
|
|
1042
|
+
setOpen(true);
|
|
1043
|
+
setActive((index) => (index + suggestions.length - 1) % suggestions.length);
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
if ((event.key === "Tab" || event.key === "Enter") && showing && highlighted !== null) {
|
|
1047
|
+
event.preventDefault();
|
|
1048
|
+
complete(highlighted);
|
|
1049
|
+
}
|
|
1050
|
+
},
|
|
1051
|
+
role: "combobox",
|
|
1052
|
+
type: "text",
|
|
1053
|
+
value
|
|
1054
|
+
},
|
|
1055
|
+
listboxProps: {
|
|
1056
|
+
"aria-label": messages.zoneSuggestions,
|
|
1057
|
+
hidden: !showing,
|
|
1058
|
+
id: listId,
|
|
1059
|
+
role: "listbox"
|
|
1060
|
+
},
|
|
1061
|
+
open: showing,
|
|
1062
|
+
optionProps: (zone) => ({
|
|
1063
|
+
"aria-selected": zone === highlighted,
|
|
1064
|
+
id: optionId(listId, zone),
|
|
1065
|
+
onMouseDown: (event) => {
|
|
1066
|
+
event.preventDefault();
|
|
1067
|
+
complete(zone);
|
|
1068
|
+
},
|
|
1069
|
+
role: "option"
|
|
1070
|
+
}),
|
|
1071
|
+
suggestions
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
1074
|
+
//#endregion
|
|
1075
|
+
//#region src/connect.ts
|
|
1076
|
+
var connect_exports = /* @__PURE__ */ __exportAll({
|
|
1077
|
+
AccountsState: () => AccountsState,
|
|
1078
|
+
State: () => State$1,
|
|
1079
|
+
ZonesState: () => ZonesState,
|
|
1626
1080
|
answeredInPlace: () => answeredInPlace,
|
|
1081
|
+
attempted: () => attempted,
|
|
1082
|
+
completionOf: () => completionOf,
|
|
1083
|
+
describeMethods: () => describeMethods,
|
|
1084
|
+
displayName: () => displayName,
|
|
1085
|
+
holdsConnection: () => holdsConnection,
|
|
1627
1086
|
hostProvider: () => hostProvider,
|
|
1628
|
-
|
|
1087
|
+
offering: () => offering,
|
|
1088
|
+
placementOf: () => placementOf,
|
|
1089
|
+
providerOf: () => providerOf,
|
|
1090
|
+
reconnect: () => reconnect,
|
|
1091
|
+
rejectedField: () => rejectedField,
|
|
1092
|
+
reusableConnections: () => reusableConnections,
|
|
1093
|
+
suggestionsFor: () => suggestionsFor,
|
|
1094
|
+
useAccounts: () => useAccounts,
|
|
1095
|
+
useController: () => useController$2,
|
|
1096
|
+
useDomainField: () => useDomainField,
|
|
1097
|
+
useZones: () => useZones
|
|
1629
1098
|
});
|
|
1630
1099
|
const State$1 = Data.taggedEnum();
|
|
1100
|
+
/**
|
|
1101
|
+
* An interactive method leaves the page and comes back, so what the library knew is gone with it.
|
|
1102
|
+
* It writes down the domain it sent the customer away for, and the load that follows reads it once:
|
|
1103
|
+
* that, not a query parameter a host would have to keep, is how a return is told from a reload.
|
|
1104
|
+
*/
|
|
1105
|
+
const RETURNING = "domainkit.returning";
|
|
1106
|
+
const markReturn = (domain) => {
|
|
1107
|
+
try {
|
|
1108
|
+
sessionStorage.setItem(RETURNING, domain);
|
|
1109
|
+
} catch {}
|
|
1110
|
+
};
|
|
1111
|
+
const tookReturn = (domain) => {
|
|
1112
|
+
try {
|
|
1113
|
+
if (sessionStorage.getItem(RETURNING) !== domain) return false;
|
|
1114
|
+
sessionStorage.removeItem(RETURNING);
|
|
1115
|
+
return true;
|
|
1116
|
+
} catch {
|
|
1117
|
+
return false;
|
|
1118
|
+
}
|
|
1119
|
+
};
|
|
1631
1120
|
/** Read at connect time, not at render, so it is the page the customer is actually on. */
|
|
1632
1121
|
const currentUrl = () => typeof window === "undefined" ? null : window.location.href;
|
|
1633
1122
|
const snapshotOf = (state) => {
|
|
@@ -1657,10 +1146,12 @@ const settled = (snapshot, discovery) => {
|
|
|
1657
1146
|
* transport declares it and the domain has no connection yet; a discovery failure is not the
|
|
1658
1147
|
* customer's problem, so the provider list still renders.
|
|
1659
1148
|
*/
|
|
1660
|
-
function useController$2({ domain, returnTo }) {
|
|
1149
|
+
function useController$2({ domain, readOnly: refused, returnTo }) {
|
|
1661
1150
|
const { emit, navigate, revision, transport } = useDomainKit();
|
|
1662
|
-
const
|
|
1151
|
+
const inherited = useReadOnly();
|
|
1152
|
+
const readOnly = refused ?? inherited;
|
|
1663
1153
|
const connection = transport.connection;
|
|
1154
|
+
const provisioning = transport.provisioning;
|
|
1664
1155
|
const runner = useRunner();
|
|
1665
1156
|
const [state, setState] = useState(State$1.Loading({ snapshot: null }));
|
|
1666
1157
|
const held = useRef({
|
|
@@ -1670,6 +1161,7 @@ function useController$2({ domain, returnTo }) {
|
|
|
1670
1161
|
});
|
|
1671
1162
|
const lastCommand = useRef(null);
|
|
1672
1163
|
const attempted = useRef(null);
|
|
1164
|
+
const [established, setEstablished] = useState(0);
|
|
1673
1165
|
const [inspected, setInspected] = useState(domain);
|
|
1674
1166
|
if (inspected !== domain) {
|
|
1675
1167
|
setInspected(domain);
|
|
@@ -1719,6 +1211,7 @@ function useController$2({ domain, returnTo }) {
|
|
|
1719
1211
|
domain,
|
|
1720
1212
|
snapshot
|
|
1721
1213
|
};
|
|
1214
|
+
if (tookReturn(domain) && snapshot.status === "connected") setEstablished((count) => count + 1);
|
|
1722
1215
|
setState(settled(snapshot, discovery));
|
|
1723
1216
|
}
|
|
1724
1217
|
});
|
|
@@ -1732,20 +1225,28 @@ function useController$2({ domain, returnTo }) {
|
|
|
1732
1225
|
const started = useCallback((result) => {
|
|
1733
1226
|
if (held.current.domain !== domain) return;
|
|
1734
1227
|
switch (result._tag) {
|
|
1735
|
-
case "Connected":
|
|
1228
|
+
case "Connected": {
|
|
1229
|
+
const snapshot = result.snapshot;
|
|
1230
|
+
setEstablished((count) => count + 1);
|
|
1231
|
+
if (snapshot === null) {
|
|
1232
|
+
load();
|
|
1233
|
+
return;
|
|
1234
|
+
}
|
|
1736
1235
|
held.current = {
|
|
1737
1236
|
discovery: null,
|
|
1738
1237
|
domain,
|
|
1739
|
-
snapshot
|
|
1238
|
+
snapshot
|
|
1740
1239
|
};
|
|
1741
|
-
setState(settled(
|
|
1742
|
-
if (
|
|
1743
|
-
connectionId:
|
|
1240
|
+
setState(settled(snapshot, null));
|
|
1241
|
+
if (snapshot.connectionId !== null) emit(Event.Connected({
|
|
1242
|
+
connectionId: snapshot.connectionId,
|
|
1744
1243
|
domain,
|
|
1745
|
-
snapshot
|
|
1244
|
+
snapshot
|
|
1746
1245
|
}));
|
|
1747
1246
|
return;
|
|
1247
|
+
}
|
|
1748
1248
|
case "Redirect":
|
|
1249
|
+
markReturn(domain);
|
|
1749
1250
|
setState(State$1.Redirecting({ url: result.authorizationUrl }));
|
|
1750
1251
|
navigate(result.authorizationUrl);
|
|
1751
1252
|
return;
|
|
@@ -1760,13 +1261,17 @@ function useController$2({ domain, returnTo }) {
|
|
|
1760
1261
|
}, [
|
|
1761
1262
|
domain,
|
|
1762
1263
|
emit,
|
|
1264
|
+
load,
|
|
1763
1265
|
navigate
|
|
1764
1266
|
]);
|
|
1765
1267
|
const submit = useCallback((effect, attempt) => {
|
|
1766
1268
|
if (held.current.domain !== domain) return;
|
|
1767
1269
|
attempted.current = attempt;
|
|
1768
1270
|
const command = () => {
|
|
1769
|
-
setState(State$1.Submitting({
|
|
1271
|
+
setState(State$1.Submitting({
|
|
1272
|
+
discovery: held.current.discovery,
|
|
1273
|
+
snapshot: held.current.snapshot
|
|
1274
|
+
}));
|
|
1770
1275
|
runner.run(effect, {
|
|
1771
1276
|
onFailure,
|
|
1772
1277
|
onSuccess: started
|
|
@@ -1784,11 +1289,16 @@ function useController$2({ domain, returnTo }) {
|
|
|
1784
1289
|
started
|
|
1785
1290
|
]);
|
|
1786
1291
|
const connect = useCallback((input) => {
|
|
1787
|
-
if (connection === void 0) return;
|
|
1292
|
+
if (connection === void 0 || readOnly) return;
|
|
1788
1293
|
const destination = input.returnTo ?? (returnTo === void 0 ? currentUrl() : returnTo);
|
|
1789
1294
|
const interactive = destination === null ? {} : { returnTo: destination };
|
|
1790
1295
|
const method = input.method === "token" ? Transport.Method.token(input.values ?? {}) : input.method === "oauth" ? Transport.Method.oauth(interactive) : Transport.Method.integration(interactive);
|
|
1791
|
-
|
|
1296
|
+
const snapshot = held.current.snapshot;
|
|
1297
|
+
const stale = snapshot?.status === "reconnect" && snapshot.provider === input.provider && snapshot.connectionId !== null;
|
|
1298
|
+
submit(stale && snapshot?.connectionId != null ? connection.reconnect({
|
|
1299
|
+
connectionId: snapshot.connectionId,
|
|
1300
|
+
method
|
|
1301
|
+
}) : connection.start({
|
|
1792
1302
|
domain,
|
|
1793
1303
|
method,
|
|
1794
1304
|
provider: input.provider
|
|
@@ -1799,11 +1309,12 @@ function useController$2({ domain, returnTo }) {
|
|
|
1799
1309
|
}, [
|
|
1800
1310
|
connection,
|
|
1801
1311
|
domain,
|
|
1312
|
+
readOnly,
|
|
1802
1313
|
returnTo,
|
|
1803
1314
|
submit
|
|
1804
1315
|
]);
|
|
1805
1316
|
const reuse = useCallback((input) => {
|
|
1806
|
-
if (connection === void 0) return;
|
|
1317
|
+
if (connection === void 0 || readOnly) return;
|
|
1807
1318
|
submit(connection.attach({
|
|
1808
1319
|
connectionId: input.connectionId,
|
|
1809
1320
|
domain,
|
|
@@ -1812,6 +1323,7 @@ function useController$2({ domain, returnTo }) {
|
|
|
1812
1323
|
}, [
|
|
1813
1324
|
connection,
|
|
1814
1325
|
domain,
|
|
1326
|
+
readOnly,
|
|
1815
1327
|
submit
|
|
1816
1328
|
]);
|
|
1817
1329
|
const select = useCallback((zone) => {
|
|
@@ -1825,7 +1337,10 @@ function useController$2({ domain, returnTo }) {
|
|
|
1825
1337
|
if (held.current.domain !== domain) return;
|
|
1826
1338
|
attempted.current = null;
|
|
1827
1339
|
const command = () => {
|
|
1828
|
-
setState(State$1.Submitting({
|
|
1340
|
+
setState(State$1.Submitting({
|
|
1341
|
+
discovery: held.current.discovery,
|
|
1342
|
+
snapshot: held.current.snapshot
|
|
1343
|
+
}));
|
|
1829
1344
|
runner.run(effect, {
|
|
1830
1345
|
onFailure,
|
|
1831
1346
|
onSuccess: () => {
|
|
@@ -1847,17 +1362,20 @@ function useController$2({ domain, returnTo }) {
|
|
|
1847
1362
|
runner
|
|
1848
1363
|
]);
|
|
1849
1364
|
const detach = useCallback(() => {
|
|
1850
|
-
const attachmentId = held.current.snapshot?.
|
|
1851
|
-
if (connection === void 0 || attachmentId == null
|
|
1365
|
+
const attachmentId = held.current.snapshot?.attachment?.id;
|
|
1366
|
+
if (readOnly || connection === void 0 || attachmentId == null) return;
|
|
1367
|
+
if (held.current.domain !== domain) return;
|
|
1852
1368
|
release(connection.detach(attachmentId), Event.Detached({ domain }));
|
|
1853
1369
|
}, [
|
|
1854
1370
|
connection,
|
|
1855
1371
|
domain,
|
|
1372
|
+
readOnly,
|
|
1856
1373
|
release
|
|
1857
1374
|
]);
|
|
1858
1375
|
const disconnect = useCallback(() => {
|
|
1859
1376
|
const connectionId = held.current.snapshot?.connectionId;
|
|
1860
|
-
if (connection === void 0 || connectionId == null
|
|
1377
|
+
if (readOnly || connection === void 0 || connectionId == null) return;
|
|
1378
|
+
if (held.current.domain !== domain) return;
|
|
1861
1379
|
release(connection.disconnect(connectionId), Event.Disconnected({
|
|
1862
1380
|
connectionId,
|
|
1863
1381
|
domain
|
|
@@ -1865,6 +1383,7 @@ function useController$2({ domain, returnTo }) {
|
|
|
1865
1383
|
}, [
|
|
1866
1384
|
connection,
|
|
1867
1385
|
domain,
|
|
1386
|
+
readOnly,
|
|
1868
1387
|
release
|
|
1869
1388
|
]);
|
|
1870
1389
|
const retry = useCallback(() => {
|
|
@@ -1876,12 +1395,53 @@ function useController$2({ domain, returnTo }) {
|
|
|
1876
1395
|
load,
|
|
1877
1396
|
readOnly
|
|
1878
1397
|
]);
|
|
1398
|
+
const sighted = useRef(null);
|
|
1399
|
+
useEffect(() => {
|
|
1400
|
+
if (readOnly || state._tag !== "Disconnected") return;
|
|
1401
|
+
const discovery = state.discovery;
|
|
1402
|
+
if (discovery === null || discovery._tag !== "Resolved") return;
|
|
1403
|
+
const sight = `${domain}|${discovery.connectionId}|${discovery.zone}`;
|
|
1404
|
+
if (sighted.current === sight) return;
|
|
1405
|
+
sighted.current = sight;
|
|
1406
|
+
reuse({
|
|
1407
|
+
connectionId: discovery.connectionId,
|
|
1408
|
+
zone: discovery.zone
|
|
1409
|
+
});
|
|
1410
|
+
}, [
|
|
1411
|
+
domain,
|
|
1412
|
+
readOnly,
|
|
1413
|
+
reuse,
|
|
1414
|
+
state
|
|
1415
|
+
]);
|
|
1416
|
+
const [receipt, setReceipt] = useState(null);
|
|
1417
|
+
const receiptId = snapshotOf(state)?.lastReceiptId ?? null;
|
|
1418
|
+
const readReceipt = useRef(null);
|
|
1419
|
+
useEffect(() => {
|
|
1420
|
+
if (receiptId === null) {
|
|
1421
|
+
readReceipt.current = null;
|
|
1422
|
+
setReceipt(null);
|
|
1423
|
+
return;
|
|
1424
|
+
}
|
|
1425
|
+
if (provisioning === void 0 || readReceipt.current === receiptId) return;
|
|
1426
|
+
readReceipt.current = receiptId;
|
|
1427
|
+
runner.run(provisioning.receipt(Receipt.ReceiptId.make(receiptId)), {
|
|
1428
|
+
onFailure: () => setReceipt(null),
|
|
1429
|
+
onSuccess: (value) => setReceipt(value.id === receiptId ? value : null)
|
|
1430
|
+
});
|
|
1431
|
+
}, [
|
|
1432
|
+
provisioning,
|
|
1433
|
+
receiptId,
|
|
1434
|
+
runner
|
|
1435
|
+
]);
|
|
1879
1436
|
return {
|
|
1880
1437
|
connect,
|
|
1881
1438
|
detach,
|
|
1882
1439
|
disconnect,
|
|
1883
|
-
|
|
1440
|
+
domain,
|
|
1441
|
+
established,
|
|
1442
|
+
discovery: state._tag === "Disconnected" || state._tag === "Failure" || state._tag === "Submitting" ? state.discovery : null,
|
|
1884
1443
|
providers: snapshotOf(state)?.providers ?? [],
|
|
1444
|
+
receipt: receipt?.id === receiptId ? receipt : null,
|
|
1885
1445
|
refresh: load,
|
|
1886
1446
|
retry,
|
|
1887
1447
|
reuse,
|
|
@@ -1890,18 +1450,24 @@ function useController$2({ domain, returnTo }) {
|
|
|
1890
1450
|
state
|
|
1891
1451
|
};
|
|
1892
1452
|
}
|
|
1893
|
-
function Root$2({ controller, ...props }) {
|
|
1894
|
-
return usePart("div", props, { status: controller.state._tag }, {
|
|
1895
|
-
"data-domainkit-part": "connection-root",
|
|
1896
|
-
"data-state": controller.state._tag
|
|
1897
|
-
});
|
|
1898
|
-
}
|
|
1899
1453
|
/**
|
|
1900
1454
|
* Whether a failure is already answered where it was raised: beside the method the customer used.
|
|
1901
|
-
* A
|
|
1455
|
+
* A surface rendering one outcome for the whole flow skips it, so the failure announces once.
|
|
1902
1456
|
*/
|
|
1903
1457
|
const answeredInPlace = (controller) => controller.state._tag === "Failure" && controller.state.attempt !== null;
|
|
1904
1458
|
/**
|
|
1459
|
+
* Whether the surface should read as connected: it is, or a command it started is in flight over a
|
|
1460
|
+
* connection that was. The provider row and the dialog it opened stay on screen for the length of
|
|
1461
|
+
* the command rather than vanishing under the customer half way through it.
|
|
1462
|
+
*/
|
|
1463
|
+
const holdsConnection = (controller) => controller.state._tag === "Connected" || controller.state._tag === "Submitting" && controller.snapshot?.status === "connected";
|
|
1464
|
+
/**
|
|
1465
|
+
* Whether the provider turned the credential down, so the customer must prove the account again.
|
|
1466
|
+
* Proving it re-credits the connection the workspace already holds and leaves its domains where
|
|
1467
|
+
* they are, which is why the surface offers a reconnect rather than a second account.
|
|
1468
|
+
*/
|
|
1469
|
+
const reconnect = (controller) => controller.state._tag === "Reconnect" || controller.state._tag === "Submitting" && controller.snapshot?.status === "reconnect";
|
|
1470
|
+
/**
|
|
1905
1471
|
* The provider whose nameservers serve this domain, as the snapshot describes it. Discovery names
|
|
1906
1472
|
* it by id; without a descriptor there is nothing to draw, so there is no host.
|
|
1907
1473
|
*/
|
|
@@ -1911,498 +1477,227 @@ const hostProvider = (controller) => {
|
|
|
1911
1477
|
const named = discovery.host.provider;
|
|
1912
1478
|
return controller.providers.find((entry) => entry.id === named) ?? null;
|
|
1913
1479
|
};
|
|
1480
|
+
/** The descriptor for one provider id, or `null` when the snapshot describes no such provider. */
|
|
1481
|
+
const providerOf = (controller, providerId) => providerId == null ? null : controller.providers.find((entry) => entry.id === providerId) ?? null;
|
|
1914
1482
|
/** The provider as the customer knows it, falling back to the id when the snapshot has no name. */
|
|
1915
|
-
const displayName = (controller,
|
|
1483
|
+
const displayName = (controller, providerId) => providerOf(controller, providerId)?.name ?? providerId;
|
|
1916
1484
|
/**
|
|
1917
|
-
*
|
|
1918
|
-
*
|
|
1485
|
+
* Whether the connect surface has anything to offer for this domain: a provider that serves the
|
|
1486
|
+
* zone, a connection discovery already found, one the owner already holds, or a command already
|
|
1487
|
+
* running. Never while a connection is held, which is what `holdsConnection` answers. A host reads
|
|
1488
|
+
* it to order its own offers beside DomainKit's rather than competing with them.
|
|
1919
1489
|
*/
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
const readOnly = useReadOnly();
|
|
1490
|
+
const offering = (controller, connect = "detected") => {
|
|
1491
|
+
if (holdsConnection(controller)) return false;
|
|
1923
1492
|
const state = controller.state;
|
|
1924
|
-
if (state._tag
|
|
1925
|
-
|
|
1926
|
-
const
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
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
|
-
})
|
|
1943
|
-
});
|
|
1944
|
-
}
|
|
1945
|
-
function Status$2({ controller, ...props }) {
|
|
1946
|
-
const { messages } = useDomainKit();
|
|
1493
|
+
if (state._tag === "Submitting" || state._tag === "Redirecting" || state._tag === "SelectionRequired" || state._tag === "Reconnect") return true;
|
|
1494
|
+
if (connect !== "detected") return connect === "always";
|
|
1495
|
+
const discovery = controller.discovery;
|
|
1496
|
+
return hostProvider(controller) !== null || discovery !== null && discovery._tag !== "NotFound" || (controller.snapshot?.reusable.length ?? 0) > 0;
|
|
1497
|
+
};
|
|
1498
|
+
/**
|
|
1499
|
+
* Every connection the customer can attach without proving anything: the one discovery resolved,
|
|
1500
|
+
* the candidates a zone choice is between, and the connections the snapshot lists as reusable.
|
|
1501
|
+
*/
|
|
1502
|
+
const reusableConnections = (controller) => {
|
|
1947
1503
|
const state = controller.state;
|
|
1948
|
-
const
|
|
1949
|
-
const
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1504
|
+
const discovery = controller.discovery;
|
|
1505
|
+
const candidates = state._tag === "SelectionRequired" ? state.candidates.map((candidate) => ({
|
|
1506
|
+
...candidate,
|
|
1507
|
+
connectionId: state.connectionId
|
|
1508
|
+
})) : discovery !== null && discovery._tag === "SelectionRequired" ? discovery.candidates : [];
|
|
1509
|
+
return [
|
|
1510
|
+
...(discovery !== null && discovery._tag === "Resolved" ? [discovery] : []).map((entry) => ({
|
|
1511
|
+
connectionId: entry.connectionId,
|
|
1512
|
+
label: entry.label,
|
|
1513
|
+
zone: entry.zone
|
|
1514
|
+
})),
|
|
1515
|
+
...candidates.map((entry) => ({
|
|
1516
|
+
connectionId: entry.connectionId,
|
|
1517
|
+
label: entry.label,
|
|
1518
|
+
zone: entry.zone
|
|
1519
|
+
})),
|
|
1520
|
+
...(controller.snapshot?.reusable ?? []).map((entry) => ({
|
|
1521
|
+
connectionId: entry.connectionId,
|
|
1522
|
+
label: entry.provider,
|
|
1523
|
+
zone: void 0
|
|
1524
|
+
}))
|
|
1525
|
+
];
|
|
1526
|
+
};
|
|
1527
|
+
const fieldsOf = (method) => {
|
|
1528
|
+
const fields = method.fields;
|
|
1529
|
+
if (fields === null) return null;
|
|
1530
|
+
const required = fields.filter((field) => field.required);
|
|
1531
|
+
const optional = fields.filter((field) => !field.required);
|
|
1532
|
+
return {
|
|
1533
|
+
explains: (required.find((field) => field.secret) ?? required[0] ?? optional[0])?.name ?? null,
|
|
1534
|
+
optional,
|
|
1535
|
+
required
|
|
1960
1536
|
};
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1537
|
+
};
|
|
1538
|
+
/** One provider's auth methods, arranged for a surface that asks for one decision at a time. */
|
|
1539
|
+
const describeMethods = (provider) => {
|
|
1540
|
+
const described = provider.methods.map((method) => ({
|
|
1541
|
+
descriptor: method,
|
|
1542
|
+
fields: fieldsOf(method),
|
|
1543
|
+
kind: method.kind
|
|
1544
|
+
}));
|
|
1545
|
+
const interactive = described.filter((method) => method.fields === null);
|
|
1546
|
+
const typed = described.filter((method) => method.fields !== null);
|
|
1547
|
+
return {
|
|
1548
|
+
alternate: interactive.length > 0 && typed.length > 0,
|
|
1549
|
+
interactive,
|
|
1550
|
+
typed
|
|
1551
|
+
};
|
|
1552
|
+
};
|
|
1968
1553
|
/**
|
|
1969
1554
|
* The one field a rejected command was about, if any. A provider that turns down credentials is
|
|
1970
1555
|
* answering the secret it was given, and it names no field, so the first secret carries the
|
|
1971
1556
|
* answer; anything else is about the request rather than one input.
|
|
1972
1557
|
*/
|
|
1973
|
-
const
|
|
1974
|
-
if (
|
|
1558
|
+
const rejectedField = (error, fields) => {
|
|
1559
|
+
if (error === null) return null;
|
|
1560
|
+
const reason = error.reason;
|
|
1975
1561
|
if (reason._tag === "InvalidInput") return fields.find((field) => field.name === reason.field)?.name ?? null;
|
|
1976
1562
|
if (reason._tag !== "Unauthenticated" && reason._tag !== "Forbidden") return null;
|
|
1977
1563
|
return fields.find((field) => field.secret)?.name ?? null;
|
|
1978
1564
|
};
|
|
1979
|
-
/**
|
|
1980
|
-
|
|
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
|
-
/**
|
|
2031
|
-
* One auth method, rendered from its descriptor. A token method draws an input per declared
|
|
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.
|
|
2034
|
-
*/
|
|
2035
|
-
function Method({ controller, method, provider }) {
|
|
2036
|
-
const { messages } = useDomainKit();
|
|
1565
|
+
/** Whether a failure was raised by this provider's method, so the form answers beside it. */
|
|
1566
|
+
const attempted = (controller, provider, method) => {
|
|
2037
1567
|
const state = controller.state;
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
disabled: busy,
|
|
2044
|
-
onClick: () => controller.connect({
|
|
2045
|
-
method: method.kind,
|
|
2046
|
-
provider: provider.id
|
|
2047
|
-
}),
|
|
2048
|
-
type: "button",
|
|
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] });
|
|
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);
|
|
2065
|
-
return /* @__PURE__ */ jsxs("form", {
|
|
2066
|
-
"data-domainkit-part": "token-connect",
|
|
2067
|
-
"data-provider": provider.id,
|
|
2068
|
-
onSubmit: (event) => {
|
|
2069
|
-
event.preventDefault();
|
|
2070
|
-
const data = new FormData(event.currentTarget);
|
|
2071
|
-
const values = {};
|
|
2072
|
-
for (const field of fields) {
|
|
2073
|
-
const value = data.get(field.name);
|
|
2074
|
-
if (typeof value === "string" && (value !== "" || field.required)) values[field.name] = value;
|
|
2075
|
-
}
|
|
2076
|
-
controller.connect({
|
|
2077
|
-
method: method.kind,
|
|
2078
|
-
provider: provider.id,
|
|
2079
|
-
values
|
|
2080
|
-
});
|
|
2081
|
-
},
|
|
2082
|
-
children: [
|
|
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)]
|
|
2091
|
-
}),
|
|
2092
|
-
failed && answered === null ? /* @__PURE__ */ jsx(Outcome$2, {
|
|
2093
|
-
controller,
|
|
2094
|
-
layout: "inline"
|
|
2095
|
-
}) : null,
|
|
2096
|
-
/* @__PURE__ */ jsx("button", {
|
|
2097
|
-
"data-domainkit-part": "token-submit",
|
|
2098
|
-
disabled: busy,
|
|
2099
|
-
type: "submit",
|
|
2100
|
-
children: messages.methodToken
|
|
2101
|
-
})
|
|
2102
|
-
]
|
|
2103
|
-
});
|
|
2104
|
-
}
|
|
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
|
-
}
|
|
1568
|
+
if (state._tag !== "Failure" || state.attempt === null) return null;
|
|
1569
|
+
return state.attempt.provider === provider && state.attempt.method === method ? state.error : null;
|
|
1570
|
+
};
|
|
1571
|
+
//#endregion
|
|
1572
|
+
//#region src/records.ts
|
|
2128
1573
|
/**
|
|
2129
|
-
*
|
|
2130
|
-
*
|
|
1574
|
+
* What a domain's requirements are, as data: the zone file they spell, the clipboard control a
|
|
1575
|
+
* value needs, and the standing one row reports. The table itself is the host's.
|
|
2131
1576
|
*/
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
const
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
}
|
|
2214
|
-
/** The connect surface. Without `render` it is a Base UI dialog behind `trigger`. */
|
|
2215
|
-
function Dialog$2({ children, controller, render, title, trigger }) {
|
|
2216
|
-
const { colorScheme, messages, portalContainer, themeStyle } = useDomainKit();
|
|
2217
|
-
const readOnly = useReadOnly();
|
|
2218
|
-
const [open, setOpen] = useState(false);
|
|
2219
|
-
const state = controller.state;
|
|
2220
|
-
const busy = state._tag === "Submitting";
|
|
2221
|
-
const unattributed = state._tag === "Failure" && !answeredInPlace(controller);
|
|
2222
|
-
const snapshot = controller.snapshot;
|
|
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;
|
|
2229
|
-
const body = children ?? /* @__PURE__ */ jsx(Form, { controller });
|
|
2230
|
-
if (readOnly) return /* @__PURE__ */ jsx(Fragment, {});
|
|
2231
|
-
if (render !== void 0) return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("button", {
|
|
2232
|
-
"data-domainkit-part": "connection-trigger",
|
|
2233
|
-
onClick: () => setOpen(!open),
|
|
2234
|
-
type: "button",
|
|
2235
|
-
children: trigger ?? label
|
|
2236
|
-
}), render({
|
|
2237
|
-
children: body,
|
|
2238
|
-
open
|
|
2239
|
-
})] });
|
|
2240
|
-
return /* @__PURE__ */ jsxs(Dialog.Root, {
|
|
2241
|
-
onOpenChange: (next, details) => {
|
|
2242
|
-
if (!next && busy) {
|
|
2243
|
-
details.cancel();
|
|
2244
|
-
return;
|
|
2245
|
-
}
|
|
2246
|
-
setOpen(next);
|
|
2247
|
-
},
|
|
2248
|
-
open,
|
|
2249
|
-
children: [/* @__PURE__ */ jsx(Dialog.Trigger, {
|
|
2250
|
-
"data-domainkit-part": "connection-trigger",
|
|
2251
|
-
children: trigger ?? label
|
|
2252
|
-
}), /* @__PURE__ */ jsxs(Dialog.Portal, {
|
|
2253
|
-
container: portalContainer,
|
|
2254
|
-
children: [/* @__PURE__ */ jsx(Dialog.Backdrop, {
|
|
2255
|
-
"data-color-scheme": colorScheme,
|
|
2256
|
-
"data-domainkit-part": "dialog-backdrop",
|
|
2257
|
-
"data-domainkit-root": "",
|
|
2258
|
-
style: themeStyle
|
|
2259
|
-
}), /* @__PURE__ */ jsxs(Dialog.Popup, {
|
|
2260
|
-
"data-color-scheme": colorScheme,
|
|
2261
|
-
"data-domainkit-part": "connection-dialog",
|
|
2262
|
-
"data-domainkit-root": "",
|
|
2263
|
-
style: themeStyle,
|
|
2264
|
-
children: [
|
|
2265
|
-
/* @__PURE__ */ jsxs("div", {
|
|
2266
|
-
"data-domainkit-part": "dialog-header",
|
|
2267
|
-
children: [/* @__PURE__ */ jsxs("div", {
|
|
2268
|
-
"data-domainkit-part": "dialog-heading",
|
|
2269
|
-
children: [/* @__PURE__ */ jsx(Dialog.Title, {
|
|
2270
|
-
"data-domainkit-part": "dialog-title",
|
|
2271
|
-
children: heading
|
|
2272
|
-
}), /* @__PURE__ */ jsx(Dialog.Description, {
|
|
2273
|
-
"data-domainkit-part": "dialog-description",
|
|
2274
|
-
children: messages.connectDescription(snapshot?.domain ?? "")
|
|
2275
|
-
})]
|
|
2276
|
-
}), busy ? null : /* @__PURE__ */ jsx(Dialog.Close, {
|
|
2277
|
-
"aria-label": messages.close,
|
|
2278
|
-
"data-domainkit-part": "dialog-close",
|
|
2279
|
-
children: "×"
|
|
2280
|
-
})]
|
|
2281
|
-
}),
|
|
2282
|
-
/* @__PURE__ */ jsxs("div", {
|
|
2283
|
-
"data-domainkit-part": "dialog-body",
|
|
2284
|
-
children: [
|
|
2285
|
-
running ? /* @__PURE__ */ jsx(Status$2, { controller }) : null,
|
|
2286
|
-
body,
|
|
2287
|
-
unattributed ? /* @__PURE__ */ jsx(Outcome$2, { controller }) : null
|
|
2288
|
-
]
|
|
2289
|
-
}),
|
|
2290
|
-
busy ? null : /* @__PURE__ */ jsx("div", {
|
|
2291
|
-
"data-domainkit-part": "dialog-footer",
|
|
2292
|
-
children: /* @__PURE__ */ jsx(Dialog.Close, {
|
|
2293
|
-
"data-domainkit-part": "dialog-cancel",
|
|
2294
|
-
children: messages.cancel
|
|
2295
|
-
})
|
|
2296
|
-
})
|
|
2297
|
-
]
|
|
2298
|
-
})]
|
|
2299
|
-
})]
|
|
2300
|
-
});
|
|
1577
|
+
var records_exports = /* @__PURE__ */ __exportAll({
|
|
1578
|
+
copyText: () => copyText,
|
|
1579
|
+
downloadZoneFile: () => downloadZoneFile,
|
|
1580
|
+
identity: () => identity,
|
|
1581
|
+
requirementsKey: () => requirementsKey,
|
|
1582
|
+
statusOf: () => statusOf,
|
|
1583
|
+
toZoneFile: () => toZoneFile,
|
|
1584
|
+
useCopy: () => useCopy
|
|
1585
|
+
});
|
|
1586
|
+
const absolute = (value) => value.endsWith(".") ? value : `${value}.`;
|
|
1587
|
+
const textEncoder = new TextEncoder();
|
|
1588
|
+
const escapeTxtCharacter = (character) => {
|
|
1589
|
+
if (character === "\"" || character === "\\") return `\\${character}`;
|
|
1590
|
+
const codePoint = character.codePointAt(0);
|
|
1591
|
+
if (codePoint !== void 0 && (codePoint < 32 || codePoint === 127)) return `\\${codePoint.toString().padStart(3, "0")}`;
|
|
1592
|
+
return character;
|
|
1593
|
+
};
|
|
1594
|
+
/** TXT strings are capped at 255 bytes each, so long values are split and quoted per chunk. */
|
|
1595
|
+
const txtValue = (value) => {
|
|
1596
|
+
const chunks = [];
|
|
1597
|
+
let chunk = "";
|
|
1598
|
+
let byteLength = 0;
|
|
1599
|
+
for (const character of value) {
|
|
1600
|
+
const characterByteLength = textEncoder.encode(character).byteLength;
|
|
1601
|
+
if (byteLength + characterByteLength > 255) {
|
|
1602
|
+
chunks.push(chunk);
|
|
1603
|
+
chunk = "";
|
|
1604
|
+
byteLength = 0;
|
|
1605
|
+
}
|
|
1606
|
+
chunk += escapeTxtCharacter(character);
|
|
1607
|
+
byteLength += characterByteLength;
|
|
1608
|
+
}
|
|
1609
|
+
chunks.push(chunk);
|
|
1610
|
+
return chunks.map((part) => `"${part}"`).join(" ");
|
|
1611
|
+
};
|
|
1612
|
+
/** The RR type as a zone file spells it. The tag and the type name are the same string. */
|
|
1613
|
+
const rrType = (record) => record._tag;
|
|
1614
|
+
const zoneData = (record) => {
|
|
1615
|
+
switch (record._tag) {
|
|
1616
|
+
case "A":
|
|
1617
|
+
case "AAAA": return record.address;
|
|
1618
|
+
case "CNAME": return absolute(record.target);
|
|
1619
|
+
case "NS": return absolute(record.nameserver);
|
|
1620
|
+
case "TXT": return txtValue(record.value);
|
|
1621
|
+
case "MX": return `${record.priority} ${absolute(record.exchange)}`;
|
|
1622
|
+
case "CAA": return `${record.flags} ${record.tag} "${record.value}"`;
|
|
1623
|
+
case "SRV": return `${record.priority} ${record.weight} ${record.port} ${absolute(record.target)}`;
|
|
1624
|
+
}
|
|
1625
|
+
};
|
|
1626
|
+
const toZoneFile = (records) => `${records.map((record) => `${absolute(record.name)}${record.ttl === null ? "" : ` ${record.ttl}`} IN ${rrType(record)} ${zoneData(record)}`).join("\n")}\n`;
|
|
1627
|
+
const downloadText = (filename, contents) => {
|
|
1628
|
+
if (typeof document === "undefined") return;
|
|
1629
|
+
const blob = new Blob([contents], { type: "text/plain;charset=utf-8" });
|
|
1630
|
+
const url = URL.createObjectURL(blob);
|
|
1631
|
+
const anchor = document.createElement("a");
|
|
1632
|
+
anchor.download = filename;
|
|
1633
|
+
anchor.href = url;
|
|
1634
|
+
document.body.append(anchor);
|
|
1635
|
+
anchor.click();
|
|
1636
|
+
anchor.remove();
|
|
1637
|
+
URL.revokeObjectURL(url);
|
|
1638
|
+
};
|
|
1639
|
+
const downloadZoneFile = (domain, records) => downloadText(`${domain}.txt`, toZoneFile(records));
|
|
1640
|
+
const copyText = (value) => {
|
|
1641
|
+
if (typeof navigator === "undefined" || navigator.clipboard === void 0) return Promise.resolve(false);
|
|
1642
|
+
return navigator.clipboard.writeText(value).then(() => true, () => false);
|
|
1643
|
+
};
|
|
1644
|
+
function useCopy(value, resetAfter = 2e3) {
|
|
1645
|
+
const [copied, setCopied] = useState(false);
|
|
1646
|
+
const reset = useRef(void 0);
|
|
1647
|
+
useEffect(() => () => clearTimeout(reset.current), []);
|
|
1648
|
+
return {
|
|
1649
|
+
copied,
|
|
1650
|
+
copy: () => {
|
|
1651
|
+
copyText(value).then((ok) => {
|
|
1652
|
+
if (!ok) return;
|
|
1653
|
+
setCopied(true);
|
|
1654
|
+
clearTimeout(reset.current);
|
|
1655
|
+
reset.current = setTimeout(() => setCopied(false), resetAfter);
|
|
1656
|
+
});
|
|
1657
|
+
}
|
|
1658
|
+
};
|
|
2301
1659
|
}
|
|
1660
|
+
/** The row's standing, or `null` when neither a plan nor an observation covers the record. */
|
|
1661
|
+
const statusOf = (record, sources) => {
|
|
1662
|
+
const operation = sources.plan?.operations.find((entry) => DnsRecord.equals(entry.record, record));
|
|
1663
|
+
if (operation !== void 0) return {
|
|
1664
|
+
_tag: "Operation",
|
|
1665
|
+
operation
|
|
1666
|
+
};
|
|
1667
|
+
const status = sources.readiness?.requirements.find((requirement) => DnsRecord.equals(requirement.record, record))?.status;
|
|
1668
|
+
return status === void 0 ? null : {
|
|
1669
|
+
_tag: "Readiness",
|
|
1670
|
+
status
|
|
1671
|
+
};
|
|
1672
|
+
};
|
|
1673
|
+
/** A stable React key for a requirement: type, name, and data identify a record. */
|
|
1674
|
+
const identity = (record) => [
|
|
1675
|
+
rrType(record),
|
|
1676
|
+
record.name,
|
|
1677
|
+
DnsRecord.data(record)
|
|
1678
|
+
].join(":");
|
|
2302
1679
|
/**
|
|
2303
|
-
*
|
|
2304
|
-
*
|
|
2305
|
-
*
|
|
1680
|
+
* A requirement set keyed by everything it carries, for memos that decide whether to re-send it.
|
|
1681
|
+
* `identity` is deliberately narrower: it answers "is this the same record" for a React key, while
|
|
1682
|
+
* a plan and an observation are built from `ttl`, `policy`, and `purpose` as well. `policy` in
|
|
1683
|
+
* particular decides whether a name may hold anything else, so readiness turns on it.
|
|
2306
1684
|
*/
|
|
2307
|
-
|
|
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
|
-
}
|
|
2337
|
-
/** A connected domain: which provider holds it, and how to let it go. */
|
|
2338
|
-
function Card({ controller, ...props }) {
|
|
2339
|
-
const { messages } = useDomainKit();
|
|
2340
|
-
const readOnly = useReadOnly();
|
|
2341
|
-
const state = controller.state;
|
|
2342
|
-
const snapshot = controller.snapshot;
|
|
2343
|
-
const provider = controller.providers.find((entry) => entry.id === snapshot?.provider);
|
|
2344
|
-
return usePart("div", props, { status: state._tag }, {
|
|
2345
|
-
children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2346
|
-
/* @__PURE__ */ jsxs("div", {
|
|
2347
|
-
"data-domainkit-part": "connected-identity",
|
|
2348
|
-
children: [provider === void 0 ? null : /* @__PURE__ */ jsx(Mark, { provider }), /* @__PURE__ */ jsx(Status$2, { controller })]
|
|
2349
|
-
}),
|
|
2350
|
-
readOnly ? null : /* @__PURE__ */ jsxs("div", {
|
|
2351
|
-
"data-domainkit-part": "connected-actions",
|
|
2352
|
-
children: [/* @__PURE__ */ jsx("button", {
|
|
2353
|
-
"data-domainkit-part": "disconnect-trigger",
|
|
2354
|
-
disabled: state._tag === "Submitting",
|
|
2355
|
-
onClick: controller.detach,
|
|
2356
|
-
type: "button",
|
|
2357
|
-
children: messages.detach
|
|
2358
|
-
}), /* @__PURE__ */ jsx("button", {
|
|
2359
|
-
"data-domainkit-part": "disconnect-action",
|
|
2360
|
-
disabled: state._tag === "Submitting",
|
|
2361
|
-
onClick: controller.disconnect,
|
|
2362
|
-
type: "button",
|
|
2363
|
-
children: messages.disconnect
|
|
2364
|
-
})]
|
|
2365
|
-
}),
|
|
2366
|
-
/* @__PURE__ */ jsx(Outcome$2, { controller })
|
|
2367
|
-
] }),
|
|
2368
|
-
"data-domainkit-part": "connected-card"
|
|
2369
|
-
});
|
|
2370
|
-
}
|
|
2371
|
-
/** Connection on its own: the card once connected, the prompt until then. */
|
|
2372
|
-
function Flow$2({ connect, domain, returnTo, ...props }) {
|
|
2373
|
-
const controller = useController$2({
|
|
2374
|
-
domain,
|
|
2375
|
-
...returnTo === void 0 ? {} : { returnTo }
|
|
2376
|
-
});
|
|
2377
|
-
const state = controller.state;
|
|
2378
|
-
return /* @__PURE__ */ jsx(Root$2, {
|
|
2379
|
-
controller,
|
|
2380
|
-
...props,
|
|
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 })] })
|
|
2385
|
-
});
|
|
2386
|
-
}
|
|
1685
|
+
const requirementsKey = (records) => JSON.stringify(records);
|
|
2387
1686
|
//#endregion
|
|
2388
|
-
//#region src/provision.
|
|
1687
|
+
//#region src/provision.ts
|
|
2389
1688
|
var provision_exports = /* @__PURE__ */ __exportAll({
|
|
2390
|
-
Actions: () => Actions,
|
|
2391
|
-
Dialog: () => Dialog$1,
|
|
2392
|
-
Flow: () => Flow$1,
|
|
2393
|
-
Outcome: () => Outcome$1,
|
|
2394
|
-
Root: () => Root$1,
|
|
2395
1689
|
State: () => State$2,
|
|
2396
|
-
|
|
1690
|
+
pendingPlan: () => pendingPlan,
|
|
1691
|
+
planOf: () => planOf,
|
|
2397
1692
|
useController: () => useController$1
|
|
2398
1693
|
});
|
|
2399
|
-
/** Plan, approve, apply. `approve` authorizes the digest and applies it in the same action. */
|
|
2400
1694
|
/**
|
|
1695
|
+
* Plan, approve, apply; `approve` authorizes the digest and applies it in the same action.
|
|
2401
1696
|
* Requirements identify themselves by content, so an inline array does not abandon the attempt.
|
|
2402
1697
|
* Every field counts: a plan turns on `policy`, and `ttl` and `purpose` ride the wire with it.
|
|
2403
1698
|
*/
|
|
2404
1699
|
const keyOf = (domain, requirements) => [domain, requirementsKey(requirements)].join("|");
|
|
2405
|
-
function useController$1({ domain, onApplied, requirements }) {
|
|
1700
|
+
function useController$1({ domain, onApplied, readOnly, requirements }) {
|
|
2406
1701
|
const { transport } = useDomainKit();
|
|
2407
1702
|
const group = transport.provisioning;
|
|
2408
1703
|
return useAttempt({
|
|
@@ -2414,6 +1709,7 @@ function useController$1({ domain, onApplied, requirements }) {
|
|
|
2414
1709
|
}),
|
|
2415
1710
|
group,
|
|
2416
1711
|
onDone: onApplied,
|
|
1712
|
+
readOnly,
|
|
2417
1713
|
plan: useCallback(() => group === void 0 ? null : group.plan({
|
|
2418
1714
|
domain,
|
|
2419
1715
|
requirements
|
|
@@ -2424,63 +1720,12 @@ function useController$1({ domain, onApplied, requirements }) {
|
|
|
2424
1720
|
])
|
|
2425
1721
|
});
|
|
2426
1722
|
}
|
|
2427
|
-
function Root$1({ controller, ...props }) {
|
|
2428
|
-
return usePart("div", props, { status: controller.state._tag }, {
|
|
2429
|
-
"data-domainkit-part": "provisioning-flow",
|
|
2430
|
-
"data-state": controller.state._tag
|
|
2431
|
-
});
|
|
2432
|
-
}
|
|
2433
|
-
function Dialog$1(props) {
|
|
2434
|
-
return /* @__PURE__ */ jsx(Dialog$4, {
|
|
2435
|
-
...props,
|
|
2436
|
-
kind: "provisioning"
|
|
2437
|
-
});
|
|
2438
|
-
}
|
|
2439
|
-
/** Approve and Decline for a plan already under review. */
|
|
2440
|
-
function Actions(props) {
|
|
2441
|
-
return /* @__PURE__ */ jsx(Actions$2, {
|
|
2442
|
-
...props,
|
|
2443
|
-
kind: "provisioning"
|
|
2444
|
-
});
|
|
2445
|
-
}
|
|
2446
|
-
function Outcome$1(props) {
|
|
2447
|
-
return /* @__PURE__ */ jsx(Outcome$4, {
|
|
2448
|
-
...props,
|
|
2449
|
-
kind: "provisioning"
|
|
2450
|
-
});
|
|
2451
|
-
}
|
|
2452
|
-
function Status$1(props) {
|
|
2453
|
-
return /* @__PURE__ */ jsx(Status$4, {
|
|
2454
|
-
...props,
|
|
2455
|
-
kind: "provisioning"
|
|
2456
|
-
});
|
|
2457
|
-
}
|
|
2458
|
-
/** Provisioning on its own: a trigger that plans, then the review dialog. */
|
|
2459
|
-
function Flow$1({ domain, onApplied, requirements, trigger, ...props }) {
|
|
2460
|
-
const controller = useController$1({
|
|
2461
|
-
domain,
|
|
2462
|
-
requirements,
|
|
2463
|
-
...onApplied === void 0 ? {} : { onApplied }
|
|
2464
|
-
});
|
|
2465
|
-
return /* @__PURE__ */ jsxs(Root$1, {
|
|
2466
|
-
controller,
|
|
2467
|
-
...props,
|
|
2468
|
-
children: [/* @__PURE__ */ jsx(Dialog$1, {
|
|
2469
|
-
controller,
|
|
2470
|
-
...trigger === void 0 ? {} : { trigger }
|
|
2471
|
-
}), /* @__PURE__ */ jsx(Outcome$1, { controller })]
|
|
2472
|
-
});
|
|
2473
|
-
}
|
|
2474
1723
|
//#endregion
|
|
2475
|
-
//#region src/verify.
|
|
1724
|
+
//#region src/verify.ts
|
|
2476
1725
|
var verify_exports = /* @__PURE__ */ __exportAll({
|
|
2477
|
-
Evidence: () => Evidence,
|
|
2478
|
-
ObserveAction: () => ObserveAction,
|
|
2479
|
-
Outcome: () => Outcome,
|
|
2480
|
-
Root: () => Root,
|
|
2481
1726
|
State: () => State,
|
|
2482
|
-
|
|
2483
|
-
|
|
1727
|
+
useController: () => useController,
|
|
1728
|
+
valuesOf: () => valuesOf
|
|
2484
1729
|
});
|
|
2485
1730
|
/**
|
|
2486
1731
|
* What the observer read back for the requirement's name, or `null` when it read nothing back at
|
|
@@ -2499,9 +1744,11 @@ function useController({ domain, polling = true, requirements }) {
|
|
|
2499
1744
|
const timer = useRef(void 0);
|
|
2500
1745
|
const signature = requirements === void 0 ? null : requirementsKey(requirements);
|
|
2501
1746
|
const requested = useMemo(() => requirements === void 0 ? void 0 : { requirements }, [signature]);
|
|
1747
|
+
const held = useRef(domain);
|
|
2502
1748
|
const [observed, setObserved] = useState(domain);
|
|
2503
1749
|
if (observed !== domain) {
|
|
2504
1750
|
setObserved(domain);
|
|
1751
|
+
held.current = domain;
|
|
2505
1752
|
setState(State.Idle());
|
|
2506
1753
|
}
|
|
2507
1754
|
const observe = useCallback(() => {
|
|
@@ -2510,6 +1757,7 @@ function useController({ domain, polling = true, requirements }) {
|
|
|
2510
1757
|
setState((previous) => State.Observing({ readiness: readinessOf(previous) }));
|
|
2511
1758
|
runner.run(requested === void 0 ? verification.observe(domain) : verification.observe(domain, requested), {
|
|
2512
1759
|
onFailure: (error) => {
|
|
1760
|
+
if (held.current !== domain) return;
|
|
2513
1761
|
setState((previous) => State.Failure({
|
|
2514
1762
|
error,
|
|
2515
1763
|
readiness: readinessOf(previous)
|
|
@@ -2519,7 +1767,10 @@ function useController({ domain, polling = true, requirements }) {
|
|
|
2519
1767
|
error
|
|
2520
1768
|
}));
|
|
2521
1769
|
},
|
|
2522
|
-
onSuccess: (readiness) =>
|
|
1770
|
+
onSuccess: (readiness) => {
|
|
1771
|
+
if (held.current !== domain) return;
|
|
1772
|
+
setState(State.Observed({ readiness }));
|
|
1773
|
+
}
|
|
2523
1774
|
});
|
|
2524
1775
|
}, [
|
|
2525
1776
|
domain,
|
|
@@ -2552,302 +1803,114 @@ function useController({ domain, polling = true, requirements }) {
|
|
|
2552
1803
|
state
|
|
2553
1804
|
};
|
|
2554
1805
|
}
|
|
2555
|
-
function Root({ controller, ...props }) {
|
|
2556
|
-
const overall = controller.readiness?.overall ?? null;
|
|
2557
|
-
return usePart("div", props, { status: controller.state._tag }, {
|
|
2558
|
-
"data-domainkit-part": "verification-status",
|
|
2559
|
-
"data-overall": overall ?? void 0,
|
|
2560
|
-
"data-state": controller.state._tag
|
|
2561
|
-
});
|
|
2562
|
-
}
|
|
2563
|
-
function ObserveAction({ controller, ...props }) {
|
|
2564
|
-
const { messages } = useDomainKit();
|
|
2565
|
-
const observing = controller.state._tag === "Observing";
|
|
2566
|
-
return usePart("button", props, { status: controller.state._tag }, {
|
|
2567
|
-
"aria-busy": observing,
|
|
2568
|
-
children: controller.readiness === null ? messages.checkDns : messages.checkAgain,
|
|
2569
|
-
"data-domainkit-part": "observe-action",
|
|
2570
|
-
"data-loading": observing ? "" : void 0,
|
|
2571
|
-
disabled: observing,
|
|
2572
|
-
onClick: controller.observe,
|
|
2573
|
-
type: "button"
|
|
2574
|
-
});
|
|
2575
|
-
}
|
|
2576
|
-
/** Evidence carries no id; its source and position identify it within one requirement. */
|
|
2577
|
-
const evidenceKey = (evidence, index) => [evidence._tag, index].join("-");
|
|
2578
|
-
/**
|
|
2579
|
-
* Per-requirement readiness and, below it, the host's own evidence: an SES identity, a certificate,
|
|
2580
|
-
* anything DomainKit cannot observe but the host feeds in.
|
|
2581
|
-
*/
|
|
2582
|
-
function Evidence({ readiness }) {
|
|
2583
|
-
const { messages } = useDomainKit();
|
|
2584
|
-
const icons = useIcons();
|
|
2585
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
2586
|
-
"data-domainkit-part": "observation-list",
|
|
2587
|
-
children: [readiness.requirements.map((requirement) => {
|
|
2588
|
-
const settled = requirement.status === "satisfied";
|
|
2589
|
-
return /* @__PURE__ */ jsxs("section", {
|
|
2590
|
-
"data-domainkit-part": "observation-group",
|
|
2591
|
-
children: [
|
|
2592
|
-
/* @__PURE__ */ jsxs("div", {
|
|
2593
|
-
"data-domainkit-part": "observation-row",
|
|
2594
|
-
children: [/* @__PURE__ */ jsxs("span", {
|
|
2595
|
-
"data-domainkit-part": "observation-record",
|
|
2596
|
-
children: [
|
|
2597
|
-
messages.recordType(requirement.record),
|
|
2598
|
-
" ",
|
|
2599
|
-
requirement.record.name
|
|
2600
|
-
]
|
|
2601
|
-
}), /* @__PURE__ */ jsx(Status$5, { status: requirement.status })]
|
|
2602
|
-
}),
|
|
2603
|
-
settled ? null : /* @__PURE__ */ jsx("p", {
|
|
2604
|
-
"data-domainkit-part": "observation-expected",
|
|
2605
|
-
children: messages.expectedValue(DnsRecord.data(requirement.record))
|
|
2606
|
-
}),
|
|
2607
|
-
/* @__PURE__ */ jsx("ul", { children: requirement.evidence.map((evidence, index) => {
|
|
2608
|
-
const values = settled ? null : valuesOf(evidence);
|
|
2609
|
-
return /* @__PURE__ */ jsxs("li", {
|
|
2610
|
-
"data-domainkit-part": "observation-source",
|
|
2611
|
-
children: [
|
|
2612
|
-
messages.evidenceSource(evidence),
|
|
2613
|
-
values === null ? null : /* @__PURE__ */ jsx("span", {
|
|
2614
|
-
"data-domainkit-part": "observation-observed",
|
|
2615
|
-
children: values.length === 0 ? messages.observedNothing : messages.observedValues(values)
|
|
2616
|
-
}),
|
|
2617
|
-
evidence.detail === null ? null : /* @__PURE__ */ jsx("p", {
|
|
2618
|
-
"data-domainkit-part": "observation-note",
|
|
2619
|
-
children: evidence.detail
|
|
2620
|
-
})
|
|
2621
|
-
]
|
|
2622
|
-
}, evidenceKey(evidence, index));
|
|
2623
|
-
}) })
|
|
2624
|
-
]
|
|
2625
|
-
}, identity(requirement.record));
|
|
2626
|
-
}), readiness.host.length === 0 ? null : /* @__PURE__ */ jsxs("section", {
|
|
2627
|
-
"data-domainkit-part": "observation-group",
|
|
2628
|
-
"data-source": "host",
|
|
2629
|
-
children: [/* @__PURE__ */ jsx("h3", {
|
|
2630
|
-
"data-domainkit-part": "observation-source",
|
|
2631
|
-
children: messages.hostEvidence
|
|
2632
|
-
}), /* @__PURE__ */ jsx("ul", { children: readiness.host.map((evidence, index) => /* @__PURE__ */ jsxs("li", {
|
|
2633
|
-
"data-domainkit-part": "host-evidence",
|
|
2634
|
-
children: [
|
|
2635
|
-
/* @__PURE__ */ jsx("span", {
|
|
2636
|
-
"aria-hidden": "true",
|
|
2637
|
-
"data-icon": "inline-start",
|
|
2638
|
-
children: evidence.status === "ok" ? icons.success : evidence.status === "pending" ? icons.pending : icons.failure
|
|
2639
|
-
}),
|
|
2640
|
-
/* @__PURE__ */ jsx("span", {
|
|
2641
|
-
"data-domainkit-part": "observation-record",
|
|
2642
|
-
children: evidence.label
|
|
2643
|
-
}),
|
|
2644
|
-
/* @__PURE__ */ jsx("span", {
|
|
2645
|
-
"data-domainkit-part": "record-status",
|
|
2646
|
-
"data-status": evidence.status,
|
|
2647
|
-
children: messages.hostEvidenceStatus(evidence.status)
|
|
2648
|
-
}),
|
|
2649
|
-
evidence.detail === null ? null : /* @__PURE__ */ jsx("p", {
|
|
2650
|
-
"data-domainkit-part": "observation-note",
|
|
2651
|
-
children: evidence.detail
|
|
2652
|
-
})
|
|
2653
|
-
]
|
|
2654
|
-
}, `${evidence.source}-${index}`)) })]
|
|
2655
|
-
})]
|
|
2656
|
-
});
|
|
2657
|
-
}
|
|
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 }) {
|
|
2663
|
-
const { messages } = useDomainKit();
|
|
2664
|
-
const state = controller.state;
|
|
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
|
-
})
|
|
2680
|
-
});
|
|
2681
|
-
}
|
|
2682
|
-
/** The default verification slot: a button that observes, with the evidence behind it. */
|
|
2683
|
-
function Status({ controller, render, ...props }) {
|
|
2684
|
-
const { colorScheme, messages, portalContainer, themeStyle } = useDomainKit();
|
|
2685
|
-
const readiness = controller.readiness;
|
|
2686
|
-
const body = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2687
|
-
controller.state._tag === "Observing" ? /* @__PURE__ */ jsx("p", {
|
|
2688
|
-
"data-domainkit-part": "verification-progress",
|
|
2689
|
-
role: "status",
|
|
2690
|
-
children: messages.observing
|
|
2691
|
-
}) : null,
|
|
2692
|
-
readiness === null ? /* @__PURE__ */ jsx("p", {
|
|
2693
|
-
"data-domainkit-part": "verification-progress",
|
|
2694
|
-
children: messages.noEvidence
|
|
2695
|
-
}) : /* @__PURE__ */ jsx(Evidence, { readiness }),
|
|
2696
|
-
/* @__PURE__ */ jsx(Outcome, { controller })
|
|
2697
|
-
] });
|
|
2698
|
-
return /* @__PURE__ */ jsx(Root, {
|
|
2699
|
-
controller,
|
|
2700
|
-
...props,
|
|
2701
|
-
children: render === void 0 ? /* @__PURE__ */ jsxs(Popover.Root, { children: [/* @__PURE__ */ jsx(Popover.Trigger, { render: /* @__PURE__ */ jsx(ObserveAction, { controller }) }), /* @__PURE__ */ jsx(Popover.Portal, {
|
|
2702
|
-
container: portalContainer,
|
|
2703
|
-
children: /* @__PURE__ */ jsx(Popover.Positioner, {
|
|
2704
|
-
align: "end",
|
|
2705
|
-
"data-domainkit-part": "verification-positioner",
|
|
2706
|
-
sideOffset: 6,
|
|
2707
|
-
children: /* @__PURE__ */ jsxs(Popover.Popup, {
|
|
2708
|
-
"aria-busy": controller.state._tag === "Observing",
|
|
2709
|
-
"aria-label": messages.checkDns,
|
|
2710
|
-
"data-color-scheme": colorScheme,
|
|
2711
|
-
"data-domainkit-part": "verification-popover",
|
|
2712
|
-
"data-domainkit-root": "",
|
|
2713
|
-
style: themeStyle,
|
|
2714
|
-
children: [/* @__PURE__ */ jsx(Popover.Arrow, { "data-domainkit-part": "verification-arrow" }), body]
|
|
2715
|
-
})
|
|
2716
|
-
})
|
|
2717
|
-
})] }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(ObserveAction, { controller }), render({
|
|
2718
|
-
children: body,
|
|
2719
|
-
open: controller.state._tag !== "Idle"
|
|
2720
|
-
})] })
|
|
2721
|
-
});
|
|
2722
|
-
}
|
|
2723
1806
|
//#endregion
|
|
2724
|
-
//#region src/domain.
|
|
2725
|
-
var domain_exports = /* @__PURE__ */ __exportAll({ Flow: () => Flow });
|
|
2726
|
-
function DefaultConnection({ connect, controller }) {
|
|
2727
|
-
const stated = controller.state._tag === "Disconnected" && hostProvider(controller) !== null;
|
|
2728
|
-
return controller.state._tag === "Connected" ? /* @__PURE__ */ jsx(Card, { controller }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
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 })
|
|
2735
|
-
] });
|
|
2736
|
-
}
|
|
2737
|
-
function DefaultActions({ cleanup, connection, provisioning }) {
|
|
2738
|
-
const { capabilities, messages } = useDomainKit();
|
|
2739
|
-
const readOnly = useReadOnly();
|
|
2740
|
-
const icons = useIcons();
|
|
2741
|
-
const connected = connection.state._tag === "Connected";
|
|
2742
|
-
if (readOnly) return null;
|
|
2743
|
-
const hasReceipt = connection.snapshot?.lastReceiptId != null;
|
|
2744
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [capabilities.includes("provisioning") && connected ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2745
|
-
/* @__PURE__ */ jsx("button", {
|
|
2746
|
-
"data-domainkit-part": "plan-trigger",
|
|
2747
|
-
disabled: provisioning.state._tag === "Planning",
|
|
2748
|
-
onClick: provisioning.plan,
|
|
2749
|
-
type: "button",
|
|
2750
|
-
children: messages.reviewChanges
|
|
2751
|
-
}),
|
|
2752
|
-
/* @__PURE__ */ jsx(Status$1, { controller: provisioning }),
|
|
2753
|
-
/* @__PURE__ */ jsx(Actions, { controller: provisioning }),
|
|
2754
|
-
/* @__PURE__ */ jsx(Outcome$1, { controller: provisioning })
|
|
2755
|
-
] }) : null, capabilities.includes("cleanup") && connected && hasReceipt ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2756
|
-
/* @__PURE__ */ jsxs("button", {
|
|
2757
|
-
"data-domainkit-part": "cleanup-trigger",
|
|
2758
|
-
disabled: cleanup.state._tag === "Planning",
|
|
2759
|
-
onClick: cleanup.plan,
|
|
2760
|
-
type: "button",
|
|
2761
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
2762
|
-
"aria-hidden": "true",
|
|
2763
|
-
"data-icon": "inline-start",
|
|
2764
|
-
children: icons.close
|
|
2765
|
-
}), messages.cleanUp]
|
|
2766
|
-
}),
|
|
2767
|
-
/* @__PURE__ */ jsx(Status$3, { controller: cleanup }),
|
|
2768
|
-
/* @__PURE__ */ jsx(Actions$1, { controller: cleanup }),
|
|
2769
|
-
/* @__PURE__ */ jsx(Outcome$3, { controller: cleanup })
|
|
2770
|
-
] }) : null] });
|
|
2771
|
-
}
|
|
1807
|
+
//#region src/domain.ts
|
|
2772
1808
|
/**
|
|
2773
|
-
* The whole lifecycle for one domain: connect,
|
|
2774
|
-
*
|
|
2775
|
-
*
|
|
1809
|
+
* The whole lifecycle for one domain in one hook: connect, add the records, observe, clean up.
|
|
1810
|
+
* `useFlow` runs the four controllers together and plans as soon as the domain is attached with
|
|
1811
|
+
* nothing applied, so the styled surface is only markup.
|
|
2776
1812
|
*/
|
|
2777
|
-
|
|
1813
|
+
var domain_exports = /* @__PURE__ */ __exportAll({ useFlow: () => useFlow });
|
|
1814
|
+
/** Built from primitives alone, so the memo that reports it depends on exactly those. */
|
|
1815
|
+
const flowState = (input) => ({
|
|
1816
|
+
applied: input.receipt !== null,
|
|
1817
|
+
connected: input.connected,
|
|
1818
|
+
connection: input.status,
|
|
1819
|
+
label: input.label,
|
|
1820
|
+
offering: input.offering,
|
|
1821
|
+
provider: input.provider,
|
|
1822
|
+
provisioning: input.planning,
|
|
1823
|
+
readOnly: input.readOnly,
|
|
1824
|
+
receiptId: input.receipt === null ? null : Receipt.ReceiptId.make(input.receipt)
|
|
1825
|
+
});
|
|
1826
|
+
function useFlow({ connect = "detected", domain, onApplied, onCleaned, readOnly, requirements, returnTo }) {
|
|
2778
1827
|
const { capabilities } = useDomainKit();
|
|
2779
1828
|
const inherited = useReadOnly();
|
|
1829
|
+
const surfaceReadOnly = readOnly ?? inherited;
|
|
2780
1830
|
const connection = useController$2({
|
|
2781
1831
|
domain,
|
|
1832
|
+
readOnly: surfaceReadOnly,
|
|
2782
1833
|
...returnTo === void 0 ? {} : { returnTo }
|
|
2783
1834
|
});
|
|
2784
1835
|
const refresh = connection.refresh;
|
|
2785
1836
|
const provisioning = useController$1({
|
|
2786
1837
|
domain,
|
|
1838
|
+
readOnly: surfaceReadOnly,
|
|
2787
1839
|
onApplied: useCallback((receipt) => {
|
|
2788
1840
|
refresh();
|
|
2789
1841
|
onApplied?.(receipt);
|
|
2790
1842
|
}, [onApplied, refresh]),
|
|
2791
1843
|
requirements
|
|
2792
1844
|
});
|
|
2793
|
-
const cleanup = useController$3({
|
|
2794
|
-
domain,
|
|
2795
|
-
onCleaned: useCallback((receipt) => {
|
|
2796
|
-
refresh();
|
|
2797
|
-
onCleaned?.(receipt);
|
|
2798
|
-
}, [onCleaned, refresh]),
|
|
2799
|
-
...connection.snapshot?.lastReceiptId == null ? {} : { receiptId: Receipt.ReceiptId.make(connection.snapshot.lastReceiptId) }
|
|
2800
|
-
});
|
|
2801
1845
|
const verification = useController({
|
|
2802
1846
|
domain,
|
|
2803
1847
|
requirements
|
|
2804
1848
|
});
|
|
2805
|
-
const
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
value: readOnly ?? inherited,
|
|
2812
|
-
children: [
|
|
2813
|
-
!capabilities.includes("connection") ? null : slots.connection === void 0 ? /* @__PURE__ */ jsx(DefaultConnection, {
|
|
2814
|
-
connect,
|
|
2815
|
-
controller: connection,
|
|
2816
|
-
domain
|
|
2817
|
-
}) : slots.connection({
|
|
2818
|
-
connect,
|
|
2819
|
-
controller: connection,
|
|
2820
|
-
domain
|
|
2821
|
-
}),
|
|
2822
|
-
slots.records === void 0 ? /* @__PURE__ */ jsx(Table, {
|
|
2823
|
-
readiness,
|
|
2824
|
-
records: requirements
|
|
2825
|
-
}) : slots.records({
|
|
2826
|
-
controller: verification,
|
|
2827
|
-
domain,
|
|
2828
|
-
readiness,
|
|
2829
|
-
records: requirements
|
|
2830
|
-
}),
|
|
2831
|
-
!capabilities.includes("verification") ? null : slots.verification === void 0 ? /* @__PURE__ */ jsx(Status, { controller: verification }) : slots.verification({
|
|
2832
|
-
controller: verification,
|
|
2833
|
-
domain
|
|
2834
|
-
}),
|
|
2835
|
-
slots.actions === void 0 ? /* @__PURE__ */ jsx(DefaultActions, {
|
|
2836
|
-
cleanup,
|
|
2837
|
-
connection,
|
|
2838
|
-
domain,
|
|
2839
|
-
provisioning
|
|
2840
|
-
}) : slots.actions({
|
|
2841
|
-
cleanup,
|
|
2842
|
-
connection,
|
|
2843
|
-
domain,
|
|
2844
|
-
provisioning
|
|
2845
|
-
})
|
|
2846
|
-
]
|
|
2847
|
-
}),
|
|
2848
|
-
"data-domainkit-part": "domain-flow",
|
|
2849
|
-
"data-domain": domain
|
|
1849
|
+
const receiptId = connection.snapshot?.lastReceiptId ?? null;
|
|
1850
|
+
const cleanup = useController$3({
|
|
1851
|
+
domain,
|
|
1852
|
+
readOnly: surfaceReadOnly,
|
|
1853
|
+
...onCleaned === void 0 ? {} : { onCleaned },
|
|
1854
|
+
...receiptId === null ? {} : { receiptId: Receipt.ReceiptId.make(receiptId) }
|
|
2850
1855
|
});
|
|
1856
|
+
const attached = connection.snapshot?.attachment != null;
|
|
1857
|
+
const signature = surfaceReadOnly || !capabilities.includes("provisioning") || !attached || receiptId !== null ? null : [
|
|
1858
|
+
domain,
|
|
1859
|
+
connection.established,
|
|
1860
|
+
requirementsKey(requirements)
|
|
1861
|
+
].join("|");
|
|
1862
|
+
const planned = useRef(null);
|
|
1863
|
+
const buildPlan = provisioning.plan;
|
|
1864
|
+
useEffect(() => {
|
|
1865
|
+
if (signature === null) {
|
|
1866
|
+
planned.current = null;
|
|
1867
|
+
return;
|
|
1868
|
+
}
|
|
1869
|
+
if (planned.current === signature) return;
|
|
1870
|
+
planned.current = signature;
|
|
1871
|
+
buildPlan();
|
|
1872
|
+
}, [buildPlan, signature]);
|
|
1873
|
+
const connected = holdsConnection(connection);
|
|
1874
|
+
const offering$1 = offering(connection, connect);
|
|
1875
|
+
const provider = connection.snapshot?.provider ?? hostProvider(connection)?.id ?? null;
|
|
1876
|
+
return {
|
|
1877
|
+
capabilities,
|
|
1878
|
+
cleanup,
|
|
1879
|
+
connection,
|
|
1880
|
+
domain,
|
|
1881
|
+
invitation: connect,
|
|
1882
|
+
plan: pendingPlan(provisioning.state),
|
|
1883
|
+
provisioning,
|
|
1884
|
+
readiness: verification.readiness,
|
|
1885
|
+
requirements,
|
|
1886
|
+
state: flowState({
|
|
1887
|
+
connected,
|
|
1888
|
+
label: connection.snapshot?.attachment?.label ?? null,
|
|
1889
|
+
offering: offering$1,
|
|
1890
|
+
planning: provisioning.state._tag,
|
|
1891
|
+
provider,
|
|
1892
|
+
readOnly: surfaceReadOnly,
|
|
1893
|
+
receipt: receiptId,
|
|
1894
|
+
status: connection.state._tag
|
|
1895
|
+
}),
|
|
1896
|
+
verification
|
|
1897
|
+
};
|
|
1898
|
+
}
|
|
1899
|
+
//#endregion
|
|
1900
|
+
//#region src/outcome.ts
|
|
1901
|
+
var outcome_exports = /* @__PURE__ */ __exportAll({
|
|
1902
|
+
describe: () => describe,
|
|
1903
|
+
useDescribe: () => useDescribe
|
|
1904
|
+
});
|
|
1905
|
+
/** The catalog's title and description for one failure. */
|
|
1906
|
+
const describe = (error, catalog, context = {}) => outcome(error, catalog, context);
|
|
1907
|
+
/**
|
|
1908
|
+
* `describe` bound to the catalog `DomainKit.Root` holds. The reason alone cannot name the
|
|
1909
|
+
* provider a customer typed a token for, so the surface supplies it in the context.
|
|
1910
|
+
*/
|
|
1911
|
+
function useDescribe() {
|
|
1912
|
+
const messages = useMessages();
|
|
1913
|
+
return (error, context = {}) => outcome(error, messages, context);
|
|
2851
1914
|
}
|
|
2852
1915
|
//#endregion
|
|
2853
1916
|
//#region src/testing.ts
|
|
@@ -2861,6 +1924,6 @@ const transport = Testing.transport;
|
|
|
2861
1924
|
//#region src/failure.ts
|
|
2862
1925
|
const Failure = Data.taggedEnum();
|
|
2863
1926
|
//#endregion
|
|
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,
|
|
1927
|
+
export { cleanup_exports as Cleanup, connect_exports as Connect, domain_exports as Domain, domain_kit_exports as DomainKit, Event, Failure, messages_exports as Messages, outcome_exports as Outcome, provision_exports as Provision, records_exports as Records, testing_exports as Testing, verify_exports as Verify };
|
|
2865
1928
|
|
|
2866
1929
|
//# sourceMappingURL=index.mjs.map
|