@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/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: 0;
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) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[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
@@ -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;
@@ -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) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[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