@scalemule/news-publication 0.0.76 → 0.0.78
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/meet-view.d.cts +1 -1
- package/dist/meet-view.d.ts +1 -1
- package/dist/reader-sso-client.cjs +107 -31
- package/dist/reader-sso-client.d.cts +5 -2
- package/dist/reader-sso-client.d.ts +5 -2
- package/dist/reader-sso-client.js +105 -32
- package/dist/reader-sso.cjs +4 -4
- package/dist/reader-sso.js +4 -4
- package/package.json +4 -2
package/dist/meet-view.d.cts
CHANGED
|
@@ -18,7 +18,7 @@ declare function MeetupDetail({ meetup, joined }: {
|
|
|
18
18
|
joined: boolean;
|
|
19
19
|
}): React.JSX.Element;
|
|
20
20
|
declare function activityChoices(): {
|
|
21
|
-
value: "
|
|
21
|
+
value: "walk" | "other" | "games" | "coffee" | "hike" | "pickleball" | "tennis" | "soccer" | "bike" | "dogs" | "books" | "kids" | "volunteer";
|
|
22
22
|
label: string;
|
|
23
23
|
}[];
|
|
24
24
|
|
package/dist/meet-view.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ declare function MeetupDetail({ meetup, joined }: {
|
|
|
18
18
|
joined: boolean;
|
|
19
19
|
}): React.JSX.Element;
|
|
20
20
|
declare function activityChoices(): {
|
|
21
|
-
value: "
|
|
21
|
+
value: "walk" | "other" | "games" | "coffee" | "hike" | "pickleball" | "tennis" | "soccer" | "bike" | "dogs" | "books" | "kids" | "volunteer";
|
|
22
22
|
label: string;
|
|
23
23
|
}[];
|
|
24
24
|
|
|
@@ -4,6 +4,46 @@
|
|
|
4
4
|
var react = require('react');
|
|
5
5
|
var jsxRuntime = require('react/jsx-runtime');
|
|
6
6
|
|
|
7
|
+
// src/reader-sso-scheduler.ts
|
|
8
|
+
function mountReaderSsoScheduler({ document: doc, timers, eligible, attempt, idleDelayMs }) {
|
|
9
|
+
let active = true;
|
|
10
|
+
let attempted = false;
|
|
11
|
+
let idle;
|
|
12
|
+
let delay;
|
|
13
|
+
const cancelPending = () => {
|
|
14
|
+
if (idle !== void 0) timers.cancelIdleCallback?.(idle);
|
|
15
|
+
if (delay !== void 0) timers.clearTimeout(delay);
|
|
16
|
+
idle = delay = void 0;
|
|
17
|
+
};
|
|
18
|
+
const run = () => {
|
|
19
|
+
delay = void 0;
|
|
20
|
+
if (!active || attempted || !eligible()) return;
|
|
21
|
+
attempted = true;
|
|
22
|
+
attempt();
|
|
23
|
+
};
|
|
24
|
+
const defer = () => {
|
|
25
|
+
idle = void 0;
|
|
26
|
+
if (active && !attempted) delay = timers.setTimeout(run, idleDelayMs);
|
|
27
|
+
};
|
|
28
|
+
const schedule = () => {
|
|
29
|
+
if (!active || attempted) return;
|
|
30
|
+
cancelPending();
|
|
31
|
+
if (doc.readyState === "loading" || !eligible()) return;
|
|
32
|
+
if (timers.requestIdleCallback) idle = timers.requestIdleCallback(defer, { timeout: 4e3 });
|
|
33
|
+
else defer();
|
|
34
|
+
};
|
|
35
|
+
doc.addEventListener("DOMContentLoaded", schedule);
|
|
36
|
+
doc.addEventListener("visibilitychange", schedule);
|
|
37
|
+
doc.addEventListener("prerenderingchange", schedule);
|
|
38
|
+
schedule();
|
|
39
|
+
return () => {
|
|
40
|
+
active = false;
|
|
41
|
+
cancelPending();
|
|
42
|
+
doc.removeEventListener("DOMContentLoaded", schedule);
|
|
43
|
+
doc.removeEventListener("visibilitychange", schedule);
|
|
44
|
+
doc.removeEventListener("prerenderingchange", schedule);
|
|
45
|
+
};
|
|
46
|
+
}
|
|
7
47
|
var READER_SSO_BOT_PATTERN = /bot|crawl|spider|headless|lighthouse|pagespeed|preview|slurp|facebookexternalhit|embedly|python|curl|wget|phantom|puppeteer|playwright|selenium|prerender/i;
|
|
8
48
|
var READER_SSO_PRIVATE_PATHS = [
|
|
9
49
|
/^\/account(\/|$)/,
|
|
@@ -95,30 +135,18 @@ function ReaderSsoBoot({ enabled = true, hasSession: hasSessionCookie, suppresse
|
|
|
95
135
|
}, [enabled, hasSessionCookie, staleCookie, sessionPath]);
|
|
96
136
|
const hasSession = hasSessionCookie && !staleCookie;
|
|
97
137
|
react.useEffect(() => {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if (typeof w.requestIdleCallback === "function") idle = w.requestIdleCallback(() => {
|
|
111
|
-
idle = window.setTimeout(go, idleDelayMs);
|
|
112
|
-
}, { timeout: 4e3 });
|
|
113
|
-
else idle = window.setTimeout(go, idleDelayMs);
|
|
114
|
-
};
|
|
115
|
-
if (document.readyState === "complete") whenIdle();
|
|
116
|
-
else window.addEventListener("load", whenIdle, { once: true });
|
|
117
|
-
return () => {
|
|
118
|
-
cancelled = true;
|
|
119
|
-
window.removeEventListener("load", whenIdle);
|
|
120
|
-
if (idle !== void 0) window.clearTimeout(idle);
|
|
121
|
-
};
|
|
138
|
+
const flags = { enabled, hasSession, suppressed, privatePaths };
|
|
139
|
+
return mountReaderSsoScheduler({
|
|
140
|
+
document,
|
|
141
|
+
timers: window,
|
|
142
|
+
idleDelayMs,
|
|
143
|
+
eligible: () => silentSignInDecision(browserEnvironment(flags)).attempt === true,
|
|
144
|
+
attempt: () => {
|
|
145
|
+
const env = browserEnvironment(flags);
|
|
146
|
+
writeAttempt(env.now);
|
|
147
|
+
window.location.assign(silentSignInUrl(startPath, env.pathname, env.search));
|
|
148
|
+
}
|
|
149
|
+
});
|
|
122
150
|
}, [enabled, hasSession, suppressed, startPath, privatePaths, idleDelayMs]);
|
|
123
151
|
return null;
|
|
124
152
|
}
|
|
@@ -150,19 +178,64 @@ function greetingText(profile, now = /* @__PURE__ */ new Date(), fallback = "Wel
|
|
|
150
178
|
if (!name) return fallback;
|
|
151
179
|
return `${greetingWord(localHour(now, profile.time_zone, profile.locale))}, ${name}`;
|
|
152
180
|
}
|
|
181
|
+
var READER_GREETING_POLL_MS = 3e4;
|
|
182
|
+
var READER_GREETING_TIMEOUT_MS = 8e3;
|
|
183
|
+
var READER_IDENTITY_CHANGED_EVENT = "scalemule:reader-identity-changed";
|
|
153
184
|
function ReaderGreeting({ endpoint = "/api/news/session", className, fallback = "Welcome back", as: Tag = "span" }) {
|
|
154
185
|
const [text, setText] = react.useState(null);
|
|
155
186
|
react.useEffect(() => {
|
|
156
187
|
let alive = true;
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
188
|
+
let generation = 0;
|
|
189
|
+
let authenticated = false;
|
|
190
|
+
let controller;
|
|
191
|
+
let deadline;
|
|
192
|
+
const refresh = (clear = false) => {
|
|
193
|
+
if (!alive) return;
|
|
194
|
+
if (clear) {
|
|
195
|
+
authenticated = false;
|
|
196
|
+
setText(null);
|
|
197
|
+
}
|
|
198
|
+
const current = ++generation;
|
|
199
|
+
controller?.abort();
|
|
200
|
+
if (deadline !== void 0) window.clearTimeout(deadline);
|
|
201
|
+
if (document.visibilityState === "hidden") return;
|
|
202
|
+
const request = new AbortController();
|
|
203
|
+
controller = request;
|
|
204
|
+
const timeout = window.setTimeout(() => {
|
|
205
|
+
if (!alive || current !== generation) return;
|
|
206
|
+
generation++;
|
|
207
|
+
authenticated = false;
|
|
208
|
+
setText(null);
|
|
209
|
+
request.abort();
|
|
210
|
+
}, READER_GREETING_TIMEOUT_MS);
|
|
211
|
+
deadline = timeout;
|
|
212
|
+
fetch(endpoint, { credentials: "same-origin", cache: "no-store", signal: request.signal }).then((response) => response.ok ? response.json() : null).then((summary) => {
|
|
213
|
+
if (!alive || current !== generation) return;
|
|
214
|
+
authenticated = summary?.authenticated === true;
|
|
215
|
+
setText(authenticated && summary ? greetingText(summary, /* @__PURE__ */ new Date(), fallback) : null);
|
|
216
|
+
}).catch(() => {
|
|
217
|
+
if (!alive || current !== generation) return;
|
|
218
|
+
authenticated = false;
|
|
219
|
+
setText(null);
|
|
220
|
+
}).finally(() => window.clearTimeout(timeout));
|
|
221
|
+
};
|
|
222
|
+
const recheck = () => refresh(true);
|
|
223
|
+
const timer = window.setInterval(() => {
|
|
224
|
+
if (authenticated) refresh();
|
|
225
|
+
}, READER_GREETING_POLL_MS);
|
|
226
|
+
window.addEventListener("focus", recheck);
|
|
227
|
+
document.addEventListener("visibilitychange", recheck);
|
|
228
|
+
document.addEventListener(READER_IDENTITY_CHANGED_EVENT, recheck);
|
|
229
|
+
refresh(true);
|
|
163
230
|
return () => {
|
|
164
231
|
alive = false;
|
|
165
|
-
|
|
232
|
+
generation++;
|
|
233
|
+
controller?.abort();
|
|
234
|
+
if (deadline !== void 0) window.clearTimeout(deadline);
|
|
235
|
+
window.clearInterval(timer);
|
|
236
|
+
window.removeEventListener("focus", recheck);
|
|
237
|
+
document.removeEventListener("visibilitychange", recheck);
|
|
238
|
+
document.removeEventListener(READER_IDENTITY_CHANGED_EVENT, recheck);
|
|
166
239
|
};
|
|
167
240
|
}, [endpoint, fallback]);
|
|
168
241
|
if (text === null) return null;
|
|
@@ -350,6 +423,9 @@ exports.AccountLinkForm = AccountLinkForm;
|
|
|
350
423
|
exports.AccountSwitchPrompt = AccountSwitchPrompt;
|
|
351
424
|
exports.LEDVERY_BUTTON_LABELS = LEDVERY_BUTTON_LABELS;
|
|
352
425
|
exports.LedveryButton = LedveryButton;
|
|
426
|
+
exports.READER_GREETING_POLL_MS = READER_GREETING_POLL_MS;
|
|
427
|
+
exports.READER_GREETING_TIMEOUT_MS = READER_GREETING_TIMEOUT_MS;
|
|
428
|
+
exports.READER_IDENTITY_CHANGED_EVENT = READER_IDENTITY_CHANGED_EVENT;
|
|
353
429
|
exports.READER_SSO_ATTEMPT_WINDOW_MS = READER_SSO_ATTEMPT_WINDOW_MS;
|
|
354
430
|
exports.READER_SSO_BOT_PATTERN = READER_SSO_BOT_PATTERN;
|
|
355
431
|
exports.READER_SSO_PRIVATE_PATHS = READER_SSO_PRIVATE_PATHS;
|
|
@@ -32,7 +32,7 @@ declare function silentSignInDecision(env: SilentSignInEnvironment): SilentSignI
|
|
|
32
32
|
declare function silentSignInUrl(startPath: string, pathname: string, search: string): string;
|
|
33
33
|
declare function isFormField(element: Element | null): boolean;
|
|
34
34
|
/**
|
|
35
|
-
* Renders nothing. Once per page load, after
|
|
35
|
+
* Renders nothing. Once per page load, after the DOM is usable and an idle period, performs a
|
|
36
36
|
* top-level navigation to the silent sign-in start route when nothing argues
|
|
37
37
|
* against it. The server sets the negative-cache cookie on every silent negative,
|
|
38
38
|
* so this can never loop.
|
|
@@ -64,6 +64,9 @@ type SessionSummaryResponse = {
|
|
|
64
64
|
locale?: string;
|
|
65
65
|
time_zone?: string;
|
|
66
66
|
};
|
|
67
|
+
declare const READER_GREETING_POLL_MS = 30000;
|
|
68
|
+
declare const READER_GREETING_TIMEOUT_MS = 8000;
|
|
69
|
+
declare const READER_IDENTITY_CHANGED_EVENT = "scalemule:reader-identity-changed";
|
|
67
70
|
declare function ReaderGreeting({ endpoint, className, fallback, as: Tag }: {
|
|
68
71
|
endpoint?: string;
|
|
69
72
|
className?: string;
|
|
@@ -128,4 +131,4 @@ declare function AccountSwitchPrompt({ currentName, pendingName, endpoint, class
|
|
|
128
131
|
onDone?: (result: SwitchResult) => void;
|
|
129
132
|
}): React.JSX.Element;
|
|
130
133
|
|
|
131
|
-
export { AccountLinkForm, AccountSwitchPrompt, type GreetingProfile, LEDVERY_BUTTON_LABELS, LedveryButton, type LinkResult, READER_SSO_ATTEMPT_WINDOW_MS, READER_SSO_BOT_PATTERN, READER_SSO_PRIVATE_PATHS, ReaderGreeting, ReaderSsoBoot, type ReaderSsoClassNames, type ReaderSsoProvider, type SessionSummaryResponse, type SilentSignInDecision, type SilentSignInEnvironment, type SwitchChoice, type SwitchResult, firstName, greetingText, greetingWord, isFormField, localHour, silentSignInDecision, silentSignInUrl };
|
|
134
|
+
export { AccountLinkForm, AccountSwitchPrompt, type GreetingProfile, LEDVERY_BUTTON_LABELS, LedveryButton, type LinkResult, READER_GREETING_POLL_MS, READER_GREETING_TIMEOUT_MS, READER_IDENTITY_CHANGED_EVENT, READER_SSO_ATTEMPT_WINDOW_MS, READER_SSO_BOT_PATTERN, READER_SSO_PRIVATE_PATHS, ReaderGreeting, ReaderSsoBoot, type ReaderSsoClassNames, type ReaderSsoProvider, type SessionSummaryResponse, type SilentSignInDecision, type SilentSignInEnvironment, type SwitchChoice, type SwitchResult, firstName, greetingText, greetingWord, isFormField, localHour, silentSignInDecision, silentSignInUrl };
|
|
@@ -32,7 +32,7 @@ declare function silentSignInDecision(env: SilentSignInEnvironment): SilentSignI
|
|
|
32
32
|
declare function silentSignInUrl(startPath: string, pathname: string, search: string): string;
|
|
33
33
|
declare function isFormField(element: Element | null): boolean;
|
|
34
34
|
/**
|
|
35
|
-
* Renders nothing. Once per page load, after
|
|
35
|
+
* Renders nothing. Once per page load, after the DOM is usable and an idle period, performs a
|
|
36
36
|
* top-level navigation to the silent sign-in start route when nothing argues
|
|
37
37
|
* against it. The server sets the negative-cache cookie on every silent negative,
|
|
38
38
|
* so this can never loop.
|
|
@@ -64,6 +64,9 @@ type SessionSummaryResponse = {
|
|
|
64
64
|
locale?: string;
|
|
65
65
|
time_zone?: string;
|
|
66
66
|
};
|
|
67
|
+
declare const READER_GREETING_POLL_MS = 30000;
|
|
68
|
+
declare const READER_GREETING_TIMEOUT_MS = 8000;
|
|
69
|
+
declare const READER_IDENTITY_CHANGED_EVENT = "scalemule:reader-identity-changed";
|
|
67
70
|
declare function ReaderGreeting({ endpoint, className, fallback, as: Tag }: {
|
|
68
71
|
endpoint?: string;
|
|
69
72
|
className?: string;
|
|
@@ -128,4 +131,4 @@ declare function AccountSwitchPrompt({ currentName, pendingName, endpoint, class
|
|
|
128
131
|
onDone?: (result: SwitchResult) => void;
|
|
129
132
|
}): React.JSX.Element;
|
|
130
133
|
|
|
131
|
-
export { AccountLinkForm, AccountSwitchPrompt, type GreetingProfile, LEDVERY_BUTTON_LABELS, LedveryButton, type LinkResult, READER_SSO_ATTEMPT_WINDOW_MS, READER_SSO_BOT_PATTERN, READER_SSO_PRIVATE_PATHS, ReaderGreeting, ReaderSsoBoot, type ReaderSsoClassNames, type ReaderSsoProvider, type SessionSummaryResponse, type SilentSignInDecision, type SilentSignInEnvironment, type SwitchChoice, type SwitchResult, firstName, greetingText, greetingWord, isFormField, localHour, silentSignInDecision, silentSignInUrl };
|
|
134
|
+
export { AccountLinkForm, AccountSwitchPrompt, type GreetingProfile, LEDVERY_BUTTON_LABELS, LedveryButton, type LinkResult, READER_GREETING_POLL_MS, READER_GREETING_TIMEOUT_MS, READER_IDENTITY_CHANGED_EVENT, READER_SSO_ATTEMPT_WINDOW_MS, READER_SSO_BOT_PATTERN, READER_SSO_PRIVATE_PATHS, ReaderGreeting, ReaderSsoBoot, type ReaderSsoClassNames, type ReaderSsoProvider, type SessionSummaryResponse, type SilentSignInDecision, type SilentSignInEnvironment, type SwitchChoice, type SwitchResult, firstName, greetingText, greetingWord, isFormField, localHour, silentSignInDecision, silentSignInUrl };
|
|
@@ -2,6 +2,46 @@
|
|
|
2
2
|
import { useState, useEffect, useId } from 'react';
|
|
3
3
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
4
4
|
|
|
5
|
+
// src/reader-sso-scheduler.ts
|
|
6
|
+
function mountReaderSsoScheduler({ document: doc, timers, eligible, attempt, idleDelayMs }) {
|
|
7
|
+
let active = true;
|
|
8
|
+
let attempted = false;
|
|
9
|
+
let idle;
|
|
10
|
+
let delay;
|
|
11
|
+
const cancelPending = () => {
|
|
12
|
+
if (idle !== void 0) timers.cancelIdleCallback?.(idle);
|
|
13
|
+
if (delay !== void 0) timers.clearTimeout(delay);
|
|
14
|
+
idle = delay = void 0;
|
|
15
|
+
};
|
|
16
|
+
const run = () => {
|
|
17
|
+
delay = void 0;
|
|
18
|
+
if (!active || attempted || !eligible()) return;
|
|
19
|
+
attempted = true;
|
|
20
|
+
attempt();
|
|
21
|
+
};
|
|
22
|
+
const defer = () => {
|
|
23
|
+
idle = void 0;
|
|
24
|
+
if (active && !attempted) delay = timers.setTimeout(run, idleDelayMs);
|
|
25
|
+
};
|
|
26
|
+
const schedule = () => {
|
|
27
|
+
if (!active || attempted) return;
|
|
28
|
+
cancelPending();
|
|
29
|
+
if (doc.readyState === "loading" || !eligible()) return;
|
|
30
|
+
if (timers.requestIdleCallback) idle = timers.requestIdleCallback(defer, { timeout: 4e3 });
|
|
31
|
+
else defer();
|
|
32
|
+
};
|
|
33
|
+
doc.addEventListener("DOMContentLoaded", schedule);
|
|
34
|
+
doc.addEventListener("visibilitychange", schedule);
|
|
35
|
+
doc.addEventListener("prerenderingchange", schedule);
|
|
36
|
+
schedule();
|
|
37
|
+
return () => {
|
|
38
|
+
active = false;
|
|
39
|
+
cancelPending();
|
|
40
|
+
doc.removeEventListener("DOMContentLoaded", schedule);
|
|
41
|
+
doc.removeEventListener("visibilitychange", schedule);
|
|
42
|
+
doc.removeEventListener("prerenderingchange", schedule);
|
|
43
|
+
};
|
|
44
|
+
}
|
|
5
45
|
var READER_SSO_BOT_PATTERN = /bot|crawl|spider|headless|lighthouse|pagespeed|preview|slurp|facebookexternalhit|embedly|python|curl|wget|phantom|puppeteer|playwright|selenium|prerender/i;
|
|
6
46
|
var READER_SSO_PRIVATE_PATHS = [
|
|
7
47
|
/^\/account(\/|$)/,
|
|
@@ -93,30 +133,18 @@ function ReaderSsoBoot({ enabled = true, hasSession: hasSessionCookie, suppresse
|
|
|
93
133
|
}, [enabled, hasSessionCookie, staleCookie, sessionPath]);
|
|
94
134
|
const hasSession = hasSessionCookie && !staleCookie;
|
|
95
135
|
useEffect(() => {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (typeof w.requestIdleCallback === "function") idle = w.requestIdleCallback(() => {
|
|
109
|
-
idle = window.setTimeout(go, idleDelayMs);
|
|
110
|
-
}, { timeout: 4e3 });
|
|
111
|
-
else idle = window.setTimeout(go, idleDelayMs);
|
|
112
|
-
};
|
|
113
|
-
if (document.readyState === "complete") whenIdle();
|
|
114
|
-
else window.addEventListener("load", whenIdle, { once: true });
|
|
115
|
-
return () => {
|
|
116
|
-
cancelled = true;
|
|
117
|
-
window.removeEventListener("load", whenIdle);
|
|
118
|
-
if (idle !== void 0) window.clearTimeout(idle);
|
|
119
|
-
};
|
|
136
|
+
const flags = { enabled, hasSession, suppressed, privatePaths };
|
|
137
|
+
return mountReaderSsoScheduler({
|
|
138
|
+
document,
|
|
139
|
+
timers: window,
|
|
140
|
+
idleDelayMs,
|
|
141
|
+
eligible: () => silentSignInDecision(browserEnvironment(flags)).attempt === true,
|
|
142
|
+
attempt: () => {
|
|
143
|
+
const env = browserEnvironment(flags);
|
|
144
|
+
writeAttempt(env.now);
|
|
145
|
+
window.location.assign(silentSignInUrl(startPath, env.pathname, env.search));
|
|
146
|
+
}
|
|
147
|
+
});
|
|
120
148
|
}, [enabled, hasSession, suppressed, startPath, privatePaths, idleDelayMs]);
|
|
121
149
|
return null;
|
|
122
150
|
}
|
|
@@ -148,19 +176,64 @@ function greetingText(profile, now = /* @__PURE__ */ new Date(), fallback = "Wel
|
|
|
148
176
|
if (!name) return fallback;
|
|
149
177
|
return `${greetingWord(localHour(now, profile.time_zone, profile.locale))}, ${name}`;
|
|
150
178
|
}
|
|
179
|
+
var READER_GREETING_POLL_MS = 3e4;
|
|
180
|
+
var READER_GREETING_TIMEOUT_MS = 8e3;
|
|
181
|
+
var READER_IDENTITY_CHANGED_EVENT = "scalemule:reader-identity-changed";
|
|
151
182
|
function ReaderGreeting({ endpoint = "/api/news/session", className, fallback = "Welcome back", as: Tag = "span" }) {
|
|
152
183
|
const [text, setText] = useState(null);
|
|
153
184
|
useEffect(() => {
|
|
154
185
|
let alive = true;
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
186
|
+
let generation = 0;
|
|
187
|
+
let authenticated = false;
|
|
188
|
+
let controller;
|
|
189
|
+
let deadline;
|
|
190
|
+
const refresh = (clear = false) => {
|
|
191
|
+
if (!alive) return;
|
|
192
|
+
if (clear) {
|
|
193
|
+
authenticated = false;
|
|
194
|
+
setText(null);
|
|
195
|
+
}
|
|
196
|
+
const current = ++generation;
|
|
197
|
+
controller?.abort();
|
|
198
|
+
if (deadline !== void 0) window.clearTimeout(deadline);
|
|
199
|
+
if (document.visibilityState === "hidden") return;
|
|
200
|
+
const request = new AbortController();
|
|
201
|
+
controller = request;
|
|
202
|
+
const timeout = window.setTimeout(() => {
|
|
203
|
+
if (!alive || current !== generation) return;
|
|
204
|
+
generation++;
|
|
205
|
+
authenticated = false;
|
|
206
|
+
setText(null);
|
|
207
|
+
request.abort();
|
|
208
|
+
}, READER_GREETING_TIMEOUT_MS);
|
|
209
|
+
deadline = timeout;
|
|
210
|
+
fetch(endpoint, { credentials: "same-origin", cache: "no-store", signal: request.signal }).then((response) => response.ok ? response.json() : null).then((summary) => {
|
|
211
|
+
if (!alive || current !== generation) return;
|
|
212
|
+
authenticated = summary?.authenticated === true;
|
|
213
|
+
setText(authenticated && summary ? greetingText(summary, /* @__PURE__ */ new Date(), fallback) : null);
|
|
214
|
+
}).catch(() => {
|
|
215
|
+
if (!alive || current !== generation) return;
|
|
216
|
+
authenticated = false;
|
|
217
|
+
setText(null);
|
|
218
|
+
}).finally(() => window.clearTimeout(timeout));
|
|
219
|
+
};
|
|
220
|
+
const recheck = () => refresh(true);
|
|
221
|
+
const timer = window.setInterval(() => {
|
|
222
|
+
if (authenticated) refresh();
|
|
223
|
+
}, READER_GREETING_POLL_MS);
|
|
224
|
+
window.addEventListener("focus", recheck);
|
|
225
|
+
document.addEventListener("visibilitychange", recheck);
|
|
226
|
+
document.addEventListener(READER_IDENTITY_CHANGED_EVENT, recheck);
|
|
227
|
+
refresh(true);
|
|
161
228
|
return () => {
|
|
162
229
|
alive = false;
|
|
163
|
-
|
|
230
|
+
generation++;
|
|
231
|
+
controller?.abort();
|
|
232
|
+
if (deadline !== void 0) window.clearTimeout(deadline);
|
|
233
|
+
window.clearInterval(timer);
|
|
234
|
+
window.removeEventListener("focus", recheck);
|
|
235
|
+
document.removeEventListener("visibilitychange", recheck);
|
|
236
|
+
document.removeEventListener(READER_IDENTITY_CHANGED_EVENT, recheck);
|
|
164
237
|
};
|
|
165
238
|
}, [endpoint, fallback]);
|
|
166
239
|
if (text === null) return null;
|
|
@@ -344,4 +417,4 @@ function AccountSwitchPrompt({
|
|
|
344
417
|
] });
|
|
345
418
|
}
|
|
346
419
|
|
|
347
|
-
export { AccountLinkForm, AccountSwitchPrompt, LEDVERY_BUTTON_LABELS, LedveryButton, READER_SSO_ATTEMPT_WINDOW_MS, READER_SSO_BOT_PATTERN, READER_SSO_PRIVATE_PATHS, ReaderGreeting, ReaderSsoBoot, firstName, greetingText, greetingWord, isFormField, localHour, silentSignInDecision, silentSignInUrl };
|
|
420
|
+
export { AccountLinkForm, AccountSwitchPrompt, LEDVERY_BUTTON_LABELS, LedveryButton, READER_GREETING_POLL_MS, READER_GREETING_TIMEOUT_MS, READER_IDENTITY_CHANGED_EVENT, READER_SSO_ATTEMPT_WINDOW_MS, READER_SSO_BOT_PATTERN, READER_SSO_PRIVATE_PATHS, ReaderGreeting, ReaderSsoBoot, firstName, greetingText, greetingWord, isFormField, localHour, silentSignInDecision, silentSignInUrl };
|
package/dist/reader-sso.cjs
CHANGED
|
@@ -195,7 +195,7 @@ async function startSignIn(config, input) {
|
|
|
195
195
|
}
|
|
196
196
|
const staleSession = [];
|
|
197
197
|
if (input.cookies(c.names.session)) {
|
|
198
|
-
const current = await currentUser(c, input.cookies(c.names.session));
|
|
198
|
+
const current = await currentUser(c, input.cookies(c.names.session), mode === "silent" ? "passive" : "active");
|
|
199
199
|
if (current && current.email_verified) {
|
|
200
200
|
if (mode === "silent" && (!current.needsSsoUpgrade || keepsLocal(input.cookies(c.names.ssoChecked), input.cookies(c.names.session)))) return { ok: false, status: 204, error: "", reason: "session", redirect, cookies: [] };
|
|
201
201
|
} else {
|
|
@@ -237,9 +237,9 @@ async function startSignIn(config, input) {
|
|
|
237
237
|
return { ok: false, status: 503, error: "Ledvery sign-in is temporarily unavailable. Please try again shortly or use your existing reader account.", reason: "unavailable", redirect, cookies: [] };
|
|
238
238
|
}
|
|
239
239
|
}
|
|
240
|
-
async function currentUser(c, token) {
|
|
240
|
+
async function currentUser(c, token, activity = "active") {
|
|
241
241
|
if (!token || !SESSION_TOKEN.test(token)) return null;
|
|
242
|
-
const response = await c.request("/v1/auth/me", { method: "GET", token });
|
|
242
|
+
const response = await c.request(activity === "passive" ? "/v1/auth/me?passive=true" : "/v1/auth/me", { method: "GET", token });
|
|
243
243
|
if (failed(response)) return null;
|
|
244
244
|
const me = payload(response);
|
|
245
245
|
if (typeof me.id !== "string" || !me.id) return null;
|
|
@@ -468,7 +468,7 @@ async function signOut(config, input) {
|
|
|
468
468
|
}
|
|
469
469
|
async function sessionSummary(config, cookies) {
|
|
470
470
|
const c = resolve(config);
|
|
471
|
-
const user = await currentUser(c, cookies(c.names.session));
|
|
471
|
+
const user = await currentUser(c, cookies(c.names.session), "passive");
|
|
472
472
|
if (!user || !user.email_verified) return { authenticated: false };
|
|
473
473
|
return { authenticated: true, full_name: user.full_name, locale: user.locale, time_zone: user.time_zone, ...user.needsSsoUpgrade && !keepsLocal(cookies(c.names.ssoChecked), cookies(c.names.session)) ? { session_upgrade_required: true } : {} };
|
|
474
474
|
}
|
package/dist/reader-sso.js
CHANGED
|
@@ -180,7 +180,7 @@ async function startSignIn(config, input) {
|
|
|
180
180
|
}
|
|
181
181
|
const staleSession = [];
|
|
182
182
|
if (input.cookies(c.names.session)) {
|
|
183
|
-
const current = await currentUser(c, input.cookies(c.names.session));
|
|
183
|
+
const current = await currentUser(c, input.cookies(c.names.session), mode === "silent" ? "passive" : "active");
|
|
184
184
|
if (current && current.email_verified) {
|
|
185
185
|
if (mode === "silent" && (!current.needsSsoUpgrade || keepsLocal(input.cookies(c.names.ssoChecked), input.cookies(c.names.session)))) return { ok: false, status: 204, error: "", reason: "session", redirect, cookies: [] };
|
|
186
186
|
} else {
|
|
@@ -222,9 +222,9 @@ async function startSignIn(config, input) {
|
|
|
222
222
|
return { ok: false, status: 503, error: "Ledvery sign-in is temporarily unavailable. Please try again shortly or use your existing reader account.", reason: "unavailable", redirect, cookies: [] };
|
|
223
223
|
}
|
|
224
224
|
}
|
|
225
|
-
async function currentUser(c, token) {
|
|
225
|
+
async function currentUser(c, token, activity = "active") {
|
|
226
226
|
if (!token || !SESSION_TOKEN.test(token)) return null;
|
|
227
|
-
const response = await c.request("/v1/auth/me", { method: "GET", token });
|
|
227
|
+
const response = await c.request(activity === "passive" ? "/v1/auth/me?passive=true" : "/v1/auth/me", { method: "GET", token });
|
|
228
228
|
if (failed(response)) return null;
|
|
229
229
|
const me = payload(response);
|
|
230
230
|
if (typeof me.id !== "string" || !me.id) return null;
|
|
@@ -453,7 +453,7 @@ async function signOut(config, input) {
|
|
|
453
453
|
}
|
|
454
454
|
async function sessionSummary(config, cookies) {
|
|
455
455
|
const c = resolve(config);
|
|
456
|
-
const user = await currentUser(c, cookies(c.names.session));
|
|
456
|
+
const user = await currentUser(c, cookies(c.names.session), "passive");
|
|
457
457
|
if (!user || !user.email_verified) return { authenticated: false };
|
|
458
458
|
return { authenticated: true, full_name: user.full_name, locale: user.locale, time_zone: user.time_zone, ...user.needsSsoUpgrade && !keepsLocal(cookies(c.names.ssoChecked), cookies(c.names.session)) ? { session_upgrade_required: true } : {} };
|
|
459
459
|
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scalemule/news-publication",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.78",
|
|
4
4
|
"description": "Shared server framework for ScaleMule local news publications. Walnut Creek Times is the broadsheet default; each paper supplies its own identity and feature flags.",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"bin": {
|
|
6
|
+
"bin": {
|
|
7
|
+
"scalemule-article": "./bin/scalemule-article.mjs"
|
|
8
|
+
},
|
|
7
9
|
"main": "./dist/index.cjs",
|
|
8
10
|
"module": "./dist/index.js",
|
|
9
11
|
"types": "./dist/index.d.ts",
|