@bkazemi/verily 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/verily.js CHANGED
@@ -9575,6 +9575,7 @@ var Verily = (() => {
9575
9575
  __export(browser_exports, {
9576
9576
  Refused: () => Refused,
9577
9577
  attestationLabel: () => attestationLabel,
9578
+ externalId: () => externalId,
9578
9579
  externalLink: () => externalLink,
9579
9580
  externalName: () => externalName,
9580
9581
  fresh: () => fresh,
@@ -9583,6 +9584,7 @@ var Verily = (() => {
9583
9584
  isArtifactProvider: () => isArtifactProvider,
9584
9585
  isCodeProvider: () => isCodeProvider,
9585
9586
  isRedirectProvider: () => isRedirectProvider,
9587
+ lastProved: () => lastProved,
9586
9588
  localSide: () => localSide,
9587
9589
  presentConnections: () => presentConnections,
9588
9590
  proofTitle: () => proofTitle,
@@ -9651,7 +9653,7 @@ var Verily = (() => {
9651
9653
  });
9652
9654
  await signatures2[0].verified;
9653
9655
  const document2 = JSON.parse(data);
9654
- return document2?.type === "verily-evidence" && document2.version === 1 && typeof document2.issuedAt === "number" ? document2 : void 0;
9656
+ return document2?.type === "verily-evidence" && typeof document2.issuedAt === "number" && (document2.version === 1 || document2.version === 2 && document2.status === "retired" && typeof document2.retiredAt === "number") ? document2 : void 0;
9655
9657
  } catch {
9656
9658
  continue;
9657
9659
  }
@@ -9677,6 +9679,7 @@ var Verily = (() => {
9677
9679
  var freshnessMs = 7 * 864e5;
9678
9680
  function status(connection, now, freshness = freshnessMs) {
9679
9681
  if (connection.revokedAt !== void 0) return "revoked";
9682
+ if (connection.retiredAt !== void 0) return "retired";
9680
9683
  if (now >= connection.expiresAt) return "expired";
9681
9684
  const main = connection.attestations?.external[0];
9682
9685
  if (main && !fresh(main, now, freshness)) return "unconfirmed";
@@ -9685,8 +9688,13 @@ var Verily = (() => {
9685
9688
  function fresh(attestation, now, freshness = freshnessMs) {
9686
9689
  return !attestation.artifactUrl || attestation.hosted === true || freshness === Infinity || now < attestation.confirmedAt + freshness;
9687
9690
  }
9691
+ function lastProved(evidence) {
9692
+ const main = evidence.attestations.external[0];
9693
+ return main.artifactUrl ? main.confirmedAt : evidence.authenticatedAt;
9694
+ }
9688
9695
  function statusLabel(evidence, now) {
9689
9696
  if (evidence.status === "revoked") return "Revoked";
9697
+ if (evidence.status === "retired") return "Retired";
9690
9698
  if (evidence.status === "expired" || evidence.expiresAt <= now) return "Expired";
9691
9699
  return evidence.status === "unconfirmed" ? "Unconfirmed" : "Verified";
9692
9700
  }
@@ -9708,10 +9716,15 @@ var Verily = (() => {
9708
9716
  oauth: `Signed in with ${names.provider}`,
9709
9717
  gist: `Published a proof on ${names.provider}`,
9710
9718
  backlink: `Linked back to ${names.site}`,
9719
+ dns: `Named ${names.site} in a DNS record`,
9720
+ wellknown: `Named ${names.site} in a file it serves`,
9711
9721
  signature: "Proved with a signature",
9712
9722
  code: "Entered a code sent to this address"
9713
9723
  }[method];
9714
9724
  }
9725
+ function externalId(external) {
9726
+ return external.kind === "mailbox" || external.kind === "domain" ? void 0 : external.id;
9727
+ }
9715
9728
  function externalLink(external) {
9716
9729
  return external.kind === "mailbox" ? void 0 : external.profileUrl;
9717
9730
  }
@@ -9782,6 +9795,11 @@ var Verily = (() => {
9782
9795
  ["circle", { cx: "8", cy: "8", r: "6.2" }],
9783
9796
  ["path", { d: "M8 4.6V8h2.6" }]
9784
9797
  ]);
9798
+ var boxMark = () => statusMark("box", [
9799
+ ["path", { d: "M2.6 3.8h10.8v2.6H2.6z" }],
9800
+ ["path", { d: "M3.7 6.4v5.8h8.6V6.4" }],
9801
+ ["path", { d: "M6.6 9h2.8" }]
9802
+ ]);
9785
9803
  var dashMark = () => statusMark("dash", [["path", { d: "M4.4 8h7.2" }]]);
9786
9804
 
9787
9805
  // src/browser/provider-mark.ts
@@ -9870,7 +9888,7 @@ var Verily = (() => {
9870
9888
  openpgp: key,
9871
9889
  email: envelope
9872
9890
  };
9873
- return marks2[provider]?.() ?? (method === "backlink" ? globe() : void 0);
9891
+ return marks2[provider]?.() ?? (["backlink", "dns", "wellknown"].includes(method ?? "") ? globe() : void 0);
9874
9892
  }
9875
9893
 
9876
9894
  // src/browser/badge.ts
@@ -9926,7 +9944,9 @@ var Verily = (() => {
9926
9944
  .peek .via { display: block; margin-top: 6px; font-size: 11px; color: var(--verily-muted, #65726c); }
9927
9945
  .provider { display: block; flex-shrink: 0; width: 14px; height: 14px; }
9928
9946
  .expired .icon { color: #815a12; background: #fbefce; }
9929
- .revoked .icon, .message .icon { color: #626d69; background: #edf0ee; }
9947
+ .revoked .icon, .retired .icon, .message .icon { color: #626d69; background: #edf0ee; }
9948
+ /* Nothing ran out and nothing wants attention: the holder chose this, so it is only quiet. */
9949
+ .retired .name { color: var(--verily-muted, #65726c); }
9930
9950
  .message { color: var(--verily-muted, #65726c); }
9931
9951
  .add { font-weight: 600; }
9932
9952
  @media (prefers-reduced-motion: no-preference) {
@@ -10055,7 +10075,8 @@ var Verily = (() => {
10055
10075
  } = {}) {
10056
10076
  const provider = evidence.providerName;
10057
10077
  const current = evidence.status === "verified" && evidence.expiresAt > Date.now();
10058
- const state = current ? "verified" : evidence.status === "revoked" ? "revoked" : "expired";
10078
+ const retired = evidence.status === "retired";
10079
+ const state = current ? "verified" : retired ? "retired" : evidence.status === "revoked" ? "revoked" : "expired";
10059
10080
  const label = statusLabel(evidence, Date.now());
10060
10081
  const key2 = renderKey(evidence, current, label, more, linked, rows, peek);
10061
10082
  if (intact(element) && shown.get(element) === key2) return null;
@@ -10064,7 +10085,7 @@ var Verily = (() => {
10064
10085
  const unnamed = Math.max(more - rows.length, 0);
10065
10086
  const named = rows.map((row) => `, ${row.providerName} ${externalName(row.external)}`).join("");
10066
10087
  const others = `${named}${unnamed ? ` and ${unnamed} more` : ""}`;
10067
- paintMark(mark2, current ? "current" : "inactive");
10088
+ paintMark(mark2, current ? "current" : retired ? "pending" : "inactive");
10068
10089
  if (linked) {
10069
10090
  badge.href = evidence.evidenceUrl;
10070
10091
  badge.rel = "noreferrer";
@@ -10101,7 +10122,7 @@ var Verily = (() => {
10101
10122
  }
10102
10123
  if (!current) {
10103
10124
  const icon = span("icon", "");
10104
- icon.append(state === "expired" ? clockMark() : dashMark());
10125
+ icon.append(retired ? boxMark() : state === "expired" ? clockMark() : dashMark());
10105
10126
  icon.setAttribute("aria-hidden", "true");
10106
10127
  badge.append(span("label", label), icon);
10107
10128
  }
@@ -10232,6 +10253,7 @@ var Verily = (() => {
10232
10253
  status: _status,
10233
10254
  revokedAt: _revokedAt,
10234
10255
  signedUrl: _signedUrl,
10256
+ mark: _mark,
10235
10257
  links: _links,
10236
10258
  type: _type,
10237
10259
  version: _version,
@@ -10298,11 +10320,23 @@ ${claims(evidence)}`;
10298
10320
  const document2 = await verifySigned(signed, keys);
10299
10321
  if (!document2 || document2.id !== evidence.id || document2.verifierName !== evidence.verifierName || document2.evidenceUrl !== evidence.evidenceUrl || document2.siteName !== evidence.siteName || document2.local.reference !== evidence.local.reference || document2.provider !== evidence.provider || document2.external.id !== evidence.external.id)
10300
10322
  return { state: "invalid" };
10301
- const { type: _type, version: _version, issuedAt: _issuedAt, ...said } = document2;
10323
+ if (document2.version === 2 !== (evidence.status === "retired"))
10324
+ return { state: "unchecked", why: "unreadable" };
10325
+ const {
10326
+ type: _type,
10327
+ version: _version,
10328
+ issuedAt: _issuedAt,
10329
+ status: _status,
10330
+ retiredAt: _retiredAt,
10331
+ mark: _mark,
10332
+ ...said
10333
+ } = document2;
10302
10334
  const record2 = {
10303
10335
  ...said,
10304
10336
  status: evidence.status,
10305
10337
  ...evidence.revokedAt === void 0 ? {} : { revokedAt: evidence.revokedAt },
10338
+ ...document2.version === 2 ? { retiredAt: document2.retiredAt } : {},
10339
+ ...evidence.mark === void 0 ? {} : { mark: evidence.mark },
10306
10340
  signedUrl: evidence.signedUrl
10307
10341
  };
10308
10342
  return drawable(record2) ? { state: "valid", keyId: by2, record: record2 } : { state: "unchecked", why: "unreadable" };
@@ -10336,12 +10370,15 @@ ${claims(evidence)}`;
10336
10370
  if (evidence.signedUrl === void 0) return evidence;
10337
10371
  const key2 = shown2(evidence);
10338
10372
  const signed = signedAs.get(key2);
10339
- if (signed)
10373
+ if (signed) {
10374
+ const { mark: _mark, ...said } = signed;
10340
10375
  return {
10341
- ...signed,
10376
+ ...said,
10342
10377
  status: evidence.status,
10343
- ...evidence.revokedAt === void 0 ? {} : { revokedAt: evidence.revokedAt }
10378
+ ...evidence.revokedAt === void 0 ? {} : { revokedAt: evidence.revokedAt },
10379
+ ...evidence.mark === void 0 ? {} : { mark: evidence.mark }
10344
10380
  };
10381
+ }
10345
10382
  return evidence.status === "verified" && failures.has(key2) ? { ...evidence, status: "unconfirmed" } : evidence;
10346
10383
  }
10347
10384
  function watchSigned(records, changed) {
@@ -10379,6 +10416,7 @@ ${claims(evidence)}`;
10379
10416
  var logo = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="${-pad} ${-26 - pad} ${2637 + 2 * pad} ${916 + 2 * pad}" class="logo" role="img" aria-label="Verily"><rect x="${-pad}" y="${-26 - pad}" width="${2637 + 2 * pad}" height="${916 + 2 * pad}" rx="166" fill="#0d1117"/>` + logoPaths.map(({ d: d2, fill }) => `<path d="${d2}" fill="${fill === "currentColor" ? "#ffffff" : fill}"/>`).join("") + "</svg>";
10380
10417
 
10381
10418
  // src/browser/logo.ts
10419
+ var projectUrl = "https://github.com/bkazemi/verily";
10382
10420
  function verilyLogo() {
10383
10421
  const namespace2 = "http://www.w3.org/2000/svg";
10384
10422
  const svg = document.createElementNS(namespace2, "svg");
@@ -10396,9 +10434,14 @@ ${claims(evidence)}`;
10396
10434
  }
10397
10435
 
10398
10436
  // src/version.ts
10399
- var version = "v0.2.0";
10437
+ var version = "v0.4.0";
10400
10438
 
10401
10439
  // src/browser/evidence-dialog.ts
10440
+ function stampLink() {
10441
+ const link = outward(node("a"), projectUrl);
10442
+ link.append(verilyLogo());
10443
+ return link;
10444
+ }
10402
10445
  var openDialogs = /* @__PURE__ */ new WeakMap();
10403
10446
  var styles2 = `
10404
10447
  * { box-sizing: border-box; }
@@ -10431,6 +10474,14 @@ ${claims(evidence)}`;
10431
10474
  .summary a, .method a, .additional a { font: inherit; }
10432
10475
  .reference { margin-top: 2px; }
10433
10476
  .method { margin-top: 8px; }
10477
+ /* In the corner the heading leaves empty, apart from everything that was checked. */
10478
+ .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; }
10479
+ .listing.preferred { border-color: #245f43; color: #245f43; }
10480
+ /* Quiet, as its pill is: the surface and the name step back, and the controls do not. */
10481
+ .account.retired { background: #f6f8f5; }
10482
+ .account.retired > a, .account.retired > strong { color: #6b786f; }
10483
+ .account.retired .mark { opacity: .45; }
10484
+ .group { margin: 20px 0 0; color: #6b786f; font-size: 11px; font-weight: 550; }
10434
10485
  .additional { margin-top: 2px; padding-left: 12px; }
10435
10486
  .joiner { display: block; width: 20px; height: 20px; margin: 8px auto -4px; color: #90a096; }
10436
10487
  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; }
@@ -10443,9 +10494,10 @@ ${claims(evidence)}`;
10443
10494
  .action:disabled { opacity: .6; cursor: progress; }
10444
10495
  .action.danger:hover { border-color: #b3261e; background: #fdecea; color: #b3261e; }
10445
10496
  .row { display: flex; justify-content: end; gap: 8px; margin-top: 16px; }
10446
- .account .row { align-items: center; justify-content: start; margin-top: 12px; }
10497
+ .account .row { align-items: center; justify-content: start; flex-wrap: wrap; margin-top: 12px; }
10447
10498
  .account .action { padding: 5px 10px; font-size: 12px; }
10448
- footer { display: flex; align-items: center; justify-content: start; gap: 6px; margin-top: 14px; color: #9aa9a0; font-size: 11px; }
10499
+ footer { display: flex; align-items: center; justify-content: start; gap: 6px; margin: 20px 0 -8px; color: #9aa9a0; font-size: 11px; }
10500
+ footer a { display: flex; color: inherit; }
10449
10501
  .logo { display: block; height: 13px; }
10450
10502
  ${markStyles}
10451
10503
  `;
@@ -10510,6 +10562,7 @@ ${claims(evidence)}`;
10510
10562
  card.append(...extra);
10511
10563
  return card;
10512
10564
  }
10565
+ var retiringNote = "Retiring keeps this record as history. It will no longer read as verified, and who can read it cannot be changed afterwards.";
10513
10566
  var removable = (evidence) => evidence.links ?? (evidence.status === "revoked" ? [] : [evidence.id]);
10514
10567
  function manageRow(evidence, manage) {
10515
10568
  const row = node("div", "", "row");
@@ -10518,12 +10571,49 @@ ${claims(evidence)}`;
10518
10571
  control.type = "button";
10519
10572
  return control;
10520
10573
  };
10574
+ const [named] = removable(evidence);
10575
+ const { mark: mark2 } = manage;
10576
+ const listable = mark2 !== void 0 && named !== void 0 && !["revoked", "retired"].includes(evidence.status);
10577
+ const list = (label, as2) => {
10578
+ const control = act(label);
10579
+ control.onclick = async () => {
10580
+ for (const other of row.children) other.disabled = true;
10581
+ try {
10582
+ await mark2(named, as2);
10583
+ } catch {
10584
+ offer("That did not go through.");
10585
+ }
10586
+ };
10587
+ return control;
10588
+ };
10521
10589
  const offer = (note = "") => {
10522
10590
  const renew = act("Renew");
10523
10591
  const remove = act("Remove", true);
10524
- renew.onclick = () => manage.connect(evidence.provider);
10592
+ renew.onclick = () => manage.renew && evidence.status !== "revoked" ? manage.renew(
10593
+ evidence.id,
10594
+ evidence.provider,
10595
+ evidence.status === "retired" ? evidence.attestations.external[0].method : void 0
10596
+ ) : manage.connect(evidence.provider);
10525
10597
  remove.onclick = confirm;
10526
- row.replaceChildren(renew, remove, ...note ? [node("span", note, "muted")] : []);
10598
+ const listing = [];
10599
+ if (listable) {
10600
+ const retire = act("Retire");
10601
+ retire.onclick = retiring;
10602
+ listing.push(
10603
+ ...evidence.mark === "preferred" ? [] : [list("Mark as preferred", "preferred")],
10604
+ ...evidence.mark === "unused" ? [] : [list("Mark as unused", "unused")],
10605
+ ...evidence.mark === void 0 ? [] : [list("Mark as current", "current")],
10606
+ retire
10607
+ );
10608
+ }
10609
+ row.replaceChildren(renew, ...listing, remove, ...note ? [node("span", note, "muted")] : []);
10610
+ };
10611
+ const retiring = () => {
10612
+ const cancel = act("Cancel");
10613
+ const sure = list("Retire", "retired");
10614
+ cancel.onclick = () => offer();
10615
+ row.replaceChildren(node("span", retiringNote, "muted"), cancel, sure);
10616
+ cancel.focus();
10527
10617
  };
10528
10618
  const confirm = () => {
10529
10619
  const cancel = act("Cancel");
@@ -10626,11 +10716,15 @@ ${claims(evidence)}`;
10626
10716
  } else attribution.append(document.createTextNode(evidence.verifierName));
10627
10717
  if (evidence.signedUrl) attribution.append(signedMark(evidence, later));
10628
10718
  copy.append(node("div", status2, "state"), attribution);
10629
- summary.append(verificationMark(current ? "current" : "inactive"), copy);
10719
+ const retired = evidence.status === "retired";
10720
+ summary.append(verificationMark(current ? "current" : retired ? "pending" : "inactive"), copy);
10630
10721
  const dates = node("dl");
10631
10722
  const dateRows = [["Approved", evidence.approvedAt]];
10632
10723
  if (evidence.status === "revoked") {
10633
10724
  if (evidence.revokedAt !== void 0) dateRows.push(["Revoked on", evidence.revokedAt]);
10725
+ } else if (retired) {
10726
+ dateRows.push(["Last verified", lastProved(evidence)]);
10727
+ if (evidence.retiredAt !== void 0) dateRows.push(["Retired", evidence.retiredAt]);
10634
10728
  } else {
10635
10729
  dateRows.push([
10636
10730
  evidence.expiresAt > Date.now() ? "Valid until" : "Expired on",
@@ -10638,7 +10732,7 @@ ${claims(evidence)}`;
10638
10732
  ]);
10639
10733
  }
10640
10734
  const main = evidence.attestations.external[0];
10641
- if (main.artifactUrl) dateRows.push(["Last checked", main.confirmedAt]);
10735
+ if (main.artifactUrl && !retired) dateRows.push(["Last checked", main.confirmedAt]);
10642
10736
  for (const [label, time] of dateRows) {
10643
10737
  dates.append(node("dt", label), node("dd", moment(time)));
10644
10738
  }
@@ -10649,27 +10743,30 @@ ${claims(evidence)}`;
10649
10743
  // would print it twice here, since this heading writes it either way.
10650
10744
  logo2 ? [logo2, document.createTextNode(provider)] : [document.createTextNode(provider)],
10651
10745
  externalName(evidence.external),
10652
- // A mailbox's address is its name already, so there is no second identifier.
10653
- evidence.external.kind === "mailbox" ? void 0 : evidence.external.id,
10746
+ // A mailbox's address or a domain is its name already, so there is no second identifier.
10747
+ externalId(evidence.external),
10654
10748
  externalLink(evidence.external),
10655
10749
  // Status first, then how it was shown, then when. The proof explains the state
10656
10750
  // above it, so it cannot sit before that state has been given.
10657
10751
  summary,
10658
10752
  ...attestationNote(main, names),
10659
10753
  ...evidence.attestations.external.slice(1).flatMap((a2) => attestationNote(a2, names, true)),
10754
+ ...evidence.status === "revoked" || retired ? [] : evidence.mark === "preferred" ? [node("div", "Preferred", "listing preferred")] : evidence.mark === "unused" ? [node("div", "No longer used", "listing")] : [],
10660
10755
  dates,
10661
10756
  ...manage && removable(evidence).length ? [manageRow(evidence, manage)] : []
10662
10757
  );
10758
+ const surface = retired ? "account retired" : "account";
10759
+ card.className = surface;
10663
10760
  if (evidence.signedUrl) {
10664
10761
  const veiled = [...card.children];
10665
10762
  const checking = node("div", "", "checking");
10666
10763
  checking.setAttribute("role", "status");
10667
10764
  checking.append(node("span", "", "spinner"), document.createTextNode("Checking signature\u2026"));
10668
- card.className = "account veiled";
10765
+ card.className = `${surface} veiled`;
10669
10766
  for (const part of veiled) part.inert = true;
10670
10767
  card.append(checking);
10671
10768
  void signatures(evidence).then(() => {
10672
- card.className = "account";
10769
+ card.className = surface;
10673
10770
  for (const part of veiled) part.inert = false;
10674
10771
  checking.remove();
10675
10772
  });
@@ -10700,11 +10797,16 @@ ${claims(evidence)}`;
10700
10797
  provider: first.providerName
10701
10798
  })
10702
10799
  );
10800
+ const retired = records.filter((record2) => record2.status === "retired");
10703
10801
  content.replaceChildren(
10704
10802
  localCard,
10705
10803
  linkMark(),
10706
- ...records.map((record2) => externalCard(record2, manage, later)),
10804
+ ...records.filter((record2) => record2.status !== "retired").map((record2) => externalCard(record2, manage, later)),
10707
10805
  ...manage ? [addRow(manage)] : [],
10806
+ ...retired.length ? [
10807
+ node("h3", "Retired accounts", "group"),
10808
+ ...retired.map((record2) => externalCard(record2, manage, later))
10809
+ ] : [],
10708
10810
  // Nothing below the cards may name a provider. Approval, method and dates belong to
10709
10811
  // the card they came from, and a second provider on this subject gets its own card.
10710
10812
  node(
@@ -10736,11 +10838,30 @@ ${claims(evidence)}`;
10736
10838
  close.type = "button";
10737
10839
  close.setAttribute("aria-label", "Close verification details");
10738
10840
  content.setAttribute("aria-live", "polite");
10739
- stamp.append(verilyLogo(), node("span", version));
10841
+ stamp.append(stampLink(), node("span", version));
10740
10842
  header.append(heading, close);
10741
10843
  dialog.append(header, content, stamp);
10742
10844
  const removed = /* @__PURE__ */ new Map();
10845
+ const listed = [];
10846
+ const listing = (record2) => JSON.stringify([record2.status, record2.mark, record2.retiredAt, record2.approvedAt]);
10847
+ const listings = (given) => new Map([given].flat().map((record2) => [record2.id, listing(record2)]));
10743
10848
  let held;
10849
+ const relisted = (evidence) => {
10850
+ let record2 = evidence;
10851
+ for (const { ids, as: as2, at: at2 } of listed) {
10852
+ if (["revoked", "retired"].includes(record2.status)) break;
10853
+ if (!ids.includes(record2.id)) {
10854
+ if (as2 === "preferred" && record2.mark === "preferred")
10855
+ record2 = { ...record2, mark: void 0 };
10856
+ continue;
10857
+ }
10858
+ record2 = as2 === "retired" ? (
10859
+ // Its signed form is another document now, to be read with the records to come.
10860
+ { ...record2, status: "retired", retiredAt: at2, mark: void 0, signedUrl: void 0 }
10861
+ ) : { ...record2, mark: as2 === "current" ? void 0 : as2 };
10862
+ }
10863
+ return record2;
10864
+ };
10744
10865
  const actions = manage && {
10745
10866
  // The other dialog opens over this one before this one goes, so the page behind is
10746
10867
  // never uncovered between the two.
@@ -10748,11 +10869,32 @@ ${claims(evidence)}`;
10748
10869
  manage.connect(provider);
10749
10870
  dialog.close();
10750
10871
  },
10872
+ ...manage.renew ? {
10873
+ renew(id, provider, method) {
10874
+ manage.renew(id, provider, method);
10875
+ dialog.close();
10876
+ }
10877
+ } : {},
10751
10878
  async remove(ids) {
10752
10879
  await manage.remove(ids);
10753
10880
  for (const id of ids) removed.set(id, Date.now());
10754
10881
  if (held && dialog.open) draw(held);
10755
- }
10882
+ },
10883
+ ...manage.mark ? {
10884
+ async mark(id, as2) {
10885
+ await manage.mark(id, as2);
10886
+ const account = arrange2([held ?? []].flat()).find(
10887
+ (card) => removable(card).includes(id)
10888
+ );
10889
+ listed.push({
10890
+ ids: account ? removable(account) : [id],
10891
+ as: as2,
10892
+ at: Date.now(),
10893
+ before: listings(held ?? [])
10894
+ });
10895
+ if (held && dialog.open) draw(held);
10896
+ }
10897
+ } : {}
10756
10898
  };
10757
10899
  root.append(dialog);
10758
10900
  document.body.append(host);
@@ -10763,9 +10905,13 @@ ${claims(evidence)}`;
10763
10905
  const retries = /* @__PURE__ */ new Set();
10764
10906
  const draw = (given) => {
10765
10907
  held = given;
10908
+ const now = listings(given);
10909
+ for (const change of [...listed])
10910
+ if (change.ids.some((id) => now.get(id) !== change.before.get(id)))
10911
+ listed.splice(listed.indexOf(change), 1);
10766
10912
  const records = arrange2(
10767
10913
  [given].flat().map(
10768
- (e2) => removed.has(e2.id) && e2.status !== "revoked" ? { ...e2, status: "revoked", revokedAt: removed.get(e2.id) } : standing(e2)
10914
+ (e2) => removed.has(e2.id) && e2.status !== "revoked" ? { ...e2, status: "revoked", revokedAt: removed.get(e2.id) } : relisted(standing(e2))
10769
10915
  )
10770
10916
  );
10771
10917
  const key2 = JSON.stringify(records.map((e2) => [e2, statusLabel(e2, Date.now())]));
@@ -10906,7 +11052,7 @@ ${claims(evidence)}`;
10906
11052
  return element;
10907
11053
  }
10908
11054
  var openDialogs2 = /* @__PURE__ */ new WeakMap();
10909
- function openConnectDialog(opener, api, back, provider) {
11055
+ function openConnectDialog(opener, api, back, provider, renew) {
10910
11056
  const existing = openDialogs2.get(opener);
10911
11057
  if (existing?.open) {
10912
11058
  existing.focus();
@@ -10928,7 +11074,7 @@ ${claims(evidence)}`;
10928
11074
  close.type = "button";
10929
11075
  close.setAttribute("aria-label", "Close");
10930
11076
  content.setAttribute("aria-live", "polite");
10931
- stamp.append(verilyLogo(), node("span", version));
11077
+ stamp.append(stampLink(), node("span", version));
10932
11078
  header.append(heading, close);
10933
11079
  dialog.append(header, content, stamp);
10934
11080
  root.append(dialog);
@@ -11006,7 +11152,11 @@ ${claims(evidence)}`;
11006
11152
  return;
11007
11153
  }
11008
11154
  if (!current(asked)) return;
11009
- const all = methods.methods;
11155
+ const renewing = methods.methods.filter(
11156
+ (m2) => m2.provider === provider && (renew?.method === void 0 || m2.method === renew.method)
11157
+ );
11158
+ if (renew && !renewing.length) renew = void 0;
11159
+ const all = renew ? renewing : methods.methods;
11010
11160
  if (group !== void 0 && !all.some((m2) => m2.provider === group)) group = void 0;
11011
11161
  const count = (provider2) => all.filter((m2) => m2.provider === provider2).length;
11012
11162
  const list = node("div", "", "choices");
@@ -11035,7 +11185,7 @@ ${claims(evidence)}`;
11035
11185
  return;
11036
11186
  }
11037
11187
  }
11038
- void busy(control, () => api.start(choice.provider, choice.method), show);
11188
+ void busy(control, () => api.start(choice.provider, choice.method, renew?.id), show);
11039
11189
  };
11040
11190
  list.append(control);
11041
11191
  }
@@ -11049,7 +11199,7 @@ ${claims(evidence)}`;
11049
11199
  ),
11050
11200
  list
11051
11201
  ];
11052
- if (group !== void 0) {
11202
+ if (group !== void 0 && !renew) {
11053
11203
  const back2 = button("\u2039 All options");
11054
11204
  back2.onclick = () => {
11055
11205
  group = void 0;
@@ -11209,7 +11359,7 @@ ${claims(evidence)}`;
11209
11359
  const external = accountCard(
11210
11360
  logo2 ? [logo2, document.createTextNode(flow.provider.name)] : [document.createTextNode(flow.provider.name)],
11211
11361
  externalName(flow.external),
11212
- flow.external.kind === "mailbox" ? void 0 : flow.external.id,
11362
+ externalId(flow.external),
11213
11363
  externalLink(flow.external)
11214
11364
  );
11215
11365
  const parts = [subjectCard(flow.local), linkMark(), external];
@@ -11399,7 +11549,11 @@ ${claims(evidence)}`;
11399
11549
  // Every record the card speaks for, so its signature mark answers for all of them.
11400
11550
  sources: verified.length ? verified : [base]
11401
11551
  };
11402
- }).sort((a2, b2) => a2.connectedAt - b2.connectedAt);
11552
+ }).sort((a2, b2) => place2(a2) - place2(b2) || a2.connectedAt - b2.connectedAt);
11553
+ }
11554
+ function place2(account) {
11555
+ if (account.status === "retired") return 3;
11556
+ return account.mark === "preferred" ? 0 : account.mark === "unused" ? 2 : 1;
11403
11557
  }
11404
11558
  function presentGroup(element, records, load2) {
11405
11559
  const ordered = accounts(records.map(standing));
@@ -11506,7 +11660,12 @@ ${claims(evidence)}`;
11506
11660
  if (typeof data.local.profileUrl === "string") safeUrl(data.local.profileUrl);
11507
11661
  return data;
11508
11662
  },
11509
- start: async (provider, method) => flowView(await ask("/sessions", { kind: "connect", provider, method })),
11663
+ start: async (provider, method, renew) => flowView(
11664
+ await ask(
11665
+ "/sessions",
11666
+ renew ? { kind: "renew", connectionId: renew, provider, method } : { kind: "connect", provider, method }
11667
+ )
11668
+ ),
11510
11669
  read: async (id) => flowView(await ask(`/flows/${encodeURIComponent(id)}?format=json`, void 0, id)),
11511
11670
  submit: async (id, artifact) => flowView(await ask(`/flows/${encodeURIComponent(id)}/submit`, { artifact }, id)),
11512
11671
  async approve(id, visibility, cancel) {
@@ -11550,11 +11709,11 @@ ${claims(evidence)}`;
11550
11709
  * Opens the connect dialog over the current page. Resolves when it closes, with how
11551
11710
  * the last attempt in it ended. Call from a click: a sign-in method opens a window.
11552
11711
  */
11553
- openConnect(opener = document.body, back, provider) {
11712
+ openConnect(opener = document.body, back, provider, renew) {
11554
11713
  if (remote && !handoffUrl)
11555
11714
  throw new Error("A backend on another origin requires a handoff URL");
11556
11715
  session = void 0;
11557
- return openConnectDialog(opener, flows, back, provider);
11716
+ return openConnectDialog(opener, flows, back, provider, renew);
11558
11717
  },
11559
11718
  /**
11560
11719
  * Draws the pill that opens the connect dialog. The host receives a `verily-result`
@@ -11682,6 +11841,14 @@ ${claims(evidence)}`;
11682
11841
  if (remote) session = void 0;
11683
11842
  return ask(`/connections/${encodeURIComponent(id)}/disconnect`, {});
11684
11843
  },
11844
+ /**
11845
+ * Says how the holder would have an account listed, or retires it. Names one record
11846
+ * and acts on the account: every record of it the holder has.
11847
+ */
11848
+ mark(id, as2) {
11849
+ if (remote) session = void 0;
11850
+ return ask(`/connections/${encodeURIComponent(id)}/mark`, { as: as2 });
11851
+ },
11685
11852
  issueShare: (id) => request(`/connections/${encodeURIComponent(id)}/share`, {}),
11686
11853
  revokeShare: (id) => request(`/connections/${encodeURIComponent(id)}/share-revoke`, {})
11687
11854
  };
@@ -11753,7 +11920,8 @@ ${claims(evidence)}`;
11753
11920
  (k2) => typeof value.external[k2] === "string"
11754
11921
  ) && (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
11755
11922
  // keys it is checked against are read from where the record lives.
11756
- (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"].includes(String(value.external.kind))) && (value.revokedAt === void 0 || typeof value.revokedAt === "number" && Number.isFinite(value.revokedAt)) && ["verified", "unconfirmed", "expired", "revoked"].includes(String(value.status)) && ["public", "unlisted"].includes(String(value.visibility)) && (value.connectedAt === void 0 || typeof value.connectedAt === "number" && Number.isFinite(value.connectedAt)) && ["authenticatedAt", "approvedAt", "expiresAt"].every(
11923
+ (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.
11924
+ (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(
11757
11925
  (k2) => typeof value[k2] === "number" && Number.isFinite(value[k2])
11758
11926
  );
11759
11927
  }
@@ -11862,6 +12030,11 @@ ${claims(evidence)}`;
11862
12030
  if (result.outcome === "complete") changed(result);
11863
12031
  });
11864
12032
  },
12033
+ renew: (id, provider, method) => {
12034
+ void client.openConnect(this, () => details(this), provider, { id, method }).then((result) => {
12035
+ if (result.outcome === "complete") changed(result);
12036
+ });
12037
+ },
11865
12038
  // One at a time, each told as it goes: if a later one fails, the host still
11866
12039
  // hears of those that went and reads its records again.
11867
12040
  remove: async (ids2) => {
@@ -11869,6 +12042,10 @@ ${claims(evidence)}`;
11869
12042
  await client.disconnect(id);
11870
12043
  changed({ outcome: "removed", connectionId: id });
11871
12044
  }
12045
+ },
12046
+ mark: async (id, as2) => {
12047
+ await client.mark(id, as2);
12048
+ changed({ outcome: "marked", connectionId: id, as: as2 });
11872
12049
  }
11873
12050
  });
11874
12051
  } else managers.delete(this);
package/dist/version.d.ts CHANGED
@@ -7,4 +7,4 @@
7
7
  * output stays what tsc emitted and nothing has to be substituted into it. A version bump
8
8
  * changes this line with package.json.
9
9
  */
10
- export declare const version = "v0.2.0";
10
+ export declare const version = "v0.4.0";
package/dist/version.js CHANGED
@@ -7,4 +7,4 @@
7
7
  * output stays what tsc emitted and nothing has to be substituted into it. A version bump
8
8
  * changes this line with package.json.
9
9
  */
10
- export const version = 'v0.2.0';
10
+ export const version = 'v0.4.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bkazemi/verily",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Self-hosted account-to-account verification for Node.js",
5
5
  "type": "module",
6
6
  "license": "MIT",