@aglyn/aglyn 1.0.0-beta.274 → 1.0.0-beta.277

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.
@@ -619,6 +619,65 @@ export interface SignupLandingOrg {
619
619
  visitor?: string | null;
620
620
  }
621
621
  export declare function signupLandingHealth(orgs: SignupLandingOrg[] | null, ms: number, maxStranded?: number, windowHours?: number, graceMinutes?: number): SignupLandingCheck;
622
+ /**
623
+ * Did each new verified account end up in a workspace (AGL-4021)?
624
+ *
625
+ * {@link SignupLandingCheck} grades orgs, so an account that never got an org
626
+ * is invisible to it. That is exactly how AGL-4021 hid: every phone Google
627
+ * sign-up for a day reached the console with no workspace, and nothing went
628
+ * red. The canary signs up through the password door, and the three failures
629
+ * sat among healthy desktop and password sign-ups, so a streak over everyone
630
+ * kept resetting.
631
+ *
632
+ * So the streak is taken per sign-up DOOR (`users/{uid}.acquisition.door`):
633
+ * red when the newest {@link MAX_WORKSPACELESS_SIGNUPS} graded accounts that
634
+ * came through any one door all have no workspace. A broken door strands
635
+ * every account it makes. A person who leaves still has the workspace their
636
+ * door made before they left, so leaving does not count here.
637
+ *
638
+ * Graded: verified accounts that accepted the Terms. An unverified password
639
+ * account gets its workspace on its first verified session (AGL-2590), so it
640
+ * waits by design. A Google account created from /signin is signed out and
641
+ * sent to /signup to accept the Terms (AGL-1497), and one that walks away
642
+ * there never agreed to anything, so it has no workspace by design too.
643
+ *
644
+ * Pure. The body carries counts only, never a uid or a door name, because the
645
+ * endpoint is public.
646
+ */
647
+ export interface SignupWorkspaceCheck extends HealthCheck {
648
+ /** Graded accounts in the window with no workspace. Null when the read failed. */
649
+ workspacelessAccounts: number | null;
650
+ /** The longest per-door streak of newest accounts with no workspace. */
651
+ workspacelessStreak: number | null;
652
+ /** Graded accounts in the window, the context for the counts above. */
653
+ gradedInWindow: number | null;
654
+ /** How far back the window reaches. */
655
+ windowHours: number;
656
+ /** How old an account must be before having no workspace counts. */
657
+ graceMinutes: number;
658
+ /** The per-door streak at which this goes red. */
659
+ maxWorkspaceless: number;
660
+ }
661
+ /** One account the route read for {@link signupWorkspaceHealth}. */
662
+ export interface SignupWorkspaceAccount {
663
+ /** The sign-up door, or null when the account recorded none. */
664
+ door: string | null;
665
+ createdAtMs: number | null;
666
+ verified: boolean;
667
+ /** Whether the account has any Terms acceptance on record. */
668
+ accepted: boolean;
669
+ hasWorkspace: boolean;
670
+ }
671
+ /** Red when the two newest verified accounts through one door have no workspace. */
672
+ export declare const MAX_WORKSPACELESS_SIGNUPS = 2;
673
+ /**
674
+ * Thirty-six hours of accounts, not the landing check's twelve. A door can be
675
+ * quiet for longer than twelve hours (AGL-4021's three phone sign-ups were
676
+ * 12 h and 5 h apart), and a window that never holds two of them can never
677
+ * see the streak.
678
+ */
679
+ export declare const SIGNUP_WORKSPACE_WINDOW_HOURS = 36;
680
+ export declare function signupWorkspaceHealth(accounts: SignupWorkspaceAccount[] | null, ms: number, maxWorkspaceless?: number, windowHours?: number, graceMinutes?: number): SignupWorkspaceCheck;
622
681
  /**
623
682
  * Rate-limiter degradation, reduced to a health verdict (AGL-1693).
624
683
  *
@@ -620,6 +620,62 @@ export function signupLandingHealth(orgs, ms, maxStranded = MAX_STRANDED_SIGNUPS
620
620
  orgsInWindow: orgs.length
621
621
  }, shape);
622
622
  }
623
+ /** Red when the two newest verified accounts through one door have no workspace. */ export const MAX_WORKSPACELESS_SIGNUPS = 2;
624
+ /**
625
+ * Thirty-six hours of accounts, not the landing check's twelve. A door can be
626
+ * quiet for longer than twelve hours (AGL-4021's three phone sign-ups were
627
+ * 12 h and 5 h apart), and a window that never holds two of them can never
628
+ * see the streak.
629
+ */ export const SIGNUP_WORKSPACE_WINDOW_HOURS = 36;
630
+ export function signupWorkspaceHealth(accounts, ms, maxWorkspaceless = MAX_WORKSPACELESS_SIGNUPS, windowHours = SIGNUP_WORKSPACE_WINDOW_HOURS, graceMinutes = SIGNUP_LANDING_GRACE_MINUTES) {
631
+ const shape = {
632
+ windowHours,
633
+ graceMinutes,
634
+ maxWorkspaceless
635
+ };
636
+ if (accounts === null) {
637
+ return _extends({
638
+ ok: false,
639
+ ms,
640
+ code: 'accounts-unavailable',
641
+ workspacelessAccounts: null,
642
+ workspacelessStreak: null,
643
+ gradedInWindow: null
644
+ }, shape);
645
+ }
646
+ const graded = accounts.filter((account)=>account.verified === true && account.accepted === true);
647
+ const byDoor = new Map();
648
+ for (const account of graded){
649
+ var _byDoor_get;
650
+ const door = account.door || 'unknown';
651
+ byDoor.set(door, [
652
+ ...(_byDoor_get = byDoor.get(door)) != null ? _byDoor_get : [],
653
+ account
654
+ ]);
655
+ }
656
+ let workspacelessStreak = 0;
657
+ for (const doorAccounts of byDoor.values()){
658
+ const newestFirst = [
659
+ ...doorAccounts
660
+ ].sort((a, b)=>(Number(b.createdAtMs) || 0) - (Number(a.createdAtMs) || 0));
661
+ let streak = 0;
662
+ while(streak < newestFirst.length && newestFirst[streak].hasWorkspace !== true){
663
+ streak += 1;
664
+ }
665
+ workspacelessStreak = Math.max(workspacelessStreak, streak);
666
+ }
667
+ const stranded = workspacelessStreak >= maxWorkspaceless;
668
+ return _extends({
669
+ ok: !stranded,
670
+ ms
671
+ }, stranded ? {
672
+ code: 'signups-without-workspace'
673
+ } : {}, {
674
+ workspacelessAccounts: graded.filter((account)=>account.hasWorkspace !== true).length,
675
+ workspacelessStreak,
676
+ gradedInWindow: graded.length
677
+ }, shape);
678
+ }
623
679
  /**
624
680
  * Trailing window the degradation counts cover.
625
681
  *