@bkazemi/verily 0.5.0 → 0.7.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 +25 -6
- package/dist/browser/badge.d.ts +4 -1
- package/dist/browser/badge.js +7 -4
- package/dist/browser/connect-dialog.js +10 -7
- package/dist/browser/evidence-dialog.d.ts +23 -4
- package/dist/browser/evidence-dialog.js +335 -49
- package/dist/browser/index.d.ts +7 -1
- package/dist/browser/index.js +27 -42
- package/dist/core/index.d.ts +106 -2
- package/dist/core/index.js +74 -2
- package/dist/server/discord.js +3 -0
- package/dist/server/domain.js +37 -10
- package/dist/server/github-gist.js +16 -4
- package/dist/server/github.js +3 -0
- package/dist/server/index.js +108 -11
- package/dist/server/link.js +9 -4
- package/dist/server/openpgp.d.ts +4 -0
- package/dist/server/openpgp.js +10 -1
- package/dist/server/service.d.ts +59 -2
- package/dist/server/service.js +240 -22
- package/dist/server/style.d.ts +1 -1
- package/dist/server/style.js +50 -1
- package/dist/server/youtube.js +5 -0
- package/dist/verily.js +345 -84
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/verily.js
CHANGED
|
@@ -9573,13 +9573,18 @@ var Verily = (() => {
|
|
|
9573
9573
|
// src/browser.ts
|
|
9574
9574
|
var browser_exports = {};
|
|
9575
9575
|
__export(browser_exports, {
|
|
9576
|
+
Absent: () => Absent,
|
|
9576
9577
|
Refused: () => Refused,
|
|
9578
|
+
accountsOf: () => accountsOf,
|
|
9577
9579
|
attestationLabel: () => attestationLabel,
|
|
9578
9580
|
dialogPath: () => dialogPath,
|
|
9579
9581
|
dialogRoute: () => dialogRoute,
|
|
9582
|
+
expired: () => expired,
|
|
9580
9583
|
externalId: () => externalId,
|
|
9584
|
+
externalIdGroups: () => externalIdGroups,
|
|
9581
9585
|
externalLink: () => externalLink,
|
|
9582
9586
|
externalName: () => externalName,
|
|
9587
|
+
externalSince: () => externalSince,
|
|
9583
9588
|
fresh: () => fresh,
|
|
9584
9589
|
freshnessMs: () => freshnessMs,
|
|
9585
9590
|
init: () => init,
|
|
@@ -9588,6 +9593,7 @@ var Verily = (() => {
|
|
|
9588
9593
|
isRedirectProvider: () => isRedirectProvider,
|
|
9589
9594
|
lastProved: () => lastProved,
|
|
9590
9595
|
localSide: () => localSide,
|
|
9596
|
+
place: () => place,
|
|
9591
9597
|
presentConnections: () => presentConnections,
|
|
9592
9598
|
proofTitle: () => proofTitle,
|
|
9593
9599
|
providerMethod: () => providerMethod,
|
|
@@ -9713,6 +9719,8 @@ var Verily = (() => {
|
|
|
9713
9719
|
// src/core/index.ts
|
|
9714
9720
|
var Refused = class extends Error {
|
|
9715
9721
|
};
|
|
9722
|
+
var Absent = class extends Refused {
|
|
9723
|
+
};
|
|
9716
9724
|
function isArtifactProvider(provider) {
|
|
9717
9725
|
return "verify" in provider;
|
|
9718
9726
|
}
|
|
@@ -9726,15 +9734,19 @@ var Verily = (() => {
|
|
|
9726
9734
|
return provider.method ?? "oauth";
|
|
9727
9735
|
}
|
|
9728
9736
|
var freshnessMs = 7 * 864e5;
|
|
9737
|
+
function expired(record2, now) {
|
|
9738
|
+
return record2.expiresAt <= now;
|
|
9739
|
+
}
|
|
9729
9740
|
function status(connection, now, freshness = freshnessMs) {
|
|
9730
9741
|
if (connection.revokedAt !== void 0) return "revoked";
|
|
9731
9742
|
if (connection.retiredAt !== void 0) return "retired";
|
|
9732
|
-
if (now
|
|
9743
|
+
if (expired(connection, now)) return "expired";
|
|
9733
9744
|
const main = connection.attestations?.external[0];
|
|
9734
9745
|
if (main && !fresh(main, now, freshness)) return "unconfirmed";
|
|
9735
9746
|
return "verified";
|
|
9736
9747
|
}
|
|
9737
9748
|
function fresh(attestation, now, freshness = freshnessMs) {
|
|
9749
|
+
if (attestation.absentAt !== void 0) return false;
|
|
9738
9750
|
return !attestation.artifactUrl || attestation.hosted === true || freshness === Infinity || now < attestation.confirmedAt + freshness;
|
|
9739
9751
|
}
|
|
9740
9752
|
function lastProved(evidence) {
|
|
@@ -9744,9 +9756,32 @@ var Verily = (() => {
|
|
|
9744
9756
|
function statusLabel(evidence, now) {
|
|
9745
9757
|
if (evidence.status === "revoked") return "Revoked";
|
|
9746
9758
|
if (evidence.status === "retired") return "Retired";
|
|
9747
|
-
if (evidence.status === "expired" || evidence
|
|
9759
|
+
if (evidence.status === "expired" || expired(evidence, now)) return "Expired";
|
|
9748
9760
|
return evidence.status === "unconfirmed" ? "Unconfirmed" : "Verified";
|
|
9749
9761
|
}
|
|
9762
|
+
function place(account) {
|
|
9763
|
+
if (account.status === "retired") return 3;
|
|
9764
|
+
return account.mark === "preferred" ? 0 : account.mark === "unused" ? 2 : 1;
|
|
9765
|
+
}
|
|
9766
|
+
function accountsOf(records, now) {
|
|
9767
|
+
const when = (e2) => e2.connectedAt ?? e2.approvedAt;
|
|
9768
|
+
const byAccount = /* @__PURE__ */ new Map();
|
|
9769
|
+
for (const record2 of records) {
|
|
9770
|
+
const key2 = JSON.stringify([record2.provider, record2.external.id]);
|
|
9771
|
+
byAccount.set(key2, [...byAccount.get(key2) ?? [], record2]);
|
|
9772
|
+
}
|
|
9773
|
+
return [...byAccount.values()].map((unsorted) => {
|
|
9774
|
+
const held = [...unsorted].sort((a2, b2) => when(a2) - when(b2));
|
|
9775
|
+
const verified = held.filter((e2) => e2.status === "verified" && e2.expiresAt > now);
|
|
9776
|
+
const lead = verified[0] ?? held.reduce((a2, b2) => b2.approvedAt > a2.approvedAt ? b2 : a2);
|
|
9777
|
+
const external = [...lead.attestations.external];
|
|
9778
|
+
for (const other of verified.slice(1))
|
|
9779
|
+
for (const attestation of other.attestations.external)
|
|
9780
|
+
if (!external.some((shown3) => shown3.method === attestation.method))
|
|
9781
|
+
external.push(attestation);
|
|
9782
|
+
return { lead, held, verified, external, connectedAt: when(held[0]) };
|
|
9783
|
+
}).sort((a2, b2) => place(a2.lead) - place(b2.lead) || a2.connectedAt - b2.connectedAt);
|
|
9784
|
+
}
|
|
9750
9785
|
function localSide(local, fallback) {
|
|
9751
9786
|
const siteName = local.siteName ?? fallback;
|
|
9752
9787
|
if (local.kind === "site") return { heading: "Website", value: siteName };
|
|
@@ -9776,6 +9811,12 @@ var Verily = (() => {
|
|
|
9776
9811
|
function externalId(external) {
|
|
9777
9812
|
return external.kind === "mailbox" || external.kind === "domain" ? void 0 : external.id;
|
|
9778
9813
|
}
|
|
9814
|
+
function externalIdGroups(external) {
|
|
9815
|
+
return external.kind === "key" ? external.id.match(/.{1,4}/g) ?? void 0 : void 0;
|
|
9816
|
+
}
|
|
9817
|
+
function externalSince(external, day) {
|
|
9818
|
+
return typeof external.createdAt === "number" && Number.isFinite(external.createdAt) ? `Joined ${day(external.createdAt)}` : void 0;
|
|
9819
|
+
}
|
|
9779
9820
|
function externalLink(external) {
|
|
9780
9821
|
return external.kind === "mailbox" ? void 0 : external.profileUrl;
|
|
9781
9822
|
}
|
|
@@ -10067,11 +10108,12 @@ var Verily = (() => {
|
|
|
10067
10108
|
function badgeShown(element) {
|
|
10068
10109
|
return intact(element) && shown.has(element);
|
|
10069
10110
|
}
|
|
10070
|
-
function renderKey(evidence, current, label, more, linked, rows, peek) {
|
|
10111
|
+
function renderKey(evidence, current, label, more, linked, rows, peek, href) {
|
|
10071
10112
|
return JSON.stringify([
|
|
10072
10113
|
more,
|
|
10073
10114
|
linked,
|
|
10074
10115
|
peek,
|
|
10116
|
+
href,
|
|
10075
10117
|
rows.map((row) => [row.provider, row.providerName, externalName(row.external)]),
|
|
10076
10118
|
evidence.provider,
|
|
10077
10119
|
evidence.providerName,
|
|
@@ -10122,14 +10164,15 @@ var Verily = (() => {
|
|
|
10122
10164
|
more = 0,
|
|
10123
10165
|
linked = true,
|
|
10124
10166
|
rows = [],
|
|
10125
|
-
peek = false
|
|
10167
|
+
peek = false,
|
|
10168
|
+
href = evidence.evidenceUrl
|
|
10126
10169
|
} = {}) {
|
|
10127
10170
|
const provider = evidence.providerName;
|
|
10128
10171
|
const current = evidence.status === "verified" && evidence.expiresAt > Date.now();
|
|
10129
10172
|
const retired = evidence.status === "retired";
|
|
10130
10173
|
const state = current ? "verified" : retired ? "retired" : evidence.status === "revoked" ? "revoked" : "expired";
|
|
10131
10174
|
const label = statusLabel(evidence, Date.now());
|
|
10132
|
-
const key2 = renderKey(evidence, current, label, more, linked, rows, peek);
|
|
10175
|
+
const key2 = renderKey(evidence, current, label, more, linked, rows, peek, href);
|
|
10133
10176
|
if (intact(element) && shown.get(element) === key2) return null;
|
|
10134
10177
|
const handle = externalName(evidence.external);
|
|
10135
10178
|
const { pill: badge, mark: mark2 } = frame(element, state, !linked);
|
|
@@ -10138,7 +10181,7 @@ var Verily = (() => {
|
|
|
10138
10181
|
const others = `${named}${unnamed ? ` and ${unnamed} more` : ""}`;
|
|
10139
10182
|
paintMark(mark2, current ? "current" : retired ? "pending" : "inactive");
|
|
10140
10183
|
if (linked) {
|
|
10141
|
-
badge.href =
|
|
10184
|
+
badge.href = href;
|
|
10142
10185
|
badge.rel = "noreferrer";
|
|
10143
10186
|
} else badge.type = "button";
|
|
10144
10187
|
badge.setAttribute(
|
|
@@ -10213,7 +10256,7 @@ var Verily = (() => {
|
|
|
10213
10256
|
} catch {
|
|
10214
10257
|
return;
|
|
10215
10258
|
}
|
|
10216
|
-
|
|
10259
|
+
place2(panel, pill, listed, unnamed);
|
|
10217
10260
|
globalThis.addEventListener?.("scroll", hide, true);
|
|
10218
10261
|
};
|
|
10219
10262
|
panel.setAttribute("popover", "manual");
|
|
@@ -10235,7 +10278,7 @@ var Verily = (() => {
|
|
|
10235
10278
|
if (event.key === "Escape") hide();
|
|
10236
10279
|
};
|
|
10237
10280
|
}
|
|
10238
|
-
function
|
|
10281
|
+
function place2(panel, pill, listed, unnamed) {
|
|
10239
10282
|
const gap = 6;
|
|
10240
10283
|
const edge = 8;
|
|
10241
10284
|
const at2 = pill.getBoundingClientRect();
|
|
@@ -10485,7 +10528,7 @@ ${claims(evidence)}`;
|
|
|
10485
10528
|
}
|
|
10486
10529
|
|
|
10487
10530
|
// src/version.ts
|
|
10488
|
-
var version = "v0.
|
|
10531
|
+
var version = "v0.7.0";
|
|
10489
10532
|
|
|
10490
10533
|
// src/browser/evidence-dialog.ts
|
|
10491
10534
|
function stampLink() {
|
|
@@ -10502,8 +10545,16 @@ ${claims(evidence)}`;
|
|
|
10502
10545
|
h2 { margin: 0; font-size: 18px; font-weight: 650; letter-spacing: -.3px; }
|
|
10503
10546
|
button { width: 32px; height: 32px; border: 1px solid #dce2de; border-radius: 8px; background: #fff; color: #52645a; font: 20px system-ui, sans-serif; cursor: pointer; }
|
|
10504
10547
|
button:hover { background: #f2f5f1; }
|
|
10548
|
+
.icon { display: grid; place-items: center; padding: 0; }
|
|
10549
|
+
.icon svg { width: 16px; height: 16px; }
|
|
10550
|
+
.title { display: flex; align-items: center; gap: 6px; min-width: 0; }
|
|
10551
|
+
.all { display: grid; place-items: center; width: 28px; height: 28px; border-radius: 8px; color: #52645a; }
|
|
10552
|
+
.all:hover { background: #f2f5f1; }
|
|
10553
|
+
.all svg { width: 16px; height: 16px; }
|
|
10505
10554
|
a { color: #245f43; text-underline-offset: 3px; overflow-wrap: anywhere; }
|
|
10506
|
-
a:focus-visible, button:focus-visible { outline: 2px solid #357ce5; outline-offset: 3px; }
|
|
10555
|
+
a:focus-visible, button:focus-visible, summary:focus-visible { outline: 2px solid #357ce5; outline-offset: 3px; }
|
|
10556
|
+
/* An element given its own display would otherwise go on showing while hidden. */
|
|
10557
|
+
[hidden] { display: none !important; }
|
|
10507
10558
|
.summary { display: flex; align-items: center; gap: 8px; margin-top: 16px; }
|
|
10508
10559
|
.mark { width: 36px; height: 36px; flex-shrink: 0; }
|
|
10509
10560
|
.provider { width: 14px; height: 14px; }
|
|
@@ -10515,24 +10566,45 @@ ${claims(evidence)}`;
|
|
|
10515
10566
|
.spinner { width: 14px; height: 14px; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: spin .7s linear infinite; }
|
|
10516
10567
|
@keyframes spin { to { transform: rotate(360deg); } }
|
|
10517
10568
|
@media (prefers-reduced-motion: reduce) { .spinner { animation: none; } }
|
|
10518
|
-
|
|
10519
|
-
.
|
|
10520
|
-
.
|
|
10569
|
+
/* The "signed" pill after the verifier's name, one size smaller than the line it sits on. */
|
|
10570
|
+
.summary .signed { display: inline-block; margin-left: 5px; padding: 0 6px; border: 1px solid #cfdcd3; border-radius: 999px; font-size: 10px; font-weight: 550; line-height: 1.3; text-decoration: none; vertical-align: 1px; }
|
|
10571
|
+
.summary a.signed:hover { background: #f2f5f1; }
|
|
10572
|
+
.summary .signed.bad { border-color: #b3261e; color: #b3261e; font-weight: 650; cursor: help; }
|
|
10573
|
+
.caution { display: inline-block; margin-left: 3px; color: #a15c00; vertical-align: -1.5px; }
|
|
10574
|
+
.caution svg { display: block; width: 10px; height: 10px; }
|
|
10521
10575
|
.account { padding: 14px 16px; border: 1px solid #e0e6df; border-radius: 10px; margin-top: 12px; }
|
|
10522
10576
|
.account h3 { display: flex; align-items: center; gap: 6px; margin: 0 0 3px; color: #6b786f; font-size: 11px; font-weight: 550; }
|
|
10523
10577
|
.account a, .account strong { font-weight: 650; font-size: 14px; }
|
|
10524
10578
|
/* The account's own name carries that weight; a link inside a line of prose does not. */
|
|
10525
10579
|
.summary a, .method a, .additional a { font: inherit; }
|
|
10526
10580
|
.reference { margin-top: 2px; }
|
|
10581
|
+
/* A fingerprint in fours: it breaks between groups, and copies as one unbroken word. */
|
|
10582
|
+
.fingerprint { font-family: ui-monospace, monospace; text-wrap: balance; }
|
|
10583
|
+
.fingerprint span { display: inline-block; margin-right: .5ch; }
|
|
10527
10584
|
.method { margin-top: 8px; }
|
|
10528
10585
|
/* In the corner the heading leaves empty, apart from everything that was checked. */
|
|
10529
10586
|
.listing { position: absolute; top: 12px; right: 14px; padding: 1px 8px; border: 1px solid #e0e6df; border-radius: 999px; color: #6b786f; font-size: 11px; font-weight: 550; }
|
|
10530
10587
|
.listing.preferred { border-color: #245f43; color: #245f43; }
|
|
10531
10588
|
/* Quiet, as its pill is: the surface and the name step back, and the controls do not. */
|
|
10532
10589
|
.account.retired { background: #f6f8f5; }
|
|
10533
|
-
.account.retired > a, .account.retired > strong { color: #6b786f; }
|
|
10590
|
+
.account.retired > a, .account.retired > strong, .account.retired > .named > a, .account.retired > .named > strong { color: #6b786f; }
|
|
10591
|
+
/* The name, then a dot and the account's creation date. The date wraps under a long name. */
|
|
10592
|
+
/* Clipped 4px left of the text, which hides the dot when the date wraps to a new line. */
|
|
10593
|
+
.named { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 14px; margin-left: -4px; padding-left: 4px; overflow-x: clip; }
|
|
10594
|
+
.since { position: relative; white-space: nowrap; }
|
|
10595
|
+
/* The dot sits in the gap before the date. */
|
|
10596
|
+
.since::before { content: "\xB7"; position: absolute; left: -7px; transform: translateX(-50%); }
|
|
10534
10597
|
.account.retired .mark { opacity: .45; }
|
|
10535
10598
|
.group { margin: 20px 0 0; color: #6b786f; font-size: 11px; font-weight: 550; }
|
|
10599
|
+
summary.group { display: flex; align-items: center; gap: 4px; width: fit-content; border-radius: 4px; list-style: none; cursor: pointer; }
|
|
10600
|
+
summary::-webkit-details-marker { display: none; }
|
|
10601
|
+
.fold { display: flex; align-items: center; gap: 4px; width: auto; height: auto; padding: 2px 8px 2px 4px; margin-left: -4px; border: 0; background: none; font: 550 12px/1.6 system-ui, sans-serif; }
|
|
10602
|
+
.fold svg, summary svg { width: 12px; height: 12px; }
|
|
10603
|
+
.fold[aria-expanded="true"] svg, details[open] > summary svg { transform: rotate(90deg); }
|
|
10604
|
+
@media (prefers-reduced-motion: no-preference) { .fold svg, summary svg { transition: transform .15s; } }
|
|
10605
|
+
/* The fold and what the holder may do share a line, until a question needs the whole of it. */
|
|
10606
|
+
.bar { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
|
|
10607
|
+
.account .bar .row { margin: 0 0 0 auto; }
|
|
10536
10608
|
.additional { margin-top: 2px; padding-left: 12px; }
|
|
10537
10609
|
.joiner { display: block; width: 20px; height: 20px; margin: 8px auto -4px; color: #90a096; }
|
|
10538
10610
|
dl { margin: 16px 0 0; padding-top: 12px; border-top: 1px solid #e5e9e3; display: grid; grid-template-columns: auto 1fr; gap: 5px 16px; font-size: 11px; }
|
|
@@ -10547,6 +10619,14 @@ ${claims(evidence)}`;
|
|
|
10547
10619
|
.row { display: flex; justify-content: end; gap: 8px; margin-top: 16px; }
|
|
10548
10620
|
.account .row { align-items: center; justify-content: start; flex-wrap: wrap; margin-top: 12px; }
|
|
10549
10621
|
.account .action { padding: 5px 10px; font-size: 12px; }
|
|
10622
|
+
.account .action.dots { display: grid; place-items: center; align-self: stretch; width: 30px; padding: 0; }
|
|
10623
|
+
.dots svg { width: 16px; height: 16px; }
|
|
10624
|
+
/* Placed under its button each time it opens, above the dialog and clipped by none of it. */
|
|
10625
|
+
.menu { position: fixed; inset: auto; margin: 0; min-width: 170px; padding: 6px; border: 1px solid #dce2de; border-radius: 10px; background: #fff; color: #23312b; box-shadow: 0 12px 40px #10201930; }
|
|
10626
|
+
.menu:popover-open { display: grid; }
|
|
10627
|
+
.account .menu .action { border: 0; border-radius: 6px; padding: 6px 10px; font-weight: 550; }
|
|
10628
|
+
.menu .danger { color: #b3261e; }
|
|
10629
|
+
.menu hr { width: 100%; margin: 4px 0; border: 0; border-top: 1px solid #e5e9e3; }
|
|
10550
10630
|
footer { display: flex; align-items: center; justify-content: start; gap: 6px; margin: 20px 0 -8px; color: #9aa9a0; font-size: 11px; }
|
|
10551
10631
|
footer a { display: flex; color: inherit; }
|
|
10552
10632
|
.logo { display: block; height: 13px; }
|
|
@@ -10564,6 +10644,58 @@ ${claims(evidence)}`;
|
|
|
10564
10644
|
anchor.target = "_blank";
|
|
10565
10645
|
return anchor;
|
|
10566
10646
|
}
|
|
10647
|
+
function subjectUrl(e2) {
|
|
10648
|
+
const url = new URL(e2.evidenceUrl);
|
|
10649
|
+
const path = url.pathname.replace(/\/connections\/[^/]+$/, "/published");
|
|
10650
|
+
if (path === url.pathname) return e2.evidenceUrl;
|
|
10651
|
+
url.pathname = path;
|
|
10652
|
+
url.search = `site=${encodeURIComponent(e2.siteName)}&reference=${encodeURIComponent(e2.local.reference)}`;
|
|
10653
|
+
return url.href;
|
|
10654
|
+
}
|
|
10655
|
+
function awayMark() {
|
|
10656
|
+
const namespace2 = "http://www.w3.org/2000/svg";
|
|
10657
|
+
const svg = document.createElementNS(namespace2, "svg");
|
|
10658
|
+
svg.setAttribute("viewBox", "0 0 24 24");
|
|
10659
|
+
svg.setAttribute("fill", "none");
|
|
10660
|
+
svg.setAttribute("aria-hidden", "true");
|
|
10661
|
+
svg.setAttribute("focusable", "false");
|
|
10662
|
+
for (const d2 of [
|
|
10663
|
+
"M14 5h5v5",
|
|
10664
|
+
"M19 5l-8 8",
|
|
10665
|
+
"M18 14v4a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h4"
|
|
10666
|
+
]) {
|
|
10667
|
+
const path = document.createElementNS(namespace2, "path");
|
|
10668
|
+
path.setAttribute("d", d2);
|
|
10669
|
+
path.setAttribute("stroke", "currentColor");
|
|
10670
|
+
path.setAttribute("stroke-width", "2");
|
|
10671
|
+
path.setAttribute("stroke-linecap", "round");
|
|
10672
|
+
path.setAttribute("stroke-linejoin", "round");
|
|
10673
|
+
svg.append(path);
|
|
10674
|
+
}
|
|
10675
|
+
return svg;
|
|
10676
|
+
}
|
|
10677
|
+
function strokes(box, width, paths) {
|
|
10678
|
+
const namespace2 = "http://www.w3.org/2000/svg";
|
|
10679
|
+
const svg = document.createElementNS(namespace2, "svg");
|
|
10680
|
+
svg.setAttribute("viewBox", `0 0 ${box} ${box}`);
|
|
10681
|
+
svg.setAttribute("fill", "none");
|
|
10682
|
+
svg.setAttribute("aria-hidden", "true");
|
|
10683
|
+
svg.setAttribute("focusable", "false");
|
|
10684
|
+
for (const d2 of paths) {
|
|
10685
|
+
const path = document.createElementNS(namespace2, "path");
|
|
10686
|
+
path.setAttribute("d", d2);
|
|
10687
|
+
path.setAttribute("stroke", "currentColor");
|
|
10688
|
+
path.setAttribute("stroke-width", String(width));
|
|
10689
|
+
path.setAttribute("stroke-linecap", "round");
|
|
10690
|
+
path.setAttribute("stroke-linejoin", "round");
|
|
10691
|
+
svg.append(path);
|
|
10692
|
+
}
|
|
10693
|
+
return svg;
|
|
10694
|
+
}
|
|
10695
|
+
var foldMark = () => strokes(16, 1.8, ["M6 3.5 10.5 8 6 12.5"]);
|
|
10696
|
+
var moreMark = () => strokes(16, 2.4, ["M3 8h.01", "M8 8h.01", "M13 8h.01"]);
|
|
10697
|
+
var closeMark = () => strokes(16, 1.8, ["M4 4l8 8", "M12 4l-8 8"]);
|
|
10698
|
+
var backMark = () => strokes(16, 1.8, ["M10 3.5 5.5 8 10 12.5"]);
|
|
10567
10699
|
function linkMark() {
|
|
10568
10700
|
const namespace2 = "http://www.w3.org/2000/svg";
|
|
10569
10701
|
const svg = document.createElementNS(namespace2, "svg");
|
|
@@ -10603,18 +10735,91 @@ ${claims(evidence)}`;
|
|
|
10603
10735
|
return new Date(time).toLocaleString(void 0, { dateStyle: "medium", timeStyle: "short" });
|
|
10604
10736
|
}
|
|
10605
10737
|
function accountCard(heading, name, reference, url, ...extra) {
|
|
10738
|
+
return side(heading, name, reference, url, void 0, extra);
|
|
10739
|
+
}
|
|
10740
|
+
function namedCard(heading, external, ...extra) {
|
|
10741
|
+
return side(
|
|
10742
|
+
heading,
|
|
10743
|
+
externalName(external),
|
|
10744
|
+
// A mailbox's address or a domain is its name already, so there is no second identifier.
|
|
10745
|
+
externalIdGroups(external) ?? externalId(external),
|
|
10746
|
+
externalLink(external),
|
|
10747
|
+
// In UTC, so the day matches the one the verifier's own pages print.
|
|
10748
|
+
externalSince(
|
|
10749
|
+
external,
|
|
10750
|
+
(time) => new Date(time).toLocaleDateString(void 0, { dateStyle: "medium", timeZone: "UTC" })
|
|
10751
|
+
),
|
|
10752
|
+
extra
|
|
10753
|
+
);
|
|
10754
|
+
}
|
|
10755
|
+
function side(heading, name, reference, url, since, extra) {
|
|
10606
10756
|
const card = node("section", "", "account");
|
|
10607
10757
|
const title = node("h3");
|
|
10608
10758
|
const value = node(url ? "a" : "strong", name);
|
|
10609
10759
|
title.append(...heading);
|
|
10610
10760
|
if (value instanceof HTMLAnchorElement) outward(value, url);
|
|
10611
|
-
|
|
10612
|
-
|
|
10761
|
+
if (since) {
|
|
10762
|
+
const named = node("div", "", "named");
|
|
10763
|
+
named.append(value, node("span", since, "muted since"));
|
|
10764
|
+
card.append(title, named);
|
|
10765
|
+
} else card.append(title, value);
|
|
10766
|
+
if (typeof reference === "string") card.append(node("div", reference, "muted reference"));
|
|
10767
|
+
else if (reference) {
|
|
10768
|
+
const groups = node("div", "", "muted reference fingerprint");
|
|
10769
|
+
groups.append(...reference.map((group) => node("span", group)));
|
|
10770
|
+
card.append(groups);
|
|
10771
|
+
}
|
|
10613
10772
|
card.append(...extra);
|
|
10614
10773
|
return card;
|
|
10615
10774
|
}
|
|
10616
10775
|
var retiringNote = "Retiring keeps this record as history. It will no longer read as verified, and who can read it cannot be changed afterwards.";
|
|
10617
10776
|
var removable = (evidence) => evidence.links ?? (evidence.status === "revoked" ? [] : [evidence.id]);
|
|
10777
|
+
function actionsMenu(more, choices) {
|
|
10778
|
+
const menu = node("div", "", "menu");
|
|
10779
|
+
if (typeof menu.showPopover !== "function") return void 0;
|
|
10780
|
+
const label = "More actions";
|
|
10781
|
+
more.className = "action dots";
|
|
10782
|
+
more.title = label;
|
|
10783
|
+
more.setAttribute("aria-label", label);
|
|
10784
|
+
more.setAttribute("aria-haspopup", "menu");
|
|
10785
|
+
more.append(moreMark());
|
|
10786
|
+
more.popoverTargetElement = menu;
|
|
10787
|
+
menu.setAttribute("popover", "auto");
|
|
10788
|
+
menu.setAttribute("role", "menu");
|
|
10789
|
+
for (const choice of choices) choice.setAttribute("role", "menuitem");
|
|
10790
|
+
menu.append(...choices.slice(0, -1), node("hr"), choices.at(-1));
|
|
10791
|
+
const shut = () => {
|
|
10792
|
+
try {
|
|
10793
|
+
if (menu.matches(":popover-open")) menu.hidePopover();
|
|
10794
|
+
} catch {
|
|
10795
|
+
}
|
|
10796
|
+
};
|
|
10797
|
+
const gap = 4;
|
|
10798
|
+
const edge = 8;
|
|
10799
|
+
menu.addEventListener("beforetoggle", (event) => {
|
|
10800
|
+
if (event.newState !== "open") return;
|
|
10801
|
+
const at2 = more.getBoundingClientRect();
|
|
10802
|
+
menu.style.top = `${at2.bottom + gap}px`;
|
|
10803
|
+
menu.style.right = `${document.documentElement.clientWidth - at2.right}px`;
|
|
10804
|
+
});
|
|
10805
|
+
menu.addEventListener("toggle", (event) => {
|
|
10806
|
+
if (event.newState !== "open") return;
|
|
10807
|
+
const { height, bottom } = menu.getBoundingClientRect();
|
|
10808
|
+
if (bottom > innerHeight - edge)
|
|
10809
|
+
menu.style.top = `${Math.max(edge, more.getBoundingClientRect().top - gap - height)}px`;
|
|
10810
|
+
menu.closest("dialog")?.addEventListener("scroll", shut, { once: true });
|
|
10811
|
+
choices[0].focus();
|
|
10812
|
+
});
|
|
10813
|
+
menu.addEventListener("click", shut);
|
|
10814
|
+
menu.addEventListener("keydown", (event) => {
|
|
10815
|
+
const step = { ArrowDown: 1, ArrowUp: -1 }[event.key];
|
|
10816
|
+
if (!step) return;
|
|
10817
|
+
event.preventDefault();
|
|
10818
|
+
const at2 = choices.indexOf(event.target);
|
|
10819
|
+
choices[(at2 + step + choices.length) % choices.length].focus();
|
|
10820
|
+
});
|
|
10821
|
+
return menu;
|
|
10822
|
+
}
|
|
10618
10823
|
function manageRow(evidence, manage) {
|
|
10619
10824
|
const row = node("div", "", "row");
|
|
10620
10825
|
const act = (label, danger = false) => {
|
|
@@ -10657,7 +10862,11 @@ ${claims(evidence)}`;
|
|
|
10657
10862
|
retire
|
|
10658
10863
|
);
|
|
10659
10864
|
}
|
|
10660
|
-
|
|
10865
|
+
const said = note ? [node("span", note, "muted")] : [];
|
|
10866
|
+
const more = act("");
|
|
10867
|
+
const menu = listing.length ? actionsMenu(more, [...listing, remove]) : void 0;
|
|
10868
|
+
if (menu) row.replaceChildren(renew, more, menu, ...said);
|
|
10869
|
+
else row.replaceChildren(renew, ...listing, remove, ...said);
|
|
10661
10870
|
};
|
|
10662
10871
|
const retiring = () => {
|
|
10663
10872
|
const cancel = act("Cancel");
|
|
@@ -10700,23 +10909,24 @@ ${claims(evidence)}`;
|
|
|
10700
10909
|
)
|
|
10701
10910
|
);
|
|
10702
10911
|
}
|
|
10703
|
-
function signedMark(evidence, later) {
|
|
10912
|
+
function signedMark(evidence, later, warned) {
|
|
10704
10913
|
const mark2 = node("span");
|
|
10705
|
-
const link = outward(node("a", "signed"), `${evidence.evidenceUrl}#signed`);
|
|
10914
|
+
const link = outward(node("a", "signed", "signed"), `${evidence.evidenceUrl}#signed`);
|
|
10706
10915
|
const see = "See the signature (opens in a new tab)";
|
|
10707
10916
|
const settle = (results) => {
|
|
10708
10917
|
const first = results[0];
|
|
10709
10918
|
if (results.some((result) => result.state === "invalid")) {
|
|
10710
|
-
const warning = node("span", "
|
|
10919
|
+
const warning = node("span", "bad signature", "signed bad");
|
|
10711
10920
|
warning.setAttribute("role", "alert");
|
|
10712
10921
|
warning.title = `This record does not match the signature ${evidence.verifierName} put on it, so it may have been altered. Do not rely on it.`;
|
|
10713
|
-
mark2.replaceChildren(
|
|
10922
|
+
mark2.replaceChildren(warning);
|
|
10923
|
+
warned();
|
|
10714
10924
|
return;
|
|
10715
10925
|
}
|
|
10716
10926
|
if (first?.state === "valid" && results.every((result) => result.state === "valid")) {
|
|
10717
|
-
link.textContent = "signed
|
|
10927
|
+
link.textContent = "signed";
|
|
10718
10928
|
link.title = `Signature checked in this browser against the keys ${new URL(evidence.evidenceUrl).host} publishes (key ${first.keyId}). ${see}`;
|
|
10719
|
-
mark2.replaceChildren(
|
|
10929
|
+
mark2.replaceChildren(link);
|
|
10720
10930
|
return;
|
|
10721
10931
|
}
|
|
10722
10932
|
const why = results.find((result) => result.state === "unchecked");
|
|
@@ -10727,8 +10937,9 @@ ${claims(evidence)}`;
|
|
|
10727
10937
|
caution.setAttribute("aria-label", reason);
|
|
10728
10938
|
caution.append(cautionMark());
|
|
10729
10939
|
link.textContent = "signed";
|
|
10940
|
+
link.append(caution);
|
|
10730
10941
|
link.title = `${reason} ${see}`;
|
|
10731
|
-
mark2.replaceChildren(
|
|
10942
|
+
mark2.replaceChildren(link);
|
|
10732
10943
|
if (why) later(() => void signatures(evidence).then(settle));
|
|
10733
10944
|
};
|
|
10734
10945
|
void signatures(evidence).then(settle);
|
|
@@ -10752,7 +10963,25 @@ ${claims(evidence)}`;
|
|
|
10752
10963
|
}
|
|
10753
10964
|
return svg;
|
|
10754
10965
|
}
|
|
10966
|
+
function foldToggle(folded, fold) {
|
|
10967
|
+
const toggle = node("button", "", "fold");
|
|
10968
|
+
let shown3 = fold.open;
|
|
10969
|
+
const set = (open) => {
|
|
10970
|
+
shown3 = open;
|
|
10971
|
+
for (const part of folded) part.hidden = !open;
|
|
10972
|
+
toggle.setAttribute("aria-expanded", String(open));
|
|
10973
|
+
};
|
|
10974
|
+
toggle.type = "button";
|
|
10975
|
+
toggle.append(foldMark(), document.createTextNode("Details"));
|
|
10976
|
+
set(fold.open);
|
|
10977
|
+
toggle.onclick = () => {
|
|
10978
|
+
set(!shown3);
|
|
10979
|
+
fold.toggle(shown3);
|
|
10980
|
+
};
|
|
10981
|
+
return toggle;
|
|
10982
|
+
}
|
|
10755
10983
|
function externalCard(evidence, manage, later = () => {
|
|
10984
|
+
}, fold, warned = () => {
|
|
10756
10985
|
}) {
|
|
10757
10986
|
const current = evidence.status === "verified" && evidence.expiresAt > Date.now();
|
|
10758
10987
|
const status2 = statusLabel(evidence, Date.now());
|
|
@@ -10765,7 +10994,7 @@ ${claims(evidence)}`;
|
|
|
10765
10994
|
verifier.title = `View this record at ${evidence.verifierName} (opens in a new tab)`;
|
|
10766
10995
|
attribution.append(verifier);
|
|
10767
10996
|
} else attribution.append(document.createTextNode(evidence.verifierName));
|
|
10768
|
-
if (evidence.signedUrl) attribution.append(signedMark(evidence, later));
|
|
10997
|
+
if (evidence.signedUrl) attribution.append(signedMark(evidence, later, warned));
|
|
10769
10998
|
copy.append(node("div", status2, "state"), attribution);
|
|
10770
10999
|
const retired = evidence.status === "retired";
|
|
10771
11000
|
summary.append(verificationMark(current ? "current" : retired ? "pending" : "inactive"), copy);
|
|
@@ -10788,23 +11017,25 @@ ${claims(evidence)}`;
|
|
|
10788
11017
|
dates.append(node("dt", label), node("dd", moment(time)));
|
|
10789
11018
|
}
|
|
10790
11019
|
const names2 = { site: evidence.siteName, provider };
|
|
11020
|
+
const row = manage && removable(evidence).length ? [manageRow(evidence, manage)] : [];
|
|
11021
|
+
const bar = node("div", "", "bar");
|
|
11022
|
+
const methods = [
|
|
11023
|
+
...attestationNote(main, names2),
|
|
11024
|
+
...evidence.attestations.external.slice(1).flatMap((a2) => attestationNote(a2, names2, true))
|
|
11025
|
+
];
|
|
11026
|
+
if (fold) bar.append(foldToggle([...methods, dates], fold), ...row);
|
|
10791
11027
|
const logo2 = providerMark(evidence.provider, evidence.attestations.external[0].method);
|
|
10792
|
-
const card =
|
|
11028
|
+
const card = namedCard(
|
|
10793
11029
|
// The mark and the name, or just the name. A mark that falls back to writing the name
|
|
10794
11030
|
// would print it twice here, since this heading writes it either way.
|
|
10795
11031
|
logo2 ? [logo2, document.createTextNode(provider)] : [document.createTextNode(provider)],
|
|
10796
|
-
|
|
10797
|
-
// A mailbox's address or a domain is its name already, so there is no second identifier.
|
|
10798
|
-
externalId(evidence.external),
|
|
10799
|
-
externalLink(evidence.external),
|
|
11032
|
+
evidence.external,
|
|
10800
11033
|
// Status first, then how it was shown, then when. The proof explains the state
|
|
10801
11034
|
// above it, so it cannot sit before that state has been given.
|
|
10802
11035
|
summary,
|
|
10803
|
-
...
|
|
10804
|
-
...evidence.attestations.external.slice(1).flatMap((a2) => attestationNote(a2, names2, true)),
|
|
11036
|
+
...fold ? [] : methods,
|
|
10805
11037
|
...evidence.status === "revoked" || retired ? [] : evidence.mark === "preferred" ? [node("div", "Preferred", "listing preferred")] : evidence.mark === "unused" ? [node("div", "No longer used", "listing")] : [],
|
|
10806
|
-
dates,
|
|
10807
|
-
...manage && removable(evidence).length ? [manageRow(evidence, manage)] : []
|
|
11038
|
+
...fold ? [bar, ...methods, dates] : [dates, ...row]
|
|
10808
11039
|
);
|
|
10809
11040
|
const surface = retired ? "account retired" : "account";
|
|
10810
11041
|
card.className = surface;
|
|
@@ -10832,7 +11063,7 @@ ${claims(evidence)}`;
|
|
|
10832
11063
|
row.append(add);
|
|
10833
11064
|
return row;
|
|
10834
11065
|
}
|
|
10835
|
-
function render(content, records, manage, later) {
|
|
11066
|
+
function render(content, records, manage, later, folds = { open: false, cards: /* @__PURE__ */ new Set() }) {
|
|
10836
11067
|
const [first] = records;
|
|
10837
11068
|
const local = localSide(first.local, first.siteName);
|
|
10838
11069
|
const localCard = accountCard(
|
|
@@ -10849,15 +11080,40 @@ ${claims(evidence)}`;
|
|
|
10849
11080
|
})
|
|
10850
11081
|
);
|
|
10851
11082
|
const retired = records.filter((record2) => record2.status === "retired");
|
|
11083
|
+
const listed = records.filter((record2) => record2.status !== "retired");
|
|
11084
|
+
const cards = (among, warned) => among.map((record2) => {
|
|
11085
|
+
const key2 = `${record2.provider}
|
|
11086
|
+
${record2.external.id}`;
|
|
11087
|
+
return externalCard(
|
|
11088
|
+
record2,
|
|
11089
|
+
manage,
|
|
11090
|
+
later,
|
|
11091
|
+
among.length > 1 ? {
|
|
11092
|
+
open: folds.cards.has(key2) !== folds.open,
|
|
11093
|
+
toggle: (open) => void (open === folds.open ? folds.cards.delete(key2) : folds.cards.add(key2))
|
|
11094
|
+
} : void 0,
|
|
11095
|
+
warned
|
|
11096
|
+
);
|
|
11097
|
+
});
|
|
11098
|
+
const history = node("details", "", "history");
|
|
11099
|
+
const count = node("summary", "", "group");
|
|
11100
|
+
count.append(foldMark(), document.createTextNode(`Retired accounts (${retired.length})`));
|
|
11101
|
+
history.append(
|
|
11102
|
+
count,
|
|
11103
|
+
...cards(retired, () => {
|
|
11104
|
+
history.open = true;
|
|
11105
|
+
})
|
|
11106
|
+
);
|
|
11107
|
+
history.open = folds.retired ?? !listed.length;
|
|
11108
|
+
count.addEventListener("click", () => {
|
|
11109
|
+
folds.retired = !history.open;
|
|
11110
|
+
});
|
|
10852
11111
|
content.replaceChildren(
|
|
10853
11112
|
localCard,
|
|
10854
11113
|
linkMark(),
|
|
10855
|
-
...
|
|
11114
|
+
...cards(listed),
|
|
10856
11115
|
...manage ? [addRow(manage)] : [],
|
|
10857
|
-
...retired.length ? [
|
|
10858
|
-
node("h3", "Retired accounts", "group"),
|
|
10859
|
-
...retired.map((record2) => externalCard(record2, manage, later))
|
|
10860
|
-
] : [],
|
|
11116
|
+
...retired.length ? [history] : [],
|
|
10861
11117
|
// Nothing below the cards may name a provider. Approval, method and dates belong to
|
|
10862
11118
|
// the card they came from, and a second provider on this subject gets its own card.
|
|
10863
11119
|
node(
|
|
@@ -10867,7 +11123,7 @@ ${claims(evidence)}`;
|
|
|
10867
11123
|
)
|
|
10868
11124
|
);
|
|
10869
11125
|
}
|
|
10870
|
-
function openEvidenceDialog(opener, load2, opened, arrange2 = (records) => records, manage) {
|
|
11126
|
+
function openEvidenceDialog(opener, load2, opened, arrange2 = (records) => records, manage, detailsOpen = false) {
|
|
10871
11127
|
const existing = openDialogs.get(opener);
|
|
10872
11128
|
if (existing?.open) {
|
|
10873
11129
|
existing.focus();
|
|
@@ -10880,7 +11136,9 @@ ${claims(evidence)}`;
|
|
|
10880
11136
|
root.adoptedStyleSheets = [sheet2];
|
|
10881
11137
|
const dialog = node("dialog");
|
|
10882
11138
|
const heading = node("h2", "Verification details");
|
|
10883
|
-
const close = node("button", "
|
|
11139
|
+
const close = node("button", "", "icon");
|
|
11140
|
+
const all = node("a", "", "all");
|
|
11141
|
+
const title = node("div", "", "title");
|
|
10884
11142
|
const header = node("header");
|
|
10885
11143
|
const stamp = node("footer");
|
|
10886
11144
|
const content = node("div", "Checking verification\u2026");
|
|
@@ -10890,13 +11148,18 @@ ${claims(evidence)}`;
|
|
|
10890
11148
|
close.setAttribute("aria-label", "Close verification details");
|
|
10891
11149
|
content.setAttribute("aria-live", "polite");
|
|
10892
11150
|
stamp.append(stampLink(), node("span", version));
|
|
10893
|
-
|
|
11151
|
+
all.append(awayMark());
|
|
11152
|
+
close.append(closeMark());
|
|
11153
|
+
close.autofocus = true;
|
|
11154
|
+
title.append(heading);
|
|
11155
|
+
header.append(title, close);
|
|
10894
11156
|
dialog.append(header, content, stamp);
|
|
10895
11157
|
const removed = /* @__PURE__ */ new Map();
|
|
10896
11158
|
const listed = [];
|
|
10897
11159
|
const listing = (record2) => JSON.stringify([record2.status, record2.mark, record2.retiredAt, record2.approvedAt]);
|
|
10898
11160
|
const listings = (given) => new Map([given].flat().map((record2) => [record2.id, listing(record2)]));
|
|
10899
11161
|
let held;
|
|
11162
|
+
const folds = { open: detailsOpen, cards: /* @__PURE__ */ new Set() };
|
|
10900
11163
|
const relisted = (evidence) => {
|
|
10901
11164
|
let record2 = evidence;
|
|
10902
11165
|
for (const { ids, as: as2, at: at2 } of listed) {
|
|
@@ -10965,11 +11228,19 @@ ${claims(evidence)}`;
|
|
|
10965
11228
|
(e2) => removed.has(e2.id) && e2.status !== "revoked" ? { ...e2, status: "revoked", revokedAt: removed.get(e2.id) } : relisted(standing(e2))
|
|
10966
11229
|
)
|
|
10967
11230
|
);
|
|
11231
|
+
const listable = records.filter((e2) => e2.visibility === "public" && e2.status !== "revoked");
|
|
11232
|
+
title.replaceChildren(heading, ...listable.length > 1 ? [all] : []);
|
|
11233
|
+
if (listable.length > 1) {
|
|
11234
|
+
const label = `View all on ${listable[0].verifierName} (opens in a new tab)`;
|
|
11235
|
+
outward(all, subjectUrl(listable[0]));
|
|
11236
|
+
all.title = label;
|
|
11237
|
+
all.setAttribute("aria-label", label);
|
|
11238
|
+
}
|
|
10968
11239
|
const key2 = JSON.stringify(records.map((e2) => [e2, statusLabel(e2, Date.now())]));
|
|
10969
11240
|
if (key2 === drawn) return;
|
|
10970
11241
|
drawn = key2;
|
|
10971
11242
|
retries.clear();
|
|
10972
|
-
render(content, records, actions, (retry) => retries.add(retry));
|
|
11243
|
+
render(content, records, actions, (retry) => retries.add(retry), folds);
|
|
10973
11244
|
};
|
|
10974
11245
|
const watch = (given) => {
|
|
10975
11246
|
clearTimeout(expiryTimer);
|
|
@@ -11048,13 +11319,15 @@ ${claims(evidence)}`;
|
|
|
11048
11319
|
/* The block scrolls and its frame does not, so the copy button stays in its corner. */
|
|
11049
11320
|
.code { position: relative; margin: 10px 0; }
|
|
11050
11321
|
pre { margin: 0; padding: 10px 64px 10px 12px; border: 1px solid #e0e6df; border-radius: 8px; background: #f6f8f5; font: 12px/1.5 ui-monospace, monospace; white-space: pre; overflow-x: auto; }
|
|
11051
|
-
.code .action { position: absolute; top:
|
|
11322
|
+
.code .action { position: absolute; top: 9px; right: 9px; padding: 3px 8px; font-size: 11px; }
|
|
11052
11323
|
label { display: grid; gap: 4px; margin-top: 12px; font-weight: 600; }
|
|
11053
11324
|
input[type=url], input[type=email], input[type=tel], input[type=text], textarea { width: 100%; padding: 8px 10px; border: 1px solid #cfd8d2; border-radius: 8px; font: 13px/1.4 system-ui, sans-serif; color: inherit; background: #fff; }
|
|
11054
11325
|
textarea { font-family: ui-monospace, monospace; font-size: 12px; resize: vertical; }
|
|
11055
11326
|
fieldset { margin: 16px 0 0; padding: 10px 14px; border: 1px solid #e0e6df; border-radius: 10px; }
|
|
11056
11327
|
legend { padding: 0 4px; color: #6b786f; font-size: 11px; font-weight: 550; }
|
|
11057
11328
|
fieldset label { display: flex; align-items: baseline; gap: 8px; margin-top: 4px; font-weight: 500; }
|
|
11329
|
+
/* A radio has no baseline to speak of, so it is set in the middle of the label's first line. */
|
|
11330
|
+
fieldset input { flex-shrink: 0; align-self: flex-start; width: 13px; height: 13px; font: inherit; margin-top: 4px; margin-top: calc((1lh - 13px) / 2); margin-bottom: 0; }
|
|
11058
11331
|
.error { color: #9b2c33; }
|
|
11059
11332
|
`;
|
|
11060
11333
|
function codeBlock(code) {
|
|
@@ -11116,7 +11389,7 @@ ${claims(evidence)}`;
|
|
|
11116
11389
|
root.adoptedStyleSheets = [sheet2];
|
|
11117
11390
|
const dialog = node("dialog");
|
|
11118
11391
|
const heading = node("h2", "Verify an account");
|
|
11119
|
-
const close = node("button", "
|
|
11392
|
+
const close = node("button", "", "icon");
|
|
11120
11393
|
const header = node("header");
|
|
11121
11394
|
const stamp = node("footer");
|
|
11122
11395
|
const content = node("div", "Loading\u2026", "steps");
|
|
@@ -11124,6 +11397,7 @@ ${claims(evidence)}`;
|
|
|
11124
11397
|
dialog.setAttribute("aria-labelledby", heading.id);
|
|
11125
11398
|
close.type = "button";
|
|
11126
11399
|
close.setAttribute("aria-label", "Close");
|
|
11400
|
+
close.append(closeMark());
|
|
11127
11401
|
content.setAttribute("aria-live", "polite");
|
|
11128
11402
|
stamp.append(stampLink(), node("span", version));
|
|
11129
11403
|
header.append(heading, close);
|
|
@@ -11133,9 +11407,10 @@ ${claims(evidence)}`;
|
|
|
11133
11407
|
openDialogs2.set(opener, dialog);
|
|
11134
11408
|
let returning = false;
|
|
11135
11409
|
if (back) {
|
|
11136
|
-
const before = node("button", "
|
|
11410
|
+
const before = node("button", "", "icon");
|
|
11137
11411
|
before.type = "button";
|
|
11138
11412
|
before.setAttribute("aria-label", "Back");
|
|
11413
|
+
before.append(backMark());
|
|
11139
11414
|
header.className = "backed";
|
|
11140
11415
|
header.replaceChildren(before, heading, close);
|
|
11141
11416
|
before.onclick = () => {
|
|
@@ -11407,11 +11682,9 @@ ${claims(evidence)}`;
|
|
|
11407
11682
|
}
|
|
11408
11683
|
function approve(flow) {
|
|
11409
11684
|
const logo2 = providerMark(flow.provider.id, flow.provider.method);
|
|
11410
|
-
const external =
|
|
11685
|
+
const external = namedCard(
|
|
11411
11686
|
logo2 ? [logo2, document.createTextNode(flow.provider.name)] : [document.createTextNode(flow.provider.name)],
|
|
11412
|
-
|
|
11413
|
-
externalId(flow.external),
|
|
11414
|
-
externalLink(flow.external)
|
|
11687
|
+
flow.external
|
|
11415
11688
|
);
|
|
11416
11689
|
const parts = [subjectCard(flow.local), linkMark(), external];
|
|
11417
11690
|
const choice = node("fieldset");
|
|
@@ -11561,15 +11834,19 @@ ${claims(evidence)}`;
|
|
|
11561
11834
|
},
|
|
11562
11835
|
latestGroup.get(element),
|
|
11563
11836
|
accounts,
|
|
11564
|
-
managers.get(element)
|
|
11837
|
+
managers.get(element),
|
|
11838
|
+
openHosts.has(element)
|
|
11565
11839
|
);
|
|
11566
11840
|
}
|
|
11567
11841
|
}
|
|
11568
11842
|
var stackedHosts = /* @__PURE__ */ new WeakSet();
|
|
11569
11843
|
var quietHosts = /* @__PURE__ */ new WeakSet();
|
|
11844
|
+
var openHosts = /* @__PURE__ */ new WeakSet();
|
|
11570
11845
|
var stackedAccounts = 4;
|
|
11571
11846
|
var peekedAccounts = 8;
|
|
11572
|
-
function arrange(element, { stacked, peek }) {
|
|
11847
|
+
function arrange(element, { stacked, peek, detailsOpen }) {
|
|
11848
|
+
if (detailsOpen) openHosts.add(element);
|
|
11849
|
+
else openHosts.delete(element);
|
|
11573
11850
|
if (stacked) stackedHosts.add(element);
|
|
11574
11851
|
else stackedHosts.delete(element);
|
|
11575
11852
|
if (peek === false) quietHosts.add(element);
|
|
@@ -11577,34 +11854,14 @@ ${claims(evidence)}`;
|
|
|
11577
11854
|
}
|
|
11578
11855
|
var live = (evidence) => evidence.status === "verified" && evidence.expiresAt > Date.now();
|
|
11579
11856
|
function accounts(records) {
|
|
11580
|
-
|
|
11581
|
-
|
|
11582
|
-
|
|
11583
|
-
|
|
11584
|
-
|
|
11585
|
-
|
|
11586
|
-
|
|
11587
|
-
|
|
11588
|
-
const verified = sorted.filter(live);
|
|
11589
|
-
const base = verified[0] ?? sorted.reduce((a2, b2) => b2.approvedAt > a2.approvedAt ? b2 : a2);
|
|
11590
|
-
const external = [...base.attestations.external];
|
|
11591
|
-
for (const other of verified.slice(1))
|
|
11592
|
-
for (const attestation of other.attestations.external)
|
|
11593
|
-
if (!external.some((shown3) => shown3.method === attestation.method))
|
|
11594
|
-
external.push(attestation);
|
|
11595
|
-
return {
|
|
11596
|
-
...base,
|
|
11597
|
-
connectedAt: when(sorted[0]),
|
|
11598
|
-
attestations: { ...base.attestations, external },
|
|
11599
|
-
links: sorted.filter((e2) => e2.status !== "revoked").map((e2) => e2.id),
|
|
11600
|
-
// Every record the card speaks for, so its signature mark answers for all of them.
|
|
11601
|
-
sources: verified.length ? verified : [base]
|
|
11602
|
-
};
|
|
11603
|
-
}).sort((a2, b2) => place2(a2) - place2(b2) || a2.connectedAt - b2.connectedAt);
|
|
11604
|
-
}
|
|
11605
|
-
function place2(account) {
|
|
11606
|
-
if (account.status === "retired") return 3;
|
|
11607
|
-
return account.mark === "preferred" ? 0 : account.mark === "unused" ? 2 : 1;
|
|
11857
|
+
return accountsOf(records, Date.now()).map(({ lead, held, verified, external, connectedAt }) => ({
|
|
11858
|
+
...lead,
|
|
11859
|
+
connectedAt,
|
|
11860
|
+
attestations: { ...lead.attestations, external },
|
|
11861
|
+
links: held.filter((e2) => e2.status !== "revoked").map((e2) => e2.id),
|
|
11862
|
+
// Every record the card speaks for, so its signature mark answers for all of them.
|
|
11863
|
+
sources: verified.length ? verified : [lead]
|
|
11864
|
+
}));
|
|
11608
11865
|
}
|
|
11609
11866
|
function presentGroup(element, records, load2) {
|
|
11610
11867
|
const ordered = accounts(records.map(standing));
|
|
@@ -11617,6 +11874,8 @@ ${claims(evidence)}`;
|
|
|
11617
11874
|
const badge = renderBadge(element, lead, {
|
|
11618
11875
|
more: Math.max(verified.length - 1, 0),
|
|
11619
11876
|
linked: lead.visibility === "public",
|
|
11877
|
+
// A modified click follows the link, and must reach what a plain one shows: every account.
|
|
11878
|
+
...ordered.length > 1 ? { href: subjectUrl(lead) } : {},
|
|
11620
11879
|
rows: stacked ? verified.slice(1, stackedAccounts) : peek ? verified.slice(1, peekedAccounts) : [],
|
|
11621
11880
|
peek
|
|
11622
11881
|
});
|
|
@@ -11972,7 +12231,7 @@ ${claims(evidence)}`;
|
|
|
11972
12231
|
(k2) => typeof value.local[k2] === "string"
|
|
11973
12232
|
) && ["id", "handle", "profileUrl"].every(
|
|
11974
12233
|
(k2) => typeof value.external[k2] === "string"
|
|
11975
|
-
) && (value.local.profileUrl === void 0 || typeof value.local.profileUrl === "string") && // A signed record lives beside the record it was made from and nowhere else, since the
|
|
12234
|
+
) && (value.local.profileUrl === void 0 || typeof value.local.profileUrl === "string") && (value.external.createdAt === void 0 || typeof value.external.createdAt === "number" && Number.isFinite(value.external.createdAt)) && // A signed record lives beside the record it was made from and nowhere else, since the
|
|
11976
12235
|
// keys it is checked against are read from where the record lives.
|
|
11977
12236
|
(value.signedUrl === void 0 || value.signedUrl === `${value.evidenceUrl}?format=signed`) && validAttestations(value.attestations) && (value.local.kind === void 0 || typeof value.local.kind === "string") && (value.external.kind === void 0 || ["account", "key", "page", "mailbox", "domain"].includes(String(value.external.kind))) && (value.revokedAt === void 0 || typeof value.revokedAt === "number" && Number.isFinite(value.revokedAt)) && ["verified", "unconfirmed", "expired", "retired", "revoked"].includes(String(value.status)) && // A retired record stands on its dates, so one that does not say when is not one.
|
|
11978
12237
|
(value.status === "retired" ? typeof value.retiredAt === "number" && Number.isFinite(value.retiredAt) : value.retiredAt === void 0) && (value.mark === void 0 || ["preferred", "unused"].includes(String(value.mark))) && ["public", "unlisted"].includes(String(value.visibility)) && (value.connectedAt === void 0 || typeof value.connectedAt === "number" && Number.isFinite(value.connectedAt)) && ["authenticatedAt", "approvedAt", "expiresAt"].every(
|
|
@@ -12017,7 +12276,8 @@ ${claims(evidence)}`;
|
|
|
12017
12276
|
"handoff-url",
|
|
12018
12277
|
"connect",
|
|
12019
12278
|
"stacked",
|
|
12020
|
-
"peek"
|
|
12279
|
+
"peek",
|
|
12280
|
+
"details-open"
|
|
12021
12281
|
];
|
|
12022
12282
|
handed;
|
|
12023
12283
|
/**
|
|
@@ -12071,7 +12331,8 @@ ${claims(evidence)}`;
|
|
|
12071
12331
|
const handoffUrl = this.getAttribute("handoff-url");
|
|
12072
12332
|
const arrangement = {
|
|
12073
12333
|
stacked: this.getAttribute("stacked") !== null,
|
|
12074
|
-
peek: this.getAttribute("peek") !== "off"
|
|
12334
|
+
peek: this.getAttribute("peek") !== "off",
|
|
12335
|
+
detailsOpen: this.getAttribute("details-open") !== null
|
|
12075
12336
|
};
|
|
12076
12337
|
if (backendUrl && (handoffUrl !== null || this.getAttribute("connect") !== null)) {
|
|
12077
12338
|
const client = init({ backendUrl, handoffUrl: handoffUrl ?? void 0 });
|