@oxyhq/core 7.0.0 → 7.1.1
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/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/index.js +17 -2
- package/dist/cjs/session/SessionClient.js +181 -10
- package/dist/cjs/session/accountDialogController.js +626 -0
- package/dist/cjs/session/accountProjection.js +131 -0
- package/dist/cjs/session/createSessionClient.js +9 -2
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/index.js +11 -0
- package/dist/esm/session/SessionClient.js +181 -10
- package/dist/esm/session/accountDialogController.js +621 -0
- package/dist/esm/session/accountProjection.js +127 -0
- package/dist/esm/session/createSessionClient.js +9 -2
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/index.d.ts +4 -0
- package/dist/types/session/SessionClient.d.ts +51 -0
- package/dist/types/session/accountDialogController.d.ts +273 -0
- package/dist/types/session/accountProjection.d.ts +142 -0
- package/dist/types/session/createSessionClient.d.ts +9 -2
- package/package.json +1 -1
- package/src/index.ts +31 -0
- package/src/session/SessionClient.ts +201 -11
- package/src/session/__tests__/SessionClient.signedOut.test.ts +224 -0
- package/src/session/__tests__/accountDialogController.test.ts +592 -0
- package/src/session/__tests__/accountProjection.test.ts +181 -0
- package/src/session/accountDialogController.ts +769 -0
- package/src/session/accountProjection.ts +263 -0
- package/src/session/createSessionClient.ts +9 -2
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Unified account-list projection — THE single source of truth.
|
|
4
|
+
*
|
|
5
|
+
* Produces the flat `SwitchableAccount[]` every account chooser renders, by
|
|
6
|
+
* merging the device's server-authoritative session set (`DeviceSessionState`
|
|
7
|
+
* from {@link SessionClient}) with the caller's account graph (`AccountNode[]`
|
|
8
|
+
* from `oxyServices.listAccounts()`), deduped by `accountId`. This lives in
|
|
9
|
+
* `@oxyhq/core` so `@oxyhq/services` (RN) and `@oxyhq/auth` (web) — and
|
|
10
|
+
* `auth.oxy.so` — all render the SAME list from the SAME logic and cannot
|
|
11
|
+
* diverge.
|
|
12
|
+
*
|
|
13
|
+
* Pure and I/O-free: the caller resolves per-account profiles via
|
|
14
|
+
* `oxyServices.getUsersByIds(...)` and passes them in as `profilesById`, and
|
|
15
|
+
* binds `resolveAvatarUrl` to `oxyServices.getFileDownloadUrl`. This is the same
|
|
16
|
+
* split the former `@oxyhq/services` `buildSwitchableAccounts` used — hoisted
|
|
17
|
+
* into core, keyed directly on `DeviceSessionState` (whose `activeAccountId` is
|
|
18
|
+
* atomic, so no cross-call current-row reconciliation is needed).
|
|
19
|
+
*/
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.projectSwitchableAccounts = projectSwitchableAccounts;
|
|
22
|
+
exports.switchableAccountIds = switchableAccountIds;
|
|
23
|
+
const accountUtils_1 = require("../utils/accountUtils");
|
|
24
|
+
/**
|
|
25
|
+
* Pure union of device sign-ins and account-graph nodes into the flat
|
|
26
|
+
* {@link SwitchableAccount}[] every switcher renders.
|
|
27
|
+
*
|
|
28
|
+
* Order: device rows first (in `state.accounts` order, active flagged), then
|
|
29
|
+
* graph-only rows (in graph order). An account present as BOTH a device session
|
|
30
|
+
* and a graph node is deduped into ONE device row enriched with the graph
|
|
31
|
+
* metadata (relationship / kind / parent / membership).
|
|
32
|
+
*/
|
|
33
|
+
function projectSwitchableAccounts(input) {
|
|
34
|
+
const { state, graph, profilesById, activeUser, locale, resolveAvatarUrl } = input;
|
|
35
|
+
const activeAccountId = state?.activeAccountId ?? null;
|
|
36
|
+
const toRow = (accountUser, opts) => {
|
|
37
|
+
const accountId = accountUser.id?.toString() ?? '';
|
|
38
|
+
const handle = (0, accountUtils_1.getAccountFallbackHandle)(accountUser);
|
|
39
|
+
const secondaryHandle = handle ? `@${handle}` : null;
|
|
40
|
+
return {
|
|
41
|
+
accountId,
|
|
42
|
+
sessionId: opts.sessionId,
|
|
43
|
+
authuser: opts.authuser,
|
|
44
|
+
isCurrent: Boolean(accountId) && accountId === activeAccountId,
|
|
45
|
+
onDevice: Boolean(opts.sessionId),
|
|
46
|
+
relationship: opts.relationship,
|
|
47
|
+
kind: opts.kind,
|
|
48
|
+
parentAccountId: opts.parentAccountId,
|
|
49
|
+
callerMembership: opts.callerMembership,
|
|
50
|
+
displayName: (0, accountUtils_1.getAccountDisplayName)(accountUser, locale),
|
|
51
|
+
// Real email, or the `@handle` fallback (NEVER synthesized).
|
|
52
|
+
email: accountUser.email ?? secondaryHandle,
|
|
53
|
+
avatarUrl: resolveAvatarUrl(accountUser.avatar),
|
|
54
|
+
color: accountUser.color ?? null,
|
|
55
|
+
user: accountUser,
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
// --- Device rows (from the server-authoritative session set) ---
|
|
59
|
+
const deviceRows = (state?.accounts ?? []).flatMap((account) => {
|
|
60
|
+
const isActive = account.accountId === activeAccountId;
|
|
61
|
+
// The active row prefers the freshest `activeUser` (when supplied), then the
|
|
62
|
+
// batch-resolved profile; every other row uses the batch-resolved profile.
|
|
63
|
+
const accountUser = isActive && activeUser
|
|
64
|
+
? activeUser
|
|
65
|
+
: profilesById.get(account.accountId);
|
|
66
|
+
if (!accountUser) {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
return [toRow(accountUser, { sessionId: account.sessionId, authuser: account.authuser })];
|
|
70
|
+
});
|
|
71
|
+
// --- Merge graph nodes, deduping by account id ---
|
|
72
|
+
const byAccountId = new Map();
|
|
73
|
+
const order = [];
|
|
74
|
+
const remember = (row) => {
|
|
75
|
+
if (!row.accountId || byAccountId.has(row.accountId)) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
byAccountId.set(row.accountId, row);
|
|
79
|
+
order.push(row.accountId);
|
|
80
|
+
};
|
|
81
|
+
for (const row of deviceRows) {
|
|
82
|
+
remember(row);
|
|
83
|
+
}
|
|
84
|
+
for (const node of graph) {
|
|
85
|
+
const existing = byAccountId.get(node.accountId);
|
|
86
|
+
if (existing) {
|
|
87
|
+
// On-device account that is ALSO in the graph: enrich the device row with
|
|
88
|
+
// graph metadata; keep its (freshest) profile + sessionId + active flag.
|
|
89
|
+
byAccountId.set(node.accountId, {
|
|
90
|
+
...existing,
|
|
91
|
+
relationship: node.relationship,
|
|
92
|
+
kind: node.kind,
|
|
93
|
+
parentAccountId: node.parentAccountId,
|
|
94
|
+
callerMembership: node.callerMembership,
|
|
95
|
+
});
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
// Graph-only account (owned org / shared, not yet a device session).
|
|
99
|
+
remember(toRow(node.account, {
|
|
100
|
+
relationship: node.relationship,
|
|
101
|
+
kind: node.kind,
|
|
102
|
+
parentAccountId: node.parentAccountId,
|
|
103
|
+
callerMembership: node.callerMembership,
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
106
|
+
return order.flatMap((id) => {
|
|
107
|
+
const row = byAccountId.get(id);
|
|
108
|
+
return row ? [row] : [];
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Every distinct account id referenced by a device session set AND an account
|
|
113
|
+
* graph, sorted for a stable profile-fetch key. Feed to
|
|
114
|
+
* `oxyServices.getUsersByIds(...)`; graph nodes already embed their `account`
|
|
115
|
+
* document, but including their ids lets the caller pass one id set and lets the
|
|
116
|
+
* projection prefer freshly-fetched profiles uniformly.
|
|
117
|
+
*/
|
|
118
|
+
function switchableAccountIds(state, graph) {
|
|
119
|
+
const ids = new Set();
|
|
120
|
+
for (const account of state?.accounts ?? []) {
|
|
121
|
+
if (account.accountId) {
|
|
122
|
+
ids.add(account.accountId);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
for (const node of graph) {
|
|
126
|
+
if (node.accountId) {
|
|
127
|
+
ids.add(node.accountId);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return Array.from(ids).sort();
|
|
131
|
+
}
|
|
@@ -25,8 +25,15 @@ const sessionClientHost_1 = require("./sessionClientHost");
|
|
|
25
25
|
* specifier (bundler-fragile in Metro/Expo-web and Vite against the published
|
|
26
26
|
* dist). When omitted, the client falls back to the lazy loader.
|
|
27
27
|
*/
|
|
28
|
-
function createSessionClient(oxyServices, transport, socketFactory
|
|
28
|
+
function createSessionClient(oxyServices, transport, socketFactory,
|
|
29
|
+
/**
|
|
30
|
+
* Optional signed-out realtime wiring: `signedOutSocketAuth` (open the socket
|
|
31
|
+
* while signed out so an idle tab receives its device pushes) and
|
|
32
|
+
* `onSessionAppeared` (self-acquire when a sibling signs in). See
|
|
33
|
+
* {@link SessionClientOptions}.
|
|
34
|
+
*/
|
|
35
|
+
extra) {
|
|
29
36
|
const host = (0, sessionClientHost_1.createSessionClientHost)(oxyServices);
|
|
30
|
-
const client = new SessionClient_1.SessionClient(host, { transport, socketFactory });
|
|
37
|
+
const client = new SessionClient_1.SessionClient(host, { transport, socketFactory, ...extra });
|
|
31
38
|
return { client, host };
|
|
32
39
|
}
|