@ofidj/entry 3.10.1 → 3.13.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 +153 -17
- package/dom.d.ts +28 -0
- package/dom.js +251 -0
- package/index.d.ts +2 -4
- package/index.js +15 -11
- package/model.d.ts +131 -0
- package/model.js +406 -0
- package/package.json +13 -1
- package/remembered.d.ts +3 -0
- package/remembered.js +33 -0
- package/server.d.ts +24 -0
- package/server.js +83 -0
- package/style.css +3 -1
- package/window.d.ts +1 -0
- package/window.js +7 -0
- package/screens.d.ts +0 -19
- package/screens.js +0 -52
- package/service-agreement.d.ts +0 -13
- package/service-agreement.js +0 -184
- package/version.d.ts +0 -1
- package/version.js +0 -22
package/server.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { agreementModel, credentialsModel, returnNoticeModel, } from "./model.js";
|
|
2
|
+
import { escape } from "./dom.js";
|
|
3
|
+
export const oidcInteractionScript = `(() => {
|
|
4
|
+
const bindPasswordReveal = (root) => {
|
|
5
|
+
root.querySelectorAll("button[aria-controls]").forEach((button) => {
|
|
6
|
+
const field = root.querySelector("#" + button.getAttribute("aria-controls"));
|
|
7
|
+
if (!field) return;
|
|
8
|
+
button.addEventListener("click", () => {
|
|
9
|
+
const hidden = field.type === "password";
|
|
10
|
+
field.type = hidden ? "text" : "password";
|
|
11
|
+
button.textContent = hidden ? "Hide" : "Show";
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
const bindAgreement = (root) => {
|
|
16
|
+
const checkbox = root.querySelector('input[name="terms"]');
|
|
17
|
+
const submit = root.querySelector('button[value="continue"]');
|
|
18
|
+
if (!checkbox || !submit) return;
|
|
19
|
+
const update = () => { submit.disabled = !checkbox.checked; };
|
|
20
|
+
checkbox.addEventListener("change", update);
|
|
21
|
+
update();
|
|
22
|
+
};
|
|
23
|
+
const bindAgreementWindow = (root) => {
|
|
24
|
+
root.querySelectorAll("a.agreement-document").forEach((link) => {
|
|
25
|
+
link.addEventListener("click", (event) => {
|
|
26
|
+
event.preventDefault();
|
|
27
|
+
window.open(link.href, "fidj-agreement", "popup,width=640,height=720,left=40,top=40,noopener");
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
bindPasswordReveal(document);
|
|
32
|
+
bindAgreement(document);
|
|
33
|
+
bindAgreementWindow(document);
|
|
34
|
+
})();`;
|
|
35
|
+
const baseStyles = `*{box-sizing:border-box}body{margin:0;padding:24px;font:16px system-ui;color:#153e37;background:#f3f6f1}main{width:min(100%,720px);min-height:calc(100vh - 48px);margin:0 auto;overflow:hidden;border:1px solid #b9ccc0;border-radius:20px;background:white}.oidc-brand{display:flex;align-items:center;gap:16px;padding:18px 28px;background:#173e36;color:white}.oidc-brand img{width:48px;height:48px}.oidc-brand p{margin:0;font-size:21px;font-weight:700;line-height:1.08}section{padding:32px 36px 40px}section>h1{margin:0 0 10px;font-size:30px;line-height:1.15}section>p{line-height:1.45}form{margin-top:24px}label{display:block;margin:20px 0 8px}input,button{width:100%;padding:14px;border:1px solid #b9ccc0;border-radius:10px;font:inherit}button{margin-top:14px;background:#173e36;color:white;cursor:pointer;font-weight:600}.secondary{background:white;color:#173e36}.cancel{border-color:transparent;background:transparent}.notice{margin:0 0 4px;padding:12px 14px;border:1px solid #ef4b42;background:#fdecea;color:#8a1c16;border-radius:8px}.return{margin:0;font-size:14px;color:#3f5c52}.field-head,.password-field{display:flex;align-items:flex-end;gap:12px}.field-head{justify-content:space-between}.field-head label{margin-bottom:0}.field-link{font-size:14px;color:#3f5c52}.password-field input{flex:1}.password-field button{width:auto;margin-top:0}.account-picker{display:grid;grid-template-columns:44px minmax(0,1fr) 24px;align-items:center;gap:12px;padding:12px 14px;border:1px solid #b9ccc0;border-radius:12px;background:#f3f6f1}.account-avatar{display:grid;width:44px;height:44px;place-items:center;border-radius:50%;background:#173e36;color:white;font-size:18px;font-weight:700}.account-copy{display:flex;min-width:0;flex-direction:column;gap:2px}.account-copy small{font-size:12px;color:#3f5c52}.account-email{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.account-check{font-size:20px;font-weight:700}.permission-title{margin:22px 0 8px;font-weight:600}.permission-list{margin:0;padding:12px 14px 12px 36px;border:1px solid #b9ccc0;border-radius:12px}.permission-list li{padding:3px 0}.account-switch{text-align:left}.agreement-choice{display:flex;align-items:flex-start;gap:10px;margin:20px 0 8px}.agreement-choice input{width:auto}.fineprint{margin:6px 0;font-size:14px;color:#3f5c52}.agreement-link{display:inline-block;margin:2px 0 8px;color:#173e36;font-weight:600}@media(max-width:700px){body{padding:0}main{min-height:100vh;border:0;border-radius:0}.oidc-brand{padding:16px 24px}.oidc-brand img{width:44px;height:44px}.oidc-brand p{font-size:19px}section{padding:26px 24px 34px}section>h1{font-size:26px}}`;
|
|
36
|
+
const styles = `${baseStyles}button:disabled{border-color:#d4d9d5;background:#d4d9d5;color:#778079;cursor:not-allowed}.agreement-choice a{color:#173e36;font-weight:600}`;
|
|
37
|
+
const loginFields = (page) => {
|
|
38
|
+
const model = credentialsModel({ email: page.email || "", password: "" });
|
|
39
|
+
return `<label for="email">${escape(model.email.label)}</label><input id="email" type="email" name="email" value="${escape(model.email.value)}" autocomplete="username" required><div class="field-head"><label for="password">${escape(model.password.label)}</label>${page.forgotHref ? `<a class="field-link" href="${escape(page.forgotHref)}">${escape(model.forgot.label)}</a>` : ""}</div><div class="password-field"><input id="password" type="password" name="password" autocomplete="current-password" required><button type="button" aria-controls="password">${escape(model.reveal.label)}</button></div><button name="action" value="continue">Sign in</button><button class="secondary" name="action" value="signup">Create a Fidj account</button>${page.googleEnabled ? '<button class="secondary" name="action" value="google" formnovalidate>Continue with linked Google account</button>' : ""}<button class="secondary" name="action" value="cancel" formnovalidate>Cancel</button>`;
|
|
40
|
+
};
|
|
41
|
+
const waitFields = (page) => {
|
|
42
|
+
const state = page.notYet
|
|
43
|
+
? "The link has not been opened yet. Open it, then press Continue again."
|
|
44
|
+
: page.resent
|
|
45
|
+
? "The link was sent again. Only the newest one works."
|
|
46
|
+
: "";
|
|
47
|
+
return `<p class="fineprint">The link may take a minute, and it sometimes lands in spam. Open it, then come back here.</p>${state ? `<p class="fineprint">${state}</p>` : ""}<button name="action" value="continue">Continue</button><button class="secondary" name="action" value="resend">Send the link again</button><button class="secondary" name="action" value="cancel" formnovalidate>Cancel</button>`;
|
|
48
|
+
};
|
|
49
|
+
const consentFields = (page) => {
|
|
50
|
+
const agreement = agreementModel(page.appTitle, page.agreement || {});
|
|
51
|
+
const initial = (page.recognisedEmail || "?").trim().charAt(0).toUpperCase();
|
|
52
|
+
const identity = page.recognisedEmail
|
|
53
|
+
? `<div class="account-picker"><span class="account-avatar" aria-hidden="true">${escape(initial)}</span><span class="account-copy"><small>Signed in with Fidj</small><strong class="account-email">${escape(page.recognisedEmail)}</strong></span><span class="account-check" aria-hidden="true">✓</span></div>`
|
|
54
|
+
: "";
|
|
55
|
+
const agreementChoice = page.agreement && page.agreementHref
|
|
56
|
+
? `<label class="agreement-choice"><input type="checkbox" name="terms" value="true" required> <span>I accept the <a class="agreement-document" href="${escape(page.agreementHref)}" target="fidj-agreement" rel="noopener">service agreement · version ${escape(page.agreement.version)} ↗</a></span></label>`
|
|
57
|
+
: `<label class="agreement-choice"><input type="checkbox" name="terms" value="true" required> ${escape(agreement.checkboxLabel)}</label>`;
|
|
58
|
+
const agreementText = page.agreement
|
|
59
|
+
? `<div class="agreement-text" tabindex="0">${escape(agreement.text)}</div>`
|
|
60
|
+
: "";
|
|
61
|
+
return `${identity}<p class="permission-title">${escape(page.appTitle)} will receive:</p><ul class="permission-list">${(page.scopes || []).map((scope) => `<li>${escape(scope)}</li>`).join("")}</ul>${agreementText}${agreementChoice}<button name="action" value="continue" disabled>Allow and continue</button><button class="secondary account-switch" name="action" value="switch" formnovalidate>Use another account</button><button class="cancel" name="action" value="cancel" formnovalidate>Cancel</button><script src="/oidc/assets/entry.js" defer></script>`;
|
|
62
|
+
};
|
|
63
|
+
export function oidcInteractionPage(page) {
|
|
64
|
+
const waiting = page.mode === "waiting";
|
|
65
|
+
const login = page.mode === "login";
|
|
66
|
+
const heading = waiting
|
|
67
|
+
? "Check your email"
|
|
68
|
+
: login
|
|
69
|
+
? `Sign in to your Fidj account to continue to ${escape(page.appTitle)}.`
|
|
70
|
+
: `Continue to ${escape(page.appTitle)}`;
|
|
71
|
+
const lead = waiting
|
|
72
|
+
? `Your account is created. Waiting for you to open the link sent to <strong>${escape(page.waitingEmail)}</strong>.`
|
|
73
|
+
: login
|
|
74
|
+
? ""
|
|
75
|
+
: "Share the requested identity information with this app. Optional privacy choices stay independent.";
|
|
76
|
+
const fields = waiting
|
|
77
|
+
? waitFields(page)
|
|
78
|
+
: login
|
|
79
|
+
? loginFields(page)
|
|
80
|
+
: consentFields(page);
|
|
81
|
+
return `<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>${waiting ? "Check your email" : login ? "Sign in" : "Authorize"} · Fidj</title><style>${styles}</style><main><header class="oidc-brand"><img src="/oidc/assets/logo.png" alt="Fidj" width="58" height="58"><p>Your identity.<br>Your choices.</p></header><section><h1>${heading}</h1>${lead ? `<p>${lead}</p>` : ""}<p class="return">${escape(returnNoticeModel(page.appTitle))}</p>${waiting || !page.notice ? "" : `<p class="notice" role="alert">${escape(page.notice)}</p>`}<form method="post" action="${escape(page.action)}"><input type="hidden" name="csrf" value="${escape(page.csrf)}">${fields}</form><p><small>Fidj never shares your password with the app.</small></p></section></main>${login ? '<script src="/oidc/assets/entry.js" defer></script>' : ""}</html>`;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3NlcnZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsY0FBYyxFQUNkLGdCQUFnQixFQUNoQixpQkFBaUIsR0FDbEIsTUFBTSxZQUFZLENBQUM7QUFDcEIsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLFVBQVUsQ0FBQztBQXNCbEMsTUFBTSxDQUFDLE1BQU0scUJBQXFCLEdBQUc7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7TUErQi9CLENBQUM7QUFFUCxNQUFNLFVBQVUsR0FBRyxxa0ZBQXFrRixDQUFDO0FBQ3psRixNQUFNLE1BQU0sR0FBRyxHQUFHLFVBQVUsNklBQTZJLENBQUM7QUFFMUssTUFBTSxXQUFXLEdBQUcsQ0FBQyxJQUF5QixFQUFFLEVBQUU7SUFDaEQsTUFBTSxLQUFLLEdBQUcsZ0JBQWdCLENBQUMsRUFBRSxLQUFLLEVBQUUsSUFBSSxDQUFDLEtBQUssSUFBSSxFQUFFLEVBQUUsUUFBUSxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDMUUsT0FBTyxzQkFBc0IsTUFBTSxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLDhEQUE4RCxNQUFNLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsb0ZBQW9GLE1BQU0sQ0FBQyxLQUFLLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxXQUFXLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLCtCQUErQixNQUFNLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxLQUFLLE1BQU0sQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsa0xBQWtMLE1BQU0sQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyw4SkFBOEosSUFBSSxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUMsb0hBQW9ILENB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
|
package/style.css
CHANGED
|
@@ -801,7 +801,7 @@ body.has-module {
|
|
|
801
801
|
padding: 8px 12px;
|
|
802
802
|
font-size: 13.5px;
|
|
803
803
|
color: var(--fidj-on-ink-faint);
|
|
804
|
-
border-bottom:
|
|
804
|
+
border-bottom: 2px solid transparent;
|
|
805
805
|
white-space: nowrap;
|
|
806
806
|
}
|
|
807
807
|
.topbar .content-nav button:hover:not(:disabled) {
|
|
@@ -810,6 +810,7 @@ body.has-module {
|
|
|
810
810
|
.topbar .content-nav button.selected {
|
|
811
811
|
color: var(--fidj-on-ink);
|
|
812
812
|
font-weight: 600;
|
|
813
|
+
border-bottom-color: var(--fidj-on-ink);
|
|
813
814
|
}
|
|
814
815
|
.topbar .content-nav button:last-child {
|
|
815
816
|
margin-left: 0;
|
|
@@ -989,6 +990,7 @@ summary:focus-visible {
|
|
|
989
990
|
.signin-agreement button { margin-block: 0.5rem; padding: 0; border: 0; background: transparent; text-decoration: underline; }
|
|
990
991
|
#agreement-dialog { color: var(--fidj-text); background: var(--fidj-paper); border: 1px solid var(--fidj-line); border-radius: var(--fidj-radius); padding: 1.5rem; max-width: min(40rem, calc(100vw - 2rem)); max-height: 80vh; overflow: auto; }
|
|
991
992
|
#agreement-text { white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
993
|
+
.agreement-text { max-height: 12rem; overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; margin: 1rem 0; padding: 1rem; border: 1px solid var(--fidj-line-soft); border-radius: var(--fidj-radius); background: var(--fidj-surface-sunk); font-size: 13px; line-height: 1.55; }
|
|
992
994
|
|
|
993
995
|
/* The entry explains itself before it offers its button. A person whose account
|
|
994
996
|
* lives somewhere else needs to know that before they look for the form that is
|
package/window.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { openProviderWindow, relayProviderAnswer, type ProviderWindow, } from "./provider-window.js";
|
package/window.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// The Fidj window, and the answer it relays back.
|
|
2
|
+
//
|
|
3
|
+
// A subpath of its own because it is neither pure nor a view layer: it drives
|
|
4
|
+
// `window.open` and `postMessage`, which every framework reaches for the same
|
|
5
|
+
// way. An app on React or Vue uses this exactly as a hand-written shell does.
|
|
6
|
+
export { openProviderWindow, relayProviderAnswer, } from "./provider-window.js";
|
|
7
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2luZG93LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3dpbmRvdy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxrREFBa0Q7QUFDbEQsRUFBRTtBQUNGLDhFQUE4RTtBQUM5RSw4RUFBOEU7QUFDOUUsOEVBQThFO0FBQzlFLE9BQU8sRUFDTCxrQkFBa0IsRUFDbEIsbUJBQW1CLEdBRXBCLE1BQU0sc0JBQXNCLENBQUMifQ==
|
package/screens.d.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
export declare const escape: (value: unknown) => string;
|
|
2
|
-
export declare function masthead(logo: string, title: string): string;
|
|
3
|
-
export declare function highlightCells(entries?: Array<{
|
|
4
|
-
heading: string;
|
|
5
|
-
body: string;
|
|
6
|
-
}>): string;
|
|
7
|
-
export declare function badgeStrip(entries?: string[]): string;
|
|
8
|
-
export declare function credentialFields(state: {
|
|
9
|
-
email: string;
|
|
10
|
-
password: string;
|
|
11
|
-
}): string;
|
|
12
|
-
export type AccountState = {
|
|
13
|
-
linkToken?: string;
|
|
14
|
-
verificationConfirmed?: boolean;
|
|
15
|
-
emailVerified?: boolean;
|
|
16
|
-
accountEmail?: string;
|
|
17
|
-
};
|
|
18
|
-
export declare function accountForm(route: string, state: AccountState): string;
|
|
19
|
-
export declare function returnNotice(asking: string): string;
|
package/screens.js
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
// The screens the entry draws, as markup.
|
|
2
|
-
//
|
|
3
|
-
// Lifted out of generator-fidj's content.ts, where the only way to ship them
|
|
4
|
-
// was to generate an app. What stayed behind is the shell that holds them:
|
|
5
|
-
// the sign-in screen nests signin-trust inside its inner div and the
|
|
6
|
-
// interaction screen makes it a sibling, so a single shell builder would have
|
|
7
|
-
// had to change one of them. That is a decision, not a move.
|
|
8
|
-
export const escape = (value) => String(value ?? "").replace(/[&<>"']/g, (char) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[char]);
|
|
9
|
-
// The app's own mark beside its own name, decorative because the name is right
|
|
10
|
-
// there: every shell opens with this.
|
|
11
|
-
export function masthead(logo, title) {
|
|
12
|
-
return `<header class="signin-masthead"><img class="app-mark" src="${escape(logo)}" alt=""><strong>${escape(title)}</strong></header>`;
|
|
13
|
-
}
|
|
14
|
-
export function highlightCells(entries) {
|
|
15
|
-
if (!entries?.length)
|
|
16
|
-
return "";
|
|
17
|
-
return `<div class="signin-highlights">${entries
|
|
18
|
-
.map((entry, index) => `<article><p class="eyebrow">${String(index + 1).padStart(2, "0")}</p><h2>${escape(entry.heading)}</h2><p>${escape(entry.body)}</p></article>`)
|
|
19
|
-
.join("")}</div>`;
|
|
20
|
-
}
|
|
21
|
-
export function badgeStrip(entries) {
|
|
22
|
-
if (!entries?.length)
|
|
23
|
-
return "";
|
|
24
|
-
return `<footer class="signin-badges">${entries
|
|
25
|
-
.map((entry) => `<span>${escape(entry)}</span>`)
|
|
26
|
-
.join("")}</footer>`;
|
|
27
|
-
}
|
|
28
|
-
// Both doors, and what was already typed. The entry is rebuilt on every render
|
|
29
|
-
// and a refused sign-in is a render, so the values come back in rather than
|
|
30
|
-
// being lost with the markup that held them.
|
|
31
|
-
export function credentialFields(state) {
|
|
32
|
-
return `<label for="email">Email</label><input id="email" type="email" value="${escape(state.email)}" placeholder="you@company.com" autocomplete="username"><div class="field-head"><label for="password">Password</label><a href="#/forgot">Forgot?</a></div><div class="password-field"><input id="password" type="password" value="${escape(state.password)}" placeholder="••••••••••" autocomplete="current-password"><button type="button" id="reveal" aria-controls="password">Show</button></div><button class="primary" type="submit" name="entry" value="credentials">Continue</button><button class="secondary" type="submit" name="signup" value="true">Create an account</button>`;
|
|
33
|
-
}
|
|
34
|
-
// The four account screens. My account is shown inside the app; the recovery
|
|
35
|
-
// and verification ones are reached without a session and stand alone.
|
|
36
|
-
export function accountForm(route, state) {
|
|
37
|
-
return route === "forgot"
|
|
38
|
-
? `<h2>Reset your password</h2><p>We’ll email you a link to choose a new password for your shared Fidj account.</p><form id="recovery"><label for="recovery-email">Email address</label><input id="recovery-email" type="email" autocomplete="email" required><button class="primary">Send reset link</button></form>`
|
|
39
|
-
: route === "reset"
|
|
40
|
-
? `<h2>Choose a new password</h2><p>This changes your Fidj password across all your apps and signs out existing sessions.</p>${state.linkToken ? '<form id="recovery"><label for="new-password">New password</label><input id="new-password" type="password" autocomplete="new-password" minlength="12" required><label for="confirm-password">Confirm password</label><input id="confirm-password" type="password" autocomplete="new-password" minlength="12" required><p>Use at least 12 characters (up to 72 UTF-8 bytes).</p><button class="primary">Save new password</button></form>' : '<p>Request a new link if you no longer have an active reset link.</p><a href="#/forgot">Request a reset link</a>'}`
|
|
41
|
-
: route === "verify"
|
|
42
|
-
? `<h2>${state.verificationConfirmed ? "Email verified" : "Verify your email"}</h2>${state.verificationConfirmed ? "<p>Your account is ready. Return to your app to continue.</p>" : "<p>Confirm that this email address belongs to you.</p>"}${state.verificationConfirmed ? "" : state.linkToken ? '<form id="recovery"><button class="primary">Confirm email address</button></form>' : "<p>Sign in to your account to request a new verification email.</p>"}`
|
|
43
|
-
: `<h2>My Fidj account</h2><p class="account-identity">Signed in as <strong>${escape(state.accountEmail)}</strong></p><p>Your identity is shared across your apps. Privacy choices remain separate for each app.</p><p id="verification-status">${state.emailVerified ? "Your email address is verified." : "Your email is not verified yet."}</p><button id="check-verification">Refresh verification status</button>${state.emailVerified ? "" : '<button id="resend-verification">Send verification email</button>'}<p><a href="#/forgot">Reset my password</a></p>`;
|
|
44
|
-
}
|
|
45
|
-
// A window that opened on its own, over the page somebody was on, owes them the
|
|
46
|
-
// way out before it asks for anything. Naming the app they came from is also the
|
|
47
|
-
// only thing on this screen that they can check against what they were doing a
|
|
48
|
-
// second ago — which is exactly what a page asking for a password should offer.
|
|
49
|
-
export function returnNotice(asking) {
|
|
50
|
-
return `<p class="signin-return" role="note">When you are done, this window closes and takes you back to ${escape(asking)}.</p>`;
|
|
51
|
-
}
|
|
52
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2NyZWVucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9zY3JlZW5zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLDBDQUEwQztBQUMxQyxFQUFFO0FBQ0YsNkVBQTZFO0FBQzdFLDJFQUEyRTtBQUMzRSxxRUFBcUU7QUFDckUsOEVBQThFO0FBQzlFLDZEQUE2RDtBQUU3RCxNQUFNLENBQUMsTUFBTSxNQUFNLEdBQUcsQ0FBQyxLQUFjLEVBQUUsRUFBRSxDQUN2QyxNQUFNLENBQUMsS0FBSyxJQUFJLEVBQUUsQ0FBQyxDQUFDLE9BQU8sQ0FDekIsVUFBVSxFQUNWLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FDUCxDQUFDLEVBQUUsR0FBRyxFQUFFLE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSxFQUFFLEdBQUcsRUFBRSxNQUFNLEVBQUUsR0FBRyxFQUFFLFFBQVEsRUFBRSxHQUFHLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FDdkUsSUFBSSxDQUNKLENBQ0wsQ0FBQztBQUVKLCtFQUErRTtBQUMvRSxzQ0FBc0M7QUFDdEMsTUFBTSxVQUFVLFFBQVEsQ0FBQyxJQUFZLEVBQUUsS0FBYTtJQUNsRCxPQUFPLDhEQUE4RCxNQUFNLENBQUMsSUFBSSxDQUFDLG9CQUFvQixNQUFNLENBQUMsS0FBSyxDQUFDLG9CQUFvQixDQUFDO0FBQ3pJLENBQUM7QUFFRCxNQUFNLFVBQVUsY0FBYyxDQUM1QixPQUFrRDtJQUVsRCxJQUFJLENBQUMsT0FBTyxFQUFFLE1BQU07UUFBRSxPQUFPLEVBQUUsQ0FBQztJQUNoQyxPQUFPLGtDQUFrQyxPQUFPO1NBQzdDLEdBQUcsQ0FDRixDQUFDLEtBQUssRUFBRSxLQUFLLEVBQUUsRUFBRSxDQUNmLCtCQUErQixNQUFNLENBQUMsS0FBSyxHQUFHLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLEVBQUUsR0FBRyxDQUFDLFdBQVcsTUFBTSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsV0FBVyxNQUFNLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxnQkFBZ0IsQ0FDako7U0FDQSxJQUFJLENBQUMsRUFBRSxDQUFDLFFBQVEsQ0FBQztBQUN0QixDQUFDO0FBRUQsTUFBTSxVQUFVLFVBQVUsQ0FBQyxPQUFrQjtJQUMzQyxJQUFJLENBQUMsT0FBTyxFQUFFLE1BQU07UUFBRSxPQUFPLEVBQUUsQ0FBQztJQUNoQyxPQUFPLGlDQUFpQyxPQUFPO1NBQzVDLEdBQUcsQ0FBQyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsU0FBUyxNQUFNLENBQUMsS0FBSyxDQUFDLFNBQVMsQ0FBQztTQUMvQyxJQUFJLENBQUMsRUFBRSxDQUFDLFdBQVcsQ0FBQztBQUN6QixDQUFDO0FBRUQsK0VBQStFO0FBQy9FLDRFQUE0RTtBQUM1RSw2Q0FBNkM7QUFDN0MsTUFBTSxVQUFVLGdCQUFnQixDQUFDLEtBQTBDO0lBQ3pFLE9BQU8seUVBQXlFLE1BQU0sQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLHFPQUFxTyxNQUFNLENBQUMsS0FBSyxDQUFDLFFBQVEsQ0FBQyxnVUFBZ1UsQ0FBQztBQUNqcUIsQ0FBQztBQVNELDZFQUE2RTtBQUM3RSx1RUFBdUU7QUFDdkUsTUFBTSxVQUFVLFdBQVcsQ0FBQyxLQUFhLEVBQUUsS0FBbUI7SUFDNUQsT0FBVSxLQUFLLEtBQUssUUFBUTtRQUN4QixDQUFDLENBQUMsb1RBQW9UO1FBQ3RULENBQUMsQ0FBQyxLQUFLLEtBQUssT0FBTztZQUNqQixDQUFDLENBQUMsNkhBQTZILEtBQUssQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLDBhQUEwYSxDQUFDLENBQUMsQ0FBQyxrSEFBa0gsRUFBRTtZQUNsckIsQ0FBQyxDQUFDLEtBQUssS0FBSyxRQUFRO2dCQUNsQixDQUFDLENBQUMsT0FBTyxLQUFLLENBQUMscUJBQXFCLENBQUMsQ0FBQyxDQUFDLGdCQUFnQixDQUFDLENBQUMsQ0FBQyxtQkFBbUIsUUFBUSxLQUFLLENBQUMscUJBQXFCLENBQUMsQ0FBQyxDQUFDLCtEQUErRCxDQUFDLENBQUMsQ0FBQyx3REFBd0QsR0FBRyxLQUFLLENBQUMscUJBQXFCLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsbUZBQW1GLENBQUMsQ0FBQyxDQUFDLHFFQUFxRSxFQUFFO2dCQUNsYyxDQUFDLENBQUMsNEVBQTRFLE1BQU0sQ0FBQyxLQUFLLENBQUMsWUFBWSxDQUFDLDBJQUEwSSxLQUFLLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQyxpQ0FBaUMsQ0FBQyxDQUFDLENBQUMsaUNBQWlDLDJFQUEyRSxLQUFLLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLG1FQUFtRSxpREFBaUQsQ0FBQztBQUNsakIsQ0FBQztBQUVELGdGQUFnRjtBQUNoRixpRkFBaUY7QUFDakYsK0VBQStFO0FBQy9FLGdGQUFnRjtBQUNoRixNQUFNLFVBQVUsWUFBWSxDQUFDLE1BQWM7SUFDekMsT0FBTyxvR0FBb0csTUFBTSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUM7QUFDbkksQ0FBQyJ9
|
package/service-agreement.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export declare function agreementMarkup(): string;
|
|
2
|
-
export declare function acceptedAgreement(form: HTMLFormElement): {
|
|
3
|
-
termsAccepted: boolean;
|
|
4
|
-
termsVersion: string;
|
|
5
|
-
} | null;
|
|
6
|
-
export declare function signInErrorMessage(error: unknown): "Too many attempts. Please wait before trying again." | "We could not sign in to this account. Check the email and password." | "An account already uses this email. Check the password or sign in instead." | "We cannot reach Fidj right now. Please try again." | "We could not sign in to this account. Please try again.";
|
|
7
|
-
export declare function bindAgreement(form: HTMLFormElement | null, title: string, endpoint: string, appId: string, checked?: boolean): Promise<void>;
|
|
8
|
-
export declare function signInHint(appId: string): string;
|
|
9
|
-
export declare function rememberSignIn(appId: string, label: string): void;
|
|
10
|
-
export declare function forgetSignIn(appId: string): void;
|
|
11
|
-
export type SigninShape = "button" | "inline" | "both";
|
|
12
|
-
export declare function providerEntry(title: string, appId: string, credentials: string, isFidjItself?: boolean, shape?: SigninShape): string;
|
|
13
|
-
export declare function showEmailEntry(open: boolean, focus?: boolean): void;
|
package/service-agreement.js
DELETED
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
export function agreementMarkup() {
|
|
2
|
-
return `<div class="signin-agreement"><label class="agreement-choice"><input id="service-agreement" type="checkbox" aria-required="true" disabled><span id="agreement-label">I accept the service agreement for this app.</span></label><button type="button" id="read-agreement" disabled>Read service agreement</button><p id="agreement-status" class="fineprint" role="status">Loading service agreement…</p><button type="button" id="retry-agreement" hidden>Retry</button></div><dialog id="agreement-dialog" aria-labelledby="agreement-heading"><h2 id="agreement-heading">Service agreement</h2><p id="agreement-version"></p><p id="agreement-text"></p><button type="button" id="close-agreement">Close agreement</button></dialog>`;
|
|
3
|
-
}
|
|
4
|
-
export function acceptedAgreement(form) {
|
|
5
|
-
const checkbox = form.querySelector("#service-agreement");
|
|
6
|
-
if (!checkbox?.checked || checkbox.disabled || !checkbox.dataset.version)
|
|
7
|
-
return null;
|
|
8
|
-
return { termsAccepted: true, termsVersion: checkbox.dataset.version };
|
|
9
|
-
}
|
|
10
|
-
export function signInErrorMessage(error) {
|
|
11
|
-
const detail = error;
|
|
12
|
-
const reason = typeof detail?.reason === "string" ? detail.reason : typeof detail?.message === "string" ? detail.message : "";
|
|
13
|
-
if (detail?.code === 429)
|
|
14
|
-
return "Too many attempts. Please wait before trying again.";
|
|
15
|
-
if (reason === "unknown-user")
|
|
16
|
-
return "We could not sign in to this account. Check the email and password.";
|
|
17
|
-
if (reason === "already exists - inconsistent request")
|
|
18
|
-
return "An account already uses this email. Check the password or sign in instead.";
|
|
19
|
-
if (/ECONNREFUSED|ENOTFOUND|ETIMEDOUT|ECONNRESET|EHOSTUNREACH|network/i.test(reason))
|
|
20
|
-
return "We cannot reach Fidj right now. Please try again.";
|
|
21
|
-
return "We could not sign in to this account. Please try again.";
|
|
22
|
-
}
|
|
23
|
-
export async function bindAgreement(form, title, endpoint, appId, checked = false) {
|
|
24
|
-
if (!form)
|
|
25
|
-
return;
|
|
26
|
-
// An app whose only door is Fidj carries no agreement block: Fidj asks for
|
|
27
|
-
// this app's agreement on the screen that names it, and records the version.
|
|
28
|
-
const checkbox = form.querySelector("#service-agreement");
|
|
29
|
-
if (!checkbox)
|
|
30
|
-
return;
|
|
31
|
-
const read = form.querySelector("#read-agreement");
|
|
32
|
-
const dialog = form.querySelector("#agreement-dialog");
|
|
33
|
-
const status = form.querySelector("#agreement-status");
|
|
34
|
-
const retry = form.querySelector("#retry-agreement");
|
|
35
|
-
// What this agreement gates is the app's own door, and only that one. The
|
|
36
|
-
// Fidj door is about to be asked the same question on the screen that names
|
|
37
|
-
// the app, where the answer is recorded with its version — so an agreement
|
|
38
|
-
// that is still loading, or that could not be loaded at all, must not be what
|
|
39
|
-
// stands between a person and the door that does not need it.
|
|
40
|
-
const gated = form.querySelector("#email-entry") || form;
|
|
41
|
-
const submitButtons = gated.querySelectorAll('button[type="submit"]');
|
|
42
|
-
const update = () => submitButtons.forEach(button => { button.disabled = checkbox.disabled; });
|
|
43
|
-
form.querySelector("#agreement-label").textContent = `I accept the service agreement for ${title}.`;
|
|
44
|
-
update();
|
|
45
|
-
checkbox.addEventListener("change", update);
|
|
46
|
-
read.addEventListener("click", () => dialog.showModal());
|
|
47
|
-
form.querySelector("#close-agreement").addEventListener("click", () => dialog.close());
|
|
48
|
-
const load = async () => {
|
|
49
|
-
retry.hidden = true;
|
|
50
|
-
status.textContent = "Loading service agreement…";
|
|
51
|
-
try {
|
|
52
|
-
const response = await fetch(`${endpoint}/apps/${encodeURIComponent(appId)}`, { signal: AbortSignal.timeout(10000) });
|
|
53
|
-
if (!response.ok)
|
|
54
|
-
throw new Error(response.status === 404 ? "missing" : "unreachable");
|
|
55
|
-
const agreement = (await response.json()).app?.agreement;
|
|
56
|
-
if (!agreement || typeof agreement.version !== "string" || !agreement.version || typeof agreement.text !== "string" || !agreement.text)
|
|
57
|
-
throw new Error("missing");
|
|
58
|
-
if (!form.isConnected)
|
|
59
|
-
return;
|
|
60
|
-
checkbox.dataset.version = agreement.version;
|
|
61
|
-
checkbox.disabled = false;
|
|
62
|
-
checkbox.checked = checked;
|
|
63
|
-
read.disabled = false;
|
|
64
|
-
dialog.querySelector("#agreement-version").textContent = `Version ${agreement.version}`;
|
|
65
|
-
dialog.querySelector("#agreement-text").textContent = agreement.text;
|
|
66
|
-
status.textContent = "Required to sign in. Optional data choices stay separate.";
|
|
67
|
-
update();
|
|
68
|
-
}
|
|
69
|
-
catch (error) {
|
|
70
|
-
if (!form.isConnected)
|
|
71
|
-
return;
|
|
72
|
-
status.textContent = error instanceof Error && error.message === "missing"
|
|
73
|
-
? "This app has no service agreement available."
|
|
74
|
-
: "We cannot reach Fidj right now. Try again.";
|
|
75
|
-
retry.hidden = false;
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
retry.addEventListener("click", load);
|
|
79
|
-
await load();
|
|
80
|
-
}
|
|
81
|
-
// The entry every app that delegates to the provider renders, in one place
|
|
82
|
-
// because both app shapes render it and they had drifted apart.
|
|
83
|
-
//
|
|
84
|
-
// "Continue with Fidj" alone borrows the grammar of an optional social login —
|
|
85
|
-
// that button always sits next to an email and a password — so on an app whose
|
|
86
|
-
// accounts *are* Fidj accounts, the missing form reads as something broken. The
|
|
87
|
-
// explanation therefore comes before the button, not as reassurance after it,
|
|
88
|
-
// and nothing presupposes an account the person may not have yet.
|
|
89
|
-
const hintKey = (appId) => "fidj.entry." + appId;
|
|
90
|
-
export function signInHint(appId) {
|
|
91
|
-
try {
|
|
92
|
-
return localStorage.getItem(hintKey(appId)) || "";
|
|
93
|
-
}
|
|
94
|
-
catch {
|
|
95
|
-
return "";
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
// Remembered on this app's own origin, about this app's own member: no
|
|
99
|
-
// cross-site question is asked, and none is answered. Signing out forgets, so a
|
|
100
|
-
// shared browser does not show the next person an address.
|
|
101
|
-
export function rememberSignIn(appId, label) {
|
|
102
|
-
try {
|
|
103
|
-
if (label)
|
|
104
|
-
localStorage.setItem(hintKey(appId), label);
|
|
105
|
-
}
|
|
106
|
-
catch { }
|
|
107
|
-
}
|
|
108
|
-
export function forgetSignIn(appId) {
|
|
109
|
-
try {
|
|
110
|
-
localStorage.removeItem(hintKey(appId));
|
|
111
|
-
}
|
|
112
|
-
catch { }
|
|
113
|
-
}
|
|
114
|
-
export function providerEntry(title, appId, credentials, isFidjItself = false, shape = "button") {
|
|
115
|
-
const escapeText = (value) => String(value ?? "").replace(/[&<>"']/g, (char) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[char]);
|
|
116
|
-
const hint = signInHint(appId);
|
|
117
|
-
const both = shape === "both" && Boolean(credentials);
|
|
118
|
-
// The app's own form and nothing else. Nothing is folded away because there is
|
|
119
|
-
// no second way in to fold it under, and the trade-off is stated plainly:
|
|
120
|
-
// on this path it is this site that holds the password.
|
|
121
|
-
if (shape === "inline")
|
|
122
|
-
return (`<p class="signin-lead">${isFidjItself
|
|
123
|
-
? "One account across every app that uses Fidj, and a separate set of choices for each one."
|
|
124
|
-
: `${escapeText(title)} accounts are Fidj accounts. Sign in below — ${escapeText(title)} handles your password itself on this page.`}</p>` +
|
|
125
|
-
agreementMarkup() +
|
|
126
|
-
credentials);
|
|
127
|
-
const lead = hint
|
|
128
|
-
? `<p class="signin-lead">You signed in here with Fidj before. ${escapeText(title)} accounts are Fidj accounts — continue as yourself, or use another.</p>`
|
|
129
|
-
: both
|
|
130
|
-
? `<p class="signin-lead">${escapeText(title)} accounts are Fidj accounts. Fidj asks in a window of its own, so this site never sees your password.</p>`
|
|
131
|
-
: isFidjItself
|
|
132
|
-
? `<p class="signin-lead">One account across every app that uses Fidj, and a separate set of choices for each one. Fidj asks in a window of its own; this page stays where it is.</p>`
|
|
133
|
-
: `<p class="signin-lead">${escapeText(title)} accounts are Fidj accounts. You will sign in — or create yours — in a Fidj window, so this site never sees your password.</p>`;
|
|
134
|
-
// The accent is the Fidj mark, and this is the one control on the screen that
|
|
135
|
-
// is Fidj's rather than the app's. It is also the door the app would rather
|
|
136
|
-
// people took, and those two happen to want the same thing.
|
|
137
|
-
const fidj = hint
|
|
138
|
-
? `<button class="primary fidj-entry" type="submit" name="entry" value="fidj">Continue as ${escapeText(hint)}</button><button type="button" id="forget-hint" class="quiet">Use a different account</button>`
|
|
139
|
-
: `<button class="primary fidj-entry" type="submit" name="entry" value="fidj">${isFidjItself ? "Sign in" : "Sign in with Fidj"}</button>`;
|
|
140
|
-
// Fidj is the only door: it collects the agreement itself, a moment later, on
|
|
141
|
-
// the screen that names the app — and records it with its version. Collecting
|
|
142
|
-
// it here first recorded nothing and asked the same question twice.
|
|
143
|
-
if (shape !== "both" || !both)
|
|
144
|
-
return lead + fidj;
|
|
145
|
-
// Both doors. The app's own one is a second thought for the person who wants
|
|
146
|
-
// it, which is what a disclosure is for: it costs one click and takes nothing
|
|
147
|
-
// away, where a second button of equal weight cost everybody a decision.
|
|
148
|
-
return (lead +
|
|
149
|
-
fidj +
|
|
150
|
-
`<div class="signin-alternate"><button type="button" id="use-email" class="signin-toggle" aria-expanded="false" aria-controls="email-entry"><span class="signin-toggle-label">Inline form<span class="caret" aria-hidden="true"></span></span></button>
|
|
151
|
-
<div id="email-entry" hidden>${agreementMarkup()}${credentials}</div></div>`);
|
|
152
|
-
}
|
|
153
|
-
// Opening the app's own form takes the space the Fidj door was using.
|
|
154
|
-
//
|
|
155
|
-
// The two are alternatives, not a list, and watching one fold away as the other
|
|
156
|
-
// arrives is what says so — where a form appearing underneath a button that is
|
|
157
|
-
// still there reads as "and also". The toggle stays put through both states, so
|
|
158
|
-
// it ends up labelling whichever one is on screen: a caret pointing down at a
|
|
159
|
-
// form that is not here yet, and up at the one it would put away.
|
|
160
|
-
//
|
|
161
|
-
// Shared, because both app shapes render the same entry.
|
|
162
|
-
export function showEmailEntry(open, focus = false) {
|
|
163
|
-
const fields = document.getElementById("email-entry");
|
|
164
|
-
const toggle = document.getElementById("use-email");
|
|
165
|
-
if (!fields || !toggle)
|
|
166
|
-
return;
|
|
167
|
-
toggle.setAttribute("aria-expanded", String(open));
|
|
168
|
-
fields.hidden = !open;
|
|
169
|
-
const door = document.querySelector(".fidj-entry");
|
|
170
|
-
if (door) {
|
|
171
|
-
door.classList.toggle("is-folded", open);
|
|
172
|
-
// Out of the tab order and out of the accessibility tree the moment it
|
|
173
|
-
// starts leaving: something mid-fold is not something to press, and an
|
|
174
|
-
// animation is not what decides that.
|
|
175
|
-
if (open)
|
|
176
|
-
door.setAttribute("aria-hidden", "true");
|
|
177
|
-
else
|
|
178
|
-
door.removeAttribute("aria-hidden");
|
|
179
|
-
door.tabIndex = open ? -1 : 0;
|
|
180
|
-
}
|
|
181
|
-
if (open && focus)
|
|
182
|
-
document.getElementById("email")?.focus();
|
|
183
|
-
}
|
|
184
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmljZS1hZ3JlZW1lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvc2VydmljZS1hZ3JlZW1lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsTUFBTSxVQUFVLGVBQWU7SUFDN0IsT0FBTyx5c0JBQXlzQixDQUFDO0FBQ250QixDQUFDO0FBRUQsTUFBTSxVQUFVLGlCQUFpQixDQUFDLElBQXFCO0lBQ3JELE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxhQUFhLENBQW1CLG9CQUFvQixDQUFDLENBQUM7SUFDNUUsSUFBSSxDQUFDLFFBQVEsRUFBRSxPQUFPLElBQUksUUFBUSxDQUFDLFFBQVEsSUFBSSxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsT0FBTztRQUFFLE9BQU8sSUFBSSxDQUFDO0lBQ3RGLE9BQU8sRUFBRSxhQUFhLEVBQUUsSUFBSSxFQUFFLFlBQVksRUFBRSxRQUFRLENBQUMsT0FBTyxDQUFDLE9BQU8sRUFBRSxDQUFDO0FBQ3pFLENBQUM7QUFFRCxNQUFNLFVBQVUsa0JBQWtCLENBQUMsS0FBYztJQUMvQyxNQUFNLE1BQU0sR0FBRyxLQUE2RCxDQUFDO0lBQzdFLE1BQU0sTUFBTSxHQUFHLE9BQU8sTUFBTSxFQUFFLE1BQU0sS0FBSyxRQUFRLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLE9BQU8sTUFBTSxFQUFFLE9BQU8sS0FBSyxRQUFRLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztJQUM5SCxJQUFJLE1BQU0sRUFBRSxJQUFJLEtBQUssR0FBRztRQUFFLE9BQU8scURBQXFELENBQUM7SUFDdkYsSUFBSSxNQUFNLEtBQUssY0FBYztRQUFFLE9BQU8scUVBQXFFLENBQUM7SUFDNUcsSUFBSSxNQUFNLEtBQUssdUNBQXVDO1FBQUUsT0FBTyw0RUFBNEUsQ0FBQztJQUM1SSxJQUFJLG1FQUFtRSxDQUFDLElBQUks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
|
package/version.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function showVersionBadge(version: string, apiEndpoint?: string): void;
|
package/version.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
export function showVersionBadge(version, apiEndpoint) {
|
|
2
|
-
if (!/^\d+\.\d+\.\d+(-[\w.]+)?$/.test(version || ""))
|
|
3
|
-
return;
|
|
4
|
-
const badge = document.createElement("div");
|
|
5
|
-
badge.className = "fidj-version";
|
|
6
|
-
badge.setAttribute("aria-label", `Fidj version ${version}`);
|
|
7
|
-
badge.textContent = `fidj@${version}`;
|
|
8
|
-
document.body.append(badge);
|
|
9
|
-
if (apiEndpoint) {
|
|
10
|
-
void fetch(`${apiEndpoint.replace(/\/$/, "")}/status`)
|
|
11
|
-
.then(response => response.ok ? response.json() : null)
|
|
12
|
-
.then(status => {
|
|
13
|
-
const apiVersion = status?.version || status?.built;
|
|
14
|
-
if (!apiVersion)
|
|
15
|
-
return;
|
|
16
|
-
badge.textContent = `fidj@${version} · API ${apiVersion}`;
|
|
17
|
-
badge.setAttribute("aria-label", `Fidj version ${version}, API version ${apiVersion}`);
|
|
18
|
-
})
|
|
19
|
-
.catch(() => undefined);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmVyc2lvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy92ZXJzaW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE1BQU0sVUFBVSxnQkFBZ0IsQ0FBQyxPQUFlLEVBQUUsV0FBb0I7SUFDcEUsSUFBSSxDQUFDLDJCQUEyQixDQUFDLElBQUksQ0FBQyxPQUFPLElBQUksRUFBRSxDQUFDO1FBQUUsT0FBTztJQUM3RCxNQUFNLEtBQUssR0FBRyxRQUFRLENBQUMsYUFBYSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQzVDLEtBQUssQ0FBQyxTQUFTLEdBQUcsY0FBYyxDQUFDO0lBQ2pDLEtBQUssQ0FBQyxZQUFZLENBQUMsWUFBWSxFQUFFLGdCQUFnQixPQUFPLEVBQUUsQ0FBQyxDQUFDO0lBQzVELEtBQUssQ0FBQyxXQUFXLEdBQUcsUUFBUSxPQUFPLEVBQUUsQ0FBQztJQUN0QyxRQUFRLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUM1QixJQUFJLFdBQVcsRUFBRSxDQUFDO1FBQ2hCLEtBQUssS0FBSyxDQUFDLEdBQUcsV0FBVyxDQUFDLE9BQU8sQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLFNBQVMsQ0FBQzthQUNuRCxJQUFJLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQzthQUN0RCxJQUFJLENBQUMsTUFBTSxDQUFDLEVBQUU7WUFDYixNQUFNLFVBQVUsR0FBRyxNQUFNLEVBQUUsT0FBTyxJQUFJLE1BQU0sRUFBRSxLQUFLLENBQUM7WUFDcEQsSUFBSSxDQUFDLFVBQVU7Z0JBQUUsT0FBTztZQUN4QixLQUFLLENBQUMsV0FBVyxHQUFHLFFBQVEsT0FBTyxVQUFVLFVBQVUsRUFBRSxDQUFDO1lBQzFELEtBQUssQ0FBQyxZQUFZLENBQUMsWUFBWSxFQUFFLGdCQUFnQixPQUFPLGlCQUFpQixVQUFVLEVBQUUsQ0FBQyxDQUFDO1FBQ3pGLENBQUMsQ0FBQzthQUNELEtBQUssQ0FBQyxHQUFHLEVBQUUsQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUM1QixDQUFDO0FBQ0gsQ0FBQyJ9
|