@oxyhq/core 17.0.0 → 17.0.3
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/i18n/locales/en-US.json +6 -0
- package/dist/cjs/i18n/locales/es-ES.json +6 -0
- package/dist/cjs/i18n/locales/locales/en-US.json +6 -0
- package/dist/cjs/i18n/locales/locales/es-ES.json +6 -0
- package/dist/cjs/mixins/OxyServices.accounts.js +3 -1
- package/dist/cjs/server/userInvalidation.js +6 -0
- package/dist/cjs/session/accountDialogController.js +3 -1
- package/dist/cjs/session/accountProjection.js +19 -1
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/i18n/locales/en-US.json +6 -0
- package/dist/esm/i18n/locales/es-ES.json +6 -0
- package/dist/esm/i18n/locales/locales/en-US.json +6 -0
- package/dist/esm/i18n/locales/locales/es-ES.json +6 -0
- package/dist/esm/mixins/OxyServices.accounts.js +1 -1
- package/dist/esm/server/userInvalidation.js +6 -0
- package/dist/esm/session/accountDialogController.js +3 -1
- package/dist/esm/session/accountProjection.js +19 -1
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/crypto/keyManager.d.ts +2 -2
- package/dist/types/mixins/OxyServices.accounts.d.ts +8 -7
- package/dist/types/session/accountDialogController.d.ts +1 -1
- package/dist/types/session/accountProjection.d.ts +6 -0
- package/package.json +3 -3
- package/src/crypto/keyManager.ts +1 -2
- package/src/i18n/locales/en-US.json +6 -0
- package/src/i18n/locales/es-ES.json +6 -0
- package/src/mixins/OxyServices.accounts.ts +8 -8
- package/src/server/__tests__/userInvalidation.test.ts +14 -2
- package/src/server/userInvalidation.ts +6 -0
- package/src/session/__tests__/accountDialogController.test.ts +13 -1
- package/src/session/__tests__/accountProjection.test.ts +31 -0
- package/src/session/accountDialogController.ts +4 -2
- package/src/session/accountProjection.ts +19 -1
- package/src/types/color.d.ts +0 -20
- package/src/types/elliptic.d.ts +0 -70
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Handles secure generation, storage, and retrieval of cryptographic keys.
|
|
5
5
|
* Private keys are stored securely using expo-secure-store and never leave the device.
|
|
6
6
|
*/
|
|
7
|
-
import
|
|
7
|
+
import { ec as EC } from 'elliptic';
|
|
8
8
|
import { type IdentityMarker } from './identityMarker';
|
|
9
9
|
/**
|
|
10
10
|
* Thrown when an identity-mutating operation (createIdentity / importKeyPair)
|
|
@@ -536,7 +536,7 @@ export declare class KeyManager {
|
|
|
536
536
|
* Get the elliptic curve key object from the stored private key
|
|
537
537
|
* Used internally for signing operations
|
|
538
538
|
*/
|
|
539
|
-
static getKeyPairObject(): Promise<
|
|
539
|
+
static getKeyPairObject(): Promise<EC.KeyPair | null>;
|
|
540
540
|
/**
|
|
541
541
|
* Derive public key from a private key (without storing)
|
|
542
542
|
*/
|
|
@@ -33,19 +33,20 @@
|
|
|
33
33
|
* registers the switched session into the operator's device-set directly).
|
|
34
34
|
*/
|
|
35
35
|
import type { User } from '../models/interfaces';
|
|
36
|
-
import type { OrganizationCategory } from '@oxyhq/contracts';
|
|
36
|
+
import type { AccountKind, OrganizationCategory } from '@oxyhq/contracts';
|
|
37
37
|
import type { SessionLoginResponse } from '../models/session';
|
|
38
38
|
import type { OxyServicesBase } from '../OxyServices.base';
|
|
39
39
|
/**
|
|
40
40
|
* Account classification, orthogonal to the federation `type`
|
|
41
41
|
* (`local|federated|agent|automated`). `personal` accounts have a direct login;
|
|
42
|
-
* `organization` / `project` / `bot` accounts are operated via
|
|
43
|
-
* and have no direct login.
|
|
42
|
+
* `organization` / `project` / `bot` / `channel` accounts are operated via
|
|
43
|
+
* `AccountMember` and have no direct login. Of those, only the first three may
|
|
44
|
+
* be acted AS — see `isActAsEligibleKind`.
|
|
45
|
+
*
|
|
46
|
+
* Single source of truth is `@oxyhq/contracts`.
|
|
44
47
|
*/
|
|
45
|
-
export type AccountKind
|
|
46
|
-
|
|
47
|
-
export type { OrganizationCategory } from '@oxyhq/contracts';
|
|
48
|
-
export { ORGANIZATION_CATEGORIES } from '@oxyhq/contracts';
|
|
48
|
+
export type { AccountKind, OrganizationCategory } from '@oxyhq/contracts';
|
|
49
|
+
export { ACCOUNT_KINDS, ORGANIZATION_CATEGORIES, isActAsEligibleKind } from '@oxyhq/contracts';
|
|
49
50
|
/**
|
|
50
51
|
* The calling user's relationship to an account node, as resolved by the API:
|
|
51
52
|
* - `self` — the caller's own personal (root) account.
|
|
@@ -434,7 +434,7 @@ export declare class AccountDialogController {
|
|
|
434
434
|
* subscription, which re-projects the active row. Concurrent switches are
|
|
435
435
|
* ignored while one is in flight.
|
|
436
436
|
*/
|
|
437
|
-
switchTo(accountId: string): Promise<
|
|
437
|
+
switchTo(accountId: string): Promise<boolean>;
|
|
438
438
|
/**
|
|
439
439
|
* Start "Sign in with Oxy". Native devices with a shared identity mint a
|
|
440
440
|
* session silently (`signInWithSharedIdentity`); everything else (web, or a
|
|
@@ -130,6 +130,9 @@ export interface ProjectSwitchableAccountsInput {
|
|
|
130
130
|
* graph-only rows (in graph order). An account present as BOTH a device session
|
|
131
131
|
* and a graph node is deduped into ONE device row enriched with the graph
|
|
132
132
|
* metadata (relationship / kind / parent / membership).
|
|
133
|
+
*
|
|
134
|
+
* Graph nodes of a kind nobody may act as (`channel`) are omitted — see the
|
|
135
|
+
* filter below.
|
|
133
136
|
*/
|
|
134
137
|
export declare function projectSwitchableAccounts(input: ProjectSwitchableAccountsInput): SwitchableAccount[];
|
|
135
138
|
/**
|
|
@@ -138,5 +141,8 @@ export declare function projectSwitchableAccounts(input: ProjectSwitchableAccoun
|
|
|
138
141
|
* `oxyServices.getUsersByIds(...)`; graph nodes already embed their `account`
|
|
139
142
|
* document, but including their ids lets the caller pass one id set and lets the
|
|
140
143
|
* projection prefer freshly-fetched profiles uniformly.
|
|
144
|
+
*
|
|
145
|
+
* Applies the SAME act-as filter as {@link projectSwitchableAccounts} to graph
|
|
146
|
+
* nodes, so this never fetches a profile for a row the projection will drop.
|
|
141
147
|
*/
|
|
142
148
|
export declare function switchableAccountIds(state: DeviceSessionState | null, graph: AccountNode[]): string[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxyhq/core",
|
|
3
|
-
"version": "17.0.
|
|
3
|
+
"version": "17.0.3",
|
|
4
4
|
"description": "OxyHQ SDK Foundation — API client, authentication, cryptographic identity, and shared utilities",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -115,9 +115,10 @@
|
|
|
115
115
|
"dependencies": {
|
|
116
116
|
"@noble/ciphers": "^1.3.0",
|
|
117
117
|
"@noble/hashes": "^1.8.0",
|
|
118
|
-
"@oxyhq/contracts": "^0.
|
|
118
|
+
"@oxyhq/contracts": "^0.21.0",
|
|
119
119
|
"@oxyhq/protocol": "^0.1.6",
|
|
120
120
|
"@scure/bip39": "^1.6.0",
|
|
121
|
+
"@types/elliptic": "^6.4.18",
|
|
121
122
|
"buffer": "^6.0.3",
|
|
122
123
|
"elliptic": "^6.6.1",
|
|
123
124
|
"invariant": "^2.2.4",
|
|
@@ -154,7 +155,6 @@
|
|
|
154
155
|
"devDependencies": {
|
|
155
156
|
"@biomejs/biome": "^1.9.4",
|
|
156
157
|
"@react-native-async-storage/async-storage": "^2.2.0",
|
|
157
|
-
"@types/elliptic": "^6.4.18",
|
|
158
158
|
"@types/express": "^4.17.21",
|
|
159
159
|
"@types/invariant": "^2.2.34",
|
|
160
160
|
"@types/node": "^20.19.43",
|
package/src/crypto/keyManager.ts
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { ec as EC } from 'elliptic';
|
|
9
|
-
import type { ECKeyPair } from 'elliptic';
|
|
10
9
|
import { isWeb, isIOS, isAndroid } from '../utils/platform';
|
|
11
10
|
import { type ExpoCryptoLike, type ExpoSecureStoreLike, isReactNative, isNodeJS, loadAsyncStorage, loadExpoCrypto, loadNodeCrypto, loadSecureStore, loadSharedIdentityBridge } from '@oxyhq/protocol';
|
|
12
11
|
import { isDev, logger } from '../logger';
|
|
@@ -2490,7 +2489,7 @@ export class KeyManager {
|
|
|
2490
2489
|
* Get the elliptic curve key object from the stored private key
|
|
2491
2490
|
* Used internally for signing operations
|
|
2492
2491
|
*/
|
|
2493
|
-
static async getKeyPairObject(): Promise<
|
|
2492
|
+
static async getKeyPairObject(): Promise<EC.KeyPair | null> {
|
|
2494
2493
|
if (isWebPlatform()) {
|
|
2495
2494
|
return null; // Identity storage is only available on native platforms
|
|
2496
2495
|
}
|
|
@@ -54,6 +54,12 @@
|
|
|
54
54
|
"status": {
|
|
55
55
|
"accountSwitched": "Now using {{name}}",
|
|
56
56
|
"signingIn": "Signing in…"
|
|
57
|
+
},
|
|
58
|
+
"errors": {
|
|
59
|
+
"notConfigured": "Sign-in isn't available",
|
|
60
|
+
"notConfiguredDescription": "{{app}} isn't set up for sign-in yet. Contact the app's developer.",
|
|
61
|
+
"failed": "Couldn't start sign-in",
|
|
62
|
+
"failedDescription": "Something went wrong. Please try again."
|
|
57
63
|
}
|
|
58
64
|
},
|
|
59
65
|
"signup": {
|
|
@@ -54,6 +54,12 @@
|
|
|
54
54
|
"status": {
|
|
55
55
|
"accountSwitched": "Ahora usando {{name}}",
|
|
56
56
|
"signingIn": "Iniciando sesión…"
|
|
57
|
+
},
|
|
58
|
+
"errors": {
|
|
59
|
+
"notConfigured": "El inicio de sesión no está disponible",
|
|
60
|
+
"notConfiguredDescription": "{{app}} todavía no está configurada para iniciar sesión. Contacta con quien desarrolla la app.",
|
|
61
|
+
"failed": "No se pudo iniciar sesión",
|
|
62
|
+
"failedDescription": "Algo ha ido mal. Inténtalo de nuevo."
|
|
57
63
|
}
|
|
58
64
|
},
|
|
59
65
|
"signup": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
* registers the switched session into the operator's device-set directly).
|
|
34
34
|
*/
|
|
35
35
|
import type { User } from '../models/interfaces';
|
|
36
|
-
import type { OrganizationCategory } from '@oxyhq/contracts';
|
|
36
|
+
import type { AccountKind, OrganizationCategory } from '@oxyhq/contracts';
|
|
37
37
|
import type { SessionLoginResponse } from '../models/session';
|
|
38
38
|
import type { OxyServicesBase } from '../OxyServices.base';
|
|
39
39
|
import { normalizeUserIdentity } from '../utils/userIdentity';
|
|
@@ -46,14 +46,14 @@ import { CACHE_TIMES } from './mixinHelpers';
|
|
|
46
46
|
/**
|
|
47
47
|
* Account classification, orthogonal to the federation `type`
|
|
48
48
|
* (`local|federated|agent|automated`). `personal` accounts have a direct login;
|
|
49
|
-
* `organization` / `project` / `bot` accounts are operated via
|
|
50
|
-
* and have no direct login.
|
|
49
|
+
* `organization` / `project` / `bot` / `channel` accounts are operated via
|
|
50
|
+
* `AccountMember` and have no direct login. Of those, only the first three may
|
|
51
|
+
* be acted AS — see `isActAsEligibleKind`.
|
|
52
|
+
*
|
|
53
|
+
* Single source of truth is `@oxyhq/contracts`.
|
|
51
54
|
*/
|
|
52
|
-
export type AccountKind
|
|
53
|
-
|
|
54
|
-
/** Real-estate / team taxonomy for `kind: 'organization'` accounts. */
|
|
55
|
-
export type { OrganizationCategory } from '@oxyhq/contracts';
|
|
56
|
-
export { ORGANIZATION_CATEGORIES } from '@oxyhq/contracts';
|
|
55
|
+
export type { AccountKind, OrganizationCategory } from '@oxyhq/contracts';
|
|
56
|
+
export { ACCOUNT_KINDS, ORGANIZATION_CATEGORIES, isActAsEligibleKind } from '@oxyhq/contracts';
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
59
|
* The calling user's relationship to an account node, as resolved by the API:
|
|
@@ -112,7 +112,13 @@ describe('@oxyhq/core/server createOxyUserInvalidationHandler', () => {
|
|
|
112
112
|
createOxyUserInvalidationHandler({ oxy: evictor })(validMessage);
|
|
113
113
|
|
|
114
114
|
expect(entries).toEqual(['GET:/users/user-1']);
|
|
115
|
-
expect(prefixes).toEqual([
|
|
115
|
+
expect(prefixes).toEqual([
|
|
116
|
+
'GET:/session/user/',
|
|
117
|
+
'GET:/users/me',
|
|
118
|
+
'GET:/auth/lookup/',
|
|
119
|
+
'GET:/profiles/username/',
|
|
120
|
+
'GET:/profiles/resolve',
|
|
121
|
+
]);
|
|
116
122
|
});
|
|
117
123
|
|
|
118
124
|
it('hands the parsed event to app-specific eviction', () => {
|
|
@@ -203,6 +209,12 @@ describe('@oxyhq/core/server evictOxyIdentityCache', () => {
|
|
|
203
209
|
evictOxyIdentityCache(evictor, 'abc123');
|
|
204
210
|
|
|
205
211
|
expect(entries).toEqual(['GET:/users/abc123']);
|
|
206
|
-
expect(prefixes).toEqual([
|
|
212
|
+
expect(prefixes).toEqual([
|
|
213
|
+
'GET:/session/user/',
|
|
214
|
+
'GET:/users/me',
|
|
215
|
+
'GET:/auth/lookup/',
|
|
216
|
+
'GET:/profiles/username/',
|
|
217
|
+
'GET:/profiles/resolve',
|
|
218
|
+
]);
|
|
207
219
|
});
|
|
208
220
|
});
|
|
@@ -215,6 +215,12 @@ export function createOxyUserInvalidationHandler(
|
|
|
215
215
|
* wrong data.
|
|
216
216
|
*/
|
|
217
217
|
export function evictOxyIdentityCache(oxy: OxyIdentityCacheEvictor, userId: string): void {
|
|
218
|
+
// Match the sweep the user mixin runs after a local profile write — session-
|
|
219
|
+
// bound and /users/me entries are keyed without the user id, so they must be
|
|
220
|
+
// prefix-swept on cross-service invalidation too.
|
|
221
|
+
oxy.clearCacheByPrefix('GET:/session/user/');
|
|
222
|
+
oxy.clearCacheByPrefix('GET:/users/me');
|
|
223
|
+
oxy.clearCacheByPrefix('GET:/auth/lookup/');
|
|
218
224
|
oxy.clearCacheEntry(`GET:/users/${userId}`);
|
|
219
225
|
oxy.clearCacheByPrefix('GET:/profiles/username/');
|
|
220
226
|
oxy.clearCacheByPrefix('GET:/profiles/resolve');
|
|
@@ -472,7 +472,7 @@ describe('AccountDialogController — switchTo (uniform switch)', () => {
|
|
|
472
472
|
sc.set(state([{ accountId: 'a1', sessionId: 's1' }], 'a1'));
|
|
473
473
|
oxy.switchToAccount.mockRejectedValue(new Error('Cannot switch into a personal account'));
|
|
474
474
|
|
|
475
|
-
await expect(controller.switchTo('org1')).resolves.
|
|
475
|
+
await expect(controller.switchTo('org1')).resolves.toBe(false);
|
|
476
476
|
|
|
477
477
|
const snap = controller.getSnapshot();
|
|
478
478
|
expect(snap.error).toBe('Cannot switch into a personal account');
|
|
@@ -517,6 +517,18 @@ describe('AccountDialogController — switchTo (uniform switch)', () => {
|
|
|
517
517
|
release();
|
|
518
518
|
await first;
|
|
519
519
|
});
|
|
520
|
+
|
|
521
|
+
it('returns true when the switch succeeds even if the post-switch refresh fails', async () => {
|
|
522
|
+
const { controller, oxy, sc } = makeHarness();
|
|
523
|
+
sc.set(state([{ accountId: 'a1', sessionId: 's1' }, { accountId: 'a2', sessionId: 's2' }], 'a1'));
|
|
524
|
+
jest.spyOn(sc, 'switchAccount').mockResolvedValue(undefined);
|
|
525
|
+
oxy.listAccounts.mockRejectedValue(new Error('network down'));
|
|
526
|
+
|
|
527
|
+
await expect(controller.switchTo('a2')).resolves.toBe(true);
|
|
528
|
+
|
|
529
|
+
expect(controller.getSnapshot().error).toBe('network down');
|
|
530
|
+
expect(controller.getSnapshot().switchingAccountId).toBeNull();
|
|
531
|
+
});
|
|
520
532
|
});
|
|
521
533
|
|
|
522
534
|
describe('AccountDialogController — sign in with Oxy', () => {
|
|
@@ -115,6 +115,29 @@ describe('projectSwitchableAccounts', () => {
|
|
|
115
115
|
expect(org.parentAccountId).toBe('a1');
|
|
116
116
|
});
|
|
117
117
|
|
|
118
|
+
/**
|
|
119
|
+
* A channel is a content identity nobody acts as, so it must never be offered
|
|
120
|
+
* as a switch target. It reaches the projection through the GRAPH lane, not
|
|
121
|
+
* the device lane — a channel has no credentials and so can never be a device
|
|
122
|
+
* session, which is exactly why "no-login accounts can't appear here by
|
|
123
|
+
* construction" is false: `listAccounts()` contributes credential-less
|
|
124
|
+
* accounts on purpose (that is how an org first becomes switchable).
|
|
125
|
+
*/
|
|
126
|
+
it('omits a graph-only channel account from the switcher', () => {
|
|
127
|
+
const rows = projectSwitchableAccounts({
|
|
128
|
+
state: state([{ accountId: 'a1', sessionId: 's1' }], 'a1'),
|
|
129
|
+
graph: [
|
|
130
|
+
graphNode('org1', { kind: 'organization' }),
|
|
131
|
+
graphNode('chan1', { kind: 'channel' }),
|
|
132
|
+
],
|
|
133
|
+
profilesById: mapOf(user('a1')),
|
|
134
|
+
resolveAvatarUrl: noAvatar,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
expect(rows.map((r) => r.accountId)).toEqual(['a1', 'org1']);
|
|
138
|
+
expect(rows.some((r) => r.kind === 'channel')).toBe(false);
|
|
139
|
+
});
|
|
140
|
+
|
|
118
141
|
it('dedups an account present as BOTH device session and graph node into ONE enriched row', () => {
|
|
119
142
|
const rows = projectSwitchableAccounts({
|
|
120
143
|
state: state([{ accountId: 'a1', sessionId: 's1', authuser: 0 }], 'a1'),
|
|
@@ -208,4 +231,12 @@ describe('switchableAccountIds', () => {
|
|
|
208
231
|
it('returns [] for null state and empty graph', () => {
|
|
209
232
|
expect(switchableAccountIds(null, [])).toEqual([]);
|
|
210
233
|
});
|
|
234
|
+
|
|
235
|
+
it('omits a graph-only channel, so no profile is fetched for a dropped row', () => {
|
|
236
|
+
const ids = switchableAccountIds(null, [
|
|
237
|
+
graphNode('org1', { kind: 'organization' }),
|
|
238
|
+
graphNode('chan1', { kind: 'channel' }),
|
|
239
|
+
]);
|
|
240
|
+
expect(ids).toEqual(['org1']);
|
|
241
|
+
});
|
|
211
242
|
});
|
|
@@ -783,8 +783,8 @@ export class AccountDialogController {
|
|
|
783
783
|
* subscription, which re-projects the active row. Concurrent switches are
|
|
784
784
|
* ignored while one is in flight.
|
|
785
785
|
*/
|
|
786
|
-
async switchTo(accountId: string): Promise<
|
|
787
|
-
if (this.switchingAccountId) return;
|
|
786
|
+
async switchTo(accountId: string): Promise<boolean> {
|
|
787
|
+
if (this.switchingAccountId) return false;
|
|
788
788
|
this.switchingAccountId = accountId;
|
|
789
789
|
this.error = null;
|
|
790
790
|
this.emit();
|
|
@@ -815,8 +815,10 @@ export class AccountDialogController {
|
|
|
815
815
|
}
|
|
816
816
|
// Re-project + refetch immediately; the subscription also fires.
|
|
817
817
|
await this.refresh();
|
|
818
|
+
return true;
|
|
818
819
|
} catch (error) {
|
|
819
820
|
this.error = errorMessage(error);
|
|
821
|
+
return false;
|
|
820
822
|
} finally {
|
|
821
823
|
this.switchingAccountId = null;
|
|
822
824
|
this.emit();
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import type { DeviceSessionState } from '@oxyhq/contracts';
|
|
21
|
+
import { isActAsEligibleKind } from '@oxyhq/contracts';
|
|
21
22
|
import type { User } from '../models/interfaces';
|
|
22
23
|
import type {
|
|
23
24
|
AccountNode,
|
|
@@ -142,6 +143,9 @@ export interface ProjectSwitchableAccountsInput {
|
|
|
142
143
|
* graph-only rows (in graph order). An account present as BOTH a device session
|
|
143
144
|
* and a graph node is deduped into ONE device row enriched with the graph
|
|
144
145
|
* metadata (relationship / kind / parent / membership).
|
|
146
|
+
*
|
|
147
|
+
* Graph nodes of a kind nobody may act as (`channel`) are omitted — see the
|
|
148
|
+
* filter below.
|
|
145
149
|
*/
|
|
146
150
|
export function projectSwitchableAccounts(input: ProjectSwitchableAccountsInput): SwitchableAccount[] {
|
|
147
151
|
const { state, graph, profilesById, activeUser, locale, resolveAvatarUrl } = input;
|
|
@@ -227,6 +231,17 @@ export function projectSwitchableAccounts(input: ProjectSwitchableAccountsInput)
|
|
|
227
231
|
continue;
|
|
228
232
|
}
|
|
229
233
|
// Graph-only account (owned org / shared, not yet a device session).
|
|
234
|
+
//
|
|
235
|
+
// This lane is why a no-login account is NOT kept out of the switcher "by
|
|
236
|
+
// construction": the graph contributes accounts that have no device session
|
|
237
|
+
// and no credentials at all, which is exactly how an org first becomes
|
|
238
|
+
// switchable. So a kind that must never be switched into has to be filtered
|
|
239
|
+
// HERE, and `isActAsEligibleKind` is the same predicate the server enforces
|
|
240
|
+
// on `POST /accounts/:id/switch` — offering a row the server would 403 is a
|
|
241
|
+
// dead button.
|
|
242
|
+
if (!isActAsEligibleKind(node.kind)) {
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
230
245
|
remember(toRow(node.account, {
|
|
231
246
|
relationship: node.relationship,
|
|
232
247
|
kind: node.kind,
|
|
@@ -247,6 +262,9 @@ export function projectSwitchableAccounts(input: ProjectSwitchableAccountsInput)
|
|
|
247
262
|
* `oxyServices.getUsersByIds(...)`; graph nodes already embed their `account`
|
|
248
263
|
* document, but including their ids lets the caller pass one id set and lets the
|
|
249
264
|
* projection prefer freshly-fetched profiles uniformly.
|
|
265
|
+
*
|
|
266
|
+
* Applies the SAME act-as filter as {@link projectSwitchableAccounts} to graph
|
|
267
|
+
* nodes, so this never fetches a profile for a row the projection will drop.
|
|
250
268
|
*/
|
|
251
269
|
export function switchableAccountIds(
|
|
252
270
|
state: DeviceSessionState | null,
|
|
@@ -259,7 +277,7 @@ export function switchableAccountIds(
|
|
|
259
277
|
}
|
|
260
278
|
}
|
|
261
279
|
for (const node of graph) {
|
|
262
|
-
if (node.accountId) {
|
|
280
|
+
if (node.accountId && isActAsEligibleKind(node.kind)) {
|
|
263
281
|
ids.add(node.accountId);
|
|
264
282
|
}
|
|
265
283
|
}
|
package/src/types/color.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
declare module 'color' {
|
|
2
|
-
interface Color {
|
|
3
|
-
(color: string | Color | undefined, model?: string): Color;
|
|
4
|
-
rgb(): Color;
|
|
5
|
-
rgba(): Color;
|
|
6
|
-
alpha(): number;
|
|
7
|
-
alpha(val: number): Color;
|
|
8
|
-
fade(val: number): Color;
|
|
9
|
-
lighten(val: number): Color;
|
|
10
|
-
darken(val: number): Color;
|
|
11
|
-
string(): string;
|
|
12
|
-
hex(): string;
|
|
13
|
-
toString(): string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function color(color: string | Color | undefined, model?: string): Color;
|
|
17
|
-
export default color;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
package/src/types/elliptic.d.ts
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
declare module 'elliptic' {
|
|
2
|
-
export interface BN {
|
|
3
|
-
toArray(endian?: 'be' | 'le', length?: number): number[];
|
|
4
|
-
toString(base?: number): string;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export interface KeyPair {
|
|
8
|
-
getPrivate(enc?: 'hex' | 'array' | 'bn'): string | number[] | any;
|
|
9
|
-
getPublic(): Point;
|
|
10
|
-
getPublic(enc: 'hex'): string;
|
|
11
|
-
getPublic(enc: 'array'): number[];
|
|
12
|
-
getPublic(compressed: boolean, enc?: 'hex'): string;
|
|
13
|
-
getPublic(compressed: boolean, enc?: 'array'): number[];
|
|
14
|
-
sign(msg: string | number[]): Signature;
|
|
15
|
-
verify(msg: string | number[], signature: Signature | string | { r: string | number[]; s: string | number[] }): boolean;
|
|
16
|
-
/** ECDH: derive the shared point's x-coordinate against another party's public point. */
|
|
17
|
-
derive(pub: Point): BN;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface Signature {
|
|
21
|
-
r: any;
|
|
22
|
-
s: any;
|
|
23
|
-
recoveryParam: number | null;
|
|
24
|
-
toDER(enc?: 'hex'): string;
|
|
25
|
-
toCompact(enc?: 'hex', param?: number): string;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface Curve {
|
|
29
|
-
n: any;
|
|
30
|
-
red: any;
|
|
31
|
-
g: Point;
|
|
32
|
-
decodePoint(bytes: string | number[], enc?: string): Point;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface Point {
|
|
36
|
-
x: any;
|
|
37
|
-
y: any;
|
|
38
|
-
inf: boolean;
|
|
39
|
-
getX(): any;
|
|
40
|
-
getY(): any;
|
|
41
|
-
encode(enc?: string, compressed?: boolean): string | number[];
|
|
42
|
-
add(p: Point): Point;
|
|
43
|
-
mul(k: any): Point;
|
|
44
|
-
eq(p: Point): boolean;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export class EC {
|
|
48
|
-
constructor(curve: string | Curve);
|
|
49
|
-
keyFromPrivate(priv: string | number[], enc?: string): KeyPair;
|
|
50
|
-
keyFromPublic(pub: string | number[] | Point, enc?: string): KeyPair;
|
|
51
|
-
genKeyPair(opts?: { entropy?: string | number[]; pers?: string | number[] }): KeyPair;
|
|
52
|
-
keyPair(opts?: { priv?: string | number[]; pub?: string | number[] | Point; privEnc?: string; pubEnc?: string }): KeyPair;
|
|
53
|
-
curve: Curve;
|
|
54
|
-
n: any;
|
|
55
|
-
nh: any;
|
|
56
|
-
g: Point;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export type ECKeyPair = KeyPair;
|
|
60
|
-
|
|
61
|
-
// ec can be called as a function or used as a constructor
|
|
62
|
-
export interface ECConstructor {
|
|
63
|
-
new (curve: string | Curve): EC;
|
|
64
|
-
(curve: string | Curve): EC;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export const ec: ECConstructor;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
|