@franzzemen/brokenstock-shell 0.4.4 → 0.4.5
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.
|
@@ -233,8 +233,17 @@ path here is bounded regardless.
|
|
|
233
233
|
fall back to running uncoordinated — today's behaviour — never throw. A missing
|
|
234
234
|
API must not be the reason nobody can sign in.
|
|
235
235
|
*/
|
|
236
|
-
|
|
237
|
-
|
|
236
|
+
/**
|
|
237
|
+
* Checked at CALL time, not once at module load.
|
|
238
|
+
*
|
|
239
|
+
* Module-level would be evaluated during import — before `navigator` exists at
|
|
240
|
+
* all under prerendering, and before a test can arrange anything — permanently
|
|
241
|
+
* fixing the answer to "no" for the life of the process. Found by writing the
|
|
242
|
+
* test for the fallback path, which could not be reached any other way.
|
|
243
|
+
*/
|
|
244
|
+
function locksAvailable() {
|
|
245
|
+
return typeof navigator !== 'undefined' && typeof navigator.locks?.request === 'function';
|
|
246
|
+
}
|
|
238
247
|
/**
|
|
239
248
|
* Runs `work` if this window can take the lock right now.
|
|
240
249
|
*
|
|
@@ -246,7 +255,7 @@ const LOCKS_AVAILABLE = typeof navigator !== 'undefined'
|
|
|
246
255
|
* deliberate degradation to today's behaviour rather than a refusal.
|
|
247
256
|
*/
|
|
248
257
|
async function runExclusive(name, work) {
|
|
249
|
-
if (!
|
|
258
|
+
if (!locksAvailable()) {
|
|
250
259
|
console.warn(`[shell] navigator.locks unavailable; "${name}" runs without cross-window coordination`);
|
|
251
260
|
return { ran: true, value: await work() };
|
|
252
261
|
}
|
|
@@ -281,6 +290,40 @@ async function awaitChange(read, unchanged, timeoutMs, pollMs = 100) {
|
|
|
281
290
|
}
|
|
282
291
|
}
|
|
283
292
|
|
|
293
|
+
/*
|
|
294
|
+
Created by Franz Zemen 2026-08-29
|
|
295
|
+
License Type: UNLICENSED
|
|
296
|
+
|
|
297
|
+
What a window should do once it can act on a session renewal
|
|
298
|
+
(multiple-windows PRD D5).
|
|
299
|
+
|
|
300
|
+
WHY THIS IS A NAMED FUNCTION RATHER THAN THREE `if`s INSIDE THE SERVICE. The
|
|
301
|
+
rule was originally written as "retry with a token different from the one you
|
|
302
|
+
failed with", which is not a well-defined instruction: the in-window
|
|
303
|
+
single-flight already collapses requests that were sent with DIFFERENT tokens,
|
|
304
|
+
so "the one you failed with" is not a single value. Pulling the decision out
|
|
305
|
+
forces the three cases to be named, and lets them be tested without mocking a
|
|
306
|
+
network call — which is what they deserve, because getting this wrong shows up
|
|
307
|
+
hours later as "a window signed me out" with nothing linking cause to effect.
|
|
308
|
+
*/
|
|
309
|
+
/**
|
|
310
|
+
* @param before the token this window held immediately BEFORE asking for the lock
|
|
311
|
+
* @param current the token in shared storage now that this window can act
|
|
312
|
+
*/
|
|
313
|
+
function decideRenewal(before, current) {
|
|
314
|
+
// Gone. Retrying would present nothing and 401 again; worse, it would look
|
|
315
|
+
// like a transient failure when it is a definite answer.
|
|
316
|
+
if (before && !current)
|
|
317
|
+
return 'sign-out';
|
|
318
|
+
// Someone else's fresh token. Rotating again would invalidate the token every
|
|
319
|
+
// OTHER window just adopted — turning one window's recovery into everyone
|
|
320
|
+
// else's sign-out.
|
|
321
|
+
if (current && current !== before)
|
|
322
|
+
return 'adopt';
|
|
323
|
+
// Unchanged, or nothing stored to begin with.
|
|
324
|
+
return 'renew';
|
|
325
|
+
}
|
|
326
|
+
|
|
284
327
|
/** Name of the cross-window renewal lock (PRD D5). */
|
|
285
328
|
const RENEWAL_LOCK = 'brokenstock.session.renew';
|
|
286
329
|
/** How long a window waits for the renewing window to publish a token (PRD D5). */
|
|
@@ -547,17 +590,15 @@ class AuthenticationService {
|
|
|
547
590
|
*/
|
|
548
591
|
async #renewOrAdopt(before) {
|
|
549
592
|
const current = this.#store.read();
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
return true;
|
|
593
|
+
switch (decideRenewal(before, current)) {
|
|
594
|
+
case 'sign-out':
|
|
595
|
+
return false;
|
|
596
|
+
case 'adopt':
|
|
597
|
+
this.#auth = current;
|
|
598
|
+
return true;
|
|
599
|
+
case 'renew':
|
|
600
|
+
return this.#renew();
|
|
559
601
|
}
|
|
560
|
-
return this.#renew();
|
|
561
602
|
}
|
|
562
603
|
/**
|
|
563
604
|
* The renewal request itself.
|
|
@@ -3165,5 +3206,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
3165
3206
|
* Generated bundle index. Do not edit.
|
|
3166
3207
|
*/
|
|
3167
3208
|
|
|
3168
|
-
export { AuthenticationService, CLEAR_USAGE, CommandRegistryService, CommandService, CommandTerminal, ConfirmDialog, FAQ_CONTENT_LOADER, FAQ_DEV_EDITOR, FAQ_DEV_MODE, FAQ_DEV_WORKSPACE_ROOT, FAQ_PUBLIC_PUBLISHER, FAQ_PUBLIC_SITE_BASE_URL, FaqAuthoringPanel, FaqHistoryService, FaqMarkdownRendererService, FaqModal, FaqService, HELP_USAGE, HttpFaqContentLoader, KeyboardShortcutService, Login, MainContent, PublicFaqIndex, PublicFaqPage, PublicFaqToc, RegexValidatorDirective, SESSION_TOKEN_KEY, SHELL_APP_NAME, SHELL_AUTH_URLS, SHELL_COMMANDS, SHELL_MENU_ITEMS, SHELL_POST_LOGIN_ROUTE, SHELL_SHARED_SESSION, SessionTokenStore, Shell, SideMenu, StatusBar, TerminalFocusService, TerminalVisibilityService, ThemeService, ThinkingService, audienceAllowed, authGuard, authInterceptor, buildFallbackChain, guestGuard, provideShellConfig, tokenize };
|
|
3209
|
+
export { AuthenticationService, CLEAR_USAGE, CommandRegistryService, CommandService, CommandTerminal, ConfirmDialog, FAQ_CONTENT_LOADER, FAQ_DEV_EDITOR, FAQ_DEV_MODE, FAQ_DEV_WORKSPACE_ROOT, FAQ_PUBLIC_PUBLISHER, FAQ_PUBLIC_SITE_BASE_URL, FaqAuthoringPanel, FaqHistoryService, FaqMarkdownRendererService, FaqModal, FaqService, HELP_USAGE, HttpFaqContentLoader, KeyboardShortcutService, Login, MainContent, PublicFaqIndex, PublicFaqPage, PublicFaqToc, RegexValidatorDirective, SESSION_TOKEN_KEY, SHELL_APP_NAME, SHELL_AUTH_URLS, SHELL_COMMANDS, SHELL_MENU_ITEMS, SHELL_POST_LOGIN_ROUTE, SHELL_SHARED_SESSION, SessionTokenStore, Shell, SideMenu, StatusBar, TerminalFocusService, TerminalVisibilityService, ThemeService, ThinkingService, audienceAllowed, authGuard, authInterceptor, buildFallbackChain, decideRenewal, guestGuard, provideShellConfig, tokenize };
|
|
3169
3210
|
//# sourceMappingURL=franzzemen-brokenstock-shell.mjs.map
|