@ai-matrx/auth 0.2.2 → 0.2.4
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/CHANGELOG.md +22 -0
- package/dist/{active-organization-Dy4UtiyR.d.cts → active-organization-Cxm6GDIy.d.cts} +59 -3
- package/dist/{active-organization-Dy4UtiyR.d.ts → active-organization-Cxm6GDIy.d.ts} +59 -3
- package/dist/guest.d.cts +1 -1
- package/dist/guest.d.ts +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/organizations.cjs +95 -9
- package/dist/organizations.d.cts +1 -1
- package/dist/organizations.d.ts +1 -1
- package/dist/organizations.js +95 -9
- package/dist/react.cjs +3 -1
- package/dist/react.d.cts +20 -1
- package/dist/react.d.ts +20 -1
- package/dist/react.js +3 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog — @ai-matrx/auth
|
|
2
2
|
|
|
3
|
+
## 0.2.4
|
|
4
|
+
### active organization options (round 2)
|
|
5
|
+
|
|
6
|
+
All options default to today's behaviour. `createActiveOrganization`:
|
|
7
|
+
- `accountChoices` — the snapshot carries `favourites` and `startupOrganizationId` (always present: `[]` / null when off); when on,
|
|
8
|
+
`memberships` is ordered favourites-first and a failed favourites read leaves them out. New `setFavourite(id, on)` (optimistic,
|
|
9
|
+
rolled back and rethrown on failure) and `setStartup(id | null)`.
|
|
10
|
+
- `cached` `{ userId, read, write? }` — a new `unchecked` status: the memberships could not be read, so the host's cached last answer
|
|
11
|
+
(or the list already on screen) is shown unverified. `current()` / `ensureOrgId()` answer in `unchecked`.
|
|
12
|
+
- `onLapse({ was, now })` — the organization the person was in is no longer a membership and the ladder chose another.
|
|
13
|
+
- `refreshGapMs` + `refresh({ force? })` — a throttled re-read.
|
|
14
|
+
- `holdResolved` (default true) — false: the ladder's own answer is never written as the held choice; only `switchTo` writes it
|
|
15
|
+
(the extension's rule).
|
|
16
|
+
- `ActiveOrganizationSnapshot` gains `favourites` and `startupOrganizationId`; new types `CachedActiveOrganization`, `OrganizationLapse`.
|
|
17
|
+
Tests: `active-organization.test.ts` (+9).
|
|
18
|
+
|
|
19
|
+
## 0.2.3
|
|
20
|
+
|
|
21
|
+
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
22
|
+
`git diff npm/auth/v0.2.2..npm/auth/v0.2.3 -- apps/shared/auth`).
|
|
23
|
+
No source changes intended and no consumer action required.
|
|
24
|
+
|
|
3
25
|
## 0.2.2
|
|
4
26
|
### extension/desktop options (round 2)
|
|
5
27
|
|
|
@@ -515,7 +515,11 @@ interface HeldOrganizationStore {
|
|
|
515
515
|
write(userId: string, organizationId: string): void | Promise<void>;
|
|
516
516
|
clear(): void | Promise<void>;
|
|
517
517
|
}
|
|
518
|
-
|
|
518
|
+
/**
|
|
519
|
+
* `unchecked`: the memberships could not be read, and the answer shown is the host's cached last answer
|
|
520
|
+
* (`options.cached`), not verified against the database. Only ever produced when `cached` is given.
|
|
521
|
+
*/
|
|
522
|
+
type ActiveOrganizationStatus = "idle" | "resolving" | "ready" | "none" | "error" | "unchecked";
|
|
519
523
|
interface ActiveOrganizationSnapshot {
|
|
520
524
|
readonly status: ActiveOrganizationStatus;
|
|
521
525
|
readonly userId: string | null;
|
|
@@ -525,6 +529,25 @@ interface ActiveOrganizationSnapshot {
|
|
|
525
529
|
/** Which ladder rung answered (or "switched" after a person's click). */
|
|
526
530
|
readonly rung: OrganizationRung | "switched" | "link" | null;
|
|
527
531
|
readonly error: string | null;
|
|
532
|
+
/**
|
|
533
|
+
* The account's favourite organization ids. Read only when `options.accountChoices` is on; `[]`
|
|
534
|
+
* otherwise. When on, `memberships` is ordered favourites, then own, then the rest.
|
|
535
|
+
*/
|
|
536
|
+
readonly favourites: readonly string[];
|
|
537
|
+
/** The account's start-up organization setting. Read only when `options.accountChoices` is on; null otherwise. */
|
|
538
|
+
readonly startupOrganizationId: string | null;
|
|
539
|
+
}
|
|
540
|
+
/** What a host keeps so a launch with no network can still show the last answer (`options.cached`). */
|
|
541
|
+
interface CachedActiveOrganization {
|
|
542
|
+
readonly memberships: readonly OrganizationMembership[];
|
|
543
|
+
readonly organizationId: string | null;
|
|
544
|
+
readonly favourites: readonly string[];
|
|
545
|
+
readonly startupOrganizationId: string | null;
|
|
546
|
+
}
|
|
547
|
+
/** A held organization that is no longer a membership, and the one the ladder chose instead. */
|
|
548
|
+
interface OrganizationLapse {
|
|
549
|
+
readonly was: OrganizationMembership;
|
|
550
|
+
readonly now: OrganizationMembership;
|
|
528
551
|
}
|
|
529
552
|
declare class NoActiveOrganizationError extends Error {
|
|
530
553
|
readonly reason: "signed-out" | "no-memberships" | "unreadable";
|
|
@@ -542,6 +565,14 @@ interface ActiveOrganization {
|
|
|
542
565
|
/** The active organization id now, or null while resolving / with none. Sync (api's getter). */
|
|
543
566
|
current(): string | null;
|
|
544
567
|
/** THE HELD REQUEST: the record's own organization, else the active one once resolved. */
|
|
568
|
+
/** Re-read, unless `options.refreshGapMs` has not elapsed since the last read (`force` always reads). */
|
|
569
|
+
refresh(options?: {
|
|
570
|
+
readonly force?: boolean;
|
|
571
|
+
}): Promise<ActiveOrganizationSnapshot>;
|
|
572
|
+
/** Save the account's start-up organization (null: the first). Needs `accountChoices`; throws on refusal. */
|
|
573
|
+
setStartup(organizationId: string | null): Promise<void>;
|
|
574
|
+
/** Add or remove a favourite and re-order. Optimistic; rolled back and rethrown if the save fails. */
|
|
575
|
+
setFavourite(organizationId: string, favourite: boolean): Promise<void>;
|
|
545
576
|
ensureOrgId(recordOrganizationId?: string | null): Promise<string>;
|
|
546
577
|
/** A person's click: switch, remember for this tab, save as last active for the next load. */
|
|
547
578
|
switchTo(organizationId: string): Promise<void>;
|
|
@@ -582,13 +613,38 @@ interface CreateActiveOrganizationOptions {
|
|
|
582
613
|
readonly refusalRecheckGapMs?: number | undefined;
|
|
583
614
|
/** Load as soon as it is created (default true). */
|
|
584
615
|
readonly autoLoad?: boolean | undefined;
|
|
616
|
+
/**
|
|
617
|
+
* Read the account's favourites and start-up organization with the memberships, expose both in
|
|
618
|
+
* the snapshot and order `memberships` favourites-first. A failed favourites read leaves them out
|
|
619
|
+
* (the list still shows). Default false.
|
|
620
|
+
*/
|
|
621
|
+
readonly accountChoices?: boolean | undefined;
|
|
622
|
+
/**
|
|
623
|
+
* Offline answer. When the memberships cannot be read and nothing is shown yet, the cached answer
|
|
624
|
+
* is published as `unchecked` if its organization is the held one (or the cached one). `userId`
|
|
625
|
+
* says whose cache to read when the person cannot be verified; `write` is called after each good read.
|
|
626
|
+
*/
|
|
627
|
+
readonly cached?: {
|
|
628
|
+
readonly userId: () => string | null | undefined;
|
|
629
|
+
readonly read: (userId: string) => CachedActiveOrganization | null | Promise<CachedActiveOrganization | null>;
|
|
630
|
+
readonly write?: ((userId: string, answer: CachedActiveOrganization) => void | Promise<void>) | undefined;
|
|
631
|
+
} | undefined;
|
|
632
|
+
/** Called once when the organization the person was in is no longer a membership and the ladder chose another. */
|
|
633
|
+
readonly onLapse?: ((lapse: OrganizationLapse) => void) | undefined;
|
|
634
|
+
/** `refresh()` without `force` does nothing within this many ms of the last read. Default 0 (always reads). */
|
|
635
|
+
readonly refreshGapMs?: number | undefined;
|
|
636
|
+
/**
|
|
637
|
+
* Write the ladder's own answer back as the held organization. Default true. False: only an
|
|
638
|
+
* explicit `switchTo` writes the held choice (the extension's rule: a computed answer is never a choice).
|
|
639
|
+
*/
|
|
640
|
+
readonly holdResolved?: boolean | undefined;
|
|
585
641
|
}
|
|
586
642
|
/** A held store over a `Storage` (browser `sessionStorage` = per tab), identity-keyed. */
|
|
587
|
-
declare function storageHeldOrganization(storage: Pick<Storage, "getItem" | "setItem" | "removeItem">, key?: string): HeldOrganizationStore & {
|
|
643
|
+
declare function storageHeldOrganization(storage: Pick<Storage, "getItem" | "setItem" | "removeItem">, key?: string): Omit<HeldOrganizationStore, "read"> & {
|
|
588
644
|
read(userId: string): string | null;
|
|
589
645
|
};
|
|
590
646
|
/** Held in memory: the running app's own choice (desktop, scripts, tests). */
|
|
591
647
|
declare function memoryHeldOrganization(): HeldOrganizationStore;
|
|
592
648
|
declare function createActiveOrganization(options: CreateActiveOrganizationOptions): ActiveOrganization;
|
|
593
649
|
|
|
594
|
-
export {
|
|
650
|
+
export { createNotAMemberRefusalNotice as $, type AuthClient as A, NoActiveOrganizationError as B, type CachedActiveOrganization as C, NotAMemberError as D, type NotAMemberRefusalNotice as E, ORGANIZATION_RESOLUTION_TIMEOUT_MS as F, type OrganizationBootstrapGate as G, type HeldOrganizationStore as H, type OrganizationLapse as I, type OrganizationMembership as J, type OrganizationRefusal as K, LINK_ORGANIZATION_QUERY_KEY as L, type OrganizationRung as M, NOT_A_MEMBER_RECHECK_GAP_MS as N, ORGANIZATION_ENTITY_TYPE as O, type OrganizationsDb as P, type ParentOrganization as Q, type ReadMembershipsOptions as R, type SessionSourceLike as S, type ResolveWithLinkInput as T, type UserCredentialsPort as U, type ResolvedOrganization as V, type ResolvedWithLink as W, adminSeatReplacesUserContext as X, classifyLinkOrganizationValue as Y, classifyOrganizationRefusal as Z, createActiveOrganization as _, SignInError as a, createOrganizationBootstrapGate as a0, decideLinkOrganization as a1, isNotAMemberRefusal as a2, linkOrganizationMalformedMessage as a3, linkOrganizationNotAMemberMessage as a4, linkOrganizationOfferMessage as a5, linkOrganizationSwitchAnnouncement as a6, memoryHeldOrganization as a7, oldestMembership as a8, orderOrganizations as a9, organizationMatches as aa, readAccountOrganizationChoices as ab, readLinkOrganizationParam as ac, readMemberships as ad, readOrganizationFavourites as ae, resolveActiveOrganization as af, resolveActiveOrganizationLazily as ag, resolveAdminSeat as ah, resolveChildOrganizationId as ai, resolveOrganizationWithLink as aj, setOrganizationFavourite as ak, storageHeldOrganization as al, toAccountOrganizationChoices as am, writeLastActiveOrganization as an, writeStartupOrganization as ao, getSession as b, signInWithOAuth as c, signInWithPassword as d, signOut as e, userCredentials as f, getPerson as g, type AccountOrganizationChoices as h, invalidatePerson as i, type ActiveOrganization as j, type ActiveOrganizationSnapshot as k, type ActiveOrganizationStatus as l, type AdminSeat as m, type AdminSeatInput as n, onSessionChange as o, type ArchiveFilter as p, type CreateActiveOrganizationOptions as q, refreshSession as r, sessionCredentials as s, type LadderInput as t, updatePerson as u, type LinkOrganizationDecision as v, type LinkOrganizationDecisionInput as w, type LinkOrganizationMembership as x, type LinkOrganizationMessages as y, type LinkOrganizationParam as z };
|
|
@@ -515,7 +515,11 @@ interface HeldOrganizationStore {
|
|
|
515
515
|
write(userId: string, organizationId: string): void | Promise<void>;
|
|
516
516
|
clear(): void | Promise<void>;
|
|
517
517
|
}
|
|
518
|
-
|
|
518
|
+
/**
|
|
519
|
+
* `unchecked`: the memberships could not be read, and the answer shown is the host's cached last answer
|
|
520
|
+
* (`options.cached`), not verified against the database. Only ever produced when `cached` is given.
|
|
521
|
+
*/
|
|
522
|
+
type ActiveOrganizationStatus = "idle" | "resolving" | "ready" | "none" | "error" | "unchecked";
|
|
519
523
|
interface ActiveOrganizationSnapshot {
|
|
520
524
|
readonly status: ActiveOrganizationStatus;
|
|
521
525
|
readonly userId: string | null;
|
|
@@ -525,6 +529,25 @@ interface ActiveOrganizationSnapshot {
|
|
|
525
529
|
/** Which ladder rung answered (or "switched" after a person's click). */
|
|
526
530
|
readonly rung: OrganizationRung | "switched" | "link" | null;
|
|
527
531
|
readonly error: string | null;
|
|
532
|
+
/**
|
|
533
|
+
* The account's favourite organization ids. Read only when `options.accountChoices` is on; `[]`
|
|
534
|
+
* otherwise. When on, `memberships` is ordered favourites, then own, then the rest.
|
|
535
|
+
*/
|
|
536
|
+
readonly favourites: readonly string[];
|
|
537
|
+
/** The account's start-up organization setting. Read only when `options.accountChoices` is on; null otherwise. */
|
|
538
|
+
readonly startupOrganizationId: string | null;
|
|
539
|
+
}
|
|
540
|
+
/** What a host keeps so a launch with no network can still show the last answer (`options.cached`). */
|
|
541
|
+
interface CachedActiveOrganization {
|
|
542
|
+
readonly memberships: readonly OrganizationMembership[];
|
|
543
|
+
readonly organizationId: string | null;
|
|
544
|
+
readonly favourites: readonly string[];
|
|
545
|
+
readonly startupOrganizationId: string | null;
|
|
546
|
+
}
|
|
547
|
+
/** A held organization that is no longer a membership, and the one the ladder chose instead. */
|
|
548
|
+
interface OrganizationLapse {
|
|
549
|
+
readonly was: OrganizationMembership;
|
|
550
|
+
readonly now: OrganizationMembership;
|
|
528
551
|
}
|
|
529
552
|
declare class NoActiveOrganizationError extends Error {
|
|
530
553
|
readonly reason: "signed-out" | "no-memberships" | "unreadable";
|
|
@@ -542,6 +565,14 @@ interface ActiveOrganization {
|
|
|
542
565
|
/** The active organization id now, or null while resolving / with none. Sync (api's getter). */
|
|
543
566
|
current(): string | null;
|
|
544
567
|
/** THE HELD REQUEST: the record's own organization, else the active one once resolved. */
|
|
568
|
+
/** Re-read, unless `options.refreshGapMs` has not elapsed since the last read (`force` always reads). */
|
|
569
|
+
refresh(options?: {
|
|
570
|
+
readonly force?: boolean;
|
|
571
|
+
}): Promise<ActiveOrganizationSnapshot>;
|
|
572
|
+
/** Save the account's start-up organization (null: the first). Needs `accountChoices`; throws on refusal. */
|
|
573
|
+
setStartup(organizationId: string | null): Promise<void>;
|
|
574
|
+
/** Add or remove a favourite and re-order. Optimistic; rolled back and rethrown if the save fails. */
|
|
575
|
+
setFavourite(organizationId: string, favourite: boolean): Promise<void>;
|
|
545
576
|
ensureOrgId(recordOrganizationId?: string | null): Promise<string>;
|
|
546
577
|
/** A person's click: switch, remember for this tab, save as last active for the next load. */
|
|
547
578
|
switchTo(organizationId: string): Promise<void>;
|
|
@@ -582,13 +613,38 @@ interface CreateActiveOrganizationOptions {
|
|
|
582
613
|
readonly refusalRecheckGapMs?: number | undefined;
|
|
583
614
|
/** Load as soon as it is created (default true). */
|
|
584
615
|
readonly autoLoad?: boolean | undefined;
|
|
616
|
+
/**
|
|
617
|
+
* Read the account's favourites and start-up organization with the memberships, expose both in
|
|
618
|
+
* the snapshot and order `memberships` favourites-first. A failed favourites read leaves them out
|
|
619
|
+
* (the list still shows). Default false.
|
|
620
|
+
*/
|
|
621
|
+
readonly accountChoices?: boolean | undefined;
|
|
622
|
+
/**
|
|
623
|
+
* Offline answer. When the memberships cannot be read and nothing is shown yet, the cached answer
|
|
624
|
+
* is published as `unchecked` if its organization is the held one (or the cached one). `userId`
|
|
625
|
+
* says whose cache to read when the person cannot be verified; `write` is called after each good read.
|
|
626
|
+
*/
|
|
627
|
+
readonly cached?: {
|
|
628
|
+
readonly userId: () => string | null | undefined;
|
|
629
|
+
readonly read: (userId: string) => CachedActiveOrganization | null | Promise<CachedActiveOrganization | null>;
|
|
630
|
+
readonly write?: ((userId: string, answer: CachedActiveOrganization) => void | Promise<void>) | undefined;
|
|
631
|
+
} | undefined;
|
|
632
|
+
/** Called once when the organization the person was in is no longer a membership and the ladder chose another. */
|
|
633
|
+
readonly onLapse?: ((lapse: OrganizationLapse) => void) | undefined;
|
|
634
|
+
/** `refresh()` without `force` does nothing within this many ms of the last read. Default 0 (always reads). */
|
|
635
|
+
readonly refreshGapMs?: number | undefined;
|
|
636
|
+
/**
|
|
637
|
+
* Write the ladder's own answer back as the held organization. Default true. False: only an
|
|
638
|
+
* explicit `switchTo` writes the held choice (the extension's rule: a computed answer is never a choice).
|
|
639
|
+
*/
|
|
640
|
+
readonly holdResolved?: boolean | undefined;
|
|
585
641
|
}
|
|
586
642
|
/** A held store over a `Storage` (browser `sessionStorage` = per tab), identity-keyed. */
|
|
587
|
-
declare function storageHeldOrganization(storage: Pick<Storage, "getItem" | "setItem" | "removeItem">, key?: string): HeldOrganizationStore & {
|
|
643
|
+
declare function storageHeldOrganization(storage: Pick<Storage, "getItem" | "setItem" | "removeItem">, key?: string): Omit<HeldOrganizationStore, "read"> & {
|
|
588
644
|
read(userId: string): string | null;
|
|
589
645
|
};
|
|
590
646
|
/** Held in memory: the running app's own choice (desktop, scripts, tests). */
|
|
591
647
|
declare function memoryHeldOrganization(): HeldOrganizationStore;
|
|
592
648
|
declare function createActiveOrganization(options: CreateActiveOrganizationOptions): ActiveOrganization;
|
|
593
649
|
|
|
594
|
-
export {
|
|
650
|
+
export { createNotAMemberRefusalNotice as $, type AuthClient as A, NoActiveOrganizationError as B, type CachedActiveOrganization as C, NotAMemberError as D, type NotAMemberRefusalNotice as E, ORGANIZATION_RESOLUTION_TIMEOUT_MS as F, type OrganizationBootstrapGate as G, type HeldOrganizationStore as H, type OrganizationLapse as I, type OrganizationMembership as J, type OrganizationRefusal as K, LINK_ORGANIZATION_QUERY_KEY as L, type OrganizationRung as M, NOT_A_MEMBER_RECHECK_GAP_MS as N, ORGANIZATION_ENTITY_TYPE as O, type OrganizationsDb as P, type ParentOrganization as Q, type ReadMembershipsOptions as R, type SessionSourceLike as S, type ResolveWithLinkInput as T, type UserCredentialsPort as U, type ResolvedOrganization as V, type ResolvedWithLink as W, adminSeatReplacesUserContext as X, classifyLinkOrganizationValue as Y, classifyOrganizationRefusal as Z, createActiveOrganization as _, SignInError as a, createOrganizationBootstrapGate as a0, decideLinkOrganization as a1, isNotAMemberRefusal as a2, linkOrganizationMalformedMessage as a3, linkOrganizationNotAMemberMessage as a4, linkOrganizationOfferMessage as a5, linkOrganizationSwitchAnnouncement as a6, memoryHeldOrganization as a7, oldestMembership as a8, orderOrganizations as a9, organizationMatches as aa, readAccountOrganizationChoices as ab, readLinkOrganizationParam as ac, readMemberships as ad, readOrganizationFavourites as ae, resolveActiveOrganization as af, resolveActiveOrganizationLazily as ag, resolveAdminSeat as ah, resolveChildOrganizationId as ai, resolveOrganizationWithLink as aj, setOrganizationFavourite as ak, storageHeldOrganization as al, toAccountOrganizationChoices as am, writeLastActiveOrganization as an, writeStartupOrganization as ao, getSession as b, signInWithOAuth as c, signInWithPassword as d, signOut as e, userCredentials as f, getPerson as g, type AccountOrganizationChoices as h, invalidatePerson as i, type ActiveOrganization as j, type ActiveOrganizationSnapshot as k, type ActiveOrganizationStatus as l, type AdminSeat as m, type AdminSeatInput as n, onSessionChange as o, type ArchiveFilter as p, type CreateActiveOrganizationOptions as q, refreshSession as r, sessionCredentials as s, type LadderInput as t, updatePerson as u, type LinkOrganizationDecision as v, type LinkOrganizationDecisionInput as w, type LinkOrganizationMembership as x, type LinkOrganizationMessages as y, type LinkOrganizationParam as z };
|
package/dist/guest.d.cts
CHANGED
|
@@ -137,7 +137,7 @@ declare const DEFAULT_GUEST_CARRIER_COOKIE = "matrx_guest_visitor";
|
|
|
137
137
|
declare function createGuestHandover(options: GuestHandoverOptions): GuestHandover;
|
|
138
138
|
/**
|
|
139
139
|
* Cookie options for the guest's browser client: its OWN cookie name, never the app singleton's. Pass as
|
|
140
|
-
*
|
|
140
|
+
* the browser client constructor with `isSingleton: false` and `cookieOptions: guestClientCookieOptions(name)`.
|
|
141
141
|
*/
|
|
142
142
|
declare function guestClientCookieOptions(cookieName: string, secure?: boolean): {
|
|
143
143
|
name: string;
|
package/dist/guest.d.ts
CHANGED
|
@@ -137,7 +137,7 @@ declare const DEFAULT_GUEST_CARRIER_COOKIE = "matrx_guest_visitor";
|
|
|
137
137
|
declare function createGuestHandover(options: GuestHandoverOptions): GuestHandover;
|
|
138
138
|
/**
|
|
139
139
|
* Cookie options for the guest's browser client: its OWN cookie name, never the app singleton's. Pass as
|
|
140
|
-
*
|
|
140
|
+
* the browser client constructor with `isSingleton: false` and `cookieOptions: guestClientCookieOptions(name)`.
|
|
141
141
|
*/
|
|
142
142
|
declare function guestClientCookieOptions(cookieName: string, secure?: boolean): {
|
|
143
143
|
name: string;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { H as HeldOrganizationStore, A as AuthClient } from './active-organization-
|
|
2
|
-
export { S as SessionSourceLike, a as SignInError, U as UserCredentialsPort, g as getPerson, b as getSession, i as invalidatePerson, o as onSessionChange, r as refreshSession, s as sessionCredentials, c as signInWithOAuth, d as signInWithPassword, e as signOut, u as updatePerson, f as userCredentials } from './active-organization-
|
|
1
|
+
import { H as HeldOrganizationStore, A as AuthClient } from './active-organization-Cxm6GDIy.cjs';
|
|
2
|
+
export { S as SessionSourceLike, a as SignInError, U as UserCredentialsPort, g as getPerson, b as getSession, i as invalidatePerson, o as onSessionChange, r as refreshSession, s as sessionCredentials, c as signInWithOAuth, d as signInWithPassword, e as signOut, u as updatePerson, f as userCredentials } from './active-organization-Cxm6GDIy.cjs';
|
|
3
3
|
import { S as SessionStorage, M as MatrxOAuthClient } from './matrx-oauth-_Ji8X5Wm.cjs';
|
|
4
4
|
export { C as ChromeStorageAreaLike, a as StorageUnavailableError, b as browserStorage, e as extensionStorage, k as keyValueStorage, m as memoryStorage } from './matrx-oauth-_Ji8X5Wm.cjs';
|
|
5
5
|
import { SupabaseClient, User } from '@supabase/supabase-js';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { H as HeldOrganizationStore, A as AuthClient } from './active-organization-
|
|
2
|
-
export { S as SessionSourceLike, a as SignInError, U as UserCredentialsPort, g as getPerson, b as getSession, i as invalidatePerson, o as onSessionChange, r as refreshSession, s as sessionCredentials, c as signInWithOAuth, d as signInWithPassword, e as signOut, u as updatePerson, f as userCredentials } from './active-organization-
|
|
1
|
+
import { H as HeldOrganizationStore, A as AuthClient } from './active-organization-Cxm6GDIy.js';
|
|
2
|
+
export { S as SessionSourceLike, a as SignInError, U as UserCredentialsPort, g as getPerson, b as getSession, i as invalidatePerson, o as onSessionChange, r as refreshSession, s as sessionCredentials, c as signInWithOAuth, d as signInWithPassword, e as signOut, u as updatePerson, f as userCredentials } from './active-organization-Cxm6GDIy.js';
|
|
3
3
|
import { S as SessionStorage, M as MatrxOAuthClient } from './matrx-oauth-_Ji8X5Wm.js';
|
|
4
4
|
export { C as ChromeStorageAreaLike, a as StorageUnavailableError, b as browserStorage, e as extensionStorage, k as keyValueStorage, m as memoryStorage } from './matrx-oauth-_Ji8X5Wm.js';
|
|
5
5
|
import { SupabaseClient, User } from '@supabase/supabase-js';
|
package/dist/organizations.cjs
CHANGED
|
@@ -523,7 +523,9 @@ var IDLE = Object.freeze({
|
|
|
523
523
|
organization: null,
|
|
524
524
|
memberships: Object.freeze([]),
|
|
525
525
|
rung: null,
|
|
526
|
-
error: null
|
|
526
|
+
error: null,
|
|
527
|
+
favourites: Object.freeze([]),
|
|
528
|
+
startupOrganizationId: null
|
|
527
529
|
});
|
|
528
530
|
function createActiveOrganization(options) {
|
|
529
531
|
const { client } = options;
|
|
@@ -534,8 +536,11 @@ function createActiveOrganization(options) {
|
|
|
534
536
|
let generation = 0;
|
|
535
537
|
let waiters = [];
|
|
536
538
|
let disposed = false;
|
|
539
|
+
let lastSettled = IDLE;
|
|
540
|
+
let lastReadAt = Number.NEGATIVE_INFINITY;
|
|
537
541
|
const publish = (next) => {
|
|
538
542
|
current = Object.freeze(next);
|
|
543
|
+
if (current.status !== "resolving") lastSettled = current;
|
|
539
544
|
if (current.status !== "resolving" && current.status !== "idle") {
|
|
540
545
|
options.bootstrapGate?.markResolved();
|
|
541
546
|
const ready = waiters;
|
|
@@ -545,6 +550,7 @@ function createActiveOrganization(options) {
|
|
|
545
550
|
for (const l of [...listeners]) l();
|
|
546
551
|
};
|
|
547
552
|
const run = async (gen) => {
|
|
553
|
+
const prior = current.status === "resolving" ? lastSettled : current;
|
|
548
554
|
publish({ ...current, status: "resolving", error: null });
|
|
549
555
|
try {
|
|
550
556
|
const person = await getPerson(client);
|
|
@@ -553,8 +559,24 @@ function createActiveOrganization(options) {
|
|
|
553
559
|
publish({ ...IDLE, status: "none", error: "signed-out" });
|
|
554
560
|
return current;
|
|
555
561
|
}
|
|
556
|
-
const
|
|
562
|
+
const rawMemberships = await readMemberships(client, person.id);
|
|
557
563
|
if (gen !== generation) return current;
|
|
564
|
+
let favourites = current.favourites;
|
|
565
|
+
let startupOrganizationId = current.startupOrganizationId;
|
|
566
|
+
let account = null;
|
|
567
|
+
if (options.accountChoices) {
|
|
568
|
+
const [fav, acct] = await Promise.all([
|
|
569
|
+
readOrganizationFavourites(client).catch(() => favourites),
|
|
570
|
+
readAccountOrganizationChoices(client, person.id)
|
|
571
|
+
]);
|
|
572
|
+
favourites = fav;
|
|
573
|
+
account = acct;
|
|
574
|
+
startupOrganizationId = acct.startupOrganizationId;
|
|
575
|
+
if (gen !== generation) return current;
|
|
576
|
+
}
|
|
577
|
+
const memberships = options.accountChoices ? orderOrganizations(rawMemberships, favourites) : rawMemberships;
|
|
578
|
+
const readAccount = () => account ? Promise.resolve(account) : readAccountOrganizationChoices(client, person.id);
|
|
579
|
+
const before = prior;
|
|
558
580
|
const linkOptions = options.link;
|
|
559
581
|
let resolved;
|
|
560
582
|
let linkDecision = null;
|
|
@@ -562,7 +584,7 @@ function createActiveOrganization(options) {
|
|
|
562
584
|
const r = await resolveOrganizationWithLink({
|
|
563
585
|
memberships,
|
|
564
586
|
held: await held.read(person.id),
|
|
565
|
-
readAccount
|
|
587
|
+
readAccount,
|
|
566
588
|
link: linkOptions.source() ?? null,
|
|
567
589
|
switchWhenALinkAsks: linkOptions.switchWhenALinkAsks?.(),
|
|
568
590
|
signedInAs: linkOptions.signedInAs?.(),
|
|
@@ -577,7 +599,7 @@ function createActiveOrganization(options) {
|
|
|
577
599
|
resolved = await resolveActiveOrganizationLazily({
|
|
578
600
|
memberships,
|
|
579
601
|
held: heldId,
|
|
580
|
-
readAccount
|
|
602
|
+
readAccount
|
|
581
603
|
});
|
|
582
604
|
}
|
|
583
605
|
if (gen !== generation) return current;
|
|
@@ -585,7 +607,9 @@ function createActiveOrganization(options) {
|
|
|
585
607
|
publish({ ...IDLE, status: "none", userId: person.id, error: "no-memberships" });
|
|
586
608
|
return current;
|
|
587
609
|
}
|
|
588
|
-
await held.write(person.id, resolved.organization.id);
|
|
610
|
+
if (options.holdResolved ?? true) await held.write(person.id, resolved.organization.id);
|
|
611
|
+
const wasOrg = before.status === "ready" || before.status === "unchecked" ? before.organization : null;
|
|
612
|
+
const lapsed = wasOrg && wasOrg.id !== resolved.organization.id && !memberships.some((m) => m.id === wasOrg.id) ? wasOrg : null;
|
|
589
613
|
if (linkDecision && linkDecision.kind !== "no-link") options.link?.onDecision?.(linkDecision);
|
|
590
614
|
publish({
|
|
591
615
|
status: "ready",
|
|
@@ -594,12 +618,51 @@ function createActiveOrganization(options) {
|
|
|
594
618
|
organization: resolved.organization,
|
|
595
619
|
memberships,
|
|
596
620
|
rung: resolved.rung,
|
|
597
|
-
error: null
|
|
621
|
+
error: null,
|
|
622
|
+
favourites,
|
|
623
|
+
startupOrganizationId
|
|
598
624
|
});
|
|
625
|
+
if (lapsed) options.onLapse?.({ was: lapsed, now: resolved.organization });
|
|
626
|
+
lastReadAt = Date.now();
|
|
627
|
+
if (options.cached?.write) {
|
|
628
|
+
try {
|
|
629
|
+
await options.cached.write(person.id, { memberships, organizationId: resolved.organization.id, favourites, startupOrganizationId });
|
|
630
|
+
} catch {
|
|
631
|
+
}
|
|
632
|
+
}
|
|
599
633
|
return current;
|
|
600
634
|
} catch (e) {
|
|
601
635
|
if (gen !== generation) return current;
|
|
602
|
-
|
|
636
|
+
const message = e?.message ?? String(e);
|
|
637
|
+
if (options.cached && (prior.status === "ready" || prior.status === "unchecked") && prior.organization) {
|
|
638
|
+
publish({ ...prior, status: "unchecked", error: message });
|
|
639
|
+
return current;
|
|
640
|
+
}
|
|
641
|
+
const cachedUser = options.cached?.userId();
|
|
642
|
+
if (options.cached && cachedUser) {
|
|
643
|
+
try {
|
|
644
|
+
const answer = await options.cached.read(cachedUser);
|
|
645
|
+
if (gen !== generation) return current;
|
|
646
|
+
const wanted = await held.read(cachedUser) ?? answer?.organizationId ?? null;
|
|
647
|
+
const organization = answer?.memberships.find((m) => m.id === wanted) ?? null;
|
|
648
|
+
if (answer && organization) {
|
|
649
|
+
publish({
|
|
650
|
+
status: "unchecked",
|
|
651
|
+
userId: cachedUser,
|
|
652
|
+
organizationId: organization.id,
|
|
653
|
+
organization,
|
|
654
|
+
memberships: orderOrganizations(answer.memberships, answer.favourites),
|
|
655
|
+
rung: "held",
|
|
656
|
+
error: message,
|
|
657
|
+
favourites: answer.favourites,
|
|
658
|
+
startupOrganizationId: answer.startupOrganizationId
|
|
659
|
+
});
|
|
660
|
+
return current;
|
|
661
|
+
}
|
|
662
|
+
} catch {
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
publish({ ...current, status: "error", error: message });
|
|
603
666
|
return current;
|
|
604
667
|
}
|
|
605
668
|
};
|
|
@@ -643,11 +706,34 @@ function createActiveOrganization(options) {
|
|
|
643
706
|
};
|
|
644
707
|
},
|
|
645
708
|
load,
|
|
646
|
-
current: () => current.status === "ready" ? seatFor(current) ?? current.organizationId : null,
|
|
709
|
+
current: () => current.status === "ready" || current.status === "unchecked" ? seatFor(current) ?? current.organizationId : null,
|
|
710
|
+
refresh(refreshOptions) {
|
|
711
|
+
const gap = options.refreshGapMs ?? 0;
|
|
712
|
+
if (!refreshOptions?.force && gap > 0 && current.status !== "idle" && Date.now() - lastReadAt < gap) return Promise.resolve(current);
|
|
713
|
+
if (inflight) return inflight;
|
|
714
|
+
return load();
|
|
715
|
+
},
|
|
716
|
+
async setStartup(organizationId) {
|
|
717
|
+
const snap = await settled();
|
|
718
|
+
await writeStartupOrganization(client, organizationId);
|
|
719
|
+
publish({ ...snap, startupOrganizationId: organizationId });
|
|
720
|
+
},
|
|
721
|
+
async setFavourite(organizationId, favourite) {
|
|
722
|
+
const snap = await settled();
|
|
723
|
+
const before = snap.favourites;
|
|
724
|
+
const next = favourite ? [...before.filter((f) => f !== organizationId), organizationId] : before.filter((f) => f !== organizationId);
|
|
725
|
+
publish({ ...snap, favourites: next, memberships: orderOrganizations(snap.memberships, next) });
|
|
726
|
+
try {
|
|
727
|
+
await setOrganizationFavourite(client, organizationId, favourite);
|
|
728
|
+
} catch (e) {
|
|
729
|
+
publish({ ...current, favourites: before, memberships: orderOrganizations(current.memberships, before) });
|
|
730
|
+
throw e;
|
|
731
|
+
}
|
|
732
|
+
},
|
|
647
733
|
async ensureOrgId(recordOrganizationId) {
|
|
648
734
|
if (recordOrganizationId) return recordOrganizationId;
|
|
649
735
|
const snap = await settled();
|
|
650
|
-
if (snap.status === "ready" && snap.organizationId) return seatFor(snap) ?? snap.organizationId;
|
|
736
|
+
if ((snap.status === "ready" || snap.status === "unchecked") && snap.organizationId) return seatFor(snap) ?? snap.organizationId;
|
|
651
737
|
if (snap.status === "error") throw new NoActiveOrganizationError("unreadable", snap.error ?? void 0);
|
|
652
738
|
throw new NoActiveOrganizationError(snap.error === "signed-out" ? "signed-out" : "no-memberships");
|
|
653
739
|
},
|
package/dist/organizations.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { h as AccountOrganizationChoices, j as ActiveOrganization, k as ActiveOrganizationSnapshot, l as ActiveOrganizationStatus, m as AdminSeat, n as AdminSeatInput, p as ArchiveFilter, C as CreateActiveOrganizationOptions, H as HeldOrganizationStore, L as LINK_ORGANIZATION_QUERY_KEY,
|
|
1
|
+
export { h as AccountOrganizationChoices, j as ActiveOrganization, k as ActiveOrganizationSnapshot, l as ActiveOrganizationStatus, m as AdminSeat, n as AdminSeatInput, p as ArchiveFilter, C as CachedActiveOrganization, q as CreateActiveOrganizationOptions, H as HeldOrganizationStore, L as LINK_ORGANIZATION_QUERY_KEY, t as LadderInput, v as LinkOrganizationDecision, w as LinkOrganizationDecisionInput, x as LinkOrganizationMembership, y as LinkOrganizationMessages, z as LinkOrganizationParam, N as NOT_A_MEMBER_RECHECK_GAP_MS, B as NoActiveOrganizationError, D as NotAMemberError, E as NotAMemberRefusalNotice, O as ORGANIZATION_ENTITY_TYPE, F as ORGANIZATION_RESOLUTION_TIMEOUT_MS, G as OrganizationBootstrapGate, I as OrganizationLapse, J as OrganizationMembership, K as OrganizationRefusal, M as OrganizationRung, P as OrganizationsDb, Q as ParentOrganization, R as ReadMembershipsOptions, T as ResolveWithLinkInput, V as ResolvedOrganization, W as ResolvedWithLink, X as adminSeatReplacesUserContext, Y as classifyLinkOrganizationValue, Z as classifyOrganizationRefusal, _ as createActiveOrganization, $ as createNotAMemberRefusalNotice, a0 as createOrganizationBootstrapGate, a1 as decideLinkOrganization, a2 as isNotAMemberRefusal, a3 as linkOrganizationMalformedMessage, a4 as linkOrganizationNotAMemberMessage, a5 as linkOrganizationOfferMessage, a6 as linkOrganizationSwitchAnnouncement, a7 as memoryHeldOrganization, a8 as oldestMembership, a9 as orderOrganizations, aa as organizationMatches, ab as readAccountOrganizationChoices, ac as readLinkOrganizationParam, ad as readMemberships, ae as readOrganizationFavourites, af as resolveActiveOrganization, ag as resolveActiveOrganizationLazily, ah as resolveAdminSeat, ai as resolveChildOrganizationId, aj as resolveOrganizationWithLink, ak as setOrganizationFavourite, al as storageHeldOrganization, am as toAccountOrganizationChoices, an as writeLastActiveOrganization, ao as writeStartupOrganization } from './active-organization-Cxm6GDIy.cjs';
|
|
2
2
|
import '@supabase/supabase-js';
|
|
3
3
|
|
|
4
4
|
/**
|
package/dist/organizations.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { h as AccountOrganizationChoices, j as ActiveOrganization, k as ActiveOrganizationSnapshot, l as ActiveOrganizationStatus, m as AdminSeat, n as AdminSeatInput, p as ArchiveFilter, C as CreateActiveOrganizationOptions, H as HeldOrganizationStore, L as LINK_ORGANIZATION_QUERY_KEY,
|
|
1
|
+
export { h as AccountOrganizationChoices, j as ActiveOrganization, k as ActiveOrganizationSnapshot, l as ActiveOrganizationStatus, m as AdminSeat, n as AdminSeatInput, p as ArchiveFilter, C as CachedActiveOrganization, q as CreateActiveOrganizationOptions, H as HeldOrganizationStore, L as LINK_ORGANIZATION_QUERY_KEY, t as LadderInput, v as LinkOrganizationDecision, w as LinkOrganizationDecisionInput, x as LinkOrganizationMembership, y as LinkOrganizationMessages, z as LinkOrganizationParam, N as NOT_A_MEMBER_RECHECK_GAP_MS, B as NoActiveOrganizationError, D as NotAMemberError, E as NotAMemberRefusalNotice, O as ORGANIZATION_ENTITY_TYPE, F as ORGANIZATION_RESOLUTION_TIMEOUT_MS, G as OrganizationBootstrapGate, I as OrganizationLapse, J as OrganizationMembership, K as OrganizationRefusal, M as OrganizationRung, P as OrganizationsDb, Q as ParentOrganization, R as ReadMembershipsOptions, T as ResolveWithLinkInput, V as ResolvedOrganization, W as ResolvedWithLink, X as adminSeatReplacesUserContext, Y as classifyLinkOrganizationValue, Z as classifyOrganizationRefusal, _ as createActiveOrganization, $ as createNotAMemberRefusalNotice, a0 as createOrganizationBootstrapGate, a1 as decideLinkOrganization, a2 as isNotAMemberRefusal, a3 as linkOrganizationMalformedMessage, a4 as linkOrganizationNotAMemberMessage, a5 as linkOrganizationOfferMessage, a6 as linkOrganizationSwitchAnnouncement, a7 as memoryHeldOrganization, a8 as oldestMembership, a9 as orderOrganizations, aa as organizationMatches, ab as readAccountOrganizationChoices, ac as readLinkOrganizationParam, ad as readMemberships, ae as readOrganizationFavourites, af as resolveActiveOrganization, ag as resolveActiveOrganizationLazily, ah as resolveAdminSeat, ai as resolveChildOrganizationId, aj as resolveOrganizationWithLink, ak as setOrganizationFavourite, al as storageHeldOrganization, am as toAccountOrganizationChoices, an as writeLastActiveOrganization, ao as writeStartupOrganization } from './active-organization-Cxm6GDIy.js';
|
|
2
2
|
import '@supabase/supabase-js';
|
|
3
3
|
|
|
4
4
|
/**
|
package/dist/organizations.js
CHANGED
|
@@ -449,7 +449,9 @@ var IDLE = Object.freeze({
|
|
|
449
449
|
organization: null,
|
|
450
450
|
memberships: Object.freeze([]),
|
|
451
451
|
rung: null,
|
|
452
|
-
error: null
|
|
452
|
+
error: null,
|
|
453
|
+
favourites: Object.freeze([]),
|
|
454
|
+
startupOrganizationId: null
|
|
453
455
|
});
|
|
454
456
|
function createActiveOrganization(options) {
|
|
455
457
|
const { client } = options;
|
|
@@ -460,8 +462,11 @@ function createActiveOrganization(options) {
|
|
|
460
462
|
let generation = 0;
|
|
461
463
|
let waiters = [];
|
|
462
464
|
let disposed = false;
|
|
465
|
+
let lastSettled = IDLE;
|
|
466
|
+
let lastReadAt = Number.NEGATIVE_INFINITY;
|
|
463
467
|
const publish = (next) => {
|
|
464
468
|
current = Object.freeze(next);
|
|
469
|
+
if (current.status !== "resolving") lastSettled = current;
|
|
465
470
|
if (current.status !== "resolving" && current.status !== "idle") {
|
|
466
471
|
options.bootstrapGate?.markResolved();
|
|
467
472
|
const ready = waiters;
|
|
@@ -471,6 +476,7 @@ function createActiveOrganization(options) {
|
|
|
471
476
|
for (const l of [...listeners]) l();
|
|
472
477
|
};
|
|
473
478
|
const run = async (gen) => {
|
|
479
|
+
const prior = current.status === "resolving" ? lastSettled : current;
|
|
474
480
|
publish({ ...current, status: "resolving", error: null });
|
|
475
481
|
try {
|
|
476
482
|
const person = await getPerson(client);
|
|
@@ -479,8 +485,24 @@ function createActiveOrganization(options) {
|
|
|
479
485
|
publish({ ...IDLE, status: "none", error: "signed-out" });
|
|
480
486
|
return current;
|
|
481
487
|
}
|
|
482
|
-
const
|
|
488
|
+
const rawMemberships = await readMemberships(client, person.id);
|
|
483
489
|
if (gen !== generation) return current;
|
|
490
|
+
let favourites = current.favourites;
|
|
491
|
+
let startupOrganizationId = current.startupOrganizationId;
|
|
492
|
+
let account = null;
|
|
493
|
+
if (options.accountChoices) {
|
|
494
|
+
const [fav, acct] = await Promise.all([
|
|
495
|
+
readOrganizationFavourites(client).catch(() => favourites),
|
|
496
|
+
readAccountOrganizationChoices(client, person.id)
|
|
497
|
+
]);
|
|
498
|
+
favourites = fav;
|
|
499
|
+
account = acct;
|
|
500
|
+
startupOrganizationId = acct.startupOrganizationId;
|
|
501
|
+
if (gen !== generation) return current;
|
|
502
|
+
}
|
|
503
|
+
const memberships = options.accountChoices ? orderOrganizations(rawMemberships, favourites) : rawMemberships;
|
|
504
|
+
const readAccount = () => account ? Promise.resolve(account) : readAccountOrganizationChoices(client, person.id);
|
|
505
|
+
const before = prior;
|
|
484
506
|
const linkOptions = options.link;
|
|
485
507
|
let resolved;
|
|
486
508
|
let linkDecision = null;
|
|
@@ -488,7 +510,7 @@ function createActiveOrganization(options) {
|
|
|
488
510
|
const r = await resolveOrganizationWithLink({
|
|
489
511
|
memberships,
|
|
490
512
|
held: await held.read(person.id),
|
|
491
|
-
readAccount
|
|
513
|
+
readAccount,
|
|
492
514
|
link: linkOptions.source() ?? null,
|
|
493
515
|
switchWhenALinkAsks: linkOptions.switchWhenALinkAsks?.(),
|
|
494
516
|
signedInAs: linkOptions.signedInAs?.(),
|
|
@@ -503,7 +525,7 @@ function createActiveOrganization(options) {
|
|
|
503
525
|
resolved = await resolveActiveOrganizationLazily({
|
|
504
526
|
memberships,
|
|
505
527
|
held: heldId,
|
|
506
|
-
readAccount
|
|
528
|
+
readAccount
|
|
507
529
|
});
|
|
508
530
|
}
|
|
509
531
|
if (gen !== generation) return current;
|
|
@@ -511,7 +533,9 @@ function createActiveOrganization(options) {
|
|
|
511
533
|
publish({ ...IDLE, status: "none", userId: person.id, error: "no-memberships" });
|
|
512
534
|
return current;
|
|
513
535
|
}
|
|
514
|
-
await held.write(person.id, resolved.organization.id);
|
|
536
|
+
if (options.holdResolved ?? true) await held.write(person.id, resolved.organization.id);
|
|
537
|
+
const wasOrg = before.status === "ready" || before.status === "unchecked" ? before.organization : null;
|
|
538
|
+
const lapsed = wasOrg && wasOrg.id !== resolved.organization.id && !memberships.some((m) => m.id === wasOrg.id) ? wasOrg : null;
|
|
515
539
|
if (linkDecision && linkDecision.kind !== "no-link") options.link?.onDecision?.(linkDecision);
|
|
516
540
|
publish({
|
|
517
541
|
status: "ready",
|
|
@@ -520,12 +544,51 @@ function createActiveOrganization(options) {
|
|
|
520
544
|
organization: resolved.organization,
|
|
521
545
|
memberships,
|
|
522
546
|
rung: resolved.rung,
|
|
523
|
-
error: null
|
|
547
|
+
error: null,
|
|
548
|
+
favourites,
|
|
549
|
+
startupOrganizationId
|
|
524
550
|
});
|
|
551
|
+
if (lapsed) options.onLapse?.({ was: lapsed, now: resolved.organization });
|
|
552
|
+
lastReadAt = Date.now();
|
|
553
|
+
if (options.cached?.write) {
|
|
554
|
+
try {
|
|
555
|
+
await options.cached.write(person.id, { memberships, organizationId: resolved.organization.id, favourites, startupOrganizationId });
|
|
556
|
+
} catch {
|
|
557
|
+
}
|
|
558
|
+
}
|
|
525
559
|
return current;
|
|
526
560
|
} catch (e) {
|
|
527
561
|
if (gen !== generation) return current;
|
|
528
|
-
|
|
562
|
+
const message = e?.message ?? String(e);
|
|
563
|
+
if (options.cached && (prior.status === "ready" || prior.status === "unchecked") && prior.organization) {
|
|
564
|
+
publish({ ...prior, status: "unchecked", error: message });
|
|
565
|
+
return current;
|
|
566
|
+
}
|
|
567
|
+
const cachedUser = options.cached?.userId();
|
|
568
|
+
if (options.cached && cachedUser) {
|
|
569
|
+
try {
|
|
570
|
+
const answer = await options.cached.read(cachedUser);
|
|
571
|
+
if (gen !== generation) return current;
|
|
572
|
+
const wanted = await held.read(cachedUser) ?? answer?.organizationId ?? null;
|
|
573
|
+
const organization = answer?.memberships.find((m) => m.id === wanted) ?? null;
|
|
574
|
+
if (answer && organization) {
|
|
575
|
+
publish({
|
|
576
|
+
status: "unchecked",
|
|
577
|
+
userId: cachedUser,
|
|
578
|
+
organizationId: organization.id,
|
|
579
|
+
organization,
|
|
580
|
+
memberships: orderOrganizations(answer.memberships, answer.favourites),
|
|
581
|
+
rung: "held",
|
|
582
|
+
error: message,
|
|
583
|
+
favourites: answer.favourites,
|
|
584
|
+
startupOrganizationId: answer.startupOrganizationId
|
|
585
|
+
});
|
|
586
|
+
return current;
|
|
587
|
+
}
|
|
588
|
+
} catch {
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
publish({ ...current, status: "error", error: message });
|
|
529
592
|
return current;
|
|
530
593
|
}
|
|
531
594
|
};
|
|
@@ -569,11 +632,34 @@ function createActiveOrganization(options) {
|
|
|
569
632
|
};
|
|
570
633
|
},
|
|
571
634
|
load,
|
|
572
|
-
current: () => current.status === "ready" ? seatFor(current) ?? current.organizationId : null,
|
|
635
|
+
current: () => current.status === "ready" || current.status === "unchecked" ? seatFor(current) ?? current.organizationId : null,
|
|
636
|
+
refresh(refreshOptions) {
|
|
637
|
+
const gap = options.refreshGapMs ?? 0;
|
|
638
|
+
if (!refreshOptions?.force && gap > 0 && current.status !== "idle" && Date.now() - lastReadAt < gap) return Promise.resolve(current);
|
|
639
|
+
if (inflight) return inflight;
|
|
640
|
+
return load();
|
|
641
|
+
},
|
|
642
|
+
async setStartup(organizationId) {
|
|
643
|
+
const snap = await settled();
|
|
644
|
+
await writeStartupOrganization(client, organizationId);
|
|
645
|
+
publish({ ...snap, startupOrganizationId: organizationId });
|
|
646
|
+
},
|
|
647
|
+
async setFavourite(organizationId, favourite) {
|
|
648
|
+
const snap = await settled();
|
|
649
|
+
const before = snap.favourites;
|
|
650
|
+
const next = favourite ? [...before.filter((f) => f !== organizationId), organizationId] : before.filter((f) => f !== organizationId);
|
|
651
|
+
publish({ ...snap, favourites: next, memberships: orderOrganizations(snap.memberships, next) });
|
|
652
|
+
try {
|
|
653
|
+
await setOrganizationFavourite(client, organizationId, favourite);
|
|
654
|
+
} catch (e) {
|
|
655
|
+
publish({ ...current, favourites: before, memberships: orderOrganizations(current.memberships, before) });
|
|
656
|
+
throw e;
|
|
657
|
+
}
|
|
658
|
+
},
|
|
573
659
|
async ensureOrgId(recordOrganizationId) {
|
|
574
660
|
if (recordOrganizationId) return recordOrganizationId;
|
|
575
661
|
const snap = await settled();
|
|
576
|
-
if (snap.status === "ready" && snap.organizationId) return seatFor(snap) ?? snap.organizationId;
|
|
662
|
+
if ((snap.status === "ready" || snap.status === "unchecked") && snap.organizationId) return seatFor(snap) ?? snap.organizationId;
|
|
577
663
|
if (snap.status === "error") throw new NoActiveOrganizationError("unreadable", snap.error ?? void 0);
|
|
578
664
|
throw new NoActiveOrganizationError(snap.error === "signed-out" ? "signed-out" : "no-memberships");
|
|
579
665
|
},
|
package/dist/react.cjs
CHANGED
|
@@ -182,7 +182,9 @@ var SERVER_ACTIVE = Object.freeze({
|
|
|
182
182
|
organization: null,
|
|
183
183
|
memberships: Object.freeze([]),
|
|
184
184
|
rung: null,
|
|
185
|
-
error: null
|
|
185
|
+
error: null,
|
|
186
|
+
favourites: Object.freeze([]),
|
|
187
|
+
startupOrganizationId: null
|
|
186
188
|
});
|
|
187
189
|
function useActiveOrganization(store) {
|
|
188
190
|
return (0, import_react.useSyncExternalStore)(store.subscribe, store.snapshot, () => SERVER_ACTIVE);
|
package/dist/react.d.cts
CHANGED
|
@@ -90,7 +90,11 @@ type OrganizationRung = "held" | "last-active" | "startup" | "first";
|
|
|
90
90
|
* the last person's organization, and a sign-out resets the store.
|
|
91
91
|
*/
|
|
92
92
|
|
|
93
|
-
|
|
93
|
+
/**
|
|
94
|
+
* `unchecked`: the memberships could not be read, and the answer shown is the host's cached last answer
|
|
95
|
+
* (`options.cached`), not verified against the database. Only ever produced when `cached` is given.
|
|
96
|
+
*/
|
|
97
|
+
type ActiveOrganizationStatus = "idle" | "resolving" | "ready" | "none" | "error" | "unchecked";
|
|
94
98
|
interface ActiveOrganizationSnapshot {
|
|
95
99
|
readonly status: ActiveOrganizationStatus;
|
|
96
100
|
readonly userId: string | null;
|
|
@@ -100,6 +104,13 @@ interface ActiveOrganizationSnapshot {
|
|
|
100
104
|
/** Which ladder rung answered (or "switched" after a person's click). */
|
|
101
105
|
readonly rung: OrganizationRung | "switched" | "link" | null;
|
|
102
106
|
readonly error: string | null;
|
|
107
|
+
/**
|
|
108
|
+
* The account's favourite organization ids. Read only when `options.accountChoices` is on; `[]`
|
|
109
|
+
* otherwise. When on, `memberships` is ordered favourites, then own, then the rest.
|
|
110
|
+
*/
|
|
111
|
+
readonly favourites: readonly string[];
|
|
112
|
+
/** The account's start-up organization setting. Read only when `options.accountChoices` is on; null otherwise. */
|
|
113
|
+
readonly startupOrganizationId: string | null;
|
|
103
114
|
}
|
|
104
115
|
interface ActiveOrganization {
|
|
105
116
|
snapshot(): ActiveOrganizationSnapshot;
|
|
@@ -109,6 +120,14 @@ interface ActiveOrganization {
|
|
|
109
120
|
/** The active organization id now, or null while resolving / with none. Sync (api's getter). */
|
|
110
121
|
current(): string | null;
|
|
111
122
|
/** THE HELD REQUEST: the record's own organization, else the active one once resolved. */
|
|
123
|
+
/** Re-read, unless `options.refreshGapMs` has not elapsed since the last read (`force` always reads). */
|
|
124
|
+
refresh(options?: {
|
|
125
|
+
readonly force?: boolean;
|
|
126
|
+
}): Promise<ActiveOrganizationSnapshot>;
|
|
127
|
+
/** Save the account's start-up organization (null: the first). Needs `accountChoices`; throws on refusal. */
|
|
128
|
+
setStartup(organizationId: string | null): Promise<void>;
|
|
129
|
+
/** Add or remove a favourite and re-order. Optimistic; rolled back and rethrown if the save fails. */
|
|
130
|
+
setFavourite(organizationId: string, favourite: boolean): Promise<void>;
|
|
112
131
|
ensureOrgId(recordOrganizationId?: string | null): Promise<string>;
|
|
113
132
|
/** A person's click: switch, remember for this tab, save as last active for the next load. */
|
|
114
133
|
switchTo(organizationId: string): Promise<void>;
|
package/dist/react.d.ts
CHANGED
|
@@ -90,7 +90,11 @@ type OrganizationRung = "held" | "last-active" | "startup" | "first";
|
|
|
90
90
|
* the last person's organization, and a sign-out resets the store.
|
|
91
91
|
*/
|
|
92
92
|
|
|
93
|
-
|
|
93
|
+
/**
|
|
94
|
+
* `unchecked`: the memberships could not be read, and the answer shown is the host's cached last answer
|
|
95
|
+
* (`options.cached`), not verified against the database. Only ever produced when `cached` is given.
|
|
96
|
+
*/
|
|
97
|
+
type ActiveOrganizationStatus = "idle" | "resolving" | "ready" | "none" | "error" | "unchecked";
|
|
94
98
|
interface ActiveOrganizationSnapshot {
|
|
95
99
|
readonly status: ActiveOrganizationStatus;
|
|
96
100
|
readonly userId: string | null;
|
|
@@ -100,6 +104,13 @@ interface ActiveOrganizationSnapshot {
|
|
|
100
104
|
/** Which ladder rung answered (or "switched" after a person's click). */
|
|
101
105
|
readonly rung: OrganizationRung | "switched" | "link" | null;
|
|
102
106
|
readonly error: string | null;
|
|
107
|
+
/**
|
|
108
|
+
* The account's favourite organization ids. Read only when `options.accountChoices` is on; `[]`
|
|
109
|
+
* otherwise. When on, `memberships` is ordered favourites, then own, then the rest.
|
|
110
|
+
*/
|
|
111
|
+
readonly favourites: readonly string[];
|
|
112
|
+
/** The account's start-up organization setting. Read only when `options.accountChoices` is on; null otherwise. */
|
|
113
|
+
readonly startupOrganizationId: string | null;
|
|
103
114
|
}
|
|
104
115
|
interface ActiveOrganization {
|
|
105
116
|
snapshot(): ActiveOrganizationSnapshot;
|
|
@@ -109,6 +120,14 @@ interface ActiveOrganization {
|
|
|
109
120
|
/** The active organization id now, or null while resolving / with none. Sync (api's getter). */
|
|
110
121
|
current(): string | null;
|
|
111
122
|
/** THE HELD REQUEST: the record's own organization, else the active one once resolved. */
|
|
123
|
+
/** Re-read, unless `options.refreshGapMs` has not elapsed since the last read (`force` always reads). */
|
|
124
|
+
refresh(options?: {
|
|
125
|
+
readonly force?: boolean;
|
|
126
|
+
}): Promise<ActiveOrganizationSnapshot>;
|
|
127
|
+
/** Save the account's start-up organization (null: the first). Needs `accountChoices`; throws on refusal. */
|
|
128
|
+
setStartup(organizationId: string | null): Promise<void>;
|
|
129
|
+
/** Add or remove a favourite and re-order. Optimistic; rolled back and rethrown if the save fails. */
|
|
130
|
+
setFavourite(organizationId: string, favourite: boolean): Promise<void>;
|
|
112
131
|
ensureOrgId(recordOrganizationId?: string | null): Promise<string>;
|
|
113
132
|
/** A person's click: switch, remember for this tab, save as last active for the next load. */
|
|
114
133
|
switchTo(organizationId: string): Promise<void>;
|
package/dist/react.js
CHANGED
|
@@ -157,7 +157,9 @@ var SERVER_ACTIVE = Object.freeze({
|
|
|
157
157
|
organization: null,
|
|
158
158
|
memberships: Object.freeze([]),
|
|
159
159
|
rung: null,
|
|
160
|
-
error: null
|
|
160
|
+
error: null,
|
|
161
|
+
favourites: Object.freeze([]),
|
|
162
|
+
startupOrganizationId: null
|
|
161
163
|
});
|
|
162
164
|
function useActiveOrganization(store) {
|
|
163
165
|
return useSyncExternalStore(store.subscribe, store.snapshot, () => SERVER_ACTIVE);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-matrx/auth",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "AI Matrx sign-in, session and organizations for every app: sign in/out, session storage and refresh per platform (browser, Next.js, extension, desktop, Node), the current person, their organizations and favourites, the one active organization (held requests, never a list filter), and the credentials @ai-matrx/data and @ai-matrx/api read.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -211,5 +211,5 @@
|
|
|
211
211
|
"test": "vitest run",
|
|
212
212
|
"verify:tarball": "node ./scripts/verify-tarball.mjs"
|
|
213
213
|
},
|
|
214
|
-
"gitHead": "
|
|
214
|
+
"gitHead": "f9020cf33c7ac2b8b854856131eb0b1268eb220d"
|
|
215
215
|
}
|